calling c++ from golang with swig--windows dll 二
Name mangling && Name demangling
在讲述golang如何利用swig调用windows dll之前,需要了解一个概念:Name Mangling (或者叫Decorated Name).在百度翻译中输入Name Mangling,翻译成中文是“名字改编”,或者“名称重整”。Decorated Name,是微软的叫法,被百度翻译为“修饰名”。不管是英文名,还是翻译后的结果看,Name Mangling的叫法是比较合适的,并且是C++领域内的普遍叫法。通常情况下,C++程序员不必了解Name Mangling,但是在工程内生成或者调用动态链接库,可能需要对其有所了解。先看看下面的例子。
打开VS2010,File--New-Project,选择Visual C++--Win32,右侧选中Win32 Console Application,创建名称Simple的工程

点击OK,然后next,

选中DLL,勾选Export symbols,点击Finish完成创建。
下面是自动生成的Simple.h文件
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the SIMPLE_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// SIMPLE_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef SIMPLE_EXPORTS
#define SIMPLE_API __declspec(dllexport)
#else
#define SIMPLE_API __declspec(dllimport)
#endif
// This class is exported from the Simple.dll
class SIMPLE_API CSimple {
public:
CSimple(void);
// TODO: add your methods here.
};
extern SIMPLE_API int nSimple;
SIMPLE_API int fnSimple(void);
自动生成的代码导出了一个类,一个函数,一个变量。给CSimple类添加一个简单的方法,SayHello
#ifdef SIMPLE_EXPORTS
#define SIMPLE_API __declspec(dllexport)
#else
#define SIMPLE_API __declspec(dllimport)
#endif
// This class is exported from the Simple.dll
class SIMPLE_API CSimple {
public:
CSimple(void);
// TODO: add your methods here.
int SayHello();
};
extern SIMPLE_API int nSimple;
SIMPLE_API int fnSimple(void);
Cpp文件:
// This is an example of an exported variable
SIMPLE_API int nSimple=0;
// This is an example of an exported function.
SIMPLE_API int fnSimple(void)
{
return 42;
}
// This is the constructor of a class that has been exported.
// see Simple.h for the class definition
CSimple::CSimple()
{
return;
}
int CSimple::SayHello()
{
return 0;
}
在Configuration Manager中添加x64

编译后,在D:\Sample\Simple\x64\Debug生成的主要文件有Simple.dll,Simple.lib,Simple.pdb。
通常情况下,C++调用Dll只需要在工程链接属性中设置附加库路径和附加依赖引入Simple.lib就可以了,动态链接库的生成和调用就到此为止了。
调用LoadLibrary 和GetProcAddress接口时,或者解决运行时缺少动态链接库问题时,往往会用到windows开发者熟知的著名工具Dependency Walker,这个工具可以查看依赖和导入导出工具,用x64版打开上面生成的64位dll,

