Answered by:
P/Invoke Returning an Byte[] from C++ to C#

Question
-
I'd appreciate any help, all the threads and articles I've searched say this should be possible.
Here is a simplified example, I need to pass a string to C++ method and get back a byte[]. Upon reallocation I get an access violation. 2nd parameter is actual size newly allocated byte array.
I have tried several variations wth and with out ref on Byte[] (Changing C++ accordingly), tried specifying sizeParameterIndex=2. If I have to I will break the call into 2, 1) to get required size and 2) to get updated buffer. The actual C++ is legacy code, so I'm specifying charset and calling convention.
C#
class Test
{
static void TestUnManaged()
{
int l_Ret=0;
int BuffSize=0;
Byte[] labData = null;
l_Ret = TestAllocate("Fred", ref BuffSize, ref labData);
return;
}[
DllImport("MvvTestUnManaged.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int TestAllocate(String Data, ref int BufSize, [In, Out, MarshalAs(UnmanagedType.LPArray)] ref Byte[] Buffer);
}
C++
extern "C"
{
EXPORT_API int
TestAllocate(LPCSTR Data, int *BufSize, LPVOID *Buffer)
{
int lnRet = 0;
if(Data != NULL)
{
size_t BfSize = strlen(Data);
size_t AllocSize = BfSize + 1;
*BufSize = BfSize;
*Buffer = (LPVOID)::HeapAlloc(NULL, 0L, AllocSize);
memset(*Buffer, AllocSize, 0x00);
memcpy(*Buffer, Data, BfSize);
}
return(lnRet);
}
}Monday, March 16, 2009 7:39 PM
Answers
-
I think you'll have to do your own custom marshalling here :-
__declspec( dllexport ) int TestAllocate(LPCSTR Data, int *BufSize, LPVOID* Buffer)
{
int lnRet = 0;
if(Data != NULL)
{
size_t BfSize = strlen(Data);
size_t AllocSize = BfSize + 1;
*BufSize = BfSize;
*Buffer = ::HeapAlloc(GetProcessHeap(), 0L, AllocSize);
memset(*Buffer, AllocSize, 0x00);
memcpy(*Buffer, Data, BfSize) ;
}
return(lnRet);
}
and
[DllImport("TestDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int TestAllocate(String Data, ref int BufSize, out IntPtr Buffer);
static void Main(string[] args)
{
IntPtr buffer = IntPtr.Zero;
int size = 0;
TestAllocate("hello world", ref size, out buffer);
Byte[] byteArray = new Byte[size];
Marshal.Copy(buffer, byteArray, 0, size);
}
http://blog.voidnish.comTuesday, March 17, 2009 7:01 PMModerator -
Hi,
Did you mean when the proptype is [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] Byte[] , an error will arise? What did you mean re-allocate? Is it HeapAlloc?
The first parameter of heapAlloc is a handle to the heap from which the memory will be allocated. So please try to alloc memory using ::HeapAlloc(GetProcessHeap(), 0L, AllocSize) as Nishant suggested.
The buffer is allocated by the marshaler using CoTaskMemAlloc. While buffer is set to point to the memory allocated by HeapAlloc, the memory it pointed to will leak.
When the method return , the buffer which is allocated by HeapAlloc will not be marshaled back , so the c# method can’t get the value.
Please try to check out the msdn sample code about pinvoke which has show how to marshal array properly.
Best regards,
Harry
http://cfx.codeplex.com where you can find samples you are looking forFriday, March 20, 2009 12:47 PM
All replies
-
You need to specify SizeParamIndex to 1 (not 2) on your array:
[DllImport("MvvTestUnManaged.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int TestAllocate(String Data, ref int BufSize, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref Byte[] Buffer);
-SteveTuesday, March 17, 2009 3:27 PM -
Sorry the 2 was a typo.
Found out you can't use a SizeParamIndex with a ByRef type.
If I take the ref off Byte[], then I get an access violation when I re-allocate.
"An unhandled exception of type 'System.AccessViolationException' occurred in TestConversion.exe"
C#[DllImport("MvvTestUnManaged.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] public static extern int TestAllocate(String Data, ref int BufSize, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] Byte[] Buffer);
C++
EXPORT_API int TestAllocate(LPCSTR Data, int *BufSize, LPVOID Buffer) { int lnRet = 0; if(Data != NULL) { size_t BfSize = strlen(Data); size_t AllocSize = BfSize + 1; *BufSize = BfSize; if(Buffer == NULL) Buffer = (LPVOID)::HeapAlloc(NULL, 0L, AllocSize); memset(Buffer, AllocSize, 0x00); memcpy(Buffer, Data, BfSize) } return(lnRet); }
Thanks,
MikeTuesday, March 17, 2009 5:16 PM -
I think you'll have to do your own custom marshalling here :-
__declspec( dllexport ) int TestAllocate(LPCSTR Data, int *BufSize, LPVOID* Buffer)
{
int lnRet = 0;
if(Data != NULL)
{
size_t BfSize = strlen(Data);
size_t AllocSize = BfSize + 1;
*BufSize = BfSize;
*Buffer = ::HeapAlloc(GetProcessHeap(), 0L, AllocSize);
memset(*Buffer, AllocSize, 0x00);
memcpy(*Buffer, Data, BfSize) ;
}
return(lnRet);
}
and
[DllImport("TestDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int TestAllocate(String Data, ref int BufSize, out IntPtr Buffer);
static void Main(string[] args)
{
IntPtr buffer = IntPtr.Zero;
int size = 0;
TestAllocate("hello world", ref size, out buffer);
Byte[] byteArray = new Byte[size];
Marshal.Copy(buffer, byteArray, 0, size);
}
http://blog.voidnish.comTuesday, March 17, 2009 7:01 PMModerator -
Hi,
Did you mean when the proptype is [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] Byte[] , an error will arise? What did you mean re-allocate? Is it HeapAlloc?
The first parameter of heapAlloc is a handle to the heap from which the memory will be allocated. So please try to alloc memory using ::HeapAlloc(GetProcessHeap(), 0L, AllocSize) as Nishant suggested.
The buffer is allocated by the marshaler using CoTaskMemAlloc. While buffer is set to point to the memory allocated by HeapAlloc, the memory it pointed to will leak.
When the method return , the buffer which is allocated by HeapAlloc will not be marshaled back , so the c# method can’t get the value.
Please try to check out the msdn sample code about pinvoke which has show how to marshal array properly.
Best regards,
Harry
http://cfx.codeplex.com where you can find samples you are looking forFriday, March 20, 2009 12:47 PM