0

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.

4
  • please, show the code, which you use to instantiate those structures and call the method. Commented Jul 5, 2013 at 11:13
  • Your username confused me there :) Done. Commented Jul 5, 2013 at 11:18
  • Thats what i do. I lurk around StackOverflow and steal people's names. Commented Jul 5, 2013 at 11:41
  • The pinvoke marshaller for .NET CF has lots of limitations. You'll probably need to declare that array as IntPtr and use GCHandle.Alloc() + AddrOfPinnedObject() to marshal the array yourself. The CallingConvention is another one, you've ignored it. Only __stdcall is supported, you must declare it that way in your native code. Commented Jul 5, 2013 at 11:51

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

2 Comments

Using IntPtr and GCHandle.Alloc() + AddrOfPinnedObject() as mentioned by @Hans Passant did the trick - thanks.
LPArray is for parameters and cannot be used on struct members.
0

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 Comments

Be really careful about how you handle lpiResult. Where should it point? Does it point into the I2C library code? Does it point to your own data? If it points to your own data, will the call be synchronous? If so, great. If not, you're in for a world of pain if you point it to something contained in a managed object. If you're totally clear on the ownership and lifespan of the dynamically allocated memory and so on, great. If not, consider writing a C adapter to make the ownership 100% on the C side.
If its points to own data, and that data is pinned, does that remove that concern?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.