简体   繁体   English

C ++ DLL Export:装饰/ Mangled名称

[英]C++ DLL Export: Decorated/Mangled names

Created basic C++ DLL and exported names using Module Definition file (MyDLL.def). 使用模块定义文件(MyDLL.def)创建基本C ++ DLL和导出的名称。 After compilation I check the exported function names using dumpbin.exe I expect to see: 编译后我使用dumpbin.exe检查导出的函数名称,我希望看到:

SomeFunction

but I see this instead: 但我看到了这一点:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

Why? 为什么?

The exported function appears undecorated (especially compared to not using the Module Def file), but what's up with the other stuff? 导出的函数显示为未修饰(特别是与不使用Module Def文件相比),但其他内容是什么?

If I use dumpbin.exe against a DLL from any commercial application, you get the clean: 如果我对任何商业应用程序的DLL使用dumpbin.exe ,你会得到干净的:

SomeFunction

and nothing else... 没有别的......

I also tried removing the Module Definition and exporting the names using the "C" style of export, namely: 我还尝试删除模块定义并使用“C”导出方式导出名称,即:

extern "C" void __declspec(dllexport) SomeFunction();

(Simply using "extern "C" did not create an exported function) (简单地使用“extern”C“没有创建导出函数)

However, this still creates the same output, namely: 但是,这仍然会产生相同的输出,即:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

I also tried the #define dllexport __declspec(dllexport) option and created a LIB with no problem. 我还尝试了#define dllexport __declspec(dllexport)选项并创建了一个没有问题的LIB。 However, I don't want to have to provide a LIB file to people using the DLL in their C# application. 但是,我不想在他们的C#应用​​程序中向使用DLL的人提供LIB文件。

It's a plain vanilla C++ DLL (unmanaged code), compiled with C++ nothing but a simple header and code. 它是一个普通的C ++ DLL(非托管代码),用C ++编译,只是一个简单的头和代码。 Without Module Def I get mangled exported functions (I can create a static library and use the LIB no problem. I'm trying to avoid that). 没有Module Def我得到了错误的导出函数(我可以创建一个静态库并使用LIB没问题。我试图避免这种情况)。 If I use extern "C" __declspec(dllexport) OR a Module Definition I get what appears to be an undecorated function name... the only problem is that it is followed by an "=" and what looks like a decorated version of the function. 如果我使用extern "C" __declspec(dllexport) 模块定义我得到的似乎是一个未修饰的函数名...唯一的问题是它后跟一个“=”,看起来像是一个装饰版本的功能。 I want to get rid of the stuff after the "=" - or at least understand why it is there. 我想摆脱“=”之后的东西 - 或者至少理解它为什么存在。

As it stands, I'm pretty certain that I can call the function from C# using a P/Invoke... I just want to avoid that junk at the end of the "=". 就目前而言,我非常肯定我可以使用P / Invoke从C#调用该函数......我只想在“=”的末尾避免使用该垃圾。

I'm open to suggestions on how to change the project/compiler settings, but I just used the standard Visual Studio DLL template - nothing special. 我对如何更改项目/编译器设置的建议持开放态度,但我只使用了标准的Visual Studio DLL模板 - 没什么特别的。

Instead of using .def file just insert pragma comment like this 而不是使用.def文件只需插入这样的pragma comment

#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")

Edit: Or even easier: Inside the body of the function use 编辑:甚至更容易:在函数体内使用

#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)

. . . if you have troubles finding the decorated function name. 如果你找到装饰的功能名称有麻烦。 This last pragma can be further reduced with a simple macro definition. 使用简单的宏定义可以进一步减少最后一个pragma。

You can get what you want by turning off debug info generation. 您可以通过关闭调试信息生成来获得所需的内容。 Project + Properties, Linker, Debugging, Generate Debug Info = No. 项目+属性,链接器,调试,生成调试信息=否。

Naturally, you only want to do this for the Release build. 当然,您只想为Release版本执行此操作。 Where the option is already set that way. 选项已经设置的位置。

如果您不希望它们的名称被破坏,您必须将函数声明为extern "C"

From experience, be careful if you use __stdcall in your function signature. 根据经验,如果在函数签名中使用__stdcall ,请务必小心。 With __stdcall , the name will remain mangled to some extent (you will find out quickly enough). 使用__stdcall ,名称将在某种程度上受损(您会很快发现)。 Apparently, there are two levels of mangling, one the extern "C" deals with at the C++ level, but it does not deal with another level of name mangling caused by __stdcall . 显然,有两个级别的修改,一个extern "C"处理C ++级别,但它不处理由__stdcall引起的另一级别的名称修改。 The extra mangling is apparently relevant to overloading -- but I am not certain of that. 额外的重整显然与超载相关 - 但我不确定。

Sorry for replying to an old thread, but what has been marked as the answer did not work for me. 很抱歉回复旧帖子,但标记为答案的内容对我不起作用。

