程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言基礎知識 >> CFileDialog的鉤子函數解決對話框的多選之DoModal問題

CFileDialog的鉤子函數解決對話框的多選之DoModal問題

編輯:C語言基礎知識

前幾天領導問我一個問題:就是使用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字符集。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved