Cannot p/invoke C method from C# due to marshallin

2019-09-06 01:33发布

问题:

I'm trying to invoke the I2CTransfer function below, and immediately getting a System.NotSupportedException. I suspect my marshalling is wrong, but cannot work out the problem.


Here are the C structures:

BOOL I2CTransfer(HANDLE hDev, PI2C_TRANSFER_BLOCK pI2CTransferBlock);


typedef struct {
    I2C_PACKET *pI2CPackets;
    INT32 iNumPackets;
} I2C_TRANSFER_BLOCK, *PI2C_TRANSFER_BLOCK;



typedef struct {
    BYTE byAddr;
    BYTE byRW;
    PBYTE pbyBuf;
    WORD wLen;
    LPINT lpiResult;
} I2C_PACKET, *PI2C_PACKET;

And here are the c# structures I'm attempting:

[DllImport("i2csdk.dll", EntryPoint = "I2CTransfer")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool I2CTransfer(IntPtr hI2C,ref I2C_TRANSFER_BLOCK pI2CTransferBlock);


[StructLayout(LayoutKind.Sequential)]
public struct I2C_TRANSFER_BLOCK
{
    public I2C_PACKET[] pI2CPackets;
    public int iNumPackets;
}


[StructLayout(LayoutKind.Sequential)]
public struct I2C_PACKET
{
    public byte byAddr;
    public byte byRW;
    public byte[] pbyBuf;
    public UInt16 wLen;
    public IntPtr lpiResult;
}

Calling code:

I2C_TRANSFER_BLOCK i2CTransferBlock = new I2C_TRANSFER_BLOCK();
I2C_PACKET packet = new I2C_PACKET();
int result;
IntPtr resultPtr = IntPtr.Zero;


//Populating data...
byte[] pBuf = new byte[1 + pbData.Length];
pBuf[0] = (byte) ((regStart & 0x7F) << 1);
Array.Copy(pbData, 0, pBuf, 1, pbData.Length);

// Fill packet for register write
packet.pbyBuf = pBuf;
packet.wLen = (ushort) pBuf.Length;
packet.byRW = NativeConstants.I2C_RW_WRITE;
packet.byAddr = address;
packet.lpiResult = resultPtr;

// Fill transfer block
i2CTransferBlock.pI2CPackets = new I2C_PACKET[] {packet};
i2CTransferBlock.iNumPackets = 1;

// NotSupportedException here
bool brc = I2CTransfer(port, ref i2CTransferBlock);

The arrays are initialized in C# before calling the method.

I've tried adding [MarshalAs(UnmanagedType.LPArray)]

to the arrays (pI2cPackets, and pbyBuf) to no avail.

This is on Windows CE - compact framework, .NET 3.5.

Is there something obviously wrong with the above translation?

Many thanks in advance.

回答1:

Your problems are in the pointers contained in the structs:

[StructLayout(LayoutKind.Sequential)]
public struct I2C_TRANSFER_BLOCK
{
    public I2C_PACKET[] pI2CPackets; // here ....
    public int iNumPackets;
}


[StructLayout(LayoutKind.Sequential)]
public struct I2C_PACKET
{
    public byte byAddr;
    public byte byRW;
    public byte[] pbyBuf; // .... and here
    public UInt16 wLen;
    public IntPtr lpiResult;
}

You cannot persuade the p/invoke marshaller to marshal pointers to arrays that are embedded inside structs. That form of marshalling is only available for function parameters.

You'll need to declare both of those fields as IntPtr and do the marshalling by hand.



回答2:

By no means I'm an expert on Marshaling but i think i'll throw in few ideas just in case.

1) try to manually marshal arrays (as IntPtr) by allocating memory for them in your code.

2) This line IntPtr resultPtr = IntPtr.Zero; looks suspicious. Normally when you pass a pointer to unmanaged code from managed code it is your job to allocate (and free) memory for this pointer. Check this out for details http://msdn.microsoft.com/en-us/library/0szztey7%28v=VS.90%29.aspx