Question

I am referencing a DLL in my C# project as follows:

[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);

The FeeCalculation function is exported as follows in the 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);

The DLL function returns a reference to it's internal structures in the form of char * so if you were to reference this DLL in C++, you would do the following to do the calculation and get the returned structures:

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

Now, how do I retrieve those values that are returned by reference using C#? Meaning, how do I do the same thing in C# to get the returned structures to get my returned calculation? I know I need to create an unsafe method, but I am unclear on how to deal with the memory addresses in C# like you would in C++.

Edit: Below states to use IntPtr but how do you place into identical structure so the fields of the structure can be referenced?

Edit: Here is the returned structure that I am interested in (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

};

Here is the (cin) that I would pass along with other structures (they are zero byte at the moment, I want to get this to work first then I will implement the rest):

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;

};
Was it helpful?

Solution

Edit: now that we have structures to work with, a better solution is possible. Just declare structs in C# that match your C++ structs, and use them in the extern declaration

[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,
           ...



        ....

original answer (before we had structs) below


It appears to me that these are not references to internal strings, but rather pointers to string buffers that will be filled in by the call. If you were returning string pointers, then these would be declared char** rather than char*.

So I think these are just standard out parameters. There's just a lot of them. So your C# interop would look like this

[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,

or this if your "strings" aren't really 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,
        ....

OTHER TIPS

The char* parameters in this case are not strings but pointers to chunks of raw bytes representing the data. You should marshal your parameters as instances of the IntPtr type, in conjunction with Marshal.AllocHGlobal to create a chunk of memory and then Marshal.PtrToStructure to convert that block of memory into a usable .NET type.

As an example:

[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);
}

To answer your Edit, you need to create a struct, and then use the StructLayoutAttribute on the fields in order to make the byte order and padding the same as the original dll did.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow