Initial filename text for open-file dialog

Hi there

I am trying to show an initial filename in the open-file dialog, but my program is crashing.
I attempt to convert the std::wstring to non-const wchar_t*.
Here's my code.

CGUIUtility.h:
1
2
3
...
static std::vector<std::string>			openFileDialog(std::string strDefaultPath = "", std::wstring strExtensionFilters = L"", bool bAllowMultiSelect = true, std::wstring strDefaultFileName = L"");
...


CGUIUtility.cpp:
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
...
vector<string>		CGUIUtility::openFileDialog(string strDefaultPath, wstring strExtensionFilters, bool bAllowMultiSelect, wstring strDefaultFileName)
{
	CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | (bAllowMultiSelect ? OFN_ALLOWMULTISELECT : 0), NULL, NULL, 0);
	dlg.m_ofn.nMaxFile = 8192;
	dlg.m_ofn.lpstrFile = (wchar_t*) strDefaultFileName.c_str(); // this line isn't working!
	dlg.m_ofn.lpstrFilter = strExtensionFilters.c_str();
	dlg.m_ofn.lpstrInitialDir = CStringUtility::convertStdStringToStdWString(strDefaultPath).c_str();
	
	vector<string> vecFilePaths;
	CString filename;
	if (dlg.DoModal() == IDOK)
	{
		// get the list of files
		POSITION pos = dlg.GetStartPosition();

		while (pos)
		{
			// one filename retreived per loop
			filename = dlg.GetNextPathName(pos);
			vecFilePaths.push_back(CStringUtility::convertCStringToStdString(filename));
		}
	}
	else
	{
		vecFilePaths.push_back("");
	}
	return vecFilePaths;
}
...


MyFile.cpp:
1
2
3
4
5
6
7
8
9
10
...
void myfunc() {
	wstring w = L"AA.img";
	vector<string> vecPaths = CGUIUtility::openFileDialog("", L"", "", w);
	if (vecPaths[0] == "")
	{
		return;
	}
}
...
All of the pointer members of the OPENFILENAME object that reference file names, titles, paths, extensions, etc., require pre-allocated buffers to which to point. You can't simply assign character strings to them because they aren't buffers which can contain characters, but rather pointers to buffers which you must allocate.
Fearing that you might not understand what I just wrote above, I quickly modified one of my template programs to provide a fully compileable working example named Form20. There are two files - Form20.cpp and Form20.h. The app creates a window with a button on it which when clicked puts up an Open File Dialog Box with the Text "Form20.cpp" in the File Name Text Box, which, I believe, is what you wanted to know. Compiles to 17k as x64 with MinGW C++...

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
//Form20.h
#ifndef Form20_h
#define Form20_h

#define  IDC_BUTTON1          1500
#define dim(x)                (sizeof(x) / sizeof(x[0]))

struct WndEventArgs
{
 HWND                         hWnd;
 WPARAM                       wParam;
 LPARAM                       lParam;
 HINSTANCE                    hIns;
};

long fnWndProc_OnCreate       (WndEventArgs& Wea);
long fnWndProc_OnCommand      (WndEventArgs& Wea);
long fnWndProc_OnDestroy      (WndEventArgs& Wea);

struct EVENTHANDLER
{
 unsigned int                 iMsg;
 long                         (*fnPtr)(WndEventArgs&);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                  fnWndProc_OnCreate},
 {WM_COMMAND,                 fnWndProc_OnCommand},
 {WM_DESTROY,                 fnWndProc_OnDestroy}
};
#endif 


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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//Form20.cpp
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include "Form20.h"


long fnWndProc_OnCreate(WndEventArgs& Wea)
{
 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
 CreateWindowEx(0,L"button",L"Show Open File Dialog",WS_CHILD | WS_VISIBLE,62,50,175,30,Wea.hWnd,(HMENU)IDC_BUTTON1,Wea.hIns,0);

 return 0;
}


