2013-02-03 9 views
7

Ich schrieb eine C++ DLL und jetzt muss ich eine native Funktion von einer verwalteten App aufrufen.Wrap native DLL für C#

Die exportierte native Funktion scheint wie folgt aus:

extern "C" __declspec(dllexport) 
bool NativeMethod(char *param1, char *param2, char *result); 

von C# Also, ich werde diese Funktion aufrufen vorbei 2 Eingang params, 1 Ausgang param und natürlich werde ich die Rückkehr Bool Wert lesen.

Ich habe versucht, all dies auf viele Arten zu wickeln, aber immer bekomme ich eine PInvokeStackImbalance Ausnahme. Die einzige Möglichkeit, native Funktion aufzurufen, ist die Anwendung von CallingConvention = CallingConvention.Cdecl) auf .NET-Funktionsdeklaration. Allerdings kann ich auf diese Weise den Ausgabeparameter nicht lesen (es ist immer ein leerer String) und auch der Rückgabewert ist immer wahr.

+1

welchen Import u haben versucht? –

+0

Ich habe versucht, etwas wie: [DllImport ("MyDll", EntryPoint = "NativeMethod")] public static extern Boolean NativeMethod (Zeichenfolge param1, Zeichenfolge param2, Zeichenfolge param3); Auch mit outparam3 oder StringBuilder oder MarshalAs (UnmanagedType.LPStr)] – bit

+0

Sie erkennen, dass dieser Ausgangsparameter nicht funktioniert, oder? C# -Strings sind ** unveränderlich **. – antonijn

Antwort

12

Zuerst würde ich den Prototyp Ihrer nativen Funktion anpassen.

Da diese Funktion über eine C-Schnittstelle verfügt, sollten Sie einen C-Typ für boolesche Werte verwenden, keinen C++ - Typ wie bool. Möglicherweise möchten Sie den Typ BOOL von Win32 verwenden.

Außerdem ist, wie es derzeit ist, ist Ihre Funktion anfällig für Puffer Überschreitungen: es ist besser, einen anderen Parameter hinzuzufügen, um die maximale Größe des Ziel result String-Puffer angeben.

Beachten Sie auch, dass ein weit verbreitetes Aufrufkonvention für DLLs reinen C-Schnittstelle Funktionen Export (wie viele Win32-API-Funktionen) ist __stdcall (nicht __cdecl). Ich würde das auch benutzen.

Last, da die ersten beiden Parameter sind Eingang Strings, können Sie wollen const verwenden, um deutlich zu machen, und const-Korrektheit erzwingen.

So würde ich den Prototyp der exportierten native Funktion wie diese machen:

extern "C" __declspec(dllexport) 
BOOL __stdcall NativeFunction(
    const char *in1, 
    const char *in2, 
    char *result, 
    int resultMaxSize); 

Dann wird auf der C# Seite, können Sie die folgenden P/Invoke verwenden:

