打开文件对话框在xp和win7上的实现文件任意多选

作者:朱金灿

来源:http://blog.csdn.net/clever101

   

       在xp系统上进行文件多选,实际上其文件字符串数组的缓冲区是有限,并不能支持选择任意多个文件,为此以前我还写过一篇文章:使用CFileDialog的钩子函数解决对话框的多选问题。实际上这种做法在vista系统及win7系统上并不支持。那么如何在vista系统及win7系统支持打开文件对话框任意多选文件呢?我想到windows是一个基于com的系统,没准使用com接口能做到。事实上是这样的,需要使用一个叫IFileOpenDialog的接口类。下面是示例代码:



// Return the file system path for a given IShellItem.
static bool PathFromShellItem ( IShellItem* pItem, CString& sPath )
{
	HRESULT hr;
	LPOLESTR pwsz = NULL;

	hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pwsz );

	if ( FAILED(hr) )
		return false;

	sPath = pwsz;
	CoTaskMemFree ( pwsz );
	return true;
}

// Convert a pipe-separated list of filter strings into a vector of
// COMDLG_FILTERSPEC. The vector<CString> is needed to actually hold the strings
// that the COMDLG_FILTERSPEC structs will point to.
static bool BuildFilterSpecList (_U_STRINGorID szFilterList,
								 COMDLG_FILTERSPEC*& pVecFilter,int& nFilterNum )
{
	std::vector<CString> vecsFilterParts;
	CString sFilterList = szFilterList.m_lpstr;
	CString sToken;
	int nIdx = 0;

	// Split the passed-in filter list on pipe characters (MFC-style)
	for(;;)
	{
		sToken = sFilterList.Tokenize(_T("|"), nIdx );
		if ( sToken.IsEmpty() )
			break;

		vecsFilterParts.push_back ( sToken );
	}

	// There should be an even number of tokens in the string
	if ( vecsFilterParts.size() & 1 )
	{
		ATLASSERT(0);
		vecsFilterParts.pop_back();
	}
   
	if(vecsFilterParts.empty())
		return false;

    nFilterNum = vecsFilterParts.size()/2.0;
    pVecFilter = new COMDLG_FILTERSPEC[nFilterNum];

	// Use each pair of tokens for a COMDLG_FILTERSPEC struct.
	/*for (std::vector<CString>::size_type i = 0; i < vecsFilterParts.size(); i += 2 )*/
	for (std::vector<CString>::size_type i = 0; i <nFilterNum; i++)
	{
		USES_CONVERSION;
     
		int j = i*2;

		std::wstring strName = A2W(vecsFilterParts[j]);
        pVecFilter[i].pszName = new WCHAR[strName.length()+1];
        memset((void*)pVecFilter[i].pszName,'',(strName.length()+1)*sizeof(WCHAR));
        wcsncpy((wchar_t*)pVecFilter[i].pszName,strName.c_str(),strName.length());

		j = j+1;
		std::wstring strSpec = A2W(vecsFilterParts[j]);
		pVecFilter[i].pszSpec = new WCHAR[strSpec.length()+1];
		memset((void*)pVecFilter[i].pszSpec,'',(strSpec.length()+1)*sizeof(WCHAR));
		wcsncpy((wchar_t*)pVecFilter[i].pszSpec,strSpec.c_str(),strSpec.length());
	}
//	return !vecFilters.empty();
	return true;
}

void CMultiSelectDlg::OnBnClickedBtnVista()
{
	// TODO: 在此添加控件通知处理程序代码
	HRESULT hr;
	CComPtr<IFileOpenDialog> pDlg;
//	std::vector<CString> vecsFilterParts;
//	std::vector<COMDLG_FILTERSPEC> vecFilters;

	COMDLG_FILTERSPEC* pVecFilter = NULL;;
    int nFilterNum = 0;

//	std::vector<std::wstring> vecFilters;

	CString sDlgTitle = _T("Multi-selection File Open Dialog");
	CString sOKButtonLabel = _T("确定");
	CString sFilenameLabel = _T("文件名(N):");
	DWORD dwFlags = 0;

	// Create the file-open dialog COM object.
	hr = pDlg.CoCreateInstance( __uuidof(FileOpenDialog) );

	if ( FAILED(hr) )
		return;

	// Tell the dlg to load the state data associated with this GUID:
	// {7D5FE367-E148-4a96-B326-42EF237A3662}
	// This is not strictly necessary for our app (normally you'd wand loads
	// and saves to share the same state data) but I'm putting this in for the demo.
	static const GUID guidFileOpen = { 0x7D5FE367, 0xE148, 0x4A96, { 0xB3, 0x26, 0x42, 0xEF, 0x23, 0x7A, 0x36, 0x62 } };

	hr = pDlg->SetClientGuid ( guidFileOpen );

	// Call this helper function to convert a pipe-separated file spec list 
	// (like MFC uses) to a vector of COMDLG_FILTERSPEC.
	if ( BuildFilterSpecList(_T("Text files (*.txt)|*.txt|Executable files (*.exe;*.dll)|*.exe;*.dll|All files (*.*)|*.*|"),
		pVecFilter,nFilterNum))
		hr = pDlg->SetFileTypes(nFilterNum,pVecFilter);

	// Set some other properties of the dialog. It's not the end of the world if
	// any of these calls fail.
	 USES_CONVERSION; 
	hr = pDlg->SetTitle (A2W(sDlgTitle));
	hr = pDlg->SetOkButtonLabel(A2W(sOKButtonLabel));
	hr = pDlg->SetFileNameLabel(A2W(sFilenameLabel));

	// Set the multi-select option flag.
	hr = pDlg->GetOptions ( &dwFlags );
	hr = pDlg->SetOptions ( dwFlags | FOS_ALLOWMULTISELECT );

	// Set up our event listener.
//	CComObjectStackEx<CDlgEventHandler> cbk;

	// Show the dialog!
	hr = pDlg->Show ( m_hWnd );

	//if ( bAdvised )
	//	pDlg->Unadvise ( dwCookie );

	// Get the list of selected items and add each filename to the list ctrl.
	if ( SUCCEEDED(hr) )
	{
		CComPtr<IShellItemArray> pItemArray;

		hr = pDlg->GetResults ( &pItemArray );

		if ( SUCCEEDED(hr) )
		{
			DWORD cSelItems;
			hr = pItemArray->GetCount ( &cSelItems );

			if ( SUCCEEDED(hr) )
			{
				int nCount = 0;
				for ( DWORD j = 0; j < cSelItems; j++ )
				{
					CComPtr<IShellItem> pItem;
					hr = pItemArray->GetItemAt ( j, &pItem );
					if ( SUCCEEDED(hr) )
					{
						CString sPath;
						if ( PathFromShellItem ( pItem, sPath ) )
						{
							m_listbox.AddString(sPath);
							nCount++;
						}
					}
				}
				CString str;
				str.Format(_T("%u files selected"), nCount);
				m_static.SetWindowText(str);
			}
		}
	}
    
	for (int i  = 0;i<nFilterNum;i++)
	{
		delete []pVecFilter[i].pszName;
		delete []pVecFilter[i].pszSpec;
	}
	delete []pVecFilter;
}

 

        值得注意的是这个做法并不兼容xp系统,因此在使用哪种做法时需要你先对操作系统的版本进行判断。我专门写了一个例程供大家参考:VC文件多选对话框

 

参考文献:

 

1. WindowsVista for Developers——第六部分:新的文件对话框

 

2. IFileDialog使用指南

原文地址:https://www.cnblogs.com/lanzhi/p/6470137.html