void ShowOpenFileDlg(WndEventArgs& Wea)
{
 static wchar_t szFilter[]=L"C Files (*.C),CPP Files (*.cpp)\0*.c;*.cpp\0\0";
 static wchar_t szTitleName[_MAX_FNAME+_MAX_EXT];
 static wchar_t szFileName[_MAX_PATH];
 wchar_t lpszBuffer[256];
 OPENFILENAME ofn;

 GetCurrentDirectory(256,lpszBuffer);
 memset(&ofn,0,sizeof(OPENFILENAME));
 wcscpy(szFileName,L"Form20.cpp");             // copied "Form20.cpp" to buffer
 ofn.lStructSize     = sizeof(OPENFILENAME);
 ofn.lpstrFilter     = szFilter;
 ofn.nMaxFile        = _MAX_PATH;
 ofn.nMaxFileTitle   = _MAX_FNAME+_MAX_EXT;
 ofn.lpstrInitialDir = lpszBuffer;
 ofn.lpstrDefExt     = L"cpp";
 ofn.hInstance       = GetModuleHandle(L"");
 ofn.hwndOwner       = Wea.hWnd;
 ofn.Flags           = OFN_HIDEREADONLY | OFN_CREATEPROMPT;
 ofn.lpstrFile       = szFileName;
 ofn.lpstrFileTitle  = szTitleName;
 GetOpenFileName(&ofn);
}


long fnWndProc_OnCommand(WndEventArgs& Wea)
{
 switch(LOWORD(Wea.wParam))
 {
    case IDC_BUTTON1:
         ShowOpenFileDlg(Wea);
         break;
 }

 return 0;
}


long fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 PostQuitMessage(0);
 return 0;
}



LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"Form20";
 WNDCLASSEX wc={0};
 MSG messages;
 HWND hWnd;

 wc.lpszClassName = szClassName;                 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof (WNDCLASSEX);         wc.hInstance     = hIns;
 wc.hCursor       = LoadCursor(NULL,IDC_ARROW);  wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,320,200,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


The part to take particular note of is my function ShowOpenFileDlg(). These are the pre-allocated buffers of which I am speaking ...

1
2
3
static wchar_t szFilter[]=L"C Files (*.C),CPP Files (*.cpp)\0*.c;*.cpp\0\0";
static wchar_t szTitleName[_MAX_FNAME+_MAX_EXT];
static wchar_t szFileName[_MAX_PATH];


In the top one the compiler will simply throw the string literal into the program's data segment, but the bottom two will get buffers allocated. When GetOpenFileName() returns, it will copy the chosen file into the szFileName buffer. Anything copied to that buffer beforehand (as I did) will show up in the textbox when the Open File Dialog Shows Itself.
Last edited on
Wow, thanks for all that.

I am avoiding using GetOpenFileName() because when using the flag OFN_ALLOWMULTISELECT , it uses a very outdated GUI.

I have another question, why does the extensions filter work when i set it to the OPENFILENAME structure directly but not when I send them to the CFileDialog constructor?

But I have got this resolved now using CFileDilog and the buffers you mentioned.
Thank you very much for your help.
Here's my working code:

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
vector<string>		CGUIUtility::openFileDialog(string strInitialDir, wstring strExtensionFilters, bool bAllowMultiSelect, string strDefaultFileName)
{
	wchar_t szInitialDirBuffer[_MAX_PATH];
	wchar_t szDefaultFileNameBuffer[_MAX_PATH];
	wchar_t szDefaultExtensionBuffer[256];

	wcscpy_s(szInitialDirBuffer, CStringUtility::convertStdStringToStdWString(strInitialDir).c_str());
	wcscpy_s(szDefaultFileNameBuffer, CStringUtility::convertStdStringToStdWString(strDefaultFileName).c_str());
	wcscpy_s(szDefaultExtensionBuffer, CStringUtility::convertStdStringToStdWString(CStringUtility::toLowerCase(CPathUtility::getFileExtension(strDefaultFileName))).c_str());

	CFileDialog dlg(
		TRUE,
		szDefaultExtensionBuffer,
		szDefaultFileNameBuffer,
		OFN_FILEMUSTEXIST | (bAllowMultiSelect ? OFN_ALLOWMULTISELECT : 0),
		NULL,
		NULL,
		0);
	dlg.m_ofn.lpstrInitialDir = szInitialDirBuffer;
	dlg.m_ofn.lpstrFilter = strExtensionFilters.c_str();

	vector<string> vecFilePaths;
	CString filename;
	if (dlg.DoModal() == IDOK)
	{
		// get the list of files
		POSITION pos = dlg.GetStartPosition();

		while (pos)
		{
			// one filename retreived per loop
			filename = dlg.GetNextPathName(pos);
			vecFilePaths.push_back(CStringUtility::convertCStringToStdString(filename));
		}
	}
	return vecFilePaths;
}

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
class CStringUtility
{
	static std::string						convertCStringToStdString(CString str)
	{
		CT2CA pszConvertedAnsiString(str);
		return std::string(pszConvertedAnsiString);
	}
	static std::wstring						convertStdStringToStdWString(std::string str)
	{
		int iLength = MultiByteToWideChar(0, 0, str.c_str(), str.length() + 1, 0, 0);
		WCHAR *wstr = new WCHAR[iLength + 1];
		MultiByteToWideChar(0, 0, str.c_str(), str.length() + 1, wstr, iLength + 1);
		wstr[iLength] = 0;
		std::wstring wstr2(wstr, iLength);
		delete wstr;
		return wstr2;
	}
	static std::string						convertStdWStringToStdString(std::wstring str)
	{
		int len;
		int slength = (int)str.length() + 1;
		len = WideCharToMultiByte(CP_ACP, 0, str.c_str(), slength, 0, 0, 0, 0);
		char* buf = new char[len];
		WideCharToMultiByte(CP_ACP, 0, str.c_str(), slength, buf, len, 0, 0);
		std::string r(buf);
		delete[] buf;
		return r;
	}
};
Glad its working now. Looks like you got the idea.