[DllImport(
     "NativeDll.dll", 
     CharSet = CharSet.Ansi, 
     CallingConvention = CallingConvention.StdCall)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool NativeFunction(
     string in1, 
     string in2, 
     StringBuilder result, 
     int resultMaxSize); 

Beachten Sie, dass für die Ausgabezeichenfolge StringBuilder verwendet wird.

Beachten Sie auch, dass CharSet = CharSet.Ansi verwendet C# 's Unicode UTF-16-Strings in das ANSI (achten Sie auf die Tatsache, Marschall, dass die Umwandlung verlustbehaftete ist - wenn Sie eine nicht-verlustbehaftete Umwandlung wollen, nur wchar_t* Saiten auf der C++ verwenden Seite auch).

habe ich einen Test mit einem einfachen C++ nativen DLL:

// NativeDll.cpp 

#include <string.h> 
#include <windows.h> 

extern "C" __declspec(dllexport) 
BOOL __stdcall NativeFunction(
    const char *in1, 
    const char *in2, 
    char *result, 
    int resultMaxSize) 
{ 
    // Parameter check 
    if (in1 == nullptr 
     || in2 == nullptr 
     || result == nullptr 
     || resultMaxSize <= 0) 
     return FALSE; 

    // result = in1 + in2 
    strcpy_s(result, resultMaxSize, in1); 
    strcat_s(result, resultMaxSize, in2); 

    // All right 
    return TRUE; 
} 

Und es wird erfolgreich durch den folgenden C# Konsole App-Code genannt:

using System; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace CSharpClient 
{ 
    class Program 
    { 
     [DllImport(
      "NativeDll.dll", 
      CharSet = CharSet.Ansi, 
      CallingConvention = CallingConvention.StdCall)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     static extern bool NativeFunction(
      string in1, 
      string in2, 
      StringBuilder result, 
      int resultMaxSize); 

     static void Main(string[] args) 
     { 
      var result = new StringBuilder(200); 
      if (! NativeFunction("Hello", " world!", result, result.Capacity)) 
      { 
       Console.WriteLine("Error."); 
       return; 
      } 

      Console.WriteLine(result.ToString()); 
     } 
    } 
} 
+0

Perfekt. Jetzt funktioniert alles gut. Vielen Dank!! – bit

+0

@bit: Gern geschehen. –

1

warum note .Net Code Rangierung mit Verwendung DLLImport wie die folgenden

[DllImport(@"C:\TestLib.dll")] 
     public static extern void ProtectDocument(
      out [MarshalAs(UnmanagedType.LPStr)]string validToDate); 

und dann können Sie die Funktion als lokale Funktion wie die folgende

string x=string.empty; 
ProtectDocument(out x); 
+0

DLL Import ist genau das, was ich benutze, aber ich bekomme die oben erwähnte Ausnahme. Wie kann ich den dritten Parameter angeben, den ich als "out" -Typ verwenden möchte? – bit

+0

können Sie outfront der Variablen –

0
[DllImport("MyDll.dll", EntryPoint = "NativeMethod", CallingConvention = CallingConvention.Cdecl)] 
    static extern bool NativeMethod(
     [MarshalAs(UnmanagedType.LPStr)]string param1, 
     [MarshalAs(UnmanagedType.LPStr)]string param2, 
     [MarshalAs(UnmanagedType.LPStr)]string param3); 

LPStr Ersetzen Sie rufen mit LPWStr Wenn Sie mit breiten Zeichen arbeiten.

0
[DllImport("MyLibrary.dll", EntryPoint = "NativeMethod")] 
public static unsafe extern bool NativeMethod(
    [MarshalAs(UnmanagedType.LPStr)] string param1, 
    [MarshalAs(UnmanagedType.LPStr)] string param2, 
    [MarshalAs(UnmanagedType.LPStr)] char *param3); 

Der Ausgabeparameter hat ein char * sein, da C# Strings unveränderlich sind. Sie rufen die Methode (in einem unsicheren Kontext) wie folgt aus:

char[] output = new char[100]; 
fixed (char *param = &output[0]) 
{ 
    NativeMethod("blahblah", "blahblah", param); 
} 

Es sei denn, der Ausgangsparameter kein String ist, sondern nur ein einzelnes Zeichen, in dem Fall, dass Sie dies nur tun können:

[DllImport("MyLibrary.dll", EntryPoint = "NativeMethod")] 
public static unsafe extern bool NativeMethod(
    [MarshalAs(UnmanagedType.LPStr)] string param1, 
    [MarshalAs(UnmanagedType.LPStr)] string param2, 
    out char param3); 

und Sie können es wie folgt verwenden:

char output; 
NativeMethod("blahblah", "blahblah", out output); 
+0

tut mir leid, aber ich bekomme immer noch eine Ausnahme. – bit

+0

@bit Wie wäre es jetzt? – antonijn

+0

@ WAS IST DIE AUSNAHME? –

3

Sie sich eine Menge P sparen werde/Invoke Kopfschmerzen, wenn Sie nur statt COM-Interop verwenden. Legen Sie die Methode in einer COM-Schnittstelle und die Signatur ändern COM Konventionen folgen:

interface ISomeInterface : IUnknown 
{ 
    HRESULT NativeMethod([in] BSTR bstrParam1, [in] BSTR bstrParam2, 
         [out] BSTR* pbstrParam3, [out, retval] VARIANT_BOOL* pvbResult); 
} 

Ich änderte char * zu BSTR und Bool zu VARIANT_BOOL denn das sind die Typen von COM verwendet für Streicher und Bools. Außerdem müssen alle COM-Methoden eine HRESULT zurückgeben. Wenn Sie einen "tatsächlichen" Rückgabewert wünschen, müssen Sie ihn als letzten out Parameter hinzufügen und ihn auch mit dem Attribut retval kennzeichnen.

Dann einen Verweis auf die COM-Komponente aus dem C# Projekt hinzufügen, und Sie werden über eine intuitive C# Unterschrift erhalten, ohne zu erraten, die wie C++ Typen mit C# Typen entsprechen:

bool NativeMethod(string bstrParam1, string bstrParam2, out string pbstrParam3) 

(Das ist, wie es scheint, im Objektbrowser.)

+0

Ich stimme Ihnen zu, dass COM den C# -Kunden Bequemlichkeit und Einfachheit bietet (in der Tat gab ich Ihrer Antwort eine +1 Stimme). _building_ der COM-Server in C++ mit ATL hat jedoch eine Lernkurve im Vergleich zum reinen Export von reinen C-Interface-Funktionen aus nativen DLLs. Wenn das OP COM-Komponenten in C++ erstellen kann, stimme ich zu, dass es ein guter Vorschlag ist. Aber wenn er es nicht kann, ist es wahrscheinlich besser, ein korrektes P/Invoke für die native C-Interface DLL zu schreiben. –

+0

+1 Für ernsthafte Arbeit zwischen der CLR und nativen Code auf der gleichen Maschine, COM ist der Weg zu gehen. Aber zu viel, wenn Sie nur eine Sache nennen wollen. –