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(); } } }