求助:Win32 API C++ UNBUFFERED MODE写入150字节数据异常
非缓冲模式下Win32 WriteFile写入内容错误排查
核心问题分析
你的代码能编译通过但写入内容错误,主要源于以下几个关键问题:
- 测试数据未写入目标缓冲区:代码中定义了
DataBuffer但从未将其内容复制到对齐后的BufferAligned中,导致写入文件的是堆内存中的随机垃圾数据。 - 写入缓冲区大小计算错误:非缓冲IO要求写入大小必须是磁盘扇区大小的整数倍,你当前的计算
sizeNeeded = fileConf.BytesPerSector + ROUND_UP_SIZE(dwBytesToWrite, fileConf.BytesPerSector)多添加了一个扇区,属于冗余分配且逻辑不合理。 - 未检查文件句柄有效性:调用
CreateFileA后未判断返回的句柄是否为INVALID_HANDLE_VALUE,若文件打开失败会导致后续操作无意义甚至崩溃。 - 文件指针位置未处理:使用
OPEN_ALWAYS打开文件时,若文件已存在,文件指针默认位于末尾,写入操作会追加内容而非覆盖,可能不符合预期。
修复方案及修正代码
针对上述问题,以下是修正后的完整代码:
#include <Windows.h> #include <iostream> #include <tchar.h> #include <stdio.h> #include <strsafe.h> #include <string> #include <cstring> void DisplayError(LPTSTR lpszFunction); // 内存与大小对齐宏 #define ROUND_UP_SIZE(Value,Pow2) ((SIZE_T) ((((ULONG)(Value)) + (Pow2) - 1) & (~(((LONG)(Pow2)) - 1)))) #define ROUND_UP_PTR(Ptr,Pow2) ((void *) ((((ULONG_PTR)(Ptr)) + (Pow2) - 1) & (~(((LONG_PTR)(Pow2)) - 1)))) // 磁盘参数结构体 struct HandleConfig { DWORD SectorsPerCluster; DWORD BytesPerSector; DWORD NumberOfFreeClusters; DWORD TotalNumberOfClusters; }; void MesureDisk(HandleConfig* config, BOOL debug) { DWORD lpSectorsPerCluster; DWORD lpBytesPerSector; DWORD lpNumberOfFreeClusters; DWORD lpTotalNumberOfClusters; if (GetDiskFreeSpaceA( NULL, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters, &lpTotalNumberOfClusters)) { if (!debug) { std::cout << "磁盘参数获取成功.\r\n"; } else { std::cout << "磁盘参数获取成功.\r\n"; std::cout << "磁盘结构信息: " << "\r\n" << "每簇扇区数: " << lpSectorsPerCluster << "\r\n" << "每扇区字节数: " << lpBytesPerSector << "\r\n" << "空闲簇数: " << lpNumberOfFreeClusters << "\r\n" << "总簇数: " << lpTotalNumberOfClusters << "\r\n"; } } else { DisplayError((LPTSTR)"GetDiskFreeSpaceA"); return; } config->SectorsPerCluster = lpSectorsPerCluster; config->BytesPerSector = lpBytesPerSector; config->NumberOfFreeClusters = lpNumberOfFreeClusters; config->TotalNumberOfClusters = lpTotalNumberOfClusters; } void Windows_write_UNBEFFERED() { HANDLE hFile; const char DataBuffer[] = "Some data for test !\r\n"; DWORD dwBytesToWrite = 150; DWORD dwBytesWritten = 0; LPCSTR filePath = "File.txt"; // 打开文件,添加FILE_FLAG_WRITE_THROUGH确保数据立即写入磁盘(非缓冲模式常用搭配) hFile = CreateFileA( filePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL ); // 检查文件句柄是否有效 if (hFile == INVALID_HANDLE_VALUE) { DisplayError((LPTSTR)"CreateFileA"); return; } HandleConfig fileConf; MesureDisk(&fileConf, TRUE); // 计算需要的缓冲区大小:对齐到扇区大小的整数倍 size_t alignedWriteSize = ROUND_UP_SIZE(dwBytesToWrite, fileConf.BytesPerSector); // 分配足够的内存,确保对齐后仍有足够空间 size_t sizeNeeded = alignedWriteSize + fileConf.BytesPerSector - 1; char* Buffer = new char[sizeNeeded]; void* BufferAligned = ROUND_UP_PTR(Buffer, fileConf.BytesPerSector); printf("需要的内存大小: %zu \r\n", sizeNeeded); printf("原始分配地址: %p \r\n", Buffer); printf("对齐后地址: %p \r\n", BufferAligned); // 将测试数据复制到对齐后的缓冲区 memset(BufferAligned, 0, alignedWriteSize); // 先清空缓冲区,避免残留数据 strncpy((char*)BufferAligned, DataBuffer, sizeof(DataBuffer)-1); // 填充剩余字节(确保写入满150字节测试数据) if (dwBytesToWrite > sizeof(DataBuffer)-1) { memset((char*)BufferAligned + sizeof(DataBuffer)-1, 'X', dwBytesToWrite - sizeof(DataBuffer)-1); } // 将文件指针移动到开头(实现覆盖写入,若需追加可删除此步骤) LARGE_INTEGER filePos = {0}; if (!SetFilePointerEx(hFile, filePos, NULL, FILE_BEGIN)) { DisplayError((LPTSTR)"SetFilePointerEx"); delete[] Buffer; CloseHandle(hFile); return; } if (!WriteFile( hFile, BufferAligned, alignedWriteSize, // 使用对齐后的写入大小 &dwBytesWritten, NULL)) { DisplayError((LPTSTR)"WriteFile"); delete[] Buffer; CloseHandle(hFile); return; } printf("成功写入 %lu 字节\r\n", dwBytesWritten); CloseHandle(hFile); delete[] Buffer; } int main() { Windows_write_UNBEFFERED(); return EXIT_SUCCESS; } void DisplayError(LPTSTR lpszFunction) { LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); if (FAILED(StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("%s 调用失败,错误码 %d:\n%s"), lpszFunction, dw, lpMsgBuf))) { printf("致命错误:无法输出错误信息.\n"); } _tprintf(TEXT("错误信息: %s\n"), (LPCTSTR)lpDisplayBuf); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); }
关键修复点说明
- 数据复制到对齐缓冲区:添加
memset清空缓冲区,用strncpy将测试数据写入,若写入长度大于测试字符串长度,补充填充数据确保写入满150字节内容。 - 修正写入大小计算:直接将
dwBytesToWrite对齐到扇区大小的整数倍作为写入大小,分配内存时预留足够空间保证对齐后不会越界。 - 添加句柄有效性检查:判断
CreateFileA返回的句柄是否合法,避免后续无效操作。 - 处理文件指针位置:使用
SetFilePointerEx将指针移到文件开头,实现覆盖写入(若需要追加可删除此步骤)。 - 完善错误处理:在
MesureDisk中添加GetDiskFreeSpaceA的错误检查,避免获取磁盘参数失败后使用无效数据。
内容的提问来源于stack exchange,提问作者Julien Poivret
相关产品推荐
相关产品推荐

