Como variáveis do tipo ponteiro são muito comuns no C/C++, ficou faltando mostrar como lidar com uma função que tivesse parâmetros desse tipo. Veja abaixo a declaração em C de uma função existente numa biblioteca:
int GetName (unsigned char * pName);
O Delphi também lida com ponteiros, sendo o tipo PChar correspondente ao unsigned char *. Supondo que a DLL seja BIBLIO.DLL e a função esteja exportada como _GetName, o mapeamento da função em Delphi ficaria:
function GetName (pName: PChar): Integer;cdecl;
external 'BIBLIO.DLL' name '_GetName';
external 'BIBLIO.DLL' name '_GetName';
Normalmente, esse tipo de chamada supõe que o ponteiro passado como parâmetro já esteja alocado antes de ser usado. Então, para utilizar a função, devemos antes fazer a alocação e, depois de usar o retorno, devolver a memória alocada:
var lName: PChar;
begin
{ Aloca espaço para até 50 caracteres. A documentação da função deve indicar qual o tamanho necessário.}
GetMem (lName, 50);
if (GetName (lName) = 0) then begin
{ Usa aqui o nome contido em lNome }
end;
{ Devolve a memória alocada }
FreeMem (lName);
end;
begin
{ Aloca espaço para até 50 caracteres. A documentação da função deve indicar qual o tamanho necessário.}
GetMem (lName, 50);
if (GetName (lName) = 0) then begin
{ Usa aqui o nome contido em lNome }
end;
{ Devolve a memória alocada }
FreeMem (lName);
end;
Outra dúvida recorrente foi quanto ao uso do cdecl: o que é isso exatamente ? Esse recurso das linguagens de programação chama-se formalmente Convenção de chamada. Isto afeta as chamadas de funções e procedures de duas maneiras: como é feita a passagem de parâmetros para a função e de quem é a responsabilidade de fazer a limpeza da "pilha de chamadas" (stack) quando terminar a chamada à função. O quadro abaixo mostra as convenções mais comuns:
Convenção | Limpeza | Parâmetros |
cdecl | Rotina chamadora | Passados da direita para a esquerda usando o stack. Comum em bibliotecas desenvolvidas em C/C++ |
pascal | A própria rotina | Passados da esquerda para a direita usando o stack. Usada pela linguagem Pascal. |
stdcall | A própria rotina | Passados da direita para a esquerda usando endereços múltiplos de 4 bytes no stack. Usada principalmente em funções da API Win32. |
fastcall | A própria rotina | Os 2 primeiros parâmetros do tipo DWORD (ou menores) são passados em registradores. Os demais são passados da direita para a esquerda usando o stack. |
register | A própria rotina | Passados em registradores, quando possível. Os que não forem possíveis de passar em registradores são passados da esquerda para a direita usando o stack. |
safecall | A própria rotina | Passados da direita para a esquerda usando o stack. Usada normalmente em métodos de interface COM, tratando exceções que sejam levantadas pela interface. |
E como é que eu sei qual das convenções deve ser usada para mapear uma determinada função ? Bem, se a documentação da função não for explícita quanto a isso, será preciso dar uns chutes, fazer algumas experiências... O normal é que funções em C/C++ usem o tipo cdecl e que funções desenvolvidas com o Delphi/Pascal usem o tipo pascal ou register. E bibliotecas que sigam o padrão da Microsoft normalmente usam o tipo stdcall.
Ainda sobrou um assunto: como carregar DLLs dinamicamente e como declarar os ponteiros para funções corretamente para usar nesse cenário. Falo disso num próximo post.
9 comentários :
Tudo bem mestre...
Estou com uma função em uma DLL feita em C++ com a variável "const unsigned char *data" e não consigo fazer ela funcionar no delphi. Outras funcções desta DLL eu já consegui.
Pode me dar uma ajuda, abraço.
Rafael
O tipo de dado const unsigned char* do C/C++ deveria ser mapeado sem problemas para PChar no Delphi. A palavra const indica, a grosso modo, que o endereço apontado pelo parâmetro com esse tipo não poderá ser modificado.
Se usou esse mapeamento e não funcionou, acredito que o problema seja outro. Talvez relacionado com a forma de fazer a chamada (cdecl, fastcall, etc.). Se não conseguir encontrar, envie mais detalhes para o email do blog; por exemplo, a assinatura da função ou documentação dela, se tiver.
Tudo bem Mestre?
O tipo de dados que usei foi uma array de bytes e a chamada foi a stdcall e deu certo. Aconteceu outra coisa também, como essa dll é para comunicação serial, eu estava alocando a serial de forma errada.
Grande abraço e obriga pela ajuda.
Olá!!
Tenho uma DLL que foi feita em C++ onde existe uma função que está declarada da seguinte forma:
teste(const char* e).
estou tentando chamar esse método na string, mas a dll não está retornando os dados que deveria, ela está retornando valores vazios.
Em delphi, a única maneira que consegui executá-la foi a seguinte:
function teste(TESTE:String):string; safecall; external 'teste.dll' name'getteste';
Caso tenha alguma sujestão do que pode estar ocorrendo, por favor responda.
Desde já agradeço a atenção.
Você tem o projeto dessa DLL ? Certifique-se que ela foi compilada usando mesmo o SAFECALL como convenção de chamada. Se não foi, modifique a declaração no Delphi para refletir a convenção correta.
O ideal é deixar explícito na declaração do C++ qual é o tipo de chamada pra evitar que mudanças no projeto façam com que programas usando a DLL deixem de funcionar repentinamente.
De qq modo, em Delphi vc mapeou o tipo CONST CHAR* do C++ para STRING mas eles não são compatíveis. O correto é mesmo o PCHAR, como mostra o post.
A dll que estou executando está no padrão UTF, acho que foi por isso que eu só consegui executá-la dessa forma, mas não consigo pegar os resultados dessa DLL.
poderia me dar alguma sugestão sobre isso, como eu poderia executá-la?
Ela foi programada em C++.
O que exatamente vc quer dizer com "a biblioteca está no padrão UTF" ? Isso normalmente diz respeito ao conjunto de caracteres que um texto é capaz de lidar e não deveria influenciar a chamada de funções em si.
Vc não consegue fazer a chamada trocando o STRING por PCHAR no Delphi e seguindo as instruções do post para alocar a memória que será passada para a função ? Isso dá alguma mensagem de erro ?
Desculpe a demora na resposta, mas é o seguinte, essa DLL foi programada em C++ só que para ser lida por um sistema desenvolvido em power builder 12 que utiliza os padrões UTF, portando ao tentar executar essa DLL em delphi a única maneira disso acontecer foi da seguinte maneira: teste(TESTE:String):string; safecall; external 'teste.dll' name'getteste';
Em C++ o pessoal chamou ela da seguinte forma:Function string teste(string teste) Library "teste.dll" ALIAS FOR "getteste;ansi".
alguma sugestão de como chamar essa DLL em Delphi parecido com a function em c++?
Tem que ver como exatamente foi criada a função no C++ para determinar como declará-la e usá-la no Delphi. Se ela está mesmo declarada como teste(const char* e), não faz sentido tentar usá-la em Delphi passando uma variável do tipo string.
Se puder, envie o header C++ com a declaração da função e a DLL onde ela está compilada para o email aqui do Balaio pra eu dar uma olhada.
[]s
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.