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 DLLs
- 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:
- Opção do
menu File/New e selecione a opção DLL;
- Opção do
menu File/Save Project As e altere o nome Project1
para Teste32 (coloque em um diretório separado);
- Opção do
menu File/New e selecione a opção Unit;
- Opção do
menu File/Save As e altere o nome para untFuncoes;
- 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.
- 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.
- 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:
- Opção do
menu File/New Project;
- Digite
a palavra library sobre a palavra program;
- Clique
no botão Pasta (Remova arquivos do projeto) Form1
se sua DLL não vai usar formulário;
- Opção do
menu File/Save Project As e altere o nome Project1
para Teste16 (coloque em um diretório separado);
- Opção do
menu File/New Unit;
- Opção do
menu File/Save as e altere o nome para untFun;
- 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.
- 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.
- 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)
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 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 dlls, 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
- 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).
- 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.
- 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.
- Executa,
mas quando chama alguma função da DLL ocorre um erro (Error
in loading DLL)
- Executa,
mas quando chama alguma função da DLL ocorre um erro (Run-time
error 48 Error in loading DLL)
- 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.
- 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.
- Executa,
mas quando chama alguma função da DLL ocorre um erro (Run-time
error 49 Bad DLL calling convention).
- Trava na
execução. Foi necessário resetar a máquina.
- 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)
- Executa,
mas quando chama alguma função da DLL ocorre um erro (Error
creating process: Internal windows error (15))
- 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).
- Na execução
ocorre um erro (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)
- 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).
- 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).
- 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).
- 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.
- 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.
lisiane@celepar.gov.br

|