Windows核心编程系列文章仅作为实验报告和Windows编程学习参考,不作为任何技术文章,还望大佬们勿喷。

1. 实验名称

DLL编程

2. 实验环境

  1. VS2015
  2. Windows 10

3. 实验目的

  1. 掌握动态链接的两种不同方法,以及动态链接的不同加载方式基本原理
  2. 采用api函数加载动态链接,LoadLibrary(),以及FreeLibrary(),GetProcAddress()函数的加载动态链接方式
  3. 采用MFC和SDK方式编写动态链接,了解动态链接入口函数DllMain
  4. 了解Hook编程基本概念,原理。采用动态链接中Hook编程

4. 实验内容、步骤及结果

1. 实验内容

  1. 编写一个win32动态链接库,实现两个数简单加法和减法,乘法,乘法,然后采用显式和隐式两种方式调用。调用界面采用MFC或者wind32窗口程序实现。
  2. 编写一个钩子程序,实现鼠标钩子和键盘钩子。当测试程序运行时,对话框窗口放到最大,鼠标不可用。只有在发生键盘F2按键按下事件发生时,则对话框测试应用程序关闭。鼠标重新可用。

2. 实验步骤

  1. 编写一个win32动态链接库,实现两个数简单加法和减法,乘法,乘法,然后采用显式和隐式两种方式调用。
  2. (1). 文件→新建→项目→win32项目,创建ArithmeticDll,应用程序类型选择DLL。

【实验五】DLL编程 - 图1

(2). 新建头文件ArithmeticDll.h
ArithmeticDll.h代码如下:

  1. #pragma once
  2. #ifndef ArithmeticDll_H_
  3. #define ArithmeticDll_H_
  4. #ifdef MYLIBDLL
  5. #define MYLIBDLL Arithmetic "C" _declspec(dllimport)
  6. #else
  7. #define MYLIBDLL extern "C" _declspec(dllexport)
  8. #endif
  9. MYLIBDLL int Add(int plus1, int plus2);
  10. MYLIBDLL int Sub(int sub1, int sub2);
  11. MYLIBDLL int Mul(int mul1, int mul2);
  12. MYLIBDLL int Div(int div1, int div2);
  13. //You can also write like this:
  14. //extern "C" {
  15. //_declspec(dllexport) int Add(int plus1, int plus2);
  16. //};
  17. #endif

(3). 源文件ArithmeticDLL.cpp的代码如下:

  1. // ArithmeticDll.cpp : 定义 DLL 应用程序的导出函数。
  2. //
  3. #include "stdafx.h"
  4. #include <iostream>
  5. #include "ArithmeticDll.h"
  6. using namespace std;
  7. int Add(int plus1, int plus2)
  8. {
  9. int add_result = plus1 + plus2;
  10. return add_result;
  11. }
  12. int Sub(int sub1, int sub2)
  13. {
  14. int sub_result = sub1 - sub2;
  15. return sub_result;
  16. }
  17. int Mul(int mul1, int mul2)
  18. {
  19. int mul_result = mul1*mul2;
  20. return mul_result;
  21. }
  22. int Div(int div1, int div2)
  23. {
  24. if (div2 != 0)
  25. {
  26. int div_result = div1 / div2;
  27. return div_result;
  28. }
  29. }

(4). 新建模块定义文件Arithmeticdef.def

  1. LIBRARY "ArithmeticDll"
  2. EXPORTS
  3. Add @1
  4. Sub @2
  5. Mul @3
  6. Div @4

(5). 在vs2015自动创建的dllmain.cpp代码如下:

  1. // dllmain.cpp : 定义 DLL 应用程序的入口点。
  2. #include "stdafx.h"
  3. BOOL APIENTRY DllMain( HMODULE hModule,
  4. DWORD ul_reason_for_call,
  5. LPVOID lpReserved
  6. )
  7. {
  8. switch (ul_reason_for_call)
  9. {
  10. case DLL_PROCESS_ATTACH:
  11. case DLL_THREAD_ATTACH:
  12. case DLL_THREAD_DETACH:
  13. case DLL_PROCESS_DETACH:
  14. break;
  15. }
  16. return TRUE;
  17. }

(6) . 最后,编译生成ArithmeticDLL.dll文件和ArithmeticDLL.lib文件

【实验五】DLL编程 - 图2

(7). 然后采用win32 或者MFC 程序中,调用动态链接库,显式和隐式两种方式调用,完成调用。

