5

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

class a{
  private:
    int _answer;

  public:
    a(int answer) { _answer = answer; }
    __declspec(dllexport) int GetAnswer() { return _answer; }
}

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

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

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

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

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

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

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

4

4 回答 4

4

您不能禁用 C++ 类方法的修改,但您可以使用您选择的名称/EXPORT或 .def 文件导出函数。

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

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

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

在我看来,选项 2 更可取。

于 2013-07-10T19:58:42.707 回答
2

您可以使用注释/链接器#pragma 将/EXPORT开关传递给链接器,这应该允许您重命名导出的符号:

#pragma comment(linker, "/EXPORT:GetAnswer=?GetAnswer@a@@UAEHXZ")

不幸的是,这并不能解决您使用依赖或其他工具查找损坏名称的需要。

于 2013-07-10T19:12:21.627 回答
1

您不必禁用实际上包含函数本身如何声明的大量信息的重整名称,它基本上代表了函数名称被解除重整后的函数的整个签名。我了解您已经找到了一个单词,并且另一个答案已被标记为正确答案。我在下面写的是我们如何使它按您的意愿工作。

[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")]
public static extern int GetAnswer(IntPtr thisA);

如果您将“#OrdinalNumber”替换为 GetAnsweer 的实际序数,例如“#1”,它将按您的意愿工作。

您可能只是认为 EntryPoint 属性与我们传递给 GetProcAddress 的函数名称相同,您可以在其中传递函数名称或函数的序号。

您调用 C++ 类的非静态函数成员的方法确实是正确的,并且正确使用了 thiscall,这正是 thiscall 调用约定在 C# P/Invoke 中发挥作用。这种方法的问题是您必须查看 DLL 的 PE 信息、导出函数信息并找出要调用的每个函数的序号,如果您有大量 C++ 函数要调用,您可能想要自动化这样的过程。

于 2014-02-23T03:32:59.877 回答
0

From the Question Author: The solution I actually went with

I ended up going with a c-style function that wraps the instance method and takes an instance of an a as a parameter. That way, if the class ever does get inherited from, the right virtual method will get called.

I deliberately chose not to go with C++/CLI because it's just one more project to manage. If I needed to use all of the methods on a class, I would consider it, but I really only need this one method that serializes the class data.

于 2013-07-25T21:39:04.860 回答