Séquence de marshalling avec pointeur incorporé de C # au pilote non géré

J’essaie d’interfacer C # (.NET Compact Framework 3.5) avec un pilote de stream Windows CE 6 R2 à l’aide d’appels P / Invoked DeviceIoControl (). Pour l’un des codes IOCTL, le pilote nécessite un tampon d’entrée DeviceIoControl qui correspond à la structure non gérée suivante qui contient un pointeur incorporé:

typedef struct { DWORD address; const void* pBuffer; DWORD size; // buffer size } IOCTL_TWL_WRITEREGS_IN; 

J’ai défini la structure en C # comme:

 [StructLayout(LayoutKind.Sequential)] public struct IoctlWriteRegsIn { public uint Address; public byte[] Buffer; public uint Size; } 

et ma signature P / Invoke en tant que:

 [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool DeviceIoControl(IntPtr hDevice, UInt32 dwIoControlCode, ref IoctlWriteRegsIn lpInBuffer, UInt32 nInBufferSize, UInt32[] lpOutBuffer, UInt32 nOutBufferSize, ref UInt32 lpBytesReturned, IntPtr lpOverlapped); 

Cependant, chaque fois que j’appelle DeviceIoControl () en C #, elle renvoie toujours la valeur false, avec la dernière erreur Win32 de ERROR_INVALID_PARAMETER . Voici un extrait de code source de l’instruction de commutateur IOCTL dans le pilote qui gère le code IOCTL et vérifie les erreurs dans la mémoire tampon d’entrée, où inSize est le paramètre nInBufferSize:

  case IOCTL_TWL_WRITEREGS: if ((pInBuffer == NULL) || (inSize address; pBuffer = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->pBuffer; size = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->size; if (inSize < (sizeof(IOCTL_TWL_WRITEREGS_IN) + size)) { SetLastError(ERROR_INVALID_PARAMETER); break; } rc = TWL_WriteRegs(context, address, pBuffer, size); 

J’ai essayé des tailles de codage rigoureuses qui devraient passer sans succès la vérification d’erreur du conducteur, suggérant qu’il s’agisse d’un problème de marshalling. Je n’ai probablement pas défini correctement le pointeur incorporé dans la structure C #, ni ma signature P / Invoke erronée. Des idées?

Merci d’avance, Ben

Pour référence, je peux parler au pilote à partir de C ++ sans problèmes comme celui-ci:

 IOCTL_TWL_WRITEREGS_IN reg; reg.address = 0x004B0014; unsigned char data = 0xBE; reg.pBuffer = &data; reg.size = sizeof(char); BOOL writeSuccess = DeviceIoControl(driver, IOCTL_TWL_WRITEREGS, &reg, sizeof(IOCTL_TWL_WRITEREGS_IN) + 1, NULL, 0, NULL, NULL); 

Mise à jour: voici ce qui a fonctionné! J’ai utilisé la suggestion IntPtr de JaredPar et nettoyé ma signature P / Invoke selon la suggestion de SwDevMan81:

  [StructLayout(LayoutKind.Sequential)] public struct IoctlWriteRegsIn { public uint Address; public IntPtr Buffer; public uint Size; } // elided byte regData = 0xFF; GCHandle pin = GCHandle.Alloc(regData, GCHandleType.Pinned); IoctlWriteRegsIn writeInBuffer = new IoctlWriteRegsIn{Address = twlBackupRegA, Buffer = pin.AddrOfPinnedObject(), Size = 1}; bool writeSuccess = DeviceIoControl(driverHandle, IoctlTwlWriteRegs, ref writeInBuffer, (uint) Marshal.SizeOf(writeInBuffer) + 1, IntPtr.Zero, 0, ref numBytesReturned, IntPtr.Zero); // P/Invoke signature [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool DeviceIoControl(IntPtr hDevice, UInt32 dwIoControlCode, ref IoctlWriteRegsIn lpInBuffer, UInt32 nInBufferSize, IntPtr lpOutBuffer, UInt32 nOutBufferSize, ref UInt32 lpBytesReturned, IntPtr lpOverlapped); 

Lors du marshaling d’une structure qui a un pointeur inline, vous devez définir la valeur comme un IntPtr et non un tableau.

 [StructLayout(LayoutKind.Sequential)] public struct IoctlWriteRegsIn { public uint Address; public IntPtr Buffer; public uint Size; } 

Essayez-le en remplaçant le tableau d’ octets [] par un IntPtr ..

Vous devrez peut-être spécifier la taille de l’octet [] (remplacez 64 par la taille réelle)

 [StructLayout(LayoutKind.Sequential, Pack=1)] public struct IoctlWriteRegsIn { public uint Address; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public byte[] Buffer; public uint Size; } 

Ensuite, vous devriez pouvoir définir la taille comme ceci:

 IoctlWriteRegsIn io_struct = new IoctlWriteRegsIn(); io_struct.Address = 5; io_struct.Buffer = new byte[1] { 0xBE }; // size of buffer, not struct io_struct.Size = 1;//Marshal.SizeOf(io_struct); 

Je changerais l’appel P / Invoke en ceci:

 [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool DeviceIoControl(IntPtr hDevice, UInt32 dwIoControlCode, ref IoctlWriteRegsIn lpInBuffer, UInt32 nInBufferSize, IntPtr lpOutBuffer, UInt32 nOutBufferSize, ref UInt32 lpBytesReturned, IntPtr lpOverlapped); 

et appelez-le en utilisant ceci:

 uint num_bytes = (uint)Marshal.SizeOf(writeInBuffer); bool writeSuccess = DeviceIoControl(driverHandle, IoctlTwlWriteRegs, ref writeInBuffer, num_bytes, IntPtr.Zero, 0, ref numBytesReturned, IntPtr.Zero);