简体   繁体   English

如何通过禁用名称管理来启用实例方法

[英]How to PInvoke an Instance Method by disabling Name Mangling

Given the following c++ class in foo.dll 给出foo.dll中的以下c ++类

class a{
  private:
    int _answer;

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

I would like the pInvoke GetAnswer from C#. 我想从C#获得pInvoke GetAnswer。 To do that, I use the following method: 为此,我使用以下方法:

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

And I pass in an IntPtr that points to an a (that I got from somewhere else, it's not important). 我传入一个指向a的IntPtr(我从其他地方得到的,这并不重要)。 CallingConvention = CallingConvention.ThisCall makes sure it's handled correctly CallingConvention = CallingConvention.ThisCall确保它被正确处理

What's cool about this question is that I know I'm right so far because it's already working great! 这个问题很酷,我知道我到目前为止是因为它已经很好了! Using Depends.exe, I can see that "GetAnswer" is exported as ?GetAnswer@a@@UAEHXZ (Or something close - the point being that it's been name mangled). 使用Depends.exe,我可以看到“GetAnswer”被导出为?GetAnswer @ a @@ UAEHXZ(或者某些东西很接近 - 这就是它的名字被破坏了)。 When I plug the mangled name into the "something" for the EntryPoint everything works great! 当我将受损的名称插入EntryPoint的“东西”时,一切都很棒! It took me about a day before it dawned on me to use Depends.exe, so I'm going to leave this here as a help to anybody who has a similar issue. 我花了大约一天的时间才意识到我使用了Depends.exe,所以我将把这个留在这里作为对任何有类似问题的人的帮助。

My REAL Question is: Is there any way to disable C++ name mangling on GetAnswer so that I don't need to put the mangled name in as my entry point. 我的真实问题是:有没有办法在GetAnswer上禁用C ++名称修改,这样我就不需要将受损的名称作为我的入口点。 Having the mangled name in there seems like it could break, because my understanding of name mangling is that it can change if the compiler changes. 在那里有一个受损的名称似乎可能会破坏,因为我对名称修改的理解是,如果编译器改变它可以改变。 Also it's a pain in the butt to use Depends.exe for every instance method that I want to pInvoke. 对于我想要pInvoke的每个实例方法使用Depends.exe也很痛苦。

Edit: Forgot to add what I've tried: I don't seem to be able to put extern "C" on the function declaration, although I can stick it on the definition. 编辑:忘了添加我尝试过的东西:我似乎无法在函数声明上放置extern“C”,虽然我可以坚持定义。 This doesn't seem to help though (which is obvious when you think about it) 这似乎没有帮助(当你想到这一点时很明显)

The only other solution I can think of is a c-style function that wraps the instance method and takes an instance of an a as a parameter. 我能想到的唯一其他解决方案是包含实例方法的c风格函数,并将a的实例作为参数。 Then, disable name mangling on that wrapper and pInvoke that. 然后,禁用该包装上的名称修改和pInvoke。 I'd rather stick with the solution that I already have, though. 不过,我宁愿坚持我已有的解决方案。 I already told my co-workers that pInvoke is great. 我已经告诉我的同事,pInvoke很棒。 I'm going to look like an idiot if I have to put special functions in our c++ library just to make pInvoke work. 如果我必须在我们的c ++库中放置特殊函数以使pInvoke工作,我将会看起来像个白痴。

You cannot disable mangling for a C++ class method, but you may well be able to export the function under a name of your choice using /EXPORT or a .def file. 您不能为C ++类方法禁用修改,但您可以使用/EXPORT或.def文件以您选择的名称导出该函数。

However, your entire approach is brittle because you rely on an implementation detail, namely that this is passed as an implicit parameter. 但是,因为你靠的实现细节,即你的整个做法是易碎this将作为隐式参数传递。 And what's more, exporting individual methods of a class is a recipe for pain. 更重要的是,输出一个类的单个方法是痛苦的一个方法。

The most sensible strategies for exposing a C++ class to .net languages are: 将C ++类暴露给.net语言的最明智的策略是:

  1. Create flat C wrapper functions and p/invoke those. 创建平面C包装函数并调用它们。
  2. Create a C++/CLI mixed mode layer that publishes a managed class that wraps the native class. 创建一个C ++ / CLI混合模式层,用于发布包装本机类的托管类。

Option 2 is preferable in my opinion. 在我看来,选项2更可取。

You may be able to use the comment/linker #pragma to pass the /EXPORT switch to the linker which should allow you to rename the exported symbol: 您可以使用comment / linker #pragma将/EXPORT开关传递给链接器,该链接器允许您重命名导出的符号:

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

Unfortunately, this does not resolve your need to look up the mangled name using depends or some other tool. 不幸的是,这并不能解决您使用依赖或其他工具查找损坏名称的需要。

You do not have to disable the mangled name which actually contains lots of information of how the function itself is declared, it basically represents the whole signature of the function after the function name gets de-mangled. 您不必禁用实际包含有关如何声明函数本身的大量信息的受损名称,它基本上表示函数名称被去除后的函数的整个签名。 I understand you already found a word-around and the other answer has been marked as a correct answer. 我知道你已经发现了一个单词,而另一个答案已被标记为正确的答案。 What I am writing below is how we can make it work as you desired. 我在下面写的是我们如何使它按照您的期望工作。

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

If you replace "#OrdinalNumber" with the real ordinal number of GetAnsweer, such as "#1", it will work as you desired. 如果将“#OrdinalNumber”替换为GetAnsweer的实数序号,例如“#1”,它将按您的需要工作。

You may just consider the EntryPoint property is the same as the function name we pass to GetProcAddress where you can either pass the function name or the ordinal number of the function. 您可以只考虑EntryPoint属性与我们传递给GetProcAddress的函数名称相同,您可以在其中传递函数名称或函数的序号。

Your approach to calling non-static function members of a C++ class is indeed correct and thiscall is used correctly and that is exactly thiscall calling convention comes in play in C# P/Invoke. 您调用C ++类的非静态函数成员的方法确实是正确的,并且正确使用了此调用,这正是调用约定在C#P / Invoke中发挥作用。 The issue with this approach is that you will have to look into the DLL's PE information, Export Function Information and find out the ordinal number for each function you would like to call, if you have a big number of C++ functions to call, you may want to automate such a process. 这种方法的问题是你必须查看DLL的PE信息,导出函数信息并找出你想要调用的每个函数的序号,如果你有大量的C ++函数可以调用,你可以想要自动化这样一个过程。

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. 我结束了与C风格的功能封装了实例方法和采用的一个实例作为一个参数去。 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. 我故意选择不使用C ++ / CLI,因为它只是另一个要管理的项目。 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. 如果我需要在类上使用所有方法,我会考虑它,但我真的只需要这个序列化类数据的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM