Saturday, December 29, 2007

Instanciação dinâmica - VCL e VCL.NET

Utilizo bastante a instanciação dinâmica de classes em bibliotecas e projetos Delphi. Algumas vezes a partir do nome da classe, outras a partir da classe propriamente dita. No meu caminho de migração - ou melhor, compatibilização - de algumas bibliotecas para a VCL.NET criei duas funções utilitárias que me permitem trabalhar com o mesmo fonte tanto para Win32 quanto para .NET. A unit segue abaixo. Um projeto completo mostrando a utilização pode ser encontrado aqui.

unit uCreateInstance;

interface

uses
  Classes, SysUtils;

function CreateInstance(AClass: TClass): TObject; overload;
function CreateInstance(AClassName: string): TObject; overload;

implementation

function CreateInstance(AClass: TClass): TObject;
begin
   {$IFDEF CLR}
   Result := Activator.CreateInstance(AClass.ClassInfo); // .NET
   {$ELSE}
   Result := AClass.Create; // Win32
   {$ENDIF}
end;

function CreateInstance(AClassName: string): TObject;
var
   AClass: TClass;
begin
   AClass := GetClass(AClassName);
   if not Assigned(AClass) then
     raise Exception.Create('A classe "' + AClassName + '" não está registrada.');
   Result := CreateInstance(AClass);
end;

end.

Wednesday, December 26, 2007

Quanto mais eu uso o C#...

...mais eu gosto do Pascal e do Delphi!!!!
Francamente. Um tanto de coisa na linguagem é feita para agradar os adoradores do C++ que não podem viver sem os operadores de incremento, ou para rivalizarem com o Java no quesito "quem consegue escrever código com menos linhas". O que, na prática, contribui pouco para o que realmente importa. EMO, clareza de código e facilidade de manutenção é muito, mas muito mais importante, do quê "quem escreve menos código para criar uma classe".

Veja só que beleza de código:

int i = 1;
i = i++;
i = i > 1 ? ++i : --i;
label1.Text = i.ToString();

Qual será o texto do label1????
.
.
.
Tem certeza???
.
.
.
Levou mais de 2 segundos para chegar ao resultado? Então tem algo errado com o código certo? Este tipo de aberração de código é que a linguagem, pela sua estrutura cheia de operadores "mágicos", permite escrever. Claro que a turma do C# vai me dizer "mas o cara que escreveu isto é um asshole!" ou "os operadores são poderosíssimos quando bem usados!". Tudo isto é verdade, claro, mas existem as linguagens que exigem ou reforçam boas práticas, e as que não o fazem. O C# é uma delas.

Mas fazer o quê. Para VS2005 acho melhor mesmo o Chrome!

Monday, December 24, 2007

So this is Christmas

Tem cada uma que a gente lê que desanima... Esta foi mais uma. Não sei quantos "artigos" do Danny Thorpe eu li. Acho que tenho mais idade que ele, mas quando eu era iniciante na informática, ele já escrevia livros.
Aí segue a história: Uma pena realmente, perder este monte de mentes a cada ano.

Thats too bad

Sunday, December 23, 2007

TIOBE Index

Final de ano está aí. E vale uma olhada no TIOBE Index que "mede" a popularidade das linguagens de programação. Para quem não conhece, atenção: Não é um número "cientificamente comprovado"! Mas dá para ter uma idéia do que está acontecendo por aí.

O Delphi está em 11a posição perdendo uma posição para o Ruby (não é novidade) e para o JavaScript (não que eu considere JavaScritp uma linguagem de fato! ). De qualquer forma o número "bom" disso é que o share do Delphi aumentou 0.10 no último ano. Pouco mas eu sinceramente esperava uma queda!

O IDE e a velha paleta

Há bastante tempo uso o plug-in DDevExtensions para o BDS 2006 que, entre outras coisas, me permite ter a "velha paleta" de componentes de volta. Para quem quiser baixar e testar: DDevExtensions. Aproveite e baixe também o DelphiSpeedUp no mesmo site! Este link acima contém a versão oficial. Quem quiser ser um "beta tester" pode baixar os snapshot builds do DDevExtensions, tanto quanto do DelphiSpeedUp no seguinte link: Snapshot Builds. Eu uso e recomendo!

