Lors du développement de l'outil USBLIST disponible en téléchargement sur mon site, je me suis arraché les cheveux pendant un bon moment sur l'appel de SetupDiOpenDeviceInterface en CSHARP (C#). En effet cette API n'est pas exposée dans ce langage et il faut appeler la fonction native de setupapi.dll par pinvoke. Cependant tous mes appels se sont soldés par une erreur ERROR_INVALID_USER_BUFFER sans comprendre pourquoi. Les recheches sur Internet et les questions sur les forums se sont révélées infructueuses et aucun exemple de code source fonctionnel n'était disponible pour s'en inspirer.
Le seul moyen de trouver la cause du problème a été de développer un exemple en C++ et de comparer son exécution en assembleur avec le programme en C# pour comprendre les différences.
Voici les extraits clés de la session de debugging:
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);[...]
if (SetupDiOpenDeviceInterface(
DeviceInfoSet,
lpdbv2->dbcc_name,
0,
&DeviceInterfaceData
)== 0)
Au breakpoint suivant, l'instruction compare la taille de DeviceInterfaceData.cbSize. Le registre ecx contient la valeur de la donnée.
eax=76d9c680 ebx=76d9c65c ecx=0012fa14 edx=00000000 esi=00000000 edi=001e2c28
eip=76cbca46 esp=0012f460 ebp=0012f464 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
SETUPAPI!DeviceInterfaceDataFromNode+0x3a:
76cbca46 83391c cmp dword ptr [ecx],1Ch ds:0023:0012fa14=0000001c
0:000> dd ecx
0012fa14 0000001c cccccccc cccccccc cccccccc
0012fa24 cccccccc cccccccc cccccccc cccccccc
0012fa34 cccccccc 001e2c28 cccccccc cccccccc
0012fa44 001e22f8 cccccccc cccccccc cccccccc
0012fa54 cccccccc cccccccc 001e22f8 cccccccc
0012fa64 cccccccc cccccccc cccccccc cccccccc
0012fa74 cccccccc cccccccc cccccccc cccccccc
0012fa84 cccccccc cccccccc cccccccc cccccccc
Le même code en # (apparemment)!
SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA(); // Note it should be SP_DEVICE_INTERFACE_DATA but the structures are identical
// DeviceInfoData.cbSize = Marshal.SizeOf(DeviceInfoData);
DeviceInfoData.cbSize = 28;
if (!SetupDiOpenDeviceInterfaceA(
hDev,
// dvi.dbcc_name,
@"\\?\USB#VID_07AB&PID_FCCA#FW520_200000000000B703#{a5dcbf10-6530-11d2-901f-00c04fb951ed}",
0,
ref DeviceInfoData))
Le breakpoint à la même instruction:
0:000> t
Breakpoint 1 hit
eax=76d9c680 ebx=76d9c65c ecx=002aeaac edx=00000000 esi=00000000 edi=005f70c0
eip=76cbca46 esp=002ae820 ebp=002ae824 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
setupapi!DeviceInterfaceDataFromNode+0x3a:
76cbca46 83391c cmp dword ptr [ecx],1Ch ds:0023:002aeaac=00612a18
0:000> dd 002aeaac
002aeaac 00612a18 00196fd8 5c3f5c5c 23425355
002aeabc 5f444956 42413730 44495026 4343465f
002aeacc 57462341 5f303235 30303032 30303030
002aeadc 30303030 33303742 35617b23 66626364
002aeaec 362d3031 2d303335 32643131 3130392d
002aeafc 30302d66 66343063 31353962 007d6465
002aeb0c 00196c00 002aeb24 79e7b080 00000006
002aeb1c 00196ca8 00000006 002aeb30 79e7b06b
0:000> dd poi(ecx)
00612a18 0000001c 00000000 00000000 00000000
00612a28 00000000 00000000 00000000 abababab
00612a38 abababab feeefeee 00000000 00000000
00612a48 63574a97 00000e3e 00612ae0 00581fc0
Forcément, ça plante car ecx ne contient pas 0000001C mais l'adresse mémoire où est stocké 1C. La fonction sort donc en erreur et retourne 0x6f8 ERROR_INVALID_USER_BUFFER
Pour résumer,
En C++ , ecx contient la longueur de la structure.
En c#, par contre, ecx contient l'adresse mémoire de la longueur de la structure.
Pour résoudre le problème, il a suffit de remplacer la classe SP_DEVINFO_DATA par une structure qui n'a donc pas besoin d'être instanciée.
Le code source est disponible ici: http://www.ilinfo.fr/docs/DeviceInformation.cs
Post Original : http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/2eca9b5d-fd96-4181-943f-3157a09020f0