动态链接库
程序员文章站
2022-06-25 18:44:30
...
一、DLL种类
不使用MFC的win32 DLL和使用MFC的MFC DLLMFC DLL三种开发方式
1.使用静态链接MFC类库的常规DLL
2.使用动态链接MFC类库的常规DLL
3.MFC扩展DLL。现有MFC类库中的类派生而来的可以重复使用的类。扩展DLL使用MFC的动态链接库版本构建
二、DLL文件组成
DEF文件模块定义语句1.文件的第一条语句必须是LIBRARY语句。语句后写入DLL名称。
2.文件中的EXPORTS语句用于列出导出的函数名称和为其分配序号值。格式是函数名称后写入@符号和序号值
3.DESCRIPTION语句描述DLL的功能
4.所有以分号开头的行都是注释行
;MFCDLL1.def:Declares the module parameters for the DLL.
LIBRARY "MFCDLL1"
DESCRIPTION "MFCDLL1 windows Dynamic Link Library"
EXPORTS
Writelog @1
三.函数介绍
1.DLLMain 入口函数
BOOL WINAPI DLLMain(HINSTANCE hinstDll,//Dll模块的句柄
DWORD fdwReason,//调用函数的来源
LPVOID lpvReserved//预留
);
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
//根据调用DLL的来源完成相应的工作
case DLL_PROCESS_ATTACH:
//对于每个新进程,初始化一次,如果DLL装载失败,则返回false
case DLL_THREAD_ATTACH:
//执行线程指定初始化
case DLL_THREAD_DETACH:
//执行线程指定的清除工作
case DLL_PROCESS_DETACH
//执行任何需要的清除工作
break;
}
return true;//进程装载入口函数成功完成
}
2.LoadLibrary
DLL映射到进程地址空间HMODULE LoadLibrary(PCTSTR pszDLLPathName);
3.LoadLibraryEx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspxHMODULE LoadLibraryEx(PCTSTR pszDLLPathName,HANDLE hFile,DWORD dwFlags);
4.FreeLibrary
将DLL从进程地址空间中卸载BOOL FreeLibrary( HMODULE hInstDll );
5.FreeLibraryAndExitThread
从进程地址空间卸载某DLL的功能,还能退出调用线程VOID FreeLibraryAndExitThread(HMODULE hInstDll,DWORD dwExitCode)
6.GetModuleFileName
返回执行文件包含的指定模块的完整路径和文件名DWORD GetModuleFileName(
HMODULE hModule,//要获取文件名的模块句柄
LPTSTR lpFilename,//接收模块路径的缓冲区的指针
DWORD nSize//缓冲区的大小
);
7.GetModuleHandle
返回模块的模块句柄HMODULE GetModuleHandle(
LPCTSTR lpModuleName)//要获取句柄的模块的名称地址
8.GetProcAddress
返回导出的DLL函数的地址FARPROC GetProcAddress(
HMODULE hModule,//DLL模块的句柄
LPCSTR lpProcName);//函数名
四.从动态库中获取位图资源
LoadLibraryEx() 函数加载动态库,EnumResourceNames函数枚举指定类型的资源。
HINSTANCE hModule,//指定要枚举资源的可执行文件的模块句柄
LPCTSTR lpszType,//指定要枚举的资源类型
ENUMRESNAMEPROC lpEnumFunc,//指定查找到每个资源后都要执行的回调函数
LONG lParam);//传递给回调函数的用户自定义参数值
回调函数
BOOL CALLBACK EnumResNameProc(
HANDLE hModule,//枚举函数 正在枚举的资源所在的可执行文件的句柄
LPCTSTR lpszType,//正在枚举创建当前进程的模块的资源
LPTSTR lpszName,//当前枚举项的资源名称
LONG lParam);//EnumResourceNames函数LParam参数传进来的用户自定义参数值
DLLAppSampleDlg.cpp:
// 枚举位图
void CDLLAppSampleDlg::OnButtonGetbitmap()
{
m_iconList.ShowWindow(SW_HIDE);
if( (hLibrary = LoadLibraryEx( "MORICONS.DLL", NULL, LOAD_LIBRARY_AS_DATAFILE )) == NULL )
{
WriteLog("文件载入错误!");
return;
}
if(!EnumResourceNames(hLibrary,RT_BITMAP,(ENUMRESNAMEPROC)EnumBitmapProcedure,(LPARAM)GetSafeHwnd()))
WriteLog("列举位图资源停止!");
FreeLibrary(hLibrary);
}
//显示位图
LRESULT CDLLAppSampleDlg::OnBitmapMessage(WPARAM wParam,LPARAM lParam)
{
CStatic* m_Bitmap = (CStatic*)GetDlgItem(IDC_STATIC_BITMAP);
m_Bitmap->SetBitmap((HBITMAP)wParam);
WriteLog((const char*)&lParam);
return 1;
}
DLLAppSample.cpp:
BOOL CALLBACK EnumBitmapProcedure(HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG lParam)
{
HBITMAP bitmap = LoadBitmap((HINSTANCE)hModule,lpszName);
if (true)
{
SendMessage((HWND)lParam, WM_BITMAP_MESSAGE, (LPARAM)bitmap, (WPARAM)lpszName);
return FALSE;
}
}
五.枚举模块中的所有图标
DLLAppSampleDlg.cpp:// 枚举图标
void CDLLAppSampleDlg::OnButtonGetallicon()
{
ResetContent();
HINSTANCE hLibrary;
if( (hLibrary = LoadLibraryEx( "MORICONS.DLL", NULL, LOAD_LIBRARY_AS_DATAFILE )) == NULL )
{
WriteLog("文件载入错误!");
return;
}
if(!EnumResourceNames(hLibrary,RT_GROUP_ICON,(ENUMRESNAMEPROC)EnumIconProcedure,(LPARAM)GetSafeHwnd()))
WriteLog("列举图标资源停止!");
FreeLibrary(hLibrary);
CString log;
log.Format("DLL*包含%d个图标资源!", m_iconList.GetItemCount());
WriteLog(log);
}
// 显示图标
LRESULT CDLLAppSampleDlg::OnIconMessage(WPARAM wParam,LPARAM lParam)
{
int iIconRet = imagelist.Add((HICON)wParam);
if (iIconRet!=-1)
{
m_iconList.SetImageList (&imagelist,LVSIL_SMALL);
int iIndex = m_iconList.GetItemCount();
m_iconList.InsertItem(iIndex, (const char*)&lParam, iIndex);
}
return 1;
}
DLLAppSample.cpp:
BOOL CALLBACK EnumIconProcedure(HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG lParam)
{
HICON icon = LoadIcon((HINSTANCE)hModule,lpszName);
SendMessage((HWND)lParam, WM_ICON_MESSAGE, (LPARAM)icon, (WPARAM)lpszName);
return TRUE;
}
六.使用模块对话框资源
AfxSetResourceHandle函数可以切换要使用的资源所在的实例,从而实现调用其他模块中的对话框资源的功能AfxSetResourceHandle(
HINSTANCE hInstResource);//指定应用程序要载入的资源所在的EXE或DLL文件的模块句柄或实例
AfxGetResourceHandle函数保存原来的实例句柄。
//使用模块对话框资源
void CDLLAppSampleDlg::OnButtonGetdialog()
{
HINSTANCE m_hInstOld=AfxGetInstanceHandle();
HINSTANCE m_hInstNew = LoadLibrary("RedDLL.exe");
if (m_hInstNew == NULL) WriteLog("装载可执行文件失败!");
AfxSetResourceHandle(m_hInstNew);
CDialog* dlg = new CDialog();
if (dlg->Create(IDD_ABOUTBOX)) dlg->ShowWindow(SW_SHOW);
AfxSetResourceHandle(m_hInstOld);
}
七.替换应用程序的对话框资源
1.使用LoadLibrary函数装载替换内容的可执行文件2.使用FindResource函数和LoadResource函数查找定位并装载用于替换对话框资源
3.调用LockResource函数获取对话框资源的数据指针
4.使用BeginUpdateResource函数打开要更新的资源
5.使用UpdateResource函数将用于替换的对话框资源复制到要替换的对话框资源
6.使用EndUpadteResource函数完成替换
void CDLLAppSampleDlg::OnButtonReplacedialog()
{
HRSRC hRes, hResLoad;
HANDLE hExe, hUpdateExe;
char *lpResLock;
BOOL bResult;
hExe = LoadLibrary("RedDLL.exe");
if (hExe == NULL) WriteLog("装载可执行文件失败!");
hRes = FindResource((HINSTANCE)hExe, MAKEINTRESOURCE(IDD_ABOUTBOX), RT_DIALOG);
if (hRes == NULL) WriteLog("无法查找要替换的资源!");
hResLoad = (HRSRC)LoadResource((HINSTANCE)hExe, hRes);
if (hResLoad == NULL) WriteLog("无法装载对话框!");
lpResLock = (char*)LockResource(hResLoad);
if (lpResLock == NULL) WriteLog("无法锁定对话框!");
hUpdateExe = (HRSRC)BeginUpdateResource("GreenDLL.exe", FALSE);
if (hUpdateExe == NULL) WriteLog("无法打开要写入资源的文件!");
bResult = UpdateResource((HINSTANCE)hUpdateExe, RT_DIALOG, MAKEINTRESOURCE(IDD_ABOUTBOX),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), lpResLock,
SizeofResource((HINSTANCE)hExe, hRes));
if (bResult == FALSE) WriteLog("替换资源失败!");
if (!EndUpdateResource(hUpdateExe, FALSE)) WriteLog("不能写入对文件的修改!");
if (!FreeLibrary((HINSTANCE)hExe)) WriteLog("释放文件失败!");
}
八.屏蔽键盘Power键
通过回调函数,可以屏蔽系统按键的处理。通过底层键盘钩子的回调函数来屏蔽POWER键的方法。由于POWER键是底层键盘按键,因此需要使用底层键盘钩子回调函数
LRESULT CALLBACK LowLevelKeyboardProc(
int nCode,//指定钩子处理代码
WPARAM wParam,//指定键盘信息的标志符
LPARAM lParam);//定向KBDKKHOOKSTRUCT结构的指针,其中存储按下的按键的信息
Power键的按键代码是1,因此将其传入DLL中的StartShieldKey()函数后可以屏蔽Power按键。
//屏蔽键盘POWER键
void CDLLAppSampleDlg::OnButtonDisablePower()
{
DWORD dwVerKey[] = {0x00000001};
DWORD dwConKey[] = {0};
int nLength = sizeof(dwVerKey) / sizeof(DWORD);
if (StartShieldKey(dwVerKey, dwConKey, nLength)) WriteLog("已经屏蔽POWER键");
else WriteLog("屏蔽POWER键失败");
}
ShieldKeyBordSample.dll:
// 底层键盘钩子回调函数
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION) // 禁用键盘的某个按键
{
KBDLLHOOKSTRUCT* pHookStruct = (KBDLLHOOKSTRUCT*)lParam; // 取出钩子结构
LPDWORD tmpVirKey = m_lpdwVirtualKey; // 取出要屏蔽的键的列表指针
for (int i = 0; i < m_nLength; i++)
{
if (*m_lpdwContentKey++ == 1)
{
DWORD dwAltKey = 32;
if ((pHookStruct->vkCode == *tmpVirKey++)
&& (pHookStruct->flags == dwAltKey)) return TRUE;
}
if (pHookStruct->vkCode == *tmpVirKey++) return TRUE;
}
}
return CallNextHookEx(m_hHook, nCode, wParam, lParam); // 如果不是要屏蔽的按键则传给系统中的下一个钩子
}
// 启动屏蔽
SHIELDKEYBORDSAMPLE_API bool StartShieldKey(LPDWORD lpdwVirtualKey, LPDWORD lpdwContentKey, int nLength)
{
if (m_hHook != NULL) return StopShieldKey();
m_lpdwVirtualKey = (LPDWORD)malloc(sizeof(DWORD) * nLength);
m_lpdwContentKey = (LPDWORD)malloc(sizeof(DWORD) * nLength);
LPDWORD tmpVirKey = m_lpdwVirtualKey;// 要屏蔽的底层键盘;
LPDWORD tmpConKey = m_lpdwContentKey;// 要屏蔽的底层键盘组合键;
for (int i = 0; i < nLength; i++)
{
*tmpVirKey++ = *lpdwVirtualKey++;
*tmpConKey++ = *lpdwContentKey++;
}
m_nLength = nLength;
// 安装底层键盘钩子
m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, m_hInstance, NULL);
if (m_hHook == NULL) return FALSE;
return TRUE;
}
// 停止屏蔽
SHIELDKEYBORDSAMPLE_API bool StopShieldKey()
{
if (UnhookWindowsHookEx(m_hHook) == 0) return FALSE;
m_hHook = NULL;
return TRUE;
}