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

Bob*_*Bob 26 c++ dll export name-decoration

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

SomeFunction
Run Code Online (Sandbox Code Playgroud)

但我看到了这一点:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@
Run Code Online (Sandbox Code Playgroud)

为什么?

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

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

SomeFunction
Run Code Online (Sandbox Code Playgroud)

没有别的......

我还尝试删除模块定义并使用"C"导出方式导出名称,即:

extern "C" void __declspec(dllexport) SomeFunction();
Run Code Online (Sandbox Code Playgroud)

(简单地使用"extern"C"没有创建导出函数)

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

SomeFunction = SomeFunction@@@23mangledstuff#@@@@
Run Code Online (Sandbox Code Playgroud)

我也尝试了这个#define dllexport __declspec(dllexport)选项并创建了一个没有问题的LIB.但是,我不想在他们的C#应用​​程序中向使用DLL的人提供LIB文件.

它是一个普通的C++ DLL(非托管代码),用C++编译,只是一个简单的头和代码.没有Module Def我得到了错误的导出函数(我可以创建一个静态库并使用LIB没问题.我试图避免这种情况).如果我使用extern "C" __declspec(dllexport) OR模块定义我得到的似乎是一个未修饰的函数名...唯一的问题是它后跟一个"=",看起来像是函数的装饰版本.我想摆脱"="之后的东西 - 或者至少理解它为什么存在.

既然这样,我敢肯定,我可以使用P调用从C#中的函数/调用...我只是想避免垃圾在"="号结束.

我对如何更改项目/编译器设置的建议持开放态度,但我只使用了标准的Visual Studio DLL模板 - 没什么特别的.

wqw*_*wqw 40

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

#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")
Run Code Online (Sandbox Code Playgroud)

编辑:甚至更容易:在函数体内使用

#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
Run Code Online (Sandbox Code Playgroud)

...如果你找到装饰的功能名称有麻烦.使用简单的宏定义可以进一步减少最后一个pragma.

  • 使用Visual Studio 2015或更高版本时,请务必在双引号和下划线字符之间添加空格.这可以防止编译器将代码构造解释为用户定义的文字:`#pragma comment(linker,"/ EXPORT:"__ FUNCTION __"="__ FUNCDNAME __)` (3认同)
  • 这就是诀窍!正如@jjones所说,`extern"C"`不适用于`__stdcall`.`pragma`在DLL导出的函数列表中创建第二个条目.我希望我能给你更多积分! (2认同)

Han*_*ant 16

您可以通过关闭调试信息生成来获得所需的内容.项目+属性,链接器,调试,生成调试信息=否.

当然,您只想为Release版本执行此操作.选项已经设置的位置.

  • 可悲的是,它在VC2010中没有成功. (5认同)

jwi*_*mar 14

您必须声明这些函数,就extern "C"好像您不希望它们的名称被破坏一样.


小智 11

根据经验,如果您__stdcall在功能签名中使用,请务必小心.有了__stdcall,名称将在某种程度上受损(你会发现很快).显然,有两个级别的修改,一个是extern "C"C++级别的处理,但它不涉及由另一个级别的名称修改引起的__stdcall.额外的重整显然与超载相关 - 但我不确定.


Alw*_*ing 6

很抱歉回复旧帖子,但标记为答案的内容对我不起作用.

正如一些人所指出的那样,外部的"C"装饰很重要.更改"项目/属性/链接器/调试/生成调试信息"设置对于在调试或发布构建模式下为我生成的错位名称完全没有区别.

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

这是一个适合我的示例配方......

在project.h中:

#define DllExport extern "C" __declspec( dllexport )

DllExport bool API_Init();
DllExport bool API_Shutdown();
Run Code Online (Sandbox Code Playgroud)

在project.cpp中:

#include "project.h"

bool API_Init()
{
  return true;
}

bool API_Shutdown()
{
  return true;
}
Run Code Online (Sandbox Code Playgroud)

然后从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();
    }
}
Run Code Online (Sandbox Code Playgroud)

无论生成调试信息设置如何,执行上述操作都会在调试和释放模式下防止出现损坏的名称.祝好运.

  • 最后不是,我的问题来自于在C函数声明中使用__declspec(dllexport)和__stdcall.事实证明,使用__stdcall得到函数void myfunction(void)装饰如下_myfunction @ 0而不使用__stdcall它现在导出为myfunction而没有任何装饰! (2认同)

小智 5

即使没有修改,32位和64位版本的名称输出也不同,即使使用extern"C"也是如此.使用DEPENDS.EXE查看它.

对于任何执行LoadLibrary + GetProcAdress访问您的功能的客户来说,这可能意味着很大的麻烦.

因此,除了所有其他人之外,还使用模块定义文件,如下所示:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction
Run Code Online (Sandbox Code Playgroud)

是的,维护起来有点痛苦,但是你每天写出多少导出函数?

此外,我通常更改如下所示的宏,因为我的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);
Run Code Online (Sandbox Code Playgroud)

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


SoL*_*LaR 5

我知道我尝试过多少次使用代码和#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.
Run Code Online (Sandbox Code Playgroud)

我想知道为什么没有人这样做,我只花了 10 分钟就测试了所有案例。