Engraçado é que não conheço sequer um desenvolvedor - desenvolvedor MESMO, não aqueles que só abrem o Delphi para estudar para certificação - que goste de paleta introduzida no BDS 2005 e continuada nas demais versões do Delphi. No Delphi Day online dia 7/12/2007 lembro-me de ter ouvido o Andreano Lanusse dizer mais ou menos "acostume-se com ela pois vai continuar assim!". O pessoal da CodeGear poderia rever isto aí na próxima versão. Pelo menos deveria poder deixá-la como o DDevExtensions faz e assim agradar os desenvolvedores que, assim como eu, preferem a IDE com a paleta antiga.

E preferir a paleta antiga não é "picuinha". A paleta antiga tem 2 vantagens importantes EMO:

1- Ocupa uma faixa estreita acima. Desta forma não torna minúscula a área disponível para quem ainda escreve CÓDIGO (CodeGear tem a ver com isto não? )

2- Permite ver os ícones dos componentes e escolhê-los sem ter que digitar seus nomes

A busca ao digitar é feita exclusivamente num Edit que pode ser colocado onde bem se desejar.

Monday, December 17, 2007

DataSnap.NET - Parte III

Recebi hoje os fontes dos projetos de Ondrej Kelle (http://tondrej.blogspot.com):

1) ASP.NET replacement of CodeGear's httpsrvr.dll

2) WinForms .NET replacement of CodeGear's Scktsrvr.exe

Ambos em C# (BDS 2006). Vou verificar e colocar para rodar em breve!

Obrigado Ondrej!

Saturday, December 15, 2007

Sunday, December 9, 2007

RemObjects & Pascal for VisualStudio

Há muito acompanho o que a empresa RemObjects vem fazendo no mundo do desenvolvimento (www.remobjects.com). Atualmente, ando olhando um pouco mais a fundo os produtos desta empresa. Além do DataAbstract (vulgo Vinci, o original RemObjects), tem o Chrome, que nada mais é que o Pascal para Visual Studio, incluindo o 2005 e 2008.
O primeiro RemObjects eu usei há muito, quando lançado. Hoje olho para ele com outros olhos, uma vez que a perspectiva de desenvolvimento corporativo e em multicamadas é, aparentemente, bem amplo. Não pude deixar de ver também o Chrome - Pascal para VS - e aí é que a coisa complica.... O Object Pascal é, de fato, adorado pelos seus usuários porquê nós de fato achamos que linhas de código como estas

static void Main(string[] args)
{
   int i = 0;
   i = i++;
}

diferentemente do que pensam os fanáticos por C++ e C# (o códito compila em ambos, com resultados diferentes!), nunca, nunca mesmo, irão contribuir para que o código fonte seja mais claro, que a qualidade do software seja mais garantida, que meu trabalho seja mais fácil ou mais bem feito, e que eu chegue mais cedo em casa! E ponto final.

A partir do momento que a RemObjects começa a transformar o ObjectPascal num híbrido eu, particularmente, começo a ficar desconfiado.

Quem quiser, tire a prova: Chrome Language Features

Vou até olhar mais profundamente, mas... com certeza tenho minhas restriçôes.

Friday, December 7, 2007

CodeGear Delphi Day - 7 Dezembro

Acabei de assistir ao Delphi Day online. De novo tive a mesma impressão que tive há pouco mais de um mês na Borcon 2007: Me parece que a CodeGear não vê o Delphi em ambiente corporativo/sistemas distribuídos. Batem na tecla Client/Server e desktop. E claro, ECO e ASP.NET.
Acho que já passou da hora deles abrirem o olho. Muita gente desenvolveu grandes sistemas multi-camadas em Delphi/Win32 ao longo de vários anos, principalmente com o surgimento do Midas/DataSnap. Agora não tem para onde correr. Se vai ter que jogar tudo fora e reescrever, porquê manter em Delphi? Porquê não ir para o VisualStudio?

Será que só eu que enxergo isto?

ATENÇÃO CODEGEAR: Passou da hora de ter um DataSnap.NET!!!! ACORDEM!!!


Tuesday, December 4, 2007

DataSnap.NET - Parte II: Aguardando o código

Continuando sobre o DataSnap.NET...

