DLL点点滴滴

系统中经常用到DLL程序,多数DLL程序工作在底层,为上层应用提供支持,开发DLL程序时应注意以下几点:
1、数据保存与交换
很多DLL程序都是动态加载的(用Loadlibrary函数),DLL中的数据随着Freelibrary也随之消亡,有时DLL中的函数是有联系的,如DLL中函数2需要DLL中函数1生成的数据,此时在执行完函数1时DLL已经Freelibrary了,另一时刻客户重新加载了DLL并且调用DLL中的函数2,那函数2怎样获取到函数1生成的数据呢?是否还需重新调用函数1呢?不用的,此时可以用windows文件的映射机制(IPC)来保存函数1生成的数据,供函数2使用,代码如下:
内存映射文件(共享内存)示例:
//写入内存映射文件
extern "C" __declspec(dllexport)  void  __cdecl WriteMap(unsigned char* buff, int len=1024)
{
char MapName[16]={0};
HANDLE hMap;
LPVOID pBuffer;
sprintf(MapName,"%s","ShareMemory");        
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, MapName);
if (hMap==NULL){
hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
                                    NULL,
                                    PAGE_READWRITE,
                                    0,
                                    len+1,
                                    MapName);
}
        pBuffer = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
        memcpy(pBuffer,buff,len);
}

//读取内存映射文件
extern "C" __declspec(dllexport) int __cdecl  ReadMap(unsigned char* buff,int len=1024)

{
char MapName[16]={0};
HANDLE hMap;
LPVOID pBuffer;
sprintf(MapName,"%s","ShareMemory");
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, MapName);
if (hMap==NULL){
return -1;
}
pBuffer = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
memcpy(buff,pBuffer,len);
UnmapViewOfFile(pBuffer);
CloseHandle(hMap);
return 0;
}
WriteMap函数负责将DLL中函数1的数据写入到内存映射文件,ReadMap函数负责读出写入的数据,数据一旦写入,只要客户的程序处于运行的状态,不管DLL已经释放还是处于加载状态,数据就一直存在(此例的内存映射文件相当于共享内存)
2、重入预防和排队
很多DLL执行的是费时通讯或设备IO操作,为安全起见DLL中的函数应防止线程重入,或进行排队,保证操作的正确性,可以使用信号量或临界区等技术对代码进行保护。
3、路径问题
有时需要在DLL中获取DLL文件的路径,此时可如下获得(win32 DLL 无MFC支持)
首先获取并保存DLL句柄
HMODULE m_hModule;  //定义m_hModule,保存DLL句柄    
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
 )
{
    m_hModule=hModule;  //保存DLL句柄
    return TRUE;
}

然后用getDLLPath函数用来获取DLL路径:
void getDLLPath(HANDLE hModule, char* DLLPath) {
char DLLPathTemp[1024] = { 0 };
GetModuleFileName((HMODULE)hModule, DLLPathTemp, 1024);
strncpy(
DLLPath, DLLPathTemp, strrchr(DLLPathTemp, '\\') - DLLPathTemp + 1);  //取出文件的路径
}

这样做的好处是无论DLL改成什么名字,都能动态的获取到DLL的路径。
如果生成DLL时使用了MFC,可以用如下的方法获取DLL的路径,以下是程序片段
// 唯一的一个CMemoryMapDllApp 对象
CMemoryMapDllApp theApp;
// CMemoryMapDllApp 初始化
BOOL CMemoryMapDllApp::InitInstance()
{
CWinApp::InitInstance();
char disp[128]={0};
char DLLPathTemp[1024] = { 0 };
sprintf(disp,"%s.dll",theApp.m_pszAppName);
GetModuleFileName(GetModuleHandle(disp), DLLPathTemp, 1024);  //DLLPathTemp中保存了DLL的路径
//MessageBox(NULL,DLLPathTemp,"dll path",0);
return TRUE;
}
4、文件的权限
有时我们的DLL程序需安装在嵌入式wince或xp等嵌入式终端设备上,这些系统对某些目录设定了读写权限,这时我们的DLL对文件操作时就应该注意了,以防止操作错误导致程序运行崩溃。
5、导出函数和_cdecl _stdcall
_stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈。WIN32 Api都采用_stdcall调用方式。_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。_cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,这样带来了一个的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。 到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall。6、链接问题
DLL程序在编译时要尽量的把所有依赖库都一次性链入程序中,尤其是MFC的DLL要注意编译时选择“在静态库中使用MFC”,以免DLL在发行后在不同的设备上可能出现的错误。
7、Dll打包
有时我们在DLL中动态加载了另外的DLL,这时可以考虑将另外的DLL作为资源打包到我们的DLL中,使用时动态的释放出DLL,代码如下:
HMODULE hThis = hModuleDll;  //DLL句柄
HRSRC   hRes = FindResource(hThis, MAKEINTRESOURCE(IDR_DLL1), "DLL");
HGLOBAL hGres = LoadResource(hThis, hRes);
HANDLE  hFile = CreateFile("TesoLive.dll", GENERIC_WRITE, NULL, NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, NULL);
PVOID   pRes = LockResource(hGres);
DWORD   dwSize = SizeofResource(hThis, hRes);
DWORD dwSizeWritten = 0;
WriteFile(hFile, pRes, dwSize, &dwSizeWritten,
NULL);
CloseHandle(hFile);
下面就可以加载“
TesoLive.dll了。
8、DLL开发工具选择
如果我们的DLL需要用在java或python等环境中,不要选择版本太高的vs,如vs2105环境下的DLL在jdk1.6或python2上就有兼容的问题,现在很多的企业应用还仍然在jdk1.6或python2这样的平台上,所以目前开发DLL使用vs2005是比较合适的,做DLL不能太超前。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝知此事要躬行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值