Pergunta

Estou referenciando uma DLL em meu projeto C# da seguinte maneira:

[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
           CharSet = CharSet.Ansi)]

        public static extern void FeeCalculation(string cin, string cout, string flimit,
            string frate, string fwindow, string fincrement, string fbird, 
            string fparameter, string fvalidation, string fcoupon);

A função FeeCalculation é exportada da seguinte forma na DLL:

extern "C" __declspec(dllexport) void __stdcall FeeCalculation(char *cin, 
char *cout, char *flimit, char *frate,
char *fwindow, char *fincrement, char *fbird,
char *fparameter, char *fvalidation, char *fcoupon);

A função DLL retorna uma referência às suas estruturas internas na forma de char * portanto, se você referenciasse essa DLL em C++, faria o seguinte para fazer o cálculo e obter as estruturas retornadas:

FeeCalculation(buff, (char *)&fans, (char *)fl, (char *)ft, (char *)fw, (char *)fi, (char *)fe, (char *)&fm, (char *)val, (char *)cpn);

Agora, como recupero os valores retornados por referência usando C#?Ou seja, como faço a mesma coisa em C# para obter as estruturas retornadas para obter meu cálculo retornado?Eu sei que preciso criar um método inseguro, mas não estou claro sobre como lidar com os endereços de memória em C# como você faria em C++.

Editar:Abaixo afirma para usar IntPtr, mas como você coloca em uma estrutura idêntica para que os campos da estrutura possam ser referenciados?

Editar:Aqui está a estrutura retornada na qual estou interessado (cout):

struct feeAnswer {


    unsigned int    fee;

    unsigned int    tax1;

    unsigned int    tax2;

    unsigned int    tax3;

    unsigned int    tax4;

    unsigned int    surcharge1;

    unsigned int    surcharge2;

    unsigned int    validationFee;

    unsigned int    couponFee1;

    unsigned int    couponFee2;

    unsigned int    couponFee3;

    unsigned int    couponFee4;

    unsigned short int dstay;       //Day Stay

    unsigned short int mstay;       //Minute Stay

};

Aqui está o (cin) que eu passaria junto com outras estruturas (elas são zero bytes no momento, quero fazer isso funcionar primeiro e depois implementarei o resto):

struct feeRequest {

    unsigned char   day;

    unsigned char   month;

    unsigned int    year;   //2000 ~ 2099



    unsigned char   hour;

    unsigned char   minute;

    unsigned char   rate;

    unsigned char   validation;



    unsigned char   coupon1;

    unsigned char   coupon2;

    unsigned char   coupon3;

    unsigned char   coupon4;

};
Foi útil?

Solução

Editar:agora que temos estruturas com as quais trabalhar, é possível encontrar uma solução melhor.Basta declarar estruturas em C# que correspondam às suas estruturas C++ e usá-las na declaração externa

[StructLayout(LayoutKind.Sequential)]
public struct feeAnswer {
   public uint    fee;
   public uint    tax1;
   public uint    tax2;
   public uint    tax3;
   public uint    tax4;
   public uint    surcharge1;
   public uint    surcharge2;
   public uint    validationFee;
   public uint    couponFee1;
   public uint    couponFee2;
   public uint    couponFee3;
   public uint    couponFee4;
   public ushort  dstay;       //Day Stay
   public ushort  mstay;       //Minute Stay
   };

  [StructLayout(LayoutKind.Sequential, Pack=1)]
  public struct feeRequest {
   public byte   day;
   public byte   month;
   public uint   year;   //2000 ~ 2099
   public byte   hour;
   public byte   minute;
   public byte   rate;
   public byte   validation;
   public byte   coupon1;
   public byte   coupon2;
   public byte   coupon3;
   public byte   coupon4;
   };

  [DllImport ("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
             CharSet = CharSet.Ansi)]
  public static extern void FeeCalculation (
          feeRequest cin,
          out feeAnswer cout,
           ...



        ....

resposta original (antes de termos estruturas) abaixo


Parece-me que estas não são referências a strings internas, mas sim ponteiros para buffers de strings que serão preenchidos pela chamada.Se você fosse retornando ponteiros de string, então estes seriam declarados char** em vez de char*.

Então eu acho que esses são apenas parâmetros padrão.Há muitos deles.Então sua interoperabilidade C# ficaria assim

[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
           CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin, 
        [MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
        out string cout, 
        [MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
        out string flimit,

ou isto se suas "strings" não forem realmente strings

[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
           CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] cout, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] flimit,
        ....

Outras dicas

Os parâmetros char* neste caso não são strings, mas ponteiros para pedaços de bytes crus representando os dados. Você deve marcar seus parâmetros como instâncias do tipo intptr, em conjunto com Marshal.alochglobal para criar um pedaço de memória e depois Marshal.ptrtoStructure Para converter esse bloco de memória em um tipo .NET utilizável.

Como um exemplo:

[StructLayout(LayoutKind.Sequential)]
struct MyUnmanagedType
{
    public int Foo;
    public char C;
}

IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyUnmanagedType)));

try
{
    FeeCalculation(memory);

    MyUnmanagedType result = (MyUnmanagedType)Marshal.PtrToStructure(
        memory, typeof(MyUnmanagedType));
}
finally
{
    Marshal.FreeHGlobal(memory);
}

Para responder à sua edição, você precisa criar uma estrutura e depois usar o StructLayoutattribute Nos campos, a fim de fazer a ordem de bytes e preencher o mesmo que a DLL original.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top