0

I declare such c++ structure:

struct orders
{
signed long long replID; // i8
signed long long replRev; // i8
signed long long replAct; // i8
signed long long id_ord; // i8
signed int status; // i4
signed char action; // i1
signed int isin_id; // i4
signed char dir; // i1
char price[11]; // d16.5
signed int amount; // i4
signed int amount_rest; // i4
signed long long id_ord1; // i8
signed int init_amount; // i4

};

and similar c# structure:

public struct Orders
{
    public long replID; // i8
    public long replRev; // i8
    public long replAct; // i8
    public long id_ord; // i8
    public int status; // i4
    public char action; // i1
    public int isin_id; // i4
    public char dir; // i1
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
    public char[] price; // d16.5
    public int amount; // i4
    public int amount_rest; // i4
    public long id_ord1; // i8
    public int init_amount; // i4
}

I pass structure from c++:

__declspec(dllexport) void InitializeCallbacks(OrdersCallback ordersCallbackAddress_) {

OrdersCallback ordersCallbackFunction = ordersCallbackAddress_;
orders test;
test.init_amount = 123;
ordersCallbackFunction(&test);
}

To c#:

        OrdersCallback ordersCallback =
            delegate(ref Orders value)
            {
                Console.WriteLine("C# Orders call received = " +
                    " init_amount = " + value.init_amount);
            };
        InitializeCallbacks(ordersCallback);

I can read at console " init_amount = 123" what I think proves that call works as expected and structures are correctly aligned. However while debugging I receive this error: "Run-Time Check Failure #2 - Stack around the variable test is corrupted."

If I comment this line ordersCallbackFunction(&test); then error is gone. What's wrong with my code?

upd I forgot to mention that on c++ side I have #pragma pack(push, 4)

Cœur
  • 37,241
  • 25
  • 195
  • 267
Oleg Vazhnev
  • 23,239
  • 54
  • 171
  • 305
  • Probably related: [struct padding in C++](http://stackoverflow.com/q/5397447/335858). – Sergey Kalinichenko Jun 20 '13 at 18:24
  • This may also be a good read: [PInvokeStackImbalance C# call to unmanaged C++ function](http://stackoverflow.com/q/2390407/335858) – Sergey Kalinichenko Jun 20 '13 at 18:26
  • How does the declaration of the `OrdersCallback` type look like? Did you apply the [UnmanagedFunctionPointer Attribute](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedfunctionpointerattribute.aspx)? Maybe it's a problem with the calling convention? – dtb Jun 20 '13 at 18:46
  • Does this help? http://stackoverflow.com/questions/268772 – dtb Jun 20 '13 at 18:50
  • @dtb yes UnmanagedFunctionPointer attribute is applied: `[UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void OrdersCallback(ref Orders value);` – Oleg Vazhnev Jun 20 '13 at 19:42

2 Answers2

3

Most likely, the stack corruption is due to your flawed assumption that a char type in C# is the same size as that of C++. char in C# is a UTF-16 codepoint. See MSDN for details. If you want something similar to a C++ char, consider byte or sbyte.

Nathan Ernst
  • 4,540
  • 25
  • 38
  • Then again the marshaller marshals a two-byte .NET `char` to a single-byte C `char` if the [CharSet](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.charset.aspx) is not `Unicode`. `Marshal.SizeOf(typeof(char))` returns 1 (unlike `sizeof(char)` which returns 2). – dtb Jun 20 '13 at 18:43
  • Point taken, but I don't see the CharSet being specified here, so I'd imagine it defaults to Unicode (but, I've not done PInvoke in quite a while, so I could be wrong). – Nathan Ernst Jun 20 '13 at 18:52
  • [MSDN says](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.charset.aspx) "by default C# marks all methods and types as Ansi". Anyway, the error message ("Run-Time Check Failure...") seems to come from some C/C++ safeguard, so I wonder if the problem is related to P/Invoke at all... – dtb Jun 20 '13 at 18:55
  • I've seen this error before, and it's generally related to conflicting type sizes or padding issues. There may be other causes, but I'm unsure. – Nathan Ernst Jun 20 '13 at 18:57
1

It's likely your packing of the structs is not in parity (you should be able to see this in the debugger by creating each of the structs and checking the allocated size).

Check for "#pragma pack" in C++ and if your struct is packed, do something like the following for your C# struct:

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct Orders {
...
}

Also, as Nathan points out, your assumptions on Char are off. you'll want to use char or unsigned char in C++ and map that to sbyte or byte in C++.

I'd also recommend explicitly reordering your struct for alignment as follows assuming you have control over both the C++ and C# sides:

struct orders {
    signed long long replID; // i8
    signed long long replRev; // i8
    signed long long replAct; // i8
    signed long long id_ord; // i8
    signed long long id_ord1; // i8
    signed int status; // i4
    signed int isin_id; // i4
    signed int amount; // i4
    signed int amount_rest; // i4
    signed int init_amount; // i4
    signed char action; // i1
    signed char dir; // i1
    char price[11]; // d16.5

};

Adam Markowitz
  • 12,919
  • 3
  • 29
  • 21
  • thanks, I've inserted `[StructLayout(LayoutKind.Sequential, Pack = 4)]` and this fixed the problem. I can not control c++ side, I control only c# side. probably you know how exactly data is transferred? is it serialized and then deserealized or I just pass pointer to c++ block of memory? probably it would be more efficient and flexible at the same time to pass IntPtr and just work with unmanaged memory in c#? – Oleg Vazhnev Jun 20 '13 at 19:39