As a number of people have pointed out, the extern "C" decoration is important. 正如一些人所指出的那样,外部的“C”装饰很重要。 Changing the "Project / Properties / Linker / Debugging / Generate debug info" setting made absolutely no difference to the mangled names being generated for me in either Debug or Release build mode. 更改“项目/属性/链接器/调试/生成调试信息”设置对于在调试或发布构建模式下为我生成的错位名称完全没有区别。

Setup: VS2005 compiling a Visual C++ Class Library project. 安装程序:VS2005编译Visual C ++类库项目。 I was checking the compiled .dll output with Microsoft's Dependency Walker tool. 我正在使用Microsoft的Dependency Walker工具检查已编译的.dll输出。

Here is an example recipe that worked for me... 这是一个适合我的示例配方......

In project.h: 在project.h中:

#define DllExport extern "C" __declspec( dllexport )

DllExport bool API_Init();
DllExport bool API_Shutdown();

In project.cpp: 在project.cpp中:

#include "project.h"

bool API_Init()
{
  return true;
}

bool API_Shutdown()
{
  return true;
}

Then being called from C# managed code, class.cs: 然后从C#托管代码调用class.cs:

using System.Runtime.Interopservices;
namespace Foo
{
    public class Project
    {
        [DllImport("project.dll")]
        public static extern bool API_Init();

        [DllImport("project.dll")]
        public static extern bool API_Shutdown();
    }
}

Doing the above prevented the mangled names in both Debug and Release mode, regardless of the Generate debug info setting. 无论生成调试信息设置如何,执行上述操作都会在调试和释放模式下防止出现损坏的名称。 Good luck. 祝好运。

Even without the mangling, the 32-bit and 64-bit builds name exports differently, even with extern "C". 即使没有修改,32位和64位版本的名称输出也不同,即使使用extern“C”也是如此。 Check it out with DEPENDS.EXE. 使用DEPENDS.EXE查看它。

This can mean BIG trouble to any client that does a LoadLibrary+GetProcAdress to access your function. 对于任何执行LoadLibrary + GetProcAdress访问您的功能的客户来说,这可能意味着很大的麻烦。

So, on top of all the others use a Module Definition File as follows: 因此,除了所有其他人之外,还使用模块定义文件,如下所示:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction

Yeap, it's a bit of a pain to maintain, but then how many exported functions do you write a day? 是的,维护起来有点痛苦,但是你每天写出多少导出函数?

Moreover, I usually change the macros like shown below, since my DLLs export functions not C++ classes and I want them to be callable by most programming environments: 此外,我通常更改如下所示的宏,因为我的DLL导出函数不是C ++类,我希望它们可以被大多数编程环境调用:

#ifdef WTS_EXPORTS
#define WTS_API(ReturnType) extern "C" __declspec(dllexport) ReturnType WINAPI
#else
#define WTS_API(ReturnType) extern "C" __declspec(dllimport) ReturnType WINAPI
#endif

WTS_API(int) fnWTS(void);

The last line used to confuse VisualAssistX a couple of years ago, I don't know if it properly digests it now :-) 几年前用于混淆VisualAssistX的最后一行,我不知道它现在是否正确消化它:-)

I know how many times I've tried forcing function names using code and #pragma's. 我知道有多少次我尝试使用代码强制函数名称和#pragma's。 And I always end with exactly same thing, using Module-Definition File (*.def) at the end. 我总是以完全相同的方式结束,最后使用模块定义文件(* .def)。 And here is the reason: 这就是原因:

//---------------------------------------------------------------------------------------------------
// Test cases built using VC2010 - Win32 - Debug / Release << doesn't matter
//---------------------------------------------------------------------------------------------------
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = Yes (/DEBUG)
//  || (or, also doesn't matter)
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = No + delete PDB file!

extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> _SetCallback@4

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> ?SetCallback@@YAXP6AXHPADPAX@Z@Z

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> ?SetCallback@@YGXP6GXHPADPAX@Z@Z    

//---------------------------------------------------------------------------------------------------
// this also big is nonsense cause as soon you change your calling convention or add / remove
// extern "C" code won't link anymore.

// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback=SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=_SetCallback@4")
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YAXP6AXHPADPAX@Z@Z")
__declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YGXP6GXHPADPAX@Z@Z")
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

//---------------------------------------------------------------------------------------------------
// So far only repetable case is using Module-Definition File (*.def) in all possible cases:
EXPORTS
  SetCallback

extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback

// And by far this is most acceptable as it will reproduce exactly same exported function name 
// using most common compilers. Header is dictating calling convention so not much trouble for
// other sw/ppl trying to build Interop or similar.

I wonder why no one did this, it took me only 10 mins to test all cases. 我想知道为什么没有人这样做,我花了10分钟来测试所有病例。

In case it wasn't clear from the hundreds of lines of waffle on the subject of mangled exports. 如果从数百行华夫饼干出口受损的问题不清楚的话。 Here's my 2c worth :) 这是我的2c值得:)

