VC++动态库导出函数和类

导出动态库中的函数和类首先需要加载动态库,加载的方式有两种,分为:隐式加载和显示加载。(编译器:VC++ 6.0)
对隐式加载还是显示加载的选择不明白请百度。


(一)隐式加载动态库

新建动态库工程,添加头文件和源文件,用以下几种方式分别导出“加减乘除”4个函数和point类,由于实现函数需要,因此添加了两个头文件。

动态库工程头文件(dlltest.h)代码如下:

#include<windows.h>
#include<stdio.h>

//_declspec(dllexport)作为导出函数的前缀,但是导出的函数名会发生改变
_declspec(dllexport) int add(int a, int b);

//extern "C"作为导出函数的前缀,导出的函数名不会发生改变,可以解决C和C++函数
//调用时名字改编的问题,但是extern "C"只能导出全局函数
extern "C" _declspec(dllexport) int sub(int m, int n);


//添加.def文件导出此函数,解决了标准调用(pascal)和c调用之间名字改编的问题。

//当实际函数名和def文件设置要导出的函数名一致时,导出的函数按实际名称导出;
_declspec(dllexport) int mul(int x, int y);

//当二者不一致时,可以按照如下格式导出函数:entryname = internalname
_declspec(dllexport) int fun(int i, int j);

class _declspec(dllexport) point 
{
public:
	point(int x, int y);
	void output();
private:
	int m;
	int n;
};


动态库工程源文件(dlltest.cpp)代码如下:

#include "dlltest.h"

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

int sub(int m, int n)
{
	return m-n;
}

int mul(int x, int y)
{
	return x*y;
}

int fun(int i, int j)
{
	return i/j;
}

point::point(int x, int y)
{
	m = x;
	n = y;
}

void point::output()
{
   HWND m_hwnd = GetForegroundWindow();
   HDC m_hdc = GetDC(m_hwnd);

   char buf[20] = {0};
   sprintf(buf,"x=%d, y=%d",m,n);
   TextOut(m_hdc, 0, 0, buf, strlen(buf));
   ReleaseDC(m_hwnd, m_hdc);

}
动态库工程编译成功之后生成引入库文件(dlltest.lib)和动态库文件(dlltest.dll).

导出的函数和类在VC++ 6.0自带的Denpedency Walker中查看如下所示,可以看到有的函数名字已经改变了,而有的函数没有改变:



接下来创建一个对话框工程,在对话框只放置一个按钮,按钮里对动态库的导出函数进行测试,分别创建对话框工程的头文件和源文件。

对话框工程的头文件(dllimport.h)代码如下:

//extern作为前缀声明add是外部定义的,此处也可以用_declspec(dllimport)
//做外部声明,效率更高。
extern int add(int a, int b);

//由于在动态库中定义时前面还添加了extern "C" ,因此这里也必须添加,否
//则编译不成功!
extern "C" _declspec(dllimport) int sub(int m, int n);<pre name="code" class="cpp">
//_declspec(dllimport)为前缀声明point类为导入类;//extern "C"只能用来导出全局函数,不能导出类和成员函数。class _declspec(dllimport) point {public:point(int x, int y);void output();private:int m;int n;};
 
 
对话框工程源文件(dlltest_jiemianDlg.cpp)按钮响应函数添加代码如下:

void CDlltest_jiemianDlg::OnBtnTest() 
{
	// TODO: Add your control notification handler code here
	point pt(3,5);
	pt.output();

	int num;
	CString strNum;

	num = add(1,2);
	strNum.Format("%d",num);
	MessageBox(strNum);

	num = sub(5,3);
	strNum.Format("%d",num);
	MessageBox(strNum);

}
对话框生成可执行程序(dlltest_jiemian.exe)


当对话框程序静态调用动态库时,需要做到3个步骤即可实现调用:

1>先把动态库的引入库文件和动态库文件放到对话框工程目录或Debug目录;

2>在对话框工程的Project->Settings->Object/library modules:设置栏添加动态库的引入库文件;

3>确保对话框对动态库中的导出函数和类做了声明(即是否包含dllimport.h这类文件)。


经测试,可以调用动态库导出的函数和类,结果如下图:



(二)显示加载动态库

由于显示加载和隐式加载动态库差不太多,这里就简单讲了。


动态库还是之前建立的动态库dlltest.dll,这里只拿其中的sub函数为例作说明:

显示加载和隐式加载声明函数的方式有很大不同,需注意!

头文件添加如下代码,此处的ADDFUC是定义的函数指针,声明sub函数式外部定义:

//>>=======================显式调用动态库dlltest.dll======================>>
//注意:显式调用动态库无法导入类和类成员函数!!
typedef int (*SUBFUC)(int a, int b);
//<<=======================显式调用动态库dlltest.dll======================<<

源文件的按钮响应函数添加如下代码:

void CDlltest_jiemianDlg::OnBtnTest() 
{
	// TODO: Add your control notification handler code here
	HMODULE hmodule;
    int num;
	CString strNum;

	hmodule = LoadLibrary("dlltest.dll");
	if (NULL == hmodule)
	{
		MessageBox("can not find the dll");
		return;
	}

    SUBFUC subfun = (SUBFUC)GetProcAddress(hmodule, "sub");
	if (!subfun)
	{
		MessageBox("get fun address failure");
		return;
	}

    num = subfun(5, 3);
	strNum.Format("%d",num);
	MessageBox("5-3="+strNum);

}

运行结果如下:



由于动态库导出的add函数名字改成成了“?add@@YAHHH@”,因此若要导出add函数,则GetProcAddress函数的第二个参数不能之家些“add”,

而应该写“?add@@YAHHH@”,表示导出动态库的add函数,修改如下:

SUBFUC subfun = (SUBFUC)GetProcAddress(hmodule, "?add@@YAHHH@Z");


动态加载动态库有几个关键函数要注意,具体用法自行百度:

1>HMODULE WINAPI LoadLibrary( _In_ LPCTSTR lpFileName);

2>BOOL WINAPI FreeLibrary( _In_ HMODULE hModule);

3>FARPROC GetProcAddress(HMODULE hModule,LPCSTR lpProcName);


当对话框程序静态调用动态库时,需要做到3个步骤即可实现调用:

1>先把动态库的引入库文件和动态库文件放到对话框工程目录或Debug目录;

2>确保对话框对动态库中的导出函数和类做了声明(即是否包含dllimport.h这类文件)。


尤其注意,不要在对话框工程的Project->Settings->Object/library modules:设置栏添加动态库的引入库文件!




猜你喜欢

转载自blog.csdn.net/GK_2014/article/details/45016995