none
Dll Delphi com assinaturas do tipo record RRS feed

  • Pergunta

  • Boa tarde pessoal,

    Estou quebrando a cabeça ao usar uma dll desenvolvida em Delphi com o C#.

    Já olhei todos os tópicos com com o uso de dll delphi em C#, tentei aplicar todos as instruções, porém sem o resultado esperado.

    Acredito que o problema esteja na assinatura da função em delphi, onde é usado dois parâmetros do tipo "record", para usar a função eu tentei simular o "record" do Delphi com uma classe e com uma struct(não sei qual seria o mais adequado), usando os dois eu passo pela chamada da funçao sem erro algum, porém não me é retornado os valores esperados, e as propriedades da classe e da struct passam pela função e permanecem com os mesmos valores setados na inicialização das mesmas.

    Abaixo segue a assinatura da função em Delphi:

    function recebeMarcacoes(var marcacao: TMarcacao; var controle TControle): boolean; stdcall

    As estruturas de TMarcacao e TControle são as seguintes:

    TMarcacao = record                       |             TControle = record

    nsr:LongWord;                              |             total: Word;

    dia: byte;                                     |             start: boolean;

    mes: byte;                                   |             erro: Shortint;

    ano: word;                                   |             porta: byte;

    hora: byte;                                  |             endereco: string[15];

    minuto: byte;       

    cont: LongWord;                                    

     

    Em C# eu tentei usar uma estruct assim:

      public struct TMarcacao
    
      {
    
       public int nsr;
    
       public IntPtr cont;
    
       public int dia;
    
       public int mes;
    
       public int ano;
    
       public int hora;
    
       public int minuto;
    
      }
    
      public struct TControle
    
      {
    
       public bool start;
    
       public int total;
    
       public int atual;
    
       public int erro;
    
       public int porta;
    
       public string endereco;
    
      }
    
    

    Para usar a função eu faço assim:

    [DllImport("authotelcom.dll", CallingConvention = CallingConvention.StdCall,CharSet=CharSet.Ansi)]
    
      public static extern bool recebeMarcacoesTCP(MarcacaoPtr marc, ControlePtr ctrl);
    
    
    
    
    
    
       TMarcacao marcacao;
    
       TControle controle;
    
    
    
       marcacao.nsr = 20;
    
       marcacao.ano = 0;
    
       marcacao.cont = Marshal.AllocHGlobal(255);
    
       marcacao.dia = 0;
    
       marcacao.hora = 0;
    
       marcacao.mes = 0;
    
       marcacao.minuto = 0;
    
       marcacao.pis = "";
    
    
    
    
    
       controle.start = true;
    
       controle.endereco = strEndereco;
    
       controle.s_tipo = 2;
    
       controle.porta = 1001;
    
       controle.atual =0;
    
       controle.total = 0;
    
       controle.erro = 0;
    
    
    
    
    bool retorno = false; //declaro essa variavel bool, para receber o retorno
    
    retorno = recebeMarcacoesTCP(marcacao,controle);
    
    

    O código passa sem erros, o retorno é true, mas quando vou visualizar as posições da struct não retorna valores nenhum.

    A mesma estrutura da struct eu simulei com uma classe, criando as propriedades com os mesmo nomes e tipo de dados, e o comportamento é idêntico, roda sem erro, retorna true mas as propriedades ficam com os mesmo valores de quando foram inicializadas.

     

    A dll em Delphi não foi feita por mim, apenas me foi fornecida com a documentação da mesma.

     

    Se alguem tiver alguma luz para que eu possa seguir, ficarei grato.

    Abraços.

     

    Schaurich.

    • Editado Schaurich domingo, 1 de agosto de 2010 01:37
    sábado, 31 de julho de 2010 18:52

Respostas

