dll函数导出(环境vc6.0+xp)

 

        总觉得自己写过的程序没有做下记录和分析是件很可惜的事情,也许在别人看来是很简单的事情,但是如果没有自己深入研究和思考,并且分析整理的话,那编过的程序也仅仅是个程序,并没有太大的价值,所以想开始对自己写的程序开始记录,分析,这就作为自己编程的一个新的开始。

        这次的程序是关于DLL的,其实一直想要掌握DLL的相关知识,但是无奈工作中没有涉及到这方面的程序编写。这次也是自己找个相关的程序练手,打算以这个为切入点了解DLL,好了开始编程了。

        先把程序贴出来吧:DLL的头文件HDI.h(所有程序编译的环境是xp+vc6.0)

#ifndef _HDI_H
#define _HDI_H

#if defined DLLEXPORT 
#define DLL_EXPORT  extern"C"_declspec(dllexport)
#else
#define DLL_EXPORE  extern"C"_declspec(dllimport)
#endif

DLL_EXPORT int fun(int a, int b);

#endif

       然后是cpp文件

#define DLLEXPORT
#include "hdi.h"
int fun(int a,int b)
{
	return a+b;
}

下面是测试程序的cpp

#include <iostream>
#include <windows.h>
#include "hdi.h"
#pragma comment(lib,"HID.lib")
//extern"C" int fun(int,int);

typedef  int (*pFun)(int ,int);

int main()
{
//	HMODULE hmodle = ::LoadLibrary("F:\\HID\\HID\\Debug\\HID.dll");
//	HMODULE hmodle = ::LoadLibrary("HID.dll");
// 	if (hmodle == NULL)
// 	{
// 		std::cout<<"Load Library fail!"<<std::endl;
// 	}
// 	else
// 	{
// 		pFun addFun = (pFun)GetProcAddress(hmodle , "fun");
// 		int k = addFun(1,2);
// 		std::cout<<k<<std::endl;
// 		FreeLibrary(hmodle);
// 	}
	std::cout<<fun(1,2)<<std::endl;
	system("pause");

	return 0;
};

        针对这个程序做个简要的说明:它是一个普通的只导出函数的DLL,并没有导出变量和类,网上说导出类是不能动态加载,这里先不考虑。

        首先来看看h文件,这里有一句define DLL_EXPORT extern"C"_declspec(dllexport) ,关键词extern"C"可以让导出的函数名格式符合C的标准,也就是只有函数名(这同时也意味着同名函数的重载将不可能,编译会出错),他的好处是其他编译器也能正确导入这个函数,否则他的函数名会按照c++的风格导出,会变成“@@fun_int_int “之类,这样只有c++的编译器才能正确导入该函数(虽然我的机器貌似只有加extern”C“才能正确导入)。

         接着来看看关键字dllexport和下面的dllimport,dllexport表示这是一个导出函数,凡是要导出的函数必须加这个修饰,如果不用_declspec(dllexport)修饰,那么就要写def文件,这个我也没有去测试,觉得这种方式方便。dllimport函数说明这是一个导入函数,之所以这样写是在编写的dll的cpp文件中会预编译dllexport,而在应用dll的程序中就会预编译dllimport,可能这样说还是不清楚,这么说吧应用程序中也会用得到dll的头文件但是如果吧dll的头文件直接拿过来用而没有加这个预编译判断,那么程序用应用程序中也声明成dllexport显然是不正确的,所以加了这个判断就可以直接把dll头文件拿来用(至于哪里用到我下面写)。

         最后只要用这个宏修饰函数,并在cpp文件中实现它就可以了。程序编写完成以后,编译通过后会在debug文件下生成dll文件和lib文件,网上有人说在debug环境下生成的dll只要函数参数有拷贝赋值函数或重载=,他就只能被debug版本的应用程序编译通过,这我没有发现。倒是我测试过以string作为参数,应用程序传的参数是字符串常量时编译通过,运行会报错,而传string变量就没有错,搞了半天没有头绪。string变量是采用应用计数和写时复制的技术,以变量为参数是没问题,但是以常量为参数就会在跳出dll函数时出错,我理解是string析构问题,但是dll有自己的数据段,像参数应该是堆栈段,是共用主程序的,理论上应该析构不会有什么问题。这里我给的解释是由于字符常量是在主函数中定义,在作为参数时他同时也会在主程序中new一个string,然后这个string被传到了dll中,dll函数结束时引用计数为0,他就析构但是他要访问主程序的内存,这不被允许所以出错,而传string变量时虽然dll函数也结束了,但引用计数不为0,他不会去释放主程序的内存空间。解决的方法就是在主程序中开辟的内存在主程序中析构,在dll中开辟的在dll中析构

         DLL的调用,网上都说了有两种一种是动态调用(也叫显示的调用),一种叫静态调用(也叫隐式调用):

         动态调用:函数LoadLibrary(path),GetProcaddress(hmodule),头文件在<windows.h>,这里的path可以是dll文件的绝对路径,也可以是相对路径。绝对路径要注意windows里面用'\\'表示文件夹路径,相对路径要将dll文件放对位置,加载dll完成后有必要判断是否加载成功。导出函数要用函数指针获取,注意函数指针的声明,最后使用完后要记得释放动态库。动态加载真正体现了动态库的意义,他能有效的减小程序的代码体积,只有在用到时才调用。

        静态调用:会用到dll生成时同时生成的lib文件,这个文件只有导出函数的信息,没有代码,在应用程序编译时会将它一起编进去。将lib放到工作目录下,dll放到exe同一个目录下编译。代码中要加入预编译指令#pragma comment(lib,'hid.ilb''),并且要申明导出函数,这里你就可以直接将dll的头文件引入,不需要自己申明函数了。

         总结:这只是一个简单的讨论,并且稍微深入的研究了下,还有许多问题没有涉及,例如dll的入口函数有什么用,dll的debug版本和release有什么区别,还有延伸出去的com组件,导出变量和函数等等。


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值