我几乎不知道如何描述这种行为,所以这可能是我无法自己找到答案的原因 - 我根本不知道如何命名这个问题。因此,如果标题具有误导性或者问题在某种程度上是错误的,请耐心等待,但这里来了
我有一个类方法,我打开一个文件并获取文件名:
OPENFILENAME GuiUtils::ChooseFileDialog(HWND hwnd)
{
OPENFILENAME ofn;
char szFile[260];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = (LPWSTR)szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrFilter = TEXT("Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0");
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
HANDLE hf;
// Display the Open dialog box.
if (GetOpenFileName(&ofn) == TRUE)
{
hf = CreateFile(ofn.lpstrFile,
GENERIC_READ,
0,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
}
return ofn;
}
当我查看ofn.lpstrFile
时,所有内容看起来都是正确的
当我继续使用我的调试器并跳回调用函数时,有些东西搞砸了:
调用类方法:
INT_PTR CALLBACK FormCreator::Callback(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_COMMAND:
if (LOWORD(wParam) == IDCHOOSEFILE)
{
GuiUtils *guiUtils = new GuiUtils();
OPENFILENAME ofn = guiUtils->ChooseFileDialog(hwnd);
ORMFactory* db = new ORMFactory(); // value in debugger changes here
AbstractORM* sqlite = db->GetORMProvider(TEXT("SQLITE"));
this->databaseFileName = (wstring)ofn.lpstrFile;
}
break;
return 0;
}
有人可以告诉我ofn.lpstrFile
这里有什么问题以及如何解决这个问题?
谢谢
答案 0 :(得分:5)
您使用的是GetOpenFileName()
的Unicode版本,但是您为其提供了一个ANSI缓冲区来存储所选的文件名。在将缓冲区分配给lpstrFile
字段时隐藏了一个缓冲区编译器错误会提醒您这种不匹配。
您需要将缓冲区从char
更改为wchar_t
并摆脱类型转换。
但是,即使您修复了此问题,代码中也会出现其他错误。
您将填充的OPENFILENAME
返回给调用者,但是当ChooseFileDialog()
退出时,它仍然指向超出范围的本地缓冲区。因此,当调用者访问lpstrFile
字段时,它正在调用未定义的行为。
ChooseFileDialog()
成功时, GetOpenFileName()
都会泄漏文件句柄。您没有对文件句柄执行任何操作,并且您没有关闭它。因此,您不应该打开所有文件
你在回调中泄漏了动态的alloxated对象。
尝试更像这样的东西:
std::wstring GuiUtils::ChooseFileDialog(HWND hwnd)
{
OPENFILENAMEW ofn;
wchar_t szFile[MAX_PATH];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = MAX_PATH;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrFilter = L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
HANDLE hf;
// Display the Open dialog box.
if (!GetOpenFileNameW(&ofn))
return L"";
// what is this for???
/*
hf = CreateFileW(ofn.lpstrFile,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
*/
return ofn.lpstrFile;
}
INT_PTR CALLBACK FormCreator::Callback(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_COMMAND:
if (LOWORD(wParam) == IDCHOOSEFILE)
{
GuiUtils guiUtils;
std::wstring fname = guiUtils.ChooseFileDialog(hwnd);
if (fname.empty()) break;
ORMFactory db;
AbstractORM* sqlite = db.GetORMProvider(L"SQLITE");
this->databaseFileName = fname;
}
break;
}
return 0;
}
答案 1 :(得分:1)
您正在使用堆栈数组szFile
来存储文件名。当ChooseFileDialog
方法完成后,所有堆栈变量都将被销毁。返回指向堆栈变量的指针是未定义的行为。
这一行也很奇怪:
ofn.lpstrFile = (LPWSTR)szFile;
您正在向char
指针投射wchar_t
数组。如果文件名大于129个宽字符,则会出现缓冲区溢出。
要解决此问题,请使用动态内存分配:
ofn.lpstrFile = new WCHAR[MAX_PATH];
ofn.nMaxFile = MAX_PATH;
使用它时, Callback
必须释放数组:
delete[] ofn.lpstrFile;