C++ DLL 导出:修饰/错位名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2804893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ DLL Export: Decorated/Mangled names
提问by Bob
Created basic C++ DLL and exported names using Module Definition file (MyDLL.def).
After compilation I check the exported function names using dumpbin.exe
I expect to see:
创建基本的 C++ DLL 并使用模块定义文件 (MyDLL.def) 导出名称。编译后,我使用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:
如果我dumpbin.exe
对来自任何商业应用程序的 DLL 使用,你会得到干净的:
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. However, I don't want to have to provide a LIB file to people using the DLL in their C# application.
我也尝试了该#define dllexport __declspec(dllexport)
选项并创建了一个没有问题的 LIB。但是,我不想向在 C# 应用程序中使用 DLL 的人提供 LIB 文件。
It's a plain vanilla C++ DLL (unmanaged code), compiled with C++ nothing but a simple header and code. 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). If I use extern "C" __declspec(dllexport)
ORa 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. I want to get rid of the stuff after the "=" - or at least understand why it is there.
它是一个普通的普通 C++ DLL(非托管代码),用 C++ 编译,只有一个简单的头文件和代码。如果没有 Module Def ,我的导出函数会被破坏(我可以创建一个静态库并使用 LIB 没问题。我试图避免这种情况)。如果我使用extern "C" __declspec(dllexport)
OR模块定义,我会得到一个看似未修饰的函数名称……唯一的问题是它后面跟有一个“=”,看起来像是函数的修饰版本。我想摆脱“=”之后的东西 - 或者至少了解它为什么在那里。
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 模板 - 没什么特别的。
采纳答案by Hans Passant
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. Where the option is already set that way.
自然,您只想为发布版本执行此操作。选项已经设置为这种方式。
回答by wqw
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.
. . . 如果您在查找装饰函数名称时遇到麻烦。最后一个编译指示可以通过一个简单的宏定义进一步减少。
回答by jwismar
You have to declare the functions as extern "C"
if you don't want their names to be mangled.
您必须声明函数,就extern "C"
好像您不希望它们的名称被破坏一样。
回答by jjones
From experience, be careful if you use __stdcall
in your function signature. With __stdcall
, the name will remain mangled to some extent (you will find out quickly enough). 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
. The extra mangling is apparently relevant to overloading -- but I am not certain of that.
根据经验,如果您__stdcall
在函数签名中使用,请小心。使用__stdcall
,名称将在某种程度上保持混乱(您很快就会发现)。显然,有两个级别的修改,一个extern "C"
在 C++ 级别处理,但它不处理由__stdcall
. 额外的重整显然与重载有关——但我不确定这一点。
回答by AlwaysLearning
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. 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.
正如许多人指出的那样,外部“C”装饰很重要。更改“项目/属性/链接器/调试/生成调试信息”设置对在调试或发布构建模式下为我生成的损坏名称完全没有区别。
Setup: VS2005 compiling a Visual C++ Class Library project. I was checking the compiled .dll output with Microsoft's Dependency Walker tool.
设置:VS2005 编译 Visual C++ 类库项目。我正在使用 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.
无论 Generate debug info 设置如何,执行上述操作都可以防止在 Debug 和 Release 模式下出现损坏的名称。祝你好运。
回答by Dimitrios Staikos
Even without the mangling, the 32-bit and 64-bit builds name exports differently, even with extern "C". Check it out with DEPENDS.EXE.
即使没有修改,32 位和 64 位构建名称导出的方式也不同,即使使用 extern "C" 也是如此。使用 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,我不知道它现在是否正确消化它:-)
回答by SoLaR
I know how many times I've tried forcing function names using code and #pragma's. And I always end with exactly same thing, using Module-Definition File (*.def) at the end. And here is the reason:
我知道我尝试使用代码和 #pragma's 强制函数名称多少次。我总是以完全相同的方式结束,最后使用模块定义文件 (*.def)。原因如下:
//---------------------------------------------------------------------------------------------------
// 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 分钟就测试了所有案例。
回答by PGP
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. You should have 2 files called Win32Project2.cpp and Win32project2.h
使用 VS 2012 创建一个名为 Win32Project2 的项目并在向导中选择导出所有符号后。您应该有 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. 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.
如果您更喜欢使用重整名称进行那一点额外的混淆(以防重整信息对某人有用)从 VC 命令行使用“dumpbin /exports Win32Project2.dll”来查找实际的引用名称。它将采用 "?fnWind32Project2@[param bytes]@[other info] 形式。如果运行 VC 命令外壳不会使您的船漂浮,那么周围还有其他 DLL 查看工具。
Exactly why MS doesn't default to this convention is a mystery. 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.
究竟为什么 MS 不默认这个约定是一个谜。实际的重整信息意味着一些东西(比如以字节为单位的参数大小等),这可能对验证和调试有用,但在其他方面却是愚蠢的。
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();
}
}
}
回答by user151019
the SomeFunction@@@23mangledstuff#@@@@ is mangled to give the types and class of the C++ function. The simple exports are functions that are callable from C i.e. 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.
SomeFunction@@@23mangledstuff#@@@@ 被破坏以提供 C++ 函数的类型和类。简单导出是可以从 C 调用的函数,即用 C 编写的,或者在 C++ 代码中声明为 extern "C'。如果你想要一个简单的接口,你必须让你导出的函数只使用 C 类型并制作它们全局命名空间中的非成员函数。
回答by Puppy
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. 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). You don't need to know about name mangling if you use this system.
如果您使用 __declspec(dllexport) 编写一个 DLL,那么它也应该生成一个库。链接到该库,您将在启动时自动链接并注册 CRT 的函数(如果您记得将所有导入更改为导出)。如果您使用此系统,则无需了解名称修改。