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)
#endif
MYLIBDLL 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"
EXPORTS
Add @1
Sub @2
Mul @3
Div @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. 附件
以下是实验代码