C ++ DLL导出:装饰/隐藏名称

创build基本的C ++ DLL并使用模块定义文件(MyDLL.def)导出名称。 编译后,我使用dumpbin.exe检查导出的函数名称,我期望看到:

 SomeFunction 

但我看到这个:

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

为什么?

导出的函数显示为未修饰(特别是与不使用Module Def文件相比),但是其他的东西呢?

如果我使用dumpbin.exe从任何商业应用程序的DLL,你会得到干净的:

 SomeFunction 

没有别的

我也尝试删除模块定义并使用“C”风格的导出导出名称,即:

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

(只需使用“extern”C“不会创build导出的函数)

但是,这仍然产生相同的输出,即:

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

我也尝试了#define dllexport __declspec(dllexport)选项,并创build了一个没有问题的LIB。 不过,我不想为在C#应用程序中使用DLL的人提供一个LIB文件。

这是一个普通的香草C ++ DLL(非托pipe代码),用C ++编译,只不过是一个简单的头文件和代码。 没有模块def我得到了损坏的导出函数(我可以创build一个静态库,并使用LIB没有问题,我试图避免这一点)。 如果我使用extern "C" __declspec(dllexport) 模块定义我得到什么似乎是一个未装饰的函数名称…唯一的问题是,它后面跟着一个“=”,看起来像一个装饰版本function。 我想摆脱“=”后的东西 – 或者至less明白为什么它在那里。

现在,我敢肯定,我可以使用P / Invoke从C#调用函数…我只是想避免在“=”结尾的垃圾。

我打开如何更改项目/编译器设置的build议,但我只使用标准的Visual Studio DLL模板 – 没有什么特别的。

您可以通过closuresdebugging信息生成来获得所需的信息。 项目+属性,链接器,debugging,生成debugging信息=否。

当然,你只想为Release版本做这个。 选项已经设置的方式。

而不是使用.def文件,只需像这样插入pragma comment

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

编辑:甚至更简单:在函数体内使用

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

。 。 。 如果你有麻烦find装饰的function名称。 这个最后的附注可以用一个简单的macros定义进一步减less。

如果你不想让他们的名字被破坏,你必须声明这个函数为extern "C"

根据经验,如果在函数签名中使用__stdcall,请小心。 使用__stdcall时,名称会在一定程度上保持不变(您将很快find答案)。 显然有两个级别的mangling,一个是extern“C”在c ++级别处理的,但是它不处理由__stdcall引起的另一个级别的名称混乱。 额外的损坏显然与超载有关 – 但我不确定。

对不起,回复旧线程,但被标记为答案没有为我工作。

正如一些人指出的那样,外在的“C”装饰是重要的。 更改“项目/属性/链接器/debugging/生成debugging信息”设置完全没有区别于在debugging或发布构build模式下为我生成的重名名称。

安装程序:VS2005编译一个Visual C ++类库项目。 我正在用Microsoft的Dependency Walker工具检查编译后的.dll输出。

这里是一个示例配方,为我工作…

在project.h中:

 #define DllExport extern "C" __declspec( dllexport ) DllExport bool API_Init(); DllExport bool API_Shutdown(); 

在project.cpp中:

 #include "project.h" bool API_Init() { return true; } bool API_Shutdown() { return true; } 

然后从C#托pipe代码调用,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(); } } 

不pipe生成debugging信息的设置如何,在Debug和Release模式下都可以防止出现重名。 祝你好运。

即使没有损坏,32位和64位构build名称的输出也不相同,即使使用外部“C”也是如此。 用DEPENDS.EXE检查出来。

这可能意味着任何执行LoadLibrary + GetProcAdress访问您的函数的客户端都会遇到大的麻烦。

所以,在所有其他人之上使用一个模块定义文件如下:

 LIBRARY MYDLL EXPORTS myFunction=myFunction 

Yeap,维护起来有点痛苦,但是你每天写多less个输出函数呢?

此外,我通常更改macros,如下所示,因为我的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); 

几年前用来混淆VisualAssistX的最后一行,我不知道它现在是否正确地消化了它:-)

我知道有多less次尝试使用代码和#pragma来强制函数名称。 而且我总是以完全相同的方式结束,最后使用模块定义文件(* .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. 

我想知道为什么没有人这样做,只花了我10分钟来testing所有的情况。

SomeFunction @@@ 23mangledstuff#@@@@被修改为C ++函数的types和类。 简单的导出是可以从C中调用的函数,即用C语言编写,否则在C ++代码中声明为extern“C”。如果你想要一个简单的接口,你必须使你输出的函数只用Ctypes,全局名称空间中的非成员函数。

基本上,当你在C ++中使用函数时,他们名字的一部分现在包含了他们的签名和诸如此类,以便于重载等语言特性。

如果你使用__declspec(dllexport)编写一个DLL,那么它也应该产生一个lib。 链接到这个库,你会自动链接和CRT在启动时注册的function(如果你记得把所有的import改为出口)。 如果你使用这个系统,你不需要知道如何改名。

如果从数百条华夫cookies出口受到严重冲击的话,还不清楚。 这是我的2c价值:)

使用VS 2012创build一个名为Win32Project2的项目并select向导中导出所有符号。 你应该有2个文件叫做Win32Project2.cpp和Win32project2.h

这两个将引用一个示例可导出的variables和一个示例导出的函数。

在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); 

为了解开CHANGE最后两行,将“C”声明外联到:

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

在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; } 

要解决这些问题:

 // 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; } 

基本上,你必须在声明前使用extern“C”前缀来强制链接器产生未加载的C类名称。

如果您更喜欢使用重名的名称进行额外的混淆处理(如果重复信息对某人有用),请使用VC命令行中的“dumpbin / exports Win32Project2.dll”来查找实际的参考名称。 它的forms是“?fnWind32Project2 @ [param bytes] @ [other info]。如果运行一个VC命令shell并不会让你的船浮起来,还有其他的DLL查看工具。

究竟为什么MS不默认这个惯例是一个谜。 实际的mangling信息意味着某些东西(比如以字节为单位的参数大小等等),这对于validation和debugging可能是有用的,否则就是guff。

要将上面的DLL函数导入到C#项目(在这种情况下,一个基本的C#窗口应用程序,其中包含button“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(); } } }