2009-11-05 2 views
3

J'essaie d'interfacer C# (.NET Compact Framework 3.5) avec un pilote de flux Windows CE 6 R2 à l'aide d'appels DeviceIoControl() P/Invoqués. Pour l'un des codes IOCTL, le pilote a besoin d'un tampon d'entrée DeviceIoControl qui est le struct non géré suivant qui contient un pointeur intégré:Structure de regroupement avec pointeur intégré de C# vers un pilote non géré

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

je définissais la struct en C# comme:

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

et mon signature P/Invoke comme:

[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#, il retourne toujours faux, avec une dernière erreur Win32 de ERROR_INVALID_PARAMETER. Voici un extrait de code source de l'instruction switch IOCTL du pilote qui gère le code IOCTL et fait la vérification des erreurs sur la mémoire tampon d'entrée, où inSize est le paramètre nInBufferSize:

case IOCTL_TWL_WRITEREGS: 
     if ((pInBuffer == NULL) || 
      (inSize < sizeof(IOCTL_TWL_WRITEREGS_IN))) 
      { 
      SetLastError(ERROR_INVALID_PARAMETER); 
      break; 
      } 
     address = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->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é dur de codage des tailles qui doivent passer la erreur de vérification du pilote sans succès, ce qui suggère que c'est un problème de triage. Je n'ai probablement pas correctement défini le pointeur intégré dans la structure C# ou ma signature P/Invoke est incorrecte. Des idées?

Merci à l'avance, Ben

Pour référence, je peux parler au conducteur de C++ sans problème 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é! occasion suggestion IntPtr de JaredPar et nettoyé ma signature P/Invoke par 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); 
+0

Vous mettez l'IntPtr à une valeur aléatoire e en mémoire. Vous devez réellement allouer le pointeur à la mémoire réelle – JaredPar

+0

Oups, bonne prise. Ça fonctionne maintenant! Merci beaucoup, j'apprécie vraiment votre aide. –

+0

pouvez-vous s'il vous plaît m'expliquer cette ligne 'GCHandle pin = GCHandle.Alloc (regData, GCHandleType.Pinned);' que fait-il exactement? – FosterZ

Répondre

2

Lorsque marshaling une struct qui a un pointeur en ligne, vous devez définir la valeur comme IntPtr et non un tableau

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

Essayé cela sans succès. J'ai défini la structure comme vous l'avez suggéré, puis effectué ces appels: IoctlWriteRegsIn writeInBuffer = new IoctlWriteRegsIn {Adresse = twlBackupRegA, Data = new IntPtr (0xFF), Size = 1}; bool writeSuccess = DeviceIoControlWrite (driverHandle, IoctlTwlWriteRegs, réf writeInBuffer, (uint) Marshal.SizeOf (IoctlTwlWriteRegs) +1, nouvelle uint [0], 0, réf numBytesReturned, IntPtr.Zero); La taille et (uint) Marshal.SizeOf (IoctlTwlWriteRegs) +1 correspond à mon code C++, donc je ne pense pas que ce soit la cause. –

+0

Également essayé d'épingler l'IntPtr avant main sans succès. –

+0

@Ben, pouvez-vous poster ceci dans votre question? Il est vraiment difficile à suivre dans les commets – JaredPar

1

Donnez un coup de feu en remplaçant l'octet [] tableau avec un IntPtr ..

+1

Et assurez-vous que vous épinglez la mémoire que les références IntPtr et détacher lorsque vous avez terminé. –

+0

Et montre que GC comment c'est fait :) ie. que vous serez gentil/peu fréquent et rapide à redonner/dégager :) –

0

Vous pourriez avoir à préciser la taille de l'octet [] (remplacer 64 avec 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; 
} 

alors vous devriez être en mesure de 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 le P/Invoke appel à 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 l'appeler 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); 
+0

J'ai essayé ceci et cela n'a pas fonctionné. Je peux me débrouiller en passant un seul octet - je n'ai pas besoin d'un tampon complet puisque j'ai seulement besoin d'écrire un registre de 8 bits à la fois - alors j'ai essayé ceci avec SizeConst = 1 sans chance. –

+0

Même erreur? Pouvez-vous changer le lpOutBuffer en IntPtr lpOutBuffer et essayer à nouveau – SwDevMan81

+0

Pourriez-vous également poster votre appel à DeviceIoControl – SwDevMan81

Questions connexes