Escrevi para o TOndrej que mantém o excelente blog sobre programação & Delphi http://tondrej.blogspot.com solicitando o código que ele desenvolveu para criar servidores DataSnap.NET. Ele ficou de me enviar o código, em C# segundo está no blog. Assim que o tiver nas mãos vou dar uma bela fuçada! Seria um belo ponto de partida para um framework funcional. Apesar que... eu obviamente iria criar código em Delphi.NET, BDS 2006 ou RAD Studio 2007, pois o suporte a C# é mínimo.

 

Como NÃO programar um componente Trial/Shareware

Ultimamente tenho tido certa necessidade de “brincar” com os DCU’s de alguns componentes comerciais. “Brincar” aí significa disassemblá-los mesmo, e se possível alterar diretamente o DCU.
Isto pode ser necessário, por exemplo, quando você não possui o código fonte de um componente descontinuado pelo seu desenvolvedor, ou então quando é necessário ter certo nível de “engenharia reversa”.

Inevitavelmente me vem à cabeça uma outra possibilidade: quebrar algum mecanismo de proteção contra execução de componentes shareware, o que obviamente é ilegal e não recomendável. Um mecanismo comum de desenvolvedores de componentes shareware Delphi é checar se o IDE do Delphi está sendo executado. Se estiver, o componente executa normalmente. Caso contrário, a execução é abortada ou alguma nag screen é exibida.

A forma mais infantil de criar este mecanismo de proteção é criar uma função única de checagem e/ou verificação que retorna algum tipo de resultado, geralmente booleano. Quem desenvolve componentes em versões trial nunca deveria fazer isto a não ser que queria que seus DCU’s sejam quebrados até por crackers amadores.

Recentemente disassemblei um componente e encontrei um código deste tipo:

function CheckProtection: boolean;
var
  Reg: TRegistry;
begin
  result := False;
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    Reg.OpenKey('My Software Key', False);
    if Reg.ValueExists('RegistryKey') then
      Result := True;
  finally
    Reg.Free;
  end;
end;

procedure TMyQuery.InternalPost;
begin
  if not CheckProtection then
  begin
    ShowMessage(‘Versão Demo. Bla Bla Bla’);
    Abort;
  end;
  // o código normal do componente “fucional” continua
  inherited InternalPost;
end;

O código não é exatamente assim, claro. É só uma reconstrução resumida do que faz o componente, deduzido a partir da dissecação do seu DCU.
O mais engraçado aqui é que não importa o código da função CheckProtection, muito menos o código que é executado depois da chamada, caso ela retorne False. Se eu modificar 3 bytes no DCU, este código NUNCA será executado.

Disassemblando o DCU gerado para o código em Pascal acima teríamos algo do tipo:

function CheckProtection: Boolean;
  55                  PUSH EBP
  8B EC               MOV EBP,ESP
  83 C4 F8            ADD ESP,-8
  C6 45 FF 00         MOV BYTE PTR [EBP-1],$00
  B2 01               MOV DL,$01
  A1 00 00 00 00      MOV EAX,DWORD PTR [_Dn_TRegistry{0x102}]
  E8 00 00 00 00      CALL TRegistry.Create{0x103}
  89 45 F8            MOV DWORD PTR [EBP-8],EAX
  BA 02 00 00 80      MOV EDX,$80000002
  8B 45 F8            MOV EAX,DWORD PTR [EBP-8]
  E8 00 00 00 00      CALL TRegistry.SetRootKey{0x104}
  33 C0               XOR EAX,EAX
  55                  PUSH EBP
  68 6E 00 00 00      PUSH CheckProtection{0x10D}+$0000006E
  64 FF 30            PUSH DWORD PTR FS:[EAX]
  64 89 20            MOV DWORD PTR FS:[EAX],ESP
  33 C9               XOR ECX,ECX
  BA 84 00 00 00      MOV EDX,CheckProtection{0x10D}+$00000084
  8B 45 F8            MOV EAX,DWORD PTR [EBP-8]
  E8 00 00 00 00      CALL TRegistry.OpenKey{0x105}
  BA 9C 00 00 00      MOV EDX,CheckProtection{0x10D}+$0000009C
  8B 45 F8            MOV EAX,DWORD PTR [EBP-8]
  E8 00 00 00 00      CALL TRegistry.ValueExists{0x106}
  84 C0               TEST AL,AL
  74 04               JE +4; (0x5 )
  C6 45 FF 01         MOV BYTE PTR [EBP-1],$01
  33 C0               XOR EAX,EAX
  5A                  POP EDX
  59                  POP ECX
  59                  POP ECX
  64 89 10            MOV DWORD PTR FS:[EAX],EDX
  68 75 00 00 00      PUSH CheckProtection{0x10D}+$00000075
  8B 45 F8            MOV EAX,DWORD PTR [EBP-8]
  E8 00 00 00 00      CALL TObject.Free{0xFE}
  C3                  RET NEAR
  E9 00 00 00 00      JMP @HandleFinally{0xFF}
  EB F0               JMP -16; (0x65)
  8A 45 FF            MOV AL,BYTE PTR [EBP-1]
  59                  POP ECX
  59                  POP ECX
  5D                  POP EBP
  C3                  RET NEAR
  FF FF               ? EDI
  FF FF               ? EDI
  0F 00 00            SLDT WORD PTR [EAX]
  00 4D 79            ADD BYTE PTR [EBP+121],CL
  20 53 6F            AND BYTE PTR [EBX+111],DL
  66 74 77            JE +119; (0x103)
  61                  POPA
  72 65               JB +101; (0xF4)
  20 4B 65            AND BYTE PTR [EBX+101],CL
  79 00               JNS 0; (0x94)
  FF FF               ? EDI
  FF FF               ? EDI
  0B 00               OR EAX,DWORD PTR [EAX]
  00 00               ADD BYTE PTR [EAX],AL
  52                  PUSH EDX
  65 67 69 73 74 72 79 4B 65 IMUL DWORD PTR GS:[BP+DI+116],$654B7972
  79 00               JNS 0; (0xA )
