I am back with some more PInvoke Stuff. Recently I was working on a PInvoke issue which I found interesting.
I have a C++ dll which has a function whose signature is
int TestFunc(IN_STRUCT in_Params, RET_STRUCT * pret_Par).
I wanted to call this function from C#. Function has two arguments. First argument is input structure which will be filled from C# code and passed to C++ code. Second argument is output structure which is filled in C++ code and output to C# code.
Here are the C struct definitions and a function that needs to be marshaled
typedef struct IN_STRUCT
typedef struct RET_STRUCT
extern "C" __declspec(dllexport) \
int TestFunc(IN_STRUCT in_Params, RET_STRUCT * pret_Par)
int iRet = 0;
pret_Par->RET_OC = in_Params.CMD_OC;
pret_Par->RET_Seq = in_Params.CMD_Seq;
pret_Par->RET_RetBytes = 6;
pret_Par->RET_PD = 0;
pret_Par->RET_PD = 10;
pret_Par->RET_PD = 20;
pret_Par->RET_PD = 30;
pret_Par->RET_PD = 40;
pret_Par->RET_PD = 50;
pret_Par->str = new char(30);
strcpy(pret_Par->str,"This is sample PInvoke app");
Managed Structure equivalent to Native Structure:
//This structure will be filled up by C++ Test.dll and returned back
With values to C# code.
public struct RET_STRUCT
public byte RET_OC;
public byte RET_Seq;
public byte RET_RetBytes;
public String RET_STR;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte RET_PD;
//The values of this structure will be used to fill up IN_STRUCT and
passed to C#
public struct IN_STRUCT
public byte CMD_PT;
public byte CMD_PTType;
public byte CMD_OC;
public byte CMD_Seq;
//C++ dll containing the func
public static extern int TestFunc(IN_STRUCT i, ref RET_STRUCT r);
static void Main(string args)
IN_STRUCT cmd_params = new IN_STRUCT();
RET_STRUCT ret_Params = new RET_STRUCT();
//Fill up the cmd_params
cmd_params.CMD_OC = 0x02;
cmd_params.CMD_PTType = 0x00;
cmd_params.CMD_Seq = 1;
//Call the C++ function to fill ret_params
int iRet = TestFunc(cmd_params, ref ret_Params);
//Print out the returned values
Console.WriteLine(ret_Params.RET_OC + " " + ret_Params.RET_Seq +
for (int i = 0; i < ret_Params.RET_RetBytes; i++)
Console.WriteLine("\n" + ret_Params.RET_PD[i]);
After executing the code I was expecting a valid output. But I ended up with Access Violation. I used windbg to troubleshoot this issue.
I spawned exe from windbg and tried to see call stack.
ChildEBP RetAddr Args to Child
002cec30 76fc5883 006515c8 00000001 00000000 ntdll!RtlpLowFragHeapFree+0x31 (FPO: [0,10,4])
002cec44 76b9c56f 000b0000 00000000 049a3a48 ntdll!RtlFreeHeap+0x101 (FPO: [3,0,4])
002cec58 7565dc2c 000b0000 00000000 049a3a50 KERNEL32!HeapFree+0x14 (FPO: [3,0,0])
002cec6c 7565dc53 7573e6f4 049a3a50 002cec88 ole32!CRetailMalloc_Free+0x1c (FPO: [2,0,0])
002cec7c 6c7e8410 049a3a50 002cec9c 6c8084bd ole32!CoTaskMemFree+0x13 (FPO: [1,0,0])
002cec88 6c8084bd 00109d34 00000001 00109d48 mscorwks!FieldMarshaler_StringAnsi::DestroyNativeImpl+0x16 (FPO: [1,0,0])
002cec9c 6c8088e5 00109d30 0065340c 1670b1d2 mscorwks!LayoutDestroyNative+0x3a (FPO: [2,0,0])
002cee8c 6c73539b 002cef58 00000000 1670b182 mscorwks!CleanupWorkList::Cleanup+0x2ea (FPO: [2,116,4])
002ceedc 001cad4c 002cef18 01020000 00109d30 mscorwks!NDirectSlimStubWorker2+0x120 (FPO: [1,12,4])
WARNING: Frame IP not in any known module. Following frames may be wrong.
002cefa4 6c7013a4 00700876 002cefd8 00000000 0x1cad4c
002cefe0 6c6f1b4c 010d2816 00000003 002cf070 mscorwks!PreStubWorker+0x141 (FPO: [1,13,4])
002ceff0 6c7021b1 002cf0c0 00000000 002cf090 mscorwks!CallDescrWorker+0x33
0:000> da 049a3a50
049a3a50 "My Name is Jyoti Patel"
From the call stack it’s clear that InteropMarshaller ( NDirectSlimStubWorker2) is trying to deallocate string using CoTaskMemFree.
There are two solutions to this problem.
1. As deallocation is done using CoTaskMemFree, Allocate the memory using CoTaskMemAlloc.
Changing line of code in C++ from
pret_Par->str = new char(30);
pret_Par->str = (char*)CoTaskMemAlloc(30);
Issue was resolved.
(When a string buffer allocated by native code is marshaled to managed code, CLR Interop marshaller will allocate a managed string object and copy the contents of native buffer to the managed string object. Now in order to prevent a potential memory leak, CLR Interop Marshaller will try to free allocated native memory. It does so by calling CoTaskMemFree. The decision to call CoTaskMemFree is by-design. This can at times lead to crash, if memory was allocated by the called-native function using any API other than CoTaskMemAlloc family of API’s as custom allocators may allocate on different heaps.)
In this case, memory was allocated by malloc, and ends up being freed by CoTaskMemFree and hence we see an AV)
2. If you are not able to change the C++ code and you want to allocate memory using new/malloc other solution is to use Intptr and do custom marshalling by calling corresponding methods from Marshal class to do marshalling.
public byte RET_Seq;
public IntPtr RET_STR;
Jyoti Patel and Manish Jawa
Developer Support VC++ and C#