[95] | 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 | }
|
---|