如何通过禁用名称修改来 PInvoke 实例方法

Pet*_*man 5 c# c++ pinvoke name-mangling

给定 foo.dll 中的以下 c++ 类

class a{
  private:
    int _answer;

  public:
    a(int answer) { _answer = answer; }
    __declspec(dllexport) int GetAnswer() { return _answer; }
}
Run Code Online (Sandbox Code Playgroud)

我想要来自 C# 的 pInvoke GetAnswer。为此,我使用以下方法:

[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint= "something")]
public static extern int GetAnswer(IntPtr thisA);
Run Code Online (Sandbox Code Playgroud)

我传入一个指向 a 的 IntPtr(我从其他地方得到的,这并不重要)。 CallingConvention = CallingConvention.ThisCall确保正确处理

这个问题很酷的是,我知道到目前为止我是对的,因为它已经很好用了! 使用 Depends.exe,我可以看到“GetAnswer”被导出为 ?GetAnswer@a@@UAEHXZ (或者接近的东西 - 关键是它的名称被破坏了)。当我将损坏的名称插入 EntryPoint 的“某物”时,一切正常!我花了大约一天的时间才意识到使用 Depends.exe,所以我将把它留在这里作为对任何有类似问题的人的帮助。

我真正的问题是: 有什么方法可以在 GetAnswer 上禁用 C++ 名称重整,这样我就不需要将重整名称作为我的入口点。将重整名称放在那里似乎可能会中断,因为我对名称重整的理解是,如果编译器更改,它会更改。此外,对于我想要 pInvoke 的每个实例方法使用 Depends.exe 也很麻烦。

编辑:忘记添加我尝试过的内容:我似乎无法将 extern "C" 放在函数声明中,尽管我可以将其粘贴在定义上。虽然这似乎没有帮助(当你考虑它时这很明显)

我能想到的唯一其他解决方案是一个 c 风格的函数,它包装了实例方法并将 a 的实例作为参数。然后,在该包装器上禁用名称修改并调用它。不过,我宁愿坚持我已经拥有的解决方案。我已经告诉我的同事 pInvoke 很棒。如果我必须在我们的 c++ 库中放入特殊函数来使 pInvoke 工作,我会看起来像个白痴。

Dav*_*nan 5

您无法禁用 C++ 类方法的重整,但您可以使用/EXPORT.def 文件以您选择的名称导出该函数。

但是,您的整个方法很脆弱,因为您依赖于实现细节,即this作为隐式参数传递。更重要的是,导出类的单独方法会带来痛苦。

将 C++ 类公开给 .net 语言的最明智策略是:

  1. 创建平面 C 包装函数并 p/调用它们。
  2. 创建一个 C++/CLI 混合模式层,该层发布包装本机类的托管类。

我认为选项 2 更好。