C++ FILE_NOTIFY_INFORMATION到std::vector<std::byte>的拷贝方法
模拟ReadDirectoryChangesW返回缓冲区的实现方案
先修正现有代码的核心问题
你当前构造FILE_NOTIFY_INFORMATION的写法存在堆越界风险:
FILE_NOTIFY_INFORMATION是Windows定义的变长结构体,FileName不是定长数组,而是紧跟在三个固定字段(NextEntryOffset、Action、FileNameLength)之后的可变长Unicode字符串。- 直接
new FILE_NOTIFY_INFORMATION只会按照编译器计算的固定结构体大小分配内存(仅包含固定字段+FileName第一个字符的空间),完全没有给实际文件名预留位置,后续wcscpy_s操作直接属于未定义行为。
完整实现流程
1. 按真实接口的内存布局构造通知数据
要完全匹配ReadDirectoryChangesW输出的内存格式,需要先计算整个变长结构体的实际总内存长度,一次性分配连续空间填充数据:
// 定义要模拟的变更对应的文件名 const std::wstring mock_file_name = L"example.txt"; // 计算结构体固定字段的内存偏移(到FileName字段的起始位置) const size_t fixed_field_offset = offsetof(FILE_NOTIFY_INFORMATION, FileName); // 计算整个通知条目占的总内存:固定字段长度 + 文件名的总字节长度 // 注意FileNameLength字段存的是文件名的字节数,不是字符数 const DWORD file_name_byte_len = static_cast<DWORD>(mock_file_name.size() * sizeof(wchar_t)); const size_t total_entry_size = fixed_field_offset + file_name_byte_len; // 分配足够的连续内存存放整个变长通知结构体 auto mock_entry = static_cast<FILE_NOTIFY_INFORMATION*>(malloc(total_entry_size)); // 按照接口返回的格式填充字段 mock_entry->NextEntryOffset = 0; // 单条通知,无后续条目 mock_entry->Action = 1; // 对应FILE_ACTION_ADDED mock_entry->FileNameLength = file_name_byte_len; // 将文件名拷贝到结构体的FileName位置 memcpy(mock_entry->FileName, mock_file_name.c_str(), file_name_byte_len);
2. 将构造好的内存拷贝到业务用std::vectorstd::byte缓冲区
拷贝时必须使用之前计算的实际条目总长度,不能直接用sizeof(FILE_NOTIFY_INFORMATION)(这个值仅包含结构体固定部分,不包含变长的文件名内容),拷贝位置和真实接口写入的位置保持一致即可:
// 业务使用的接收缓冲区,和正常调用接口时的初始化逻辑完全一致 std::vector<std::byte> notifyBuffer(1024); // 将构造好的整条通知内存拷贝到缓冲区开头,和接口返回的内存布局完全一致 memcpy( notifyBuffer.data(), mock_entry, total_entry_size ); // 用完手动释放构造时分配的临时内存 free(mock_entry);
关键注意点
- 单条通知拷贝完成后,
notifyBuffer里的内容和真实调用ReadDirectoryChangesW成功返回的内容完全一致,业务侧的解析代码可以直接正常运行。 - 如果需要模拟多条连续的通知条目,只需要在当前条目填充完成后,将当前条目的
NextEntryOffset设置为本条目的总内存长度,再在缓冲区的对应偏移位置重复上述流程填充下一条目即可。 - 不要尝试直接将
new出来的结构体指针reinterpret_cast后直接拷贝,这种写法不仅拷贝长度不足,还伴随内存越界风险,完全无法还原真实接口的输出格式。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

