CreateFile 函数执行没有错误但不创建任何文件

Ale*_*nko 0 c++ file-io createfile

我的方法中有这样的实现

std::string name2 = "D:\\Buffer\\Temp\\t_libpng" + std::to_string(i) + ".png";
Run Code Online (Sandbox Code Playgroud)
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);

    // ...
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,文件名是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. 所以,我在没有任何错误的情况下结束了该方法,但仍然没有在我的目的地中看到任何文件。

这里有什么问题?

Rem*_*eau 6

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

CreateFile()是一个预处理器宏,根据是否定义映射到CreateFileW()(which requires wchar_t*) 或CreateFileA()(which takes char*) UNICODE。ClearlyUNICODE已在您的项目中定义,这就是为什么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
Run Code Online (Sandbox Code Playgroud)

您正在将filename参数从char*to类型转换wchar_t*。铸造一个char*指针wchar_t*的,当你删除型铸造编译器会抱怨这样的事实在这种情况下是行不通的,因为显而易见的。类型转换所做的只是对编译器撒谎,告诉它指针指向wchar_t数据,而实际上它实际上指向的是char数据。

因此,当您有一个char*指向charstring的指针"D:\\Buffer\\Temp\\t_libpng0.png",并且您将该指针类型转换传递给 时CreateFileW(),该函数认为您传递的是一个双字节wchar_t字符串"??????????????":

图片

注意字节不会改变,只是对它们的解释。这种不匹配通常被称为“mojibake”。

"??????????????"是一个完全有效的文件名的CreteFileW(),但它没有指定的目录,所以CreateFileW()将把字符串作为相对路径,并创建调用进程的该文件当前的工作目录。这就是为什么您的代码没有失败,但文件不在您期望的位置,或命名为您期望的内容。

因此,使用CreateFile()在这种情况下正确的宏,你就必须真正转变您的char数据,wchar_t使用数据MultiByteToWideChar()(或同等学历),例如:

// 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
Run Code Online (Sandbox Code Playgroud)

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

#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
Run Code Online (Sandbox Code Playgroud)
std::wstring name2 = L"D:\\Buffer\\Temp\\t_libpng" + std::to_wstring(i) + L".png";
_write_png_file_cv2_(name2.c_str(), ...);
Run Code Online (Sandbox Code Playgroud)

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

hFile = CreateFileA(filename, ...);
Run Code Online (Sandbox Code Playgroud)

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