One thing to be careful of when using String Classes with C based objects such as OPENFILENAME, is that the OPENFILENAME menbers will be looking for pointers to buffers that will remain constant and valid throughout the scope of the object. If you execute something like this...

std::wstring strExtensionFilters = L"";

and then assign that to OPENFILENAME::lpstrFilter, then the buffer pointed to may be quite minimal due to a null string being assigned. If later in the same scope you assign another String Object to strExtensionFilters or any of the other buffers, the C++ String Class will very likely deallocate the original String and create a different one at a different address. Therefore, the address 1st assigned will be bad. In that case, an instant crash, corruption in your data segment, or general function failure are all about equally likely.

For this reason I typically will not use a String Class at all for such operations and will allocate static char buffers or use new or HeapAlloc() or whatever to get my memory. After all operations are complete I'll assign my results to a String object and go from there. Safer that way.
Last edited on
Very interesting, thanks a lot.

Tomorrow after work I will look at putting the filters parameter into a buffer before assigning the buffer to the OPENFILENAME struct.
I have solved that problem by using wmemcpy instead of wcsncpy, for the code where I was sending the filters to the function with null bytes inside it.
Of course after reading the wcsncpy function's description, it made sense not to use wcsncpy for this.

In fact I don't event need to use wmemcpy now, because I will be using std::string not std::wstring for my filters parameter, as I found out the CFileDialog constructor actually takes the filters with pipe characters inside it instead of null bytes, which I will prefer to use.
So it appears the CFileDialog constructor converts it to the null bytes syntax for the OPENFILENAME structure property.

So here is my latest working code!

Your help has been much appreciated :)

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
vector<string>		CGUIUtility::openFileDialog(string strInitialDir, string strExtensionFilters, bool bAllowMultiSelect, string strDefaultFileName)
{
	wchar_t szInitialDirBuffer[_MAX_PATH];
	wchar_t szDefaultFileNameBuffer[_MAX_FNAME + _MAX_EXT];
	wchar_t szDefaultExtensionBuffer[256];
	wchar_t szExtensionFilters[256];

	wcscpy_s(szInitialDirBuffer, CStringUtility::convertStdStringToStdWString(strInitialDir).c_str());
	wcscpy_s(szDefaultFileNameBuffer, CStringUtility::convertStdStringToStdWString(strDefaultFileName).c_str());
	wcscpy_s(szDefaultExtensionBuffer, CStringUtility::convertStdStringToStdWString(CStringUtility::toLowerCase(CPathUtility::getFileExtension(strDefaultFileName))).c_str());
	wcscpy_s(szExtensionFilters, CStringUtility::convertStdStringToStdWString(strExtensionFilters).c_str());

	CFileDialog dlg(
		TRUE,
		szDefaultExtensionBuffer,
		szDefaultFileNameBuffer,
		OFN_FILEMUSTEXIST | (bAllowMultiSelect ? OFN_ALLOWMULTISELECT : 0),
		szExtensionFilters,
		NULL,
		0);
	dlg.m_ofn.lpstrInitialDir = szInitialDirBuffer;

	vector<string> vecFilePaths;
	CString filename;
	if (dlg.DoModal() == IDOK)
	{
		// get the list of files
		POSITION pos = dlg.GetStartPosition();

		while (pos)
		{
			// one filename retreived per loop
			filename = dlg.GetNextPathName(pos);
			vecFilePaths.push_back(CStringUtility::convertCStringToStdString(filename));
		}
	}
	return vecFilePaths;
}
Topic archived. No new replies allowed.