關鍵字: GetOpenFileName CFileDialog SDK Folder我一直不喜歡預設的檔案夾選擇. 相反地,我喜歡MFC中的CFileDialog這種對話方塊.如何使用SDK,實現CFileDialog對話方塊選擇檔案夾, 是我要解釋和舉例的.1. CFileDialog 的實現. CFileDialog 是MFC的類,實現了定製Open, Save, 這些Common Dialog 的定製. 其內部一般是使GetOpenFileName實現的. CFileDialog 中有一個 m_ofn 結構, 是 OPENFILENAME 類型. 他的詳細實現在 dlgfile.cpp 中. 有趣的是,在我的 vc9 中, CFileDialog 對Vista有特殊處置, 如果系統是Vista,而且設定了VistaStyle, 則CFileDialog使用 IFileDialog 介面而不是 GetOpenFileName,這個和我們關係不大,而且我也不打算把簡單問題複雜化. 一般而言, MFC 設定 m_ofn 的 flag 中包括了 OFN_ENABLEHOOK | OFN_EXPLORER, 這樣可以指定HookProc函數指標, 他可以接受各種訊息.MFC的是這樣寫的:m_ofn.lpfnHook = (COMMDLGPROC)_AfxCommDlgProc; 這是預設的Common Dialog 處理函數.2. 我們的實現. 前面說到這裡,我們基本上已經知道如何?選擇對話方塊的要求了. (1).首先設定 m_ofn.lpstrFile WCHAR wcsFolderPath[MAX_PATH] = {0}; ::wcscpy( wcsFolderPath, L"*..*" ); m_ofn.lpstrFile = wcsFolderPath;m_ofn.nMaxFile = sizeof( wcsFolderPath ) /sizeof(WCHAR); "*..*" 將實現屏蔽所有檔案,只顯示對話方塊的效果(2).設定 m_ofn.hInstance = (HMODULE)GetCurrentProcess();//不要使用NULL,可能造成無法定製的問題(3).設定HookProc: m_ofn.lpfnHook = (COMMDLGPROC)MyFolderProc; 做完這些事,然後你在MyFolderProc中嘗試截獲 WM_COMMAND IDOK, IDCANCEL 這些訊息,結果是: 你什麼都截獲不到.很遺憾,HookProc處理的是一個類似"偽"對話方塊的一個控制代碼, 使用spy++, 你會發現,真正處理訊息的是HookProc中對話方塊控制代碼的父控制代碼.所以我們應該這麼做:
- LONG g_lOriWndProc = NULL;
- BOOL g_bReplaced = FALSE;
- UINT_PTR static __stdcall MyFolderProc( HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam )
- {
- if( hdlg != NULL && g_bReplaced == FALSE )
- {
- WCHAR wcsClassName1[MAX_PATH];
- WCHAR wcsClassName2[MAX_PATH];
- HWND hParent = ::GetParent( hdlg );
- ::GetClassNameW( hParent, wcsClassName1 , sizeofWCAR(wcsClassName1) );
- ::GetClassNameW( hdlg , wcsClassName2 , sizeofWCAR(wcsClassName2) );
- if( 0== ::wcscmp( wcsClassName1, wcsClassName2 ) )
- {
- g_bReplaced = TRUE;
- g_lOriWndProc = ::SetWindowLongW( ::GetParent( hdlg ), GWL_WNDPROC , (LONG)_WndProc );
- }
- }
- if( uiMsg == WM_NOTIFY )
- {
- LPOFNOTIFY lpOfNotify = (LPOFNOTIFY)lParam;
- if( lpOfNotify->hdr.code == CDN_FOLDERCHANGE )
- {
- // your codes here
- }
- }
- }
- return FALSE;
- }
- LRESULT static __stdcall _WndProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
- {
- if( uMsg == WM_COMMAND )
- {
- if( wParam == IDOK )
- {
- //Your codes
- }
- }
- return CallWndProc( (WNDPROC) g_lOriWndProc , hwnd, uMsg, wParam ,lParam );
- }
上面這段代碼揭示了如何定製Common Dialog 中的FileDialog. 你可以截獲IDOK.
但是有一個問題,你選擇一個檔案夾,然後點擊按鈕"開啟", 你會發現他預設開啟這個對話方塊,而不會關閉對話方塊,返回對話方塊所選擇的檔案夾路徑. 如何解決這個問題? 很容易, 我們在截獲IDOK時, 發送訊息 CDM_GETFOLDERPATH , 擷取檔案夾路徑,寫入目標緩衝, 然後發送訊息 IDCANCEL,對話方塊就會關閉了.我就是如此實現的. 我把GetOpenFileName封裝成了一個簡單的類,設定一個_bOK, 來標識究竟是單擊了ok,還是真的單擊了cancel (因為我最後發送了IDCANCEL訊息,如果不設定_bOK,無法辨別到底是OK,還是cancel ).網上有CFolderDialog這個簡單的類,但是是MFC的,對我來說還是如鯁在喉, 所以在這裡用SDK的方法實現之.希望大家對Windows 有更深的理解.