Datentyp von Parameter einer WinAPI dynamisch an x32/x64 anpassen

  • VB.NET
  • .NET (FX) 3.0–3.5

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von Marcus Gräfe.

    Datentyp von Parameter einer WinAPI dynamisch an x32/x64 anpassen

    Ich habe folgende Deklaration:

    VB.NET-Quellcode

    1. Public Declare Function GetClientRect Lib "user32" (ByVal hwnd As Integer, ByRef lpRect As RECT) As Integer

    Beim hwnd meckert VB, weil der Datentyp scheinbar je nachdem, ob ich ein 32- oder ein 64-Bit-Betriebssystem habe (bzw. der Anwender meines Programms), entweder Integer oder Long sein muss.

    Wie macht man das dynamisch?
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Versuch es mal mit IntPtr.

    Declare Function ist in .Net btw. Legacy-Code, verwende stattdessen DllImport. Auf pinvoke.net findest du auch zu fast allen Win32-APIs die passenden .Net-Deklarationen:
    pinvoke.net/default.aspx/user32/GetClientRect.html
    Danke. Wenn du mir jetzt noch sagen könntest, wie ich einen String in den Datentyp System.IntPtr bekomme, so wäre ich glücklich. ;)

    Vorher war es Integer und das war simpel.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Marcus Gräfe schrieb:

    einen String in den Datentyp System.IntPtr bekomme
    Gugst Du hier.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wieso hast du denn einen String für das Handle?
    Ich mein du kannst einen String zu Integer konvertieren und dann mit New IntPtr(Integer) einen IntPtr erzeugen.
    Das ist meine Signatur und sie wird wunderbar sein!

    RodFromGermany schrieb:

    Gugst Du hier.

    So richtig schlau bin ich aus dem Thread jetzt nicht geworden. Muss ich da eine DLL verwenden? Oder war die nur als Beispiel gedacht?

    Mono schrieb:

    Wieso hast du denn einen String für das Handle?

    Weil der als Parameter an mein Programm übergeben wird (und das ist zwangsläufig ein String). Das ist die Windows-Art, das Handle des Previewfensters an einen Bildschirmschoner zu übergeben.

    Ich habe es nun laut social.msdn.microsoft.com/Foru…ntptr?forum=csharpgeneral so gemacht:

    VB.NET-Quellcode

    1. Dim preview_hwnd As System.IntPtr = New IntPtr(Convert.ToInt32(arg, 16))

    (arg ist string)

    Erhalte nun aber eine neue Warnung (keinen Fehler):

    Quellcode

    1. CA1901: Wie im Code deklariert, ist der 'dwNewLong'-Parameter von P/Invoke 'modStart.NativeMethods.SetWindowLong(IntPtr, Integer, IntPtr)' auf 64-Bit-Plattformen 8 Bytes breit. Dies stellt einen Fehler dar, da in der eigentlichen systemeigenen Deklaration dieser API für 64-Bit-Plattformen eine Sollbreite von 4 Bytes angegeben ist. In der MSDN-Dokumentation zum Plattform-SDK finden Sie Informationen zur Bestimmung des Datentyps, den Sie anstatt 'IntPtr' verwenden sollten

    So ist das deklariert:

    VB.NET-Quellcode

    1. <DllImport("user32.dll")>
    2. Public Shared Function SetWindowLong(hWnd As System.IntPtr, nIndex As Integer, dwNewLong As IntPtr) As Integer
    3. End Function

    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Marcus Gräfe schrieb:

    als Beispiel
    , wie Du einen String in einen IntPtr reinpackst, das DLL-Zeugs muss Dich nicht interessieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    dwNewLong ist ja auch kein Pointer sondern eine 32 Bit-Ganzzahl, was sogar zwei mal im Namen steht (dw = DWORD = 32 Bit unter Windows und Long = 32 Bit in C).
    Die Größe dieses Parameters ist damit Platformunabhängig und in .Net immer als Integer zu deklarieren.

    Siehe auch wieder:
    pinvoke.net/default.aspx/user32/SetWindowLong.html
    pinvoke.net/default.aspx/user32/SetWindowLong.html laut hier wäre es auch ein IntPtr bei der VB.NET Deklaration. Ist nicht so ganz korrekt... bissl doof manchmal die Seite. Am besten orientierst dich an der C# Deklaration, die ist meistens ok.
    Das ist meine Signatur und sie wird wunderbar sein!
    Jetzt habe ich auf euer Anraten hin extra die Deklaration von dort kopiert, aber die ist dann ja offenbar falsch. Denn dort ist die genau so definiert, wie ich es geschrieben hatte. EDIT: Mono hat diese Erkenntnis nun in seinen Post editiert ;)

    Habe es nun in Integer geändert, Warnung ist weg.

    Seltsam ist, dass ich folgenden Code habe, in dem die Funktion benutzt wird, aber dort für dwNewLong ein Handle übergeben wird:

    VB.NET-Quellcode

    1. ' GWL_PARENT des Formulars an das Previewfenster anpassen
    2. NativeMethods.SetWindowLong(frm.Handle, NativeMethods.GWL_HWNDPARENT, CType(preview_hwnd, Integer))

    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Marcus Gräfe schrieb:

    Seltsam ist, dass ich folgenden Code habe, in dem die Funktion benutzt wird, aber dort für dwNewLong ein Handle übergeben wird
    Da du GWL_HWNDPARENT setzt, ist der Parameter halt ein Handle, kann aber auch was ganz anderes sein, je nachdem, was du als zweites Argument angibst. In C sieht man Typen viel lockerer als in .Net.

    Marcus Gräfe schrieb:

    dort ist die genau so definiert, wie ich es geschrieben hatte.
    Schau auf den C#-Code. ;)
    Aber wenn das Handle nun einen Wert hätte, der nicht in einen Integer passt, so würde ich doch einen Fehler erhalten. Denn scheinbar kann das Handle so groß werden, wenn man extra den Datentyp anpassen muss (bei den anderen Parametern). Ist die API da nicht ganz durchdacht oder ist ein so großer Handle-Wert utopisch?
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Nun, ich weiß natürlich nicht, wie Windows programmiert wurde.
    Aber in diesem Fall handelt es sich im Besonderen um ein Windowhandle, und Windowhandle werden ausschließlich vom System zugewiesen. Ich halte es für wahrscheinlich, dass Windows keine unbeschränkte Anzahl an Fenstern zulässt, also lässt sich die maximale Größe eines Windowhandles in einem bestimmten Rahmen halten.
    Auf der verlinkten Seite steht:
    When compiling for 32-bit Windows, SetWindowLongPtr is defined as a call to the SetWindowLong function.

    Gilt das nur für C++ oder auch für VB.NET? Denn bei pinvoke.net steht eine eigene Funktion, die je nach Betriebssystem die jeweilige API aufruft.

    EDIT: Ich habe jetzt ein ganz anderes Problem:

    Mein alter Code funktioniert:
    funktionierender Code

    VB.NET-Quellcode

    1. Public Const SWP_NOACTIVATE = &H10
    2. Public Const SWP_NOZORDER = &H4
    3. Public Const SWP_SHOWWINDOW = &H40
    4. Public Const GWL_STYLE = -16
    5. Public Const WS_CHILD = &H40000000
    6. Public Const GWL_HWNDPARENT = -8
    7. Public Const HWND_TOP = 0
    8. Public Structure RECT
    9. Public left As Integer
    10. Public top As Integer
    11. Public right As Integer
    12. Public bottom As Integer
    13. End Structure
    14. Public Declare Function GetClientRect Lib "user32" (
    15. ByVal hwnd As Integer,
    16. ByRef lpRect As RECT) As Integer
    17. Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (
    18. ByVal hwnd As Integer,
    19. ByVal nIndex As Integer) As Integer
    20. Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (
    21. ByVal hwnd As Integer,
    22. ByVal nIndex As Integer,
    23. ByVal dwNewInteger As Integer) As Integer
    24. Public Declare Function SetWindowPos Lib "user32" (
    25. ByVal hwnd As Integer,
    26. ByVal hWndInsertAfter As Integer,
    27. ByVal x As Integer,
    28. ByVal y As Integer,
    29. ByVal cx As Integer,
    30. ByVal cy As Integer,
    31. ByVal wFlags As Integer) As Integer
    32. Public Declare Function SetParent Lib "user32" (
    33. ByVal hWndChild As Integer,
    34. ByVal hWndNewParent As Integer) As Integer

    VB.NET-Quellcode

    1. Private Sub SetForm(ByRef frm As frmMain, ByRef arg As String)
    2. Dim style As Integer
    3. Dim preview_hwnd As Integer = Integer.Parse(CType(arg, String))
    4. Dim r As New RECT
    5. ' Get the preview window's size.
    6. GetClientRect(preview_hwnd, r)
    7. With frm
    8. .WindowState = FormWindowState.Normal
    9. .FormBorderStyle = FormBorderStyle.None
    10. .Width = r.right
    11. .Height = r.bottom
    12. End With
    13. ' Add the WS_CHILD style to the form.
    14. style = GetWindowLong(frm.Handle.ToInt32, GWL_STYLE)
    15. style = style Or WS_CHILD
    16. SetWindowLong(frm.Handle.ToInt32, GWL_STYLE, style)
    17. ' Reparent the form into the preview window.
    18. SetParent(frm.Handle.ToInt32, preview_hwnd)
    19. ' Set the form's GWL_PARENT value to the preview window.
    20. SetWindowLong(frm.Handle.ToInt32, GWL_HWNDPARENT, preview_hwnd)
    21. ' Position the form in the preview window.
    22. SetWindowPos(frm.Handle.ToInt32, 0, r.left, 0, r.right, r.bottom,
    23. SWP_NOACTIVATE Or SWP_NOZORDER Or SWP_SHOWWINDOW)
    24. End Sub


    Mein neuer aber nicht mehr!
    NICHT funktionierender Code

    VB.NET-Quellcode

    1. Friend NotInheritable Class NativeMethods
    2. ' Die nachfolgenden Deklarationen sind für die Darstellung der Vorschau:
    3. Public Const SWP_NOACTIVATE = &H10
    4. Public Const SWP_NOZORDER = &H4
    5. Public Const SWP_SHOWWINDOW = &H40
    6. Public Const GWL_STYLE = -16
    7. Public Const WS_CHILD = &H40000000
    8. Public Const GWL_HWNDPARENT = -8
    9. Public Const HWND_TOP = 0
    10. Public Structure RECT
    11. Public left As Integer
    12. Public top As Integer
    13. Public right As Integer
    14. Public bottom As Integer
    15. End Structure
    16. <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    17. Public Shared Function GetClientRect(ByVal hWnd As IntPtr, ByRef lpRECT As RECT) As Integer
    18. End Function
    19. <DllImport("user32.dll", SetLastError:=True)>
    20. Public Shared Function GetWindowLong(hWnd As IntPtr, nIndex As Integer) As Integer
    21. End Function
    22. <DllImport("user32.dll")>
    23. Public Shared Function SetWindowLong(hWnd As IntPtr, nIndex As Integer, dwNewLong As Integer) As Integer
    24. End Function
    25. <DllImport("user32.dll", SetLastError:=True)>
    26. Public Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean
    27. End Function
    28. <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    29. Public Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
    30. End Function
    31. End Class

    VB.NET-Quellcode

    1. Private Sub SetForm(ByRef frm As frmMain, ByRef arg As String)
    2. ' -----------
    3. ' Bildschirmschoner an die Previewfläche anpassen
    4. ' -----------
    5. Dim style As Integer
    6. Dim preview_hwnd As System.IntPtr = New IntPtr(Convert.ToInt32(arg, 16))
    7. Dim r As New NativeMethods.RECT
    8. ' Größe des Previewfensters ermitteln und auf Form anwenden
    9. NativeMethods.GetClientRect(preview_hwnd, r)
    10. With frm
    11. .WindowState = FormWindowState.Normal
    12. .Width = r.right
    13. .Height = r.bottom
    14. End With
    15. ' Den "WS_CHILD"-Stil auf das Formular anwenden
    16. style = NativeMethods.GetWindowLong(frm.Handle, NativeMethods.GWL_STYLE)
    17. style = style Or NativeMethods.WS_CHILD
    18. NativeMethods.SetWindowLong(frm.Handle, NativeMethods.GWL_STYLE, style)
    19. ' Das Formular in das Previewfenster einsetzen
    20. NativeMethods.SetParent(frm.Handle, preview_hwnd)
    21. ' GWL_PARENT des Formulars an das Previewfenster anpassen
    22. NativeMethods.SetWindowLong(frm.Handle, NativeMethods.GWL_HWNDPARENT, CType(preview_hwnd, Integer))
    23. ' Position des Formulars im Previewfenster
    24. NativeMethods.SetWindowPos(frm.Handle, CType(0, IntPtr), r.left, 0, r.right, r.bottom, NativeMethods.SWP_NOACTIVATE Or NativeMethods.SWP_NOZORDER Or NativeMethods.SWP_SHOWWINDOW)
    25. End Sub

    Es geht um die Darstellung meines Bildschirmschoners im Previewfenster des Windows-Dialogs zur Screensaver-Auswahl.

    Der Fehler ist nun, dass keine Preview mehr angezeigt wird und jede Menge Screensaver-Instanzen im Taskmanager auftauchen, alle mit hoher CPU- und RAM-Auslastung.

    Die Unterschiede liegen in der Optimierung für VB.NET (nehme ich VB6-Style-Code, geht alles).

    EDIT #2: Ich habe beim VB6-Code nun wieder das mit dem Class NativeMethods gemacht und die Datentypen der hwnd-Variablen auf IntPtr gesetzt (mehr nicht). Läuft immer noch, aber nun wenigstens ohne Warnmeldungen. Ein Nutzen von DllImport scheint in meinem Fall nicht zu gehen.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Marcus Gräfe“ ()