Tuesday, January 17, 2012

Hack Intraweb and make it faster!

I'm always looking for a way to make my applications faster. During last couple of days I've been investigating an - already fast - Intraweb application.

1) Intraweb rendering implementation

Lots of Intraweb controls use an utility function to render their HTML. This function called TextToHTML is implemented as a class function of TIWBaseHTMLControl class. This function is used because if you have an IWText component with a caption containing a special character, lets say, an ampersand (&) it must be properly encoded to be shown by the browser. An ampersand declares the beginning of an entity reference (a special character) in HTML, so to be shown in the page it must be replaced by "&" (without quotes). If you have IW sources you can search for TextToHTML and see that it is used in a lot of places, most inside RenderHTML and RenderAsync methods.

2) TextToHTML implementation

Well, TextToHTML is not as fast as it could be. If you imagine that a lot of strings should be processed by TextToHTML before they get to the browser you may improve your application performance if you make that function faster, isn't it? Let's look at TextToHTML code:
class function TIWBaseHTMLControl.TextToHTML(const AText: string; 
  const AConvertEOLs: Boolean; 
  const AConvertSpaces: Boolean): string;
var
  f : integer;
  xIsCallBack: Boolean;
begin
  Result := '';
  xIsCallBack := GGetWebApplicationThreadVar.IsCallBack;
  for f := 1 to Length(AText) do begin
    case AText[f] of
      '<'  : Result := Result + '&lt;';
      '>'  : Result := Result + '&gt;';
      '"'  : Result := Result + '&quot;';
      '''' : Result := Result + '&#39;';
      '&'  : Result := Result + '&amp;';
    else

      {$ifdef UNICODE}
      if (Char(AText[f]) in [#10, #13]) then begin
      {$else}
      if (AnsiChar(AText[f]) in [#10, #13]) then begin
      {$endif}
        if AConvertEOLs then begin
          case AText[f] of
            #10 : Result := Result + '< br >';
            #13 : Result := Result + '';
          end;
        end else begin
          Result := Result + AText[f];
        end;
      end else begin
        if (AText[f] = #32) and AConvertSpaces then begin
          if xIsCallBack then begin
            Result := Result + '&nbsp;';
          end else begin
            Result := Result + ' '
          end;
        end else begin
          Result := Result + AText[f];
        end;
      end;
    end;
  end;
end;
We can see that there is a main loop interating throught all characters of the AText string, concatenating char by char to generate the result. This approach has two drawbacks: (1) Every string concatenation requires a new memory allocation and (2) it is SLOW!

3) A New TextToHTML implementation

We can implement TextToHTML using another approach, used by Delphi's own RTL in functions like HTMLEncode (unit HTTPApp.pas):
class function TIWBaseHTMLControlHack.TextToHTML(const AText: string; 
  const AConvertEOLs: Boolean; 
  const AConvertSpaces: Boolean): string;
var
  Sp, Rp: PChar;
  xIsCallBack: Boolean;
begin
  xIsCallBack := GGetWebApplicationThreadVar.IsCallBack;
  SetLength(Result, Length(AText) * 10);
  Sp := PChar(AText);
  Rp := PChar(Result);
  while Sp^ <> #0 do
  begin
    case Sp^ of
      '&':
        begin
          FormatBuf(Rp^, 5, '&amp;', 5, []);
          Inc(Rp, 4);
        end;
      '<',
        '>':
        begin
          if Sp^ = '<' then
            FormatBuf(Rp^, 4, '&lt;', 4, [])
          else
            FormatBuf(Rp^, 4, '&gt;', 4, []);
          Inc(Rp, 3);
        end;
      '"':
        begin
          FormatBuf(Rp^, 6, '&quot;', 6, []);
          Inc(Rp, 5);
        end;
      '''':
        begin
          FormatBuf(Rp^, 5, '&#39;', 5, []);
          Inc(Rp, 4);
        end;
      '\':
        begin
          FormatBuf(Rp^, 5, '&#92;', 5, []);
          Inc(Rp, 4);
        end;
      #10:
        if AConvertEOLs then
        begin
          FormatBuf(Rp^, 4, '< br >', 4, []);
          Inc(Rp, 3);
        end
        else
          Rp^ := Sp^;
      #13:
        if AConvertEOLs then
        begin
          Dec(Rp);
        end
        else
          Rp^ := Sp^;
      #32:
        if AConvertSpaces then
        begin
          if xIsCallBack then
          begin
            FormatBuf(Rp^, 10, '&amp;nbsp;', 10, []);
            Inc(Rp, 9);
          end else
          begin
            FormatBuf(Rp^, 6, '&nbsp;', 6, []);
            Inc(Rp, 5);
          end;
        end
        else
          Rp^ := Sp^;
    else
      Rp^ := Sp^
    end;
    Inc(Rp);
    Inc(Sp);
  end;
  SetLength(Result, Rp - PChar(Result));
