c# - 为 C# 包装 native DLL

标签 c# c++ string pinvoke

我编写了一个 C++ DLL,现在我需要从托管应用程序调用 native 函数。

导出的原生函数如下所示:

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

因此,在 C# 中,我将调用该函数传递 2 个输入参数、1 个输出参数,显然我将读取返回的 bool 值。

我尝试以多种方式包装所有这些,但总是遇到 PInvokeStackImbalance 异常。 我知道调用 native 函数的唯一方法是在 .NET 函数声明上应用 CallingConvention = CallingConvention.Cdecl)。然而,通过这种方式我无法读取输出参数(它始终为空字符串)并且返回值始终为真。

最佳答案

首先,我会调整您的原生函数的原型(prototype)。

因为这个函数有一个 C 接口(interface),你应该为 bool 值使用 C 类型,而不是像 bool 这样的 C++ 类型。您可能希望使用 Win32 的 BOOL 类型。

此外,就目前而言,您的函数很容易缓冲区溢出:最好添加另一个参数来指定目标result 字符串缓冲区的最大大小。

另请注意,导出纯 C 接口(interface)函数(如许多 Win32 API 函数)的 DLL 的广泛调用约定__stdcall(不是__cdecl).我也会用它。

最后,由于前两个参数是输入 字符串,您可能希望使用const 来明确并强制执行const 正确性。

所以,我会像这样制作导出的 native 函数的原型(prototype):

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

请注意,对于输出字符串,使用了 StringBuilder

另请注意,CharSet = CharSet.Ansi 用于将 C# 的 Unicode UTF-16 字符串编码为 ANSI(注意转换有损 - 如果你想要一个无损转换,也只需在 C++ 端使用 wchar_t* 字符串)。

我用一个简单的 C++ native 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());
        }
    }
}

关于c# - 为 C# 包装 native DLL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14673049/

相关文章:

c# - HTTP 错误 401.0 - 未经授权的 IIS 8

c++ - 创建 C++ Redis 模块 - "does not export RedisModule_OnLoad() symbol"

c++ - 为什么 std::hash 不专用于 std::reference_wrapper?

c++ - 使用 std::array<Type, N> 的实例作为模板参数

java - 如何以特定方式交换字符串中的两个数字?

java - 如何将 for-each 循环应用于字符串中的每个字符?

c# - 字符串转换器获取标准值集合

c# - 测试 HttpResponse.StatusCode 的结果

java - 如何不计算 StringTokenizer 中的空格

c# - 使用变量加载数据内文件