1 | // XBrowseForFolder.h Version 1.0
|
---|
2 | //
|
---|
3 | // Author: Hans Dietrich
|
---|
4 | // hdietrich2@hotmail.com
|
---|
5 | //
|
---|
6 | // Description:
|
---|
7 | // XBrowseForFolder.cpp implements XBrowseForFolder(), a function that
|
---|
8 | // wraps SHBrowseForFolder().
|
---|
9 | //
|
---|
10 | // History
|
---|
11 | // Version 1.0 - 2003 September 25
|
---|
12 | // - Initial public release
|
---|
13 | //
|
---|
14 | // This software is released into the public domain. You are free to use it
|
---|
15 | // in any way you like.
|
---|
16 | //
|
---|
17 | // This software is provided "as is" with no expressed or implied warranty.
|
---|
18 | // I accept no liability for any damage or loss of business that this software
|
---|
19 | // may cause.
|
---|
20 | //
|
---|
21 | ///////////////////////////////////////////////////////////////////////////////
|
---|
22 |
|
---|
23 | #include <assert.h>
|
---|
24 | #include <io.h>
|
---|
25 | #include <shlobj.h>
|
---|
26 | #include <shellapi.h>
|
---|
27 |
|
---|
28 | ///////////////////////////////////////////////////////////////////////////////
|
---|
29 | // ScreenToClientX - helper function
|
---|
30 | static void ScreenToClientX(HWND hWnd, LPRECT lpRect)
|
---|
31 | {
|
---|
32 | assert(::IsWindow(hWnd));
|
---|
33 | ::ScreenToClient(hWnd, (LPPOINT)lpRect);
|
---|
34 | ::ScreenToClient(hWnd, ((LPPOINT)lpRect)+1);
|
---|
35 | }
|
---|
36 |
|
---|
37 | ///////////////////////////////////////////////////////////////////////////////
|
---|
38 | // MoveWindowX - helper function
|
---|
39 | static void MoveWindowX(HWND hWnd, RECT& rect, BOOL bRepaint)
|
---|
40 | {
|
---|
41 | assert(::IsWindow(hWnd));
|
---|
42 | ::MoveWindow(hWnd, rect.left, rect.top,
|
---|
43 | rect.right-rect.left, rect.bottom-rect.top, bRepaint);
|
---|
44 | }
|
---|
45 |
|
---|
46 | ///////////////////////////////////////////////////////////////////////////////
|
---|
47 | // XBROWSE_FOLDERINFO - structure to pass folder properties to the dialog
|
---|
48 | struct XBROWSE_FOLDERINFO
|
---|
49 | {
|
---|
50 | LPCSTR lpszInitialFolder;
|
---|
51 | LPCSTR lpszCaption;
|
---|
52 | };
|
---|
53 |
|
---|
54 | ///////////////////////////////////////////////////////////////////////////////
|
---|
55 | // BrowseCallbackProc - SHBrowseForFolder callback function
|
---|
56 | static int CALLBACK BrowseCallbackProc(HWND hwnd, // Window handle to the browse dialog box
|
---|
57 | UINT uMsg, // Value identifying the event
|
---|
58 | LPARAM lParam, // Value dependent upon the message
|
---|
59 | LPARAM lpData) // Application-defined value that was
|
---|
60 | // specified in the lParam member of the
|
---|
61 | // BROWSEINFO structure
|
---|
62 | {
|
---|
63 | switch (uMsg)
|
---|
64 | {
|
---|
65 | case BFFM_INITIALIZED: // sent when the browse dialog box has finished initializing.
|
---|
66 | {
|
---|
67 | // remove context help button from dialog caption
|
---|
68 | LONG lStyle = ::GetWindowLong(hwnd, GWL_STYLE);
|
---|
69 | lStyle &= ~DS_CONTEXTHELP;
|
---|
70 | ::SetWindowLong(hwnd, GWL_STYLE, lStyle);
|
---|
71 | lStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
|
---|
72 | lStyle &= ~WS_EX_CONTEXTHELP;
|
---|
73 | ::SetWindowLong(hwnd, GWL_EXSTYLE, lStyle);
|
---|
74 |
|
---|
75 | // extract folder properties
|
---|
76 | XBROWSE_FOLDERINFO *xinfo = (XBROWSE_FOLDERINFO *)lpData;
|
---|
77 | // set caption
|
---|
78 | if (xinfo->lpszCaption)
|
---|
79 | ::SetWindowText(hwnd, xinfo->lpszCaption);
|
---|
80 | // set initial directory
|
---|
81 | if (xinfo->lpszInitialFolder)
|
---|
82 | ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)xinfo->lpszInitialFolder);
|
---|
83 |
|
---|
84 | // find the folder tree and make dialog larger
|
---|
85 | HWND hwndTree = FindWindowEx(hwnd, NULL, "SysTreeView32", NULL);
|
---|
86 | if (hwndTree)
|
---|
87 | {
|
---|
88 | // make the dialog larger
|
---|
89 | RECT rectDlg;
|
---|
90 | ::GetWindowRect(hwnd, &rectDlg);
|
---|
91 | rectDlg.right += 40;
|
---|
92 | rectDlg.bottom += 30;
|
---|
93 | MoveWindowX(hwnd, rectDlg, TRUE);
|
---|
94 | ::GetClientRect(hwnd, &rectDlg);
|
---|
95 |
|
---|
96 | // move the Cancel button
|
---|
97 | RECT rectCancel = {0};
|
---|
98 | HWND hwndCancel = ::GetDlgItem(hwnd, IDCANCEL);
|
---|
99 | if (hwndCancel)
|
---|
100 | ::GetWindowRect(hwndCancel, &rectCancel);
|
---|
101 | ScreenToClientX(hwnd, &rectCancel);
|
---|
102 | int w = rectCancel.right-rectCancel.left;
|
---|
103 | int h = rectCancel.bottom-rectCancel.top;
|
---|
104 | rectCancel.bottom = rectDlg.bottom - 5;
|
---|
105 | rectCancel.top = rectCancel.bottom - h;
|
---|
106 | rectCancel.right = rectDlg.right - 5;
|
---|
107 | rectCancel.left = rectCancel.right - w;
|
---|
108 | if (hwndCancel)
|
---|
109 | MoveWindowX(hwndCancel, rectCancel, FALSE);
|
---|
110 |
|
---|
111 | // move the OK button
|
---|
112 | RECT rectOK = {0};
|
---|
113 | HWND hwndOK = ::GetDlgItem(hwnd, IDOK);
|
---|
114 | if (hwndOK)
|
---|
115 | ::GetWindowRect(hwndOK, &rectOK);
|
---|
116 | ScreenToClientX(hwnd, &rectOK);
|
---|
117 | rectOK.bottom = rectDlg.bottom - 5;
|
---|
118 | rectOK.top = rectOK.bottom - h;
|
---|
119 | rectOK.right = rectCancel.left - 10;
|
---|
120 | rectOK.left = rectOK.right - w;
|
---|
121 | if (hwndOK)
|
---|
122 | MoveWindowX(hwndOK, rectOK, FALSE);
|
---|
123 |
|
---|
124 | // expand the folder tree to fill the dialog
|
---|
125 | RECT rectTree;
|
---|
126 | ::GetWindowRect(hwndTree, &rectTree);
|
---|
127 | ScreenToClientX(hwnd, &rectTree);
|
---|
128 | rectTree.top = 5;
|
---|
129 | rectTree.left= 5;
|
---|
130 | rectTree.bottom = rectOK.top - 5;
|
---|
131 | rectTree.right = rectDlg.right - 5;
|
---|
132 | MoveWindowX(hwndTree, rectTree, FALSE);
|
---|
133 | }
|
---|
134 | else
|
---|
135 | {
|
---|
136 | ::OutputDebugString("ERROR - tree control not found.\n");
|
---|
137 | assert(hwndTree);
|
---|
138 | }
|
---|
139 | }
|
---|
140 | break;
|
---|
141 |
|
---|
142 | case BFFM_SELCHANGED: // sent when the selection has changed
|
---|
143 | {
|
---|
144 | char szDir[_MAX_PATH] = { 0 };
|
---|
145 |
|
---|
146 | // fail if non-filesystem
|
---|
147 | BOOL bRet = SHGetPathFromIDList((LPITEMIDLIST) lParam, szDir);
|
---|
148 | if (bRet)
|
---|
149 | {
|
---|
150 | // fail if folder not accessible
|
---|
151 | if (_access(szDir, 00) != 0)
|
---|
152 | {
|
---|
153 | bRet = FALSE;
|
---|
154 | }
|
---|
155 | else
|
---|
156 | {
|
---|
157 | SHFILEINFO sfi;
|
---|
158 | ::SHGetFileInfo((LPCTSTR)lParam, 0, &sfi, sizeof(sfi),
|
---|
159 | SHGFI_PIDL | SHGFI_ATTRIBUTES);
|
---|
160 | //::OutputDebugString("dwAttributes=0x%08X\n", sfi.dwAttributes);
|
---|
161 |
|
---|
162 | // fail if pidl is a link
|
---|
163 | if (sfi.dwAttributes & SFGAO_LINK)
|
---|
164 | {
|
---|
165 | //::OutputDebugString("SFGAO_LINK\n");
|
---|
166 | bRet = FALSE;
|
---|
167 | }
|
---|
168 | }
|
---|
169 | }
|
---|
170 |
|
---|
171 | // if invalid selection, disable the OK button
|
---|
172 | if (!bRet)
|
---|
173 | {
|
---|
174 | ::EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
|
---|
175 | }
|
---|
176 |
|
---|
177 | //::OutputDebugString("szDir=%s\n", szDir);
|
---|
178 | }
|
---|
179 | break;
|
---|
180 | }
|
---|
181 |
|
---|
182 | return 0;
|
---|
183 | }
|
---|
184 |
|
---|
185 | ///////////////////////////////////////////////////////////////////////////////
|
---|
186 | //
|
---|
187 | // XBrowseForFolder()
|
---|
188 | //
|
---|
189 | // Purpose: Invoke the SHBrowseForFolder API. If lpszInitialFolder is
|
---|
190 | // supplied, it will be the folder initially selected in the tree
|
---|
191 | // folder list. Otherwise, the initial folder will be set to the
|
---|
192 | // current directory. The selected folder will be returned in
|
---|
193 | // lpszBuf.
|
---|
194 | //
|
---|
195 | // Parameters: hWnd - handle to the owner window for the dialog
|
---|
196 | // lpszCaption - text in title bar
|
---|
197 | // lpszInitialFolder - initial folder in tree; if NULL, the initial
|
---|
198 | // folder will be the current directory.
|
---|
199 | // lpszBuf - buffer for the returned folder path
|
---|
200 | // dwBufSize - size of lpszBuf in chars
|
---|
201 | //
|
---|
202 | // Returns: BOOL - TRUE = success; FALSE = user hit Cancel
|
---|
203 | //
|
---|
204 | BOOL XBrowseForFolder(HWND hWnd,
|
---|
205 | LPCSTR lpszCaption,
|
---|
206 | LPCSTR lpszInitialFolder,
|
---|
207 | LPSTR lpszBuf,
|
---|
208 | DWORD dwBufSize)
|
---|
209 | {
|
---|
210 | assert(lpszBuf);
|
---|
211 | assert(dwBufSize >= MAX_PATH);
|
---|
212 |
|
---|
213 | if (lpszBuf == NULL || dwBufSize < MAX_PATH)
|
---|
214 | return FALSE;
|
---|
215 |
|
---|
216 | lpszBuf[0] = '\0';
|
---|
217 |
|
---|
218 | char szInitialPath[_MAX_PATH];
|
---|
219 | ZeroMemory(szInitialPath, sizeof(szInitialPath));
|
---|
220 |
|
---|
221 | if (lpszInitialFolder && lpszInitialFolder[0] != '\0')
|
---|
222 | strncpy(szInitialPath, lpszInitialFolder, sizeof(szInitialPath) - 1);
|
---|
223 | else
|
---|
224 | {
|
---|
225 | // no initial folder, set to current directory
|
---|
226 | ::GetCurrentDirectory(sizeof(szInitialPath) - 1, szInitialPath);
|
---|
227 | }
|
---|
228 |
|
---|
229 | // set folder properties
|
---|
230 | XBROWSE_FOLDERINFO xinfo;
|
---|
231 | xinfo.lpszInitialFolder = szInitialPath;
|
---|
232 | xinfo.lpszCaption = lpszCaption;
|
---|
233 |
|
---|
234 | BROWSEINFO bi;
|
---|
235 | ZeroMemory(&bi, sizeof(BROWSEINFO));
|
---|
236 |
|
---|
237 | bi.hwndOwner = hWnd;
|
---|
238 | bi.ulFlags = BIF_RETURNONLYFSDIRS; // do NOT use BIF_NEWDIALOGSTYLE,
|
---|
239 | // BIF_EDITBOX, or BIF_STATUSTEXT
|
---|
240 | bi.lpfn = BrowseCallbackProc;
|
---|
241 | bi.lParam = (LPARAM) &xinfo;
|
---|
242 |
|
---|
243 | LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
|
---|
244 |
|
---|
245 | BOOL bRet = FALSE;
|
---|
246 |
|
---|
247 | if (pidl)
|
---|
248 | {
|
---|
249 | char szBuffer[_MAX_PATH];
|
---|
250 | szBuffer[0] = '\0';
|
---|
251 |
|
---|
252 | if (SHGetPathFromIDList(pidl, szBuffer))
|
---|
253 | {
|
---|
254 | ZeroMemory(lpszBuf, dwBufSize);
|
---|
255 | strncpy(lpszBuf, szBuffer, dwBufSize-1);
|
---|
256 | bRet = TRUE;
|
---|
257 | }
|
---|
258 | else
|
---|
259 | {
|
---|
260 | ::OutputDebugString("SHGetPathFromIDList failed\n");
|
---|
261 | }
|
---|
262 |
|
---|
263 | IMalloc *pMalloc = NULL;
|
---|
264 | if (SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc)
|
---|
265 | {
|
---|
266 | pMalloc->Free(pidl);
|
---|
267 | pMalloc->Release();
|
---|
268 | }
|
---|
269 | }
|
---|
270 |
|
---|
271 | return bRet;
|
---|
272 | }
|
---|