现在的位置: 首页 > 综合 > 正文

动态链接库小结

2013年12月05日 ⁄ 综合 ⁄ 共 4334字 ⁄ 字号 评论关闭

xxx.lib //静态库

xxxx.dll xxxx.lib //分别是动态库动态库的引入库

隐式链接:

1: 拷入动态库dll 引入库lib;

2: 加入头文件

3: alt+F7 Link选项卡 Object/library modules:中输入引入库如xxx.lib

第3步也可以用这句代码代替:#pragma comment(lib,"MyDll.lib")

然后下一句代码就是加入头文件(因头文件中开头有导入函数的相关语句:

如extern "C"_declspec(dllimport) int add(int a,int b);)可写成:

#define DLL_API_WIN32TEST extern "C"
_declspec(dllimport)
//导入
DLL1_API int add(int a,int b);

如果同时隐式加载许多DLL,会造成程序启动慢,而且有些函数根本就不需要调用的,所以可以选择只有需要调用函数时才加载函数进行调用,这时候就会到下面的显示链接了,隐式加载其实也是调用函数LoadLibrary一个一个加载的.

显示链接:

要使用函数LoadLibrary

1:c++和c调用约定

HINSTANCE hInst;
hInst=LoadLibrary("Dlltest.dll");
typedef int (*ADDPROC)(int a,int b);
ADDPROC Add=(ADDPROC)GetProcAddress(hInst,add);//add是Dlltest.dll中导出的函数名

if(!Add)

