使用GetOpenFileName 选择文件夹
关键字: GetOpenFileName CFileDialog SDK Folder
上面这段代码揭示了如何定制Common Dialog 中的FileDialog. 你可以截获IDOK.
我一直不喜欢默认的文件夹选择. 相反地,我喜欢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 );
- }
但是有一个问题,你选择一个文件夹,然后点击按钮"打开", 你会发现他默认打开这个对话框,而不会关闭对话框,返回对话框所选择的文件夹路径.
如何解决这个问题?
很容易, 我们在截获IDOK时, 发送消息 CDM_GETFOLDERPATH , 获取文件夹路径,写入目标缓存, 然后发送消息 IDCANCEL,对话框就会关闭了.
我就是如此实现的.
我把GetOpenFileName封装成了一个简单的类,设置一个_bOK, 来标识究竟是单击了ok,还是真的单击了cancel (因为我最后发送了IDCANCEL消息,如果不设置_bOK,无法辨别到底是OK,还是cancel ).
网上有CFolderDialog这个简单的类,但是是MFC的,对我来说还是如鲠在喉, 所以在这里用SDK的方法实现之.
希望大家对Windows 有更深的理解.