Geração e Utilização de DLLs no Delphi

Autora: Lisiane Volpi Sipert

Uma biblioteca de ligação dinâmica (Dynamic Link Library – DLL) é um módulo compilado que contém código ou resources utilizados por outras aplicações. Em um ambiente windows, as DLLs permitem que as aplicações compartilhem código e resources.

Quando as aplicações são carregadas na memória, o Windows cria ligações entre as chamadas de procedures e funções na aplicação e as procedures e funções na DLL. No Object Pascal, a biblioteca típica de procedure e funções é implementada como uma unit. Se uma biblioteca ou unit for ligada estaticamente, uma cópia da biblioteca compilada é incluída dentro do arquivo executável. Não é o caso da DLL, já que o próprio nome diz é dinâmica.

Fizemos alguns testes com a geração de DLLs em Sql Windows e Centura, mas esbarramos na impossibilidade de utilizá-las em outras linguagens. Além de uma série de limitações com relação ao que pode ser gerado DLL.

No estudo que gerou este documento foram encontradas dificuldades que, para solucioná-las, o help e os manuais do Delphi não foram suficientes. Para resolver algumas destas dificuldades contamos com a experiência do professor Parau Ari, professor da Faculdades Positivo.

A abordagem deste artigo é bem técnica e prática, direcionada, para quem vai construir uma DLL em Delphi, mostrando como fazer e apresentando as dificuldades encontradas.

Estruturando melhor este artigo, vamos abordar este assunto de acordo com os seguintes tópicos:

  • Vantagens no uso das DLL’s
  • Criando uma DLL em Delphi 32 bits (Versão 2.0 e 2.01)
  • Utilizando a DLL gerada em Delphi 2.0 (32 bits) no Centura (32 bits)
  • Utilizando a DLL gerada em Delphi 2.0 (32 bits) no Visual Basic 4.0 (32 bits)
  • Utilizando a DLL gerada em Delphi 2.0 (32 bits) no próprio Delphi 2.0 (32 bits)
  • Criando uma DLL em Delphi 1.0 (16 bits)
  • Utilizando a DLL gerada em Delphi 1.0 (16 bits) no Sql Window (16 bits)
  • Utilizando a DLL gerada em Delphi 1.0 (16 bits) no Visual Basic 3.0 (16 bits)
  • Utilizando a DLL gerada em Delphi 1.0 (16 bits) no Visual Basic 4.0 (16 bits)
  • Utilizando a DLL gerada em Delphi 1.0 (16 bits) no próprio Delphi 1.0 (16 bits)
  • Tabela com resultados da utilização de DLLs nos vários ambientes

VANTAGENS NO USO DAS DLLs

Há uma série de vantagens em usar DLLs:

  • Você pode alterar ou adicionar funções e procedimentos a uma DLL sem ter que recompilar as aplicações que utilizam esta DLL, contanto que a porção da interface utilizada pelo executável permaneça a mesma.
  • O tamanho dos programas executáveis e o total de utilização de memória é menor, uma vez que uma única cópia da DLL é carregada na memória e utilizada por diversas aplicações.
  • As DLLs permitem personalização. Por exemplo, você pode colocar texto para todas as mensagens da aplicação em uma DLL e fornecer diferentes versões de linguagens da DLLs, tais como inglês, espanhol e francês.
  • Usar uma DLL pode melhorar a performance, no caso do Sql Windows. Fiz um teste usando uma função direto no código, demorava 21 segundos para executar; quando gerei uma dll no próprio Sql Windows e usei a mesma função, demorou 7 a 8 segundos.
  • Você pode gerar DLLs de funções genéricas e utilizá-las em outras linguagens como Sql Windows e Visual Basic, não sendo necessário convertê-las. As DLLs geradas no Sql Windows só podem ser usadas no próprio ambiente Gupta, não podem ser usadas no Delphi, Visual Basic, etc. Vale a mesma coisa para o Centura.

CRIANDO UMA DLL EM DELPHI 2.0 OU 2.01 (32 BITS)

Para criar uma nova DLL no Delphi 2.0 você só precisa selecionar File/New no menu e então selecionar no Object Repository e dar um clique sobre o botão Ok. O Delphi automaticamente criará um novo projeto como o mostrado abaixo:

library Project1;

{ Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select View-Project Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the DELPHIMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using DELPHIMM.DLL, pass string information using PChar or ShortString parameters. }

uses

SysUtils, Classes;

begin

end.

A estrutura de uma DLL, na verdade, é a mesma de um projeto comum. A partir de agora você pode criar novas units, novos forms que eles farão parte da sua DLL.

Salve este projeto com o nome da dll que será gerada. Não altere o nome Project1 manualmente.

Para entender melhor vamos construir uma dll chamada de Teste32. Você pode declarar as funções e procedimentos neste próprio arquivo, ou colocá-las em unit(s) separadas e depois incluir estas unit(s) na library. A descrição abaixo mostra o segundo exemplo.

