键盘记录和鼠标跟踪器:我应该使用非阻塞I/O吗



我正在用C/C++为Windows编写一个简单的键盘记录程序/鼠标记录程序。为此,我使用Win32函数LowLevelMouseProcLowLevelKeyboardProc

如果相关的话,我的代码中有一个GitHub要点,这是非常基本的:定义事件回调,并将其与SIGINT的回调一起注册。我将在问题末尾添加一个摘要版本。

我的问题如下:为了最大限度地减少开销,我应该如何将这些事件保存到磁盘?

欢迎使用C或C++两种语言的答案。

每当我得到一个新事件时,简单地写入一个缓冲文件,并在缓冲区满时让文件处理刷新,这是一种好的做法吗?我听说过非阻塞I/O,但微软的文档说这会带来额外的开销。最后,我不确定我是否应该为此创建第二个线程。

我想使用某种缓冲来避免很多小磁盘I/O。理想情况下,我会在进程终止之前向磁盘写入一次。但我不知道如何做到这一点。

代码:

#include "pch.h"
#include <stdio.h>
#include <Windows.h>
HHOOK handle;
LRESULT CALLBACK lowLevelMouseProc(
_In_ int    nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
MSLLHOOKSTRUCT* lp = (MSLLHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEMOVE) {
// Best way to save pt.x and pt.y to disk?
printf("%d %d n", lp->pt.x, lp->pt.y);
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
int main()
{
handle = SetWindowsHookExA(WH_MOUSE_LL, &lowLevelMouseProc, NULL, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0));
UnhookWindowsHookEx(handle)
return 0;
}

使用2个缓冲区。一个用于写入,一个用于读取(刷新到磁盘(。一旦满足某些条件(缓冲区已满、程序关闭…(,交换缓冲区并在单独的线程中开始刷新磁盘。

这可能看起来像:

#include <Windows.h>
#include <vector>
#include <thread>
#include <fstream>
#include <atomic>
struct Point
{
long x, y;
};
class Buffer
{
public:
Buffer(std::string _file = "log.txt", const size_t _buffer_size = 100000) : buffer_size(_buffer_size), file(_file)
{
points1.reserve(_buffer_size);
points2.reserve(_buffer_size);
}
void write(Point p)
{
buf->push_back(p);
if (buf->size() >= buffer_size && !thread_running.load())
to_disk();
}
private:
const size_t buffer_size;
const std::string file;
std::atomic<bool> thread_running{ false };
std::vector<Point> points1, points2;
std::vector<Point> *buf = &points1, *other = &points2;

void swap_buffer()
{
std::swap(buf, other);
}
void to_disk()
{
swap_buffer();
auto tmp_buf = other;
auto tmp_file = file;
auto tmp_flag = &thread_running;
auto fn = [tmp_buf, tmp_file, tmp_flag]() {
tmp_flag->store(true);
std::fstream f(tmp_file, std::ios::app);
for (auto &v : *tmp_buf)
f << v.x << ' ' << v.y << 'n';
tmp_buf->clear();
tmp_flag->store(false);
};
std::thread t(fn);
t.detach();
}
};
Buffer buffer("log.txt");

HHOOK handle;
LRESULT CALLBACK lowLevelMouseProc(
_In_ int    nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
MSLLHOOKSTRUCT* lp = (MSLLHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEMOVE) {
buffer.write({ lp->pt.x, lp->pt.y });
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
int main()
{
handle = SetWindowsHookExA(WH_MOUSE_LL, &lowLevelMouseProc, NULL, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0));
UnhookWindowsHookEx(handle);
return 0;
}

在这种情况下,当达到一定的大小限制时,缓冲区会被写入磁盘。这可以进一步优化,例如,不检查每次写入的大小。

注意:在这个示例中,省略了错误处理,并且应该相应地管理内部缓冲区的生存期。

最新更新