Boletim Pascal #40
Os exemplos do código fonte desta edição estão disponíveis para download.
![]() |
![]() |
Boletim Pascal #40 - 07 DE MARÇO DE 2003 (Original: 27-OCT-2002) Índice 1. Algumas palavras dos editores 2. Windows hooks (ou "Como todos esses programas espiões funcionam?") 3. Codificação e decodificação base64 (MIME) 4. Examplo de um serviço do Windows com uma thread 5. Capturando a saída de um aplicativo console 6. Assembler nativo em Delphi (IV) - Records 7. Fóruns / Listas 8. Delphi na Rede - Sites em português - Componentes, bibliotecas e utilidades . Freeware - Artigos, dicas e truques - Tutoriais - Outros links ________________________________________________________________________ 1. Algumas palavras dos editores Editorial da versão em português -------------------------------- Agora sim, podemos ver alguns avanços no andamento do nosso Boletim Pascal! Graças (não me canso de repetir) ao trabalho voluntário dos nossos tradutores, estamos conseguindo, pouco a pouco, atualizar as traduções em português do Boletim. Até que consigamos ficar em dia com o Boletim, vou procurar manter meus comentários ao mínimo. Prefiro que vocês recuperem o tempo perdido lendo as excelentes matérias dessa edição! :) Agora chega de conversa. Divirtam-se com nosso boletim! Demian Lessa demian@knowhow-online.com.br Editor da versão em português __________________ Editorial da versão em inglês ----------------------------- Eu pretendia ter esse número publicado algum tempo atrás como prometido mas devido a circunstâncias bastante particulares um novo vírus ainda não identificado infectou meu PC e destruiu o primeiro cilindro de meu disco rígido (o MBR, o setor de boot e parte da FAT da primeira partição) então, vá lá saber... (ainda estou instalando os componentes Delphi que utilizava). Peço desculpas pelo atraso. Gostaria de agradecer aos autores que contribuíram com artigos para esse número e fico feliz em entregar-lhes os seguintes prêmios: * Florin Sabau ("Windows Hooks") · llPDFLib v1.1 - por llionsoft, Shareware ($70, $280 com fontes) llPDFLib é uma biblioteca Object Pascal para a criação de documentos PDF. Não utiliza DLLs ou software externo de terceiros para gerar os PDFs. A biblioteca consiste de um componente TPDFDocument com métodos e propriedades análogos ao TPrinter do Delphi, mas orientados à geração do arquivo PDF. http://www.llion.net/ * Jochen Fromm ("Capturando a Saída de um Aplicativo Console") . Greatis Form Designer v3.4 - por Greatis Software, Shareware ($49.95) Esse é um editor de formulários de tempo de execução que permite ao usuário movimentar e redimensionar qualquer controle do formulário. Não é preciso preparar o formulário para utilizar o Form Designer. Apenas coloque um componente TFormDesigner num formulário, defina a propriedade Active para True e pronto! Para Delphi 4-7 e BCB 3-6. http://www.greatis.com/formdes.htm * Kim Sandell ("Example of a Windows Service, with a thread") · Developer Information Library (DIL) CD - by UK Borland User Group Mais de 17,000 dicas, truques, FAQs e artigos técnicos · Atualizações e patches para ferramentas da Borland · Mais de 4000 components e Ferramentas · Mais de 4000 bitmaps prontos para uso e outros 20000 em formato compactado · Mais de 350 scripts javascript prontos para uso · Conjunto completo de How-Tos para Linux · e muito, muito mais... http://www.richplum.co.uk/html/dil.asp Para o próximo número, teremos o seguinte prêmio para um de nossos colaboradores: * TSDBGridFooter v2.0 por Jovan Sedlan, Shareware ($74.50) Esse componente é uma ferramenta poderosa que permite cálculos automáticos no seu DBGrid e exibe essa informação num rodapé configurável abaixo do grid. É projetado para funcionar com o TSDBGrid (também incluído) apesar de poder ser utilizado em qualquer descendente de TCustomDBGrid. http://www.sedlan.com/dbgrid_footer.php Mudando de assunto, a primeira atualização do Delphi 7 foi liberada: * Delphi 7 Update Pack 1 http://community.borland.com/article/0,1410,29209,00.html Espero que goste dessa edição. Cordialmente, Ernesto De Spirito boletim-pascal-owner@yahoogrupos.com.br __________________ Colaboraram nesta edição: Dave Murray e Charl Linssen ________________________________________________________________________ JfControls Library. Multi-language. Multi-appearance. Skins. Privileges. More than 40 integrated and customizable components. Impressive GUI. Centralized resources administration. Multiple programming problems solved. For Delphi 3-6 and C++ Builder 3-5. http://www.jfactivesoft.com/ ________________________________________________________________________ 2. Windows hooks (ou "Como todos esses programas espiões funcionam?") Por Florin Sabau <aaa111@go.ro> Tradução: Adilson Vahldick Você já parou para pensar como os programas espiões conseguem capturar tudo que é digitado nos computadores onde estão instalados? Bem, eu não sei exatamente como ELES fazem isto :), mas mostrarei uma possível técnica usando Windows Hooks. Este artigo irá mostrar como criar programas que "escutam" por uma certa combinação de teclas e, quando ativada, executar alguma tarefa (como ejetar a unidade de CDROM). Tecnicamente, um hook é somente uma outra subrotina ("hook procedure") que "fica no caminho" do mecanismo normal de tratamento de mensagens do Windows. A hook procedure pode ser instalada no sistema e assim ela captura certas mensagens do Windows ANTES delas serem enviadas para as devidas rotinas de tratamento. Windows contém vários tipos diferentes de hooks; cada tipo fornece acesso a um aspecto diferente do mecanismo de tratamento de mensagens do Windows. As constantes que identificam os tipos de hooks estão em Windows.pas. Abaixo temos alguns destes tipos, com uma breve descrição: WH_KEYBOARD: instala uma hook procedure que monitora as mensagens do teclado. Utilizaremos este em nosso programa. WH_MOUSE: instala uma hook procedure que monitora mensagens do mouse. WH_CBT: instala uma hook procedure que recebe notificações úteis para uma aplicação de treinamento baseada em computador (CBT). WH_JOURNALRECORD: instala uma hook procedure que grava as mensagens de entrada postadas para a fila de mensagens de sistema. Este hook é útil para a gravação de macros. WH_JOURNALPLAYBACK: instala uma hook procedure que envia mensagens previamente gravadas pela WH_JOURNALRECORD hook procedure. Devido a muitos programas poderem instalar um hook no sistema ao mesmo tempo, o Windows mantém internamente um "hook chain", que é apenas uma lista de ponteiros para as hook procedures que os programas instalaram. Quando uma mensagem acontece no sistema, o Windows primeiro passa por cada uma das procedures no hook chain, uma depois da outra. Então, caso a mensagem não tenha sido bloqueada por qualquer uma das hook procedures, o Windows encaminha a mensagem para a janela adequada. Mais um assunto antes de ir para a próxima seção: hooks podem ser classificados de uma outra forma. Existem os hooks de sistema (globais) que recebem mensagens de todos os threads do sistema, e os hooks específicos de thread (locais), que recebem mensagens apenas de um determinado thread. Devido a uma hook procedure global poder ser chamada no contexto de qualquer aplicação (que capturam mensagens de todas as aplicações), elas devem estar localizadas em uma DLL (Dynamic Link Library). Esta restrição não se aplica aos hooks específicos de threads, onde a hook procedure pode estar em qualquer parte da aplicaçao que controla o thread a ser interceptado. Neste artigo nós trataremos somente com hooks globais. Instalando uma WH_KEYBOARD hook procedure no hook chain ------------------------------------------------------- A API de hooks contém 3 importantíssimas funções: SetWindowsHookEx (que instala uma hook procedure), UnhookWindowsHookEx (que desinstala a hook procedure) e CallNextHookEx (que chama a próxima hook procedure no hook chain). Os parâmetros destas funções são os seguintes (em windows.pas): function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; stdcall; "idHook": tipo do hook a ser instalado (p.e. WH_KEYBOARD); "lpfn" : aponta para a hook procedure para onde a mensagem será enviada; "hmod" : handle da DLL que instala a hook, normalmente hInstance (para hooks globais) ou 0 para hooks locais; "HINST" : identificador do thread ao qual o hook estará associado. Se 0 a hook procedure é associada com todos os threads. Retorna um valor usado para identificar o hook. function UnhookWindowsHookEx(hhk: HHOOK): BOOL; stdcall; "hhk": identifica o hook a ser desinstalado. Retorna True se ocorreu com sucesso ou False se falhou. function CallNextHookEx(hhk: HHOOK; nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; "hhk": identificador do hook corrente; "nCode", "wParam", "lParam": parâmetros a serem enviados para a próxima hook procedure no hook chain. Retorna o valor retorna pela próxima hook procedure do chain. Nós veremos adiante o que isto significa. A hook procedure ---------------- A hook procedure para o teclado tem o seguinte formato: function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; "nCode": = HC_ACTION - os parâmetros wParam e lParam contém informações sobre as mensagens das teclas pressionadas = HC_NOREMOVE - os parâmetros wParam e lParam contêm informações sobre as mensagens das teclas pressionadas mas a mensagem não foi removida da fila de mensagens (uma aplicação chamou a função PeekMessage especificando o flag PM_NOREMOVE). "wParam": especifica o código virtual da tecla que gerou a mensagem (p.e. VK_F9 para a tecla de função F9) "lParam": especifica informações adicionais (do tipo contador de repetição, scan code ...); não utilizado em nosso programa; veja Win32SDK para maiores detalhes; HookProc deve retornar um valor não zerado para evitar que o Windows passe a mensagem para os demais hooks da cadeia, ou para a window procedure alvo, ou zero para deixar que o Windows passe a mensagem para a window procedure alvo. Exemplo ------- Este exemplo cria uma hook global para o teclado e quando uma certa combinação de teclas ocorrem ele executará alguma coisa (veja abaixo): WinKey + F9: mostra a janela principal se ela estiver oculta; WinKey + F10: ejeta a unidade de CDROM; WinKey + F12: encerra a aplicação. A comunicação entre a DLL que implementa a hook e a aplicação é feita com a função SendMessage da API, que envia uma HOOK_MSG (definida em constants.inc) para a aplicação principal, com os comandos (SHOW, EJECT, QUIT) em wParam (veja abaixo). Como não querermos ser incomodados com a janela sendo mostrada a todo instante, quando clica-se o botão de minimizar a janela é ocultada (até da lista de tarefas), mas poderá ser visualizada com a combinação WinKey+F9. Para o código fonte completo veja o arquivo anexo. Eu somente mostrarei aqui as partes mais importantes da aplicação: 1. HookDll.dpr function KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; var Handled: Boolean; KeyState: TKeyboardState; Han: HWND; function WinKeyPressed: boolean; begin Result := (KeyState[VK_LWIN] and $80 <> 0) or (KeyState[VK_RWIN] and $80 <> 0); end; begin Handled := False; Result := 1; if nCode = HC_ACTION then begin GetKeyboardState(KeyState); Han:=FindWindow('TForm1',APP_CAPTION); if (IsWindow(Han)) and (KeyState[wParam] and $80 <> 0) and WinKeyPressed then begin Handled := True; case wParam of VK_F9: SendMessage(Han, HOOK_MSG, APP_SHOW, 0); VK_F10: SendMessage(Han, HOOK_MSG, EJECT_CDROM, 0); VK_F12: SendMessage(Han, HOOK_MSG, APP_QUIT, 0); else Handled := False; end; end; end; if not Handled then Result := CallNextHookEx(hhk, nCode, wParam, lParam); end; Na entrada da hook procedure nós testamos se o flag HC_ACTION em nCode (assim sabemos que tivemos o pressionamento de uma tecla), então salvamos em KeyState o estado (se foi pressionado ou não, CAPSLOCK ligado, etc.) de todas as teclas virtuais utilizando GetKeyboardState. Nós também procuramos pelo handle da janela principal da aplicação para a qual serão enviados os comandos (APP_SHOW, EJECT_CDROM, APP_QUIT). Se a janela for encontrada (IsWindow(han)) e tiver um mensagem de tecla pressionada, então enviamos um comando para a janela principal de acordo com o pressionamento. Se não foi possível tratar a mensagem (Handled=false) então os parâmetros são passados para a próxima hook na cadeia. 2. Hooks.Dpr & Unit1.pas A principal função aqui é HOOK_MSG_PROC que recebe os comandos enviados da DLL: type TForm1=class(TForm) ... procedure HOOK_MSG_PROC(var Msg: TMessage); message HOOK_MSG; ... end; ... procedure TForm1.HOOK_MSG_PROC(var Msg: TMessage); begin case Msg.WParam of APP_SHOW: begin Application.ShowMainForm := True; Visible:=True; end; EJECT_CDROM: mciSendString('set CDAudio door open', nil, 0, 0); APP_QUIT: Close; end; end; O recurso de "Ocultar na minimização" é executado pela especialização do tratamento da mensagem WM_SYSCOMMAND que é enviada pelo Windows quando um comando de sistema (close, minimize, maximize) ocorre. Então a janela será oculta se receber um comando SC_MINIMIZE: procedure TForm1.OnMinimize(var Msg: TMessage); begin if Msg.WParam = SC_MINIMIZE then begin if not IsHookInstalled then begin ShowMessage('Install the hook first or you''ll'#13#10 + 'not be able to access the program'); Exit; end; Application.ShowMainForm := False; Visible := False; end else Inherited; end; Para esconder o programa da lista de tarefas utilizamos: function RegisterServiceProcess(dwProcessID, dwType: integer): integer; stdcall; external 'KERNEL32.DLL'; NOTA: A técnica de ocultamento acima funciona somente no Windows 9x. É claro que isto é exemplo bem simples de aplicação de hooks, mas as possibilidades são enormes. Um pequeno programa espião, talvez! :) De qualquer maneira, se você tiver alguma dúvida sobre este artigo, sinta-se a vontade de me mandar uma mensagem para <aaa111@go.ro>. __________________ OBSERVAÇÃO: O código completo está anexo. ________________________________________________________________________ Qual foi a última vez que você votou para o Boletim Pascal? Suporte essa iniciativa votando para o Boletim Pascal no The Programming Top 100! http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium ________________________________________________________________________ 3. Codificação e decodificação base64 (MIME) Por Daniel Wischnewski delphi3000@wischnewski.tv Tradução: Demian Lessa Eu escrevi a unidade a seguir para substituir os componentes INDY TIdEncoderMIME e TIdDecoderMIME. Esse "Codec" é utilizado em software de email, basicamente. A primeira razão foi a lentidão dos componentes INDY. A segunda foi que por serem componentes, precisam da VCL, uma carga extra para sistemas não baseados na VCL. Ambas as rotinas são escritas em Assembler e sobrecarregadas em versões distintas para facilitar o acesso. Estou certo de que alguns de vocês serão capazes de aumentar ainda mais a velocidade do código. Por favor, me avisem. Grato. __________________ NOTA: O código fonte completo está em anexo. ________________________________________________________________________ AnyShape Transpack v2.0 - by MindBlast Software (DELPHI + KYLIX) Tired of boring, rectangular windows? AnyShape Transpack is a cross- platform component that eases the creation of transparent, weirdly shaped windows with WYSIWYG editing, design-time preview, automatic dragging, REAL stay-on-top forms, and the ability to combine regions. http://www.mindblastsoftware.com/?page=transpack&ref=PascalNL ________________________________________________________________________ 4. Examplo de um serviço do Windows com uma thread Por Kim Sandell - kim.sandell@nsftele.com www.nsftele.com Tradução: Demian Lessa Delphi 5 e 6 têm um modelo de projeto para serviços, mas é incompleto. Esse exemplo estende esse modelo e o completa. Também mostra como iniciar uma thread que emite um sinal (beep) a cada dois segundos. Você pode usar esse código como modelo quando for desenvolver servidores como serviços. O exemplo foi desenvolvido para Delphi 5-7 e funciona nos Windows NT/2000/XP. A opção de inicialização do serviço está definida como MANUAL. Se você quiser que o serviço inicie automaticamente com o Windows, então terá que mudar isso. MAS CUIDADO! Se sua aplicação travar enquanto roda como um serviço não há como finalizá-la. __________________ NOTA: O código fonte completo está em anexo. __________________ Perfil sumário do autor: Nome : Kim Sandell Nascido : 1973 Helsinki, Finland Habilidades : VB, Delphi (1-6), C/C++, SQL, Interbase, IP Networks. Áreas especiais: System Service, Protocols, Security (VPN), Authentication, Servers (Deamons) + More Empresa: NSF Telecom Ab Título: CTO WWW : http://www.nsftele.com EMail : kim.sandell@nsftele.com ________________________________________________________________________ 5. Capturando a saída de um aplicativo console Por Jochen Fromm <Jochen.Fromm@manserv.de> Tradução: Demian Lessa Como você inicia um aplicativo DOS ou de console e captura a saída enquanto o programa está rodando? Por exemplo, como você captura a saída do comando FileCompare (FC)? Já existem dois artigos tratando desse assunto, http://www.delphi3000.com/articles/article_2112.asp http://www.delphi3000.com/articles/article_2298.asp mas eles só capturam a saída quando o processo finaliza. Existem ainda dois artigos na Microsoft sobre o redirecionamento das saídas de aplicativos DOS: * Microsoft Knowledge Base Article - Q190351 HOWTO: Spawn Console Processes with Redirected Standard Handles http://support.microsoft.com/default.aspx?scid=kb;en-us;Q190351 * Microsoft Knowledge Base Article - Q150956 INFO: Redirection Issues on Windows 95 MS-DOS Applications http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q150956 A idéia básica é iniciar o aplicativo de console com CreateProcess e redirecionar os canais (pipes) de entrada e saída, permitindo ao processo que disparou o CreateProcess (processo pai) o acesso à saída do aplicativo de console. É importante capturar a saída enquanto o processo ainda está em execução. Se o canal de saída for bloqueado por overflow, novos dados não podem ser escritos do aplicativo de console para o canal de saída e o programa pára. Nesse caso, temos o clássico caso de deadlock: o usuário (processo pai) espera que o processo filho termine e o processo filho espera a liberação do buffer de saída pelo processo pai. __________________ NOTA: Código fonte e aplicativo de exemplo estão em anexo. ________________________________________________________________________ 6. Assembler nativo em Delphi (IV) - Records Por Ernesto De Spirito <eds2004 @ latiumsoftware.com> Tradução: Cristiano de Favari Passando records como parâmetros ================================ Como arrays estáticos, records são internamente passados como ponteiros para os dados, independentemente se o parâmetro é passado por valor ou por referência (também como "var" ou como "const"). Dada as seguintes declarações... type TRecord = record Id: integer; Name: string; end; var a, b: TRecord; procedure InitRecord(var r: TRecord; Id: integer; const Name: string); begin r.Id := Id; r.Name := Name; end; ...uma chamada para a procedure InitRecord em assembler seria assim: // Em Object Pascal: // InitRecord(a, n, s); // Em Inline Assembler: asm lea eax, a // EAX := @a; // 1st parameter in EAX mov edx, n // EDX := n; // 2nd parameter in EDX mov ecx, s // ECX := s; // 3rd parameter in ECX call InitRecord // InitRecord; end; Acessando os campos de um record ================================ Campos de records estão localizados em um certo offset de um endereço do record (o endereço do primeiro campo). No exemplo, assumindo que nós temos o endereço do record do tipo TRecord no registrador EAX, o campo Id está localizado em [EAX+0] (ou simplesmente [EAX]), e o campo Name está localizado em [EAX+4], mas normalmente nós não escrevemos código usando números explicitamente. Ao invés disto, para produzir código auto-explicável e de fácil manutenção, nós temos cinco alternativas: mov edx, [eax + TRecord.Name] mov edx, (TRecord PTR [eax]).Name mov edx, (TRecord [eax]).Name mov edx, TRecord[eax].Name mov edx, [eax].TRecord.Name As cinco sentenças anteriores seriam montadas como: mov edx, [eax + 4] No lugar de um registrador (como EAX), as sintaxes também se aplicam para nome de variáveis locais ou globais. Você pode deduzir da primeira sintaxe que em inline assembler a expressão RecordType.Field é avaliada em tempo de compilação como uma constante representando o offset no qual o campo está localizado no RecorType. Por exemplo, a seguinte sentença é válida: mov ecx, TRecord.Name // mov ecx, 4 Voltando ao assunto, a procedure InitRecord (apresentada acima) pode ser implementada em assembler desta forma: procedure InitRecord(var r: TRecord; Id: integer; const Name: string); asm // EAX = @r; EDX = Id; ECX = @Name[1] mov (TRecord PTR [eax]).Id, edx // EAX^.Id := EDX; // Id // _LStrAsg(@EAX^.Name, @Name) --> EAX^.Name := Name lea eax, (TRecord PTR [eax]).Name // EAX := @(EAX^.Name); mov edx, ecx // EDX := @Name[1]; call System.@LStrAsg // _LStrAsg(EAX, EDX) end; Na entrada da procedure, nós temos EAX apontando para o registro (primeiro parâmetro), EDX contendo o Id (segundo parâmetro), EDX apontando para o dado da string Name (terceiro parâmetro). Atribuir um inteiro é bem simples, mas atribuir uma string é um pouco mais complicado. Se a string destino não é uma string vazia então begin Decremente a contagem de referência da string destino; Se a contagem de referência da string destino chegou a zero então Libere a string destino; end; Se a String Origem não for uma string vazia então Incremente a contagem de referência da String origem; Designe origem para o destino; A procedure _LStrAsg (da Unit System) implementa esta lógica para nós. A procedure recebe dois parâmetros: o primeiro (em EAX) é a string destino passada por referência e o segundo (em EDX) é a string origem passada por valor (o que é passado na verdade é o ponteiro, visto que strings são ponteiros para os caracteres de fato). Então, no nosso caso, EAX deveria ser o endereço de uma variável string que será atribuída (isto é @r.Name), enquanto EDX deveria ser o valor a ser atribuído: EAX --> r.Name --> r.Name[1] ==> EAX = @r.Name EDX --> Name[1] ==> EDX = @Name[1] Ref.: "-->" significa "aponta para" Então, preparamos EAX e EDX e então chamamos _LStrAsg: lea eax, (TRecord PTR [eax]).Name // EAX := @(EAX^.Name); mov edx, ecx // EDX := @Name[1]; call System.@LStrAsg // _LStrAsg(EAX, EDX) Funções de baixo nível para trabalhar com records ================================================= Como arrays estáticos, se o record é passado por valor, é responsabilidade da função chamada preservar o record. Quando uma função precisa trocar o valor de um ou mais campos do record passado por valor, normalmente ela cria uma cópia local e trabalha com a cópia. O compilador cria uma cópia para nós no "begin" das funções Pascal, mas nas funções puramente assembler temos que fazê-lo nós mesmos. Um jeito de fazer isto é como mostrado na parte III com arrays estáticos. Aqui está outro modo: procedure OperateOnRecordPassedByValue(r: TRecord); var _r: TRecord; asm // Copia os elementos de "r" (parâmetros) em "_r" (cópia local) // Move(r, _r, sizeof(TRecord)); lea edx, _r // EDX := @_r; mov ecx, type TRecord // ECX := sizeof(TRecord); call Move // Move(EAX^, EDX^, ECX); lea eax, _r // EAX := @_r; mov edx, TRecord_TypeInfo // EDX := TRecord_TypeInfo; call System.@AddRefRecord // System._AddRefRecord(EAX,EDX); lea eax, _r // EAX := @_r; // optional // Aqui vai o resto da função. Nós trabalharemos no // record "_r" (a cópia local), agora apontada por EAX. end; Desta vez nós chamamos a procedure Move ao invés de copiarmos os dados com REP MOVSB. Deste modo, nós escrevemos menos código. IMPORTANTE: Copiar os valores da memória apenas funciona com records que não contém campos do tipo reference-counted tais como strings, arrays dinâmicos ou variantes do tipo string ou arrays dinâmicos. Se nós tivermos um ou mais campos string, ou campos de algum outro tipo reference-counted, depois de copiar os valores de memória, nós temos que incrementar seus respectivos contadores de referência. A procedure _AddRefRecord (da Unit System) realiza isto. Ela possui dois parâmetros: um ponteiro para o record (em EAX) e um ponteiro para informação do tipo de dado para o record gerado pelo compilador (em EDX). A informação de tipo para o record é basicamente uma estrutura de dados que contém as posições e tipos de campos reference-counted do registro. As procedures que trabalham com records declaradas na Unit System, (_InitializeRecord, _AddRefRecord, _CopyRecord, e _FinalizeRecord) requerem um ponteiro para a informação do tipo de dado como seu último parâmetro. Mas, onde estão os dados? Bem, infelizmente, não há um símbolo para acessar sua localização diretamente. Nós temos que conseguir seu endereço através de uma chamada para a função TypeInfo, mas não há uma função que nós possamos chamar através do código assembler porque não é uma função verdadeira, e sim uma função interna que o compilador resolve em tempo de compilação. Um possível contorno é inicializar uma variável global, chamando a função TypeInfo de nosso código Pascal: var TRecord_TypeInfo: pointer; : initialization TRecord_TypeInfo := TypeInfo(TRecord); E então podemos usá-la como: procedure OperateOnRecordPassedByValue(r: TRecord); var _r: TRecord; asm // Copiar os elementos de "r" (parâmetro) para "_r" (cópia local) // Move(_r, r, sizeof(TRecord)); lea edx, _r // EDX := @_r; mov ecx, TYPE TRecord // ECX := sizeof(TRecord); call Move // Move(EAX^, EDX^, ECX); // System._AddRefRecord(@_r, TypeInfo(TRecord)); lea eax, _r // EAX := @_r; mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord); call System.@AddRefRecord // System._AddRefRecord(EAX, EDX); lea eax, _r // EAX := @_r; // opcional // Aqui vai o resto da função. Nós trabalharemos no // record "_r" (a cópia local), agora apontada em EAX. // Nós temos que finalizar a cópia local antes de retornarmos // System._FinalizeRecord(@_r, TypeInfo(TRecord)); lea eax, _r // EAX := @_r; mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord); call System.@FinalizeRecord // System._FinalizeRecord(EAX, EDX); end; Note que antes da função retornar, nós temos que fazer a chamada a _FinalizeRecord para destruir o record local (por exemplo, isto decrementará a contagem de referência de strings apontadas por campos string). Chamar Move e então _AddRefRecord é um jeito válido de copiar records se e apenas se o record de destino tenha sido inicializado (depois de chamar _AddRefRecord, o record é inicializado). Se o record de destino já estiver inicializado, então nós temos que chamar _CopyRecord ao invés disto. Por Exemplo: procedure proc(const r: TRecord); var _r: TRecord; begin // _r := r; asm mov edx, eax // EDX := @r; lea eax, _r // EAX := @_r; mov ecx, TRecord_TypeInfo // ECX := TypeInfo(TRecord); call System.@CopyRecord // System._CopyRecord(EAX, EDX, ECX); end; end; Note que como isto é uma função Pascal normal (não uma função Assembler completa), o compilador automaticamente gera código para inicializar e finalizar a variável record local (no "begin" e "end" da procedure respectivamente). A combinação Move mais _AddRefRecord é idêntica em efeito a _InitializeRecord mais _CopyRecord: procedure OperateOnRecordPassedByValue(r: TRecord); var _r: TRecord; asm // Copiar os elementos de "r" (parâmetro) para "_r" (cópia local) // Move(_r, r, sizeof(TRecord)); // System._InitializeRecord(@_r, TypeInfo(TRecord)); push eax // Push(EAX); // @r lea eax, _r // EAX := @_r; mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord); call System.@InitializeRecord // System._InitializeRecord(EAX, EDX); // _r := r; lea eax, _r // EAX := @_r; pop edx // EDX := Pop(); // @r mov ecx, TRecord_TypeInfo // EDX := TypeInfo(TRecord); call System.@CopyRecord // System._CopyRecord(EAX, EDX, ECX); lea eax, _r // EAX := @_r; // optional // Aqui vai o resto da função. Nós trabalharemos no // record "_r" (a cópia local), agora apontada em EAX. // Nós temos que finalizar a cópia local antes de retornarmos // System._FinalizeRecord(@_r, TypeInfo(TRecord)); lea eax, _r // EAX := @_r; mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord); call System.@FinalizeRecord // System._FinalizeRecord(EAX, EDX); end; Como _AddRefRecord, a procedure _InitializeRecord é apenas destinada para ser usada com records não inicializados. Retornando valores de records ============================= Retornar valores de records é exatamente o mesmo que retornar valores de array estático. Funções que retornam records recebem um último parâmetro adicional que é o ponteiro para a localização em memória onde o valor de retorno deve ser armazenado, isto é, o valor do último parâmetro é @Result. A memória para o record de resultado deveria ser alocada, inicializada e liberada pelo chamador (não é de responsabilidade da função chamada). Por exemplo, vamos considerar a seguinte função: function MakeRecord(Id: integer; const Name: string): TRecord; begin Result.Id := Id; Result.Name := Name; end; A função é declarada para receber dois parâmetros e retornar um record, mas internamente é como uma procedure com três parâmetros: 1) EAX = O Id para o novo record 2) EDX = O nome para o novo record 3) ECX = O endereço do record de resultado (@Result) A função pode ser reescrita em assembler como segue : function MakeRecord(Id: integer; const Name: string): TRecord; asm // EAX = Id; EDX = @Name[1]; ECX = @Result mov (TRecord PTR [ecx]).Id, eax // ECX^.Id := EAX; // Id // (@Result)^.Id := EAX; // Result.Id := EAX; // Result.Name := Name; // System.@LStrAsg(@(Result.Name), @Name[1]) // System.@LStrAsg(@(ECX^.Name), @Name[1]) lea eax, (TRecord PTR [ecx]).Name // EAX := @(ECX^.Name); call System.@LStrAsg // _LStrAsg(EAX, EDX) end; NOTA: Nós não designamos o valor EDX antes de chamar _LStrAsg porque EDX já contém o valor desejado (passado como parâmetro) Chamando funções que retornam records ===================================== Considere o seguinte código: a := MakeRecord(n, s); Alguém seria tentado a pensar que o compilador traduz como: asm mov eax, n mov edx, s lea ecx, a // ECX := @a; // @Result call MakeRecord end; Mas as coisas não acontecem deste jeito, ao menos no Delphi 5. O compilador aloca e inicializa uma variável local que armazena o resultado e então copia o resultado do record para o record de destino. Nós não temos apenas ineficiência realizando uma cópia que seria desnecessária se usássemos um código como o acima, mas- como nós temos visto acima- a cópia por si mesma não é tão inocente como uma chamada para a procedure Move (_CopyRecord checa a informação de tipo de dado em runtime para localizar os campos que requerem tratamento especial). É claro, a variável local invisível é primeiro inicializada e eventualmente finalizada. Este modo é extremamente ineficiente. Se você precisa de velocidade, chame funções record-returning usando assembler como mostrado acima, passando diretamente o endereço da variável que irá guardar o resultado como o último parâmetro (@Result). Bem, é isto por enquanto. Na próxima parte, veremos algumas coisas básicas sobre o trabalho com objetos. __________________ NOTA: O Código fonte e a aplicação DEMO estão anexados. ________________________________________________________________________ 7. Fóruns / Listas Para juntar-se a qualquer um de nossos fóruns de discussão, a melhor forma é assinar através da web, já que dessa forma você poderá acessar todas as funcionalidades disponíveis no site (como alterar suas opções de assinatura, visualizar mensagens passadas, baixar arquivos, etc.). Um ID Yahoo! é necessário para isso e você pode adquirir o seu de forma gratuita registrando-se como um usuário Yahoo! Mas se você não deseja registrar-se ou se não tem acesso completo à Internet, você pode fazer sua assinatura por e-mail (basta ter acesso a uma conta de e-mail). * Delphi: Participe da lista de discussão do Grupo Delphi-BR e faça parte da mais profissional lista Delphi do Brasil. Essa lista tem como objetivo a troca de informações entre os diversos programadores Delphi em lingua portuguesa. Aqui você encontrará grandes programadores brasileiros dando suas contribuições para a comunidade e mantendo uma via de comunicação entre todos os programadores Delphi brasileiros. http://br.groups.yahoo.com/group/delphi-br/ Assinatura: http://br.groups.yahoo.com/group/delphi-br/join delphi-br-subscribe@yahoogrupos.com.br * Kylix (inglês): Programação Kylix. http://groups.yahoo.com/group/KylixGroup/ Assinatura: http://groups.yahoo.com/group/KylixGroup/join KylixGroup-subscribe@yahoogroups.com * Componentes (inglês): Esse é um fórum para pesquisa/recomendação de componentes de software (componetes VCL e CLX, objetos ActiveX, DLLs, SOs, etc.), assim como utilitários, tutoriais, informações, etc. http://groups.yahoo.com/group/components/ Assinatura: http://groups.yahoo.com/group/components/join components-subscribe@yahoogroups.com * Desenvolvedores de Software (inglês): Esse é um fórum para discussões sobre o desenvolvimento de software e para a troca de experiências de trabalho em ambientes profissionais e comerciais. Esse não é um fórum sobre programação; os assuntos abordados são mais gerais e independentes de linguagem de programação. http://groups.yahoo.com/group/software-developers/ Assinatura: http://groups.yahoo.com/group/software-developers/join software-developers-subscribe@yahoogroups.com ________________________________________________________________________ 8. Delphi na Rede Por Dave Murray <irongut @ vodafone.net> Sites em português ================== * Comunidade Delphi de Rio Preto Portal com componentes, novidades, artigos, links e mais. http://www.delphirp.com.br/ * Planet Delphi Site brasileiro especializado em componentes para Delphi. Download grátis. http://www.fprass.hpg.ig.com.br/ * Delphi Company Site do Fernando Gonçalves dedicado ao mundo Delphi http://www.delphicompany.hpg.ig.com.br/ * IntereSite Tudo sobre Delphi http://www.ulbrajp.com.br/~tecnobyte/ * Advanced Developers Delphi, productos, novidades, consultoria... http://www.adev.com.br * Fórum Delphi-BR http://br.groups.yahoo.com/group/delphi-br/ * Fórum Delphi !! http://www.qualyinf.com.br/forum/delphi/delphi.html Componentes, bibliotecas e utilidades ===================================== Freeware -------- * Copernic Agent 6.0 - Freeware (adware, but not spyware) *NEW* It's the successor of Copernic 2001 5.0, the top metasearch software for the Internet ("Top Ten Internet Tools" by USA Today, 5-star rating from ZDNet, and "Best For 2001 - Top Five Software" by PC Magazine). Are you tired of wasting your time with meaningless results from search engines? Try searching with Copernic. It will surprise you! http://www.copernic.com/desktop/products/agent/index.html * MathX 2D and 3D graphics of one- and two-variables functions using OpenGL. Full source code is included. http://www.opensource.as.ro/public/MathX.zip Artigos, dicas e truques ======================== * Delphi 7 Update 1 - by Anders Ohlsson This update for Delphi 7 contains an updated MSSQL driver that fixes the problem with empty user names and passwords. It also fixes an issue with extra NULL characters being added to VARCHAR columns. The update also contains several documentation updates. http://community.borland.com/article/0,1410,29209,00.html * Migrating BDE Applications to dbExpress - by Bill Todd This white paper discusses the migration of BDE-based applications to dbExpress. http://community.borland.com/article/0,1410,29106,00.html * Cross-platform Development with Borland RAD Tools A white paper on native cross-platform development with Borland tools. http://community.borland.com/article/0,1410,29139,00.html * Report to the Delphi Community on Project JEDI - by Alan C. Moore Alan C. Moore, the Project JEDI director, provides an update on the many projects under development with the Project JEDI team. http://community.borland.com/article/0,1410,29157,00.html * Borland Kylix 3 versus Linux GCC Development - by William Roetzheim http://community.borland.com/article/0,1410,29171,00.html * Using the .NET Preview compiler in the Delphi 7 IDE - by John Kaster An Open Tool for using the preview compiler with the Delphi 7 IDE is available for download. http://community.borland.com/article/0,1410,29159,00.html * Top Windows API Books for Delphi Developers - by Zarko Gajic Recommended selection of top books on programming Delphi with Windows API, with links to book reviews. Books cover all of the most common Windows API functions, and each function has the syntax and an example of its use in Delphi Pascal. http://delphi.about.com/library/toppicks/aatpdelphiapibook.htm * How to validate an IBAN? http://www.swissdelphicenter.ch/en/showcode.php?id=1470 * How to get the installed keyboard layouts? http://www.swissdelphicenter.ch/en/showcode.php?id=1471 * How to get/set the caret blink time? http://www.swissdelphicenter.ch/en/showcode.php?id=1472 * How to encrypt/decrypt files with Windows NTFS functions? http://www.swissdelphicenter.ch/en/showcode.php?id=1473 * How to get mouse wheel line count? http://www.swissdelphicenter.ch/en/showcode.php?id=1474 * How to obtain all characters in Delphi 4+ IDE - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=179 * How to obtain object property list - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=180 * How to refresh Windows desktop - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=181 * How to draw rotated text - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=182 * How to swap two numbers without a third variable - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=183 * How to turn numlock on by code - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=184 * Access a MySQL database from Delphi Standard or Personal - TheSaviour How to access a MySQL database from Delphi Standard or Personal using the TMySQL component. www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=185 * How to sort a TList - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=186 * How to set the width of a TComboBox - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=187 * How to access a remote registry - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=188 * How to change a property of all components at one time - by M. Suesens How to change a special property of all components that own this property at one time. http://www.delphi3000.com/articles/article_3365.asp * CPU ID unit - by Mironescu Tony http://www.delphi3000.com/articles/article_3367.asp * How to implement a Sequential List in a database - by Adesh Jain http://www.delphi3000.com/articles/article_3372.asp * Export ALL tables from MS jet to CSV via ADO - by John Pears http://www.delphi3000.com/articles/article_3373.asp * High speed parser V1.5 - by Yuriy Pisarev Component is intended for some mathematics and logical calculations. Works at high speed - about 10000000 operations per second for simple mathematics formulas and a little more for logical formulas. http://www.delphi3000.com/articles/article_3374.asp * Safety Design with a Static Instance - by Max Kleiner How to build a real Singleton? http://www.delphi3000.com/articles/article_3376.asp * Interbase Backup on the Fly in a thread - by Kim Sandell Make interbase database backups on the fly, in a background thread. http://www.delphi3000.com/articles/article_3378.asp * Example of a Windows Service, with a thread - by Kim Sandell Delphi 5&6 has a template project for services, but it is incomplete. This example builds on that template and completes the service. It also shows how to start a thread that beeps every 2 seconds. You can use this as a base when developing servers as services. http://www.delphi3000.com/articles/article_3379.asp * Interbase Sweep on the Fly in a thread - by Kim Sandell In the Interbase Admin components there is a IBValidationService but is hard to use as it is. Sweeping is just one of the functions of the validation service. This component makes doing sweeps of databases alot easier, and also works in a thread. http://www.delphi3000.com/articles/article_3380.asp * How to write a TCP Redirector - by Kim Sandell Many people ask how to write servers in Indy, this primer goes through how a TCP server is created, and how to redirect all traffic to another remote server. This is the same as port-mapping in firewalls. http://www.delphi3000.com/articles/article_3381.asp * ENTER instead of TAB key - by Léo Souza A simple alternative to allow form navigation with the ENTER key. http://www.delphi3000.com/articles/article_3382.asp * How to create a animated (rotating) hourglass - Henk Schreij http://www.delphi3000.com/articles/article_3383.asp * How to implement an Array Property - Max Kleiner In an interface we can't use fields so when you declare a class that implements one or more interfaces, you must provide an implementation of all the methods declared in the interface and the fields too. http://www.delphi3000.com/articles/article_3387.asp * Change the color of a Listview or TreeView - by LExter LExter http://www.delphi3000.com/articles/article_3388.asp * SQL without bureaucracy - by Josir Gomes How to quickly run SQL without dropping components on your form. http://www.delphi3000.com/articles/article_3389.asp * Keyboard Hook - by William Egge Creating a keyboard hook in your application. http://www.delphi3000.com/articles/article_3390.asp * Converting a SID to a string - by Bryan Ashby Convert a Security Identifier (SID) into a human-readable string. http://www.delphi3000.com/articles/article_3391.asp * Changing Creation and Last accessed date/time for files - David Bolton http://www.delphi3000.com/articles/article_3392.asp * How to check if an OLE object is installed - by Mike Shkolnik http://www.delphi3000.com/articles/article_3394.asp * How to read contact list from MS Outlook - by Mike Shkolnik http://www.delphi3000.com/articles/article_3395.asp * Fast data transfer to MS Excel - by Mike Shkolnik http://www.delphi3000.com/articles/article_3396.asp * Multi Lingual BoolToStr() and SexToStr() - by Mike Heydon http://www.delphi3000.com/articles/article_3398.asp Tutoriais ========= * "Navigating and Editing a ClientDataSet" - by Cary Jensen You navigate and edit a ClientDataSet in a manner similar to how you navigate and edit almost another other dataset. This article provides an introductory look at basic ClientDataSet navigation and editing. http://community.borland.com/article/0,1410,29122,00.html * Sophisticated Delphi Pascal techniques - by Zarko Gajic A Beginner’s Guide to Delphi Programming: Chapter 7. Time to extend your Delphi Pascal knowledge to the max. Here are some intermediate Delphi problems and articles for everyday development tasks. http://delphi.about.com/library/weekly/aa091702a.htm * Designing an XML Grammar with DTDs - by Philip Page XML is a great medium for data transfer and definition, but it must be consistent to make it consumable. Learn more about creating a DTD to determine consistent XML. http://builder.com.com/article.jhtml?id=u00320021004ppg01.htm * SQL Basics: Creating and Altering Tables - by Shelley Doll Learn the basic DDL commands to add, alter, and remove tables and databases with this article from the Builder.com SQL basics series. http://builder.com.com/article.jhtml?id=u00320020902dol01.htm * SQL Basics: Number Data Types - by Shelley Doll Failing to understand number data types poses a DBA's greatest risk of compromised data. The SQL92 standard dictates how database manufacturers define number behaviors, such as length and truncation. http://builder.com.com/article.jhtml?id=u00320020924dol01.htm * SQL Basics: String Data Types - by Shelley Doll Data type implementations vary from database to database but a working knowledge of the SQL specification will always give you a good idea of what's going on. This article breaks down the basic rules of deploying string data types. http://builder.com.com/article.jhtml?id=u00320020918dol01.htm * A Guide to Automatic Garbage Collection Systems - by Paul Tyma Java and .NET feature automatic garbage collection, which allows you to worry about programming instead of system cleanup. Learn more about the approaches often used to add this feature to applications. http://builder.com.com/article.jhtml?id=u00320020930gcn01.htm Outros links ============ * The Delphi Bug List http://buglist.jrsoftware.org/ * Delphi 7/.NET User Group Tour 2002, US / Canada - by David Intersimone Borland is taking Delphi 7 and the Delphi Preview for Microsoft .Net on the road in the US and Canada. http://community.borland.com/article/0,1410,29089,00.html * Pascal and its Successors - by Niklaus Wirth Pascal was designed in 1969 in the spirit of Algol 60 with a concisely defined syntax representing the paradigm of structured programming. With the advent of the micro computer it became widely known and was adopted in many schools and universities. In 1979 it was followed by Modula-2 which catered for the needs of modular programming in teams. In an effort to reduce language complexity and to accommodate object- oriented programming, Oberon was designed in 1988. This article presents some aspects of the evolution of this family of languages. http://www.swissdelphicenter.ch/en/niklauswirth.php * Pascal IRC channel There is a very nice IRC channel about Pascal on DalNet (http://www.dal.net). The channel is just about Pascal (not Delphi). You can find it on irc.dal.net at #turbopascal. The channel also has a nice page with lots of sample codes. You can find their homepage at http://www.pastcow.org - As you can imagine, a channel for Delphi users can also be found there: #delphi. * BorCon Europe London UK, October 28-29 2002 http://www.borconeurope2002.com/ * BorCon Japan Tokyo Japan, November 19-20 2002 http://www.borland.co.jp/ * BorCon France Paris France, November 21-22 2002 http://info.borland.fr/conference/2002/ ________________________________________________________________________ VOCÊ PODE NOS AJUDAR Nós precisamos de sua ajuda para manter esse boletim ativo e cada vez maior. Você pode ajudar indicando o boletim a seus amigos e colegas: http://www.latiumsoftware.com/br/pascal/index.php Você também pode votar para nós em um ou todos esses rankings para dar maior visibilidade a nosso site e assim aumentar o número de assinantes do boletim: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 São alguns segundos para você que REALMENTE significam muito para nós. Não esqueça que também precisamos de artigos para os boletins e que existe um prêmio para um dos autores em cada número (em inglês). Todos os artigos serão considerados mas nós estamos particularmente interessados em artigos sobre Kylix já que existe pouco material disponível online. * Envie seu artigo em inglês para pascal-newsletter-owner@yahoogroups.com * Envie seu artigo em português para boletim-pascal-owner@yahoogrupos.com.br ________________________________________________________________________ Se você não recebeu o código fonte completo dos exemplos neste número, você pode obtê-los em http://www.latiumsoftware.com/br/pascal/p0040.zip ________________________________________________________________________ Esse boletim é fornecido "COMO ESTÁ" sem garantias de qualquer tipo. Seu uso implica na aceitação dos termos de licença e isenção de garantia que podem ser lidos em http://www.latiumsoftware.com/br/pascal/index.php Os artigos são propriedade e copyright de seus respectivos autores e foram reproduzidos aqui com sua permissão. Você pode redistribuir esse boletim desde que na sua íntegra (incluindo notas de propriedade e copyright), sem alterações e de forma gratuita. ________________________________________________________________________ Página do grupo:.....: http://br.groups.yahoo.com/group/boletim-pascal/ Assinar..............: boletim-pascal-subscribe@yahoogrupos.com.br Cancelar assinatura..: boletim-pascal-unsubscribe@yahoogrupos.com.br Assinar/cancelar.....: http://groups.yahoo.com/group/boletim-pascal/join Problemas com sua assinatura? boletim-pascal-owner@yahoogrupos.com.br ________________________________________________________________________ Boletim Pascal http://www.latiumsoftware.com/br/pascal/index.php Copyright (c) 2003 por Ernesto De Spirito. Todos os direitos reservados. ________________________________________________________________________ |
Os exemplos do código fonte desta edição estão disponíveis para download.
![]() |
Erros? Omissões? Comentários? Por favor contate-nos!






