views:

224

answers:

1

I've got the following structs in C++: (Using pragma pack 1)

typedef struct _wfs_cdm_physicalcu
{
 LPSTR           lpPhysicalPositionName;
 CHAR            cUnitID[5];
 ULONG           ulInitialCount;
 ULONG           ulCount;
 ULONG           ulRejectCount;
 ULONG           ulMaximum;
 USHORT          usPStatus;
 BOOL            bHardwareSensor;
} WFSCDMPHCU, * LPWFSCDMPHCU;

typedef struct _wfs_cdm_cashunit
{
 USHORT          usNumber;
 USHORT          usType;
 LPSTR           lpszCashUnitName;
 CHAR            cUnitID[5];
 CHAR            cCurrencyID[3];
 ULONG           ulValues;
 ULONG           ulInitialCount;
 ULONG           ulCount;
 ULONG           ulRejectCount;
 ULONG           ulMinimum;
 ULONG           ulMaximum;
 BOOL            bAppLock;
 USHORT          usStatus;
 USHORT          usNumPhysicalCUs;
 LPWFSCDMPHCU   *lppPhysical;
} WFSCDMCASHUNIT, * LPWFSCDMCASHUNIT;


typedef struct _wfs_cdm_cu_info
{
 USHORT          usTellerID;
 USHORT          usCount;
 LPWFSCDMCASHUNIT *lppList;
} WFSCDMCUINFO, * LPWFSCDMCUINFO;

P/Invoke Interop Assistant gives me the following output:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi, Pack = 1)]
public struct WFSCDMPHCU {

 /// LPSTR->CHAR*
 [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
 public string lpPhysicalPositionName;

 /// CHAR[5]
 [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=5)]
 public string cUnitID;

 /// ULONG->unsigned int
 public uint ulInitialCount;

 /// ULONG->unsigned int
 public uint ulCount;

 /// ULONG->unsigned int
 public uint ulRejectCount;

 /// ULONG->unsigned int
 public uint ulMaximum;

 /// USHORT->unsigned short
 public ushort usPStatus;

 /// BOOL->int
 public int bHardwareSensor;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi, Pack = 1)]
public struct WFSCDMCASHUNIT {

 /// USHORT->unsigned short
 public ushort usNumber;

 /// USHORT->unsigned short
 public ushort usType;

 /// LPSTR->CHAR*
 [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
 public string lpszCashUnitName;

 /// CHAR[5]
 [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=5)]
 public string cUnitID;

 /// CHAR[3]
 [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=3)]
 public string cCurrencyID;

 /// ULONG->unsigned int
 public uint ulValues;

 /// ULONG->unsigned int
 public uint ulInitialCount;

 /// ULONG->unsigned int
 public uint ulCount;

 /// ULONG->unsigned int
 public uint ulRejectCount;

 /// ULONG->unsigned int
 public uint ulMinimum;

 /// ULONG->unsigned int
 public uint ulMaximum;

 /// BOOL->int
 public int bAppLock;

 /// USHORT->unsigned short
 public ushort usStatus;

 /// USHORT->unsigned short
 public ushort usNumPhysicalCUs;

 /// LPWFSCDMPHCU*
 public System.IntPtr lppPhysical;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)]
public struct WFSCDMCUINFO {

 /// USHORT->unsigned short
 public ushort usTellerID;

 /// USHORT->unsigned short
 public ushort usCount;

 /// LPWFSCDMCASHUNIT*
 public System.IntPtr lppList;
}

However, when using Marshal.PtrToStruct (say, on WFSCDMCUINFO.lppList) I get garbage; when viewing memory using Visual Studio, I see the actual contents in another memory entry (not lppList per se).

Are these conversions correct? Is my trust in P/Invoke Interop Assistant misplaced?

EDIT:

This is the code I use to Marshal to/from IntPtr. I know the Thread idea sucks, but I'll get to that later...

    public static IntPtr GetIntPtr(this object obj) {
        try {
            var handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
            var thread = new Thread(() => {
                Thread.Sleep(20000);
                handle.Free();
            });
            thread.Start();

            return handle.AddrOfPinnedObject();
        } catch (ArgumentException) {
            var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));

            Marshal.StructureToPtr(obj, ptr, false);

            return ptr;
        }
    }

    public static T FromIntPtr<T>(this IntPtr ptr) {
        if (ptr == IntPtr.Zero)
            return default(T);

        return (T) Marshal.PtrToStructure(ptr, typeof (T));
    }
+2  A: 

I haven't done a deep dive into the structure but it definitely looks like the generated signatures are correct. There is nothing particularly tricky in these structs and all of the types are appropriately matched.

What's likely the problem is the marshalling of the lppList parameter. Can you give a quick sample of how you 1) get this data back and 2) the code which attempts to marshal lppList.

Also there may be issues if #pragma pack was used on the original structure. The PInvoke Interop Assistant won't attempt to interpret pack commands. You may have to adjust that setting on the struct manually.

EDIT

One issue may be the lpPhysicalPositionName and lpPhysicalCashUnit parameters. Try switching them to IntPtr vs. String and removing the Marshal attribute.

Caveat: I wrote this tool so I'm heavily biased on it's reliability :)

JaredPar
As usual, minutes after posting I read about Marshal.ReadIntPtr - which *might* be my solution. I'll post back after testing...
hb
ReadIntPtr() does give me the correct memory address; however, I'm getting errors when Marshalling. I'll edit my post w/ my code in a min
hb