{//函数加载失败}

FreeLibrary(hInst);//调用完,卸载DLL

2:C++和标准调用约定

HINSTANCE hInst;
hInst=LoadLibrary("Dlltest.dll");

typedef int (_stdcall *ADDPROC)(int a,int b);//如果DLL是paska调用约定也就是标准调用约定

ADDPROC Add=(ADDPROC)GetProcAddress(hInst,add);//add是Dlltest.dll中导出的函数名

if(!Add)

{//函数加载失败}

FreeLibrary(hInst);//调用完,卸载DLL

3:C++和C++调用约定,编写DLL时没有加extern "C",查得DLL导出函数为
?add@YAHHH@Z
并且为第1个函数

HINSTANCE hInst;
hInst=LoadLibrary("Dlltest.dll");

typedef int (*ADDPROC)(int a,int b);//如果DLL是paska调用约定也就是标准调用约

ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"?add@YAHHH@Z");//add是Dlltest.dll中导出的函数名

//ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));//或者这样写,指的是第1个函数add

if(!Add)

{//函数加载失败}

FreeLibrary(hInst);//调用完,卸载DLL

假设c盘有文件:xxx.exe xxxx.dll

在cmd下输入:

进入C盘.

执行命令: dumpin -imports xxx.exe 以查看导入xxx.exe 的函数

执行命令: dumpin -exports xxxx.dll 以查xxxx.dll 导出了哪些函数

C++编译器在导出函数的时候,会做名字改编(比如:?函数名@@YAHHH@Z),把函数名字改编后,C编译器编写的调用端程序,无法识别函数,所以需要加关键字 extern "C" 至使C++编译器没有发生名字改编(比如:函数名),以确保C调用端正确识别以调用。extern "C" 中的字母C要大写;这就是C++和C的调用约定了.

但是如果在函数名前面有加关键字 _stdcall 就成C++和标准调用语言 之间调用约定了.比如 得尔非语言..

例一 a:导出全局函数:c++与c 调用约定, 在查看DLL 直接导出的 函数名

//头文件 dllxxx.h//是给调用者用的 比如: #include "dllxxx.h"

//DLL_API_WIN32TEST 在调用者不要再定义

#ifdef DLL_API_WIN32TEST
#else
#define DLL_API_WIN32TEST extern "C"
_declspec(dllimport)
//导入
#endif

DLL_API_WIN32TEST int add(int a,int b);//未写成_stdcall add(int a,int b);
DLL_API_WIN32TEST int _stdcall subtract(int a,int b);//未写成_stdcall subtract(int a,int b);

//源文件

#define DLL_API_WIN32TEST extern "C"
_declspec(dllexport)
//导出

#include "dllxxx.h"

int add(int a,int b

{
return a+b;
}

int subtract(int a,int b)
{
return a-b;
}

例一 b:导出全局函数:c++和标准调用语言 调用约定, 在查看DLL 直接导出的 _+函数名+参数所占字节数

//头文件 dllxxx.h//是给调用者用的 比如: #include "dllxxx.h"

//所以DLL_API_WIN32TEST 在调用者不要再定义

#ifdef DLL_API_WIN32TEST
#else
#define DLL_API_WIN32TEST extern "C"
_declspec(dllimport)
//导入
#endif

DLL_API_WIN32TEST int _stdcall add(int a,int b);
DLL_API_WIN32TEST int _stdcall subtract(int a,int b);

//源文件

#define DLL_API_WIN32TEST extern "C"
_declspec(dllexport)
//导出

#include "dllxxx.h"

int _stdcall add(int a,int b)//_stdcall表示的是标准调用约定,表示winapi,paska调用约定
{
return a+b;
}

int _stdcall subtract(int a,int b))//_stdcall表示的是标准调用约定,表示winapi,paska调用约定
{
return a-b;
}

例二 导出整个类:

//头文件 dllxxx.h

#ifdef DLL_API_WIN32TEST
#else
#define DLL_API_WIN32TEST extern "C"
_declspec(dllimport)
//导入
#endif

class DLL_API_WIN32TEST Point//导出整个类,类保持类的本性 如:private:还是一样,是私有的
{
public:
void output(int x,int y);

void input(int x,int y);

private:
void test();

int x;

int y;
};

//源文件

#define DLL_API_WIN32TEST extern "C"
_declspec(dllexport)
//导出

#include "dllxxx.h"

#include<iostream>

using namespace std;
void Point::output(int x,int y)
{

cout<<endl<<x+y<<endl;
}

void Point::test()
{

cout<<endl<<"test"<<endl;
}

void input(int x,int y)

{

this->x = x;

this->y = y;

}

例三 导出类中各别函数:

//头文件 dllxxx.h

#ifdef DLL_API_WIN32TEST
#else
#define DLL_API_WIN32TEST extern "C"
_declspec(dllimport)
//导入
#endif

class Point//导出整个类,类保持类的本性如:private:还是一样,是私有的
{
public:
void DLL_API_WIN32TEST output(int x,int y);//只导出此函数

void input(int x,int y);

private:
void test();

int x;

int y;
};

//源文件

#define DLL_API_WIN32TEST extern "C"
_declspec(dllexport)
//导出

#include "dllxxx.h"

#include<iostream>

using namespace std;
void Point::output(int x,int y)
{

cout<<endl<<x+y<<endl;
}

void Point::test()
{

cout<<endl<<"test"<<endl;
}

void input(int x,int y)

{

this->x = x;

this->y = y;

}

//以上导出类的调用 Point point; point.公有函数(参数);

孙鑫最后说"..那么为了让我们所写的这些动态链接库呢,在其它语言当中,在其它的编译器当中,那么都可以被调用,那么我们可以呢,采用模块定义文件,让我们所输入出的函数的符号名不发生改变.."

以下是模块定义文件方法:

//头文件 dllxxx.def

LIBRARY Dll2 //内部名称 Dll2这个名称要和导出的动态库名称要匹配,这行也不是必须的

EXPORTS //这个有其它使用方法
add
subtract

//源文件

int add(int a,int b)
{
return a+b;
}

int subtract(int a,int b)
{
return a-b;
}

调用方法就是上面所使用的动态加载方法:

void CDLLUseDemoDlg::OnBtnUsefun()
{
HINSTANCE hInst;
hInst=LoadLibrary("Dll2.dll");
typedef int (*ADDPROC)(int a,int b);//如果 DLL是用的标准调用方法写的,那么这里应用写成

//typedef int (_stdcall *ADDPROC)(int a,int b);

ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");
if(!Add)
{
MessageBox("获取函数地址失败!");
return;
}
CString str;
str.Format("5+3=%d",Add(5,3));
MessageBox(str);

}

抱歉!评论已关闭.