Discussion:
Marshal.PtrToStructure Returns Garbage
(too old to reply)
Charles Parker
2003-08-20 14:05:59 UTC
Permalink
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
2003-08-20 14:11:26 UTC
Permalink
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...
Post by Charles Parker
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#
[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;
}
// Retrieve the records
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(DATA_MAP)) *
iRecordsReturned);
IData.GetDataAttr(sUserName, sbEncodedPwd.ToString(), iAppID,
iConnectType,
Post by Charles Parker
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...
Andrei Barborica
2003-08-20 23:29:50 UTC
Permalink
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.
Post by Charles Parker
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...
Charles Parker
2003-08-21 11:37:03 UTC
Permalink
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...
Post by Andrei Barborica
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
[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
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.
Post by Charles Parker
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...
Thomas Scheidegger [MVP]
2003-08-21 06:11:24 UTC
Permalink
Hi Charles
Post by Charles Parker
[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)]
Post by Charles Parker
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.
--
Thomas Scheidegger - MVP .NET - 'NETMaster'
http://www.cetus-links.org/oo_dotnet.html - http://dnetmaster.net/
Charles Parker
2003-08-21 11:57:12 UTC
Permalink
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...
Post by Thomas Scheidegger [MVP]
Hi Charles
Post by Charles Parker
[StructLayout(LayoutKind.Sequential)]
public struct DATA_MAP
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=65)]
public string szRegDB;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
Post by Charles Parker
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...
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.
--
Thomas Scheidegger - MVP .NET - 'NETMaster'
http://www.cetus-links.org/oo_dotnet.html - http://dnetmaster.net/
Charles Parker
2003-08-21 12:05:56 UTC
Permalink
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...
Post by Thomas Scheidegger [MVP]
Hi Charles
Post by Charles Parker
[StructLayout(LayoutKind.Sequential)]
public struct DATA_MAP
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=65)]
public string szRegDB;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
Post by Charles Parker
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...
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.
--
Thomas Scheidegger - MVP .NET - 'NETMaster'
http://www.cetus-links.org/oo_dotnet.html - http://dnetmaster.net/
Loading...