一、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; }