龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

CFileDialog的钩子函数解决对话框的多选之DoModal问

时间:2014-05-21 15:52来源:网络整理 作者:网络 点击:
分享到:
前几天领导问我一个问题:就是使用CFileDialog类在设置多选时选中的文件所放的文件缓冲区不知设置多大合适,本文将详细介绍,需要的朋友可以参考下

   <P

   前几天领导问我一个问题:就是使用CFileDialog类在设置多选时选中的文件所放的文件缓冲区不知设置多大合适,设置小了DoModal返回为失败, 通过CommDlgExtendedError函数获取错误码为FNERR_BUFFERTOOSMALL(即缓冲区太小),设置大了又浪费内存。(我们 一次要选几百个文件,实在不知设置多大合适)。
   
         我谈了我的思路:CFileDialog的数据成员m_ofn有一个数据成员为钩子函数指针,通过设置这个函数,可以勾取CFileDialog的相关消 息,比如用户改变路径的消息,然后获取当前路径的文件个数,以此为依据来设置缓冲区的大小。领导不是很明白我的思路,他上网搜了搜,找到一种方法,就是通过派生CFileDialog类的方法来做,具体如下:

        Multiple Selection in a File Dialog

       上面的链接提到的方法确实可行。但是我也相信我的方法是可行的。下班后我上网搜索了一下,发现微软官网上有一个对此问题的解决办法,链接如下:

   如何处理在 Windows 中 FNERR_BUFFERTOOSMALL  

        该链接提供的代码适合的是Win 32的程序,并不适合MFC的程序,而且我建了一个Win32的程序测试该例子的代码时,发现一个问题,就是当选择的文件过多时,就是需要分配的缓冲区比较多时,使用链接中的HeapAlloc函数会出现错误,错误提示如下:

   

        因此要将链接中分配内存和释放的内存的HeapAlloc和HeapFree函数分别用C++的new和delete操作符替换。

         在微软官网提供的做法的基础上我摸索出用在MFC程序的做法,具体代码如下:

    代码如下:
   
   // 钩子函数
   UINT_PTR CALLBACK MyOFNHookProc( HWND hdlg, // handle to child dialog box
   UINT uiMsg, // message identifier
   WPARAM wParam, // message parameter
   LPARAM lParam // message parameter
   )
   {
   int nResult = FALSE;
   if (hdlg == NULL)
   return 0;
   #ifdef _DEBUG
   // from "_AfxCommDlgProc()" of the file "dlgcomm.cpp"
   _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
   if (pThreadState->m_pAlternateWndInit != NULL)
   pThreadState->m_pAlternateWndInit = NULL;
   #endif
   switch(uiMsg)
   {
   case WM_NOTIFY:
   {
   LPOFNOTIFY pOfn = (LPOFNOTIFY)lParam;
   switch(pOfn->hdr.code)
   {
   case CDN_SELCHANGE:
   {
   TCHAR dummy_buffer;
   // Get the required size for the 'files' buffer
   HWND hOwner = GetParent(hdlg);
   HWND hParent = GetParent(hOwner);
   UINT nfiles = CommDlg_OpenSave_GetSpec(hOwner, &dummy_buffer, 1);
   // Get the required size for the 'folder' buffer
   int cbLength = CommDlg_OpenSave_GetSpec(GetParent(hdlg), NULL, 0);
   cbLength += _MAX_PATH;
   if(cbLength>(pOfn->lpOFN)->nMaxFile)
   {
   delete (pOfn->lpOFN)->lpstrFile;
   (pOfn->lpOFN)->lpstrFile = new TCHAR[cbLength];
   ZeroMemory((pOfn->lpOFN)->lpstrFile,cbLength);
   (pOfn->lpOFN)->nMaxFile = cbLength;
   }
   nResult = TRUE;
   break;
   }
   default:
   break;
   }
   break;
   }
   default:
   break;
   }
   return nResult;
   }
   #define NAMEBUF 1024
   // 调用函数
   void CMultiSelectDlg::OnButton1()
   {
   m_listbox.ResetContent();
   m_static.SetWindowText(_T("0 files selected"));
   TCHAR szFilters[]= _T("MyType Files (*.doc)|*.doc||");
   // Create an Open dialog; the default file name extension is ".doc".
   CFileDialog fileDlg(TRUE, _T("doc"), _T("*.doc"),
   OFN_FILEMUSTEXIST | OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT, szFilters);
   fileDlg.m_ofn.lpstrFile=new TCHAR[NAMEBUF]; // 重新定义lpstrFile 缓冲大小
   memset(fileDlg.m_ofn.lpstrFile,0,NAMEBUF); // 初始化定义的缓冲
   fileDlg.m_ofn.nMaxFile = NAMEBUF; // 重定义nMaxFile
   fileDlg.m_ofn.lpfnHook = (LPOFNHOOKPROC)MyOFNHookProc;
   INT_PTR ret = fileDlg.DoModal();
   if (ret == IDOK)
   {
   int width = 0;
   CString str;
   CDC *pDC = m_listbox.GetDC();
   int saved = pDC->SaveDC();
   pDC->SelectObject(GetFont());
   UINT count = 0;
   POSITION pos = fileDlg.GetStartPosition();
   while (pos)
   {
   str = fileDlg.GetNextPathName(pos);
   m_listbox.AddString(str);
   CSize size(0, 0);
   size = pDC->GetTextExtent(str);
   width = width > size.cx ? width : size.cx;
   ++count;
   }
   pDC->RestoreDC(saved);
   ReleaseDC(pDC);
   m_listbox.SetHorizontalExtent(width + 5);
   str.Format(_T("%u files selected"), count);
   m_static.SetWindowText(str);
   }
   DWORD dwCode = CommDlgExtendedError();
   if (FNERR_BUFFERTOOSMALL==dwCode)
   {
   AfxMessageBox(_T("获取文件路径失败!"));
   }
   delete []fileDlg.m_ofn.lpstrFile;
   fileDlg.m_ofn.lpstrFile = NULL;
   }

   

   

       另外使用钩子函数的一个严重缺点是程序必须使用Unicode字符集进行编译,使用多字节字符集编译程序执行后FNERR_BUFFERTOOSMALL的错误(这一点已经测试过,我比较难以理解的是为何在这一点上微软不予支持多字节程序)。我的测试环境为: VS C++ 2005 + sp1,Win XP + sp3,unicode字符集。

   

精彩图集

赞助商链接