end;
This new implemenation has two main advantages over the former: (1) There is less overhead due memory allocations, and (2) it is FASTER! :)

4) The problem

TextToHTML is a public method of TIWBaseHTMLControl but it is not virtual. Even if it were virtual, we would have to create descendant classes to override this method and it is not acceptable.

5) The solution: Hack it!

Thanks to great code (RtlVclOptimize.pas) created and made available by Andreas Hausladen we can change TIWBaseHTMLControl implementation on the fly, patching the class. I will not enter in detail about Andreas code, but the hack is done replacing, in memory, the TIWBaseHTMLControl.TextToHTML method by another method from some other class. I needed to change RtlVclOptimize.pas a little, putting the declaration of the function "CodeRedirect" in the interface section, so it could be used by my code, outside that unit.

6) Speed comparison

The use of Andy's RtlVclOptimize unit alone can significantly improve IW applications performance. Moreover, in my tests, my TextToHTML implemenation is more than 4 times faster than the original. Original code: 14.7 seconds versus 3.4 seconds using my code (1,000,000 function calls). Another advantage is less memory allocations for strings (less memory fragmentation and less LOCK+multicore related issues - read more about it here).

7) More Hacks!

Well, not satisfied hacking TextToHTML code, I did the same thing with another TIWBaseHTMLControl method, TextToJSStringLiteral . Finally I've packed all these little things inside a unit. To speed up your IWApplication a bit just declare RtlVclOptimize (from Andreas) and my unit, IntrawebPatch.pas inside your .DPR file and you are done!

8) Finally, the downloads!

1) You can download modified Andreas Hausladen's RtlVclOptimize.pas here.
2) You can download my patch unit (IntrawebPatch.pas) here (requires modified RtlVclOptimize.pas).

Friday, January 13, 2012

Delphi 5 Update packs available again!

Sorry folks, my links to Delphi 5 Update packs were broken. Now I've fixed them :-) You may find my original post with download links here.

Tuesday, January 10, 2012

BDE and Windows 7, 32 and 64 bits

I know, I know... BDE is discontinued and stuff. But many people still need BDE running in new Windows 7 32 and 64 machines. Here is a working Setup for BDE 5.2.0.2 (the latest version released by Borland) plus all SQL Links.

Notes:
  • Installs BDE 5.2.0.2 + all SQL Links
  • During setup there is an option to remove previous BDE registry entries.If you have an old corrupted BDE installation, maybe this option can fix it (It will remove EVERYTHING below HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Database Engine registry entry, and you have been warned!).
  • This setup was tested and works perfectly under Windows 2000, 2003 Server, XP, 2008 Server, Vista and Seven, 32 and 64 bits.
  • The setup correctly prompts for UAC elevation. By the way, it was created using Jordan Russell's excellent installer creator Inno Setup (also created with Delphi)!

Download BDE 5.2.0.2 Setup in English:
http://www.alex7691.com/download/Setup_BDE52.zip

Download BDE 5.2.0.2 Setup in brazilian portuguese:
http://www.alex7691.com/download/Setup_BDE52_PTB.zip

Wednesday, May 25, 2011

Erro: Expected return value not received usando Intraweb + SocketServer

Tenho um projeto Delphi Intraweb que faz chamadas a servidores de aplicação remotos, via Socket (TSocketServer).

Um dia destes, sem mais nem menos, obtive um erro estranho "Expected return value not received" ao tentar chamar um método do servidor de aplicação, através do Socket. Usei o amigo Google mas ele não me ajudou muito. Não tive tempo ainda de debugar o fonte do SocketServer para verificar o porquê do erro, mas a solução foi relativamente simples: Basta fazer:
SocketServer.SupportCallbacks := False;