end;

O código fonte em Pascal não é grande, mas o código assembly correspondente mete medo em qualquer um, certo? Talvez... Mas... Não importa o tamanho ou a complexidade do assembly da função CheckProtection. Se eu pegar as 2 primeiras linhas:

function CheckProtection: Boolean;
  55                  PUSH EBP
  8B EC               MOV EBP,ESP

E transformá-las em

function CheckProtection: Boolean;
  B0 A1               MOV AL,$01
  C3                  RET NEAR

Teríamos exatamente o código que é gerado se eu tivesse uma função do tipo:

function CheckProtection: Boolean;
begin
  Result := True;
  Exit;
  //  o código a partir daqui nunca será executado
  ...
End;

Ou seja, não importa o que tenha após o EXIT! Ele nunca será executado e a função SEMPRE retornará TRUE. Se o desenvolvedor que trabalhou muitas horas para criar este componente basear seu mecanismo de proteção em algo ingênuo deste tipo... sorry, but... it is cracked!

Isto é só um exemplo do que é possível fazer com um DCU, e de como não se deve programar um mecanismo de proteção de versão de componente trial ou shareware!

Pode-se fazer outras coisas interessantes com uma DCUs, editando-a diretamente, tipo mudar o timestamp permitindo burlar o erro "A unit XPTO foi compilada com versão diferente da unit ABCD".

Monday, December 3, 2007

Firebird em Windows XP: Primeira conexão muito lenta

Assim como 90% dos Delpheiros também utilizo o Firebird em alguns sistemas - ainda na versão 1.5. Recentemente fui rodar um sistema que estava parado há tempos, e comecei a perceber que a primeira conexão levava minutos. Muitos minutos. Parecia até que travava o Windows XP.

Bem, depois de vasculhar a internet descobri o motivo: A extensão .GDB do arquivo de banco de dados e as interferências do System Restore (Restauração do Sistema) do Windows XP. Toda vez que um arquivo .GDB é acessado/modificado o "System Restore" automaticamente faz uma cópia do mesmo. Quando o arquivo tem poucos Megabytes isto pode ser imperceptível. Mas eu estava trabalhando com um BD de 800 Mb. Como resultado, a primeira conexão - via sistema ou via IBExpert - levava cerca de 5 minutos!

http://support.borland.com/kbshow.php?q=25953

Workaround:

1) Desabilite o System Restore do Windows XP - Não recomendável por razões óbvias

2) Modifique a extensão de seus arquivos Interbase/Firebird. Eu preferi usar esta. Padronizei então meus arquivos .GDB como sendo: Extensão .IB para os Interbase, e extensão .FDB para os arquivos Firebird. E voilá!