如何通过禁用名称篡改来PInvoke实例方法
How to PInvoke an Instance Method by disabling Name Mangling
给定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的"something"时,一切都很好!我花了大约一天的时间才开始使用Depends.exe,所以我将把它留在这里,作为对任何有类似问题的人的帮助。
我真正的问题是:有没有什么方法可以在GetAnswer上禁用C++名称篡改,这样我就不需要把被篡改的名称作为我的入口点。在那里有一个被篡改的名称似乎可能会崩溃,因为我对名称篡改的理解是,如果编译器改变,它可能会改变。此外,对我想pInvoke的每个实例方法使用Depends.exe也是一件令人头疼的事。
编辑:忘记添加我尝试过的内容:我似乎无法将外部"C"放在函数声明上,尽管我可以将其放在定义上。这似乎没有帮助(当你仔细想想的时候,这是显而易见的)
我能想到的唯一其他解决方案是一个c风格的函数,它封装实例方法并将a的实例作为参数。然后,禁用该包装器上的名称篡改,并取消它。不过,我宁愿坚持我现有的解决方案。我已经告诉我的同事pInvoke很棒。如果我不得不在我们的c++库中放入特殊函数来使pInvoke工作,我会看起来像个白痴。
您不能禁用C++类方法的mangling,但您可以使用/EXPORT
或.def文件以您选择的名称导出函数。
然而,您的整个方法是脆弱的,因为您依赖于实现细节,即this
作为隐式参数传递。更重要的是,导出类的单个方法会带来痛苦。
将C++类暴露在.net语言中最明智的策略是:
- 创建平面C包装器函数并p/调用这些函数
- 创建一个C++/CLI混合模式层,该层发布封装本机类的托管类
在我看来,备选方案2更可取。
您可以使用注释/链接器#pragma将/EXPORT
开关传递给链接器,链接器应允许您重命名导出的符号:
#pragma comment(linker, "/EXPORT:GetAnswer=?GetAnswer@a@@UAEHXZ")
不幸的是,这并不能解决您需要使用dependent或其他工具查找损坏的名称的问题。
您不必禁用被破坏的名称,它实际上包含了函数本身如何声明的大量信息,它基本上代表了函数名称被破坏后的整个签名。我知道你已经找到了一个单词,而另一个答案已经被标记为正确答案。我在下面写的是我们如何让它按你的意愿工作。
[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")]
public static extern int GetAnswer(IntPtr thisA);
如果将"#OrdinalNumber"替换为GetAnsweer的实数,例如"#1",它将按您的要求工作。
您可能只认为EntryPoint属性与我们传递给GetProcAddress的函数名称相同,在GetProcAddress中您可以传递函数名称或函数的序号。
您调用C++类的非静态函数成员的方法确实是正确的,并且正确使用了thiscall,这正是thiscall调用约定在C#p/Invoke中发挥作用的原因。这种方法的问题是,你必须查看DLL的PE信息,导出函数信息,并找出你想调用的每个函数的序号,如果你有大量的C++函数要调用,你可能想自动化这样的过程。
来自问题作者:我实际使用的解决方案
我最终使用了一个c样式函数,该函数封装了instance方法,并将a的一个实例作为参数。这样,如果类确实从继承,那么将调用正确的虚拟方法。
我故意选择不使用C++/CLI,因为它只是需要管理的又一个项目。如果我需要在一个类上使用所有的方法,我会考虑它,但我实际上只需要这一个序列化类数据的方法。
- 有没有一种"cleaner"的方法可以在指向基的指针向量中找到派生类的第一个实例?
- 绑定派生类方法C++从实例范围之外的分隔 std::function 变量调用
- 初始化指向类实例的智能指针并访问其方法
- 如何在没有实例的情况下获取非静态方法的类型?
- 使用动态实例化的对象填充矢量的快速方法
- 检查类是否在方法中实例化
- 所有类实例方法的打印语句最后都在打印吗?
- 当对象为值时访问实例方法<map>
- CPU寄存器中返回的用户定义类型的C 对象.实例方法如何工作
- 是否可以在实例方法中使用带有"this"的重载运算符?
- 如何使用带有实例方法的C++11线程
- 将 boost::function 与实例方法一起使用
- 在 Visual Studio 2008 C++ Express 中评估实例方法
- 如何通过禁用名称篡改来PInvoke实例方法
- 将C++实例方法与C回调函数混合使用
- 在同一个c#或c++类中可以有静态方法和实例方法吗?
- 使用随机数生成器:多实例或单实例方法
- 对象切片,不需要额外的实例方法和变量
- 为什么子类的非重写实例方法中的"this"类型是父类型?
- 子类调用基类构造函数,然后使用子类Arduino c++中基类的实例方法