动态库相关知识解析
目录
1 Windows平台下查看动态库工具
2 Linux平台下查看动态库命令
3 Windows平台下动态库的静态加载
4 Windows平台下动态库的动态加载
5 Linux平台下动态库的动态加载
6 在Win32 Dll中创建modal对话框
7 在Win32 Dll 中创建modeless对话框
8 在MFC Regular Dll中创建modal对话框
9 动态库中导出头文件示例
1 Windows平台下查看动态库工具
32位工具:depends.EXE
64位工具:depends_x64.exe
2 Linux平台下查看动态库命令
nm xxx.so
3 Windows平台下动态库的静态加载
#include “UsbDriver.h”
#pragma comment(lib," UsbDriver.lib")
4 Windows平台下动态库的动态加载
(1)加载动态库。使用函数LoadLibrary()或函数LoadLibraryEx()来打开动态链接库,返回一个动态库的句柄。例如:
#include <windows.h>
HMODULE nDllHandle = LoadLibrary(“ex_3_2.dll”);
被加载动态库的搜索途径、优先顺序和加载应用程序是一致的。
(2)获取要调用函数的函数指针。使用函数GetProcAddress()可以获得给定名称函数的指针。例如:
typedef int (*CHECK_ARGS_FUNC)(int argc, char *argv[]);
CHECK_ARGS_FUNC localFunc;
localFunc = (CHECK_ARGS_FUNC)GetProcAddress(nDllHandle,”CheckArgs”);
例子里的第1行定义了一个类型CHECK_ARGS_FUNC,是指向函数的指针。第3行用函数GetProcAddress()取出了动态库中名为CheckArgs的函数的函数指针并赋值到变量localFunc中,通过调用localFunc就可以调用对应的函数了。 Windows的动态库还有一种机制,动态库可以无名称,即编译导出时使用了NONAME限制,如果要想获取此函数调用地址,则只能使用序号来获取了。调用时,用序号来表示函数名,并把此整数的类型强行指定为LPCSTR。例如;
localFunc = (CHECK_ARGS_FUNC) GetProcAddress(nDllHandle,(LPCSTR)101);
例子里,要获取序号为101的函数的调用地址。根据SDK文档里的描叙,序号是一个短整数,参数的高16bit必须为0。
(3)调用函数。用函数指针(参数列表)的方式就可以调用。例如:
//假定已经定义int localArgC; char * localArgV[];
int rc = localFunc(localArgC, localArgV);
这样,就等于调用了动态库中的函数CheckArgs()。如果在此处设断点,单步调试时,会进入函数CheckArgs()的实现源代码。
(4)关闭动态加载的模块。当确定目前不需要此动态库时,可以关闭此动态库的加载句柄。调用函数FreeLibrary()或函数FreeLibraryEx()完成此项操作。例如:
FreeLibrary(nDllHandle);
备注:
根据SDK文档里的描述,不要在动态库的入口函数DllMain()里加载其他动态库。
动态加载一个动态库需要消耗一定的CPU时间和I/O读写。被频繁调用的函数,最好不要每次都调用前加载而调用后就卸载,避免太多的系统开销。
动态加载也适合于变量的动态存取,但对变量的动态存取时,用动态调用函数,且在此函数里进行读写比较适宜。
5 Linux平台下动态库的动态加载
(1)加载动态库。使用函数dlopen()来打开动态链接库,返回一个动态库的句柄。例如:
#include <dlfcn.h>
void *nDllHandle = dlopen(“libex_3_2.so”, RTLD_LAZY); //RTLD_NOW
如果没有指明动态库的绝对或相对路径,那么,被加载的动态库应该在环境变量LD_LIBRARY_PATH的路径下,或系统配置的默认加载路径下。
(2)获取要调用函数的函数指针。使用函数dlsym()可以获得给定名称函数的指针。例如:
typedef int (*CHECK_ARGS_FUNC)(int argc, char *argv[]);
CHECK_ARGS_FUNC localFunc;
localFunc = (CHECK_ARGS_FUNC)dlsym(nDllHandle,”CheckArgs”);
例子里的第1行定义了一个类型CHECK_ARGS_FUNC,是指向函数的指针。第3行用函数dlsym()取出了动态库中名为CheckArgs函数的函数指针并赋值到变量localFunc中,通过调用localFunc就可以调用对应的函数了。
(3)调用函数。用函数指针(参数列表)的方式就可以调用。例如:
//假定已经定义int
localArgC; char * localArgV[];
int rc = localFunc(localArgC, localArgV);
这样,就等于调用了动态库中的函数CheckArgs()。如果在此处设断点,单步调试时,会进入函数CheckArgs()的实现源代码。
(4)关闭动态加载的模块。当确定目前不需要此动态库时,可以关闭此动态库的加载句柄。调用函数dlclose()完成此项操作。例如:
dlclose(nDllHandle);
备注:
编译时,对应的头文件是dlfcn.h,要连接的库名是dl,即要添加连接开关 –ldl。
动态加载一个动态库需要消耗一定的CPU时间和I/O读写。被频繁调用的函数,最好不要每次都调用前加载而调用后就卸载,避免太多的系统开销。
动态加载也适合于变量的动态存取,但对变量的动态存取时,用动态调用函数,且在此函数里进行读写比较适宜。
void *dlopen(const char *filename, int flag);
其中flag有:RTLD_LAZY RTLD_NOW RTLD_GLOBAL,其含义分别为:
RTLD_LAZY:在dlopen返回前,对于动态库中存在的未定义的变量(如外部变量extern,也可以是函数)不执行解析,就是不解析这个变量的地址。
RTLD_NOW:与上面不同,他需要在dlopen返回前,解析出每个未定义变量的地址,如果解析不出来,在dlopen会返回NULL,错误为:undefined symbol: xxxx.......
RTLD_GLOBAL:它的含义是使得库中的解析的定义变量在随后的随后其它的链接库中变得可以使用。
6 在Win32 Dll中创建modal对话框
Win32函数DialogBox能够创建一个modal对话框,并且modal对话框的消息是独立于主程序消息循环的,所以可以直接在Dll内部建立对话框自己的窗口过程。
DialogBox函数根据对话框模板资源创建一个模态的对话框。DialogBox函数直到指定的回调函数通过调用EndDialog函数中止模态的对话框才能返回控制。该宏使用函数。
DialogBoxParam函数根据对话框模板资源创建一个模态的对话框。在显示对话框之前,函数把一个应用程序定义的值作为WM_INITDIALOG消息的IParam参数传到对话框过程,应用程序可用此值来初始化对话框控制。
(1)创建Win32 Dll工程:File->New->Projects->Win32 Dynamic-Link Library->A simple DLL project
(2)添加对话框资源:File->New->Files->Resource Script->右键->Insert->Dialog
(3)添加def文件,定义导出文件名和接口名:File->New->Files->Text File->dll.def
LIBRARY "Dll.DLL" ;导出文件名
EXPORTS
DllShowModalDialog ;导出接口名
(4)添加实现代码:
HANDLE g_hModule;
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
PVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
g_hModule = hModule;
}
return TRUE;
}
#include<windows.h>
#include"resource.h"
//对话框参数
typedef struct tagDlgParam
{
HWND hWndDlg;
int a;
int b;
int result;
}DlgParam;
//处理线程
DWORD WINAPI GetSumThread(LPVOID lpParam)
{
DlgParam* pParam = (DlgParam*)lpParam;
HWND hWndDlg = pParam->hWndDlg;
int a = pParam->a;
int b = pParam->b;
int sum = a+b;
pParam->result = sum;
Sleep(5000);
::EndDialog(hWndDlg, sum);
return 0;
}
//消息循环
BOOL CALLBACK DlgProc(HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
switch(Msg)
{
case WM_INITDIALOG:
{
::SetWindowText(hwnd,"模态对话框示例");
::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,SWP_NOSIZE|SWP_NOMOVE);//对话框始终在最上层 DlgParam*
pParam = (DlgParam*)lParam;
pParam->hWndDlg = hwnd;
CloseHandle(CreateThread(NULL,0,GetSumThread,pParam, 0, NULL));
return TRUE;
}
case WM_COMMAND:
{
switch(wParam)
{
case IDOK:
EndDialog(hwnd, 0);
return TRUE;
case IDCANCEL:
EndDialog(hwnd, -1);
return TRUE;
}
break;
}
}
return FALSE;
}
//接口函数
int DllShowModalDialog(int a,int b)
{
int ret = 0;
//MessageBox(NULL, "模态对话框", "提示信息", MB_OK);
HINSTANCE hinst;
//hinst=LoadLibrary("Dll.dll");
//获取库所在的实例句柄
hinst = (HINSTANCE)g_hModule; //关联实例句柄
DlgParam param;
param.hWndDlg = NULL;
param.a = a;
param.b = b;
param.result = 0;
HWND hParentWnd = GetForegroundWindow();
//DialogBox(hinst,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc);
ret = DialogBoxParam(hinst,MAKEINTRESOURCE(IDD_DIALOG1),hParentWnd,DlgProc,(LPARAM)¶m);
//DialogBoxParam第3个参数设置为NULL,实现类似非模态对话框效果,原因不明?
//ret = DialogBoxParam(hinst,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc,(LPARAM)¶m);
//FreeLibrary(hinst);
return ret;
}
7 在Win32 Dll 中创建modeless对话框
与modal对话框不同,modeless对话框的消息是要经过主程序消息循环的,这样一来,就必须在调用该Dll的主程序中处理对话框的消息循环,这就无法达到利用Dll来完成组件化开发的要求。其实我们可以人为的Modeless对话框创建一个窗口类,我们知道窗口过程是属于窗口类的,这样一来就有了主窗口的消息循环。
Win32函数CreateDialog宏从一个对话框模板资源创建一个无模式的对话框。
CreateDialog函数用CreateWindowEx函数来创建对话框。然后CreateDialog函数把一个WM_INITDIALOG消息(如果模板指定DS_SETFONT类型,则加上一个WM_SETFONT消息)传送到对话框应用程序。如果模板指定WS_VISIBLE风格,则函数显示对话框,最后CreateDlalog返回指向对话框的窗口句柄。CreateDialog函数返回之后,应用程序通过Showwindow函数显示对话框(如果还没有显示)。应用程序通过利用DestroyWindow函数来清除对话框。
CreateDialogParam函数根据对话框模板资源创建一个无模式的对话框。在显示对话框之前,函数把一个应用程序定义的值作为WM_INITDIALOG消息IParam参数传到对话框过程应用程序可用此值来初始化对话框控制。
(1)创建Win32 Dll工程:File->New->Projects->Win32 Dynamic-Link Library->A simple DLL project
(2)添加对话框资源:File->New->Files->Resource Script->右键->Insert->Dialog
(3)添加def文件,定义导出文件名和接口名:File->New->Files->Text File->dll.def
LIBRARY "Dll.DLL" ;导出文件名
EXPORTS
DllShowModelessDialog ;导出接口名
(4)添加实现代码:
HANDLE g_hModule;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
g_hModule = hModule;
}
return TRUE;
}
#include<windows.h>
#include"resource.h"
//对话框参数
typedef struct tagDlgParam
{
HWND hWndDlg;
int a;
int b;
int result;
}DlgParam;
//全局变量
#pragma data_seg("Shared")
int g_iInstance = 0;
int g_iResult = 0;
#pragma data_seg()
#pragma
comment(linker,"/section:Shared,RWS")
//处理线程
DWORD WINAPI GetAveThread(LPVOID lpParam)
{
DlgParam* pParam = (DlgParam*)lpParam;
HWND hWndDlg = pParam->hWndDlg;
int a = pParam->a;
int b = pParam->b;
int ave = (a+b)/2;
pParam->result = ave;
Sleep(5000);
g_iResult = 0;
PostMessage(hWndDlg,WM_COMMAND, IDOK, ave);
return 0;
}
//对话框的消息循环
BOOL CALLBACK ModelessDlgProc(HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG :
{
::SetWindowText(hDlg, "非模态对话框示例");
::SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE);//对话框始终在最上层
DlgParam* pParam = (DlgParam*)lParam;
pParam->hWndDlg = hDlg;
CloseHandle(CreateThread(NULL,0,GetAveThread,pParam, 0, NULL));
return TRUE;
}
case WM_COMMAND:
{
switch(wParam)
{
case(IDOK):
DestroyWindow(hDlg);
PostQuitMessage(0) ;
return TRUE;
case(IDCANCEL):
DestroyWindow(hDlg);
PostQuitMessage(-1) ;
return TRUE;
}
}
}
return FALSE;
}
//主窗口的消息循环
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0) ;
return 0 ;
}
return DefWindowProc(hwnd, message, wParam, lParam) ;
}
//接口函数
int DllShowModelessDialog(int a,int b)
{
int ret = 0;
//MessageBox(NULL, "非模态对话框", "提示信息", MB_OK);
DlgParam param;
param.hWndDlg = NULL;
param.a = a;
param.b = b;
param.result = 0;
g_iResult = -1;
if(g_iInstance == 0) //保证调用一次
{
g_iInstance = 1;
}
else
{
return -10;
}
static TCHAR szAppName[] = TEXT ("Modeless_Dialog") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
HWND hDialog;
HINSTANCE hInstance;
//hInstance = LoadLibrary("Dll.dll");//获取库所在的实例句柄
hInstance = (HINSTANCE)g_hModule; //关联实例句柄
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = DLGWINDOWEXTRA ; // Note!
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (hInstance, szAppName) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
hwnd = CreateWindow (szAppName, TEXT("Modeless_Dialog"),
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
//
ShowWindow (hwnd, iCmdShow) ;
//并不在这里显示主窗口
// UpdateWindow (hwnd) ;
hDialog = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ModelessDlgProc, (LPARAM)¶m) ;
if(hDialog == NULL)
{
// FreeLibrary(hInstance);
g_iInstance = 0;
MessageBox(NULL, "创建对话框失败.", "", MB_OK);
return -11;
}
ShowWindow(hDialog,SW_SHOW);
while(GetMessage (&msg, NULL, 0, 0))
{
if(hDialog==0||!IsDialogMessage(hDialog,&msg))
{
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
}
//FreeLibrary(hInstance);
g_iInstance = 0;
ret = g_iResult;
if (ret == 0)
{
ret = param.result;
return ret;
}
return ret;
}
8 在MFC Regular Dll中创建modal对话框
在MFC Regular Dll中创建modal对话框非常简单,Cdialog基类提供有一个方法DoModal(),能够创建一个modal对话框,因此在MFC Regular Dll中创建modal对话框,仅仅需要自己继承对话框基类,在导出函数中调用DoModal就可以了.
(1)创建MFC Regular Dll工程:File->New->Projects->MFC AppWizard(dll)->Regular DLL using shared MFC DLL
(2)添加对话框资源:ResourceView->右键->Insert->Dialog,生成Dialog类CTestDlg
(3)修改def文件,定义导出文件名和接口名
LIBRARY "Dll.DLL" ;导出文件名
EXPORTS
DllShowModalDialog ;导出接口名
(4)添加实现代码:
#include "TestDlg.h"
int DllShowModalDialog(int a,int b)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());//模块状态切换
CTestDlg dlg;
dlg.DoModal();
return 0;
}
9 动态库中导出头文件示例
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef FINGERINTERFACE_EXPORTS
#define FINGERINTERFACE_API __declspec(dllexport)
#else
#define FINGERINTERFACE_API __declspec(dllimport)
#endif
#if defined(_WIN32) || defined(_WIN64)
#ifndef WINAPI
#define WINAPI __stdcall
#endif
#else
#define WINAPI
#endif
/****************************************************************************
功 能:获取算法版本号
参 数:version - 长度为100字节的字符串指针
返 回: 0 - 成功
其他 - 失败
*****************************************************************************/
FINGERINTERFACE_API int WINAPI mxGetVersion(char* version);
#ifdef __cplusplus
}
#endif