为C#包装本机DLL [英] Wrap native DLL for C#

查看:69
本文介绍了为C#包装本机DLL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个C ++ DLL,现在我需要从托管应用程序中调用一个本机函数.

I wrote a C++ DLL and now I need to call a native function from a managed app.

导出的本机函数如下所示:

The exported native function appears like this:

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

因此,从C#中,我将调用传递2个输入参数,1个输出参数的函数,显然我将读取return bool值.

So, from C# I'll call that function passing 2 input params, 1 output param and obviously I'll read the return bool value.

我试图以多种方式包装所有这些内容,但是总是出现PInvokeStackImbalance异常. 我知道调用本机函数的唯一方法是在.NET函数声明上应用CallingConvention = CallingConvention.Cdecl).但是,以这种方式,我无法读取输出参数(始终为空字符串),而且返回值始终为true.

I tried to wrap all this in many ways, but always I get a PInvokeStackImbalance exception. The only way I know to call native function is by applying CallingConvention = CallingConvention.Cdecl) on .NET function declaration. However in this way I'm not able to read the output param (it's empty string always) and also the return value is always true.

推荐答案

首先,我将调整本机函数的原型.

First, I'd adjust the prototype of your native function.

由于此函数具有 C接口,因此您应该对布尔类型使用C类型,而不是像bool这样的C ++类型.您可能要使用Win32的BOOL类型.

Since this function has a C interface, you should use a C type for booleans, not a C++ type like bool. You may want to use Win32's BOOL type.

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

Moreover, as it currently is, your function is prone to buffer overruns: it's better to add another parameter to specify the maximum size of the destination result string buffer.

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

Note also that a widespread calling convention for DLLs exporting pure C interface functions (like lots of Win32 API functions) is __stdcall (not __cdecl). I'd use that as well.

最后,由于前两个参数是 input 字符串,因此您可能想使用const使其清晰明了并强制执行const-correctity.

Last, since the first two parameters are input strings, you may want to use const to make it clear and enforce const-correctness.

因此,我将制作导出的本机函数的原型,如下所示:

So, I'd make the prototype of the exported native function like this:

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

然后,在C#端,您可以使用以下P/Invoke:

Then, on the C# side, you can use the following 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.

Note that for the output string, a StringBuilder is used.

还请注意,CharSet = CharSet.Ansi用于将C#的Unicode UTF-16字符串编组为ANSI(请注意转换为有损的事实-如果要进行无损转换,只需在C ++方面也使用wchar_t*字符串).

Note also that CharSet = CharSet.Ansi is used to marshal C#'s Unicode UTF-16 strings to ANSI (pay attention to the fact that the conversion is lossy - if you want a non-lossy conversion, just use wchar_t* strings on the C++ side as well).

我用一个简单的C ++本机DLL进行了测试:

I did a test with a simple 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#控制台应用程序代码成功调用:

And it is called successfully by the following C# console app code:

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#包装本机DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