c++ - 键盘记录器和鼠标追踪器 : should I use non-blocking I/O?

标签 c++ c winapi mouseevent nonblocking

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

如果相关,这里是一个GitHub gist使用我的代码,这是超基础的:定义事件回调并将其与 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;
}

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

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

关于c++ - 键盘记录器和鼠标追踪器 : should I use non-blocking I/O?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53698898/

相关文章:

c++ - 在 64 位 linux armv8 机器上编译 32 位二进制文​​件时遇到问题

c++ - 为什么不能在Dev C++中编译

c++ - c中的内存位置和数据竞争

c - 原地反转数组

c - 如何使程序在 C 语言中处理不同的文本文件

c - winprog.org 第一个例子奇怪的行为?

c++ - 老派的 C 树桩 - 大多数键盘输入被古老的 XWindows/Motif 应用程序忽略

c++ - C编译器如何执行这个printf语句?

c - DLL 注入(inject)器不工作(64 位编译器)

vb.net - 检测用户存在