Windows核心编程系列文章仅作为实验报告和Windows编程学习参考,不作为任何技术文章,还望大佬们勿喷。
1. 实验名称
DLL编程
2. 实验环境
- VS2015
- Windows 10
3. 实验目的
- 掌握动态链接的两种不同方法,以及动态链接的不同加载方式基本原理
- 采用api函数加载动态链接,LoadLibrary(),以及FreeLibrary(),GetProcAddress()函数的加载动态链接方式
- 采用MFC和SDK方式编写动态链接,了解动态链接入口函数DllMain
- 了解Hook编程基本概念,原理。采用动态链接中Hook编程
4. 实验内容、步骤及结果
1. 实验内容
- 编写一个win32动态链接库,实现两个数简单加法和减法,乘法,乘法,然后采用显式和隐式两种方式调用。调用界面采用MFC或者wind32窗口程序实现。
- 编写一个钩子程序,实现鼠标钩子和键盘钩子。当测试程序运行时,对话框窗口放到最大,鼠标不可用。只有在发生键盘F2按键按下事件发生时,则对话框测试应用程序关闭。鼠标重新可用。
2. 实验步骤
- 编写一个win32动态链接库,实现两个数简单加法和减法,乘法,乘法,然后采用显式和隐式两种方式调用。
- (1). 文件→新建→项目→win32项目,创建ArithmeticDll,应用程序类型选择DLL。

(2). 新建头文件ArithmeticDll.h
ArithmeticDll.h代码如下:
#pragma once#ifndef ArithmeticDll_H_#define ArithmeticDll_H_#ifdef MYLIBDLL#define MYLIBDLL Arithmetic "C" _declspec(dllimport)#else#define MYLIBDLL extern "C" _declspec(dllexport)#endifMYLIBDLL int Add(int plus1, int plus2);MYLIBDLL int Sub(int sub1, int sub2);MYLIBDLL int Mul(int mul1, int mul2);MYLIBDLL int Div(int div1, int div2);//You can also write like this://extern "C" {//_declspec(dllexport) int Add(int plus1, int plus2);//};#endif
(3). 源文件ArithmeticDLL.cpp的代码如下:
// ArithmeticDll.cpp : 定义 DLL 应用程序的导出函数。//#include "stdafx.h"#include <iostream>#include "ArithmeticDll.h"using namespace std;int Add(int plus1, int plus2){int add_result = plus1 + plus2;return add_result;}int Sub(int sub1, int sub2){int sub_result = sub1 - sub2;return sub_result;}int Mul(int mul1, int mul2){int mul_result = mul1*mul2;return mul_result;}int Div(int div1, int div2){if (div2 != 0){int div_result = div1 / div2;return div_result;}}
(4). 新建模块定义文件Arithmeticdef.def
LIBRARY "ArithmeticDll"EXPORTSAdd @1Sub @2Mul @3Div @4
(5). 在vs2015自动创建的dllmain.cpp代码如下:
// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "stdafx.h"BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved){switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;}
(6) . 最后,编译生成ArithmeticDLL.dll文件和ArithmeticDLL.lib文件

(7). 然后采用win32 或者MFC 程序中,调用动态链接库,显式和隐式两种方式调用,完成调用。
隐式加载动态链接库
// experience5.cpp : 定义控制台应用程序的入口点。#include "stdafx.h"#include "ArithmeticDll.h"#pragma comment (lib,"ArithmeticDLL")int main(int argc, _TCHAR* argv[]){printf("隐式加载动态链接库 Add: %d", Add(5, 10));getchar();return 0;}
显式加载动态链接库
// experience5.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "ArithmeticDll.h"#include <windows.h>//#pragma comment (lib,"ArithmeticDLL")int main(int argc, _TCHAR* argv[]){//printf("隐式加载动态链接库 Add: %d", Add(5, 10));HINSTANCE hInst;hInst = LoadLibrary(_T("ArithmeticDll.dll "));typedef int(*ADDPROC)(int sub1, int sub2);ADDPROC Sub = (ADDPROC)GetProcAddress(hInst, "Sub");printf("显示调用动态链接库减函数 Sub:%d", Sub(5, 10));getchar();return 0;}
- 编写一个钩子程序,实现鼠标钩子和键盘钩子。当测试程序运行时,对话框窗口放到最大,鼠标不可用。只有在发生键盘F2按键按下事件发生时,则对话框测试应用程序关闭。鼠标重新可用。
////////////////////////////////////////////////// KeyHookApp.cpp文件#include "resource.h"#include "KeyHookApp.h"#include "KeyHookLib.h"#pragma comment(lib, "09KeyHookLib")CMyApp theApp;BOOL CMyApp::InitInstance(){CMainDialog dlg;m_pMainWnd = &dlg;dlg.DoModal();return FALSE;}CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_MAIN, pParentWnd){}BEGIN_MESSAGE_MAP(CMainDialog, CDialog)ON_MESSAGE(HM_KEY, OnHookKey)END_MESSAGE_MAP()BOOL CMainDialog::OnInitDialog(){CDialog::OnInitDialog();SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0,0, 0, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOMOVE);if(!SetKeyHook(TRUE, 0, m_hWnd)) // 安装钩子MessageBox("安装钩子失败!");return TRUE;}void CMainDialog::OnCancel(){SetKeyHook(FALSE); // 卸载钩子CDialog::OnCancel();return;}long CMainDialog::OnHookKey(WPARAM wParam, LPARAM lParam){// 此时参数wParam为用户按键的虚拟键码,// lParam参数包含按键的重复次数、扫描码、前一个按键状态等信息char szKey[80];::GetKeyNameText(lParam, szKey, 80);CString strItem;strItem.Format(" 用户按键:%s \r\n", szKey);CString strEdit; // 添加到编辑框中GetDlgItem(IDC_KEYMSG)->GetWindowText(strEdit);GetDlgItem(IDC_KEYMSG)->SetWindowText(strItem + strEdit);::MessageBeep(MB_OK);return 0;}
3. 实验结果(实验运行截图)
- 编写一个win32动态链接库,实现两个数简单加法和减法,乘法,乘法,然后采用显式和隐式两种方式调用。
- 隐式加载动态链接库

显式加载动态链接库

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

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