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
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.
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
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.
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
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.
> 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.
Wie auch immer, das Strukturelement muss initialisiert werden. Und das geschieht in Monycas Code nicht.
> 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);
Sag nicht falsch, sag meinetwegen unvollständig. Und danke für die Vervollständigung.
> 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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.