【问题标题】:How to call C++ DLL in C#如何在 C# 中调用 C++ DLL
【发布时间】:2013-04-26 07:26:23
【问题描述】:

我用开发 C++ 编写了一个 DLL。 DLL 的名称是“DllMain.dll”,它包含两个函数:HelloWorldShowMe。头文件如下所示:

DLLIMPORT  void HelloWorld();
DLLIMPORT void ShowMe();

源文件如下所示:

DLLIMPORT void HelloWorld ()
{
  MessageBox (0, "Hello World from DLL!\n", "Hi",MB_ICONINFORMATION);
}

DLLIMPORT void ShowMe()
{
 MessageBox (0, "How are u?", "Hi", MB_ICONINFORMATION);
}

我将代码编译成 DLL 并从 C# 调用这两个函数。 C# 代码如下所示:

[DllImport("DllMain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void HelloWorld();

[DllImport("DllMain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void ShowMe();

当我调用函数“HelloWorld”时,它运行良好并弹出一个消息框,但是当我调用函数ShowMe 时,会出现EntryPointNotFoundException。如何避免此异常?需要在头文件中添加extern "C"吗?

【问题讨论】:

  • 你能发布你的 C++ 代码吗?
  • 您可能应该将调用约定更改为CallingConvention.StdCall

标签: c# dll


【解决方案1】:

VS 2012 中的以下代码运行良好:

#include <Windows.h>
extern "C"
{
    __declspec(dllexport) void HelloWorld ()
    {
        MessageBox (0, L"Hello World from DLL!\n", L"Hi",MB_ICONINFORMATION);
    }
    __declspec(dllexport) void ShowMe()
    {
        MessageBox (0, L"How are u?", L"Hi", MB_ICONINFORMATION);
    }
}

注意:如果我删除 extern "C" 我会得到异常。

【讨论】:

  • 好的,我改了代码,问题解决了,非常感谢。
  • 它没有回答如何从 C# 调用 C++(即损坏的代码)函数。
  • @Hi-Angel 我不知道什么是损坏的代码以及如何从 C# 调用它。如果您想完成我的回答,请发表评论,或者您可以发表自己的答案。
  • @atoMerz 我想知道答案 :D 好吧,根据我昨天的发现——因为我一直在寻找它——唯一的方法是在 dll 中找到损坏的名称,并写下这些而不是通常的。但当然它不会是可移植的,因为名称修饰不是标准化的,所以 GCC 的一个与 Visual Studio 不兼容,并且都与 ICC 不兼容,等等。如果你想同时使用这两个编译器,你有首先尝试一个编译器的函数名,在没有这样的函数的情况下捕获异常,然后尝试下一个编译器的函数名,类似这样。
  • @atoMerz 顺便说一句,这对我来说很有趣,你是通过指令 extern "C"{} 解决了这个问题的人,它只是从目标文件中删除了一个名称修改,但你仍然没有知道 mangling 的名字是什么 :D 在 C++ 编译器中,不能像 «HelloWorld» 那样将函数命名为对象,因为这里可能是两个同名的函数,它们接收不同的参数类型。 «Name mangling» 是解决这个问题的方法:它根据自己的算法将一些符号附加到函数的名称上。并且这个算法(和结果名称)在编译器中有所不同。
【解决方案2】:
using System;
using System.Runtime.InteropServices;

namespace MyNameSpace
{
    public class MyClass
    {
        [DllImport("DllMain.dll", EntryPoint = "HelloWorld")]
        public static extern void HelloWorld();

        [DllImport("DllMain.dll", EntryPoint = "ShowMe")]
        public static extern void ShowMe();
    }
}

【讨论】:

  • 好的,我改了代码,问题解决了,非常感谢。
  • 由于动态库中的“HelloWorld”被破坏了,所以它不能工作。
  • @mjb 你真的需要“EntryPoint”还是隐含的?我见过不使用它的代码。这是一件坏事吗?
  • @progLearner, "您可以使用 DllImportAttribute.EntryPoint 字段按名称或序号指定 DLL 函数。如果您的方法定义中的函数名称与 DLL 中的入口点相同,您不必使用 EntryPoint 字段显式标识函数。” docs.microsoft.com/en-us/dotnet/framework/interop/…
【解决方案3】:

有帮助的事情:

  • :extern "C" { 在 h 文件中的函数声明 } 将禁用 C++ 名称编码。所以c#会找到函数

  • 将 __stdcall 用于 C 声明或 CallingConvention.Cdecl 在 C#声明

  • 可能使用 BSTR/_bstr_t 作为字符串类型并使用其他 vb 类型。 http://support.microsoft.com/kb/177218/EN-US

  • 下载“PInvoke 互操作助手”https://clrinterop.codeplex.com/releases/view/14120 在第三个选项卡中粘贴来自 .h 文件的函数声明 = c# 宣言。替换为 dll 文件名。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多