8

C ++ DLLを作成しましたが、管理対象アプリからネイティブ関数を呼び出す必要があります。

エクスポートされたネイティブ関数は次のようになります。

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

したがって、C#から、2つの入力パラメーターと1つの出力パラメーターを渡してその関数を呼び出し、明らかに戻り値のブール値を読み取ります。

私はこれらすべてをさまざまな方法でラップしようとしましたが、常にPInvokeStackImbalance例外が発生します。ネイティブ関数を呼び出すために私が知っている唯一の方法はCallingConvention = CallingConvention.Cdecl、.NET関数宣言に)を適用することです。ただし、この方法では、出力パラメーター(常に空の文字列)を読み取ることができず、戻り値も常にtrueになります。

4

5 に答える 5

16

まず、ネイティブ関数のプロトタイプを調整します。

この関数にはCインターフェースがあるため、ブール値にはC ++型ではなく、C型を使用する必要がありますbool。Win32のBOOLタイプを使用することをお勧めします。

さらに、現在のように、関数はバッファオーバーランを起こしやすいです。別のパラメータを追加して、宛先result文字列バッファの最大サイズを指定することをお勧めします。

また、純粋なCインターフェイス関数(多くのWin32 API関数など)をエクスポートするDLLの一般的な呼び出し規約__stdcallは(ではありません)であることに注意してください__cdecl。私もそれを使います。

最後に、最初の2つのパラメーターは入力文字列であるため、これを使用constして明確にし、const-correctnessを適用することをお勧めします。

したがって、エクスポートされたネイティブ関数のプロトタイプを次のように作成します。

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

次に、C#側で、次のP/Invokeを使用できます。

   [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);

出力文字列にはaが使用されることに注意してくださいStringBuilder

CharSet = CharSet.Ansiこれは、C#のUnicode UTF-16文字列をANSIにマーシャリングするために使用されることにも注意してください(非可逆変換が必要な場合はwchar_t*、C ++側でも文字列を使用してください)。

単純なC++ネイティブ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;
}

そして、次のC#コンソールアプリコードによって正常に呼び出されます。

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());
        }
    }
}
于 2013-02-03T17:01:55.770 に答える
2

代わりにCOMInteropを使用するだけで、P/Invokeの頭痛の種を大幅に減らすことができます。メソッドをCOMインターフェイスに配置し、COMの規則に従うように署名を変更します。

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

char *BSTRに、boolVARIANT_BOOLに変更しました。これらは、COMが文字列とboolにそれぞれ使用する型だからです。また、すべてのCOMメソッドはHRESULTを返す必要があります。「実際の」戻り値が必要な場合は、それをlast outパラメーターとして追加し、retval属性でマークする必要があります。

次に、C#プロジェクトからCOMコンポーネントへの参照を追加すると、C ++タイプとC#タイプを一致させる方法を推測することなく、直感的なC#署名を取得できます。

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

(これがオブジェクトブラウザに表示される方法です。)

于 2013-02-03T14:36:17.957 に答える
1

次のようなDLLImportを使用した.Netコードマーシャリングの使用に注意する理由

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

次に、次のようにローカル関数として関数を呼び出すことができます

string x=string.empty;
ProtectDocument(out x); 
于 2013-02-03T13:41:43.417 に答える
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ワイド文字を使用してLPWStrいる場合は、に置き換えます。

于 2013-02-03T13:58:46.013 に答える
-1
[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);

char *C#文字列は不変であるため、出力パラメーターはである必要があります。次のように(安全でないコンテキストで)メソッドを呼び出します。

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

出力パラメータが文字列ではなく単一文字である場合を除いて、この場合は次のようにすることができます。

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

そして、あなたはそれをこのように使うことができます:

char output;
NativeMethod("blahblah", "blahblah", out output);
于 2013-02-03T13:59:17.567 に答える