.NET Forum / .NET Framework / Interop / August 2003
Marshal.PtrToStructure Returns Garbage
|
|
Thread rating:  |
Charles Parker - 20 Aug 2003 15:05 GMT I have a COM object that allocates and passes back a pointer to an array of DATA_MAP structures. A test app written in C++ work correctly however a C# .NET app returns garbage. In .NET the managed structure is defined as:
[StructLayout(LayoutKind.Sequential)] public struct DATA_MAP { [MarshalAs(UnmanagedType.ByValTStr, SizeConst=65)] public string szRegDB; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=65)] public string szTableName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=65)] public string szColumnName; public int iRestrictAccess; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=1024)] public string szRestrictionText; }
The code to retrieve the data is shown below:
// Retrieve the records IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(DATA_MAP)) * iRecordsReturned); IData.GetDataAttr(sUserName, sbEncodedPwd.ToString(), iAppID, iConnectType, iRecordsReturned, buffer);
pDataMapOut = new DATA_MAP[iRecordsReturned]; IntPtr iter = buffer; for (int i=0; i<iRecordsReturned; i++) { pDataMapOut[i] = (DATA_MAP)Marshal.PtrToStructure(iter, typeof(DATA_MAP)); iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(DATA_MAP))); }
The first string in the returned structure is garbage while the remaing strings are empty/blank. Debugging through the COM object data in the array of structures is correct so the problem must be in the marshalling to the managed structure. Any ideas on how to solve this problem would be greatly appreciated. Thanks.
Charles...
Charles Parker - 20 Aug 2003 15:11 GMT The COM structure is defined as follows:
typedef struct _DATA_MAP { unsigned char szRegDB[ 65 ]; unsigned char szTableName[ 65 ]; unsigned char szColumnName[ 65 ]; int iRestrictAccess; unsigned char szRestrictionText[ 1024 ]; } DATA_MAP;
Charles...
> I have a COM object that allocates and passes back a pointer to an array of > DATA_MAP structures. A test app written in C++ work correctly however a C# [quoted text clipped - 38 lines] > > Charles... Andrei Barborica - 21 Aug 2003 00:29 GMT Well, Charles, I don't have yet a solution, but I can give you more information regarding how the constructor for arrays of structures seems to be working in C#. I have a similar problem:
[StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )] public struct MIXERCONTROLDETAILS_LISTTEXT { public uint dwParam1; public uint dwParam2; [MarshalAs( UnmanagedType.ByValArray, SizeConst=64)] public char[] szName; } ;
When I use this structure to retrieve data from a COM object (mixerXXX API in Windows Multimedia SDK), as is (no array), works fine. The layout is as expected, folowing the sequential declarations of the members. However, if I declare an array mxcd of MIXERCONTROLDETAILS_LISTTEXT, I've found that the memory mapping of the structures in the array is something like that: 0-3: mxcd[0].dwParam1 4-7: mxcd[0].dwParam2 8-11: a 4-byte POINTER to another memory area holding mxcd[0].szName (64 bytes long), not the actual string. <----- Here ! 12-15: mxcd[1].dwParam1 16-19: mxcd[1].dwParam1 20-23: a 4-byte POINTER to another memory area holding mxcd[1].szName (64 bytes long), not the actual string. <----- Here ! Apparently, when you call the constructor for the array, the base types are left in place, where they were supposed to be, while the strings (and other arrays) are created by allocating a different memory area and inserting a pointer to it, where one would expect to find the actual data.
I tried using explicit layout of the structure, but with no better results. I even tried using the Size (Size=0x48) attribute for the structure. The result was that instead of having the actual string at the expected location, I got a pointer in the first 4 bytes, followed by 60 bytes of unused space, but the szName string would still reside somewhere else !
I hope this helps in figuring out the intricate mechanism of C# memory allocation. I wish I had a solution as much as you do !
Can anyone HELP us ???? Please ??? Andrei Barborica.
> The COM structure is defined as follows: > [quoted text clipped - 8 lines] > > Charles... Charles Parker - 21 Aug 2003 12:37 GMT Thanks for your candid help Andrei, The strange part is the are occasions when I will get all the data back except the first string in the [0] structure. It is always garbage, while all other fields including the first string of structure [1], [2], [n]. This is strange. Are you using .NET 1.1/Visual Studio .NET 2003? I am not, may this problem was fixed in this version. Hopefully, someone from Microsoft will see this thread and be able to help.
Thanks again. Charles...
> Well, Charles, > I don't have yet a solution, but I can give you more information [quoted text clipped - 57 lines] > > > > Charles... Thomas Scheidegger [MVP] - 21 Aug 2003 07:11 GMT Hi Charles
> [StructLayout(LayoutKind.Sequential)] > public struct DATA_MAP > { > [MarshalAs(UnmanagedType.ByValTStr, SizeConst=65)] > public string szRegDB;
Just to be sure, add a 'CharSet' to your struct: [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
> I have a COM object that allocates and passes back a pointer to an array of > DATA_MAP structures.
This description doesn't match your actual code. If the "...COM object allocates memory", why does your C# client the same with AllocCoTaskMem?
Then for "passes back a pointer to...", this would mean in managed code either an 'out' or 'ref' parameter, but your C# client passes the 'buffer' pointer 'by value'.
As I don't have any documentation to your COMponent, there could be many problems...
But let's try something like:
IntPtr pptr = Marshal.AllocCoTaskMem( IntPtr.Size ); IData.GetDataAttr( ....pptr ) IntPtr buffer = Marshal.ReadIntPtr( pptr ) ...use buffer & PtrToStructure - loop as before ... free the pptr, buffer according COMponent docu.
 Signature Thomas Scheidegger - MVP .NET - 'NETMaster' http://www.cetus-links.org/oo_dotnet.html - http://dnetmaster.net/
Charles Parker - 21 Aug 2003 12:57 GMT Thomas,
You have a point about allocating the IntPtr and passing it to GetDataAttr(). However, the method signature does not want an out IntPtr just and IntPtr. The IDL for this method is as follows:
[id(1), helpstring("method GetDataAttr. Retrieves User Attributes.")] HRESULT GetDataAttr([in] BSTR bstUserName, [in] BSTR bstPassword, [in] int iAppID, [in] int iConnType, [in] long lNoEntries, [out, size_is(,lNoEntries)] DATA_MAP** ppDataMapOut);
This specifies the pointer to array of DATA_MAPs as an out parameter so I do not no why the signature in the .NET app just wants a IntPtr and not out IntPtr.
If I do not allocate the IntPtr and set it IntPtr.Zero before passing to GetDataAttr() I get the following error when executing. Additional information: The parameter is incorrect.
This is what I also observered. The strange part is the are occasions when I will get all the data back except the first string in the [0] structure. It is always garbage, while all other fields including the first string of structure [1], [2], [n]. This is strange.
Thanks, Charles...
> Hi Charles > [quoted text clipped - 29 lines] > ...use buffer & PtrToStructure - loop as before > ... free the pptr, buffer according COMponent docu. Charles Parker - 21 Aug 2003 13:05 GMT Thomas,
The COM object is expecting a pointer to an array of structures. The array of structures is allocated in the COM object and assigned to the pointer. I would assume this is why the signature is not out. In the C++ app that uses the COM object I just pass the pointer to the array of structures the that COM object allocates and returns. This works as expected. The problems are the .NET marshalling back the structure.
Thanks, Charles...
> Hi Charles > [quoted text clipped - 29 lines] > ...use buffer & PtrToStructure - loop as before > ... free the pptr, buffer according COMponent docu.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|