Resolvido o problema

Saturday, May 21, 2011

Substitua a procura por arquivos no Windows 7

Que a busca por arquivos no Windows "sucks big time" todo mundo sabe. Nunca foi boa e conseguiu piorar no Windows 7. Aquela barra de progresso que não significa absolutamente nada é o fim! E por fim: Quando pesquiso uma pasta por um arquivo e este não é encontrado nunca sei se fiz algo que interrompeu a busca antes do fim... se o arquivo está lá e o Windows não encontrou...
Bem, quem não se sente bem com a busca padrão do Windows, pode encontrar vários substitutos. Aconselho dois que tenho instalados:

FileSeek
e
UltraFileSearch

Ambos são gratuitos, livres de spyware e cumprem o que prometem: Fazer uma busca NÃO INDEXADA por arquivos em seu HD com várias opções para refinar a pesquisa.
O FileSeek é um pouco mais simples que o UltraFileSearch, porém é um pouco mais rápido também. Ambos são infinitamente mais rápidos e eficientes em buscar um arquivo do que a busca padrão do Windows 7, Vista e XP.

Ambos integram-se bem ao Windows Explorer. Clicando com o botão direito do mouse sobre uma pasta qualquer no Windows Explorer ele habilita um item do menu de contexto com o atalho para a busca.

Resumindo: Vale a pena um teste. Vai ver que a busca por arquivos pode ser bem mais simples e menos traumática do que o Windows te oferece.

Abraço!

Saturday, January 29, 2011

G-Buster Browser Defense. Vamos brincar de gato & rato?

Bem, desculpem-me mas vou começar este post com a frase: "Estou de saco cheio deste programinha da GAS Treconologia." Não que eu sofra qualquer consequência do mesmo, pois uso uma máquina virtual no VirtualBox somente para acessar internet banking. Não existe nada instalado nela, e eu só uso ela para este fim. Ou seja, se eu deixar o G-Boster lá rodando, não faz a menor diferença para mim. O que me deixa realmente bravo com o pessoal que coloca esta praga no seu PC sem você saber é que eles são muito abusados! Então vou dar mais trabalho para eles!
Quem quiser ler os meus post anteriores pode vê-los aqui e aqui.

Para leitores que porventura sejam simpatizantes do G-Buster, novamente volto a frisar: Em qualquer país do mundo isto seria ilegal. Instalar um programa no seu computador sob a forma de um "inofensivo ActiveX destinado a protegê-los dos hackers", que depois não pode ser removido por quase ninguém é ilegal. Se não é ilegal aqui no Brasil (ainda), é imoral com certeza.

A instalação começa com uma MENTIRA DESLAVADA: Não se trata de um simples ActiveX. Além do ActiveX, são instalados também um serviço e um driver, o que é um abuso total! Um driver para quê??? Driver é um software destinado a fazer a interface com um hardware específico. Não existe a menor necessidade deste driver, porém os abusados colocam ele lá para tornar mais difícil sua remoção.
Além disso, um dos grandes motivos de nossos bancos não aceitarem outro browser como o Firefox, Opera ou Chrome é exatamente este! Estes três aí, muito superiores ao Internet Explorer na minha opinião (e na opinião de 98% das pessoas que realmente compararam os 4), não rodam ActiveX, ou seja, o G-Buster não é compatível com estes browsers. Aí continuamos nesta lenga-lenga de muitos Internet bankings aqui no Brasil só aceitarem IE.

É mais um caso do famoso "os fins justificam os meios", tão usado por estas bandas. Já vi administradores de rede passando sufoco com este G-Buster, sem conseguir removê-lo depois de semanas de trabalho! Imagine a situação do cidadão? Explicar para o seu superior que não conseguiu remover o programa da sua rede??? Isto não é ficção, eu vi acontecer!

Pois bem. Já escrevi dois posts ensinando como remover a praga. Aí o pessoal da GAS Treconologia fica ciente e muda o G-Buster para evitar mais uma forma de remoção. O pior: A cada nova versão do G-Buster, mais e mais caminhos de registry sofrem leituras constantes. Traduzindo: Cada versão do GASTreco Buster Defense consome mais recursos da sua pobre máquina que já está gastando quase 100% de CPU quando você está usando simultaneamente o orkut, facebook, twitter, sites adultos, poker online, msn e etc é ou não é? :-)

