使用CFileDialog的钩子函数解决对话框的多选问题

作者:朱金灿

来源:http://www.cnblogs.com/clever101

 

前几天领导问我一个问题:就是使用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(
00);
            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字符集。

 

 

 

 

posted @ 2011-09-10 22:31  是金子就会灿烂  阅读(2424)  评论(0编辑  收藏  举报