Forum: PC-Programmierung SetupDiGetDeviceInterfaceDetail ERROR_INVALID_USER_BUFFER


von monyca (Gast)


Lesenswert?

Hello,

ich bekomme getlasterror = 1784 wenn ich versuche
SetupDiGetDeviceInterfaceDetail zum 2.ten auszuführen in VB .NET

das erste SetupDiGetDeviceInterfaceDetail klappt ja

<DllImport("setupapi.dll", CharSet:=CharSet.Auto)> _
Function SetupDiGetDeviceInterfaceDetail( _
ByVal DeviceInfoSet As IntPtr, _
ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA, _
ByVal DeviceInterfaceDetailData As IntPtr, _
ByVal DeviceInterfaceDetailDataSize As Integer, _
ByRef RequiredSize As Integer, _
ByVal DeviceInfoData As IntPtr) As Boolean
End Function


Public Sub init()
Dim GUID_DEVINTERFACE_GRMNUSB As System.Guid
Dim DIDI As SP_DEVICE_INTERFACE_DATA
Dim DIDC As SP_DEVINFO_DATA
Dim DIDID As SP_DEVICE_INTERFACE_DETAIL_DATA
DIDID = Nothing
DIDI.cbSize = Marshal.SizeOf(DIDI)
DIDC.cbSize = Marshal.SizeOf(DIDC)

GUID_DEVINTERFACE_GRMNUSB = New
System.Guid("2C9C45C28E7D4C08A12D816BBAE722C0")

Dim theDevInfo As Long
Dim BufferLen As Integer

theDevInfo = SetupDiGetClassDevs(GUID_DEVINTERFACE_GRMNUSB,
vbNullString, 0, DIGCF_DEVICEINTERFACE Or DIGCF_PRESENT)

If Not SetupDiEnumDeviceInterfaces(theDevInfo, 0,
GUID_DEVINTERFACE_GRMNUSB, 0, DIDI) Then
Handle = 0
Return
End If
DIDID = Nothing

///////////////funktioniert//////////////////////
SetupDiGetDeviceInterfaceDetail(theDevInfo, DIDI, Nothing, 0,
BufferLen, IntPtr.Zero)

Dim DetailBuffer As IntPtr = Marshal.AllocHGlobal(BufferLen)
DIDID.cbSize = 5 'Marshal.SizeOf(DIDID)

///////////////////klappt nicht/////////////////////
SetupDiGetDeviceInterfaceDetail(theDevInfo, DIDI, DetailBuffer,
BufferLen, BufferLen, IntPtr.Zero)
MsgBox(GetLastError)

end sub

was stimmt denn nicht mit meinem code?

danke

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Du verwendest den Parameter BufferLen - der wird höchstwahrscheinlich
von der Funktion SetupDiGet..bla verändert, da er als vorletzter
Parameter "byref" übergeben wird.

Du solltest also vor dem zweiten Aufruf dafür sorgen, daß BufferLen
sinnvolles enthält.

Sieh Dir im Debugger mal den Wert vor dem ersten und nach dem ersten
Aufruf von SetupDiGet..bla an.

von monyca (Gast)


Lesenswert?

bufferlen = 0 vor dem 1.ten aufruf

bufferlen = 162 vor dem 2.ten Aufruf

wie gesagt der erste aufruf funktioniert und damit bekomme ich
BufferLen das ich beim zweiten aufruf benutze möchte! klappt aber nicht

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ah, ich beginne zu begreifen, was Du da anstellst. Mit dem ersten Aufruf
bestimmst Du die nötige Puffergröße, forderst dann den Puffer an und
übergibst den beim zweiten Aufruf ...
Das ist eine bei den komplexeren API-Funktionen von Windows übliche
Vorgehensweise.

Vermutlich liegt das Problem jetzt im Rückgabewert von
Marshal.AllocHGlobal; den scheint SetupDiGet..bla nicht zu mögen.


Ja, das ist in der Tat so. Das dritte Argument von SetupDiGet..bla ist
der Pointer auf Deinen so angeforderten Speicher. Das ist -laut
MSDN-Dokumentation- eine Struktur vom Typ
SP_DEVICE_INTERFACE_DETAIL_DATA.

Und in der muss das Element.cbSize korrekt gesetzt sein, siehe auch
folgenden Auszug der Dokumentation:

   DeviceInterfaceDetailData
   [out] Pointer to an SP_DEVICE_INTERFACE_DETAIL_DATA
   structure that receives information about the specified
   interface.
*  You must set the cbSize member to sizeof
*  (SP_DEVICE_INTERFACE_DETAIL_DATA) before calling this
*  function.
   The cbSize member always contains the size of the fixed
   part of the data structure, not a size reflecting the
   variable-length string at the end.

   This parameter must be NULL if the DeviceInterfaceDetailSize
   parameter is zero.

(siehe Hervorhebung).


Du setzt zwar DIDID.cbSize auf diese Größe, aber ich sehe in Deinem
Code nicht, wie DIDID und der Puffer miteinander verknüpft werden.

Da liegt wohl der sprichwörtliche Hase im Pfeffer.

von monyca (Gast)


Lesenswert?

ja ich habe mir auch die doku von microsoft angeschaut und gemerkt dass
der dritte argument von SETUPDIGETINTERFACEDETAILDATA vom typ
SP_DEVICE_INTERFACE_DETAIL_DATA ist

wenn ich es genauso deklariere dann bekomme ich eine
PInvokeStackImbalance exception

