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。
- 如果我在这一行停止调试
if (hFile == INVALID_HANDLE_VALUE)
我发现我没有收到任何错误,对我来说这意味着我可以去目的地 D:\Buffer\Temp
并在那里找到我的文件, 但什么也没有。
- 我想也许它只是一个句柄(但
OPEN_ALWAYS
应该创建一个文件)。
- 我进一步移动到第
CloseHandle(hFile)
行并检查了变量 dwBytesWritten
,我看到写入的字节数是 8977,完全相同。
- 因此,我顺利完成了该方法的结尾,但仍然没有在我的目标中看到任何文件。
这里有什么问题?
您的代码中存在无效的类型转换。
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()
。
我的方法中有这样的实现
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。
- 如果我在这一行停止调试
if (hFile == INVALID_HANDLE_VALUE)
我发现我没有收到任何错误,对我来说这意味着我可以去目的地D:\Buffer\Temp
并在那里找到我的文件, 但什么也没有。 - 我想也许它只是一个句柄(但
OPEN_ALWAYS
应该创建一个文件)。 - 我进一步移动到第
CloseHandle(hFile)
行并检查了变量dwBytesWritten
,我看到写入的字节数是 8977,完全相同。 - 因此,我顺利完成了该方法的结尾,但仍然没有在我的目标中看到任何文件。
这里有什么问题?
您的代码中存在无效的类型转换。
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()
。