views:

513

answers:

2

I am getting this weird error while trying to marshal data to my DLL function and back to the C# code. I don't see where I am passing null or reading invalid memory and this error is so vague. Any clues??

Code below:

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

[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;
            public ushort mstay;
        };

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct feeRequest
        {
            public byte day;
            public byte month;
            public uint year;
            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, string flimit,
            string frate, string fwindow, string fincrement, string fbird,
            string fparameter, 
            string fvalidation, string fcoupon);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            feeRequest freq = new feeRequest();
            feeAnswer fans = new feeAnswer();

            string flim = "";
            string frat = "";
            string fwin = "";
            string finc = "";
            string fbir = "";
            string fpar = "";
            string fval = "";
            string fcoup = "";

            freq.day = 26;

            freq.month = 2;

            freq.year = 2010;   //2000 ~ 2099

            freq.hour = 20;

            freq.minute = 47;

            freq.rate = 15;

            freq.validation = 1;

            freq.coupon1 = 2;

            freq.coupon2 = 3;

            freq.coupon3 = 4;

            freq.coupon4 = 5;


            FeeCalculation(freq, out fans, flim, frat, fwin, finc, fbir, fpar, fval, fcoup);

With John's suggestion:

public static extern void FeeCalculation(feeRequest cin,
            out feeAnswer cout, 
            [MarshalAs(UnmanagedType.LPArray)]
            IntPtr flimit,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr frate,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr fwindow,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr fincrement,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr fbird,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr fparameter,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr fvalidation,
           [MarshalAs(UnmanagedType.LPArray)]
            IntPtr fcoupon);

...

FeeCalculation(freq, out fans, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+2  A: 

Your problem is most likely that you havent finished the interop declaration. As I said before, most of your 'string' parameters are really out byte[] parameters, (or out struct)

So you need to do something more like this

    [DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
       CharSet = CharSet.Ansi)]
    public static extern void FeeCalculation(feeRequest cin,
        out feeAnswer cout, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] flimit,
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] frate, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] fwindow, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] fincrement, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] fbird,
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] fparameter, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[] fvalidation, 
        [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
        out byte[]fcoupon);

You may not care about the other arguments, but the function is still going to try and write to them so you have to supply dummy buffers and the buffers have to be large enough to hold the output. (so you may need to change SizeConst).

If your function tolerates NULL pointers to outputs, then you could declare the values you don't want as IntPtr and pass IntPtr.Zero for those.

In the long run, you really need to declare all of the structs that this function wants to see and pass them all in properly.

Edit: ok you want to use IntPtr, or MarshalAs/byte[] but not both.

public static extern void FeeCalculation(feeRequest cin,
        out feeAnswer cout, 
        IntPtr flimit,
        IntPtr frate,
        IntPtr fwindow,
        IntPtr fincrement,
        IntPtr fbird,
        IntPtr fparameter,
        IntPtr fvalidation,
        IntPtr fcoupon);

FeeCalculation(freq, out fans, IntPtr.Zero, ...
John Knoeller
Yes, I know I will have to declare them.. this is just a test to see if calling my dll from C# will work for a project I am working on.. I will try your suggestion and see if it works! Right now the function ignores the rest but fans and freq
0A0D
The other possible problem is that the packing on the structures may not match what the C++ expects. You might try pack=4 instead of pack=1 on cin. do you know what the C++ structure packing is?
John Knoeller
The structure isn't packed via the pack pragma if that's what you are asking... I did your suggested answer and it complained about having the SizeConst parameter on ByRef so I took it out and get the AccessViolation again
0A0D
Changed the unwanted parameters to IntPtr, removed out modifier except for second parameter and passed IntPtr.Zero for rest now I get the following error:
0A0D
Cannot marshal 'parameter #10': Invalid managed/unmanaged type combination (Int/UInt must be paired with SysInt or SysUInt).
0A0D
@Changeling: please post your current code, you could post as an answer if you don't want to change your question.
John Knoeller
@John: Posted edit to question
0A0D
@John: Now I am getting AccessViolation again with the edit.. strange
0A0D
@Changeling: are you using pack=4 ?
John Knoeller
@John: Wasn't but I added it and it still throws me the error..
0A0D
time to set a breakpoint in the C++ code to see which param its faulting on
John Knoeller
see my answer for the fix
0A0D
+1  A: 

To fix it, I added a try / catch block around the memmove() code in the .DLL. Then, I had to make sure I was using the ref keyword on all parameters because otherwise the memory addresses were not being referred properly to the DLL. Once I did that, it works now with no access violation. I did not need the MarshalAs declarations nor any pack statements. I simply was able to use LayoutKind.Sequential.

0A0D