Passos:

  1. Opção do menu File/New e selecione a opção DLL;
  2. Opção do menu File/Save Project As e altere o nome Project1 para Teste32 (coloque em um diretório separado);
  3. Opção do menu File/New e selecione a opção Unit;
  4. Opção do menu File/Save As e altere o nome para untFuncoes;
  5. Digite o código como descrito abaixo.

    Para a library Teste32:

    library Teste32;

    { Important note about DLL memory management: ShareMem must be the .....

    .... using PChar or ShortString parameters. }

    uses

    SysUtils, Classes,

    UntFuncoes in 'untFuncoes.pas';

    Exports

    Max index 1,

    Fruta index 2,

    TesteTipos index 3;

    Begin

    End.

    Para a Unit untFuncoes

    unit untFuncoes;

    interface

    function Max(X, Y: Integer): Integer; stdcall; export;

    procedure Fruta(a, b, c, d: PChar); stdcall; export;

    procedure TesteTipos (x1:integer; var x2:integer; y1, y2:pchar); stdcall; export;

    implementation

    uses sysutils;

    function Max(X, Y: Integer): Integer;

    begin

    if X > Y then

    Max := X

    Else

    Max := Y;

    end;

    procedure Fruta(a, b, c, d: PChar);

    begin

    StrCopy (a, 'MORANGO');

    StrCopy (b, 'ABACAXI');

    StrCopy (c, 'MELANCIA');

    StrCopy (d, 'GOIABA');

    end;

    procedure TesteTipos (x1:integer; var x2:integer; y1, y2:pchar);

    var

    aux: string;

    begin

    aux := StrPas(y1) + ' Alterado';

    x2 := x1 + x1;

    StrPCopy (y2, aux);

    end;

    end.

  6. Para gerar a DLL escolha a opção do menu Project/Build All. Com isto, se não tiver nenhum erro de compilação a sua Teste32.dll já foi gerada no mesmo diretório que está o seu projeto (Teste32.dpr). Um erro que pode ocorrer é Unknown identifier. Este erro é, provavelmente, porque a biblioteca que usa as funções que estão sendo executadas (StrCopy, por exemplo), não foi incluída. Para esta função StrCopy específica inclua a biblioteca Sysutils na cláusula Uses da seção implementation.
  7. Algumas observações importantes:
  • Poderiam ser incluídas quantas units fossem necessárias. Poderia ser incluída cada função ou procedimento dentro de uma unit isolada e incluí-las todas na library, não faria diferença alguma. A vantagem de fazer desta forma é que, quando você constrói um programa novo em Delphi, que usa a função da DLL, você pode optar por acessar a DLL (compilação melhor, execução pior), ou a própria unit.
  • Procedimentos ou funções são exportados por uma DLL quando forem listados na cláusula exports da DLL. Cada entrada em uma cláusula exports especifica um identificador de um procedimento ou função a ser exportado. Esta função ou procedimento devem ter sido declarados e implementados antes da cláusula exports. Você pode preceder o identificador na cláusula exports com um identificador de unit e um ponto – isto é conhecido como identificador qualificado.
  • Uma entrada exports pode incluir uma cláusula index, que é feita através da palavra index, seguida de uma constante inteira entre 1 e 32767. Quando uma cláusula index é especificada, o procedimento ou função a ser exportado utiliza o número ordinal especificado. Se não houver uma cláusula index em uma entrada exports, um número ordinal é automaticamente atribuído à rotina. Exemplo: exports Max index 1;
  • Uma entrada pode ter também uma cláusula name, que é feita através da palavra name, seguida por uma constante string. Quando houver uma cláusula name, o procedimento ou função a serem exportados utiliza o nome especificado na constante string (observando inclusive maiúsculas e minúsculas). Se não houver uma cláusula name em uma entrada exports, o procedimento ou função serão exportados pelo seu identificador com a mesma capitalização e letras. Exemplo: exports Max name ‘fMaximo’;
  • Uma das grandes vantagens das DLLs é que as suas rotinas podem ser acessadas por qualquer ferramenta de desenvolvimento para windows. Mas cada ferramenta de desenvolvimento possuirá o seu conjunto de tipos de dados, logo, quando você utilizar parâmetros nas suas chamadas de rotinas, certifique-se de utilizar parâmetros compatíveis com os tipos definidos dentro de sua DLL.
  • Quando precisar usar um parâmetro do tipo string, na realidade deve ser usado o tipo pchar do Delphi. Na passagem de strings como parâmetro não podemos utilizar tipo String do pascal na DLL pois não são compatíveis com o tipo string das outras linguagens.

O tipo pchar tem algumas peculiaridades. O tipo Pchar é indiretamente um parâmetro passado por referência (na realidade é um parâmetro por valor cujo conteúdo é o endereço da localização da string), sendo assim não é necessário (e se fizer isso ocorre um erro) definir o parâmetro com o VAR.

Observe a seguinte função gerada no Delphi.

Function Teste(Entrada,

Saída: Pchar): Integer;

begin

Result := 10; (*Retorno da função para o VB)

StrPCopy (Saida, Entrada); (*Cópia para modificar o string do VB)

end;

Esta função (que poderia sem problema algum ser um procedimento) realmente está copiando o conteúdo da variável Entrada para dentro da variável Saída. Ou seja, quando a seguinte chamada for feita dentro do VB o valor final da variável Saída, que antes da chamada contém 20 espaços em branco, será a palavra "Visual Basic". O espaço necessário para alocar a variável de Saída deve ser o tamanho do conteúdo de entrada mais 1. Portanto no mínimo 13. (a seguir como fica o código no VB)

sEntrada = "Visual Basic"

sSaida = String(20, " ")
‘Cria o espaço necessário para o retorno (obrigatório)

dResp = Teste(sEntrada, sSaida)

Em resumo, fazendo da forma como foi mostrado no exemplo anterior, qualquer alteração feita nas variáveis Entrada e Saída, dentro do código Delphi, irá alterar o conteúdo das respectivas variáveis dentro do VB. Neste caso, estamos realmente definindo, formalmente, a passagem de parâmetros das duas funções (VB e Delphi) por valor. O detalhe é que a passagem de parâmetros que o VB está fazendo para o string é o mesmo formato utilizado na linguagem C (ou seja, o padrão da passagem de parâmetros de um vetor de caracteres em C é sempre por referência). Ou seja, as duas definições das funções, na prática, ocorrem por referência.

  • No Delphi, quando for copiar o conteúdo para dentro de variáveis do tipo pchar, deve-se usar funções do tipo StrPCopy , StrPas, etc.em vez de usar comandos de atribuição direta.
  • Delphi tem 4 diretivas que permitem a você especificar as convenções de chamadas a serem usadas na passagem de parâmetros para procedimentos e funções. A convenção de chamada default é sempre register. As convenções de chamadas diferem em três áreas:
    • Ordem da passagem de parâmetros;
    • Responsabilidade por remover parâmetros de pilha;
    • Uso de registradores para passagem de parâmetros.

As convenções register e pascal passam parâmetros da esquerda para a direita, ou seja, o parâmetro mais à esquerda é passado primeiro e o parâmetro mais à direita é passado por último. As convenções cdecl e stdcall passam parâmetros da direita para a esquerda. Para todas as convenções exceto a cdecl, o procedimento ou função remove os parâmetros da pilha ao retornar. Com a convenção cdecl, o chamador deve remover os parâmetros da pilha quando a chamada retorna. A convenção register usa até três registradores de CPU para passar os parâmetros, ao passo que as outras convenções sempre passam todos os parâmetros na pilha.

Diretiva Ordem Limpada Usa
Registradores
Register Esquerda
p/ direita
Função Sim
Pascal Esquerda
p/ direita
Função Não
Cdecl Direita
p/ Esquerda
Chamador Não
stdcall Direita
p/ Esquerda
Função Não


A convenção register é de longe a mais eficiente, já que ela freqüentemente evita a criação de uma estrutura de pilha. As convenções pascal e cdecl são úteis principalmente para chamadas de rotinas em dynamic-link libraries(DLL) escritas em C, C++, ou outras linguagens. A convenção stdcall é usada para chamada de rotinas API do Windows.

Testamos utilizar as cláusulas stdcall e cdecl na geração da DLL e para ambas o comportamento foi o mesmo, quando incluída no próprio Delphi e no Centura. Funcionou normalmente. Usando a convenção pascal, invertem-se os parâmetros, portanto, no nosso exemplo, o procedimento TesteTipos travou, uma vez que invertendo os parâmetros foi passando inteiro no lugar de pchar e pchar no lugar de inteiro. Usando a cláusula register travou na chamada da primeira função, o que era esperado.

O código fonte do Delphi 1.0 e 2.0 não é exatamente o mesmo, mas muito parecido. Não é possível simplesmente abrir o fonte da outra versão, mas funciona se você criar um novo projeto na versão que queira gerar a DLL e depois fazer um Copy/Paste do código dos procedimentos e funções. É recomendado gerar dois projetos com nomes diferentes, uma para 16 bits outra para 32 bits. A única coisa que muda nestes nossos exemplos é que na versão 32 bits é incluída uma cláusula stdcall em todas as funções e procedimentos que são incluídos na DLL, para manter a compatibilidade com funções do Windows, conforme explicado no item anterior.

Depois de gerada uma dll no Delphi 2.0 (mesmo no Centura),, não conseguimos usar no VB 3.0. Claro, foi gerada em 32 bits, não pode rodar em 16 bits. Ao incluir no VB aparecia a mensagem: "Não consegue abrir o arquivo dll". Também não funciona no Sql Windows que é 16 bits.

UTILIZANDO A DLL GERADA EM DELPHI 2.0 (32 BITS) NO CENTURA (32 BITS)

O código fonte do Centura e Sql Windows é exatamente o mesmo. Salvando a aplicação como Text Indented (apt) nenhuma conversão será necessária.

Na seção External Funtions escreva:

Library name: Teste32.dll

Function: Max

Description:

Export Ordinal: 1

Returns

Number: INT

Parameters

Number: INT

Number: INT

Function: Fruta

Description:

Export Ordinal: 2

Returns

Parameters

String: LPSTR

String: LPSTR

String: LPSTR

String: LPSTR

Function: TesteTipos

Description:

Export Ordinal: 3

Returns

Parameters

Number: INT

Receive Number: LPINT

String: LPSTR

String: LPSTR

No código onde serão usadas as funções deve conter por exemplo: (considere uma tela com 11 data fields, e foram criadas 5 variáveis do tipo string na seção de declaração de variáveis da tela).

Set df3 = Max(df1, df2)

Set str1 = " "

Set str2 = " "

Set str3 = " "

Set str4 = " "

Call Fruta(str1, str2, str3, str4)

Set df4 = str1

Set df5 = str2

Set df6 = str4

Set df7 = str4

Set str5 = " "

Call TesteTipos(df8, df9, df10, str5)

Set df11 = str5

Pontos importantes a destacar:

  • Certifique-se de que a DLL está em um diretório visível para o Centura, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional. Caso contrário, você deverá declarar o path do diretório onde ela se encontra juntamente com seu nome na seção External Function.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.
  • É estritamente necessário usar variáveis para passagem de parâmetro quando o tipo no pascal for Pchar e retornar valor, e essas variáveis devem ser inicializadas com conteúdos do valor que será passado mais um, no mínimo. Se não inicializar estas variáveis ocorre um erro de GPF no Windows 3.11 ou trava a aplicação na Windows 95.
  • No caso de números, não é necessário tanta burocracia, pode ser usado inclusive o próprio objeto data field, até mesmo quando for do tipo por referência (retornando valor).
  • A DLL gerada no Delphi 2.0 (32 bits) não pode ser usada no Sql Windows. Se tentar fazer isto, deve aparecer uma mensagem como esta: "Cannot load or find external library or one of its components. Check to ensure the path and filename are correct and that all required libraries are available."
  • Foi necessário incluir a cláusula stdcall ou cdecl na geração da DLL em Delphi 2.0. Usando as outras cláusulas register e pascal ou usar a DLL gerada pelo Delphi 1.0 não funciona.

UTILIZANDO A DLL GERADA EM DELPHI 2.0 (32 BITS) NO VISUAL BASIC 4.0 (32 BITS)

Na seção de declarações do módulo, escreva: (importante criar um módulo.bas).

