1

EDIT: I oversimplified my example... In the real code I was assigning values to strMyStringx without correctly using wcscpy_s, so instead of assigning the values I was just passing the pointer, which was out of scope by the time the values were being marshaled into managed code...

I'm trying to marshal a struct with three string properties from C to C#, but I can't get the definition of the struct right in C#. All of the properties print as garbage. Am I marshaling wrong or do my properties have the wrong type?

My custom structure:

typedef struct _MY_STRUCT_STRING { LPWSTR strMyString1; LPWSTR strMyString2; LPWSTR strMyString3; }MY_STRUCT_STRING, *PMY_STRUCT_STRING; 

My C function returns an array of pointers to this struct:

bool bEnumerateString(OUT LONG &i_arr_size, OUT PMY_STRUCT_STRING* &pArrStringStruct) { // [...] function simplified to demonstrate building a pointer to an array of struct* long i_arr_size = 3 PMY_STRUCT_STRING *ptr_arr_string = (PMY_STRUCT_STRING *)malloc(sizeof(PMY_STRUCT_STRING)* i_arr_size); for (int i = 0; i < i_arr_size; i++) { ptr_arr_string[i] = (PMY_STRUCT_STRING)malloc(sizeof(MY_STRUCT_STRING)); ptr_arr_string[i]->strMyString1 = L"String 1"; // This would work. In the real code I was assigning values from another array and mistakenly passed the pointer rather than doing wcscpy_s ptr_arr_string[i]->strMyString2 = L"String 2"; ptr_arr_string[i]->strMyString3 = L"String 3"; } pArrStringStruct = ptr_arr_string; return true; } 

C#:

 //Import the DLL with my function [DllImport("My.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "bEnumerateString")] internal static extern bool bEnumerateString(out long count, out IntPtr pArrStringStruct); // Define the C# equivalent of the C struct [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct MY_STRUCT_STRING { [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)] public string strMyString1; [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)] public string strMyString1; [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)] public string strMyString1; } [...] // Code to marshal (try... catch etc removed for succinctness) IntPtr pArrStruct = IntPtr.Zero; long lCount = 0; bool bResult = false; bResult = bEnumerateString(out lCount, out pArrStruct); if (!bResult) { // Marshal to deref pointer IntPtr[] pArrStructList = new IntPtr[lCount]; for (ulong i = 0; i < (ulong)lCount; i++) { pArrStructList[i] = Marshal.ReadIntPtr(pArrStruct, Marshal.SizeOf(typeof(IntPtr)) * (int)i); } // Marshal pointers to struct var lstMyStringStrct = new List<MY_STRUCT_STRING>(pArrStructList.Length); foreach (IntPtr ptr in pArrStructList) { lstMyStringStrct.Add((MY_STRUCT_STRING)Marshal.PtrToStructure(ptr, typeof(MY_STRUCT_STRING))); } // Enumerate struct foreach (MY_STRUCT_STRING myStr in lstMyStringStrct) { // All of these outputs are garbage Console.WriteLine("strMyString1: " + myStr.strMyString1); Console.WriteLine("strMyString2: " + myStr.strMyString2); Console.WriteLine("strMyString3: " + myStr.strMyString3); } } 
2
  • LPWSTR is a wchar_t[], not a wchar_t*. Remove the [MarshalAs] attribute. And note the memory leak, nobody is calling free() for you. You can only get ahead by using CoTaskMemAlloc() in your native code instead of malloc() or by exposing a function that calls free(). Commented Jun 21, 2014 at 8:34
  • @HansPassant - Removing the MarshalAs attribute causes Marshal.PtrToStructure to throw an AccessViolation exception. Would using CoTaskMemAlloc() help? I haven't got to freeing the memory because I'm trying to troubleshoot with a narrower scope, though would freeing the memory help the marshaling? Commented Jun 21, 2014 at 16:18

1 Answer 1

2

I see one problem. You're C++ structure uses LPWSTR (pointers) whereas you're C# code is expecting fixed size char arrays.

Change your strings from:

[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 8)] public string strMyString1; 

which would be used when the C++ structure was defined like:

char strMyString1[8]; 

to:

[MarshalAsAttribute(UnmanagedType.LPWStr)] public string strMyString1; 
Sign up to request clarification or add additional context in comments.

2 Comments

That's what I first thought, however using that attribute causes an ExecutionEngineException with Marshal.PtrToStructure
Turns out I had some screwup copying pointers rather than doing a wcscpy_s which was causing this to work in a Win32 console app (as the pointer was still in scope) but fail once it was inside a DLL and being marshaled into managed code.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.