CreateFile 函数执行无误,但不创建任何文件

CreateFile function executed without errors but doesn't create any files

我的方法中有这样的实现

std::string name2 = "D:\Buffer\Temp\t_libpng" + std::to_string(i) + ".png";
bool _write_png_file_cv2_(char const *filename,
    int width, 
    int height,
    int widthStep,
    png_byte color_type, 
    png_byte bit_depth, 
    png_bytep *row_pointers)
{
    // ...

    hFile = CreateFile((LPCWSTR)filename,      // Open Two.txt.
                       GENERIC_WRITE,          // Open for writing
                       0,                      // Do not share
                       NULL,                   // No security
                       OPEN_ALWAYS,            // Open or create
                       FILE_ATTRIBUTE_NORMAL,  // Normal file
                       NULL);                  // No template file

    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("ERROR:: Could not open file");
        CloseHandle(hFile);            // Close the first file.
        return false;
    }

    DWORD dwBytesWritten;

    //fclose(fp);
    //writeSamsungToDisk(png->buffer,png->idx);

    BOOL result = WriteFile(
        hFile, 
        png->buffer,
        png->idx, 
        &dwBytesWritten,
        NULL);

    CloseHandle(hFile);

    // ...
}

本例中的文件名是 D:\Buffer\Temp\t_libpng0.png,buff 的大小是 8977。

  1. 如果我在这一行停止调试 if (hFile == INVALID_HANDLE_VALUE) 我发现我没有收到任何错误,对我来说这意味着我可以去目的地 D:\Buffer\Temp 并在那里找到我的文件, 但什么也没有。
  2. 我想也许它只是一个句柄(但 OPEN_ALWAYS 应该创建一个文件)。
  3. 我进一步移动到第 CloseHandle(hFile) 行并检查了变量 dwBytesWritten,我看到写入的字节数是 8977,完全相同。
  4. 因此,我顺利完成了该方法的结尾,但仍然没有在我的目标中看到任何文件。

这里有什么问题?

您的代码中存在无效的类型转换。

CreateFile() 是一个预处理器宏,根据是否映射到 CreateFileW()(需要 wchar_t*)或 CreateFileA()(需要 char*UNICODE 已定义。显然 UNICODE 已在您的项目中定义,这就是 CreateFile() 期望 wchar_t*.

的原因
// winbase.h

WINBASEAPI
__out
HANDLE
WINAPI
CreateFileA(
    __in     LPCSTR lpFileName,
    __in     DWORD dwDesiredAccess,
    __in     DWORD dwShareMode,
    __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    __in     DWORD dwCreationDisposition,
    __in     DWORD dwFlagsAndAttributes,
    __in_opt HANDLE hTemplateFile
    );
WINBASEAPI
__out
HANDLE
WINAPI
CreateFileW(
    __in     LPCWSTR lpFileName,
    __in     DWORD dwDesiredAccess,
    __in     DWORD dwShareMode,
    __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    __in     DWORD dwCreationDisposition,
    __in     DWORD dwFlagsAndAttributes,
    __in_opt HANDLE hTemplateFile
    );
#ifdef UNICODE
#define CreateFile  CreateFileW
#else
#define CreateFile  CreateFileA
#endif // !UNICODE

您正在将 filename 参数从 char* 类型转换为 wchar_t*。在这种情况下,将 char* 指针转换为 wchar_t* 是行不通的,当您删除类型转换时编译器会报错这一事实就证明了这一点。所有类型转换都对编译器说谎,告诉它指针指向 wchar_t 数据,而实际上它实际上指向 char 数据。

因此,当你有一个指向 char 字符串 "D:\Buffer\Temp\t_libpng0.png"char* 指针时,你将该指针类型转换为 CreateFileW(),该函数认为您传递给它的是双字节 wchar_t 字符串 "㩄䉜晵敦屲敔灭瑜江扩湰で瀮杮" 而不是:

注意字节没有改变,只是解释。这种不匹配通常称为 "mojibake".

"㩄䉜晵敦屲敔灭瑜江扩湰で瀮杮" 完全有效的文件名 CreteFileW(),但它没有指定目录,因此 CreateFileW() 将把字符串作为 相对路径 并在调用进程的 当前工作目录 中创建该文件。这就是为什么您的代码没有失败,但文件不在您期望的位置,或者没有按照您期望的方式命名。

因此,要在这种情况下正确使用 CreateFile() 宏,您实际上必须 您的 char 数据转换为 wchar_t数据使用 MultiByteToWideChar()(或等效),例如:

#ifdef UNICODE

int len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
wchar_t *w_filename = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, filename, -1, w_filename, len);
hFile = CreateFile(w_filename, ...);
// better:
// hFile = CreateFileW(w_filename, ...);
delete[] w_filename;

#else

hFile = CreateFile(filename, ...);
// better:
// hFile = CreateFileA(filename, ...);

#endif

或者,首先将 filename 参数从 char* 更改为 wchar_t*,然后更新调用方以使用 std::wstring 而不是 std::string ,例如:

std::wstring name2 = L"D:\Buffer\Temp\t_libpng" + std::to_wstring(i) + L".png";
_write_png_file_cv2_(name2.c_str(), ...);
bool _write_png_file_cv2_(wchar_t const *filename, ...)
{
    // ...

    hFile = CreateFile(filename, ...);
    // better:
    // hFile = CreateFileW(filename, ...);

    // ...
}

否则,如果您想继续使用 std::string/char*,那么您可以直接调用 CreateFileA(),让它在内部为您处理转换:

hFile = CreateFileA(filename, ...);

一个很好的经验法则 - 只要您不在代码中使用 TCHAR 类型,就应该远离使用任何基于 TCHAR 的函数宏。使用特定于您正在使用的实际数据类型的函数。例如,在这种情况下,通过为 char* 调用 CreateFileA() 和为 wchar_t* 调用 CreateFileW(),而不是 CreateFile()