在导出栏里我们看到Simple.dll导出了5个函数(变量),函数名都是以?开头,从代码里看,只导出了4项:CSimple类的两个成员函数CSimple::CSimple、CSimple::SayHello,fnSimple和nSimple变量,dependency Walker中的导出多了1项,从函数名上可以大概推断下,第五项是nSimple变量,第四项是fnSimple函数,第三项是CSimple::SayHello函数,其他两个不同容易看,其中一个应该是CSimple::CSimple。
在VS的安装目录下(C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64)有一些小工具,其中一个叫undname.exe,这个工具的全名应该叫Undecorated Name,与Decorated是反义,这个工具可以将导出函数名解析成程序员易读的名字。依次对1-5调用undname
|
Exported function name |
undname |
|
??0CSimple@@QEAA@XZ |
"public: __cdecl CSimple::CSimple(void) __ptr64" |
|
??4CSimple@@QEAAAEAV0@AEBV0@@Z |
"public: class CSimple & __ptr64 __cdecl CSimple::operator=(class CSimple const & __ptr64) __ptr64" |
|
?SayHello@CSimple@@QEAAHXZ |
"public: int __cdecl CSimple::SayHello(void) __ptr64" |
|
?fnSimple@@YAHXZ |
"int __cdecl fnSimple(void)" |
|
?nSimple@@3HA |
"int nSimple" |
CSimple::CSimple()成员函数名经过编译器处理后变成了??0CSimple@@QEAA@XZ,CSimple::SayHello成员函数经过编译器处理后变成了?SayHello@CSimple@@QEAAHXZ,通俗地讲这个处理过程就是Name Mangling.
引用一外国哥们博文中的三段英文,虽然非官方解释,但是个人认为他把Name Mangling很清楚地讲明白了。如果希望有更严谨的解释,自行查看维基百科。
here is a quick reminder on what is name mangling and why it is used in C++. When an executable is started, some (many) of its symbols addresses are resolved during the startup - at runtime - because these addresses are not known during the static link (i.e. when the executable is generated). One way to resolve them is to search them by symbol name. This is typically what is done when "dlsym" is used, but it is also how the runtime linker resolves the address of the functions implemented in the dynamic libraries.
When developing in C, all public symbols of an executable are unique. It is not possible to have two different variables or functions with the same name. The only exceptions to this are local variables and static variables/functions. However such symbols are private to a function or a file and cannot be retrieved by name at runtime. So in C the symbols signature is the name that appears in the code. In this case the linker can find a function by using its name as the signature.
Things are not that simple in C++. The symbols names cannot just be the name of the functions or methods because the language allows polymorphism: A method can have several prototypes to operate on different kinds of data. for example it is possible to declare "void Foo(int)" and "void Foo(double)". In this case it is not possible to use the function name as a signature without a conflict. This is why name mangling is used: Some additional information is added to the symbol name to make it unique in the executable/library. One can see C++ mangling as a concatenation of the method name with its parameters types. This is a simplification because other things are used to generate a mangled name, but it is the general idea. Once names are mangled, it is possible to resolve symbols at runtime by name without conflict.
简单的翻译就是:C++允许函数重载,需要name mangling来保证名称唯一。C++标准中没有明确name mangling的规则,每家编译器的实现不太一样,甚至同一编译器的不同版本,同一编译器在不同平台上的实现,Name Mangling也可能不一样。(参考wiki Name Mangling)。
比较常见的两个C++ 编译器分别是GNU gcc(g++)编译器和微软Visual C++编译器,这两个编译器Name Mangling的结果完全不一样。GNU编译器是开源的,所以其Name Mangling的规则及代码都可以在网上搜索到。Github.com上的gcc-mirror/gcc,gcc/gcc/cp/mangle.c是gcc编译器Name Mangling部分的源码。

Visual C++是商业闭源软件,他的Name Mangling规则是非公开的,微软的术语叫decorated Name,官方资料少得可怜,可以在MSDN上查看相关内容。

