使用模块定义文件(MyDLL.def)创建基本C ++ DLL和导出名称。
编译后,我使用dumpbin.exe
检查导出的函数名称
我希望看到:
SomeFunction
但我明白了这一点:
SomeFunction = SomeFunction@@@23mangledstuff#@@@@
为什么?
导出的函数看似未修饰(特别是与不使用Module Def文件相比),但其他东西是什么?
如果我对来自任何商业应用程序的DLL使用dumpbin.exe
,那么你就得到了干净的:
SomeFunction
,别无其他......
我还尝试删除模块定义并使用“C”导出方式导出名称,即:
extern "C" void __declspec(dllexport) SomeFunction();
(简单地使用“extern”C“没有创建导出函数)
然而,这仍然会产生相同的输出,即:
SomeFunction = SomeFunction@@@23mangledstuff#@@@@
我还尝试了#define dllexport __declspec(dllexport)
选项并创建了一个没有问题的LIB。但是,我不想在他们的C#应用程序中向使用DLL的人提供LIB文件。
这是一个普通的C ++ DLL(非托管代码),用C ++编译,只是一个简单的头和代码。没有Module Def我得到了错误的导出函数(我可以创建一个静态库并使用LIB没问题。我试图避免这种情况)。如果我使用extern "C" __declspec(dllexport)
OR 模块定义我得到的是一个看似未修饰的函数名称...唯一的问题是它后跟一个“=”,看起来像是一个装饰版的功能。我想摆脱“=”之后的东西 - 或者至少理解它为什么存在。
就目前而言,我很确定我可以使用P / Invoke从C#调用该函数......我只是想避免在“=”结束时使用该垃圾。
我愿意接受有关如何更改项目/编译器设置的建议,但我只使用了标准的Visual Studio DLL模板 - 没什么特别的。
答案 0 :(得分:40)
不要使用.def文件,只需像这样插入pragma comment
#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")
编辑:甚至更容易:在函数体内使用
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
。 。 。如果你找到装饰的功能名称有麻烦。使用简单的宏定义可以进一步减少最后一个pragma。
答案 1 :(得分:16)
您可以通过关闭调试信息生成来获得所需内容。 Project + Properties,Linker,Debugging,Generate Debug Info = No。
当然,您只想为Release版本执行此操作。选项已经设置的地方。
答案 2 :(得分:12)
如果您不希望他们的名字被破坏,您必须将这些函数声明为extern "C"
。
答案 3 :(得分:11)
根据经验,如果在功能签名中使用__stdcall
,请务必小心。使用__stdcall
,名称将在某种程度上受损(您会很快发现)。显然,有两个级别的修改,一个extern "C"
处理C ++级别,但它不处理由__stdcall
引起的另一级别的名称修改。额外的重整显然与超载相关 - 但我不确定。
答案 4 :(得分: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();
在project.cpp中:
#include "project.h"
bool API_Init()
{
return true;
}
bool API_Shutdown()
{
return true;
}
然后从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();
}
}
无论Generate debug info设置如何,执行上述操作都会在Debug和Release模式下防止出现错位。祝你好运。
答案 5 :(得分:5)
即使没有破坏,32位和64位构建名称的输出也不同,即使使用extern“C”也是如此。请使用DEPENDS.EXE进行检查。
对于任何执行LoadLibrary + GetProcAdress访问您的功能的客户来说,这可能意味着很大的麻烦。
因此,除了所有其他人之外,还使用模块定义文件,如下所示:
LIBRARY MYDLL
EXPORTS
myFunction=myFunction
是的,维护起来有点痛苦,但是你每天写出多少导出函数?
此外,我通常更改如下所示的宏,因为我的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的最后一行,我不知道它现在是否正确消化它: - )
答案 6 :(得分:4)
我知道有多少次我尝试使用代码和#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分钟来测试所有病例。
答案 7 :(得分:0)
SomeFunction @@@ 23mangledstuff#@@@@被修改为提供C ++函数的类型和类。简单的导出是可以从C调用的函数,即用C编写,或者在C ++代码中声明为extern“C”。如果你想要一个简单的接口,你必须使你导出的函数只使用C类型并制作它们全局命名空间中的非成员函数。
答案 8 :(得分:0)
基本上,当你在C ++中使用函数时,它们的部分名称现在包含它们的签名等,以便于重载等语言功能。
如果使用__declspec(dllexport)编写DLL,那么它也应该生成一个lib。链接到该库,您将自动链接并在启动时由CRT注册的功能(如果您记得将所有导入更改为导出)。如果使用此系统,则无需了解名称损坏。
答案 9 :(得分:0)
如果从数百行华夫饼干中出现错误的出口问题尚不清楚。这是我的2c价值:))
使用VS 2012创建名为Win32Project2的项目并在向导中选择导出所有符号。你应该有2个名为Win32Project2.cpp和Win32project2.h的文件
这两个都将引用示例可导出变量和示例导出函数。
在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);
取消将最后两行改为extern“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”来查找实际的引用名称。它将具有“?fnWind32Project2 @ [param bytes] @ [其他信息]的形式。如果运行VC命令shell不会漂浮你的船,还有其他DLL查看工具。
究竟为什么MS没有默认这个约定是一个谜。实际的重整信息意味着某些东西(比如参数大小,以字节为单位),这可能对验证和调试很有用,但另外就是guff。
要将上面的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();
}
}
}