Option Explicit
Public Declare Function Max Lib "Teste32.dll" (ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Public Declare Sub Fruta Lib "Teste32.dll" (ByVal s1$, s2$, ByVal s3$, ByVal s4$)
Public Declare Sub TesteTipos Lib "Teste32.dll" (ByVal n3 As Integer, n4 As Integer, ByVal s5$, ByVal s6$)

Na seção onde forem chamadas as funções:

Dim str1 As String
Dim str2 As String
Dim str3 As String
Dim str4 As String
Dim str5 As String
Dim str6 As String
Dim naux As Integer

Text3.Text = Max(Text1.Text, Text2.Text)
Str1 = String(10, " ")
Str2 = String(10, " ")
Str3 = String(10, " ")
Str4 = String(10, " ")
Str5 = String(50, " ")
Str6 = String(50, " ")
Call Fruta(str1, str2, str3, str4)
Text4.Text = str1
Text5.Text = str2
Text6.Text = str3
Text7.Text = str4
Str6 = Text10.Text
Call TesteTipos(Text8.Text, naux, str6, str5)
Text9.Text = naux
Text10.Text = str5

Pontos importantes a destacar:

  • Certifique-se de que a DLL está em um diretório visível para o VB, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional. Caso contrário, você deverá declarar o path do diretório onde ela se encontra juntamente com seu nome na seção de declarações.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.
  • As variáveis auxiliares do tipo string têm que ser declaradas linha a linha, senão dá erro, (embora seja natural que se escreva Dim str1, str2, str3, str4, str5, str6 As String, isto não funciona).
  • Outro detalhe importantíssimo e inesquecível é a inicialização das variáveis string, com o tamanho do string retornado mais um. Caso isto não seja feito ocorrerá um erro de GPF.
  • É necessário passar os tipos string por valor (ByVal) mesmo que seja passagem por referência. O porquê disto está explicado no sexto ponto do item 7 do tópico Criando uma DLL em Delphi 2.0.
  • Para rodar no VB 4.0 (32 bits) foi necessário incluir a cláusula stdcall na geração da DLL em Delphi 2.0 e ainda assim trouxe um resultado suspeito em uma das funções. O maior dos números sempre é o primeiro parâmetro. As outras duas funções testadas não deram problema. Em um outro dia, sem alterações significativas, a primeira função passou a funcionar corretamente. Não encontramos explicação lógica. Usando as outras cláusulas cdecl, register, pascal, ou usar a DLL gerada pelo Delphi 1.0 não funciona.

UTILIZANDO A DLL GERADA EM DELPHI 2.0 (32 BITS) NO PRÓPRIO DELPHI 2.0

Para utilizar uma função ou procedimento de uma DLL, você precisa primeiro declarar a rotina de interface dentro da sua aplicação e então dispará-la em alguma parte do seu código.

Na seção de implementação ou interface, escreva as declarações das funções:

function Max(X, Y: Integer): Integer; stdcall; external 'Teste32.dll' index 1;

procedure Fruta(a, b, c, d: PChar); stdcall; external 'Teste32.dll' index 2;

procedure TesteTipos (x1:integer; var x2:integer; y1, y2:pchar); stdcall; external 'Teste32.dll' index 3;


Na seção onde forem chamadas as funções, escreva:

var

str1, str2, str3, str4, str5, str6:pchar;

naux:integer;

begin

Edit3.Text := inttostr(Max(strtoint(Edit1.Text), strtoint(Edit2.Text)));

str1 := StrAlloc(50); // Tentei atribuir str1 := ' '; e não funcionou

str2 := StrAlloc(50);

str3 := StrAlloc(50);

str4 := StrAlloc(50);

Fruta(str1, str2, str3, str4);

Edit4.Text := str1;

Edit5.Text := str2;

Edit6.Text := str3;

Edit7.Text := str4;

str5 := StrAlloc(50);

StrPCopy(str5,Edit10.Text);

Str6 := StrAlloc(50);

TesteTipos(strtoint(Edit8.Text), naux, str5, str6);

Edit9.Text := inttostr(naux);

Edit10.Text := StrPas(str6);

end;

Pontos importantes a destacar:

  • Certifique-se de que a DLL está em um diretório visível para o Delphi, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional. Caso contrário, você deverá declarar o path do diretório onde ela se encontra juntamente com seu nome na rotina de interface, ou, você pode atribuir o path na opção Search Path da caixa de diálogo Project Options.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente o nome, os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.

Para um módulo (unit) utilizar um procedimento ou função de uma DLL, você deve importar o procedimento ou função utilizando uma declaração external. O Object Pascal importa procedimentos e funções de duas maneiras: por nome e por índice. Quando não houver uma cláusula index ou name especificada, a procedure ou função é importada por nome. O nome usado é o mesmo do identificador do procedimento ou função, com as mesmas letras e capitalização. Por exemplo:

Function Soma(x, y, x:real):real; external ‘minhadll.dll’;

Function Soma(x, y, x:real):real;external ‘minhadll.dll’ name ‘teste’;

Function Soma(x, y, x:real) real external ‘minhadll.dll’ index 1;

Quando houver uma cláusula name, o procedimento ou função podem ser importados por um nome diferente do seu identificador. Finalmente, quando houver uma cláusula index, o procedimento ou função serão importados por um ordinal. Quando empregamos este tipo de estratégia, reduzimos o tempo de chamada do módulo, uma vez que o nome do procedimento não precisa ser localizado na tabela de nomes da DLL.

  • As funções da DLL foram declaradas na seção implementation, isso fará com que apenas esta unit consiga usar estas funções. Se quisesse deixá-las públicas, as mesmas deveriam ser declaradas na seção interface. Isto é apenas um conceito do Delphi, não tem nada haver com o fato de ser dll ou não.
  • Como foi usada a cláusula stdcall na geração da dll, ela é obrigatória também aqui, no programa que vai chamá-la. Vale lembrar que se fosse usar esta dll apenas no Delphi, esta cláusula seria totalmente dispensável. Para entender melhor esta cláusula veja o 6o ponto no item 7 do tópico "Como gerar DLL em Delphi" e, também, a tabela com resultados da utilização da DLL em vários ambientes que basicamente comprova que a mesma cláusula usada na geração deve ser usada na utilização da DLL, e que não é possível usar DLL gerada em 16 bits.
  • É estritamente necessário usar variáveis para passagem de parâmetro quando o tipo no pascal for Pchar e elas devem ser inicializadas com conteúdos do tamanho do valor que será passado mais um, no mínimo. Se não inicializar estas variáveis ocorre um erro de violação de memória. Nas inicializações feitas usando a função StrAlloc, não adianta fazer uma alocação usando comando de atribuição.
  • Para trabalhar com Pchar e String no Delphi utiliza-se as funções StrPCopy e StrPas para fazer conversões de um tipo para outro.

CRIANDO UMA DLL EM DELPHI 1.0 (16 BITS)

Para criar uma nova DLL no Delphi 1.0 você só precisa selecionar File/New Project no menu. O Delphi automaticamente criará um novo projeto como o mostrado abaixo:

program Project1;

uses

Forms,

Unit1 in 'UNIT1.PAS' {Form1};

{$R *.RES}

begin

Application.CreateForm(TForm1, Form1);

Application.Run;

end.

A estrutura de uma DLL, na verdade, é a mesma de um projeto comum, exceto que a DLL começa com a palavra library no cabeçalho ao invés de program. Se você não vai usar uma form na sua DLL exclua a que já existe no projeto, clicando no botão "Remova o arquivo do projeto", representado por um menos na barra de ferramenta. Com isso a estrutura fica assim:

library Project1;

uses

Forms;

{$R *.RES}

begin

Application.Run;

end.

A partir de agora você pode criar novas units ou também novos forms que eles farão parte da sua DLL. Salve este projeto com o nome da dll que será gerada, através da opção de menu File/Save Project As. Não altere o nome Project1 manualmente.

Para maiores informações veja no Help do Delphi 16 bits procurando pela palavra-chave library, na opção See also, Writting DLL, Example.

Para entender melhor, vamos construir uma dll chamada de Teste16. Você pode declarar as funções e procedimentos neste próprio arquivo, ou colocá-las em unit(s) separadas e depois incluir estas unit(s) na library. A descrição abaixo mostra o segundo exemplo.

Passos:

  1. Opção do menu File/New Project;
  2. Digite a palavra library sobre a palavra program;
  3. Clique no botão Pasta – (Remova arquivos do projeto) Form1 se sua DLL não vai usar formulário;
  4. Opção do menu File/Save Project As e altere o nome Project1 para Teste16 (coloque em um diretório separado);
  5. Opção do menu File/New Unit;
  6. Opção do menu File/Save as e altere o nome para untFun;
  7. Digite o código como descrito abaixo.

Para a library Teste16:

library Teste16;

{$R *.RES}

uses

SysUtils,

Classes, Forms,

Untfun in 'UNTFUN.PAS';

exports

Max index 1,

Fruta index 2,

TesteTipos index 3;

begin

Application.Run;

end.

Para a Unit untFun:

unit untFun;

interface

function Max(X, Y: Integer): Integer; export;

procedure Fruta(a, b, c, d: PChar); export;

procedure TesteTipos (x1:integer; var x2:integer; y1, y2:pchar); export;

implementation

uses sysutils;

function Max(X, Y: Integer): Integer;

begin

if X > Y then Max := X else Max := Y;

end;

procedure Fruta(a, b, c, d: PChar);

begin

StrCopy (a, 'MORANGO');

StrCopy (b, 'ABACAXI');

StrCopy (c, 'MELANCIA');

StrCopy (d, 'GOIABA');

end;

procedure TesteTipos (x1:integer; var x2:integer; y1, y2:pchar);

var

aux: string;

begin

aux := StrPas(y1) + ' Alterado';

x2 := x1 + x1;

StrPCopy (y2, aux);

end;

end.

  1. Para gerar a DLL escolha a opção do menu Compile/Build All. Com isto, se não tiver nenhum erro de compilação, a sua Teste16.dll já foi gerada no mesmo diretório em que está o seu projeto (Teste16.dpr). Um erro que pode ocorrer é Unknown identifier. Este erro é, provavelmente, porque a biblioteca que usa as funções que estão sendo executadas (StrCopy, por exemplo), não foi incluída. Para esta função StrCopy específica, inclua a biblioteca Sysutils na cláusula Uses da seção implementation.
  2. Algumas observações importantes:
  • Poderiam ser incluídas quantas units fossem necessárias. Poderiam ser incluídos cada função ou procedimento dentro de uma unit isolada e incluí-los todos na library, não faria diferença alguma. A vantagem de fazer desta forma é que, quando você constrói um programa novo em Delphi, que usa a função da DLL, você pode optar por acessar a DLL (compilação melhor, execução pior), ou a própria unit.
  • Procedimentos ou funções são exportados por uma DLL quando forem listados na cláusula exports da DLL. Cada entrada em uma cláusula exports especifica um identificador de um procedimento ou função a ser exportado. Esta função ou procedimento devem ter sido declarados e implementados antes da cláusula exports. Você pode preceder o identificador na cláusula exports com um identificador de unit e um ponto – isto é conhecido como identificador qualificado.
  • Uma entrada exports pode incluir uma cláusula index, que é feita através da palavra index, seguida de uma constante inteira entre 1 e 32767. Quando uma cláusula index é especificada, o procedimento ou função a ser exportado utiliza o número ordinal especificado. Se não houver uma cláusula index em uma entrada exports, um número ordinal é automaticamente atribuído à rotina. Exemplo: exports Max index 1;
  • Uma entrada pode ter também uma cláusula name, que é feita através da palavra name, seguida por uma constante string. Quando houver uma cláusula name, o procedimento ou função a serem exportados utiliza o nome especificado na constante string (observando inclusive maiúsculas e minúsculas). Se não houver uma cláusula name em uma entrada exports, o procedimento ou função será exportado pelo seu identificador com a mesma capitalização e letras. Exemplo: exports Max name ‘fMaximo’;
  • Uma das grandes vantagens das DLLs é que as suas rotinas podem ser acessadas por qualquer ferramenta de desenvolvimento para windows. Mas cada ferramenta de desenvolvimento possuirá o seu conjunto de tipos de dados, logo, quando você utilizar parâmetros nas suas chamadas de rotinas, certifique-se de utilizar parâmetros compatíveis com os tipos definidos dentro de sua DLL.
  • Quando precisar usar um parâmetro do tipo string, na realidade deve ser usado o tipo pchar do Delphi. Na passagem de strings como parâmetro não podemos utilizar tipo String do pascal na DLL, pois, não são compatíveis com o tipo string das outras linguagens.

O tipo pchar tem algumas peculiaridades. O tipo Pchar é indiretamente um parâmetro passado por referência (na realidade é um parâmetro por valor cujo conteúdo é o endereço da localização da string). Sendo assim, não é necessário (e se fizer isso ocorre um erro) definir o parâmetro com o VAR. Observe a seguinte função gerada no Delphi.

Function Teste(Entrada,

Saida:Pchar): Integer;

begin

Result := 10;

(*Retorno da função para o VB)

StrPCopy (Saida, Entrada);

(*Cópia para modificar o string do VB)

end;

Esta função (que poderia sem problema algum ser um procedimento) realmente está copiando o conteúdo da variável Entrada para dentro da variável Saída. Ou seja, quando a seguinte chamada for feita dentro do VB o valor final da variável Saída, que antes da chamada contém 20 espaços em branco, será a palavra "Visual Basic". O espaço necessário para alocar a variável de Saída deve ser o tamanho do conteúdo de entrada mais 1. Portanto, no mínimo 13. (a seguir como fica o código no VB)

sEntrada = "Visual Basic"

sSaida = String(20, " ")

‘Cria o espaço necessário

para o retorno (obrigatório)

dResp = Teste(sEntrada, sSaida)

Entrada e Saída, dentro do código Delphi, irá alterar o conteúdo das respectivas variáveis dentro do VB. Neste caso, estamos realmente definindo formalmente a passagem de parâmetros das duas funções (VB e Delphi) por valor, o detalhe é que a passagem de parâmetros que o VB está fazendo para o string é o mesmo formato utilizado na linguagem C (ou seja, o padrão da passagem de parâmetros de um vetor de caracteres em C é sempre por referência). Ou seja, as duas definições das funções na prática ocorrem por referência.

  • No Delphi, quando se for copiar o conteúdo para dentro de variáveis do tipo pchar deve-se usar funções do tipo StrPCopy , StrPas, etc., ao invés de usar comandos de atribuição direta.
  • Delphi 1.0 tem 1 diretiva que permite especificar as convenções de chamadas a serem usadas na passagem de parâmetros para procedimentos e funções. A diretiva padrão cdecl habilita chamar módulos escritos em outras linguagens. (não em Object Pascal, por exemplo escritas em VB, C, etc – só que não fizemos nenhum teste deste tipo). O uso desta diretiva não suporta listas de parâmetros variáveis. Os parâmetros são passados da direita para a esquerda e o código compilado é responsável por liberar o espaço quando retorna da função. Fizemos os testes conforme mostrado na tabela com resultados da utilização de DLLs nos vários ambientes e se usar o comando cdecl na geração da DLL só é possível utilizar no próprio Delphi 1.0 colocando esta diretiva na declaração da função. E se não colocar nenhuma diretiva pode-se usar no Sql Windows, VB 3.0, VB 4.0 (16 bits) e no próprio Delphi 1.0.
  • O código fonte do Delphi 1.0 e 2.0 não é exatamente o mesmo, mas muito parecido. Não é possível simplesmente abrir o fonte da outra versão, mas funciona se você criar um novo projeto na versão que queira gerar a DLL e depois fazer um Copy/Paste do código dos procedimentos e funções. É recomendado gerar dois projetos com nomes diferentes, uma para 16 bits outra para 32 bits. A única coisa que muda nestes nossos exemplos é que na versão 32 bits é incluída uma cláusula stdcall em todas as funções e procedimentos que são incluídos na DLL, para manter a compatibilidade com funções do Windows, conforme explicado no item anterior.
  • Qualquer DLL gerada em um ambiente 32 bits (Centura, Delphi 2.0, etc.) não pode ser usada em um ambiente 16 bits. Portanto, se você quiser usar uma DLL no VB 3.0, Sql Windows, Delphi 1.0, etc. tem que usar Delphi 1.0 e não a versão 2.0.
  • Se aparecer uma mensagem como (;) esperado, posicionado bem na diretiva export na declaração da função que será usada na DLL, coloque a cláusula far logo após a declaração dos parâmetros, antes do export que resolve o problema. Esta diretiva avisa o Delphi que ele precisa procurar a função não na ordem de compilação e sim em todos os lugares possíveis: antes, depois, outras dll’s, etc.

UTILIZANDO A DLL GERADA EM DELPHI 1.0 (16BITS) BITS NO SQL WINDOWS (16 BITS)

O código fonte do Centura e Sql Windows é exatamente o mesmo. Salvando a aplicação como Text Indented (apt) nenhuma conversão será necessária.

Na seção External Funtions escreva:

Library name: Teste16.dll

Function: Max

Description:

Export Ordinal: 1

Returns

Number: INT

Parameters

Number: INT

Number: INT

Function: Fruta

Description:

Export Ordinal: 2

Returns

Parameters

String: LPSTR

String: LPSTR

String: LPSTR

String: LPSTR

Function: TesteTipos

Description:

Export Ordinal: 3

Returns

Parameters

Number: INT

Receive Number: LPINT

String: LPSTR

String: LPSTR

 

No código onde serão usadas as funções deve conter, por exemplo: (considere uma tela com 11 data fields, e foram criadas 5 variáveis do tipo string na seção de declaração de variáveis da tela).

 

Set df3 = Max(df1, df2)

Set str1 = " "

Set str2 = " "

Set str3 = " "

Set str4 = " "

Call Fruta(str1, str2, str3, str4)

Set df4 = str1

Set df5 = str2

Set df6 = str4

Set df7 = str4

Set str5 = " "

Call TesteTipos(df8, df9, df10, str5)

Set df11 = str5

 

Pontos importantes a destacar:

 

  • Certifique-se de que a DLL está em um diretório visível para o Sql Windows, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional. Caso contrário, você deverá declarar o path do diretório onde ela se encontra juntamente com seu nome na seção External Function.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.
  • É estritamente necessário usar variáveis para passagem de parâmetro quando o tipo no pascal for Pchar e retornar valor, e essas variáveis devem ser inicializadas com conteúdos do valor que será passado mais um, no mínimo. Se não inicializar estas variáveis ocorre um erro de GPF no Windows 3.11 ou trava a aplicação na Windows 95.
  • No caso de números, não é necessário tanta burocracia, pode ser usado inclusive o próprio objeto data field, até mesmo quando for do tipo por referência (retornando valor).
  • A DLL gerada no Delphi 2.0 (32 bits) não pode ser usada no Sql Windows. Se tentar fazer isto deve aparecer uma mensagem como esta: "Cannot load or find external library or one of its components. Check to ensure the path and filename are correct and that all required libraries are available."
  • Não foi necessário incluir cláusula nenhuma na geração da DLL em Delphi 1.0, muito pelo contrário, se incluir a cláusula cdecl ocorre um erro: "Este aplicativo executou uma operação ilegal" e sai da aplicação.

UTILIZANDO A DLL GERADA EM DELPHI 1.0 (16 BITS) NO VISUAL BASIC 3.0

Na seção de declarações, escreva:

Option Explicit

Declare Function Max Lib "Teste16.dll" (ByVal n1 As Integer, ByVal n2 As Integer) As Integer

Declare Sub Fruta Lib "Teste16.dll" (ByVal s1$, s2$, ByVal s3$, ByVal s4$)

Declare Sub TesteTipos Lib "Teste16.dll" (ByVal n3 As Integer, n4 As Integer, ByVal s5$, ByVal s6$)

Na seção onde forem chamadas as funções:

Dim str1 As String

Dim str2 As String

Dim str3 As String

Dim str4 As String

Dim str5 As String

Dim str6 As String

Dim naux As Integer

Text3.Text = Max(Text1.Text, Text2.Text)

Str1 = String(10, " ")

Str2 = String(10, " ")

Str3 = String(10, " ")

Str4 = String(10, " ")

Str5 = String(50, " ")

Str6 = String(50, " ")

 

Call Fruta(str1, str2, str3, str4)

Text4.Text = str1

Text5.Text = str2

Text6.Text = str3

Text7.Text = str4

Str6 = Text10.Text

 

Call TesteTipos(Text8.Text, naux, str6, str5)

Text9.Text = naux

Text10.Text = str5

Pontos importantes a destacar:

  • Certifique-se de que a DLL está em um diretório visível para o VB, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional. Caso contrário, você deverá declarar o path do diretório onde ela se encontra juntamente com seu nome na seção de declarações.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.
  • As variáveis auxiliares do tipo string têm que ser declaradas linha a linha, senão dá erro, (embora seja natural que se escreva Dim str1, str2, str3, str4, str5, str6 As String, isto não funciona).
  • Outro detalhe importantíssimo e inesquecível é a inicialização das variáveis string, com o tamanho do string retornado mais um. Caso isto não seja feito ocorrerá um erro de GPF.
  • É necessário passar os tipos string por valor (ByVal) mesmo que seja passagem por referência. O porquê disto está explicado no sexto ponto do item 7 do tópico Criando uma DLL em Delphi 1.0
  • Se na geração da DLL em Delphi 1.0 for incluída a cláusula cdecl, executa mas quando chama uma das funções da DLL mostra uma mensagem ‘Bad DLL calling convention’. Esta mensagem aparece também quando os parâmetros da funções não têm ByVal na frente, na declaração da função. Neste caso, às vezes até compila mas executa errado.

UTILIZANDO A DLL GERADA EM DELPHI 1.0 (16 BITS) NO VISUAL BASIC 4.0 (16 BITS)

Na seção de declarações do módulo, escreva: (importante criar um módulo.bas)

Option Explicit

Public Declare Function Max Lib "Teste16.dll" (ByVal n1 As Integer, ByVal n2 As Integer) As Integer

Public Declare Sub Fruta Lib "Teste16.dll" (ByVal s1$, s2$, ByVal s3$, ByVal s4$)

Public Declare Sub TesteTipos Lib "Teste16.dll" (ByVal n3 As Integer, n4 As Integer, ByVal s5$, ByVal s6$)

Na seção onde forem chamadas as funções:

Dim str1 As String

Dim str2 As String

Dim str3 As String

Dim str4 As String

Dim str5 As String

Dim str6 As String

Dim naux As Integer

Text3.Text = Max(Text1.Text, Text2.Text)

Str1 = String(10, " ")

Str2 = String(10, " ")

Str3 = String(10, " ")

Str4 = String(10, " ")

Str5 = String(50, " ")

Str6 = String(50, " ")

Call Fruta(str1, str2, str3, str4)

Text4.Text = str1

Text5.Text = str2

Text6.Text = str3

Text7.Text = str4

Str6 = Text10.Text

Call TesteTipos(Text8.Text, naux, str6, str5)

Text9.Text = naux

Text10.Text = str5

Pontos importantes a destacar:

  • Certifique-se de que a DLL está em um diretório visível para o VB, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional. Caso contrário, você deverá declarar o path do diretório onde ela se encontra juntamente com seu nome na seção de declarações.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.
  • As variáveis auxiliares do tipo string têm que ser declaradas linha a linha, senão dá erro, (embora seja natural que se escreva Dim str1, str2, str3, str4, str5, str6 As String, isto não funciona).
  • Outro detalhe importantíssimo e inesquecível é a inicialização das variáveis string, com o tamanho do string retornado mais um. Caso isto não seja feito ocorrerá um erro de GPF.
  • É necessário passar os tipos string por valor (ByVal) mesmo que seja passagem por referência. O porquê disto está explicado no sexto ponto do item 7 do tópico Criando uma DLL em Delphi 10
  • Conforme a tabela comparativa só é possível usar a DLL gerada em 16 bits sem a cláusula cdecl.
  • O código do VB 4.0 (16 bits) é o mesmo do código do VB 3.0, claro que não é possível abrir o projeto em VB 3.0 pois assim ele tenta fazer conversões de VBX para OCX. O modo mais prático, neste caso, é o Copy/Paste. Atentando para o detalhe que as declarações das funções e procedimentos da DLL devem ficar em um módulo.bas com a cláusula Public na frente.

UTILIZANDO A DLL GERADA EM DELPHI 1.0 (16 BITS) NO PRÓPRIO DELPHI 1.0 (16 BITS)

Para utilizar uma função ou procedimento de uma DLL, você precisa primeiro declarar a rotina de interface dentro da sua aplicação e então dispará-la em alguma parte do seu código.

Na seção de implementação ou interface, escreva as declarações das funções:

function Max(X, Y: Integer): Integer; external 'Teste16.dll' index 1;
procedure Fruta(a, b, c, d: PChar); external 'Teste16.dll' index 2;
procedure TesteTipos (x1:integer; var x2:integer; y1, y2:pchar); external 'Teste16.dll' index 3;

Na seção onde forem chamadas as funções, você deve criar, escreva:

var

str1, str2, str3, str4, str5, str6: pchar;

naux:integer;

begin

Edit3.Text := inttostr(Max(strtoint(Edit1.Text), strtoint(Edit2.Text)));

str1 := StrAlloc(50); // Tentei atribuir str1 := ' '; e não funcionou

str2 := StrAlloc(50);

str3 := StrAlloc(50);

str4 := StrAlloc(50);

Fruta(str1, str2, str3, str4);

Edit4.Text := str1;

Edit5.Text := str2;

Edit6.Text := str3;

Edit7.Text := str4;

str5 := StrAlloc(50);

StrPCopy(str5,Edit10.Text);

Str6 := StrAlloc(50);

TesteTipos(strtoint(Edit8.Text), naux, str5, str6);

Edit9.Text := inttostr(naux);

Edit10.Text := StrPas(str6);

end;

Pontos importantes a destacar:

  • Certifique-se de que a DLL está em um diretório visível para o Delphi, isto é, ela pode estar no mesmo diretório do seu arquivo de projeto ou em qualquer diretório que esteja no path do sistema operacional.
  • Se você estiver chamando uma rotina que possua parâmetros, você precisará saber exatamente o nome, os tipos, e a ordem dos parâmetros. Caso contrário, você não conseguirá executá-la.
  • Para um módulo (unit) utilizar um procedimento ou função de uma DLL, você deve importar o procedimento ou função utilizando uma declaração external. O Object Pascal importa procedimentos e funções de duas maneiras: por nome e por índice. Quando não houver uma cláusula index ou name especificada, a procedure ou função é importada por nome. O nome usado é o mesmo do identificador do procedimento ou função, com as mesmas letras e capitalização. Por exemplo:

Function Soma(x, y, x:real):real; external ‘minhadll.dll’;

Function Soma(x, y, x:real):real; external ‘minhadll.dll’ name ‘teste’;

Function Soma(x, y, x:real) real; external ‘minhadll.dll’ index 1;

Quando houver uma cláusula name, o procedimento ou função podem ser importados por um nome diferente do seu identificador. Finalmente, quando houver uma cláusula index, o procedimento ou função será importado por um ordinal. Quando empregamos este tipo de estratégia, reduzimos o tempo de chamada do módulo, uma vez que o nome do procedimento não precisa ser localizado na tabela de nomes da DLL.

  • As funções da DLL foram declaradas na seção implementation, isso fará com que apenas esta unit consiga usar estas funções. Se quisesse deixá-las públicas, as mesmas deveriam ser declaradas na seção interface. Isto é apenas um conceito do Delphi, não tem nada haver com o fato de ser dll ou não.
  • Se fosse usada a cláusula cdecl na geração da dll, ela seria obrigatória também aqui, no programa que vai chamá-la. Vale lembrar que se fosse usar esta dll apenas no Delphi, esta cláusula seria totalmente dispensável. Para entender melhor esta cláusula veja o 6o ponto no item 7 do tópico "Como gerar DLL em Delphi", e também a tabela com resultados da utilização da DLL em vários ambientes que basicamente comprova que a mesma cláusula usada na geração deve ser usada na utilização da DLL, e que não é possível usar DLL gerada em ambiente 32 bits em ambiente 16 bits.
  • É estritamente necessário usar variáveis para passagem de parâmetro quando o tipo no pascal for Pchar e elas devem ser inicializadas com conteúdos do tamanho do valor que será passado mais um, no mínimo. Se não inicializar estas variáveis ocorre um erro de violação de memória. Nas inicializações feitas usando a função StrAlloc, não adianta fazer uma alocação usando comando de atribuição direta.
  • Para trabalhar com Pchar e String no Delphi utiliza-se as funções StrPCopy e StrPas para fazer conversões de um tipo para outro.

TABELA COM RESULTADOS DA UTILIZAÇÃO DE DLLs NOS VÁRIOS AMBIENTES

  1. Na compilação ocorre um erro (Error... Cannot load or find external library (or one of its components). Check to ensure the path and filename are correct and that all required libraries are available).
  2. Executa, mas trava quando chama a segunda função da DLL; mesmo assim retorna um valor maluco para a primeira função, o maior número entre 12 e 56 retorna 25.
  3. Executa. A 1ª função funciona, a 2ª os parâmetros vêm invertidos (goiaba, melancia, abacaxi e morango) e a 3ª trava pois tenta somar puchar já que os parâmetros são invertidos.
  4. Executa, mas quando chama alguma função da DLL ocorre um erro (Error in loading DLL)
  5. Executa, mas quando chama alguma função da DLL ocorre um erro (Run-time error ‘48’ – Error in loading DLL)
  6. Na compilação ocorre um erro (constants, fixed-length strings, arrays, and Declare statements not allowed as Public members of class or form modules). Foi resolvido depois que foi colocado o código das declarações da DLL em um módulo.bas.
  7. Executa, mais ou menos, porque a 2ª e 3ª funções funcionam corretamente mas a 1ª função retorna sempre o primeiro parâmetro como sendo o maior. Sem a alteração do código passou a funcionar.
  8. Executa, mas quando chama alguma função da DLL ocorre um erro (Run-time error ‘49’ – Bad DLL calling convention).
  9. Trava na execução. Foi necessário resetar a máquina.
  10. Na execução ocorre um erro (Project Teste16t.exe raised exception class EGPFault with message ‘General protection fault in module Teste16.DLL at 0007:099F’. Process stopped)
  11. Executa, mas quando chama alguma função da DLL ocorre um erro (Error creating process: Internal windows error (15))
  12. Executa, mas quando chama alguma função da DLL ocorre um erro (O arquivo c:\users\lisiane\Teste16.dll parece estar corrompido. Reinstale o arquivo e tente novamente.). Depois aparece outra mensagem (Unable create process).
  13. Project Teste32tes.exe raised exception class EaccessViolation with message ‘Access violation at address 0043498D. Read of address FFFFFFFF’) quando executa a 3ª função. A 1ª função funciona corretamente; a 2ª função, vêm invertidos os parâmetros (goiaba, melancia, abacaxi e morango)
  14. Executa, mas quando chama alguma função da DLL ocorre um erro (Access violation at 0x495841: read of address 0x495841 01 00 00 00 F4 F9 76 00 F0 F9 76 00).
  15. Executa, mas quando chama alguma função da DLL ocorre um erro (Access violation at 0x412058 read of address 0xFFF02b28 FF 51 40 8B 45 FC 8B E5 5D C2 04 00).
  16. Executa, mas quando chama alguma função da DLL ocorre um erro (Access violation at 0x10291 read of address 0xFFFFFFFF 26 80 7C 01 23 74 50 1E 8E 5E 0C 8B).
  17. Executa, mas quando chama alguma função ocorre um erro de GPF (mesmo no windows 95 com tela azul e tudo) com a mensagem (Uma exceção fatal 0E ocorreu em 0137:BFF9A07C. O aplicativo atual será fechado). Foi necessário resetar a máquina.
  18. Executa, mas ocorre um erro com a mensagem (Este aplicativo executou uma operação ilegal) e sai da aplicação.