wenn ich der 3.te argument aber als intptr deklariere dann gibt es
keine exception aber DIDID kann ich einfach nicht mehr irgendwo
benutzen

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Naja, aber das ist das Problem. Die Funktion möchte einen Speicherblock
übergeben bekommen, in dem an einer bestimmten Stelle ein bestimmter
Wert steht. Ob dieser Speicherblock nun als eine bestimmte Struktur
deklariert ist, oder als Byte-Array, das ist dafür irrelevant. Wichtig
ist, daß der Wert darin an der richtigen Stelle steht.

Du aber arbeitest mit zwei getrennten Variabeln (und folglich auch
Speicherblöcken), nämlich Deiner Struktur DIDID, in die Du den
entsprechenden Wert ja auch einträgst, und einem davon völlig
unabhängigen Speicherblock, den Du mit Marshal.AllocHGlobal
anforderst.
Diesen Speicherblock intialisierst Du nicht weiter.

Möglicherweise kannst Du mit Marshal.Copy den Inhalt von DIDID in
Deinen Puffer kopieren, aber da bin ich wirklich überfragt.

Das ist ein grundlegendes Problem beim Arbeiten an der Schnittstelle
zwischen "managed" und "unmanaged" Code, wie er beim Gebrauch des
Dotnet-Geraffels und Win32-API-Funktionen auftritt.

Kennt VB.net das Konzept von "unions", also Variablen, die einen
Speicherbereich belegen, aber auf unterschiedliche Art und Weise
interpretiert werden können? Damit könntest Du DIDID wahlweise als
Struktur und als Array ansprechen. Gibt es irgendwelche Möglichkeiten
der Typkonvertierung?


Im Grunde genommen müsstest Du DIDID nicht als Struktur, sondern als
Pointer auf diese Struktur deklarieren und den Rückgabewert von
Marshall.AllocHGlobal in diesen Strukturtyp umwandeln können; wie man
das mit dem Dotnetgeraffel hinbekommt, kann Dir hoffentlich jemand
anderes erklären.

In C wäre die Angelegenheit mit wenigen Zeilen gelöst:

// so wird der Pointer deklariert
SP_DEVICE_INTERFACE_DETAIL_DATA* p;

// und so wird Speicher angefordert und dem Pointer zugewiesen
p = (SP_DEVICE_INTERFACE_DETAIL_DATA *) malloc(BufferLen);


Und das war's auch schon.

Manches ist in C oder "echtem" C++ (nicht "managed C++" oder gar
C#!) halt doch einfacher.

Wie auch immer: Viel Erfolg.

von René K. (king)


Lesenswert?

> Und das war's auch schon.

Ohne jetzt alles genau gelesen zu haben: Das war's garantiert noch
nicht. Gemäß Dokumentation ist vor dem Aufruf von
SetupDiGetDeviceInterfaceDetail das Member cbSize von
SP_DEVICE_INTERFACE_DETAIL_DATA zu initialisieren. Das ist, wie vom OP
vorgegeben, schon die 5. In der UNICODE Version ist das ellerdings 6.
Deswegen scheint mir dies das Problem zu sein: CharSet:=CharSet.Auto.

Entweder wird CharSet.Ansi mit 5 für cbSize verwendet oder
CharSet.Unicode mit 6 für cbSize. Dann sollte aber aber auch nicht
SetupDiGetDeviceInterfaceDetail referenziert werden, sondern
SetupDiGetDeviceInterfaceDetailA (Ansi) oder
SetupDiGetDeviceInterfaceDetailW (Unicode).

Allerdings bin ich mit dem .NET-Geraffel auch nicht sonderlich
sattelfest.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wie auch immer, das Strukturelement muss initialisiert werden. Und das
geschieht in Monycas Code nicht.

von René K. (king)


Lesenswert?

> Wie auch immer, das Strukturelement muss initialisiert werden. Und
> das geschieht in Monycas Code nicht.

Stimmt, Du hast recht. Jetzt, wo ich aufgrund dieser Aussage nochmnals
geschaut habe, fällt mir auf das sich die Zeile

DIDID.cbSize = 5 'Marshal.SizeOf(DIDID)

gar nicht auf den Parameter 4 von SetupDiGetDeviceInterfaceDetail
bezieht. Aber wie soll er da auch drauf kommen, wenn er das hier sogar
falsch vorgemacht bekommt, mit der Bemerkung: "Das reicht". :-)

Damit wird's vollständiger:
// und so wird cbSize initialisiert
p->cbSize = sizeof(*p);

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sag nicht falsch, sag meinetwegen unvollständig.

Und danke für die Vervollständigung.

von René K. (king)


Lesenswert?

> Sag nicht falsch, sag meinetwegen unvollständig.

Recht hast Du, also: "Unvollständig". :-)

Ich bin auch noch nicht so ganz bei der Sache. Ich hoffe, Du lässt das
als Entschuldigung gelten.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Soweit kommts noch, daß wir uns beieinander entschuldigen müssten ...

Mal sehen, wie Monyca mit den so gewonnenen Erkenntnissen klarkommt.
Ich vermute ja das grundlegende Problem in dem komplexen Gehampel, das
nötig ist, um in Dot-Net-Sprachen auf "richtigen" Speicher zugreifen
zu können, wie er in Win32-API-Funktionen verwendet wird.
Zugegeben, alles, was ich gegen Dot-Net vorzubringen habe, sind in
erster Linie Vorurteile, aber sowas wie das Problem hier verfestigt die
nur ...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.