Antes de começar: Sou um analsita de sistemas e desenvolvedor PROFISSIONAL, não sou hacker e não estou aqui para cometer ou ajudar que cometam crimes cibernéticos. Faço isto porquê, por princípio, definitivamente não concordo com a forma como a coisa é feita: debaixo dos panos, na surdina, obrigatoriamente... Resumindo: de forma suja.

Para desabilitar o G-Buster:

1) ATENÇÃO: Este programa de remoção na sua versão incial só funciona no Windows 2000, Windows XP e Windows 2003 Server. Para o Vista, Windows 7 e Windows 2008 por favor, aguarde. Estou concluindo ainda!

2) Baixe este pacote zip neste link. Este pacote contém dois arquivos: RemoveGBuster.exe e RemoveGBuster.dll. Quem estiver na dúvida se os arquivos são seguros, devem submetê-lo ao www.virustotal.com (a forma mais confiável de saber se um arquivo é ou não seguro para utilização, ou seja, se está livre de vírus).

2) Unzipe o pacote num diretório qualquer da sua máquina, sugiro C:\RemoveGBuster\.

3) Execute o programa RemoveGBuster.exe. Quando a janela se abrir, clique no botão "Desabilitar G-Buster". Depois disso, se tudo der certo, basta dar um boot e estará livre da praga. Após o boot vá ao diretório C:\Arquivos de programas\GbPlugin e veja por você mesmo! ;-)

O melhor de tudo é: Você poderá usar o Internet banking depois (O G-Buster será instalado novamente), e após novo boot estará livre dele de novo!

Eu não vou entrar em detalhes sobre o que o RemoveGBuster faz. Vou deixar o pessoal da GAS-Treko trabalhar um pouquinho, mas digo uma coisa: O programa não exclui NENHUM arquivo do seu computador, não muda permissões em arquivos/registry e não muda as configurações gerais da máquina.

Como eu disse acima, este programa por enquanto só funciona para Win2000, XP e 2003. Para Vista, Seven e 2008 estou ainda estou concluindo!

Bem.... apesar do trabalhinho, não posso dizer que não foi divertido! Programar é meu trabalho mas também meu hobby e remover uma coisa chata como estas é muito gratificante! ;-)
Não vou dizer que é definitivo, pois o pessoal da GAS irá criar outra forma do G-Treco se esquivar e voltar numa próxima versão. Mas quanto mais "íntimo" fico do G-Boster, mais fácil fica na próxima vez... E já tenho mais alguns esquemas que complicarão bastante a vida da distinta praga! heheheh

Mas o melhor de tudo é que gastei pouco mais de 4 horas para encontrar a receita. Algum tempo programando e mais algum escrevendo este post...
Quanto tempo será que gastarão para driblar este esquema aqui? :-)

Até+

Atualização (18/05/2011): Pessoal, sei que alguns usuários não conseguiram remover o G-Buster usando o meu programinha. Estou fazendo mestrado e, ainda com meu trabalho, resta-me pouquíssimo tempo para "brincar" com isto atualmente. De qualquer forma, sempre que alguém não conseguir remover usando meu programa por favor deixe no recado as seguintes informações: Qual o banco que está acessando e se possível qual a versão do G-Buster (arquivo que geralmente está em C:\Arquivos de programas\GbPlugin\gbpsv.exe). Assim poderei analisar caso a caso.

Abraço!

Friday, January 28, 2011

jQuery UI Dialog and tabindex attributes

Visual controls, like inputs, selects, etc., inside a jQuery UI modal dialog cannot have a tabindex attribute. If you do so, you won't be able to navigate using the TAB key. Simple isn't it?

Want to see for yourself?

1) First, go to the jQuery UI Dialog demo page, using Firefox + Firebug: http://jqueryui.com/demos/dialog/#modal-form

2) Click the button "Create new user"

3) Use Firebug to inspect the 3 inputs with id's: "name", "email" and "password". There is no tabindex attribute. Now, use Firebug and add the tabindex attribute with any value to these inputs. Any number like 1, 2, 3 should be ok.

4) Voilá! Once you are done you cannot navigate through the controls using TAB key anymore.

Workaround? Never put tabindex attributes in your HTML elements inside a jQuery UI modal dialog!