[ATL/WTL]_[初级]_[如何使用GetOpenFileName多选文件-根据文件名长度计算lpstrFile长度]


场景:

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


你可能感兴趣的:(C++,多选,getOpenFileName,设置缓存长度,lpstrFile)