对Name Mangling有了个初步的了解后,我们继续看例子。将上面VS2010自动生成的代码用g++编译成dll文件。
编译前需要在windows os中安装TDM-GCC,这是gcc编译器的一个windows版实现,官方下载地址(http://tdm-gcc.tdragon.net/download)。
在D:\Sample\文件夹下创建子文件夹GCC,将Simple.h和Simple.cpp文件拷贝到D:\Sample\gcc下,将Simple.cpp文件中的#include "stdafx.h"注释掉。打开命令行,切换到D:\Sample\gcc,执行g++ -shared -Wall -o Simple.dll Simple.cpp -DSIMPLE_EXPORTS,生成了Simple.dll,再次用Dependency Walker打开刚刚生成的Simple.dll

乍一看,gcc编译的dll也导出了5个函数(变量),实则不然,第三和第四的Entry Point相同,同一函数有两个名称而已。GNU C++编译器也有个工具,名字叫c++filt,他的功能和微软的undname.exe是一样的,对mangled name进行demangling。依次对1-5调用c++filt

|
Mangled names |
c++filt |
|
_Z8fnSimplev |
fnSimple() |
|
_ZN7CSimple8SayHelloEv |
CSimple::SayHello() |
|
_ZN7CSimpleC1Ev |
CSimple::CSimple() |
|
_ZN7CSimpleC2Ev |
CSimple::CSimple() |
|
nSimple |
nSimple |
通过上面的两个例子可以看出,Visual C++编译器与GNU编译器Name Mangling的结果是完全不一样的,并且demangle工具给出的信息量也是不一样的,c++filt给出的结果只有名称,可区分函数和变量。
在demangling过程中,微软和GNU编译器都提供了demangling工具;但是如果想要借助一个工具从一个Name得到Mangled Name,好像没有单独的工具,根据我百度加谷歌的结果得出的答案。不过可以借助编译器预处理来间接得到结果,具体在以后的随笔中给出。
从上面的第一个例子可以看到,vs2010 C++编译器为CSimple类自动生成了拷贝赋值操作符=,看过Effective C++第三版的会知道,条款5《了解C++默认编写并调用哪些函数》讲到:“什么时候empty class(空类)不再是一个empty class?当C++处理过它之后。是的,如果你自己没声明,编译器就会为它声明(编译器版本的)一个copy构造函数,一个copy assignment操作符和一个析构函数。此外如果你没有声明任何构造函数,编译器也会为你声明一个default构造函数。所有这些都是public切inline。”后面又讲到,“唯有当这些函数被需要(被调用),他们才会被编译器创建出来。”
在生成动态链接库时,微软的编译器似乎并没有遵守条款五,因为生成dll文件时,编译器无法知道dll的调用者是否会调用拷贝赋值操作符;GUN编译器遵守了这个条款,在导出的类成员函数中只有构造函数CSimple::CSimple()和CSimple::SayHello()。 或者说,条款五描述的是GNU C++编译器的行为。
calling c++ from golang with swig--windows dll 二的更多相关文章
- calling c++ from golang with swig--windows dll(一)
calling c++ from golang with swig--windows dll 之前项目组开发的项目核心代码全部使用C++语言,新项目可能会引入golang,花了一天多时间研究了wind ...
- calling c++ from golang with swig--windows dll (三)
calling c++ from golang with swig--windows dll 三 使用动态链接库(DLL)主要有两种方式:一种通过链接导入库,在代码中直接调用DLL中的函数:另一种借助 ...
- calling c++ from golang with swig--windows dll (四)
calling c++ from golang with swig--windows dll 四 前面讲述了windows环境下golang如何通过swig调用C++ dll.由于编译c++代码使用了 ...
- golang调用c++的dll库文件
最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...
- Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术
catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...
- go 调用windows dll 的方法
go 调用windows dll 的方法 ,代码如下: package main import ( "fmt" "syscall" "time&quo ...
- C#实现动态调用Windows DLL
调用方法: object obj = WinDllInvoke("Kernel32.dll", "Beep", , }, typeof(void)); 函数代码 ...
- Golang丰富的I/O 二----cgo版Hello World
h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...
- Windows Forms(二)
导读 1.用VS创建一个Windows Forms程序 2.分析上面的程序 3.Mediator pattern(中介者模式) 4.卡UI怎么办——BackgroundWorker组件 用VS创建一个 ...
随机推荐
- TCP/IP协议族(二) HTTP报文头解析
本篇博客我们就来详细的聊一下HTTP协议的常用头部字段,当然我们将其分为请求头和响应头进行阐述.下方是报文头每个字段的格式,首先是头部字段的名称,如Accept,冒号后方紧跟的是该字段名所对应的值,每 ...
- Python - Python2与Python3合理共存Windows平台
Install Python2 and Python3 Python 2.7.13 - Windows x86-64 MSI installer Python 3.6.0 - Windows x86- ...
- 64位win2003 IIS6运行32位的.NET程序
做web服务迁移,从32位win2003迁移到64位win2003,数据库是32位Oracle在另外一台服务器上. 迁移之后数据库各种连不上,oracle的客户端32位的装完装64位的,odp.net ...
- 矢量切片(Vector tile)番外一:Proj4js
说明:番外篇是对正篇矢量切片(Vector tile)中提到的一些值得继续延伸的关注点继续进行探索和学习,所涉及的内容以解决实际问题为主要导向. 一.新的需求? 在完成了矢量切片的工作后,新的需求出现 ...
- Array方法归类总结
数组的转换方法 valueOf()方法,数组调用该方法后返回的还是原来的数组. toString()方法,数组调用该方法后会调用每一项的toStirng()方法,之后将每一项拼接成一个以逗号分割的字符 ...
- windows下部署免费ssl证书(letsencrypt)
随着网络的发展,网络安全也越来越重要,对于网站来说,从Http升级到https也是我们要做的首要事情.要实现https,首先我们需要申请一张SSL证书,这篇文章我主要介绍下边这几个方面: 1. SSL ...
- 利用AForge.NET 调用电脑摄像头进行拍照
当然了,你需要去官网下载类库,http://www.aforgenet.com/ 调用本机摄像头常用的组件: AForge AForge.Controls AForge.Imaging AForge. ...
- linux 下安装eclipse和pydev插件用来开发python程序
1.网上的教程要求必须要安装java虚拟机,但是我觉得不用,但是为了不出意外就装吧. (1)去官网下载安装包www.sun.com (2)安装包是.tar.gz的可以用 tar -xzvf +压缩包路 ...
- 从HTML5规范弄清i、em、b、strong元素的区别
为了语义化,HTML5增加了不少新标签.其中i.em和b.strong这两组标签是最容易弄混的,不好好去探究一下,还真说不清.这个也是前端面试中经常会问的问题.今天从源头上,也就是从HTML5的文档( ...
- KEEP!
[list][*]别问我前端有没有前途,我不知道,我只知道我现在喜欢前端,以后也应该喜欢.[*]别问我前端的工作好不好找,不管哪一职位,工作好不好找都是看你的水平.[*]别问我前端累不累,这世界就没有 ...
