在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”,尽管我可以将它放在定义上。这似乎没有帮助(当你想到它时很明显)
我能想到的唯一其他解决方案是包含实例方法的c风格函数,并将a的实例作为参数。然后,禁用该包装上的名称修改和pInvoke。不过,我宁愿坚持我已有的解决方案。我已经告诉我的同事,pInvoke很棒。如果我必须在我们的c ++库中放入特殊函数来使pInvoke工作,我会看起来像个白痴。
答案 0 :(得分:4)
您无法为C ++类方法禁用修改,但您可以使用/EXPORT
或.def文件以您选择的名称导出该函数。
但是,您的整个方法很脆弱,因为您依赖于实现细节,即this
作为隐式参数传递。更重要的是,导出一个类的单个方法是一种痛苦的方法。
将C ++类暴露给.net语言的最明智的策略是:
我认为选项2更可取。
答案 1 :(得分:2)
您可以使用comment / linker #pragma将/EXPORT
开关传递给链接器,该链接器允许您重命名导出的符号:
#pragma comment(linker, "/EXPORT:GetAnswer=?GetAnswer@a@@UAEHXZ")
不幸的是,这并不能解决您使用依赖或其他工具查找损坏名称的需要。
答案 2 :(得分:1)
您不必禁用实际包含有关如何声明函数本身的大量信息的受损名称,它基本上表示函数名称被去除后的函数的整个签名。我知道你已经发现了一个单词,而另一个答案已被标记为正确的答案。我在下面写的是我们如何使它按照你的意愿工作。
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")]
public static extern int GetAnswer(IntPtr thisA);
如果将“#OrdinalNumber”替换为GetAnsweer的实际序号,例如“#1”,它将按您的意愿工作。
您可以认为EntryPoint属性与我们传递给GetProcAddress的函数名称相同,您可以在其中传递函数名称或函数的序号。
调用C ++类的非静态函数成员的方法确实是正确的,并且正确使用了thiscall,而这正是调用约定在C#P / Invoke中发挥作用。这种方法的问题是你必须查看DLL的PE信息,导出函数信息并找出你想要调用的每个函数的序号,如果你有大量的C ++函数可以调用,你可以想要自动化这样一个过程。
答案 3 :(得分:0)
来自问题作者:我实际使用的解决方案
我最终选择了一个包装实例方法的c风格函数,并将 a 的实例作为参数。这样,如果类继承自继承,则会调用正确的虚方法。
我刻意选择不使用C ++ / CLI,因为它只是另一个要管理的项目。如果我需要在类上使用所有方法,我会考虑它,但我真的只需要这个序列化类数据的方法。