Reputation: 5243
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.
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.OPEN_ALWAYS
should create a file).CloseHandle(hFile)
and checked the variable dwBytesWritten
and I see that number of written bytes is 8977, exactly the same.What is the problem here?
Upvotes: 0
Views: 1142
Reputation: 596632
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:
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