Todas as Respostas

  • Esta class foi desenvolvida em versao Delphi? Sabes dizer?
    Just Be Humble Malange!
    sábado, 31 de julho de 2010 19:08
    Moderador
  • Segundo a documentação os exemplos estão em Delphi 5, então suponho que seja em nessa versão, mas vou enviar um e-mail ao desenvolvedor da dll para ver se é essa mesma, e posto a resposta.

     

    Abraços

    Schaurich.

    sábado, 31 de julho de 2010 19:16
  • Segundo a documentação os exemplos estão em Delphi 5, então suponho que seja em nessa versão, mas vou enviar um e-mail ao desenvolvedor da dll para ver se é essa mesma, e posto a resposta.

     

    Abraços

    Schaurich.

    So aversao 7 e compativel com .Net. E tudo que eu sei.
    Just Be Humble Malange!
    sábado, 31 de julho de 2010 19:20
    Moderador
  • Segundo a documentação os exemplos estão em Delphi 5, então suponho que seja em nessa versão, mas vou enviar um e-mail ao desenvolvedor da dll para ver se é essa mesma, e posto a resposta.

     

    Abraços

    Schaurich.

    Tentar setar como verdadeiro:

    bool retorno = true; //declaro essa variavel bool, para receber o retorno
    retorno = recebeMarcacoesTCP(marcacao,controle);


    Just Be Humble Malange!
    sábado, 31 de julho de 2010 19:22
    Moderador
  • Mesmo setando a boolean como True, a função é executada sem erros e não retorna nada.

    Abraços,

    Schaurich

    sábado, 31 de julho de 2010 20:11
  • Schaurich,

     

    Veja se isso pode te ajudar: O processo fala de DLL C++, mas o processo é bem parecido para DLL Delphi.

    As questões de passagem de record seguem a mesma idéia, compatibilizando com uma struct em C#.

     

    http://ericlemes.com/2010/06/23/dotnet-interop-pt/

     

    Abraço,

    Eric

    segunda-feira, 2 de agosto de 2010 12:49
  • Olá,

    Veja se isto te ajuda:

    http://stackoverflow.com/questions/2936851/c-calling-ext-dll-function-containing-delphi-variant-record-parameter


    André Alves de Lima
    Visite o meu site: http://andrealveslima.spaces.live.com
    Me siga no Twitter: @andrealveslima


    André, já tinha visto esse fórum e tinha aplicado as dicas, mas mesmo assim não deu certo.

    Com as dicas desse fórum eu criei as Structs sequenciais, e fiz dois testes.

    O primeiro foi declarando os tipos de dados de forma compativel tanto para as variaveis de parâmetros quanto para as variaveis de retorno, e o resultado foi que o código passa pela função, não gera exceção, porém não é retornado valor nenhum.

     

    O segundo foi declarando as varivais de retorno com o tipo IntPtr para alocar memória não-gerenciada, e o resultado foi o mesmo, o código passa pela função, não gera exceção e ao converter as variaveis IntPtr para string os valores são os mesmos de antes de executar a função.

     

    Creio que seja incompatibilidade da versão delphi, que me foi confirmada hoje pela manhã pelo desenvolvedor da dll, que é a 5.

     

    Se mais alguem tiver alguma dica para que que possa tentar, ficarei grato.

     

    Abraços.

    Schaurich.

    segunda-feira, 2 de agosto de 2010 13:01
  • Schaurich,

     

    Strings desse jeito: endereco: string[15];

    Você provavelmente não vai conseguir passar. O jeito que o delphi representa as strings na memória não é mto comum. 

    No caso, vc vai ter que usar PChars, usando *char e o número de bytes passados, no melhor estilo C/C++ "clássico".

    Mesmo de C++ para Delphi acredito que você teria problemas em consumir essa DLL (talvez até de Delphi pra Delphi).

     

    Abraço,

    Eric

    segunda-feira, 2 de agosto de 2010 13:15
  • Schaurich,

     

    Strings desse jeito: endereco: string[15];

    Você provavelmente não vai conseguir passar. O jeito que o delphi representa as strings na memória não é mto comum. 

    No caso, vc vai ter que usar PChars, usando *char e o número de bytes passados, no melhor estilo C/C++ "clássico".

    Mesmo de C++ para Delphi acredito que você teria problemas em consumir essa DLL (talvez até de Delphi pra Delphi).

     

    Abraço,

    Eric


    Eric, essa dll em Delphi não foi desenvolvida por mim.

    Olhei seu post referenciado na resposta anterior e já tentei todas as dicas que tem nele, através de outros fóruns e posts.

    O interessante que as demais funções que existem na dll, onde os parametros são "abertos"(int, pchar, byte, etc...) eu consigo executar passando parâmetros e obtendo retornos, porém nas funções onde usam o tipo "record" não tem jeito de obter retorno e não sei se os parâmetros que envio, se estão sendo entendidos pela dll Delphi.

    Eu não sei nada de Delphi, portanto não sei qual a compatibilidade da "struct" para "record" com as versões Delphi 5 e Framework 3.5.

     

    Obrigado a todos pelas dicas.


    Admir Schaurich
    segunda-feira, 2 de agosto de 2010 13:38
  • Admir,

    No caso da struct funciona sim... num outro post, fizemos um teste com um float[] e tudo passa direitinho. A questão é realmente saber o que está chegando do outro lado. Talvez implementar uma "casca" da DLL em C++ e debugar o que está sendo passado seja um caminho.

    Eu acho que o que vai enroscar é a string mesmo, já que o compilador do Delphi usa uma representação dele (eu nõa sei qual é, mas não é um PChar).

    Neste caso é bem provável que você não consiga fazer a compatibilização. Mesmo que fosse com C++.

    O outro post que fala sobre passar structs é este:

    http://social.msdn.microsoft.com/Forums/pt-BR/vscsharppt/thread/0f3a725d-2876-4372-bb7d-5f616b3fbe4e/

     

    Abraço,

    Eric

    segunda-feira, 2 de agosto de 2010 16:55
  • Admir,

     

    Outra questão é o alinhamento. Você pode anotar a struct ([StructLayout(LayoutKind.Sequential)] e outras opções) para compatibilizar questões de alinhamento.

    Isso depende de como a DLL foi compilada no Delphi.

    Esse link explica um pouco do conceito no Delphi: http://www.delphibasics.co.uk/RTL.asp?Name=$Align&ExpandCode1=Yes

    Esse outro link explica um pouco do conceito de alinhamento em geral: http://en.wikipedia.org/wiki/Data_structure_alignment

     

    Neste caso sem saber mais informações sobre como a compilação foi gerada no Delphi é realmente difícil.

     

    Abraço,

    Eric

    segunda-feira, 2 de agosto de 2010 17:04
  • Admir,

     

    Outra questão é o alinhamento. Você pode anotar a struct ([StructLayout(LayoutKind.Sequential)] e outras opções) para compatibilizar questões de alinhamento.

    Isso depende de como a DLL foi compilada no Delphi.

    Esse link explica um pouco do conceito no Delphi: http://www.delphibasics.co.uk/RTL.asp?Name=$Align&ExpandCode1=Yes

    Esse outro link explica um pouco do conceito de alinhamento em geral: http://en.wikipedia.org/wiki/Data_structure_alignment

     

    Neste caso sem saber mais informações sobre como a compilação foi gerada no Delphi é realmente difícil.

     

    Abraço,

    Eric


    Eric,

    Eu já estava declarando a Struct como "sequential", veja se pode me dar uma luz quanto a compatibilidade dos tipos de dados do record para a struct.

    Abaixo a estrutura dos Records.

    TMarcacao = record 
      nsr: LongWord;              
      cont: LongWord;
      pis: string[15];
      dia: byte;
      mes: byte; 
      ano: word;
      hora: byte;
      minuto: byte;
    end;
    
    TControle = record
      total: Word;
      atual: Word;
      start: boolean;
      erro: Shortint;
      porta: byte;
      s_tipo: byte;
      endereco: string[15];
    end;

    Aqui a última estrutura das Structs, na frente de cada variavel informo se é retorno ou parâmetro.

        [StructLayout(LayoutKind.Sequential)]
        public struct TMarcacao
        {
          public int nsr; // Parametro
          public IntPtr cont; // Parametro -> Se eu usar qualquer tipo diferente de IntPtr, quando executo a função me retorna "tentativa de gravação em memória protegida"
          public String pis; // Retorno
          public byte dia; // Retorno
          public byte mes; // Retorno
          public byte ano; // Retorno
          public byte hora; // Retorno
          public byte minuto; // Retorno
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct TControle
        {
          public int total; // Retorno
          public int atual; // Retorno
          public bool start; // Parametro
          public int erro; // Retorno
          public int porta; // Parametro
          public byte s_tipo; // Parametro
          public string endereco; // Parametro
        }

     Estas são as estuturas atuais, onde passa sem gerar exception. Lembrando apenas que já fiz as mesmas structs mudando para IntPtr em todos os "retornos".

    Desde já obrigado pela ajuda.

    Abraços

     


    Admir Schaurich
    segunda-feira, 2 de agosto de 2010 18:27
  • Verifica esses detalhes:

    Utiliza class, não struct, class herda de objeto e portanto é mais maleavel...

    Na assinatura do seu metodo usa o IntPrt e não o struct diretamente... pq normalmente no Delphi as chamadas de dll externas são passados por ponteiros e não por estruturas...

    para passar o valor do IntPrt para a classe (estrutura de dados) usa o Marshal.PtrToStructure

    Algo assim:

    public static extern bool recebeMarcacoesTCP(ref IntPtr marc, ref IntPtr ctrl);
    
    var marcRec = new TMarcacao();
    var ctrlRec = new TControle();
    
    IntPtr marcPtr = Marshal.AllocHGlobal(Marshal.SizeOf(marcRec));
    IntPtr ctrlPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ctrlRec));
    try {
      if (recebeMarcacoesTCP(ref marcPtr, ref ctrlPtr)) {
        marcRec = (TMarcacao)Marshal.PtrToStructure(marcPtr, typeof(TMarcacao));
        ctrlRec = (TControle)Marshal.PtrToStructure(ctrlPtr, typeof(TControle));
      }
    } finally {
      Marshal.FreeHGlobal(marcPtr);
      Marshal.FreeHGlobal(ctrlPtr);
    }
    

    Mais detalhes:

    http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

     

    P.S.: coloca os paramentros como ref para poderem retornar algum valor... como default no C# a passagem é por valor



    What would Brian Boitano do ?
    ((2B || !2B) is Question) ? Se não da certo como voce esta fazendo... Tente fazer de um jeito totalmente diferente....
    segunda-feira, 2 de agosto de 2010 19:06
    Moderador
  • Verifica esses detalhes:

    Utiliza class, não struct, class herda de objeto e portanto é mais maleavel...

    Na assinatura do seu metodo usa o IntPrt e não o struct diretamente... pq normalmente no Delphi as chamadas de dll externas são passados por ponteiros e não por estruturas...

    para passar o valor do IntPrt para a classe (estrutura de dados) usa o Marshal.PtrToStructure

    Algo assim:

    public static extern bool recebeMarcacoesTCP(ref IntPtr marc, ref IntPtr ctrl);
    
    
    
    var marcRec = new TMarcacao();
    
    var ctrlRec = new TControle();
    
    
    
    IntPtr marcPtr = Marshal.AllocHGlobal(Marshal.SizeOf(marcRec));
    
    IntPtr ctrlPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ctrlRec));
    
    try {
    
     if (recebeMarcacoesTCP(ref marcPtr, ref ctrlPtr)) {
    
      marcRec = (TMarcacao)Marshal.PtrToStructure(marcPtr, typeof(TMarcacao));
    
      ctrlRec = (TControle)Marshal.PtrToStructure(ctrlPtr, typeof(TControle));
    
     }
    
    } finally {
    
     Marshal.FreeHGlobal(marcPtr);
    
     Marshal.FreeHGlobal(ctrlPtr);
    
    }
    
    
    
    

    Mais detalhes:

    http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

     

    P.S.: coloca os paramentros como ref para poderem retornar algum valor... como default no C# a passagem é por valor



    What would Brian Boitano do ?
    ((2B || !2B) is Question) ? Se não da certo como voce esta fazendo... Tente fazer de um jeito totalmente diferente....

    Rui,

    Já tinha tentado usar classe ao invés de struct, mas não com o Marshal. Tentei agora aplicar da forma como descreveu mas não tive sucesso, pois algumas propriedades da estruct ou classe são passados parametros, posso estar errado, mas acredito que ao usar um ponteiro IntPtr na assinatura da função ao invés da estrutura, perco os valores que quero passar para a função(me corrija se estiver errado).

    Antes apenas usando a struct ou classe, a função me retornava True, mas não trazia as propriedades preenchidas, agora usando o IntPtr a função permanece com False e como esperado,  as propriedades voltam vazias.

     

    Fiz das duas formas usando o IntPtr, com struct e class.

     

    Obrigado pela ajuda de todos, mas sigo não conseguindo fazer funcionar.

     

     


    Admir Schaurich
    terça-feira, 3 de agosto de 2010 14:20
  • Bem normalmente a ideia de usar ponteiro é passar so o endereço de memoria onde esta o registro... portanto vc nao deveria perder nada em questao de dados...

    vc não se esqueceu de preencher as propriedades das estruturas e setar o ponteiro usando o Marshal.StructureToPtr ande de fazer a chamada ao metodo... certo ?

    No exemplo q montei levei em consideração q vc não estava passando nada como parametro so recebendo...

     

    Por final tem que ver o seu codigo em Delphi... se o seu metodo esta recebendo os paramentro por valor ou por referencia... pq se for por valor... esquece... não vai retornar nada nunca mesmo...

     

    P.S. desculpa... ta numa cor tão clarinha que não tinha visto... realmente ta como referencia mesmo no delphi...


    What would Brian Boitano do ?
    ((2B || !2B) is Question) ? Se não da certo como voce esta fazendo... Tente fazer de um jeito totalmente diferente....
    terça-feira, 3 de agosto de 2010 16:21
    Moderador
  • Versao inferior a delphi 7 nao e compativel com .Net.

     


    Just Be Humble Malange!
    terça-feira, 3 de agosto de 2010 16:35
    Moderador
  • Bem normalmente a ideia de usar ponteiro é passar so o endereço de memoria onde esta o registro... portanto vc nao deveria perder nada em questao de dados...

    vc não se esqueceu de preencher as propriedades das estruturas e setar o ponteiro usando o Marshal.StructureToPtr ande de fazer a chamada ao metodo... certo ?

    No exemplo q montei levei em consideração q vc não estava passando nada como parametro so recebendo...

     

    Por final tem que ver o seu codigo em Delphi... se o seu metodo esta recebendo os paramentro por valor ou por referencia... pq se for por valor... esquece... não vai retornar nada nunca mesmo...

     

    P.S. desculpa... ta numa cor tão clarinha que não tinha visto... realmente ta como referencia mesmo no delphi...


    What would Brian Boitano do ?
    ((2B || !2B) is Question) ? Se não da certo como voce esta fazendo... Tente fazer de um jeito totalmente diferente....


    Está com a cor clarinha porque foi meu primeiro post, e me perdi na questão da formatação, e a cada vez que eu editava, ele mudava a font a cor, até que desisti e deixei daquela forma, peço desculpas por ter ficado tão pequeno(não foi proposital).

    Quanto ao caso de passar os valores eu não esqueci, fiz tudo direitinho, instanciei, setei as propriedades, setei o ponteiro e depois eu chamei o método.

    Creio que seja o que Malange me alertou antes, incompatibilidade do Delphi 5 com o .Net.

     

    Ps:O código em Delphi não foi feito por mim, é uma dll fornecida para que eu possa desenvolver o soft de comunicação com o hard da empresa. Não tenho acesso ao código fonte dela, senão já teria mudado as assinaturas dos métodos.

    Obrigado pela força.


    Admir Schaurich
    terça-feira, 3 de agosto de 2010 20:11
  • Versao inferior a delphi 7 nao e compativel com .Net.

     


    Just Be Humble Malange!


    Já falei com o desenvolvedor da dll para ver a possibilidade de migrar ela para uma versão superior a 5.

    Obrigado pela força.


    Admir Schaurich
    terça-feira, 3 de agosto de 2010 20:13
  • Olha... pede para ele fazer logo em ActiveX (COM+) ai fica muito mais facil...


    What would Brian Boitano do ?
    ((2B || !2B) is Question) ? Se não da certo como voce esta fazendo... Tente fazer de um jeito totalmente diferente....
    quarta-feira, 4 de agosto de 2010 15:52
    Moderador
  • Boa tarde,

    Teria como disponibilizar a documentação da dll ou código funcional , queria desenvolver um programa para enviar avisos para quem não registrou o ponto em determinado horário pre-estabelecido 

    sexta-feira, 6 de abril de 2018 18:35