This is kind of a shot in the dark, but I was wondering if anyone could point out possible undefined behavior within or surrounding my open_file_dialog function.
Specifically, I think there might be undefined behavior related to the OPENFILENAME ofn object (its per-member assignments), or the call to GetOpenFileName.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
#include <iostream>
#include <string>
#define UNICODE
#include <windows.h>
/// THIS FILE REQUIRES LINKING WITH
/// -lcomdlg32
/// MODIFIED FROM:
/// https://docs.microsoft.com/en-us/windows/desktop/dlgbox/using-common-dialog-boxes
bool open_file_dialog(HWND hwnd, std::wstring& file)
{
OPENFILENAME ofn; // common dialog box structure
//char szFile[260]; // buffer for file name
wchar_t szFile[260];
//HWND hwnd; // owner window
//HANDLE hf; // file handle
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
// use the contents of szFile to initialize itself.
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = L"All\0*.*\0Text\0*.TXT\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
if (GetOpenFileName(&ofn) > 0)
{
// copy file name
file = ofn.lpstrFile;
return true;
}
else
{
return false;
}
}
int main()
{
std::wstring file;
bool success = open_file_dialog(nullptr, file);
if (success)
{
std::wcout << "Opening " << file << std::endl;
}
}
|
I am suspecting there is some sort of undefined behavior, because in my full program that calls this open_file_dialog function, future file writes fail after GetOpenFileName succeeds (after open_file_dialog takes the "return true;" path). Furthermore, it's a bit weird because in my actual program, using std::cout with file (instead of std::wcout) bizarrely compiles, while this minimum example correctly would not. So I'm not perfectly reproducing my environment. Regardless, it is the same behavior if I comment out line 58.
Sadly I can't make a better minimum example right now, but perhaps in the future I'll try to. If you see any red flags in the code above, I would appreciate any thoughts.
Thanks!
I'm compiling with:
C:\dev\cplusplus\FileDialog>g++ -Wall main.cpp -lcomdlg32 -o main |
In the future, I plan on using the "Common Item Dialog" (or perhaps just using C#)
https://msdn.microsoft.com/en-us/library/Bb776913(v=VS.85).aspx
However, I can't right now because my somewhat-old MinGW-64's shlwapi.h does not contain some of the necessary structs/functions. I believe this has been fixed in 2018+ versions of MinGW-64.
https://www.mail-archive.com/mingw-w64-public@lists.sourceforge.net/msg14978.html
Edit: Debugging on my real program (with a few modifications) shows this:
> p ofn
$1 = {
lStructSize = 152,
hwndOwner = 0x9716da,
hInstance = 0x0,
lpstrFilter = 0x58cd28 <_ZL10frame_size+2348> L"\101\154", <incomplete sequence \154>,
lpstrCustomFilter = 0x0,
nMaxCustFilter = 0,
nFilterIndex = 1,
lpstrFile = 0x22f270 L"",
nMaxFile = 260,
lpstrFileTitle = 0x0,
nMaxFileTitle = 0,
lpstrInitialDir = 0x0,
lpstrTitle = 0x0,
Flags = 6144,
nFileOffset = 0,
nFileExtension = 0,
lpstrDefExt = 0x0,
lCustData = 0,
lpfnHook = 0x0,
lpTemplateName = 0x0,
pvReserved = 0x0,
dwReserved = 0,
FlagsEx = 0
}
|
Just realized I'm assigning a string literal to a non-const pointer for the filter. Not sure if that's actually the issue, but it's certainly a possible point of contention.
Edit, doing:
1 2
|
wchar_t filter[260] = L"All\0*.*\0Text\0*.TXT\0";
ofn.lpstrFilter = filter;
|
does not change the behavior.
I think the issue might be that "incomplete sequence" part of the filter?
Edit, doing:
1 2 3 4 5 6 7 8
|
std::wstring filter = L"All|*.*|Text|*.TXT|";
for (wchar_t& ch : filter)
{
if (ch == L'|')
ch = 0;
}
ofn.lpstrFilter = &filter[0]; //.c_str();
|
Also makes no difference.
I think I need to do more fine-grained debugging before coming to any conclusions here.