Geração DLLè

Utilização no
ê

Delphi1.0
(16 bits)

Delphi 1.0
(16 bits)
cdecl

Delphi2.0
(32 bits)

Delphi2.0
(32 bits)
stdcall

Delphi 2.0
(32 bits)
cdecl

Delphi 2.0(32 bits)
pascal

Delphi2.0
(32 bits)register

Sql Windows (16)

Ok

18

1

1

1

1

1

Centura (32)

1

1

2

Ok

Ok

3

2

VB 3.0 (16)

Ok

8

4

4

4

4

4

VB 4.0 (16)

Ok

8

5

5

5

5

5

VB 4.0 (32)

5

5

8

+/- Ok 6, 7

8

9

8

Delphi 1.0 (16)

Ok

10

11

11

11

11

11

Delphi 1.0 cdecl

19

Ok

11

11

11

11

11

Delphi 2.0 (32)

12

12

Ok

16

9

9

Ok

Delphi 2.0 stdcall

12

12

14

Ok

Ok

13

14

Delphi 2.0 cdecl

12

12

14

15

Ok

15

14

Delphi 2.0 pascal

12

12

14

13

13

Ok

14

Delphi 2.0 register

12

12

Ok

16

9

17, 18

Ok

Para melhor visualizar esta tabela clique aqui.