今天领导问我一个问题:就是使用CFileDialog类在设置多选时选中的文件所放的文件缓冲区不知设置多大合适,设置小了DoModal返回为失败,通过CommDlgExtendedError函数获取错误码为FNERR_BUFFERTOOSMALL(即缓冲区太小),设置大了又浪费内存。(我们一次要选几百个文件,实在不知设置多大合适)。    我谈了我的思路:CFileDialog的数据成员m_ofn有一个数据成员为钩子函数指针,通过设置这个函数,可以勾取CFileDialog的相关消息,比如用户改变路径的消息,然后获取当前路径的文件个数,以此为依据来设置缓冲区的大小。领导不是很明白我的思路,他上网搜了搜,找到一种方法,就是通过派生CFileDialog类的方法来做,具体如下:    Multiple Selection in a File Dialog    上面的链接提到的方法确实可行。但是我也相信我的方法是可行的。下班后我研究了一下,写了下面一些代码:
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();
// =_AfxActivationWndProc(hwnd,uint,uint,long)
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)
{ if((pOfn->lpOFN)->lpstrFile)
HeapFree(GetProcessHeap(),
0,
(pOfn->lpOFN)->lpstrFile); (pOfn->lpOFN)->lpstrFile = (LPTSTR) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
cbLength);
                                              (pOfn->lpOFN)->nMaxFile = cbLength;
} nResult = TRUE;
                    break;
}
default:
break;
}
break;
}
default:
break;
}
return nResult;
}// 调用函数
void CMultiSelectDlg::OnButton1() 
{
    #define  NAMEBUF   1024 TCHAR szFilters[]= _T("MyType Files (*.doc)|*.doc||");        CFileDialog fileDlg(TRUE, _T("doc"), _T("*.doc"),
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT, szFilters);        fileDlg.m_ofn.lpstrFile= (LPTSTR) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
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)
{
int i =0;
} HeapFree(GetProcessHeap(),0,
(fileDlg.m_ofn.lpstrFile));
}     上面的代码我是参考MSDN上的代码实现的:如何处理在 Windows 中 FNERR_BUFFERTOOSMALL     我发现文件缓冲区够用时,即不进入if(cbLength>(pOfn->lpOFN)->nMaxFile)判断那段代码是正确的,但是当
进入if(cbLength>(pOfn->lpOFN)->nMaxFile)判断那段代码,INT_PTR ret = fileDlg.DoModal();返回为IDCANCEL,
通过CommDlgExtendedError函数获取错误码为FNERR_BUFFERTOOSMALL,我很纳闷,我不是重新分配了文件名缓冲区吗?
怎么还是FNERR_BUFFERTOOSMALL?(后来我试验,假如cbLength>(pOfn->lpOFN)->nMaxFile,我再分配一个很大的缓冲区,
错误依然为FNERR_BUFFERTOOSMALL),这到底是为什么呢?

解决方案 »

  1.   

         我的编译环境为Win XP sp2, VS C++ 2005 + sp1。
      

  2.   


        MBCS的。
      

  3.   

    你用的方法需要调用Unicode版本的文件对话框API
      

  4.   


        老大,我把程序改为unicode字符集的,还是不行啊!你是说unicode版本的MFC类都不行吗(如CFileDialog类)?只能调用Unicode版本的文件对话框API函数吗?如果这样,我觉得这算是MFC的bug吧。
      

  5.   


         老大,按照你的说法,我用Unicode版本的文件对话框API试了一下,还是FNERR_BUFFERTOOSMALL的错误。
    我的代码如下:
    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();
    // =_AfxActivationWndProc(hwnd,uint,uint,long)
    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)
    { if((pOfn->lpOFN)->lpstrFile)
    HeapFree(GetProcessHeap(),
    0,
    (pOfn->lpOFN)->lpstrFile); //LPTSTR lpsz;
    (pOfn->lpOFN)->lpstrFile = (LPTSTR) HeapAlloc(GetProcessHeap(),
    HEAP_ZERO_MEMORY,
    cbLength);
                             (pOfn->lpOFN)->nMaxFile = cbLength;
    } nResult = TRUE;
                        break;
    }
    default:
    break;
    }
    break;
    }
    default:
    break;
    }
    return nResult;
    }// 调用函数
    void CMultiSelectDlg::OnBnClickedButton3()
    {
    // TODO: 在此添加控件通知处理程序代码
    OPENFILENAME ofn;
    //在内存中开辟一块空间,存放用户选取的文件名
    //TCHAR szFile[NAMEBUF];//MAX_PATH是系统定义的一个宏值为260,Windows操作系统文件路径的最大长度
    ZeroMemory(&ofn,sizeof(ofn));//将这个结构体变量定义为0
    ofn.lStructSize = sizeof(ofn);//获取结构体的大小
    //ofn.lpstrFile = szFile;//szFile是一个字符数组,那么szFile这个名字就代表这个字符数组的首地址这是一个指针的应用,把接收选择文件名的数组的地址传递到GetOpenFileName函数中。
    ofn.lpstrFile= (LPTSTR) HeapAlloc(GetProcessHeap(),
    HEAP_ZERO_MEMORY,
    NAMEBUF);
    ofn.nMaxFile = NAMEBUF;
    ofn.lpstrFilter = TEXT("所有文件(*.doc)\0*.doc\0");
    ofn.lpfnHook = (LPOFNHOOKPROC)MyOFNHookProc; ofn.Flags = OFN_EXPLORER |OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK;//OFN_PATHMUSTEXIST路径必须存在,OFN_FILEMUSTEXIST文件必须存在
    if (!GetOpenFileName(&ofn))
    {
    DWORD dwCode = CommDlgExtendedError();
    if (FNERR_BUFFERTOOSMALL==dwCode)
    {
    int i =0;
    }
    HeapFree(GetProcessHeap(),0,
    (ofn.lpstrFile));
    return ;
    } HeapFree(GetProcessHeap(),0,
    (ofn.lpstrFile));
    }
      

  6.   


         兄弟,没见我写的这句吗?(后来我试验,假如cbLength>(pOfn->lpOFN)->nMaxFile,我再分配一个很大的缓冲区,错误依然为FNERR_BUFFERTOOSMALL)
      

  7.   

    不好意思,没仔细看,我这里没有编译环境,没办法帮你试验一下。只能以给你个思路,一个就是cbLengthg还是不够用,还有一种可能就是其他原因引起的。你可以把几百个文件名都改为统一长度,然后自己计算一下大概需要的缓冲区大小