隐式加载动态链接库

  1. // experience5.cpp : 定义控制台应用程序的入口点。
  2. #include "stdafx.h"
  3. #include "ArithmeticDll.h"
  4. #pragma comment (lib,"ArithmeticDLL")
  5. int main(int argc, _TCHAR* argv[])
  6. {
  7. printf("隐式加载动态链接库 Add: %d", Add(5, 10));
  8. getchar();
  9. return 0;
  10. }

显式加载动态链接库

  1. // experience5.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include "ArithmeticDll.h"
  5. #include <windows.h>
  6. //#pragma comment (lib,"ArithmeticDLL")
  7. int main(int argc, _TCHAR* argv[])
  8. {
  9. //printf("隐式加载动态链接库 Add: %d", Add(5, 10));
  10. HINSTANCE hInst;
  11. hInst = LoadLibrary(_T("ArithmeticDll.dll "));
  12. typedef int(*ADDPROC)(int sub1, int sub2);
  13. ADDPROC Sub = (ADDPROC)GetProcAddress(hInst, "Sub");
  14. printf("显示调用动态链接库减函数 Sub:%d", Sub(5, 10));
  15. getchar();
  16. return 0;
  17. }
  1. 编写一个钩子程序,实现鼠标钩子和键盘钩子。当测试程序运行时,对话框窗口放到最大,鼠标不可用。只有在发生键盘F2按键按下事件发生时,则对话框测试应用程序关闭。鼠标重新可用。
  1. ////////////////////////////////////////////////
  2. // KeyHookApp.cpp文件
  3. #include "resource.h"
  4. #include "KeyHookApp.h"
  5. #include "KeyHookLib.h"
  6. #pragma comment(lib, "09KeyHookLib")
  7. CMyApp theApp;
  8. BOOL CMyApp::InitInstance()
  9. {
  10. CMainDialog dlg;
  11. m_pMainWnd = &dlg;
  12. dlg.DoModal();
  13. return FALSE;
  14. }
  15. CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_MAIN, pParentWnd)
  16. {
  17. }
  18. BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
  19. ON_MESSAGE(HM_KEY, OnHookKey)
  20. END_MESSAGE_MAP()
  21. BOOL CMainDialog::OnInitDialog()
  22. {
  23. CDialog::OnInitDialog();
  24. SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);
  25. ::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0,
  26. 0, 0, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOMOVE);
  27. if(!SetKeyHook(TRUE, 0, m_hWnd)) // 安装钩子
  28. MessageBox("安装钩子失败!");
  29. return TRUE;
  30. }
  31. void CMainDialog::OnCancel()
  32. {
  33. SetKeyHook(FALSE); // 卸载钩子
  34. CDialog::OnCancel();
  35. return;
  36. }
  37. long CMainDialog::OnHookKey(WPARAM wParam, LPARAM lParam)
  38. {
  39. // 此时参数wParam为用户按键的虚拟键码,
  40. // lParam参数包含按键的重复次数、扫描码、前一个按键状态等信息
  41. char szKey[80];
  42. ::GetKeyNameText(lParam, szKey, 80);
  43. CString strItem;
  44. strItem.Format(" 用户按键:%s \r\n", szKey);
  45. CString strEdit; // 添加到编辑框中
  46. GetDlgItem(IDC_KEYMSG)->GetWindowText(strEdit);
  47. GetDlgItem(IDC_KEYMSG)->SetWindowText(strItem + strEdit);
  48. ::MessageBeep(MB_OK);
  49. return 0;
  50. }

3. 实验结果(实验运行截图)

  1. 编写一个win32动态链接库,实现两个数简单加法和减法,乘法,乘法,然后采用显式和隐式两种方式调用。
  2. 隐式加载动态链接库

【实验五】DLL编程 - 图3

显式加载动态链接库

【实验五】DLL编程 - 图4

  1. 编写一个钩子程序,实现鼠标钩子和键盘钩子。当测试程序运行时,对话框窗口放到最大,鼠标不可用。只有在发生键盘F2按键按下事件发生时,则对话框测试应用程序关闭。鼠标重新可用。

【实验五】DLL编程 - 图5

5. 实验中的问题及心得

  • 学会了使用动态链接库以及如何使用显式和隐式来调用。
  • 隐式调用一般使用program comment,而显式调用一般使用LoadLibrary函数。
  • 在编写钩子时遇到了“错误 D8016 “/ZI”和“/Gy-”命令行选项不兼容”问题,VS2015调试中出现上述问题,需要手动改变/ZI命令行选项 或者 /Gy命令行选项。
  • 进程中的钩子:它的作用范围只是限定在某个进程中,或者某个线程中。全局的钩子:它的作用范围可以截获整个系统的操作。

6. 附件

以下是实验代码

链接:https://pan.baidu.com/s/1jkLquqgw6KdnkJWVWB06bg
提取码:cd6w