场景:
1. 使用GetOpenFileName 时, 需要预先自定义lpstrFile的长度比如,buf[1024], 但是如果选择的文件过多怎么办?总不能创建一个超大的内存空间吧,
如果选择少时又浪费内存。
2. 微软的MSDN的坏处就是不提供实际的例子,而在别的地方提供,难道他们没遇到这类普通的问题?
3. 这里stackoverflow提供了一个微软使用lpfnHook 的例子来解决这个问题,这个例子对于unicode是有问题的,计算长度没有x2. 这个bug困扰了我半天。
找这个解决方案也找了半天。
static unsigned int CALLBACK DialogHook(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
static HWND hwndParentDialog;
LPOFNOTIFY lpofn;
int cbLength;
static LPTSTR lpsz = NULL;
static int LastLen;
switch (uMsg)
{
case WM_INITDIALOG:
// You need to use a copy of the OPENFILENAME struct used to
// create this dialog. You can store a pointer to the
// OPENFILENAME struct in the ofn.lCustData so you can
// retrieve it here in the lParam. Once you have it, you
// need to hang on to it. Using window properties provides a
// good thread safe solution to using a global variable.
if(!SetProp(GetParent(hwnd), L"OFN", (void *) lParam))
MessageBox(NULL, L"SET PRop Failed", L"ERROR", MB_OK);
return (0);
case WM_COMMAND:
{
OutputDebugString(L"command\n");
}
break;
case WM_NOTIFY:
// The OFNOTIFY struct is passed in the lParam of this
// message.
lpofn = (LPOFNOTIFY) lParam;
switch (lpofn->hdr.code)
{
case CDN_SELCHANGE:
LPOPENFILENAME lpofn;
cbLength = CommDlg_OpenSave_GetSpec(GetParent(hwnd), NULL, 0);
cbLength += _MAX_PATH;
// The OFN struct is stored in a property of dialog window
lpofn = (LPOPENFILENAME) GetProp(GetParent(hwnd),
L"OFN");
if (lpofn->nMaxFile < cbLength)
{
// Free the previously allocated buffer.
if(lpsz)
HeapFree(GetProcessHeap(),
0,
lpsz);
// Allocate a new buffer, 注意要乘以2
lpsz = (LPTSTR) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
cbLength*2);
if (lpsz)
{
lpofn->lpstrFile = lpsz;
lpofn->nMaxFile = cbLength;
}
}
break;
}
return (0);
case WM_DESTROY:
// Also need to free the property with the OPENFILENAME
// struct.
RemoveProp(GetParent(hwnd), L"OFN");
return (0);
}
return (0);
}
void OpenSelectFileWindow(std::vector<std::wstring>& file_paths,
const wchar_t* filter,const wchar_t* title)
{
OPENFILENAME ofn = { sizeof ofn };
wchar_t file[1024];
file[0] = '\0';
ofn.lpstrFile = file;
ofn.lpstrFilter = filter;
ofn.nMaxFile = 1024;
ofn.lpstrTitle = title;
ofn.hwndOwner = m_hWnd;
ofn.lpfnHook = DialogHook;
ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_ENABLEHOOK;
BOOL res = GetOpenFileName(&ofn);
wchar_t *temp_buf = ofn.lpstrFile;
if(res!=0)
{
size_t len = lstrlen(temp_buf);
//文件夹
std::wstring dir;
dir.append(temp_buf,len);
wchar_t *pos = temp_buf+len;
while(*(++pos))
{
len = lstrlen(pos);
std::wstring path;
path.append(dir).append(L"\\").append(pos,len);
file_paths.push_back(path);
pos+=len;
}
if(!file_paths.size())
{
//只有一个文件时,不会出现目录和文件名分开的情况.
file_paths.push_back(dir);
}
}
}
注意: 稍微不负责任点的初级程序员估计会申请超大空间解决吧。
参考:
http://stackoverflow.com/questions/656655/getopenfilename-with-ofn-allowmultiselect-flag-set?rq=1
https://support.microsoft.com/en-us/kb/131462