Marshaling von char* als Übergabeparameter



  • Heyho Community,

    ich stehe gerade vor einem Marshaling Problem, und da dieses Thema für mich noch Neuland ist, tue ich mir hier entsprechend schwer eine Lösung zu finden. Google bietet zwar einiges, aber ich finde irgendwie nichts was, was mir 100% hilft. Das Problem:

    Mein C# Programm muss eine C++ Funktion mit folgender Signatur aufrufen:

    __declspec(dllexport) LONG __stdcall GetInstallDir(size_t buffer, char* path);
    

    Die DLL welche die Funktion enthält ist von Extern, der Code ist mir also nicht zugängig. Was sie genau macht: Sie prüft, ob ein bestimmtes Programm installiert ist und legt den Installationspfad in 'path' ab (die maximale Länge des Pfad-Strings ist mit 'buffer' definiert -- das ganze ist also typsiches C). Sollte das Programm nicht installiert sein, oder der Buffer für 'path' zu klein sein, gibt die Funktion FALSE zurück, ansonsten TRUE.

    Mein bisheriger C# Code um diese Funktion aufzurufen:

    // Marshal declarations
    [DllImport("nutins.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetInstallDir(
    	[In][MarshalAs(UnmanagedType.U4)] UInt32 size 
    	[In][MarshalAs(UnmanagedType.LPArray)] ref char[] path);
    
    // Somewhere in code
    // [...]
    char[] path = new char[256];
    if(GetInstallDir(256, ref path))
    // [...]
    

    Mein Problem ist jetzt einfach, dass mir bei Ausführung dann eine AccessViolationException geworfen wird, und wenn ich das 'path'-Array ohne 'ref' übergebe, dann bleibt das Array natürlich leer (es gibt aber dann auch keine sonstigen Exceptions mehr, und ich bekomme sogar ein TRUE zurück).

    Kann mir mal jemand bei dieser Marshaling-Sache helfen? Wie mache ich das in dem Fall korrekt? Danke schonmal für jegliche Hilfe 🙂

    Gruß
    PuerNoctis

    P.S.: Keine Sorge, ich lese mich in's Marshaling schon weiter ein, interessiert mich ja auch, aber gerade hier würde mir 'ne Hilfe zum Verständnis viel beitragen 😉



  • Okay, ich habe mein Problem lösen können, war ja auch einfacher als gedacht:

    Die Übergabe erfolgt ohne 'ref' einfach durch einen 'StringBuilder' Typ -- die ganzen Attribute wie [In] oder [MarschalAs] sind auch hinfällig:

    // Marshal declarations
    [DllImport("nutins.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetInstallDir(
        [In][MarshalAs(UnmanagedType.U4)] UInt32 size
        StringBuilder path);                           // CHANGE!
    
    // Somewhere in code
    // [...]
    StringBuilder path = new StringBuilder(size);      // CHANGE!
    if(GetInstallDir(size, path))                      // CHANGE!
    // [...]
    

    Gruß
    PuerNoctis


  • Administrator

    Du kannst übrigens die Deklaration deutlich einfacher schreiben:

    [DllImport("nutins.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    public static extern bool GetInstallDir(UInt32 size, StringBuilder path);
    

    Du kannst hier nämlich die Defaultwerte ausnutzen. CharSet und CallingConvention könnte man theoretisch auch noch weglassen, allerdings würde ich es trotzdem drinlassen, weil der Defaultwert nur per Default auf die Einstellungen rückspringt. Das heisst genau:
    Charset ist auf None , was per Default auf CharSet.Ansi springt.
    CallingConvention ist auf WinApi , was bei Funktionen ohne Ellipsen auf CallingConvention.StdCall springt.

    Ich persönlich traue dem ganzen Rückspringen-Zeug nicht. Wieso hat man überhaupt andere eingeführt? Ändert sich dieses Rückspringen in der Zukunft? Daher lieber auf Nummer sicher gehen, vor allem wenn man keine WinAPI Aufrufe tätigt.

    Die Defaultwerte für das Marshalling bei den Übergabe- und Rückgabetypen werden sie wohl kaum einfach ändern, daher kann man sich wohl auf diese verlassen.

    Grüssli



  • Aja, alles klar, danke für die Zusatzinfo 🙂


Anmelden zum Antworten