You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 08:10:56