Saturday, December 29, 2007
Instanciação dinâmica - VCL e VCL.NET
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.
Thursday, December 27, 2007
Wednesday, December 26, 2007
Quanto mais eu uso o C#...
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
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
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
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
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
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
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á!