返回

时间:2016-08-11 05:47:16

标签: c++ winapi encoding callback

我几乎不知道如何描述这种行为,所以这可能是我无法自己找到答案的原因 - 我根本不知道如何命名这个问题。因此,如果标题具有误导性或者问题在某种程度上是错误的,请耐心等待,但这里来了

我有一个类方法,我打开一个文件并获取文件名:

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时,所有内容看起来都是正确的 enter image description here

当我继续使用我的调试器并跳回调用函数时,有些东西搞砸了:

调用类方法:

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

enter image description here

有人可以告诉我ofn.lpstrFile这里有什么问题以及如何解决这个问题?

谢谢

2 个答案:

答案 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;