__stdcall, __cdecl, extern "C"等一些知识整理下~网上也有不少前辈的精彩文章整理一下放在最下面~
dll会随附生成一个lib,里面存的是函数名什么的,那么可以从他身上下手所以就不必委曲求全的去extern "C"了。也就是用静态调用~本来想用什么宏定义来控制输入输出的~太笨了我~我kao~看别人博客上的转载,如果那样的话,你还只包含一次,那岂不是输出代码就用不了了吗?转不过来弯了~一会试试吧,先把自己的代码弄上来吧,自己备忘~前辈们别笑话~
//dllTest.h
#ifndef LIB_H
#define LIB_H
int __declspec(dllexport)add(int x, int y);
float __declspec(dllexport)add(float x, float y);
#endif
//dllTest.cpp
#include "dllTest.h"
int add(int x, int y)
{
return x + y;
}
float add(float x, float y)
{
return x + y;
}
//main.cpp
#include <stdio.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"../Debug/dllTest.lib")
//其实这边若是用宏定义原来头文件时的两个导出和导入就帅多了~包含都文件就可以了~
//__declspec(dllimport)可不要~除非要用static,也是从网上前辈写的文章里看到的~
int __declspec(dllimport) add(int x,int y);
float __declspec(dllimport) add(float x,float y);
int main(int argc, char *argv[])
{
int result = add(2, 3);
float resultf = add(1.1f, 1.1f);
std::cout << result << std::endl;
std::cout << resultf << std::endl;
return 0;
}
//没错代码是从天极网讲动态链接库教程里改动过来的~
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
使用Dependency看DLL的导出函数的名字,会发现有一些有意思的东西,这大多是和编译DLL时候指定DLL导出函数的导出符有关系。
当你使用extern "C"的情况下:
__stdcall会使导出函数名字前面加一个下划线,后面加一个@再加上参数的字节数,比如_Fun@4就是4个字节
__cdecl则是前面仅仅有一个下划线
如果不用extern "C"话则使用C++命名机制,涉及到C++ Name Mangling,比较复杂,编译器之间也不太一样。
另外,__declspec(dllexport)仅会对__cdecl进行处理,去掉前面的下划线(对于一般全局函数来说缺省就是__cdecl),而对于其他两种不会处理。
extern "C"的作用是(防止C++编译器的“名字破坏”特性),使编译器按照C的方式生成函数名,C的方式实际的函数名和你写的一样。如果没有这个,则按照C++的方式生成函数名,这样实际的函数名(LoadLibrary方式GetProcAddress传入的函数名)和你写得函数名不一样,这样你用LoadLibrary、GetProcAddress这种方式调用dll就不成功。
但是用引入库(*.LIB)的方式调用,则编译器自动转换函数名,所以总是没有问题。
深入理解extern "C"
要明白为何需要使用extern "C",还得从C++中对函数的重载处理开始说起。作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了类似的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
3.“__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数”
和
“该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字”
这两个概念感觉很相似~这两个会不会冲突啊?放进符号表里的是哪个?那另一个是干什么用的?
答:一个是调用约定产生的字符,一个是本身名称进行的修饰
两者合成的名称才是最后的符号名称字符串
//////////////////////////////////////////////////////////////////////////
//天极网的教程真的太牛×了~
4.7 DLL导出类
DLL中定义的类可以在应用工程中使用。
下面的例子里,我们在DLL中定义了point和circle两个类,并在应用工程中引用了它们(单击此处下载本工程)。
//文件名:point.h,point类的声明 #ifndef POINT_H #define POINT_H #ifdef DLL_FILE class _declspec(dllexport) point //导出类point #else class _declspec(dllimport) point //导入类point #endif { public: float y; float x; point(); point(float x_coordinate, float y_coordinate); }; #endif //文件名:point.cpp,point类的实现 #ifndef DLL_FILE #define DLL_FILE #endif #include "point.h" //类point的缺省构造函数 point::point() { x = 0.0; y = 0.0; } //类point的构造函数 point::point(float x_coordinate, float y_coordinate) { x = x_coordinate; y = y_coordinate; } //文件名:circle.h,circle类的声明 #ifndef CIRCLE_H #define CIRCLE_H #include "point.h" #ifdef DLL_FILE class _declspec(dllexport)circle //导出类circle #else class _declspec(dllimport)circle //导入类circle #endif { public: void SetCentre(const point ¢rePoint); void SetRadius(float r); float GetGirth(); float GetArea(); circle(); private: float radius; point centre; }; #endif //文件名:circle.cpp,circle类的实现 #ifndef DLL_FILE #define DLL_FILE #endif #include "circle.h" #define PI 3.1415926 //circle类的构造函数 circle::circle() { centre = point(0, 0); radius = 0; } //得到圆的面积 float circle::GetArea() { return PI *radius * radius; } //得到圆的周长 float circle::GetGirth() { return 2 *PI * radius; } //设置圆心坐标 void circle::SetCentre(const point ¢rePoint) { centre = centrePoint; } //设置圆的半径 void circle::SetRadius(float r) { radius = r; } |
#include "../circle.h" //包含类声明头文件 #pragma comment(lib,"dllTest.lib"); int main(int argc, char *argv[]) { circle c; point p(2.0, 2.0); c.SetCentre(p); c.SetRadius(1.0); printf("area:%f girth:%f", c.GetArea(), c.GetGirth()); return 0; } |
class _declspec(dllexport) point //导出类point { … } |
class _declspec(dllexport) circle //导出类circle { … } |
class _declspec(dllimport) point //导入类point { … } |
class _declspec(dllimport) circle //导入类circle { … } |
class _declspec(dllexport) class_name //导出类circle { … } |
class _declspec(dllimport) class_name //导入类 { … } |
#ifdef DLL_FILE class _declspec(dllexport) class_name //导出类 #else class _declspec(dllimport) class_name //导入类 #endif |