Sirop4ik
Sirop4ik

Reputation: 5243

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

I have such implementation in my method

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);

    // ...
}

Filename in this case is D:\\Buffer\\Temp\\t_libpng0.png and size of buff is 8977.

  1. If I stop debug on this line if (hFile == INVALID_HANDLE_VALUE) I see that I don't get any errors, as for me it is means that I can go to destination D:\\Buffer\\Temp and find there my file, but there is nothing.
  2. I thought maybe it is just a handle (but OPEN_ALWAYS should create a file).
  3. I moved further to line CloseHandle(hFile) and checked the variable dwBytesWritten and I see that number of written bytes is 8977, exactly the same.
  4. So, I came up to the end of the method without any error but still any I don't see any files within my destination.

What is the problem here?

Upvotes: 0

Views: 1142

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596632

You have an invalid type-cast in your code.

CreateFile() is a preprocessor macro that maps to either CreateFileW() (which takes wchar_t*) or CreateFileA() (which takes char*) depending on whether UNICODE is defined. Clearly UNICODE is defined in your project, which is why CreateFile() expects 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

You are type-casting your filename parameter from char* to wchar_t*. Casting a char* pointer to wchar_t* will not work in this case, as evident by the fact that the compiler complains when you remove the type-cast. All the type-cast does is lie to the compiler, telling it that the pointer is pointing at wchar_t data, when in reality it is actually pointing at char data instead.

As such, when you have a char* pointer to a char string "D:\\Buffer\\Temp\\t_libpng0.png", and you pass that pointer type-casted to CreateFileW(), the function thinks you are passing it a double-byte wchar_t string "㩄䉜晵敦屲敔灭瑜江扩湰で瀮杮" instead:

image

Notice the bytes don't change, just the interpretation of them. This kind of mismatch is commonly referred to as "mojibake".

"㩄䉜晵敦屲敔灭瑜江扩湰で瀮杮" is a perfectly valid file name for CreteFileW(), but it has no directory specified, so CreateFileW() will treat the string as a relative path and create that file in the calling process's current working directory. That is why your code is not failing, but the file is not where you are expecting, or named what you are expecting.

So, to use the CreateFile() macro properly in this situation, you would have to actually convert your char data to wchar_t data using MultiByteToWideChar() (or equivalent), eg:

#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

Or, change your filename parameter from char* to wchar_t* to begin with, and then update the caller to use std::wstring instead of std::string, eg:

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, ...);

    // ...
}

Otherwise, if you want to continue using std::string/char* then you can just call CreateFileA() directly instead, and let it handle the conversion internally for you:

hFile = CreateFileA(filename, ...);

A good rule of thumb - whenever you are NOT using the TCHAR type in your code, you should stay away from using any TCHAR-based function macros. Use functions that are specific to the actual data type(s) you are using. For instance, in this situation, by calling CreateFileA() for char* and CreateFileW() for wchar_t*, not CreateFile() at all.

Upvotes: 7

Related Questions