After creating a project called Win32Project2 using VS 2012 and choosing export all symbols in the wizard. 使用VS 2012创建名为Win32Project2的项目并在向导中选择导出所有符号后。 You should have 2 files called Win32Project2.cpp and Win32project2.h 你应该有2个名为Win32Project2.cpp和Win32project2.h的文件

Both of those will reference an example exportable variable and an example exported function. 这两个都将引用示例可导出变量和示例导出函数。

In Win32Project2.h you will have the following: 在Win32Project2.h中,您将拥有以下内容:

#ifdef WIN32PROJECT2_EXPORTS
#define WIN32PROJECT2_API __declspec(dllexport)
#else
#define WIN32PROJECT2_API __declspec(dllimport)
#endif

extern WIN32PROJECT2_API int nWin32Project2;
WIN32PROJECT2_API int fnWin32Project2(void);

To unmangle CHANGE the last two lines to extern "C" declarations to: 要将最后两行更改为extern“C”声明,请将:

extern "C" WIN32PROJECT2_API int nWin32Project2;
extern "C" WIN32PROJECT2_API int fnWin32Project2(void);

In Win32Project2.cpp you will also have the following default definitions: 在Win32Project2.cpp中,您还将具有以下默认定义:

// This is an example of an exported variable
WIN32PROJECT2_API int nWin32Project2=0;

// This is an example of an exported function.
WIN32PROJECT2_API int fnWin32Project2(void)
{
    return 42;
}

To unmangle CHANGE THESE TO: 解开改变这些:

// This is an example of an exported variable
extern "C" WIN32PROJECT2_API int nWin32Project2=0;

// This is an example of an exported function.
extern "C" WIN32PROJECT2_API int fnWin32Project2(void)
{
    return 42;
}

Essentially you must use the extern "C" prefix in front of declarations in order to force the linker to produce unmangled C like names. 基本上,您必须在声明前使用extern“C”前缀,以强制链接器生成未编码的C类名称。

If you prefer to use mangled names for that bit of extra obfuscation (in case the mangling info is useful to someone somehow) use "dumpbin /exports Win32Project2.dll" from a VC command line to lookup the actual reference names. 如果您更喜欢使用受损的名称进行额外的混淆(如果修改信息对某人有用),请使用VC命令行中的“dumpbin / exports Win32Project2.dll”来查找实际的引用名称。 It will have the form "?fnWind32Project2@[param bytes]@[other info] . There are also other DLL viewing tools around if running a VC command shell doesn't float your boat. 它将具有“?fnWind32Project2 @ [param bytes] @ [其他信息]的形式。如果运行VC命令shell不会浮动您的船,还有其他DLL查看工具。

Exactly why MS doesn't default to this convention is a mystery. 究竟为什么MS没有默认这个约定是一个谜。 The actual mangling information means something (like parameter size in bytes and more) which might be useful for validation and debugging but is otherwise guff. 实际的重整信息意味着某些东西(比如参数大小,以字节为单位),这可能对验证和调试很有用,但另外就是guff。

To import the DLL function above into C# project (in this case a basic C# windows application with a form on it containing the button "button1") here's some sample code: 要将上面的DLL函数导入C#项目(在这种情况下是一个基本的C#windows应用程序,其中包含一个包含按钮“button1”的表单),这里有一些示例代码:

using System.Runtime.InteropServices;



    namespace AudioRecApp
    {

      public partial class Form1 : Form
      {
        [ DllImport("c:\\Projects\test\Debug\Win32Projects2.dll")] 
        public static extern int fnWin32Project2();

        public Form1()
        {
          InitializeComponent();
        }


        private void button1_Click(object sender, EventArgs e)
        {
          int value;

          value = fnWin32Project2();
        }
      }
    }

the SomeFunction@@@23mangledstuff#@@@@ is mangled to give the types and class of the C++ function. SomeFunction @@@ 23mangledstuff#@@@@被修改以提供C ++函数的类型和类。 The simple exports are functions that are callable from C ie are written in C or else are declared extern "C' in C++ code. If is you want a simple interface you have to make the functions you export be use just C types and make them non member functions in the global namespace. 简单的导出是可以从C调用的函数,即用C编写,或者在C ++代码中声明为extern“C”。如果你想要一个简单的接口,你必须使你导出的函数只使用C类型并制作它们全局命名空间中的非成员函数。

Basically, when you use functions in C++, parts of their names now include their signature and suchlike, in order to facilitate language features like overloading. 基本上,当你在C ++中使用函数时,它们的部分名称现在包括它们的签名等,以便于重载等语言功能。

If you write a DLL using __declspec(dllexport), then it should also produce a lib. 如果使用__declspec(dllexport)编写DLL,那么它也应该生成一个lib。 Link to that lib, and you will automatically be linked and the functions registered by the CRT at start-up time (if you remembered to change all your imports to exports). 链接到该库,您将自动链接并在启动时由CRT注册的功能(如果您记得将所有导入更改为导出)。 You don't need to know about name mangling if you use this system. 如果使用此系统,则无需了解名称损坏。

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

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