// XBrowseForFolder.h Version 1.0 // // Author: Hans Dietrich // hdietrich2@hotmail.com // // Description: // XBrowseForFolder.cpp implements XBrowseForFolder(), a function that // wraps SHBrowseForFolder(). // // History // Version 1.0 - 2003 September 25 // - Initial public release // // This software is released into the public domain. You are free to use it // in any way you like. // // This software is provided "as is" with no expressed or implied warranty. // I accept no liability for any damage or loss of business that this software // may cause. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // ScreenToClientX - helper function static void ScreenToClientX(HWND hWnd, LPRECT lpRect) { assert(::IsWindow(hWnd)); ::ScreenToClient(hWnd, (LPPOINT)lpRect); ::ScreenToClient(hWnd, ((LPPOINT)lpRect)+1); } /////////////////////////////////////////////////////////////////////////////// // MoveWindowX - helper function static void MoveWindowX(HWND hWnd, RECT& rect, BOOL bRepaint) { assert(::IsWindow(hWnd)); ::MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, bRepaint); } /////////////////////////////////////////////////////////////////////////////// // XBROWSE_FOLDERINFO - structure to pass folder properties to the dialog struct XBROWSE_FOLDERINFO { LPCSTR lpszInitialFolder; LPCSTR lpszCaption; }; /////////////////////////////////////////////////////////////////////////////// // BrowseCallbackProc - SHBrowseForFolder callback function static int CALLBACK BrowseCallbackProc(HWND hwnd, // Window handle to the browse dialog box UINT uMsg, // Value identifying the event LPARAM lParam, // Value dependent upon the message LPARAM lpData) // Application-defined value that was // specified in the lParam member of the // BROWSEINFO structure { switch (uMsg) { case BFFM_INITIALIZED: // sent when the browse dialog box has finished initializing. { // remove context help button from dialog caption LONG lStyle = ::GetWindowLong(hwnd, GWL_STYLE); lStyle &= ~DS_CONTEXTHELP; ::SetWindowLong(hwnd, GWL_STYLE, lStyle); lStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE); lStyle &= ~WS_EX_CONTEXTHELP; ::SetWindowLong(hwnd, GWL_EXSTYLE, lStyle); // extract folder properties XBROWSE_FOLDERINFO *xinfo = (XBROWSE_FOLDERINFO *)lpData; // set caption if (xinfo->lpszCaption) ::SetWindowText(hwnd, xinfo->lpszCaption); // set initial directory if (xinfo->lpszInitialFolder) ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)xinfo->lpszInitialFolder); // find the folder tree and make dialog larger HWND hwndTree = FindWindowEx(hwnd, NULL, "SysTreeView32", NULL); if (hwndTree) { // make the dialog larger RECT rectDlg; ::GetWindowRect(hwnd, &rectDlg); rectDlg.right += 40; rectDlg.bottom += 30; MoveWindowX(hwnd, rectDlg, TRUE); ::GetClientRect(hwnd, &rectDlg); // move the Cancel button RECT rectCancel = {0}; HWND hwndCancel = ::GetDlgItem(hwnd, IDCANCEL); if (hwndCancel) ::GetWindowRect(hwndCancel, &rectCancel); ScreenToClientX(hwnd, &rectCancel); int w = rectCancel.right-rectCancel.left; int h = rectCancel.bottom-rectCancel.top; rectCancel.bottom = rectDlg.bottom - 5; rectCancel.top = rectCancel.bottom - h; rectCancel.right = rectDlg.right - 5; rectCancel.left = rectCancel.right - w; if (hwndCancel) MoveWindowX(hwndCancel, rectCancel, FALSE); // move the OK button RECT rectOK = {0}; HWND hwndOK = ::GetDlgItem(hwnd, IDOK); if (hwndOK) ::GetWindowRect(hwndOK, &rectOK); ScreenToClientX(hwnd, &rectOK); rectOK.bottom = rectDlg.bottom - 5; rectOK.top = rectOK.bottom - h; rectOK.right = rectCancel.left - 10; rectOK.left = rectOK.right - w; if (hwndOK) MoveWindowX(hwndOK, rectOK, FALSE); // expand the folder tree to fill the dialog RECT rectTree; ::GetWindowRect(hwndTree, &rectTree); ScreenToClientX(hwnd, &rectTree); rectTree.top = 5; rectTree.left= 5; rectTree.bottom = rectOK.top - 5; rectTree.right = rectDlg.right - 5; MoveWindowX(hwndTree, rectTree, FALSE); } else { ::OutputDebugString("ERROR - tree control not found.\n"); assert(hwndTree); } } break; case BFFM_SELCHANGED: // sent when the selection has changed { char szDir[_MAX_PATH] = { 0 }; // fail if non-filesystem BOOL bRet = SHGetPathFromIDList((LPITEMIDLIST) lParam, szDir); if (bRet) { // fail if folder not accessible if (_access(szDir, 00) != 0) { bRet = FALSE; } else { SHFILEINFO sfi; ::SHGetFileInfo((LPCTSTR)lParam, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_ATTRIBUTES); //::OutputDebugString("dwAttributes=0x%08X\n", sfi.dwAttributes); // fail if pidl is a link if (sfi.dwAttributes & SFGAO_LINK) { //::OutputDebugString("SFGAO_LINK\n"); bRet = FALSE; } } } // if invalid selection, disable the OK button if (!bRet) { ::EnableWindow(GetDlgItem(hwnd, IDOK), FALSE); } //::OutputDebugString("szDir=%s\n", szDir); } break; } return 0; } /////////////////////////////////////////////////////////////////////////////// // // XBrowseForFolder() // // Purpose: Invoke the SHBrowseForFolder API. If lpszInitialFolder is // supplied, it will be the folder initially selected in the tree // folder list. Otherwise, the initial folder will be set to the // current directory. The selected folder will be returned in // lpszBuf. // // Parameters: hWnd - handle to the owner window for the dialog // lpszCaption - text in title bar // lpszInitialFolder - initial folder in tree; if NULL, the initial // folder will be the current directory. // lpszBuf - buffer for the returned folder path // dwBufSize - size of lpszBuf in chars // // Returns: BOOL - TRUE = success; FALSE = user hit Cancel // BOOL XBrowseForFolder(HWND hWnd, LPCSTR lpszCaption, LPCSTR lpszInitialFolder, LPSTR lpszBuf, DWORD dwBufSize) { assert(lpszBuf); assert(dwBufSize >= MAX_PATH); if (lpszBuf == NULL || dwBufSize < MAX_PATH) return FALSE; lpszBuf[0] = '\0'; char szInitialPath[_MAX_PATH]; ZeroMemory(szInitialPath, sizeof(szInitialPath)); if (lpszInitialFolder && lpszInitialFolder[0] != '\0') strncpy(szInitialPath, lpszInitialFolder, sizeof(szInitialPath) - 1); else { // no initial folder, set to current directory ::GetCurrentDirectory(sizeof(szInitialPath) - 1, szInitialPath); } // set folder properties XBROWSE_FOLDERINFO xinfo; xinfo.lpszInitialFolder = szInitialPath; xinfo.lpszCaption = lpszCaption; BROWSEINFO bi; ZeroMemory(&bi, sizeof(BROWSEINFO)); bi.hwndOwner = hWnd; bi.ulFlags = BIF_RETURNONLYFSDIRS; // do NOT use BIF_NEWDIALOGSTYLE, // BIF_EDITBOX, or BIF_STATUSTEXT bi.lpfn = BrowseCallbackProc; bi.lParam = (LPARAM) &xinfo; LPITEMIDLIST pidl = SHBrowseForFolder(&bi); BOOL bRet = FALSE; if (pidl) { char szBuffer[_MAX_PATH]; szBuffer[0] = '\0'; if (SHGetPathFromIDList(pidl, szBuffer)) { ZeroMemory(lpszBuf, dwBufSize); strncpy(lpszBuf, szBuffer, dwBufSize-1); bRet = TRUE; } else { ::OutputDebugString("SHGetPathFromIDList failed\n"); } IMalloc *pMalloc = NULL; if (SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc) { pMalloc->Free(pidl); pMalloc->Release(); } } return bRet; }