MarshalAs(UnmanagedType.Type) als Parameter

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von Facebamm.

    MarshalAs(UnmanagedType.Type) als Parameter

    Hallo zusammen,

    ich bin heute mehrfach auf ein Syntax wie diesen gestoßen
    int ContextSensitiveHelp([In, MarshalAs(UnmanagedType.Bool)] Boolean fEnterMode);
    Meine fragen sind nun:
    Für was brauch ich das und wann ist es zwingend nötig? ( [MarshalAs(UnmanagedType.Bool)] und [In]

    MfG

    Facebamm
    Auch wenn ich wenig damit am Hut habe: Sobald Du mit P/Invoke-, COM- oder allgemein Unmanaged-Methoden arbeitest, die Du aufrufen willst, braucht es m.E. spezielle Parameterdeklaration, weil Boolean und Co. ja .Net sind. In als Kennzeichnung für: Du übergibst einen Parameter und fertig, [Out] für: die Funktion modifiziert den Parameterwert (immer dann relevant, wenn die Funktion quasi mehr als einen Rückgabewert anbieten will). [In, Out]: Naja, 3x darfst Du raten.
    Aber es sollte nicht allzu lange dauern, bis ein Experte für dieses Thema ausführlich antwortet.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Damit sagst du wie die verwalteten Daten bei dem nativen Aufruf gemarshallt werden bzw. andersherum genauso. So sagst du dem Marshaller bspw, dass du einen out Parameter den du als StringBuilder deklariert als LPSTR marshallen willst.
    @Facebamm Da brauchst Du Dich eigentlich nicht drum zu kümmern.
    wenn Du eine eigene C / C++-DLL hast, gugst Du hier: Austausch von Daten zwischen einer VB.NET-exe und einer C-DLL, 32 und 64 Bit
    Wenn Du eine Third Party DLL bekommst, bekommst Du die Deklarationen mitgeliefert.
    Wenn Du eine Sytem-DLL aufrufen willst, findest Du deren Deklaration unter pinvoke.net/ und der MSDN.
    Außerdem hilft Dir die Codeanalyse Deines Projekts.
    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!
    @ErfinderDesRades
    Nö, das geht schon. Die Attribut-Syntax ist OK so. Auch die Syntax zur Deklaration einer Funktion (Ein Parameter, Rückgabetyp int).
    Ungewöhnlich ist allenfalls, dass "Boolean" verwendet wird, ist aber keineswegs falsch. Der Typ, der in mscorlib.dll deklariert ist, heißt nämlich wortwörtlich Boolean (genauso wie Int32, Single, etc.). bool, int, float, etc. sind nur Aliase, die von C# bereitgestellt werden.
    Dass die Funktion keinen Körper hat (das Semikolon am Ende) lässt sich dadurch erklären, dass die Funktion als extern deklariert ist.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Gonger96, also wenn ich zb von c++ ein integer zurück bekomme wie HRESULT speziell jetzt "S_OK" und ein [MarshalAs(UnmanagedType.Bool)] in C# als Parameter-Attribut benutze, dann konvertiert er es mir gleich Oo?

    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. namespace KMGiga.WinAPI
    3. {
    4. public enum HRESULT : UInt32 {
    5. /// <summary>
    6. /// Operation successful
    7. /// </summary>
    8. S_OK = 0x0000,
    9. /// <summary>
    10. /// Operation Fails
    11. /// </summary>
    12. S_FALSE = 0x0001,
    13. /// <summary>
    14. /// Operation aborted
    15. /// </summary>
    16. E_ABORT = 0x80004004,
    17. /// <summary>
    18. /// General access denied error
    19. /// </summary>
    20. E_ACCESSDENIED = 0x80070005,
    21. /// <summary>
    22. /// Unspecified failure
    23. /// </summary>
    24. E_FAIL = 0x80004005,
    25. /// <summary>
    26. /// Handle that is not valid
    27. /// </summary>
    28. E_HANDLE = 0x80070006,
    29. /// <summary>
    30. /// One or more arguments are not valid
    31. /// </summary>
    32. E_INVALIDARG = 0x80070057,
    33. /// <summary>
    34. /// No such interface supported
    35. /// </summary>
    36. E_NOINTERFACE = 0x80004002,
    37. /// <summary>
    38. /// Not implemented
    39. /// </summary>
    40. E_NOTIMPL = 0x80004001,
    41. /// <summary>
    42. /// Failed to allocate necessary memory
    43. /// </summary>
    44. E_OUTOFMEMORY = 0x8007000E,
    45. /// <summary>
    46. /// Pointer that is not valid
    47. /// </summary>
    48. E_POINTER = 0x80004003,
    49. /// <summary>
    50. /// Unexpected failure
    51. /// </summary>
    52. E_UNEXPECTED = 0x8000FFFF
    53. }
    54. }

    @RodFromGermany, sehr sehr hilfreich, danke erstmal.
    Bringt das irgendwelche vorteile mit sich, wenn ich die Parameter-Attribute benutze? (Mein Gedanke bei der Frage geht in richtung Performance)
    pinvoke.net/ nutze ich sehr viel, aber momentan bin ich an der Grenze der Seite angekommen von dem was da vorhanden ist.
    Danke :D

    @Niko Ortner, die Methode liegt in einem "Shell-Interface" (Interface) e.g.
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Runtime.InteropServices;
    3. namespace KMGiga.WinAPI {
    4. /// <summary>
    5. /// Implemented and used by containers and objects to obtain window handles
    6. /// and manage context-sensitive help.
    7. /// </summary>
    8. /// <remarks>
    9. /// The IOleWindow interface provides methods that allow an application to obtain
    10. /// the handle to the various windows that participate in in-place activation,
    11. /// and also to enter and exit context-sensitive help mode.
    12. /// </remarks>
    13. [ComImport]
    14. [Guid("00000114-0000-0000-C000-000000000046")]
    15. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    16. public interface IOleWindow {
    17. /// <summary>
    18. /// Returns the window handle to one of the windows participating in in-place activation
    19. /// (frame, document, parent, or in-place object window).
    20. /// </summary>
    21. /// <param name="phwnd">Pointer to where to return the window handle.</param>
    22. /// <returns>This method returns S_OK on success. Other possible return values include the following.
    23. /// <list type="">
    24. /// <listheader>
    25. /// <term>Return code</term>
    26. /// <term>Description</term>
    27. /// </listheader>
    28. /// <item>
    29. /// <term>E_FAIL</term>
    30. /// <description>The object is windowless.</description>
    31. /// </item>
    32. /// <item>
    33. /// <term>E_INVALIDARG</term>
    34. /// <description>The specified fEnterMode value is not valid.</description>
    35. /// </item>
    36. /// <item>
    37. /// <term>E_OUTOFMEMORY</term>
    38. /// <description>There is insufficient memory available for this operation.</description>
    39. /// </item>
    40. /// <item>
    41. /// <term>E_UNEXPECTED</term>
    42. /// <description>An unexpected error has occurred.</description>
    43. /// </item>
    44. /// </list>
    45. /// </returns>
    46. void GetWindow(out IntPtr phwnd);
    47. /// <summary>Determines whether context-sensitive help mode should be entered during an in-place activation session.</summary>
    48. /// <param name="fEnterMode"><c>true</c> if help mode should be entered;
    49. /// <c>false</c> if it should be exited.</param>
    50. /// <returns>This method returns S_OK if the help mode was entered or exited successfully, depending on the value passed in fEnterMode. Other possible return values include the following.</returns>
    51. void ContextSensitiveHelp([In, MarshalAs(UnmanagedType.Bool)] Boolean fEnterMode);
    52. }
    53. }



    Edit:
    Auf der docs.microsoft.com beim IOleWindow::GetWindow ist der Rückgabe-Typ ein HRESULT hingegen auf Pinvoke beim IOleWindow ist es eine void.
    Wie äußert sich der Rückgabe wert dann in C# und wie manage ich das ganze Selber, wie in meinen Interface Oo'?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Facebamm“ ()

    Facebamm schrieb:

    Bringt das irgendwelche vorteile mit sich, wenn ich die Parameter-Attribute benutze?
    Richtung Performance eigentlich nicht, der Compiler macht halt sofort den richtigen Cast.
    Ich kann mir nicht vorstellen, dass während der Lufzeit, dynamisch, probiert wird, was da besser passt.
    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!
    Das ist immer etwas umständlich bei der WinAPI.
    Am Beispiel HResult erklärt:
    Auf docs.microsoft.com/en-us/windo…inprog/windows-data-types steht, wie der Typ definiert ist. Bei HRESULT steht typedef LONG HRESULT;, also nachgucken, was LONG ist: A 32-bit signed integer.. Der äquivalente Typ in .NET ist Int32.
    Deklariere also Deine funktion mit dem Rückgabetyp Int32. Ich empfehle hier auch, wirklich die expliziten Namen auszuschreiben und nicht die C#- bzw. VB-Aliase (also int bzw. Integer) zu verwenden, damit ganz eindeutig klar ist, welche Größe gemeint ist.

    Hier einen Boolean zu verwenden wäre übrigens falsch. Der Marshaller weiß nicht, was mit HRESULT gemeint ist. Wenn Du der Funktion in C# einen Boolean als Rückgabetyp deklarierst, dann kommt False zurück, wenn der von der C-Funktion zurückgegebene Wert 0 ist, und True, wenn er nicht 0 ist. Also genau andersrum, als man es erwarten würde.
    S_OK = 0 -> False
    S_FALSE = 1 -> True

    Bei HRESULTs gibt es aber in .NET eine Vereinfachung, die man verwenden kann:

    VB.NET-Quellcode

    1. Dim Result = DeineWinApiFunktion() 'Gibt HRESULT, also Int32, zurück.
    2. If Result <> 0 Then
    3. Throw New System.ComponentModel.Win32Exception(Result)
    4. End If

    Intern wird automatisch nach einer zum Fehlercode passenden Fehlermeldung gesucht. Für 0x80070005 (E_ACCESS_DENIED) kommt dann zum Beispiel "Zugriff verweigert".
    Das kann je nach Anwendungsfall sinnvoll sein.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Speziell für HRESULT beim DLLImport kannst du auch einfach void als Rückgabetyp setzen und dann PreserveSig auf False stellen: DllImportAttribute.PreserveSig-Feld​
    ​When you set the PreserveSig field to false, the resulting method signature contains a void return type instead of an integer (HRESULT) return type. When the unmanaged method produces an HRESULT, the runtime automatically ignores a return value of S_OK (or 0) and does not throw an exception. For HRESULTs other than S_OK, the runtime automatically throws an exception that corresponds to the HRESULT. Note that the DllImportAttribute attribute only performs this conversion to methods that return an HRESULT.
    @Niko Ortner Danke für die ausführliche Erklärung :D
    Also könnte ich dafür auch schlicht das enum MarshalAs(UnmanagedType.Type) als Parameter nehmen von hier, wenn ich das Attribut von Bluespide ignoriere?

    Ich selbst hab mir diese Liste angelegt

    C#-Quellcode

    1. /*
    2. Windows Data Type .NET Data Type
    3. BOOL, BOOLEAN Boolean or Int32
    4. BSTR String
    5. BYTE Byte
    6. CHAR Char
    7. DOUBLE Double
    8. DWORD/LPDWORD Int32 or UInt32
    9. FLOAT Single
    10. (and all other handle types, such as HFONT and HMENU)
    11. HANDLE IntPtr, UintPtr, or HandleRef
    12. HRESULT Int32 or UInt32
    13. INT Int32
    14. LANGID Int16 or UInt16
    15. LCID Int32 or UInt32
    16. LONG Int32
    17. LPARAM IntPtr, UintPtr, or Object
    18. LPCSTR String
    19. LPCTSTR String
    20. LPCWSTR String
    21. LPSTR String or StringBuilder*
    22. LPTSTR String or StringBuilder
    23. LPWSTR String or StringBuilder
    24. LPVOID IntPtr, UintPtr, or Object
    25. LRESULT IntPtr
    26. SAFEARRAY .NET array type
    27. SHORT Int16
    28. TCHAR Char
    29. UCHAR SByte
    30. UINT Int32 or UInt32
    31. ULONG Int32 or UInt32
    32. VARIANT Object
    33. VARIANT_BOOL Boolean
    34. WCHAR Char
    35. WORD Int16 or UInt16
    36. WPARAM IntPtr, UintPtr, or Object
    37. */


    @Bluespide auf das Attribute bin ich auch schon gestoßen, Danke

    Facebamm schrieb:

    DWORD/LPDWORD Int32 or UInt32
    stimmt so nicht, denn DWORD ist ein UInt32, LPDWORD ist ein Pointer auf so ein Ding :!:
    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!
    @NoIde Jou.
    @Facebamm Deswegen ist es auch sehr wichtig, dass Du diese Parameter korrekt implementierst, sonst bekommst Du bei jedem Aufruf einer solchen Funktion eine entsprechende Exception.
    Ich sehe mir dazu einmal die Deklaration auf pinvoke an und dann noch die Beschreibung in der MSDN.
    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!
    oha hast recht @NoIde O.O,
    @RodFromGermany Dankee :D werd ich jetzt auch so machen :D

    C#-Quellcode

    1. [Serializable]
    2. [System.Runtime.InteropServices.ComVisible(true)]
    3. public struct IntPtr : ISerializable{
    4. /* ... */
    5. public static int Size
    6. {
    7. [Pure]
    8. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    9. [System.Runtime.Versioning.NonVersionable]
    10. get
    11. {
    12. #if WIN32
    13. return 4;
    14. #else
    15. return 8;
    16. #endif
    17. }
    18. }
    19. }
    20. }
    @Facebamm What :?:

    VB.NET-Quellcode

    1. label1.Text = Environment.Is64BitProcess.ToString();
    2. label2.Text = IntPtr.Size.ToString();
    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!
    Es geht zwar nicht direkt um MarshalAs aber ich bin gerade bei einem verständnis Problem :/

    In der Doko steht folgendes


    ist das long* ein IntPtr, oder ist das ein Array wie aus C?
    aber wenn ja, ich dachte die schreibweise in C++ dafür wäre.

    C-Quellcode

    1. void myFunction(int *param) {
    2. }


    was ich erstmal habe ist

    C#-Quellcode

    1. [DllImport("falcon.dll")]
    2. public static extern StatusResult InquireImageMem(int pnX, int pnY, int pnBits, int pnPitch);