/*++ Copyright (c) 1990-1998, Microsoft Corporation All rights reserved. Module Name: fileopen.c Abstract: This module implements the Win32 fileopen dialogs. Revision History: --*/ // precompiled headers #include "precomp.h" #pragma hdrstop #include "fileopen.h" #include "util.h" // // Constant Declarations. // #define WNTYPE_DRIVE 1 #define MIN_DEFEXT_LEN 4 #define BMPHIOFFSET 9 // // hbmpDirs array index values. // Note: Two copies: for standard background, and hilite. // Relative order is important. // #define OPENDIRBMP 0 #define CURDIRBMP 1 #define STDDIRBMP 2 #define FLOPPYBMP 3 #define HARDDRVBMP 4 #define CDDRVBMP 5 #define NETDRVBMP 6 #define RAMDRVBMP 7 #define REMDRVBMP 8 // // If the following disktype is passed to AddDisk, then bTmp will be // set to true in the DISKINFO structure (if the disk is new). // #define TMPNETDRV 9 #define MAXDOSFILENAMELEN (12 + 1) // 8.3 filename + 1 for NULL // // Maximum number of filters on one filter line. // #define MAXFILTERS 36 // // File exclusion bits (don't show files of these types). // #define EXCLBITS (FILE_ATTRIBUTE_HIDDEN) // // Global Variables. // // // Caching drive list. // extern DWORD dwNumDisks; extern OFN_DISKINFO gaDiskInfo[MAX_DISKS]; extern TCHAR g_szInitialCurDir[MAX_PATH]; DWORD dwNumDlgs = 0; // // Used to update the dialogs after coming back from the net dlg button. // BOOL bGetNetDrivesSync = FALSE; LPTSTR lpNetDriveSync = NULL; BOOL bNetworkInstalled = TRUE; // // Following array is used to send messages to all dialog box threads // that have requested enumeration updating from the worker // thread. The worker thread sends off a message to each slot // in the array that is non-NULL. // HWND gahDlg[MAX_THREADS]; // // Strings for Filter Parsing. // const static TCHAR szSemiColonSpaceTab[] = TEXT("; \t"); const static TCHAR szSemiColonTab[] = TEXT(";\t"); // // For WNet apis. // HANDLE hLNDThread = NULL; WNDPROC lpLBProc = NULL; WNDPROC lpOKProc = NULL; // // Drive/Dir bitmap dimensions. // LONG dxDirDrive = 0; LONG dyDirDrive = 0; // // REARCHITECT: This needs to be on a per dialog basis for multi-threaded apps. // WORD wNoRedraw = 0; UINT msgWOWDIRCHANGE; UINT msgLBCHANGEA; UINT msgSHAREVIOLATIONA; UINT msgFILEOKA; UINT msgLBCHANGEW; UINT msgSHAREVIOLATIONW; UINT msgFILEOKW; BOOL bInChildDlg; BOOL bFirstTime; BOOL bInitializing; // // Used by the worker thread to enumerate network disk resources. // extern DWORD cbNetEnumBuf; extern LPTSTR gpcNetEnumBuf; // // List Net Drives global variables. // extern HANDLE hLNDEvent; BOOL bLNDExit = FALSE; extern CRITICAL_SECTION g_csLocal; extern CRITICAL_SECTION g_csNetThread; extern DWORD g_tlsiCurDlg; extern HDC hdcMemory; extern HBITMAP hbmpOrigMemBmp; HBITMAP hbmpDirDrive = HNULL; // // Static Declarations. // static WORD cLock = 0; // // Not valid RGB color. // static DWORD rgbWindowColor = 0xFF000000; static DWORD rgbHiliteColor = 0xFF000000; static DWORD rgbWindowText = 0xFF000000; static DWORD rgbHiliteText = 0xFF000000; static DWORD rgbGrayText = 0xFF000000; static DWORD rgbDDWindow = 0xFF000000; static DWORD rgbDDHilite = 0xFF000000; TCHAR szCaption[TOOLONGLIMIT + WARNINGMSGLENGTH]; TCHAR szWarning[TOOLONGLIMIT + WARNINGMSGLENGTH]; LPOFNHOOKPROC glpfnFileHook = 0; // // REARCHITECT: // Of course, in the case where there is a multi-threaded process // that has > 1 threads simultaneously calling GetFileOpen, the // following globals may cause problems. // static LONG dyItem = 0; static LONG dyText; static BOOL bChangeDir = FALSE; static BOOL bCasePreserved; // // Used for formatting long unc names (ex. banyan). // static DWORD dwAveCharPerLine = 10; // // Context Help IDs. // const static DWORD aFileOpenHelpIDs[] = { edt1, IDH_OPEN_FILENAME, stc3, IDH_OPEN_FILENAME, lst1, IDH_OPEN_FILENAME, stc1, IDH_OPEN_PATH, lst2, IDH_OPEN_PATH, stc2, IDH_OPEN_FILETYPE, cmb1, IDH_OPEN_FILETYPE, stc4, IDH_OPEN_DRIVES, cmb2, IDH_OPEN_DRIVES, chx1, IDH_OPEN_READONLY, pshHelp, IDH_HELP, psh14, IDH_PRINT_NETWORK, 0, 0 }; const static DWORD aFileSaveHelpIDs[] = { edt1, IDH_OPEN_FILENAME, stc3, IDH_OPEN_FILENAME, lst1, IDH_OPEN_FILENAME, stc1, IDH_OPEN_PATH, lst2, IDH_OPEN_PATH, stc2, IDH_SAVE_FILETYPE, cmb1, IDH_SAVE_FILETYPE, stc4, IDH_OPEN_DRIVES, cmb2, IDH_OPEN_DRIVES, chx1, IDH_OPEN_READONLY, pshHelp, IDH_HELP, psh14, IDH_PRINT_NETWORK, 0, 0 }; // // Function Prototypes. // SHORT GetFileTitleX( LPTSTR lpszFile, LPTSTR lpszTitle, WORD wBufSize); BOOL GetFileName( POPENFILEINFO pOFI, DLGPROC qfnDlgProc); BOOL_PTR CALLBACK FileOpenDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); BOOL_PTR CALLBACK FileSaveDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); BOOL_PTR InitFileDlg( HWND hDlg, WPARAM wParam, POPENFILEINFO pOFI); int InitTlsValues( POPENFILEINFO pOFI); DWORD InitFilterBox( HANDLE hDlg, LPCTSTR lpszFilter); VOID InitCurrentDisk( HWND hDlg, POPENFILEINFO pOFI, WORD cmb); VOID vDeleteDirDriveBitmap(); BOOL LoadDirDriveBitmap(); void SetRGBValues(); BOOL FSetUpFile(); BOOL_PTR FileOpenCmd( HANDLE hDlg, WPARAM wParam, LPARAM lParam, POPENFILEINFO pOFI, BOOL bSave); BOOL UpdateListBoxes( HWND hDlg, POPENFILEINFO pOFI, LPTSTR lpszFilter, WORD wMask); BOOL OKButtonPressed( HWND hDlg, POPENFILEINFO pOFI, BOOL bSave); BOOL MultiSelectOKButton( HWND hDlg, POPENFILEINFO pOFI, BOOL bSave); LRESULT WINAPI dwOKSubclass( HWND hOK, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI dwLBSubclass( HWND hLB, UINT msg, WPARAM wParam, LPARAM lParam); int InvalidFileWarning( HWND hDlg, LPTSTR szFile, DWORD wErrCode, UINT mbType); VOID MeasureItem( HWND hDlg, LPMEASUREITEMSTRUCT mis); int Signum( int nTest); VOID DrawItem( POPENFILEINFO pOFI, HWND hDlg, WPARAM wParam, LPDRAWITEMSTRUCT lpdis, BOOL bSave); BOOL SpacesExist( LPTSTR szFileName); void StripFileName( HANDLE hDlg, BOOL bWowApp); LPTSTR lstrtok( LPTSTR lpStr, LPCTSTR lpDelim); LPTSTR ChopText( HWND hwndDlg, int idStatic, LPTSTR lpch); BOOL FillOutPath( HWND hList, POPENFILEINFO pOFI); BOOL ShortenThePath( LPTSTR pPath); int FListAll( POPENFILEINFO pOFI, HWND hDlg, LPTSTR pszSpec, int cchSpec); int ChangeDir( HWND hDlg, LPCTSTR lpszDir, BOOL bForce, BOOL bError); BOOL IsFileSystemCasePreserving( LPTSTR lpszDisk); BOOL IsLFNDriveX( HWND hDlg, LPTSTR szPath); int DiskAddedPreviously( TCHAR wcDrive, LPTSTR lpszName); int AddDisk( TCHAR wcDrive, LPTSTR lpName, LPTSTR lpProvider, DWORD dwType); VOID EnableDiskInfo( BOOL bValid, BOOL bDoUnc); VOID FlushDiskInfoToCmb2(); BOOL CallNetDlg( HWND hWnd); UINT GetDiskType( LPTSTR lpszDisk); DWORD GetUNCDirectoryFromLB( HWND hDlg, WORD nLB, POPENFILEINFO pOFI); VOID SelDisk( HWND hDlg, LPTSTR lpszDisk); VOID LNDSetEvent( HWND hDlg); VOID UpdateLocalDrive( LPTSTR szDrive, BOOL bGetVolName); VOID GetNetDrives( DWORD dwScope); VOID ListNetDrivesHandler(); VOID LoadDrives( HWND hDlg); DWORD GetDiskIndex( DWORD dwDriveType); VOID CleanUpFile(); VOID FileOpenAbort(); VOID TermFile(); //VOID // prototype in fileopen.h //ThunkOpenFileNameA2WDelayed( // POPENFILEINFO pOFI); //BOOL // prototype in fileopen.h //ThunkOpenFileNameA2W( // POPENFILEINFO pOFI); //BOOL // prototype in fileopen.h //ThunkOpenFileNameW2A( // POPENFILEINFO pOFI); BOOL GenericGetFileNameA( LPOPENFILENAMEA pOFNA, DLGPROC qfnDlgProc); LPWSTR ThunkANSIStrToWIDE( LPWSTR pDestW, LPSTR pSrcA, int cChars); LPWSTR ThunkMultiANSIStrToWIDE( LPWSTR pDestW, LPSTR pSrcA, int cChars); BOOL Multi_strcpyAtoW( LPWSTR pDestW, LPCSTR pSrcA, int cChars); INT Multi_strlenA( LPCSTR str); // The Win9x code relies on calling SetCurrentDirectory wherever SheChangeDirEx is // called. (Ideally SheChangeDirExA should be implemented). // Ref: NT5 bug 161292 and Millenium bug 95478 //////////////////////////////////////////////////////////////////////////// // // GetFileTitleA // // ANSI entry point for GetFileTitle when this code is built UNICODE. // //////////////////////////////////////////////////////////////////////////// SHORT WINAPI GetFileTitleA( LPCSTR lpszFileA, LPSTR lpszTitleA, WORD cbBuf) { LPWSTR lpszFileW; LPWSTR lpszTitleW; BOOL fResult; DWORD cbLen; // // Init File string. // if (lpszFileA) { cbLen = lstrlenA(lpszFileA) + 1; if (!(lpszFileW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); return (FALSE); } else { SHAnsiToUnicode((LPSTR)lpszFileA,lpszFileW,cbLen ); } } else { lpszFileW = NULL; } if (!(lpszTitleW = (LPWSTR)LocalAlloc(LPTR, (cbBuf * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); if (lpszFileW) { LocalFree(lpszFileW); } return (FALSE); } if (!(fResult = GetFileTitleW(lpszFileW, lpszTitleW, cbBuf))) { SHUnicodeToAnsi(lpszTitleW,lpszTitleA,cbBuf); } else if (fResult > 0) { // // Buffer is too small - Ansi size needed (including null terminator). // Get the offset to the filename. // SHORT nNeeded = (SHORT)(INT)LOWORD(ParseFile(lpszFileW, TRUE, FALSE, FALSE)); LPSTR lpA = (LPSTR)lpszFileA; lpA += WideCharToMultiByte( CP_ACP, 0, lpszFileW, nNeeded, NULL, 0, NULL, NULL ); fResult = lstrlenA(lpA) + 1; if (fResult <= cbBuf) { // There is enough room. EVAL(SUCCEEDED(StringCchCopyA(lpszTitleA, cbBuf, lpA))); fResult = 0; } } // // Clean up memory. // LocalFree(lpszTitleW); if (lpszFileW) { LocalFree(lpszFileW); } return ((SHORT)fResult); } //////////////////////////////////////////////////////////////////////////// // // GetFileTitle // // The GetFileTitle function returns the name of the file identified // by the lpCFile parameter. This is useful if the file name was // received via some method other than GetOpenFileName // (e.g. command line, drag drop). // // Returns: 0 on success // < 0, Parsing failure (invalid file name) // > 0, buffer too small, size needed (including NULL terminator) // //////////////////////////////////////////////////////////////////////////// SHORT WINAPI GetFileTitle( LPCTSTR lpCFile, LPTSTR lpTitle, WORD cbBuf) { LPTSTR lpFile; DWORD cchLen; SHORT fResult; // // Init File string. // if (lpCFile) { cchLen = lstrlen(lpCFile) + 1; if (!(lpFile = (LPTSTR)LocalAlloc(LPTR, (cchLen * sizeof(TCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); return (FALSE); } else { EVAL(SUCCEEDED(StringCchCopy(lpFile, cchLen, lpCFile))); // Always big enough. } } else { lpFile = NULL; } fResult = GetFileTitleX(lpFile, lpTitle, cbBuf); // // Clean up memory. // if (lpFile) { LocalFree(lpFile); } return (fResult); } //////////////////////////////////////////////////////////////////////////// // // GetFileTitleX // // Worker routine for the GetFileTitle api. // // Assumes: lpszFile points to NULL terminated DOS filename (may have path) // lpszTitle points to buffer to receive NULL terminated file title // wBufSize is the size of buffer pointed to by lpszTitle // // Returns: 0 on success // < 0, Parsing failure (invalid file name) // > 0, buffer too small, size needed (including NULL terminator) // //////////////////////////////////////////////////////////////////////////// SHORT GetFileTitleX( LPTSTR lpszFile, LPTSTR lpszTitle, WORD cchBufSize) { SHORT nNeeded; LPTSTR lpszPtr; // // New 32 bit apps will get a title based on the user's preferences. // if ((GetProcessVersion(0) >= 0x040000) && !(CDGetAppCompatFlags() & CDACF_FILETITLE)) { SHFILEINFO info; DWORD_PTR result; if (!lpszFile || !*lpszFile) { return (PARSE_EMPTYSTRING); } // // If we have a root directory name (eg. c:\), then we need to go // to the old implementation so that it will return -1. // SHGetFileInfo will return the display name for the directory // (which is the volume name). This is incompatible with Win95 // and previous versions of NT. // if ((lstrlen(lpszFile) != 3) || (lpszFile[1] != CHAR_COLON) || (!ISBACKSLASH(lpszFile, 2))) { result = SHGetFileInfo( lpszFile, FILE_ATTRIBUTE_NORMAL, &info, sizeof(info), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES ); if (result && (*info.szDisplayName)) { UINT uDisplayLen = lstrlen(info.szDisplayName); // // If no buffer or insufficient size, return the required chars. // Original GetFileTitle API did not copy on failure. // if (!lpszTitle || (uDisplayLen >= (UINT)cchBufSize)) { return ( (SHORT)(uDisplayLen + 1) ); } // // We know it fits // EVAL(SUCCEEDED(StringCchCopy(lpszTitle, cchBufSize, info.szDisplayName))); return (0); } } } // // Use the old implementation. // nNeeded = (SHORT)(int)LOWORD(ParseFile(lpszFile, TRUE, FALSE, FALSE)); if (nNeeded >= 0) { // // Is the filename valid? // lpszPtr = lpszFile + nNeeded; if ((nNeeded = (SHORT)lstrlen(lpszPtr) + 1) <= (int)cchBufSize) { // // ParseFile() fails if wildcards in directory, but OK if in name. // Since they arent OK here, the check is needed here. // if (StrChr(lpszPtr, CHAR_STAR) || StrChr(lpszPtr, CHAR_QMARK)) { nNeeded = PARSE_WILDCARDINFILE; } else { EVAL(SUCCEEDED(StringCchCopy(lpszTitle, cchBufSize, lpszPtr))); // We already checked that it's big enough. // // Remove trailing spaces. // lpszPtr = lpszTitle + lstrlen(lpszTitle) - 1; while (*lpszPtr && *lpszPtr == CHAR_SPACE) { *lpszPtr-- = CHAR_NULL; } nNeeded = 0; } } } return (nNeeded); } //////////////////////////////////////////////////////////////////////////// // // GetOpenFileNameA // // ANSI entry point for GetOpenFileName when this code is built UNICODE. // //////////////////////////////////////////////////////////////////////////// BOOL WINAPI GetOpenFileNameA( LPOPENFILENAMEA pOFNA) { if (!pOFNA) { StoreExtendedError(CDERR_INITIALIZATION); return (FALSE); } return ( GenericGetFileNameA(pOFNA, FileOpenDlgProc) ); } //////////////////////////////////////////////////////////////////////////// // // GetOpenFileName // // The GetOpenFileName function creates a system-defined dialog box // that enables the user to select a file to open. // // Returns: TRUE if user specified name // FALSE if not // //////////////////////////////////////////////////////////////////////////// BOOL WINAPI GetOpenFileName( LPOPENFILENAME pOFN) { OPENFILEINFO OFI; ZeroMemory(&OFI, sizeof(OPENFILEINFO)); if (!pOFN) { StoreExtendedError(CDERR_INITIALIZATION); return (FALSE); } OFI.pOFN = pOFN; OFI.ApiType = COMDLG_WIDE; OFI.iVersion = OPENFILEVERSION; return (GetFileName(&OFI, FileOpenDlgProc)); } //////////////////////////////////////////////////////////////////////////// // // GetSaveFileNameA // // ANSI entry point for GetSaveFileName when this code is built UNICODE. // //////////////////////////////////////////////////////////////////////////// BOOL WINAPI GetSaveFileNameA( LPOPENFILENAMEA pOFNA) { return (GenericGetFileNameA(pOFNA, FileSaveDlgProc)); } //////////////////////////////////////////////////////////////////////////// // // GetSaveFileName // // The GetSaveFileName function creates a system-defined dialog box // that enables the user to select a file to save. // // Returns: TRUE if user desires to save file and gave a proper name // FALSE if not // //////////////////////////////////////////////////////////////////////////// BOOL WINAPI GetSaveFileName( LPOPENFILENAME pOFN) { OPENFILEINFO OFI; ZeroMemory(&OFI, sizeof(OPENFILEINFO)); OFI.pOFN = pOFN; OFI.ApiType = COMDLG_WIDE; OFI.iVersion = OPENFILEVERSION; return ( GetFileName(&OFI, FileSaveDlgProc) ); } //////////////////////////////////////////////////////////////////////////// // // GetFileName // // This is the meat of both GetOpenFileName and GetSaveFileName. // // Returns: TRUE if user specified name // FALSE if not // //////////////////////////////////////////////////////////////////////////// BOOL GetFileName( POPENFILEINFO pOFI, DLGPROC qfnDlgProc) { LPOPENFILENAME pOFN = pOFI->pOFN; INT_PTR iRet = 0; LPTSTR lpDlg; HANDLE hRes, hDlgTemplate; WORD wErrorMode; HDC hdcScreen; HBITMAP hbmpTemp; LPCURDLG lpCurDlg; static fFirstTime = TRUE; UINT uiWOWFlag = 0; LANGID LangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); if (!pOFN) { StoreExtendedError(CDERR_INITIALIZATION); return (FALSE); } if (pOFN->lStructSize == OPENFILENAME_SIZE_VERSION_400) { // Note: We do not want to make a copy of the OFN structure passed in. // This confuses all MFC based apps since they query // MFC and end up getting stale data if we make a copy and endup updating only the // copy until Comdlg api returns. pOFI->iVersion = OPENFILEVERSION_NT4; } if ((pOFN->lStructSize != sizeof(OPENFILENAME)) && (pOFN->lStructSize != OPENFILENAME_SIZE_VERSION_400) ) { StoreExtendedError(CDERR_STRUCTSIZE); return (FALSE); } if (pOFN->nMaxFile == 0) { //Bail out for NULL lpstrFile Only for NT5 and above applications if (!IS16BITWOWAPP(pOFN) && (pOFI->iVersion >= OPENFILEVERSION_NT5)) { StoreExtendedError(CDERR_INITIALIZATION); return (FALSE); } } // Some parameter validation... make sure none of the buffers are larger // than the max handled by the strsafe string functions we use. if ((pOFN->nMaxFile > STRSAFE_MAX_CCH) || (pOFN->lpstrFileTitle && (pOFN->nMaxFileTitle > STRSAFE_MAX_CCH)) || ((pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter) && (pOFN->nMaxCustFilter > STRSAFE_MAX_CCH))) { StoreExtendedError(CDERR_INITIALIZATION); return (FALSE); } // // See if the application should get the new look. // // Do not allow the new look if they have hooks, templates, or // multi select without the OFN_EXPLORER bit. // // Also don't allow the new look if we are in the context of // a 16 bit process. // if ( ((pOFN->Flags & OFN_EXPLORER) || (!(pOFN->Flags & (OFN_ENABLEHOOK | OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE | OFN_ALLOWMULTISELECT)))) && (!IS16BITWOWAPP(pOFN)) ) { BOOL fRet; // // To be used by the thunking routines for multi selection. // pOFI->bUseNewDialog = TRUE; // // Show the new explorer look. // StoreExtendedError(0); g_bUserPressedCancel = FALSE; if (qfnDlgProc == FileOpenDlgProc) { fRet = (NewGetOpenFileName(pOFI)); } else { fRet = (NewGetSaveFileName(pOFI)); } return fRet; } if (fFirstTime) { // // Create a DC that is compatible with the screen and find the // handle of the null bitmap. // hdcScreen = GetDC(HNULL); if (!hdcScreen) { goto CantInit; } hdcMemory = CreateCompatibleDC(hdcScreen); if (!hdcMemory) { goto ReleaseScreenDC; } hbmpTemp = CreateCompatibleBitmap(hdcMemory, 1, 1); if (!hbmpTemp) { goto ReleaseMemDC; } hbmpOrigMemBmp = SelectObject(hdcMemory, hbmpTemp); if (!hbmpOrigMemBmp) { goto ReleaseMemDC; } SelectObject(hdcMemory, hbmpOrigMemBmp); DeleteObject(hbmpTemp); ReleaseDC(HNULL, hdcScreen); fFirstTime = FALSE; } if (pOFN->Flags & OFN_ENABLEHOOK) { if (!pOFN->lpfnHook) { StoreExtendedError(CDERR_NOHOOK); return (FALSE); } } else { pOFN->lpfnHook = NULL; } HourGlass(TRUE); StoreExtendedError(0); // // Force re-compute for font changes between calls. // dyItem = dyText = 0; g_bUserPressedCancel = FALSE; if (!FSetUpFile()) { StoreExtendedError(CDERR_INITIALIZATION); goto TERMINATE; } if (pOFN->Flags & OFN_ENABLETEMPLATE) { if (!(hRes = FindResource( pOFN->hInstance, pOFN->lpTemplateName, RT_DIALOG ))) { StoreExtendedError(CDERR_FINDRESFAILURE); goto TERMINATE; } if (!(hDlgTemplate = LoadResource(pOFN->hInstance, hRes))) { StoreExtendedError(CDERR_LOADRESFAILURE); goto TERMINATE; } LangID = GetDialogLanguage(pOFN->hwndOwner, hDlgTemplate); } else if (pOFN->Flags & OFN_ENABLETEMPLATEHANDLE) { hDlgTemplate = pOFN->hInstance; LangID = GetDialogLanguage(pOFN->hwndOwner, hDlgTemplate); } else { if (pOFN->Flags & OFN_ALLOWMULTISELECT) { lpDlg = MAKEINTRESOURCE(MULTIFILEOPENORD); } else { lpDlg = MAKEINTRESOURCE(FILEOPENORD); } LangID = GetDialogLanguage(pOFN->hwndOwner, NULL); if (!(hRes = FindResourceExFallback(g_hinst, RT_DIALOG, lpDlg, LangID))) { StoreExtendedError(CDERR_FINDRESFAILURE); goto TERMINATE; } if (!(hDlgTemplate = LoadResource(g_hinst, hRes))) { StoreExtendedError(CDERR_LOADRESFAILURE); goto TERMINATE; } } // // Warning! Warning! Warning! // // We have to set g_tlsLangID before any call for CDLoadString // TlsSetValue(g_tlsLangID, (LPVOID) LangID); // // No kernel network error dialogs. // wErrorMode = (WORD)SetErrorMode(SEM_NOERROR); SetErrorMode(SEM_NOERROR | wErrorMode); if (LockResource(hDlgTemplate)) { if (pOFN->Flags & OFN_ENABLEHOOK) { glpfnFileHook = GETHOOKFN(pOFN); } if (IS16BITWOWAPP(pOFN)) { uiWOWFlag = SCDLG_16BIT; } iRet = DialogBoxIndirectParamAorW( g_hinst, (LPDLGTEMPLATE)hDlgTemplate, pOFN->hwndOwner, qfnDlgProc, (DWORD_PTR)pOFI, uiWOWFlag ); if (iRet == -1 || ((iRet == 0) && (!g_bUserPressedCancel) && (!GetStoredExtendedError()))) { StoreExtendedError(CDERR_DIALOGFAILURE); } else { FileOpenAbort(); } glpfnFileHook = 0; } else { StoreExtendedError(CDERR_LOCKRESFAILURE); goto TERMINATE; } SetErrorMode(wErrorMode); if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) { // restore the thread list to the previous dialog (if any) TlsSetValue(g_tlsiCurDlg, (LPVOID)lpCurDlg->next); LocalFree(lpCurDlg->lpstrCurDir); LocalFree(lpCurDlg); } TERMINATE: CleanUpFile(); HourGlass(FALSE); return (iRet == IDOK); ReleaseMemDC: DeleteDC(hdcMemory); ReleaseScreenDC: ReleaseDC(HNULL, hdcScreen); CantInit: return (FALSE); } //////////////////////////////////////////////////////////////////////////// // // FileHookCmd // // Called when a hook function processes a WM_COMMAND message. // Called by FileOpenDlgProc and FileSaveDlgProc. // //////////////////////////////////////////////////////////////////////////// BOOL FileHookCmd( HANDLE hDlg, WPARAM wParam, LPARAM lParam, POPENFILEINFO pOFI) { switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ( IDCANCEL ) : { // // Set global flag stating that the // user pressed cancel. // g_bUserPressedCancel = TRUE; // Fall Thru... } case ( IDOK ) : case ( IDABORT ) : { // // Apps that side-effect these messages may // not have their internal unicode strings // updated. They may also forget to gracefully // exit the network enum'ing worker thread. // if (pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameA2W(pOFI); } break; } case ( cmb1 ) : case ( cmb2 ) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case ( MYCBN_DRAW ) : case ( MYCBN_LIST ) : case ( MYCBN_REPAINT ) : case ( MYCBN_CHANGEDIR ) : { // // In case an app has a hook, and returns // true for processing WM_COMMAND messages, // we still have to worry about our // internal message that came through via // WM_COMMAND. // FileOpenCmd( hDlg, wParam, lParam, pOFI, FALSE ); break; } } break; } } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // FileOpenDlgProc // // Gets the name of a file to open from the user. // // edt1 = file name // lst1 = list of files in current directory matching current pattern // cmb1 = lists file patterns // stc1 = is current directory // lst2 = lists directories on current drive // cmb2 = lists drives // IDOK = is Open pushbutton // IDCANCEL = is Cancel pushbutton // chx1 = is for opening read only files // // Returns the normal dialog proc values. // //////////////////////////////////////////////////////////////////////////// BOOL_PTR CALLBACK FileOpenDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { POPENFILEINFO pOFI; BOOL_PTR bRet, bHookRet; if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP)) { if (pOFI->pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFI->pOFN); bHookRet = (*lpfnHook)(hDlg, wMsg, wParam, lParam); if (bHookRet) { if (wMsg == WM_COMMAND) { return (FileHookCmd(hDlg, wParam, lParam, pOFI)); } return (bHookRet); } } } else if (glpfnFileHook && (wMsg != WM_INITDIALOG) && (bHookRet = (*glpfnFileHook)(hDlg, wMsg, wParam, lParam))) { return (bHookRet); } switch (wMsg) { case ( WM_INITDIALOG ) : { pOFI = (POPENFILEINFO)lParam; SetProp(hDlg, FILEPROP, (HANDLE)pOFI); glpfnFileHook = 0; // // If we are being called from a Unicode app, turn off // the ES_OEMCONVERT style on the filename edit control. // // if (pOFI->ApiType == COMDLG_WIDE) { LONG lStyle; HWND hEdit = GetDlgItem(hDlg, edt1); // // Grab the window style. // lStyle = GetWindowLong(hEdit, GWL_STYLE); // // If the window style bits include ES_OEMCONVERT, // remove this flag and reset the style. // if (lStyle & ES_OEMCONVERT) { lStyle &= ~ES_OEMCONVERT; SetWindowLong(hEdit, GWL_STYLE, lStyle); } } bInitializing = TRUE; bRet = InitFileDlg(hDlg, wParam, pOFI); bInitializing = FALSE; HourGlass(FALSE); return (bRet); break; } case ( WM_ACTIVATE ) : { if (!bInChildDlg) { if (bFirstTime == TRUE) { bFirstTime = FALSE; } else if (wParam) { // // If becoming active. // LNDSetEvent(hDlg); } } return (FALSE); break; } case ( WM_MEASUREITEM ) : { MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam); break; } case ( WM_DRAWITEM ) : { if (wNoRedraw < 2) { DrawItem(pOFI, hDlg, wParam, (LPDRAWITEMSTRUCT)lParam, FALSE); } break; } case ( WM_SYSCOLORCHANGE ) : { SetRGBValues(); LoadDirDriveBitmap(); break; } case ( WM_COMMAND ) : { return (FileOpenCmd(hDlg, wParam, lParam, pOFI, FALSE)); break; } case ( WM_SETFOCUS ) : { // // This logic used to be in CBN_SETFOCUS in fileopencmd, // but CBN_SETFOCUS is called whenever there is a click on // the List Drives combo. This causes the worker thread // to start up and flicker when the combo box is refreshed. // // But, refreshes are only needed when someone focuses out of // the common dialog and then back in (unless someone is logged // in remote, or there is a background thread busy connecting!) // so fix the flicker by moving the logic here. // if (!wNoRedraw) { LNDSetEvent(hDlg); } return (FALSE); break; } case ( WM_HELP ) : { if (IsWindowEnabled(hDlg)) { WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aFileOpenHelpIDs ); } break; } case ( WM_CONTEXTMENU ) : { if (IsWindowEnabled(hDlg)) { WinHelp( (HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPVOID)aFileOpenHelpIDs ); } break; } default : { return (FALSE); } } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // FileSaveDlgProc // // Obtains the name of the file that the user wants to save. // // Returns the normal dialog proc values. // //////////////////////////////////////////////////////////////////////////// BOOL_PTR CALLBACK FileSaveDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { POPENFILEINFO pOFI; BOOL_PTR bRet, bHookRet; TCHAR szTitle[64]; if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP)) { if (pOFI->pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFI->pOFN); bHookRet = (*lpfnHook)(hDlg, wMsg, wParam, lParam); if (bHookRet) { if (wMsg == WM_COMMAND) { return (FileHookCmd(hDlg, wParam, lParam, pOFI)); } return (bHookRet); } } } else if (glpfnFileHook && (wMsg != WM_INITDIALOG) && (bHookRet = (*glpfnFileHook)(hDlg, wMsg, wParam, lParam))) { return (bHookRet); } switch (wMsg) { case ( WM_INITDIALOG ) : { pOFI = (POPENFILEINFO)lParam; if (!(pOFI->pOFN->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))) { CDLoadString(g_hinst, iszFileSaveTitle, szTitle, ARRAYSIZE(szTitle)); SetWindowText(hDlg, szTitle); CDLoadString(g_hinst, iszSaveFileAsType, szTitle, ARRAYSIZE(szTitle)); SetDlgItemText(hDlg, stc2, szTitle); } glpfnFileHook = 0; SetProp(hDlg, FILEPROP, (HANDLE)pOFI); // // If we are being called from a Unicode app, turn off // the ES_OEMCONVERT style on the filename edit control. // // if (pOFI->ApiType == COMDLG_WIDE) { LONG lStyle; HWND hEdit = GetDlgItem(hDlg, edt1); // // Grab the window style. // lStyle = GetWindowLong(hEdit, GWL_STYLE); // // If the window style bits include ES_OEMCONVERT, // remove this flag and reset the style. // if (lStyle & ES_OEMCONVERT) { lStyle &= ~ES_OEMCONVERT; SetWindowLong (hEdit, GWL_STYLE, lStyle); } } bInitializing = TRUE; bRet = InitFileDlg(hDlg, wParam, pOFI); bInitializing = FALSE; HourGlass(FALSE); return (bRet); break; } case ( WM_ACTIVATE ) : { if (!bInChildDlg) { if (bFirstTime == TRUE) { bFirstTime = FALSE; } else if (wParam) { // // If becoming active. // if (!wNoRedraw) { LNDSetEvent(hDlg); } } } return (FALSE); break; } case ( WM_MEASUREITEM ) : { MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam); break; } case ( WM_DRAWITEM ) : { if (wNoRedraw < 2) { DrawItem(pOFI, hDlg, wParam, (LPDRAWITEMSTRUCT)lParam, TRUE); } break; } case ( WM_SYSCOLORCHANGE ) : { SetRGBValues(); LoadDirDriveBitmap(); break; } case ( WM_COMMAND ) : { return (FileOpenCmd(hDlg, wParam, lParam, pOFI, TRUE)); break; } case ( WM_SETFOCUS ) : { // // This logic used to be in CBN_SETFOCUS in fileopencmd, // but CBN_SETFOCUS is called whenever there is a click on // the List Drives combo. This causes the worker thread // to start up and flicker when the combo box is refreshed. // // But, refreshes are only needed when someone focuses out of // the common dialog and then back in (unless someone is logged // in remote, or there is a background thread busy connecting!) // so fix the flicker by moving the logic here. // if (!wNoRedraw) { LNDSetEvent(hDlg); } return (FALSE); break; } case ( WM_HELP ) : { if (IsWindowEnabled(hDlg)) { WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aFileSaveHelpIDs ); } break; } case ( WM_CONTEXTMENU ) : { if (IsWindowEnabled(hDlg)) { WinHelp( (HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPVOID)aFileSaveHelpIDs ); } break; } default : { return (FALSE); } } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // InitFileDlg // //////////////////////////////////////////////////////////////////////////// BOOL_PTR InitFileDlg( HWND hDlg, WPARAM wParam, POPENFILEINFO pOFI) { DWORD lRet, nFilterIndex; LPOPENFILENAME pOFN = pOFI->pOFN; int nFileOffset, nExtOffset; RECT rRect; RECT rLbox; BOOL_PTR bRet; if (!InitTlsValues(pOFI)) { // // The extended error is set inside of the above call. // EndDialog(hDlg, FALSE); return (FALSE); } lpLBProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hDlg, lst2), GWLP_WNDPROC); lpOKProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hDlg, IDOK), GWLP_WNDPROC); if (!lpLBProc || !lpOKProc) { StoreExtendedError(FNERR_SUBCLASSFAILURE); EndDialog(hDlg, FALSE); return (FALSE); } // // Save original directory for later restoration if necessary. // *pOFI->szCurDir = 0; GetCurrentDirectory(MAX_FULLPATHNAME + 1, pOFI->szCurDir); // // Check out if the filename contains a path. If so, override whatever // is contained in lpstrInitialDir. Chop off the path and put up only // the filename. // if ( pOFN->lpstrFile && *pOFN->lpstrFile && !(pOFN->Flags & OFN_NOVALIDATE) ) { if (DBL_BSLASH(pOFN->lpstrFile + 2) && ((*(pOFN->lpstrFile + 1) == CHAR_COLON))) { // Turns "c:\\foo\bar" into "\\foo\bar" (in lpstrFile) // Some backward compat thing? StringCopyOverlap(pOFN->lpstrFile, pOFN->lpstrFile + 2); } lRet = ParseFile(pOFN->lpstrFile, TRUE, IS16BITWOWAPP(pOFN), FALSE); nFileOffset = (int)(SHORT)LOWORD(lRet); nExtOffset = (int)(SHORT)HIWORD(lRet); // // Is the filename invalid? // if ( (nFileOffset < 0) && (nFileOffset != PARSE_EMPTYSTRING) && (pOFN->lpstrFile[nExtOffset] != CHAR_SEMICOLON) ) { StoreExtendedError(FNERR_INVALIDFILENAME); EndDialog(hDlg, FALSE); return (FALSE); } } pOFN->Flags &= ~(OFN_FILTERDOWN | OFN_DRIVEDOWN | OFN_DIRSELCHANGED); pOFI->idirSub = 0; if (!(pOFN->Flags & OFN_SHOWHELP)) { HWND hHelp; EnableWindow(hHelp = GetDlgItem(hDlg, pshHelp), FALSE); // // Move the window out of this spot so that no overlap will be // detected. // MoveWindow(hHelp, -8000, -8000, 20, 20, FALSE); ShowWindow(hHelp, SW_HIDE); } if (pOFN->Flags & OFN_CREATEPROMPT) { pOFN->Flags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST); } else if (pOFN->Flags & OFN_FILEMUSTEXIST) { pOFN->Flags |= OFN_PATHMUSTEXIST; } if (pOFN->Flags & OFN_HIDEREADONLY) { HWND hReadOnly; EnableWindow(hReadOnly = GetDlgItem(hDlg, chx1), FALSE); // // Move the window out of this spot so that no overlap will be // detected. // MoveWindow(hReadOnly, -8000, -8000, 20, 20, FALSE); ShowWindow(hReadOnly, SW_HIDE); } else { CheckDlgButton(hDlg, chx1, (pOFN->Flags & OFN_READONLY) != 0); } SendDlgItemMessage(hDlg, edt1, EM_LIMITTEXT, (WPARAM)MAX_PATH, 0L); // // Insert file specs into cmb1. // Custom filter first. // Must also check if filter contains anything. // if ( pOFN->lpstrFile && (StrChr(pOFN->lpstrFile, CHAR_STAR) || StrChr(pOFN->lpstrFile, CHAR_QMARK)) ) { StringCchCopyEx(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFN->lpstrFile, NULL, NULL, STRSAFE_NULL_ON_FAILURE); } else { pOFI->szLastFilter[0] = CHAR_NULL; } if (pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter) { SHORT nLength; SendDlgItemMessage( hDlg, cmb1, CB_INSERTSTRING, 0, (LONG_PTR)pOFN->lpstrCustomFilter ); nLength = (SHORT)(lstrlen(pOFN->lpstrCustomFilter) + 1); SendDlgItemMessage( hDlg, cmb1, CB_SETITEMDATA, 0, (LONG)(nLength) ); SendDlgItemMessage( hDlg, cmb1, CB_LIMITTEXT, (WPARAM)(pOFN->nMaxCustFilter), 0L ); if (pOFI->szLastFilter[0] == CHAR_NULL) { StringCchCopyEx(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFN->lpstrCustomFilter + nLength, NULL, NULL, STRSAFE_NULL_ON_FAILURE); } } else { // // Given no custom filter, the index will be off by one. // if (pOFN->nFilterIndex != 0) { pOFN->nFilterIndex--; } } // // Listed filters next. // if (pOFN->lpstrFilter && *pOFN->lpstrFilter) { if (pOFN->nFilterIndex > InitFilterBox(hDlg, pOFN->lpstrFilter)) { pOFN->nFilterIndex = 0; } } else { pOFN->nFilterIndex = 0; } pOFI->szSpecCur[0] = CHAR_NULL; // // If an entry exists, select the one indicated by nFilterIndex. // if ((pOFN->lpstrFilter && *pOFN->lpstrFilter) || (pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter)) { LPCTSTR lpFilter; SendDlgItemMessage( hDlg, cmb1, CB_SETCURSEL, (WPARAM)(pOFN->nFilterIndex), 0L ); nFilterIndex = pOFN->nFilterIndex; SendMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS( cmb1, GetDlgItem(hDlg, cmb1), MYCBN_DRAW ) ); pOFN->nFilterIndex = nFilterIndex; if (pOFN->nFilterIndex || !(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter)) { lpFilter = pOFN->lpstrFilter + SendDlgItemMessage( hDlg, cmb1, CB_GETITEMDATA, (WPARAM)pOFN->nFilterIndex, 0L ); } else { lpFilter = pOFN->lpstrCustomFilter + lstrlen(pOFN->lpstrCustomFilter) + 1; } if (*lpFilter) { TCHAR szText[MAX_FULLPATHNAME]; if (SUCCEEDED(StringCchCopy(szText, ARRAYSIZE(szText), lpFilter))) { // // Filtering is case-insensitive. // CharLower(szText); if (pOFI->szLastFilter[0] == CHAR_NULL) { EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), szText))); } if (!(pOFN->lpstrFile && *pOFN->lpstrFile)) { SetDlgItemText(hDlg, edt1, szText); } } } } InitCurrentDisk(hDlg, pOFI, cmb2); bFirstTime = TRUE; bInChildDlg = FALSE; SendMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(cmb2, GetDlgItem(hDlg, cmb2), MYCBN_DRAW) ); SendMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(cmb2, GetDlgItem(hDlg, cmb2), MYCBN_LIST) ); if (pOFN->lpstrFile && *pOFN->lpstrFile) { TCHAR szText[MAX_FULLPATHNAME]; lRet = ParseFile( pOFN->lpstrFile, IsLFNDriveX(hDlg, pOFN->lpstrFile), IS16BITWOWAPP(pOFN), FALSE ); nFileOffset = (int)(SHORT)LOWORD(lRet); nExtOffset = (int)(SHORT)HIWORD(lRet); // // Is the filename invalid? // if ( !(pOFN->Flags & OFN_NOVALIDATE) && (nFileOffset < 0) && (nFileOffset != PARSE_EMPTYSTRING) && (pOFN->lpstrFile[nExtOffset] != CHAR_SEMICOLON) ) { StoreExtendedError(FNERR_INVALIDFILENAME); EndDialog(hDlg, FALSE); return (FALSE); } if (FAILED(StringCchCopy(szText, ARRAYSIZE(szText), pOFN->lpstrFile)) && !(pOFN->Flags & OFN_NOVALIDATE)) { StoreExtendedError(FNERR_INVALIDFILENAME); EndDialog(hDlg, FALSE); return (FALSE); } SetDlgItemText(hDlg, edt1, szText); } SetWindowLongPtr(GetDlgItem(hDlg, lst2), GWLP_WNDPROC, (LONG_PTR)dwLBSubclass); SetWindowLongPtr(GetDlgItem(hDlg, IDOK), GWLP_WNDPROC, (LONG_PTR)dwOKSubclass); if (pOFN->lpstrTitle && *pOFN->lpstrTitle) { SetWindowText(hDlg, pOFN->lpstrTitle); } // // By setting dyText to rRect.bottom/8, dyText defaults to 8 items showing // in the listbox. This only matters if the applications hook function // steals all WM_MEASUREITEM messages. Otherwise, dyText will be set in // the MeasureItem() routine. Check for !dyItem in case message ordering // has already sent WM_MEASUREITEM and dyText is already initialized. // if (!dyItem) { GetClientRect(GetDlgItem(hDlg, lst1), (LPRECT) &rRect); if (!(dyText = (rRect.bottom / 8))) { // // If no size to rectangle. // dyText = 8; } } // The template has changed to make it extremely clear that // this is not a combobox, but rather an edit control and a listbox. The // problem is that the new templates try to align the edit box and listbox. // Unfortunately, when listboxes add borders, they expand beyond their // borders. When edit controls add borders, they stay within their // borders. This makes it impossible to align the two controls strictly // within the template. The code below will align the controls, but only // if they are using the standard dialog template. // if (!(pOFN->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE))) { GetWindowRect(GetDlgItem(hDlg, lst1), (LPRECT)&rLbox); GetWindowRect(GetDlgItem(hDlg, edt1), (LPRECT)&rRect); rRect.left = rLbox.left; rRect.right = rLbox.right; MapWindowPoints(NULL, hDlg, (LPPOINT)&rRect, 2); SetWindowPos( GetDlgItem(hDlg, edt1), 0, rRect.left, rRect.top, rRect.right - rRect.left, rRect.bottom - rRect.top, SWP_NOZORDER ); } if (pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameW2A(pOFI); bRet = ((*lpfnHook)( hDlg, WM_INITDIALOG, wParam, (LPARAM)pOFI->pOFNA )); // // Strange win 31 example uses lCustData to // hold a temporary variable that it passes back to // calling function. // ThunkOpenFileNameA2W(pOFI); } else { bRet = ((*lpfnHook)( hDlg, WM_INITDIALOG, wParam, (LPARAM)pOFN )); } } else { // // Have to thunk A version even when there isn't a hook proc so it // doesn't reset W version on delayed thunk back. // if (pOFI->ApiType == COMDLG_ANSI) { pOFI->pOFNA->Flags = pOFN->Flags; } bRet = TRUE; } // // At first, assume there is net support ! // if ((pOFN->Flags & OFN_NONETWORKBUTTON)) { HWND hNet; if (hNet = GetDlgItem(hDlg, psh14)) { EnableWindow(hNet = GetDlgItem(hDlg, psh14), FALSE); ShowWindow(hNet, SW_HIDE); } } else { AddNetButton( hDlg, ((pOFN->Flags & OFN_ENABLETEMPLATE) ? pOFN->hInstance : g_hinst), FILE_BOTTOM_MARGIN, (pOFN->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)) ? FALSE : TRUE, (pOFN->Flags & OFN_NOLONGNAMES) ? FALSE : TRUE, FALSE); } return (bRet); } //////////////////////////////////////////////////////////////////////////// // // InitTlsValues // //////////////////////////////////////////////////////////////////////////// int InitTlsValues( POPENFILEINFO pOFI) { // // As long as we do not call TlsGetValue before this, // everything should be ok. // LPCURDLG lpCurDlg, lpPrevDlg; DWORD dwError; LPTSTR lpCurDir; if (dwNumDlgs == MAX_THREADS) { dwError = CDERR_INITIALIZATION; goto ErrorExit0; } // alloc for the current directory lpCurDir = (LPTSTR)LocalAlloc(LPTR, CCHNETPATH * sizeof(TCHAR)); if (lpCurDir) { GetCurrentDirectory(CCHNETPATH, lpCurDir); if ( (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) && (StrChr(lpCurDir, CHAR_SPACE)) ) { GetShortPathName(lpCurDir, lpCurDir, CCHNETPATH); } } else { dwError = CDERR_MEMALLOCFAILURE; goto ErrorExit0; } // add a CurDlg struct to the list for this thread lpCurDlg = (LPCURDLG)LocalAlloc(LPTR, sizeof(CURDLG)); if (lpCurDlg) { // get start of CURDLG list for this thread // Note: lpPrevDlg will be NULL if there wasn't a previous dialog lpPrevDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); // make sure TlsGetValue() actually succeeded (a NULL return could // mean there wasn't a previous dialog in the list) if (GetLastError() != NO_ERROR) { dwError = CDERR_INITIALIZATION; goto ErrorExit2; } // push the new dlg to the front of the list lpCurDlg->next = lpPrevDlg; lpCurDlg->lpstrCurDir = lpCurDir; if (!PathAddBackslash(lpCurDlg->lpstrCurDir)) // Could fail if path is already MAX_PATH long, w/o a blackslash. { dwError = CDERR_INITIALIZATION; goto ErrorExit2; } EnterCriticalSection(&g_csLocal); lpCurDlg->dwCurDlgNum = dwNumDlgs++; LeaveCriticalSection(&g_csLocal); // save the new head of the list for the thread if (!TlsSetValue(g_tlsiCurDlg, (LPVOID)lpCurDlg)) { dwError = CDERR_INITIALIZATION; goto ErrorExit2; } } else { dwError = CDERR_MEMALLOCFAILURE; goto ErrorExit1; } return(TRUE); ErrorExit2: LocalFree(lpCurDlg); ErrorExit1: LocalFree(lpCurDir); ErrorExit0: StoreExtendedError(dwError); return (FALSE); } //////////////////////////////////////////////////////////////////////////// // // InitFilterBox // // Places the double null terminated list of filters in the combo box. // The list should consist of pairs of null terminated strings, with // an additional null terminating the list. // //////////////////////////////////////////////////////////////////////////// DWORD InitFilterBox( HANDLE hDlg, LPCTSTR lpszFilter) { DWORD nOffset = 0; DWORD nIndex = 0; register WORD nLen; while (*lpszFilter) { // // First string put in as string to show. // nIndex = (DWORD) SendDlgItemMessage( hDlg, cmb1, CB_ADDSTRING, 0, (LPARAM)lpszFilter ); nLen = (WORD)(lstrlen(lpszFilter) + 1); (LPTSTR)lpszFilter += nLen; nOffset += nLen; // // Second string put in as itemdata. // SendDlgItemMessage( hDlg, cmb1, CB_SETITEMDATA, (WPARAM)nIndex, nOffset ); // // Advance to next element. // nLen = (WORD)(lstrlen(lpszFilter) + 1); (LPTSTR)lpszFilter += nLen; nOffset += nLen; } return (nIndex); } void TokenizeFilterString(LPTSTR pszFilterString, LPTSTR *ppszFilterArray, int cFilterArray, BOOL bLFN) { LPCTSTR pszDelim = bLFN ? szSemiColonTab : szSemiColonSpaceTab; int nFilters = 0; cFilterArray--; // Need one for the NULL at the end. // // Find the first filter in the string, and add it to the // array. // ppszFilterArray[nFilters] = lstrtok(pszFilterString, pszDelim); // // Now we are going to loop through all the filters in the string // parsing the one we already have, and then finding the next one // and starting the loop over again. // while (ppszFilterArray[nFilters] && (nFilters < cFilterArray)) { // // Check to see if the first character is a space. If so, remove // the spaces, and save the pointer back into the same spot. We // need to do this because the FindFirstFile/Next api will still // work on filenames that begin with a space since they also // look at the short names. The short names will begin with the // same first real letter as the long filename. For example, the // long filename is " my document" the first letter of this short // name is "m", so searching on "m*.*" or " m*.*" will yield the // same results. // if (bLFN && (*ppszFilterArray[nFilters] == CHAR_SPACE)) { LPTSTR pszTemp = ppszFilterArray[nFilters]; while ((*pszTemp == CHAR_SPACE) && *pszTemp) { pszTemp = CharNext(pszTemp); } ppszFilterArray[nFilters] = pszTemp; } // // Ready to move on to the next filter. Find the next // filter based upon the type of file system we're using. // ppszFilterArray[++nFilters] = lstrtok(NULL, pszDelim); // // In case we found a pointer to NULL, then look for the // next filter. // while (ppszFilterArray[nFilters] && !*ppszFilterArray[nFilters]) { ppszFilterArray[nFilters] = lstrtok(NULL, pszDelim); } } } BOOL FoundFilterMatch(LPCTSTR pszIn, BOOL bLFN) { TCHAR szFilter[MAX_FULLPATHNAME]; LPTSTR pszF[MAXFILTERS + 1]; BOOL fFoundMatches = FALSE; int i; if (SUCCEEDED(StringCchCopy(szFilter, ARRAYSIZE(szFilter), pszIn))) { TokenizeFilterString(szFilter, pszF, ARRAYSIZE(pszF), bLFN); for (i = 0; i < ARRAYSIZE(pszF) && pszF[i] && !fFoundMatches; i++) { HANDLE hff; WIN32_FIND_DATA FindFileData; // // Find First for each filter. // hff = FindFirstFile(pszF[i], &FindFileData); if (hff == INVALID_HANDLE_VALUE) { continue; } do { if ((FindFileData.dwFileAttributes & EXCLBITS) || (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { continue; } fFoundMatches = TRUE; break; } while (FindNextFile(hff, &FindFileData)); FindClose(hff); } } return fFoundMatches; } //////////////////////////////////////////////////////////////////////////// // // GetAppOpenDir // //////////////////////////////////////////////////////////////////////////// void GetAppOpenDir(LPTSTR pszOut, DWORD cchOut, LPITEMIDLIST *ppidl) { BOOL fUseMyDocs = FALSE; TCHAR szPersonal[MAX_PATH]; *pszOut = 0; // prepare to return empty string if (ppidl) *ppidl = NULL; if (SHGetSpecialFolderPath(NULL, szPersonal, CSIDL_PERSONAL, FALSE)) { TCHAR szPath[MAX_FULLPATHNAME]; if (GetCurrentDirectory(ARRAYSIZE(szPath), szPath) && (PathIsTemporary(szPath) || (0 == lstrcmpi(szPath, szPersonal)))) fUseMyDocs = TRUE; } if (fUseMyDocs) { EVAL(SUCCEEDED(StringCchCopy(pszOut, cchOut, szPersonal))); if (ppidl) { SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, ppidl); } } else { EVAL(SUCCEEDED(StringCchCopy(pszOut, cchOut, L""))); } } //////////////////////////////////////////////////////////////////////////// // // InitCurrentDisk // //////////////////////////////////////////////////////////////////////////// VOID InitCurrentDisk(HWND hDlg, POPENFILEINFO pOFI, WORD cmb) { TCHAR szPath[MAX_FULLPATHNAME]; // // Clear out stale unc stuff from disk info. // Unc \\server\shares are persistent through one popup session // and then we resync with the system. This is to fix a bug // where a user's startup dir is unc but the system no longer has // a connection and hence the cmb2 appears blank. // EnableDiskInfo(FALSE, TRUE); if (pOFI->pOFN->lpstrInitialDir) { // // Notice that we force ChangeDir to succeed here // but that TlsGetValue(g_tlsiCurDlg)->lpstrCurDir will return "" which // when fed to SheChangeDirEx means GetCurrentDir will be called. // So, the default cd behavior at startup is: // 1. lpstrInitialDir // 2. GetCurrentDir // szPath[0] = 0; if ( (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) && (StrChr(pOFI->pOFN->lpstrInitialDir, CHAR_SPACE)) && (GetShortPathName( pOFI->pOFN->lpstrInitialDir, szPath, MAX_FULLPATHNAME )) && (szPath[0] != 0) ) { ChangeDir(hDlg, szPath, TRUE, FALSE); } else { ChangeDir(hDlg, pOFI->pOFN->lpstrInitialDir, TRUE, FALSE); } } else { GetAppOpenDir(szPath, ARRAYSIZE(szPath), NULL); ChangeDir(hDlg, szPath, TRUE, FALSE); } } //////////////////////////////////////////////////////////////////////////// // // vDeleteDirDriveBitmap // // Gets rid of bitmaps, if they exist. // //////////////////////////////////////////////////////////////////////////// VOID vDeleteDirDriveBitmap() { if (hbmpOrigMemBmp) { SelectObject(hdcMemory, hbmpOrigMemBmp); if (hbmpDirDrive != HNULL) { DeleteObject(hbmpDirDrive); hbmpDirDrive = HNULL; } } } //////////////////////////////////////////////////////////////////////////// // // LoadDirDriveBitmap // // Creates the drive/directory bitmap. If an appropriate bitmap // already exists, it just returns immediately. Otherwise, it // loads the bitmap and creates a larger bitmap with both regular // and highlight colors. // //////////////////////////////////////////////////////////////////////////// BOOL LoadDirDriveBitmap() { BITMAP bmp; HANDLE hbmp, hbmpOrig; HDC hdcTemp; BOOL bWorked = FALSE; if ( (hbmpDirDrive != HNULL) && (rgbWindowColor == rgbDDWindow) && (rgbHiliteColor == rgbDDHilite)) { if (SelectObject(hdcMemory, hbmpDirDrive)) { return (TRUE); } } vDeleteDirDriveBitmap(); rgbDDWindow = rgbWindowColor; rgbDDHilite = rgbHiliteColor; if (!(hdcTemp = CreateCompatibleDC(hdcMemory))) { goto LoadExit; } if (!(hbmp = LoadAlterBitmap(bmpDirDrive, rgbSolidBlue, rgbWindowColor))) { goto DeleteTempDC; } GetObject(hbmp, sizeof(BITMAP), (LPTSTR)&bmp); dyDirDrive = bmp.bmHeight; dxDirDrive = bmp.bmWidth; hbmpOrig = SelectObject(hdcTemp, hbmp); hbmpDirDrive = CreateDiscardableBitmap(hdcTemp, dxDirDrive * 2, dyDirDrive); if (!hbmpDirDrive) { goto DeleteTempBmp; } if (!SelectObject(hdcMemory, hbmpDirDrive)) { vDeleteDirDriveBitmap(); goto DeleteTempBmp; } BitBlt(hdcMemory, 0, 0, dxDirDrive, dyDirDrive, hdcTemp, 0, 0, SRCCOPY); SelectObject(hdcTemp, hbmpOrig); DeleteObject(hbmp); if (!(hbmp = LoadAlterBitmap(bmpDirDrive, rgbSolidBlue, rgbHiliteColor))) { goto DeleteTempDC; } hbmpOrig = SelectObject(hdcTemp, hbmp); BitBlt(hdcMemory, dxDirDrive, 0, dxDirDrive, dyDirDrive, hdcTemp, 0, 0, SRCCOPY); SelectObject(hdcTemp, hbmpOrig); bWorked = TRUE; DeleteTempBmp: DeleteObject(hbmp); DeleteTempDC: DeleteDC(hdcTemp); LoadExit: return (bWorked); } //////////////////////////////////////////////////////////////////////////// // // SetRGBValues // // This sets the various system colors in static variables. It's // called at init time and when system colors change. // //////////////////////////////////////////////////////////////////////////// void SetRGBValues() { rgbWindowColor = GetSysColor(COLOR_WINDOW); rgbHiliteColor = GetSysColor(COLOR_HIGHLIGHT); rgbWindowText = GetSysColor(COLOR_WINDOWTEXT); rgbHiliteText = GetSysColor(COLOR_HIGHLIGHTTEXT); rgbGrayText = GetSysColor(COLOR_GRAYTEXT); } //////////////////////////////////////////////////////////////////////////// // // FSetUpFile // // This loads in the resources & initializes the data used by the // file dialogs. // // Returns: TRUE if successful // FALSE if any bitmap fails // //////////////////////////////////////////////////////////////////////////// BOOL FSetUpFile() { if (cLock++) { return (TRUE); } SetRGBValues(); return (LoadDirDriveBitmap()); } //////////////////////////////////////////////////////////////////////////// // // GetPathOffset // // Returns the index of the last character of the drive or UNC specification // e.g.: // c:\foo will return 2 (\foo) // \\foo\bar\hoo will return 9 (\hoo) // But for \\foo\bar, there seems to be a bug in PathSkipRoot, where it will return \bar, and we'll return 4 (o\bar) //////////////////////////////////////////////////////////////////////////// int GetPathOffset(LPTSTR lpszDir) { LPTSTR lpszSkipRoot; if (!lpszDir || !*lpszDir) { return (-1); } lpszSkipRoot = PathSkipRoot(lpszDir); if (lpszSkipRoot) { return (int)((lpszSkipRoot - 1) - lpszDir); } else { // // Unrecognized format. // return (-1); } } //////////////////////////////////////////////////////////////////////////// // // FileOpenCmd // // Handles WM_COMMAND for Open & Save dlgs. // // edt1 = file name // lst1 = list of files in current directory matching current pattern // cmb1 = lists file patterns // stc1 = is current directory // lst2 = lists directories on current drive // cmb2 = lists drives // IDOK = is Open pushbutton // IDCANCEL = is Cancel pushbutton // chx1 = is for opening read only files // // Returns the normal dialog proc values. // //////////////////////////////////////////////////////////////////////////// BOOL_PTR FileOpenCmd( HANDLE hDlg, WPARAM wParam, LPARAM lParam, POPENFILEINFO pOFI, BOOL bSave) { LPOPENFILENAME pOFN; LPTSTR pch, pch2; WORD i, sCount, len; LRESULT wFlag; BOOL_PTR bRet, bHookRet; TCHAR szText[MAX_FULLPATHNAME]; HWND hwnd; LPCURDLG lpCurDlg; if (!pOFI) { return (FALSE); } pOFN = pOFI->pOFN; switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ( IDOK ) : { // // Apps that side-effect this message may not have their // internal unicode strings updated (eg. Corel Mosaic). // // NOTE: Must preserve the internal flags. // if (pOFI->ApiType == COMDLG_ANSI) { DWORD InternalFlags = pOFN->Flags & OFN_ALL_INTERNAL_FLAGS; ThunkOpenFileNameA2W(pOFI); pOFN->Flags |= InternalFlags; } // // If the focus is on the directory box, or if the selection // within the box has changed since the last listing, give a // new listing. // if (bChangeDir || ((GetFocus() == GetDlgItem(hDlg, lst2)) && (pOFN->Flags & OFN_DIRSELCHANGED))) { bChangeDir = FALSE; goto ChangingDir; } else if ((GetFocus() == (hwnd = GetDlgItem(hDlg, cmb2))) && (pOFN->Flags & OFN_DRIVEDOWN)) { // // If the focus is on the drive or filter combobox, give // a new listing. // SendDlgItemMessage(hDlg, cmb2, CB_SHOWDROPDOWN, FALSE, 0L); break; } else if ((GetFocus() == (hwnd = GetDlgItem(hDlg, cmb1))) && (pOFN->Flags & OFN_FILTERDOWN)) { SendDlgItemMessage(hDlg, cmb1, CB_SHOWDROPDOWN, FALSE, 0L); lParam = (LPARAM)hwnd; goto ChangingFilter; } else { // // Visual Basic passes in an uninitialized lpstrDefExt string. // Since we only have to use it in OKButtonPressed, update // lpstrDefExt here along with whatever else is only needed // in OKButtonPressed. // if (pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameA2WDelayed(pOFI); } if (OKButtonPressed(hDlg, pOFI, bSave)) { bRet = TRUE; if (pOFN->lpstrFile) { if (!(pOFN->Flags & OFN_NOVALIDATE)) { if (pOFN->nMaxFile >= 3) { if ((pOFN->lpstrFile[0] == 0) || (pOFN->lpstrFile[1] == 0) || (pOFN->lpstrFile[2] == 0)) { bRet = FALSE; StoreExtendedError(FNERR_BUFFERTOOSMALL); } } else { bRet = FALSE; StoreExtendedError(FNERR_BUFFERTOOSMALL); } } } goto AbortDialog; } } SendDlgItemMessage(hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1); return (TRUE); break; } case ( IDCANCEL ) : { bRet = FALSE; g_bUserPressedCancel = TRUE; goto AbortDialog; } case ( IDABORT ) : { bRet = (BYTE)lParam; AbortDialog: // // Return the most recently used filter. // pOFN->nFilterIndex = (WORD)SendDlgItemMessage( hDlg, cmb1, CB_GETCURSEL, (WPARAM)0, (LPARAM)0 ); if (pOFN->lpstrCustomFilter) { len = (WORD)(lstrlen(pOFN->lpstrCustomFilter) + 1); sCount = (WORD)lstrlen(pOFI->szLastFilter); if (pOFN->nMaxCustFilter > (DWORD)(sCount + len)) { EVAL(SUCCEEDED(StringCchCopy(pOFN->lpstrCustomFilter + len, pOFN->nMaxCustFilter - len, pOFI->szLastFilter))); // Always enough room } } if (!pOFN->lpstrCustomFilter || (*pOFN->lpstrCustomFilter == CHAR_NULL)) { pOFN->nFilterIndex++; } if (((GET_WM_COMMAND_ID(wParam, lParam)) == IDOK) && pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { ThunkOpenFileNameW2A(pOFI); bHookRet = (*lpfnHook)( hDlg, msgFILEOKA, 0, (LPARAM)pOFI->pOFNA ); // // For apps that side-effect pOFNA stuff and expect it to // be preserved through dialog exit, update internal // struct after the hook proc is called. // ThunkOpenFileNameA2W(pOFI); } else { bHookRet = (*lpfnHook)( hDlg, msgFILEOKW, 0, (LPARAM)pOFI->pOFN ); } if (bHookRet) { HourGlass(FALSE); break; } } wNoRedraw = 0; if (pOFI->pOFN->Flags & OFN_ENABLEHOOK) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); glpfnFileHook = lpfnHook; } RemoveProp(hDlg, FILEPROP); EndDialog(hDlg, bRet); if (pOFI) { if ((pOFN->Flags & OFN_NOCHANGEDIR) && *pOFI->szCurDir) { ChangeDir(hDlg, pOFI->szCurDir, TRUE, FALSE); } } // // WARNING: // If the app subclasses ID_ABORT, the worker thread will never // get exited. This will cause problems. Currently, there are // no apps that do this, though. // return (TRUE); break; } case ( edt1 ) : { if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE ) { int iIndex, iCount; HWND hLBox = GetDlgItem(hDlg, lst1); WORD wIndex = (WORD)SendMessage(hLBox, LB_GETCARETINDEX, 0, 0); szText[0] = CHAR_NULL; if (wIndex == (WORD)LB_ERR) { break; } SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), WM_GETTEXT, (WPARAM)MAX_FULLPATHNAME, (LPARAM)szText ); if ((iIndex = (int)SendMessage( hLBox, LB_FINDSTRING, (WPARAM)(wIndex - 1), (LPARAM)szText )) != LB_ERR) { RECT rRect; iCount = (int)SendMessage(hLBox, LB_GETTOPINDEX, 0, 0L); GetClientRect(hLBox, (LPRECT)&rRect); if ((iIndex < iCount) || (iIndex >= (iCount + rRect.bottom / dyText))) { SendMessage(hLBox, LB_SETCARETINDEX, (WPARAM)iIndex, 0); SendMessage(hLBox, LB_SETTOPINDEX, (WPARAM)iIndex, 0); } } return (TRUE); } else if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_SETFOCUS ) { SetModeBias(MODEBIASMODE_FILENAME); } else if ( GET_WM_COMMAND_CMD(wParam, lParam)== EN_KILLFOCUS ) { SetModeBias(MODEBIASMODE_DEFAULT); } break; } case ( lst1 ) : { // // A double click means OK. // if (GET_WM_COMMAND_CMD(wParam, lParam)== LBN_DBLCLK) { SendMessage(hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(IDOK, 0, 0)); return (TRUE); } else if (pOFN && (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)) { if (pOFN->Flags & OFN_ALLOWMULTISELECT) { int *pSelIndex; // // Muliselection allowed. // sCount = (SHORT)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETSELCOUNT, 0, 0L ); if (!sCount) { // // If nothing selected, clear edit control. // SetDlgItemText(hDlg, edt1, szNull); } else { DWORD cchMemBlockSize = 2048; DWORD cchTotalLength = 0; pSelIndex = (int *)LocalAlloc(LPTR, sCount * sizeof(int)); if (!pSelIndex) { goto LocalFailure1; } sCount = (SHORT)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETSELITEMS, (WPARAM)sCount, (LONG_PTR)(LPTSTR)pSelIndex ); pch2 = pch = (LPTSTR) LocalAlloc(LPTR, cchMemBlockSize * sizeof(TCHAR)); if (!pch) { goto LocalFailure2; } for (*pch = CHAR_NULL, i = 0; i < sCount; i++) { len = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETTEXTLEN, (WPARAM)(*(pSelIndex + i)), (LPARAM)0 ); // // Add the length of the selected file to the // total length of selected files. + 2 for the // space that goes in between files and for the // possible dot added at the end of the filename // if the file does not have an extension. // cchTotalLength += (len + 2); if (cchTotalLength > cchMemBlockSize) { LPTSTR pTemp; UINT cchPrevLen = cchTotalLength - (len + 2); cchMemBlockSize = cchMemBlockSize << 1; pTemp = (LPTSTR)LocalReAlloc( pch, cchMemBlockSize * sizeof(TCHAR), LMEM_MOVEABLE ); if (pTemp) { pch = pTemp; pch2 = pch + cchPrevLen; } else { LocalFree(pch); goto LocalFailure2; } } SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETTEXT, (WPARAM)(*(pSelIndex + i)), (LONG_PTR)pch2 ); if (!StrChr(pch2, CHAR_DOT)) { *(pch2 + len++) = CHAR_DOT; } pch2 += len; *pch2++ = CHAR_SPACE; } if (pch2 != pch) { *--pch2 = CHAR_NULL; } SetDlgItemText(hDlg, edt1, pch); LocalFree((HANDLE)pch); LocalFailure2: LocalFree((HANDLE)pSelIndex); } LocalFailure1: if (pOFN->lpfnHook) { i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCARETINDEX, 0, 0L ); if (!(i & 0x8000)) { wFlag = (SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETSEL, (WPARAM)i, 0L ) ? CD_LBSELADD : CD_LBSELSUB); } else { wFlag = CD_LBSELNOITEMS; } } } else { // // Multiselection is not allowed. // Put the file name in the edit control. // szText[0] = CHAR_NULL; i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL, 0, 0L ); if (i != (WORD)LB_ERR) { i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETTEXT, (WPARAM)i, (LONG_PTR)szText ); if (!StrChr(szText, CHAR_DOT)) { if (i < MAX_FULLPATHNAME - 1) { szText[i] = CHAR_DOT; szText[i + 1] = CHAR_NULL; } } if (!bCasePreserved) { CharLower(szText); } SetDlgItemText(hDlg, edt1, szText); if (pOFN->lpfnHook) { i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL, 0, 0L ); wFlag = CD_LBSELCHANGE; } } } if (pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { (*lpfnHook)( hDlg, msgLBCHANGEA, lst1, MAKELONG(i, wFlag) ); } else { (*lpfnHook)( hDlg, msgLBCHANGEW, lst1, MAKELONG(i, wFlag) ); } } SendDlgItemMessage(hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1); return (TRUE); } break; } case ( cmb1 ) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case ( CBN_DROPDOWN ) : { if (wWinVer >= 0x030A) { pOFN->Flags |= OFN_FILTERDOWN; } return (TRUE); break; } case ( CBN_CLOSEUP ) : { PostMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(cmb1, lParam, MYCBN_DRAW) ); return (TRUE); break; } case ( CBN_SELCHANGE ) : { // // Need to change the file listing in lst1. // if (pOFN->Flags & OFN_FILTERDOWN) { return (TRUE); break; } } case ( MYCBN_DRAW ) : { SHORT nIndex; LPCTSTR lpFilter; HourGlass(TRUE); pOFN->Flags &= ~OFN_FILTERDOWN; ChangingFilter: nIndex = (SHORT)SendDlgItemMessage( hDlg, cmb1, CB_GETCURSEL, 0, 0L ); if (nIndex < 0) { // // No current selection. // break; } // // Must also check if filter contains anything. // if (nIndex || !(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter)) { lpFilter = pOFN->lpstrFilter + SendDlgItemMessage( hDlg, cmb1, CB_GETITEMDATA, (WPARAM)nIndex, 0L ); } else { lpFilter = pOFN->lpstrCustomFilter + lstrlen(pOFN->lpstrCustomFilter) + 1; } if (*lpFilter) { GetDlgItemText( hDlg, edt1, szText, MAX_FULLPATHNAME - 1 ); bRet = (!szText[0] || (StrChr(szText, CHAR_STAR)) || (StrChr(szText, CHAR_QMARK))); if (SUCCEEDED(StringCchCopy(szText, ARRAYSIZE(szText), lpFilter))) { if (bRet) { CharLower(szText); SetDlgItemText(hDlg, edt1, szText); SendDlgItemMessage( hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1 ); } FListAll(pOFI, hDlg, szText, ARRAYSIZE(szText)); if (!bInitializing) { EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), szText))); // szText is smaller than pOFI->szLastFilter // // Provide dynamic lpstrDefExt updating // when lpstrDefExt is user initialized. // if (StrChr((LPTSTR)lpFilter, CHAR_DOT) && pOFN->lpstrDefExt) { DWORD cbLen = MIN_DEFEXT_LEN - 1; // only 1st 3 LPTSTR lpTemp = (LPTSTR)(pOFN->lpstrDefExt); while (*lpFilter++ != CHAR_DOT); if (!(StrChr((LPTSTR)lpFilter, CHAR_STAR)) && !(StrChr((LPTSTR)lpFilter, CHAR_QMARK))) { while (cbLen--) { *lpTemp++ = *lpFilter++; } *lpTemp = CHAR_NULL; } } } } } if (pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { (*lpfnHook)( hDlg, msgLBCHANGEA, cmb1, MAKELONG(nIndex, CD_LBSELCHANGE) ); } else { (*lpfnHook)( hDlg, msgLBCHANGEW, cmb1, MAKELONG(nIndex, CD_LBSELCHANGE) ); } } HourGlass(FALSE); return (TRUE); break; } default : { break; } } break; } case ( lst2 ) : { if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE) { if (!(pOFN->Flags & OFN_DIRSELCHANGED)) { if ((DWORD)SendDlgItemMessage( hDlg, lst2, LB_GETCURSEL, 0, 0L ) != pOFI->idirSub - 1) { StripFileName(hDlg, IS16BITWOWAPP(pOFN)); pOFN->Flags |= OFN_DIRSELCHANGED; } } return (TRUE); } else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SETFOCUS) { EnableWindow(GetDlgItem(hDlg, IDOK), TRUE); SendMessage( GetDlgItem(hDlg, IDCANCEL), BM_SETSTYLE, (WPARAM)BS_PUSHBUTTON, (LPARAM)TRUE ); } else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_KILLFOCUS) { if (pOFN && (pOFN->Flags & OFN_DIRSELCHANGED)) { pOFN->Flags &= ~OFN_DIRSELCHANGED; } else { bChangeDir = FALSE; } } else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_DBLCLK) { TCHAR szNextDir[CCHNETPATH]; LPTSTR lpCurDir; DWORD idir; DWORD idirNew; int cb; LPTSTR pstrPath = NULL; ChangingDir: bChangeDir = FALSE; pOFN->Flags &= ~OFN_DIRSELCHANGED; idirNew = (DWORD)SendDlgItemMessage( hDlg, lst2, LB_GETCURSEL, 0, 0L ); // // Can use relative path name. // *pOFI->szPath = 0; if (idirNew >= pOFI->idirSub) { cb = (int) SendDlgItemMessage( hDlg, lst2, LB_GETTEXT, (WPARAM)idirNew, (LPARAM)pOFI->szPath ); // // sanity check // if (!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) || !(lpCurDir = lpCurDlg->lpstrCurDir)) { break; } if (SUCCEEDED(StringCchCopy(szNextDir, ARRAYSIZE(szNextDir), lpCurDir)) && PathAddBackslash(szNextDir)) { // // Fix phenom with c:\\foobar - because of inconsistency // in directory display guaranteed to have a valid // lpCurDir here, right? // if (SUCCEEDED(StringCchCat(szNextDir, ARRAYSIZE(szNextDir), pOFI->szPath))) { pstrPath = szNextDir; idirNew = pOFI->idirSub; // for msgLBCHANGE message } } } else { // // Need full path name. // cb = (int) SendDlgItemMessage( hDlg, lst2, LB_GETTEXT, 0, (LPARAM)pOFI->szPath ); // // The following condition is necessary because wb displays // \\server\share (the disk resource name) for unc, but // for root paths (eg. c:\) for device conns, this in- // consistency is hacked around here and in FillOutPath. // if (DBL_BSLASH(pOFI->szPath) && SUCCEEDED(StringCchCat(pOFI->szPath, ARRAYSIZE(pOFI->szPath), L"\\"))) { cb++; } for (idir = 1; idir <= idirNew; ++idir) { cb += (int) SendDlgItemMessage( hDlg, lst2, LB_GETTEXT, (WPARAM)idir, (LPARAM)&pOFI->szPath[cb] ); pOFI->szPath[cb++] = CHAR_BSLASH; } // // The root is a special case. // if (idirNew) { pOFI->szPath[cb - 1] = CHAR_NULL; } pstrPath = pOFI->szPath; } if (!pstrPath || !*pstrPath || (ChangeDir(hDlg, pstrPath, FALSE, TRUE) == CHANGEDIR_FAILED)) { break; } // // List all directories under this one. // UpdateListBoxes(hDlg, pOFI, NULL, mskDirectory); if (pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { (*lpfnHook)( hDlg, msgLBCHANGEA, lst2, MAKELONG(LOWORD(idirNew), CD_LBSELCHANGE) ); } else { (*lpfnHook)( hDlg, msgLBCHANGEW, lst2, MAKELONG(LOWORD(idirNew), CD_LBSELCHANGE) ); } } return (TRUE); } break; } case ( cmb2 ) : { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case ( CBN_DROPDOWN ) : { pOFN->Flags |= OFN_DRIVEDOWN; return (TRUE); break; } case ( CBN_CLOSEUP ) : { // // It would seem reasonable to merely do the update // at this point, but that would rely on message // ordering, which isnt a smart move. In fact, if // you hit ALT-DOWNARROW, DOWNARROW, ALT-DOWNARROW, // you receive CBN_DROPDOWN, CBN_SELCHANGE, and then // CBN_CLOSEUP. But if you use the mouse to choose // the same element, the last two messages trade // places. PostMessage allows all messages in the // sequence to be processed, and then updates are // done as needed. // PostMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS( cmb2, GET_WM_COMMAND_HWND(wParam, lParam), MYCBN_DRAW ) ); return (TRUE); break; } case ( MYCBN_LIST ) : { LoadDrives(hDlg); break; } case ( MYCBN_REPAINT ) : { int cchCurDir; LPTSTR lpCurDir; // sanity if (!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) || !(lpCurDir = lpCurDlg->lpstrCurDir)) { break; } cchCurDir = GetPathOffset(lpCurDir); if (cchCurDir != -1) { TCHAR szRepaintDir[CCHNETPATH]; HWND hCmb2 = (HWND)lParam; if (SUCCEEDED(StringCchCopy(szRepaintDir, ARRAYSIZE(szRepaintDir), lpCurDir))) { szRepaintDir[cchCurDir] = CHAR_NULL; SendMessage(hCmb2, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)szRepaintDir); } } break; } case ( CBN_SELCHANGE ) : { StripFileName(hDlg, IS16BITWOWAPP(pOFN)); // // Version check not needed, since flag never set // for versions not supporting CBN_CLOSEUP. Putting // check at CBN_DROPDOWN is more efficient since it // is less frequent than CBN_SELCHANGE. if (pOFN->Flags & OFN_DRIVEDOWN) { // // Don't fill lst2 while the combobox is down. // return (TRUE); break; } } case ( MYCBN_CHANGEDIR ) : case ( MYCBN_DRAW ) : { TCHAR szTitle[WARNINGMSGLENGTH]; LPTSTR lpFilter; int nDiskInd, nInd; DWORD dwType = 0; LPTSTR lpszPath = NULL; LPTSTR lpszDisk = NULL; HWND hCmb2; OFN_DISKINFO *pofndiDisk = NULL; static TCHAR szDrawDir[CCHNETPATH]; int nRet; HourGlass(TRUE); // // Clear Flag for future CBN_SELCHANGE messeges. // pOFN->Flags &= ~OFN_DRIVEDOWN; // // Change the drive. // szText[0] = CHAR_NULL; hCmb2 = (HWND)lParam; if (hCmb2 != NULL) { nInd = (int) SendMessage(hCmb2, CB_GETCURSEL, 0, 0L); if (nInd != CB_ERR) { SendMessage( hCmb2, CB_GETLBTEXT, nInd, (LPARAM)szDrawDir ); } if ((nInd == CB_ERR) || ((INT_PTR)pofndiDisk == CB_ERR)) { if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) { if (lpCurDlg->lpstrCurDir && (lstrlen(lpCurDlg->lpstrCurDir) < ARRAYSIZE(szDrawDir))) { // Don't touch szDrawDir unless there we are assured successful copy StringCchCopy(szDrawDir, ARRAYSIZE(szDrawDir), lpCurDlg->lpstrCurDir); } } } CharLower(szDrawDir); // // Should always succeed. // nDiskInd = DiskAddedPreviously(0, szDrawDir); if (nDiskInd != 0xFFFFFFFF) { pofndiDisk = &gaDiskInfo[nDiskInd]; } else { // // Skip update in the case where it fails. // return (TRUE); } dwType = pofndiDisk->dwType; lpszDisk = pofndiDisk->lpPath; } if ((GET_WM_COMMAND_CMD(wParam, lParam)) == MYCBN_CHANGEDIR) { if (lpNetDriveSync) { lpszPath = lpNetDriveSync; lpNetDriveSync = NULL; } else { if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) { if (lpCurDlg->lpstrCurDir && (lstrlen(lpCurDlg->lpstrCurDir) < ARRAYSIZE(szDrawDir))) { // Don't touch szDrawDir unless we are assured a successful copy. StringCchCopy(szDrawDir, ARRAYSIZE(szDrawDir), lpCurDlg->lpstrCurDir); lpszPath = szDrawDir; } } } } else { lpszPath = lpszDisk; } if (bInitializing) { lpFilter = szTitle; if (pOFN->lpstrFile && (StrChr(pOFN->lpstrFile, CHAR_STAR) || StrChr(pOFN->lpstrFile, CHAR_QMARK))) { if (FAILED(StringCchCopy(szTitle, ARRAYSIZE(szTitle), pOFN->lpstrFile))) { goto NullSearch; // Don't use filter string. } } else { HWND hcmb1 = GetDlgItem(hDlg, cmb1); nInd = (int) SendMessage(hcmb1, CB_GETCURSEL, 0, 0L); if (nInd == CB_ERR) { // // No current selection. // goto NullSearch; } // // Must also check if filter contains anything. // if (nInd || !(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter)) { lpFilter = (LPTSTR)(pOFN->lpstrFilter); lpFilter += SendMessage( hcmb1, CB_GETITEMDATA, (WPARAM)nInd, 0 ); } else { lpFilter = pOFN->lpstrCustomFilter; lpFilter += lstrlen(pOFN->lpstrCustomFilter) + 1; } } } else { NullSearch: lpFilter = NULL; } // // UpdateListBoxes cuts up filter string in place. // if (lpFilter) { // It's possible these overlap, since we have done a lpFilter = szTitle if (SUCCEEDED(StringCchCopyOverlap(szTitle, ARRAYSIZE(szTitle), lpFilter))) { CharLower(szTitle); } else { lpFilter = NULL; // Don't use filter string. } } if (dwType == REMDRVBMP) { DWORD err = WNetRestoreConnection(hDlg, lpszDisk); if (err != WN_SUCCESS) { HourGlass(FALSE); return (TRUE); } pofndiDisk->dwType = NETDRVBMP; SendMessage( hCmb2, CB_SETITEMDATA, (WPARAM)SendMessage( hCmb2, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)(LPTSTR)pofndiDisk->lpAbbrName ), (LPARAM)NETDRVBMP ); } // // Calls to ChangeDir will call SelDisk, so no need // to update cmb2 on our own here (used to be after // updatelistboxes). // if ((nRet = ChangeDir( hDlg, lpszPath, FALSE, FALSE )) == CHANGEDIR_FAILED) { int mbRet; while (nRet == CHANGEDIR_FAILED) { if (dwType == FLOPPYBMP) { mbRet = InvalidFileWarning( hDlg, lpszPath, ERROR_NO_DISK_IN_DRIVE, (UINT)(MB_RETRYCANCEL | MB_ICONEXCLAMATION)); } else if (dwType == CDDRVBMP) { mbRet = InvalidFileWarning( hDlg, lpszPath, ERROR_NO_DISK_IN_CDROM, (UINT)(MB_RETRYCANCEL | MB_ICONEXCLAMATION) ); } else { // // See if it's a RAW volume. // if (dwType == HARDDRVBMP && GetLastError() == ERROR_UNRECOGNIZED_VOLUME) { mbRet = InvalidFileWarning( hDlg, lpszPath, ERROR_UNRECOGNIZED_VOLUME, (UINT)(MB_OK | MB_ICONEXCLAMATION) ); } else { mbRet = InvalidFileWarning( hDlg, lpszPath, ERROR_DIR_ACCESS_DENIED, (UINT)(MB_RETRYCANCEL | MB_ICONEXCLAMATION) ); } } if (bFirstTime || (mbRet != IDRETRY)) { lpszPath = NULL; nRet = ChangeDir(hDlg, lpszPath, TRUE, FALSE); } else { nRet = ChangeDir(hDlg, lpszPath, FALSE, FALSE); } } } UpdateListBoxes( hDlg, pOFI, lpFilter ? szTitle : lpFilter, (WORD)(mskDrives | mskDirectory) ); if (pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); nInd = (int) SendDlgItemMessage( hDlg, cmb2, CB_GETCURSEL, 0, 0 ); if (pOFI->ApiType == COMDLG_ANSI) { (*lpfnHook)( hDlg, msgLBCHANGEA, cmb2, MAKELONG(LOWORD(nInd), CD_LBSELCHANGE) ); } else { (*lpfnHook)( hDlg, msgLBCHANGEW, cmb2, MAKELONG(LOWORD(nInd), CD_LBSELCHANGE) ); } } HourGlass(FALSE); return (TRUE); break; } default : { break; } } break; } case ( pshHelp ) : { if (pOFI->ApiType == COMDLG_ANSI) { if (msgHELPA && pOFN->hwndOwner) { SendMessage( pOFN->hwndOwner, msgHELPA, (WPARAM)hDlg, (DWORD_PTR)pOFN ); } } else { if (msgHELPW && pOFN->hwndOwner) { SendMessage( pOFN->hwndOwner, msgHELPW, (WPARAM)hDlg, (DWORD_PTR)pOFN ); } } break; } case ( psh14 ) : { bGetNetDrivesSync = TRUE; if (CallNetDlg(hDlg)) { LNDSetEvent(hDlg); } else { bGetNetDrivesSync = FALSE; } break; } default : { break; } } return (FALSE); } //////////////////////////////////////////////////////////////////////////// // // UpdateListBoxes // // Fills out File and Directory List Boxes in a single pass // given (potentially) multiple filters // // It assumes the string of extensions are delimited by semicolons. // // hDlg Handle to File Open/Save dialog // pOFI pointer to OPENFILEINFO structure // lpszFilter pointer to filter, if NULL, use pOFI->szSpecCur // wMask mskDirectory and/or mskDrives, or NULL // // Returns: TRUE if match // FALSE if not // //////////////////////////////////////////////////////////////////////////// BOOL UpdateListBoxes( HWND hDlg, POPENFILEINFO pOFI, LPTSTR lpszFilter, WORD wMask) { LPTSTR lpszF[MAXFILTERS + 1]; LPTSTR lpszTemp; SHORT i, nFilters; HWND hFileList = GetDlgItem(hDlg, lst1); HWND hDirList = GetDlgItem(hDlg, lst2); BOOL bRet = FALSE; TCHAR szSpec[MAX_FULLPATHNAME]; BOOL bDriveChange; BOOL bFindAll = FALSE; RECT rDirLBox; BOOL bLFN; HANDLE hff; DWORD dwErr; WIN32_FIND_DATA FindFileData; TCHAR szBuffer[MAX_FULLPATHNAME]; // add one for CHAR_DOT WORD wCount; LPCURDLG lpCurDlg; // // Save the drive bit and then clear it out. // bDriveChange = wMask & mskDrives; wMask &= ~mskDrives; if (!lpszFilter) { lpszFilter = szSpec; GetDlgItemText( hDlg, edt1, szSpec, ARRAYSIZE(szSpec) - 1); // // If any directory or drive characters are in there, or if there // are no wildcards, use the default spec. // if ( StrChr(szSpec, CHAR_BSLASH) || StrChr(szSpec, CHAR_SLASH) || StrChr(szSpec, CHAR_COLON) || (!((StrChr(szSpec, CHAR_STAR)) || (StrChr(szSpec, CHAR_QMARK)))) ) { EVAL(SUCCEEDED(StringCchCopy(szSpec, ARRAYSIZE(szSpec), pOFI->szSpecCur))); } else { // szSpec comes from the editbox, which is limited to MAX_PATH chars, so this should always succeed. EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), szSpec))); } } // // We need to find out what kind of a drive we are running // on in order to determine if spaces are valid in a filename // or not. // bLFN = IsLFNDriveX(hDlg, TEXT("\0")); // // Find the first filter in the string, and add it to the // array. // if (bLFN) { lpszF[nFilters = 0] = lstrtok(lpszFilter, szSemiColonTab); } else { lpszF[nFilters = 0] = lstrtok(lpszFilter, szSemiColonSpaceTab); } // // Now we are going to loop through all the filters in the string // parsing the one we already have, and then finding the next one // and starting the loop over again. // while (lpszF[nFilters] && (nFilters < MAXFILTERS)) { // // Check to see if the first character is a space. // If so, remove the spaces, and save the pointer // back into the same spot. Why? because the // FindFirstFile/Next api will _still_ work on // filenames that begin with a space because // they also look at the short names. The // short names will begin with the same first // real letter as the long filename. For // example, the long filename is " my document" // the first letter of this short name is "m", // so searching on "m*.*" or " m*.*" will yield // the same results. // if (bLFN && (*lpszF[nFilters] == CHAR_SPACE)) { lpszTemp = lpszF[nFilters]; while ((*lpszTemp == CHAR_SPACE) && *lpszTemp) { lpszTemp = CharNext(lpszTemp); } lpszF[nFilters] = lpszTemp; } // // The original code used to do a CharUpper here to put the // filter strings in upper case. EG: *.TXT However, this // is not a good thing to do for Turkish. Capital 'i' does // not equal 'I', so the CharUpper is being removed. // // CharUpper(lpszF[nFilters]); // // Compare the filter with *.*. If we find *.* then // set the boolean bFindAll, and this will cause the // files listbox to be filled in at the same time the // directories listbox is filled. This saves time // from walking the directory twice (once for the directory // names and once for the filenames). // if (!lstrcmpi(lpszF[nFilters], szStarDotStar)) { bFindAll = TRUE; } // // Now we need to check if this filter is a duplicate // of an already existing filter. // for (wCount = 0; wCount < nFilters; wCount++) { // // If we find a duplicate, decrement the current // index pointer by one so that the last location // is written over (thus removing the duplicate), // and break out of this loop. // if (!lstrcmpi(lpszF[nFilters], lpszF[wCount])) { nFilters--; break; } } // // Ready to move on to the next filter. Find the next // filter based upon the type of file system we're using. // if (bLFN) { lpszF[++nFilters] = lstrtok(NULL, szSemiColonTab); } else { lpszF[++nFilters] = lstrtok(NULL, szSemiColonSpaceTab); } // // In case we found a pointer to NULL, then look for the // next filter. // while (lpszF[nFilters] && !*lpszF[nFilters]) { if (bLFN) { lpszF[nFilters] = lstrtok(NULL, szSemiColonTab); } else { lpszF[nFilters] = lstrtok(NULL, szSemiColonSpaceTab); } } } // // Add NULL terminator only if needed. // if (nFilters >= MAXFILTERS) { lpszF[MAXFILTERS] = 0; } HourGlass(TRUE); SendMessage(hFileList, WM_SETREDRAW, FALSE, 0L); SendMessage(hFileList, LB_RESETCONTENT, 0, 0L); if (wMask & mskDirectory) { wNoRedraw |= 2; // HACK!!! WM_SETREDRAW isn't complete SendMessage(hDirList, WM_SETREDRAW, FALSE, 0L); // // LB_RESETCONTENT causes InvalidateRect(hDirList, 0, TRUE) to be // sent as well as repositioning the scrollbar thumb and drawing // it immediately. This causes flicker when the LB_SETCURSEL is // made, as it clears out the listbox by erasing the background of // each item. // SendMessage(hDirList, LB_RESETCONTENT, 0, 0L); } // // Always open enumeration for *.* // lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); SetCurrentDirectory(lpCurDlg ? lpCurDlg->lpstrCurDir : NULL); hff = FindFirstFile(szStarDotStar, &FindFileData); if ( hff == INVALID_HANDLE_VALUE) { // // Error. Call GetLastError to determine what happened. // dwErr = GetLastError(); // // With the ChangeDir logic handling AccessDenied for cds, // if we are not allowed to enum files, that's ok, just get out. // if (dwErr == ERROR_ACCESS_DENIED) { wMask = mskDirectory; goto Func4EFailure; } // // For bad path of bad filename. // if (dwErr != ERROR_FILE_NOT_FOUND) { wMask = mskDrives; goto Func4EFailure; } } // // A listing was made, even if empty. // bRet = TRUE; wMask &= mskDirectory; // // GetLastError says no more files. // if (hff == INVALID_HANDLE_VALUE && dwErr == ERROR_FILE_NOT_FOUND) { // // Things went well, but there are no files. // goto NoMoreFilesFound; } do { if (pOFI->pOFN->Flags & OFN_NOLONGNAMES) { UNICODE_STRING Name; BOOLEAN fSpace = FALSE; if (NT_SUCCESS(RtlInitUnicodeStringEx(&Name, FindFileData.cFileName)) && RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace) { // // Legal 8.3 name and no spaces, so use the principal // file name. // EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName))); } else { if (FindFileData.cAlternateFileName[0] == CHAR_NULL) { continue; } // // Use the alternate file name. // EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName))); } } else { EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName))); } if ((FindFileData.dwFileAttributes & EXCLBITS)) { continue; } if ((pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT)) { if (StrChr(szBuffer, CHAR_SPACE)) { // // HPFS does not support alternate filenames // for multiselect, bump all spacey filenames. // if (FindFileData.cAlternateFileName[0] == CHAR_NULL) { continue; } EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName))); } } if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (wMask & mskDirectory) { // // Don't include the subdirectories "." and "..". // if (szBuffer[0] == CHAR_DOT) { if ((szBuffer[1] == CHAR_NULL) || ((szBuffer[1] == CHAR_DOT) && (szBuffer[2] == CHAR_NULL))) { continue; } } if (!bCasePreserved) { CharLower(szBuffer); } i = (WORD)SendMessage( hDirList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer ); } } else if (bFindAll) { if (!bCasePreserved) { CharLower(szBuffer); } SendMessage(hFileList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer); } } while (FindNextFile(hff, &FindFileData)); if (hff == INVALID_HANDLE_VALUE) { goto Func4EFailure; } FindClose(hff); if (!bFindAll) { for (i = 0; lpszF[i]; i++) { if (!lstrcmpi(lpszF[i], szStarDotStar)) { continue; } // // Find First for each filter. // hff = FindFirstFile(lpszF[i], &FindFileData); if (hff == INVALID_HANDLE_VALUE) { DWORD dwErr = GetLastError(); if ((dwErr == ERROR_FILE_NOT_FOUND) || (dwErr == ERROR_INVALID_NAME)) { // // Things went well, but there are no files. // continue; } else { wMask = mskDrives; goto Func4EFailure; } } do { if (pOFI->pOFN->Flags & OFN_NOLONGNAMES) { UNICODE_STRING Name; BOOLEAN fSpace = FALSE; if (NT_SUCCESS(RtlInitUnicodeStringEx(&Name, FindFileData.cFileName)) && RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace) { // // Legal 8.3 name and no spaces, so use the principal // file name. // EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName))); } else { if (FindFileData.cAlternateFileName[0] == CHAR_NULL) { continue; } // // Use the alternate file name. // EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName))); } } else { EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName))); if (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) { if (StrChr(szBuffer, CHAR_SPACE)) { // // HPFS does not support alternate filenames // for multiselect, bump all spacey filenames. // if (FindFileData.cAlternateFileName[0] == CHAR_NULL) { continue; } EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName))); } } } if ((FindFileData.dwFileAttributes & EXCLBITS) || (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { continue; } if (!bCasePreserved) { CharLower(szBuffer); } SendMessage(hFileList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer); } while (FindNextFile(hff, &FindFileData)); if (hff != INVALID_HANDLE_VALUE) { FindClose(hff); } } } NoMoreFilesFound: Func4EFailure: if (wMask) { if (wMask == mskDirectory) { LPTSTR lpCurDir = NULL; if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) { lpCurDir = lpCurDlg->lpstrCurDir; } FillOutPath(hDirList, pOFI); // // The win31 way of chopping the text by just passing // it on to user doesn't work for unc names since user // doesn't see the drivelessness of them (thinks drive is // a bslash char). So, special case it here. // EVAL(SUCCEEDED(StringCchCopy(pOFI->szPath, ARRAYSIZE(pOFI->szPath), lpCurDir))); if (DBL_BSLASH(pOFI->szPath)) { SetDlgItemText(hDlg, stc1, ChopText(hDlg, stc1, pOFI->szPath)); } else { DlgDirList(hDlg, pOFI->szPath, 0, stc1, DDL_READONLY); } SendMessage(hDirList, LB_SETCURSEL, pOFI->idirSub - 1, 0L); if (bDriveChange) { // // The design here is to show the selected drive whenever the // user changes drives, or whenever the number of // subdirectories is sufficiently low to allow them to be // shown along with the drive. Otherwise, show the // immediate parent and all the children that can be shown. // This all was done to meet the UITF spec. // i = 0; } else { // // Show as many children as possible. // if ((i = (SHORT)(pOFI->idirSub - 2)) < 0) { i = 0; } } // // LB_SETTOPINDEX must be after LB_SETCURSEL, as LB_SETCURSEL will // alter the top index to bring the current selection into view. // SendMessage(hDirList, LB_SETTOPINDEX, (WPARAM)i, 0L); } else { SetDlgItemText(hDlg, stc1, szNull); } wNoRedraw &= ~2; SendMessage(hDirList, WM_SETREDRAW, TRUE, 0L); GetWindowRect(hDirList, (LPRECT)&rDirLBox); rDirLBox.left++, rDirLBox.top++; rDirLBox.right--, rDirLBox.bottom--; MapWindowPoints(NULL, hDlg, (LPPOINT)&rDirLBox, 2); // // If there are less than enough directories to fill the listbox, // Win 3.0 doesn't clear out the bottom. Pass TRUE as the last // parameter to demand a WM_ERASEBACKGROUND message. // InvalidateRect(hDlg, (LPRECT)&rDirLBox, (BOOL)(wWinVer < 0x030A)); } SendMessage(hFileList, WM_SETREDRAW, TRUE, 0L); InvalidateRect(hFileList, (LPRECT)0, (BOOL)TRUE); #ifndef WIN32 ResetDTAAddress(); #endif HourGlass(FALSE); return (bRet); } // Stores in pOFN->lpstrFile void StoreFileSizeInOFN(LPOPENFILENAME pOFN, UINT cch) { ASSERT(cch >= pOFN->nMaxFile); if (pOFN->nMaxFile > 0) pOFN->lpstrFile[0] = (TCHAR)LOWORD(cch); if (pOFN->nMaxFile > 1) pOFN->lpstrFile[1] = (TCHAR)HIWORD(cch); if (pOFN->nMaxFile > 2) pOFN->lpstrFile[2] = CHAR_NULL; } // Copies pszPath into pOFN->lpstrFile if room, stores lstrlen(pszPath) otherwise void StorePathOrFileSizeInOFN(LPOPENFILENAME pOFN, LPTSTR pszPath) { if (pOFN->lpstrFile) { UINT cch = lstrlen(pszPath); if (cch < pOFN->nMaxFile) { StringCchCopy(pOFN->lpstrFile, pOFN->nMaxFile, pszPath); // Can never fail. } else { StoreFileSizeInOFN(pOFN, cch+1); } } } // // Fix bug where progman cannot OK a file being browsed for new // item because it has Execute only permission. // Returns FALSE if handle passed in was invalid, but not because of ERROR_ACCESS_DENIED // Otherwise returns TRUE. // HANDLE ProgManBugCreateFile(HANDLE hFile, LPCTSTR szPathName, DWORD *dwErrCode) { if (hFile == INVALID_HANDLE_VALUE) { *dwErrCode = GetLastError(); if (*dwErrCode == ERROR_ACCESS_DENIED) { hFile = CreateFile( szPathName, GENERIC_EXECUTE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE) { *dwErrCode = GetLastError(); } } } return hFile; } #define SP_NOERR 0 #define SP_INVALIDDRIVE 1 #define SP_PATHNOTFOUND 2 //////////////////////////////////////////////////////////////////////////// // // OKButtonPressed // // Note: There are 4 cases for validation of a file name: // 1) OFN_NOVALIDATE allows invalid characters // 2) No validation flags No invalid characters, but path need not exist // 3) OFN_PATHMUSTEXIST No invalid characters, path must exist // 4) OFN_FILEMUSTEXIST No invalid characters, path & file must exist // //////////////////////////////////////////////////////////////////////////// BOOL OKButtonPressed( HWND hDlg, POPENFILEINFO pOFI, BOOL bSave) { DWORD nErrCode = 0; DWORD cch; DWORD cchSearchPath; LPOPENFILENAME pOFN = pOFI->pOFN; int nFileOffset, nExtOffset; HANDLE hFile; BOOL bAddExt = FALSE; BOOL bUNCName = FALSE; int nTempOffset; TCHAR szPathName[MAX_FULLPATHNAME]; DWORD lRet; BOOL blfn; LPCURDLG lpCurDlg; TCHAR ch = 0; cch = GetUNCDirectoryFromLB(hDlg, lst2, pOFI); // Is it a tree headed by a UNC share? if (cch) { // If so, cch now points to the end of the entire name, and we might have // pOFI->szPath be something like \\foo\bar\bar\bar nTempOffset = (WORD)(DWORD)SendDlgItemMessage( hDlg, lst2, LB_GETTEXTLEN, 0, 0 ); // While nTempOffset points to the end of just the UNC part (so to \bar\bar) } else { nTempOffset = 0; // and of course cch == 0 } GetDlgItemText(hDlg, edt1, pOFI->szPath + cch, ARRAYSIZE(pOFI->szPath) - 1 - cch); if (cch) { // // If a drive or new UNC was specified, forget the old UNC. // if ((pOFI->szPath[cch + 1] == CHAR_COLON) || (DBL_BSLASH(pOFI->szPath + cch)) ) { StringCopyOverlap(pOFI->szPath, pOFI->szPath + cch); } else if ((ISBACKSLASH(pOFI->szPath, cch)) || (pOFI->szPath[cch] == CHAR_SLASH)) { // // If a directory from the root is given, put it immediately // after the \\server\share listing. // // For example, if the directory is \\foo\bar\bar\ba, the user typed \x in the editbox, // give us \\foo\bar\x EVAL(SUCCEEDED(StringCchCopy(pOFI->szPath + nTempOffset, ARRAYSIZE(pOFI->szPath) - nTempOffset, pOFI->szPath + cch))); } } if (pOFN->Flags & OFN_NOLONGNAMES) { blfn = FALSE; } else { blfn = IsLFNDriveX(hDlg, pOFI->szPath); } lRet = ParseFile(pOFI->szPath, blfn, IS16BITWOWAPP(pOFN), FALSE); nFileOffset = (int)(SHORT)LOWORD(lRet); nExtOffset = (int)(SHORT)HIWORD(lRet); if (nFileOffset == PARSE_EMPTYSTRING) { UpdateListBoxes(hDlg, pOFI, NULL, 0); return (FALSE); } else if ((nFileOffset != PARSE_DIRECTORYNAME) && (pOFN->Flags & OFN_NOVALIDATE)) { pOFN->nFileOffset = (WORD)(nFileOffset >= 0 ? nFileOffset : lstrlen(pOFI->szPath)); // point at NULL in error case pOFN->nFileExtension = (WORD)nExtOffset; StorePathOrFileSizeInOFN(pOFN, pOFI->szPath); return (TRUE); } else if ((pOFN->Flags & OFN_ALLOWMULTISELECT) && SpacesExist(pOFI->szPath)) { return (MultiSelectOKButton(hDlg, pOFI, bSave)); } else if (pOFI->szPath[nExtOffset] == CHAR_SEMICOLON) { pOFI->szPath[nExtOffset] = CHAR_NULL; nFileOffset = (int)(SHORT)LOWORD(ParseFile( pOFI->szPath, blfn, IS16BITWOWAPP(pOFN), FALSE )); pOFI->szPath[nExtOffset] = CHAR_SEMICOLON; if ( (nFileOffset >= 0) && (StrChr(pOFI->szPath + nFileOffset, CHAR_STAR) || StrChr(pOFI->szPath + nFileOffset, CHAR_QMARK)) ) { EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFI->szPath + nFileOffset))); if (FListAll(pOFI, hDlg, pOFI->szPath, ARRAYSIZE(pOFI->szPath)) == CHANGEDIR_FAILED) { // // Conform with cchSearchPath error code settings in // PathCheck. // cchSearchPath = SP_PATHNOTFOUND; goto PathCheck; } return (FALSE); } else { nFileOffset = PARSE_INVALIDCHAR; goto Warning; } } else if (nFileOffset == PARSE_DIRECTORYNAME) { // // End with slash? // if ((ISBACKSLASH(pOFI->szPath, nExtOffset - 1)) || (pOFI->szPath[nExtOffset - 1] == CHAR_SLASH)) { // // ... and is not the root, get rid of the slash. // if ( (nExtOffset != 1) && (pOFI->szPath[nExtOffset - 2] != CHAR_COLON) && (nExtOffset != nTempOffset + 1) ) { pOFI->szPath[nExtOffset - 1] = CHAR_NULL; } } else if ((pOFI->szPath[nExtOffset - 1] == CHAR_DOT) && ((pOFI->szPath[nExtOffset - 2] == CHAR_DOT) || (ISBACKSLASH(pOFI->szPath, nExtOffset - 2)) || (pOFI->szPath[nExtOffset - 2] == CHAR_SLASH)) && ((DBL_BSLASH(pOFI->szPath)) || ((*(pOFI->szPath + 1) == CHAR_COLON) && (DBL_BSLASH(pOFI->szPath + 2))))) { pOFI->szPath[nExtOffset] = CHAR_BSLASH; pOFI->szPath[nExtOffset + 1] = CHAR_NULL; } // // Fall through to Directory Checking. // } else if (nFileOffset < 0) { // // Put in nErrCode so that call can be used from other points. // nErrCode = (DWORD)nFileOffset; Warning: // // If the disk is not a floppy and they tell me there's no // disk in the drive, dont believe it. Instead, put up the error // message that they should have given us. // (Note that the error message is checked first since checking // the drive type is slower.) // if (nErrCode == ERROR_ACCESS_DENIED) { if (bUNCName) { nErrCode = ERROR_NETWORK_ACCESS_DENIED; } else { szPathName[0] = CharLowerChar(szPathName[0]); if (GetDiskType(szPathName) == DRIVE_REMOTE) { nErrCode = ERROR_NETWORK_ACCESS_DENIED; } else if (GetDiskType(szPathName) == DRIVE_REMOVABLE) { nErrCode = ERROR_NO_DISK_IN_DRIVE; } else if (GetDiskType(szPathName) == DRIVE_CDROM) { nErrCode = ERROR_NO_DISK_IN_CDROM; } } } if ((nErrCode == ERROR_WRITE_PROTECT) || (nErrCode == ERROR_CANNOT_MAKE) || (nErrCode == ERROR_NO_DISK_IN_DRIVE) || (nErrCode == ERROR_NO_DISK_IN_CDROM)) { pOFI->szPath[0] = szPathName[0]; } InvalidFileWarning(hDlg, pOFI->szPath, nErrCode, 0); // // Can't cd case (don't want WM_ACTIVATE to setevent to GetNetDrives!). // Reset wNoRedraw. // wNoRedraw &= ~1; return (FALSE); } bUNCName = ((DBL_BSLASH(pOFI->szPath)) || ((*(pOFI->szPath + 1) == CHAR_COLON) && (DBL_BSLASH(pOFI->szPath + 2)))); ASSERT((nFileOffset >= 0 ) || (nFileOffset == PARSE_DIRECTORYNAME)); // PARSE_DIRECTORYNAME is the only error case handled below... nTempOffset = nFileOffset; // // Get the fully-qualified path. // { BOOL bSlash; BOOL bRet; WORD nNullOffset; // Chop of the file part of the path in pOFI->szPath for the time being, to ensure we're dealing with a directory. if (nFileOffset != PARSE_DIRECTORYNAME) { ch = *(pOFI->szPath + nFileOffset); *(pOFI->szPath + nFileOffset) = CHAR_NULL; nNullOffset = (WORD) nFileOffset; } // // For files of the format c:filename where c is not the // current directory, SearchPath does not return the curdir of c // so, prefetch it - should searchpath be changed? // if (nFileOffset > 0) { if (*(pOFI->szPath + nFileOffset - 1) == CHAR_COLON) { // // If it fails, fall through to the error generated below. // if (ChangeDir(hDlg, pOFI->szPath, FALSE, FALSE) != CHANGEDIR_FAILED) { // // Replace old null offset. // *(pOFI->szPath + nFileOffset) = ch; ch = *pOFI->szPath; // // Don't pass drive-colon into search path. // *pOFI->szPath = CHAR_NULL; nNullOffset = 0; } } } bSlash = (*pOFI->szPath == CHAR_SLASH); if (bSlash) { *pOFI->szPath = CHAR_BSLASH; } szPathName[0] = CHAR_NULL; HourGlass(TRUE); // // REARCHITECT: // Each wow thread can change the current directory. // Since searchpath doesn't check current dirs on a per thread basis, // reset it here and hope that we don't get interrupted between // setting and searching... // lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); SetCurrentDirectory(lpCurDlg ? lpCurDlg->lpstrCurDir : NULL); if (pOFI->szPath[0] == TEXT('\0')) // space for name (pretend it's valid for now) { EVAL(SUCCEEDED(StringCchCopyEx(szPathName, ARRAYSIZE(szPathName), (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL), NULL, NULL, STRSAFE_IGNORE_NULLS))); bRet = 1; } else { bRet = GetFullPathName( pOFI->szPath, ARRAYSIZE(szPathName), szPathName, NULL ); } // Now szPathName contains the current directory (if pOFI->szPath was empty), or pOFI->szPath (fully qualified path?) // Except in the failure case, where bRet == FALSE... like say for example we entered a bad drive: if (!bRet && (pOFI->szPath[1] == CHAR_COLON)) { int nDriveIndex = DiskAddedPreviously(pOFI->szPath[0], NULL); // // If it's a remembered connection, try to reconnect it. // if (nDriveIndex != 0xFFFFFFFF && gaDiskInfo[nDriveIndex].dwType == REMDRVBMP) { DWORD err = WNetRestoreConnection( hDlg, gaDiskInfo[nDriveIndex].lpPath ); if (err == WN_SUCCESS) { gaDiskInfo[nDriveIndex].dwType = NETDRVBMP; nDriveIndex = (int) SendDlgItemMessage( hDlg, cmb2, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)(LPTSTR)gaDiskInfo[nDriveIndex].lpPath ); SendDlgItemMessage( hDlg, cmb2, CB_SETITEMDATA, (WPARAM)nDriveIndex, (LPARAM)NETDRVBMP ); bRet = GetFullPathName( pOFI->szPath, MAX_FULLPATHNAME, szPathName, NULL); } } } HourGlass(FALSE); if (nFileOffset != PARSE_DIRECTORYNAME) { *(pOFI->szPath + nNullOffset) = ch; } if (bSlash) { *pOFI->szPath = CHAR_SLASH; } if (bRet) // We got something. { cchSearchPath = SP_NOERR; if (nFileOffset != PARSE_DIRECTORYNAME) { // Add file name to path // (Can't use PathCombine, because it only fits MAX_PATH) if (!ISBACKSLASH(szPathName, lstrlen(szPathName) - 1)) { EVAL(SUCCEEDED(StringCchCat(szPathName, ARRAYSIZE(szPathName), L"\\"))); // Shouldn't be longer than MAX_FULLPATH } EVAL(SUCCEEDED(StringCchCat(szPathName, ARRAYSIZE(szPathName), (pOFI->szPath + nFileOffset)))); // Shouldn't be longer than MAX_FULLPATH } else // we were given a directory name, try to go to that directory. { // // Hack to get around SearchPath inconsistencies. // // searching for c: returns c: // searching for server share dir1 .. returns server share // in these two cases bypass the regular ChangeDir call that // uses szPathName and use the original pOFI->szPath instead // OKButtonPressed needs to be simplified! // int cch = GetPathOffset(pOFI->szPath); // cch now points to the last \ in "c:\" or the "\\foo\bar\" if (cch > 0) { if (bUNCName) { // // If this fails, how is szPathName used? // szPathName's disk should equal pOFI->szPath's // so the cch will be valid. // szPathName[cch] = CHAR_BSLASH; szPathName[cch + 1] = CHAR_NULL; // Removing the file part of the path. if (ChangeDir( hDlg, pOFI->szPath, FALSE, TRUE ) != CHANGEDIR_FAILED) { goto ChangedDir; } } else { // It's a drive letter. We only changedir if there is nothing after the drive letter. if (!pOFI->szPath[cch]) { if (ChangeDir( hDlg, pOFI->szPath, FALSE, TRUE) != CHANGEDIR_FAILED) { goto ChangedDir; } } } } } } else // (!bRet) { // Some kind of invalid path... perhaps a search pattern? if (!(pOFN->Flags & OFN_PATHMUSTEXIST)) { EVAL(SUCCEEDED(StringCchCopy(szPathName, ARRAYSIZE(szPathName), pOFI->szPath))); // Must fit in MAX_FULLPATH } nErrCode = GetLastError(); if ((nErrCode == ERROR_INVALID_DRIVE) || (pOFI->szPath[1] == CHAR_COLON)) { cchSearchPath = SP_INVALIDDRIVE; } else { cchSearchPath = SP_PATHNOTFOUND; } } } // // Full pattern? // if ( !cchSearchPath && (nFileOffset >= 0) && ((StrChr(pOFI->szPath + nFileOffset, CHAR_STAR)) || (StrChr(pOFI->szPath + nFileOffset, CHAR_QMARK))) ) { // Search pattern.... TCHAR szSameDirFile[MAX_FULLPATHNAME]; if (nTempOffset > 0) { // // Must restore character in case it is part of the filename, // e.g. nTempOffset is 1 for "\foo.txt". // ch = pOFI->szPath[nTempOffset]; pOFI->szPath[nTempOffset] = 0; ChangeDir(hDlg, pOFI->szPath, FALSE, TRUE); pOFI->szPath[nTempOffset] = ch; } // If there is a file extension, or if there wasn't, and we succeeded in putting a dot where the filename is (?), then if (nExtOffset || SUCCEEDED(StringCchCat(pOFI->szPath + nFileOffset, ARRAYSIZE(pOFI->szPath) - nFileOffset, L"."))) { if (SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFI->szPath + nFileOffset))) { if (SUCCEEDED(StringCchCopy(szSameDirFile, ARRAYSIZE(szSameDirFile), pOFI->szPath + nFileOffset))) { // Redo list, based on new file spec: if (FListAll(pOFI, hDlg, szSameDirFile, ARRAYSIZE(szSameDirFile)) < 0) { MessageBeep(0); } } } } return (FALSE); } // // We either have a file pattern or a real file. // If its a directory // (1) Add on default pattern // (2) Act like its a pattern (goto pattern (1)) // Else if its a pattern // (1) Update everything // (2) display files in whatever dir were now in // Else if its a file name! // (1) Check out the syntax // (2) End the dialog given OK // (3) Beep/message otherwise // // // Drive-letter:\dirpath ?? // if (!cchSearchPath) { DWORD dwFileAttr = GetFileAttributes(szPathName); if (dwFileAttr != 0xFFFFFFFF) { if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY) { if (ChangeDir(hDlg, szPathName, FALSE, TRUE) != CHANGEDIR_FAILED) { ChangedDir: SendDlgItemMessage(hDlg, edt1, WM_SETREDRAW, FALSE, 0L); if (*pOFI->szLastFilter) { SetDlgItemText(hDlg, edt1, pOFI->szLastFilter); } else { SetDlgItemText(hDlg, edt1, szStarDotStar); } SendMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS( cmb1, GetDlgItem(hDlg, cmb1), CBN_CLOSEUP ) ); SendMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS( cmb2, GetDlgItem(hDlg, cmb2), MYCBN_CHANGEDIR ) ); SendDlgItemMessage(hDlg, edt1, WM_SETREDRAW, TRUE, 0L); InvalidateRect(GetDlgItem(hDlg, edt1), NULL, FALSE); } return (FALSE); } } } // // Was there a path and did it fail? // if (nFileOffset && cchSearchPath && (pOFN->Flags & OFN_PATHMUSTEXIST)) { PathCheck: if (cchSearchPath == SP_PATHNOTFOUND) { nErrCode = ERROR_PATH_NOT_FOUND; } else if (cchSearchPath == SP_INVALIDDRIVE) { int nDriveIndex; // // Lowercase drive letters since DiskAddedPreviously is case // sensitive. // CharLower(pOFI->szPath); // We can get here without performing an OpenFile call. As such // the szPathName can be filled with random garbage. Since we // only need one character for the error message, set // szPathName[0] to the drive letter. // if (pOFI->szPath[1] == CHAR_COLON) { nDriveIndex = DiskAddedPreviously(pOFI->szPath[0], NULL); } else { nDriveIndex = DiskAddedPreviously(0, pOFI->szPath); } if (nDriveIndex == 0xFFFFFFFF) { nErrCode = ERROR_NO_DRIVE; } else { if (bUNCName) { nErrCode = ERROR_NO_DRIVE; } else { switch (GetDiskType(pOFI->szPath)) { case ( DRIVE_REMOVABLE ) : { szPathName[0] = pOFI->szPath[0]; nErrCode = ERROR_NO_DISK_IN_DRIVE; break; } case ( DRIVE_CDROM ) : { szPathName[0] = pOFI->szPath[0]; nErrCode = ERROR_NO_DISK_IN_CDROM; break; } default : { nErrCode = ERROR_PATH_NOT_FOUND; } } } } } else { // We can never get here, because cchSearchPath must equal 0, SP_PATHNOTFOUND or SP_INVALIDDRIVE. ASSERT(FALSE); nErrCode = ERROR_FILE_NOT_FOUND; } // // If we don't set wNoRedraw here, then WM_ACTIVATE will set the // GetNetDrives event. // wNoRedraw |= 1; goto Warning; } if ((nFileOffset != PARSE_DIRECTORYNAME) && PortName(pOFI->szPath + nFileOffset)) { nErrCode = ERROR_PORTNAME; goto Warning; } // // Add the default extension unless filename ends with period or no // default extension exists. If the file exists, consider asking // permission to overwrite the file. // // NOTE: When no extension given, default extension is tried 1st. // if ( (nFileOffset != PARSE_DIRECTORYNAME) && nExtOffset && !pOFI->szPath[nExtOffset] && pOFN->lpstrDefExt && *pOFN->lpstrDefExt && (((DWORD)nExtOffset + lstrlen(pOFN->lpstrDefExt)) < pOFN->nMaxFile) ) { DWORD dwFileAttr; int nExtOffset2 = lstrlen(szPathName); if (AppendExt(pOFI->szPath, ARRAYSIZE(pOFI->szPath), pOFN->lpstrDefExt, FALSE) && AppendExt(szPathName, ARRAYSIZE(szPathName), pOFN->lpstrDefExt, FALSE)) { bAddExt = TRUE; // // Directory may match default extension. Change to it as if it had // been typed in. A dir w/o the extension would have been switched // to in the logic above. // if ((dwFileAttr = GetFileAttributes(pOFI->szPath)) != 0xFFFFFFFF) { if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY) { if (ChangeDir(hDlg, szPathName, FALSE, TRUE) != CHANGEDIR_FAILED) { goto ChangedDir; } } } hFile = CreateFile( szPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); hFile = ProgManBugCreateFile(hFile, szPathName, &nErrCode); if (nErrCode == ERROR_SHARING_VIOLATION) { goto SharingViolationInquiry; } if (hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(hFile)) { nErrCode = GetLastError(); goto Warning; } AskPermission: // // Is the file read-only? // if (pOFN->Flags & OFN_NOREADONLYRETURN) { int nRet; if ((nRet = GetFileAttributes(szPathName)) != -1) { if (nRet & ATTR_READONLY) { nErrCode = ERROR_LAZY_READONLY; goto Warning; } } else { nErrCode = GetLastError(); goto Warning; } } if ((bSave || (pOFN->Flags & OFN_NOREADONLYRETURN)) && (nErrCode == ERROR_ACCESS_DENIED)) { goto Warning; } if (pOFN->Flags & OFN_OVERWRITEPROMPT) { if (bSave && !FOkToWriteOver(hDlg, szPathName)) { PostMessage( hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, edt1), (LPARAM)1L ); return (FALSE); } } if (nErrCode == ERROR_SHARING_VIOLATION) { goto SharingViolationInquiry; } goto FileNameAccepted; } else { // Remove the file extension. *(pOFI->szPath + nExtOffset) = CHAR_NULL; szPathName[nExtOffset2] = CHAR_NULL; } } } // else bAddExt is still FALSE hFile = CreateFile( szPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); hFile = ProgManBugCreateFile(hFile, szPathName, &nErrCode); if (hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(hFile)) { nErrCode = GetLastError(); goto Warning; } goto AskPermission; } else { if ((nErrCode == ERROR_FILE_NOT_FOUND) || (nErrCode == ERROR_PATH_NOT_FOUND)) { // // Figure out if the default extension should be tacked on. // // (Note: there is only one way in here where bAddExt is TRUE, and in that // case we know for certain that AppendExt will succeed) if (bAddExt) { EVAL(AppendExt(pOFI->szPath, ARRAYSIZE(pOFI->szPath), pOFN->lpstrDefExt, FALSE)); EVAL(AppendExt(szPathName, ARRAYSIZE(szPathName), pOFN->lpstrDefExt, FALSE)); } } else if (nErrCode == ERROR_SHARING_VIOLATION) { SharingViolationInquiry: // // If the app is "share aware", fall through. // Otherwise, ask the hook function. // if (!(pOFN->Flags & OFN_SHAREAWARE)) { if (pOFN->lpfnHook) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { CHAR szPathNameA[MAX_FULLPATHNAME]; RtlUnicodeToMultiByteSize( &cch, szPathName, lstrlenW(szPathName) * sizeof(TCHAR) ); SHUnicodeToAnsi(szPathName,(LPSTR)&szPathNameA[0],cch + 1); cch = (DWORD)(*lpfnHook)( hDlg, msgSHAREVIOLATIONA, 0, (LONG_PTR)(LPSTR)szPathNameA ); } else { cch = (DWORD)(*lpfnHook)( hDlg, msgSHAREVIOLATIONW, 0, (LONG_PTR)szPathName ); } if (cch == OFN_SHARENOWARN) { return (FALSE); } else if (cch != OFN_SHAREFALLTHROUGH) { goto Warning; } } else { goto Warning; } } goto FileNameAccepted; } if (!bSave) { if ((nErrCode == ERROR_FILE_NOT_FOUND) || (nErrCode == ERROR_PATH_NOT_FOUND)) { if (pOFN->Flags & OFN_FILEMUSTEXIST) { if (pOFN->Flags & OFN_CREATEPROMPT) { // // Don't alter pOFI->szPath. // bInChildDlg = TRUE; cch = (DWORD)CreateFileDlg(hDlg, pOFI->szPath); bInChildDlg = FALSE; if (cch == IDYES) { goto TestCreation; } else { return (FALSE); } } goto Warning; } } else { goto Warning; } } // // The file doesn't exist. Can it be created? This is needed because // there are many extended characters which are invalid that won't be // caught by ParseFile. // Two more good reasons: Write-protected disks & full disks. // // BUT, if they dont want the test creation, they can request that we // not do it using the OFN_NOTESTFILECREATE flag. If they want to // create files on a share that has create-but-no-modify privileges, // they should set this flag but be ready for failures that couldn't // be caught, such as no create privileges, invalid extended // characters, a full disk, etc. // TestCreation: if ((pOFN->Flags & OFN_PATHMUSTEXIST) && (!(pOFN->Flags & OFN_NOTESTFILECREATE))) { // // Must use the FILE_FLAG_DELETE_ON_CLOSE flag so that the // file is automatically deleted when the handle is closed // (no need to call DeleteFile). This is necessary in the // event that the directory only has Add & Read access. // The CreateFile call will succeed, but the DeleteFile call // will fail. By adding the above flag to the CreateFile // call, it overrides the access rights and deletes the file // during the call to CloseHandle. // hFile = CreateFile( szPathName, FILE_ADD_FILE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL ); if (hFile == INVALID_HANDLE_VALUE) { nErrCode = GetLastError(); } if (hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(hFile)) { nErrCode = GetLastError(); goto Warning; } } else { // // Unable to create it. // // If it's not write-protection, a full disk, // network protection, or the user popping the drive door // open, assume that the filename is invalid. // if ( (nErrCode != ERROR_WRITE_PROTECT) && (nErrCode != ERROR_CANNOT_MAKE) && (nErrCode != ERROR_NETWORK_ACCESS_DENIED) && (nErrCode != ERROR_ACCESS_DENIED) ) { nErrCode = 0; } goto Warning; } } } FileNameAccepted: HourGlass(TRUE); lRet = ParseFile(szPathName, blfn, IS16BITWOWAPP(pOFN), FALSE); nFileOffset = (int)(SHORT)LOWORD(lRet); cch = (DWORD)HIWORD(lRet); ASSERT(nFileOffset >= 0); // if the filename was accepted, we better parse it without error... pOFN->nFileOffset = (WORD)nFileOffset; if (nExtOffset || bAddExt) { pOFN->nFileExtension = LOWORD(cch); } else { pOFN->nFileExtension = 0; } pOFN->Flags &= ~OFN_EXTENSIONDIFFERENT; if (pOFN->lpstrDefExt && pOFN->nFileExtension) { TCHAR szPrivateExt[4]; SHORT i; for (i = 0; i < 3; i++) { szPrivateExt[i] = *(pOFN->lpstrDefExt + i); } szPrivateExt[3] = CHAR_NULL; if (lstrcmpi(szPrivateExt, szPathName + cch)) { pOFN->Flags |= OFN_EXTENSIONDIFFERENT; } } // // If we're called from wow, and the user hasn't changed // directories, shorten the path to abbreviated 8.3 format. // if (pOFN->Flags & OFN_NOLONGNAMES) { ShortenThePath(szPathName); // // If the path was shortened, the offset might have changed so // we must parse the file again. // lRet = ParseFile(szPathName, blfn, IS16BITWOWAPP(pOFN), FALSE); nFileOffset = (int)(SHORT)LOWORD(lRet); cch = (DWORD)HIWORD(lRet); ASSERT(nFileOffset >= 0); // shortening the path better not introduce a parse error... // // When in Save dialog, the file may not exist yet, so the file // name cannot be shortened. So, we need to test if it's an // 8.3 filename and popup an error message if not. // if (bSave) { LPTSTR lptmp; LPTSTR lpExt = NULL; for (lptmp = szPathName + nFileOffset; *lptmp; lptmp++) { if (*lptmp == CHAR_DOT) { if (lpExt) { // // There's more than one dot in the file, so it is // invalid. // nErrCode = FNERR_INVALIDFILENAME; goto Warning; } lpExt = lptmp; } if (*lptmp == CHAR_SPACE) { nErrCode = FNERR_INVALIDFILENAME; goto Warning; } } if (lpExt) { // // There's an extension. // *lpExt = 0; } if ((lstrlen(szPathName + nFileOffset) > 8) || (lpExt && lstrlen(lpExt + 1) > 3)) { if (lpExt) { *lpExt = CHAR_DOT; } nErrCode = FNERR_INVALIDFILENAME; goto Warning; } if (lpExt) { *lpExt = CHAR_DOT; } } } StorePathOrFileSizeInOFN(pOFN, szPathName); // // File Title. Note that it's cut off at whatever the buffer length // is, so if the buffer is too small, *no notice is given*. // if (pOFN->lpstrFileTitle && pOFN->nMaxFileTitle) { cch = lstrlen(szPathName + nFileOffset); if (cch > pOFN->nMaxFileTitle) { szPathName[nFileOffset + pOFN->nMaxFileTitle - 1] = CHAR_NULL; } EVAL(SUCCEEDED(StringCchCopy(pOFN->lpstrFileTitle, pOFN->nMaxFileTitle, szPathName + nFileOffset))); } if (!(pOFN->Flags & OFN_HIDEREADONLY)) { if (IsDlgButtonChecked(hDlg, chx1)) { pOFN->Flags |= OFN_READONLY; } else { pOFN->Flags &= ~OFN_READONLY; } } return (TRUE); } WCHAR c_szSpace[] = L" "; //////////////////////////////////////////////////////////////////////////// // // MultiSelectOKButton // //////////////////////////////////////////////////////////////////////////// BOOL MultiSelectOKButton( HWND hDlg, POPENFILEINFO pOFI, BOOL bSave) { DWORD nErrCode; LPTSTR lpCurDir; LPTSTR lpchStart; // start of an individual filename LPTSTR lpchEnd; // end of an individual filename DWORD cch; HANDLE hFile; LPOPENFILENAME pOFN; BOOL EOS = FALSE; // end of string flag BOOL bRet; TCHAR szPathName[MAX_FULLPATHNAME - 1]; LPCURDLG lpCurDlg; pOFN = pOFI->pOFN; // // Check for space for first full path element. // if(!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) || !(lpCurDir = lpCurDlg->lpstrCurDir)) { return (FALSE); } EVAL(SUCCEEDED(StringCchCopy(pOFI->szPath, ARRAYSIZE(pOFI->szPath), lpCurDir))); if (StrChr(pOFI->szPath, CHAR_SPACE)) { GetShortPathName(pOFI->szPath, pOFI->szPath, ARRAYSIZE(pOFI->szPath)); } if (!bCasePreserved) { CharLower(pOFI->szPath); } cch = (DWORD)(lstrlen(pOFI->szPath) + ARRAYSIZE(c_szSpace) + SendDlgItemMessage(hDlg, edt1, WM_GETTEXTLENGTH, 0, 0L)); if (pOFN->lpstrFile) { if (cch > pOFN->nMaxFile) { // Not enough room. StoreFileSizeInOFN(pOFN, cch); } else // pOFN->nMaxFile >= cch { // // Copy in the full path as the first element. // StringCchCopy(pOFN->lpstrFile, pOFN->nMaxFile, pOFI->szPath); // We know these both succeed. StringCchCat(pOFN->lpstrFile, pOFN->nMaxFile, c_szSpace); // // Get the other files here. // cch = lstrlen(pOFN->lpstrFile); // cch is now the length of the path plus a space. // // The path is guaranteed to be less than 64K (actually, < 260). // pOFN->nFileOffset = LOWORD(cch); lpchStart = pOFN->lpstrFile + cch; // This shouldn't get truncated, assuming the text in the edit box is still the same length as above. GetDlgItemText(hDlg, edt1, lpchStart, (pOFN->nMaxFile - cch - 1)); while (*lpchStart == CHAR_SPACE) { lpchStart = CharNext(lpchStart); } if (*lpchStart == CHAR_NULL) { return (FALSE); } // // Go along file path looking for multiple filenames delimited by // spaces. For each filename found, try to open it to make sure // it's a valid file. // while (!EOS) { // // Find the end of the filename. // lpchEnd = lpchStart; while (*lpchEnd && *lpchEnd != CHAR_SPACE) { lpchEnd = CharNext(lpchEnd); } // // Mark the end of the filename with a NULL. // if (*lpchEnd == CHAR_SPACE) { *lpchEnd = CHAR_NULL; } else { // // Already NULL, found the end of the string. // EOS = TRUE; } // // Check that the filename is valid. // bRet = GetFullPathName(lpchStart, ARRAYSIZE(szPathName), szPathName, NULL); if (!bRet) { nErrCode = ERROR_FILE_NOT_FOUND; goto MultiFileNotFound; } hFile = CreateFile(szPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); hFile = ProgManBugCreateFile(hFile, szPathName, &nErrCode); if (hFile == INVALID_HANDLE_VALUE) { nErrCode = GetLastError(); MultiFileNotFound: if ( ((pOFN->Flags & OFN_FILEMUSTEXIST) || (nErrCode != ERROR_FILE_NOT_FOUND)) && ((pOFN->Flags & OFN_PATHMUSTEXIST) || (nErrCode != ERROR_PATH_NOT_FOUND)) && (!(pOFN->Flags & OFN_SHAREAWARE) || (nErrCode != ERROR_SHARING_VIOLATION)) ) { if ( (nErrCode == ERROR_SHARING_VIOLATION) && pOFN->lpfnHook ) { LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN); if (pOFI->ApiType == COMDLG_ANSI) { CHAR szPathNameA[MAX_FULLPATHNAME]; RtlUnicodeToMultiByteSize( &cch, szPathName, lstrlenW(szPathName) * sizeof(TCHAR) ); SHUnicodeToAnsi(szPathName,(LPSTR)&szPathNameA[0],cch + 1); cch = (DWORD)(*lpfnHook)( hDlg, msgSHAREVIOLATIONA, 0, (LONG_PTR)(LPSTR)szPathNameA ); } else { cch = (DWORD)(*lpfnHook)( hDlg, msgSHAREVIOLATIONW, 0, (LONG_PTR)szPathName ); } if (cch == OFN_SHARENOWARN) { return (FALSE); } else if (cch == OFN_SHAREFALLTHROUGH) { goto EscapedThroughShare; } } else if (nErrCode == ERROR_ACCESS_DENIED) { szPathName[0] = CharLowerChar(szPathName[0]); if (GetDiskType(szPathName) != DRIVE_REMOVABLE) { nErrCode = ERROR_NETWORK_ACCESS_DENIED; } } if ((nErrCode == ERROR_WRITE_PROTECT) || (nErrCode == ERROR_CANNOT_MAKE) || (nErrCode == ERROR_ACCESS_DENIED)) { *lpchStart = szPathName[0]; } MultiWarning: InvalidFileWarning(hDlg, lpchStart, nErrCode, 0); return (FALSE); } } EscapedThroughShare: if (hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(hFile)) { nErrCode = GetLastError(); goto MultiWarning; } if ((pOFN->Flags & OFN_NOREADONLYRETURN) && (GetFileAttributes(szPathName) & FILE_ATTRIBUTE_READONLY)) { nErrCode = ERROR_LAZY_READONLY; goto MultiWarning; } if ((bSave || (pOFN->Flags & OFN_NOREADONLYRETURN)) && (nErrCode == ERROR_ACCESS_DENIED)) { goto MultiWarning; } if (pOFN->Flags & OFN_OVERWRITEPROMPT) { if (bSave && !FOkToWriteOver(hDlg, szPathName)) { PostMessage( hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, edt1), (LPARAM)1L ); return (FALSE); } } } // // This file is valid, so check the next one. // if (!EOS) { lpchStart = lpchEnd + 1; while (*lpchStart == CHAR_SPACE) { lpchStart = CharNext(lpchStart); } if (*lpchStart == CHAR_NULL) { EOS = TRUE; } else { // // Not at end, replace NULL with SPACE. // *lpchEnd = CHAR_SPACE; } } } // // Limit String. // *lpchEnd = CHAR_NULL; } } // // This doesn't really mean anything for multiselection. // pOFN->nFileExtension = 0; pOFN->nFilterIndex = (int) SendDlgItemMessage(hDlg, cmb1, CB_GETCURSEL, 0, 0L); return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // dwOKSubclass // // Simulates a double click if the user presses OK with the mouse // and the focus was on the directory listbox. // // The problem is that the UITF demands that when the directory // listbox loses the focus, the selected directory should return // to the current directory. But when the user changes the item // selected with a single click, and then clicks the OK button to // have the change take effect, the focus is lost before the OK button // knows it was pressed. By setting the global flag bChangeDir // when the directory listbox loses the focus and clearing it when // the OK button loses the focus, we can check whether a mouse // click should update the directory. // // Returns: Return value from default listbox procedure. // //////////////////////////////////////////////////////////////////////////// LRESULT WINAPI dwOKSubclass( HWND hOK, UINT msg, WPARAM wParam, LPARAM lParam) { HANDLE hDlg; POPENFILEINFO pOFI; if (msg == WM_KILLFOCUS) { if (bChangeDir) { if (pOFI = (POPENFILEINFO)GetProp(hDlg = GetParent(hOK), FILEPROP)) { SendDlgItemMessage( hDlg, lst2, LB_SETCURSEL, (WPARAM)(pOFI->idirSub - 1), 0L ); } bChangeDir = FALSE; } } return (CallWindowProc(lpOKProc, hOK, msg, wParam, lParam)); } //////////////////////////////////////////////////////////////////////////// // // dwLBSubclass // // Simulates a double click if the user presses OK with the mouse. // // The problem is that the UITF demands that when the directory // listbox loses the focus, the selected directory should return // to the current directory. But when the user changes the item // selected with a single click, and then clicks the OK button to // have the change take effect, the focus is lost before the OK button // knows it was pressed. By simulating a double click, the change // takes place. // // Returns: Return value from default listbox proceedure. // //////////////////////////////////////////////////////////////////////////// LRESULT WINAPI dwLBSubclass( HWND hLB, UINT msg, WPARAM wParam, LPARAM lParam) { HANDLE hDlg; POPENFILEINFO pOFI; if (msg == WM_KILLFOCUS) { hDlg = GetParent(hLB); bChangeDir = (GetDlgItem(hDlg, IDOK) == (HWND)wParam) ? TRUE : FALSE; if (!bChangeDir) { if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP)) { SendMessage( hLB, LB_SETCURSEL, (WPARAM)(pOFI->idirSub - 1), 0L ); } } } return (CallWindowProc(lpLBProc, hLB, msg, wParam, lParam)); } //////////////////////////////////////////////////////////////////////////// // // InvalidFileWarning // //////////////////////////////////////////////////////////////////////////// int InvalidFileWarning( HWND hDlg, LPTSTR szFile, DWORD wErrCode, UINT mbType) { SHORT isz; BOOL bDriveLetter = FALSE; int nRet = 0; if (lstrlen(szFile) > TOOLONGLIMIT) { *(szFile + TOOLONGLIMIT) = CHAR_NULL; } switch (wErrCode) { case ( ERROR_NO_DISK_IN_DRIVE ) : { isz = iszNoDiskInDrive; bDriveLetter = TRUE; break; } case ( ERROR_NO_DISK_IN_CDROM ) : { isz = iszNoDiskInCDRom; bDriveLetter = TRUE; break; } case ( ERROR_NO_DRIVE ) : { isz = iszDriveDoesNotExist; bDriveLetter = TRUE; break; } case ( ERROR_TOO_MANY_OPEN_FILES ) : { isz = iszNoFileHandles; break; } case ( ERROR_PATH_NOT_FOUND ) : { isz = iszPathNotFound; break; } case ( ERROR_FILE_NOT_FOUND ) : { isz = iszFileNotFound; break; } case ( ERROR_CANNOT_MAKE ) : case ( ERROR_DISK_FULL ) : { isz = iszDiskFull; bDriveLetter = TRUE; break; } case ( ERROR_WRITE_PROTECT ) : { isz = iszWriteProtection; bDriveLetter = TRUE; break; } case ( ERROR_SHARING_VIOLATION ) : { isz = iszSharingViolation; break; } case ( ERROR_CREATE_NO_MODIFY ) : { isz = iszCreateNoModify; break; } case ( ERROR_NETWORK_ACCESS_DENIED ) : { isz = iszNetworkAccessDenied; break; } case ( ERROR_PORTNAME ) : { isz = iszPortName; break; } case ( ERROR_LAZY_READONLY ) : { isz = iszReadOnly; break; } case ( ERROR_DIR_ACCESS_DENIED ) : { isz = iszDirAccessDenied; break; } case ( ERROR_FILE_ACCESS_DENIED ) : case ( ERROR_ACCESS_DENIED ) : { isz = iszFileAccessDenied; break; } case ( ERROR_UNRECOGNIZED_VOLUME ) : { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, wErrCode, GetUserDefaultLCID(), szWarning, WARNINGMSGLENGTH, NULL); goto DisplayError; } default : { isz = iszInvalidFileName; break; } } if (!CDLoadString( g_hinst, isz, szCaption, WARNINGMSGLENGTH )) { StringCchPrintf( szWarning, ARRAYSIZE(szWarning), TEXT("Error occurred, but error resource cannot be loaded.") ); } else { StringCchPrintf( szWarning, ARRAYSIZE(szWarning), szCaption, bDriveLetter ? (LPTSTR)(CHAR)*szFile : szFile ); DisplayError: GetWindowText(hDlg, szCaption, WARNINGMSGLENGTH); if (!mbType) { mbType = MB_OK | MB_ICONEXCLAMATION; } nRet = MessageBox(hDlg, szWarning, szCaption, mbType); } if (isz == iszInvalidFileName) { PostMessage( hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, edt1), (LPARAM)1L ); } return (nRet); } //////////////////////////////////////////////////////////////////////////// // // MeasureItem // //////////////////////////////////////////////////////////////////////////// VOID MeasureItem( HWND hDlg, LPMEASUREITEMSTRUCT mis) { if (!dyItem) { HDC hDC = GetDC(hDlg); TEXTMETRIC TM; HANDLE hFont; hFont = (HANDLE)SendMessage(hDlg, WM_GETFONT, 0, 0L); if (!hFont) { hFont = GetStockObject(SYSTEM_FONT); } hFont = SelectObject(hDC, hFont); GetTextMetrics(hDC, &TM); SelectObject(hDC, hFont); ReleaseDC(hDlg, hDC); dyText = TM.tmHeight; dyItem = max(dyDirDrive, dyText); } if (mis->CtlID == lst1) { mis->itemHeight = dyText; } else { mis->itemHeight = dyItem; } } //////////////////////////////////////////////////////////////////////////// // // Signum // // Returns the sign of an integer: // -1 if integer < 0 // 0 if integer = 0 // 1 if integer > 0 // // Note: Signum *could* be defined as an inline macro, but that causes // the C compiler to disable Loop optimization, Global register // optimization, and Global optimizations for common subexpressions // in any function that the macro would appear. The cost of a call // to the function seemed worth the optimizations. // //////////////////////////////////////////////////////////////////////////// int Signum( int nTest) { return ((nTest == 0) ? 0 : (nTest > 0) ? 1 : -1); } //////////////////////////////////////////////////////////////////////////// // // DrawItem // // Draws the drive/directory pictures in the respective combo list boxes. // // lst1 is listbox for files // lst2 is listbox for directories // cmb1 is combobox for filters // cmb2 is combobox for drives // //////////////////////////////////////////////////////////////////////////// VOID DrawItem( POPENFILEINFO pOFI, HWND hDlg, WPARAM wParam, LPDRAWITEMSTRUCT lpdis, BOOL bSave) { HDC hdcList; RECT rc; TCHAR szText[MAX_FULLPATHNAME + 1]; int dxAcross; LONG nHeight; LONG rgbBack, rgbText, rgbOldBack, rgbOldText; SHORT nShift = 1; // to shift directories right in lst2 BOOL bSel; int BltItem; int nBackMode; if ((int)lpdis->itemID < 0) { DefWindowProc(hDlg, WM_DRAWITEM, wParam, (LPARAM)lpdis); return; } *szText = CHAR_NULL; if (lpdis->CtlID != lst1 && lpdis->CtlID != lst2 && lpdis->CtlID != cmb2) { return; } if (!pOFI) { return; } hdcList = lpdis->hDC; if (lpdis->CtlID != cmb2) { SendDlgItemMessage( hDlg, (int)lpdis->CtlID, LB_GETTEXT , (WPARAM)lpdis->itemID, (LONG_PTR)szText ); if (*szText == 0) { // // If empty listing. // DefWindowProc(hDlg, WM_DRAWITEM, wParam, (LONG_PTR)lpdis); return; } if (!bCasePreserved) { CharLower(szText); } } nHeight = (lpdis->CtlID == lst1) ? dyText : dyItem; CopyRect((LPRECT)&rc, (LPRECT)&lpdis->rcItem); rc.bottom = rc.top + nHeight; if (bSave && (lpdis->CtlID == lst1)) { rgbBack = rgbWindowColor; rgbText = rgbGrayText; } else { // // Careful checking of bSel is needed here. Since the file // listbox (lst1) can allow multiselect, only ODS_SELECTED needs // to be set. But for the directory listbox (lst2), ODS_FOCUS // also needs to be set. // bSel = (lpdis->itemState & (ODS_SELECTED | ODS_FOCUS)); if ((bSel & ODS_SELECTED) && ((lpdis->CtlID != lst2) || (bSel & ODS_FOCUS))) { rgbBack = rgbHiliteColor; rgbText = rgbHiliteText; } else { rgbBack = rgbWindowColor; rgbText = rgbWindowText; } } rgbOldBack = SetBkColor(hdcList, rgbBack); rgbOldText = SetTextColor(hdcList, rgbText); // // Drives -- text is now in UI style, c: VolumeName/Server-Sharename. // if (lpdis->CtlID == cmb2) { HANDLE hCmb2 = GetDlgItem(hDlg, cmb2); dxAcross = dxDirDrive / BMPHIOFFSET; BltItem = (int) SendMessage(hCmb2, CB_GETITEMDATA, lpdis->itemID, 0); SendMessage(hCmb2, CB_GETLBTEXT, lpdis->itemID, (LPARAM)szText); if (bSel & ODS_SELECTED) { BltItem += BMPHIOFFSET; } } else if (lpdis->CtlID == lst2) { // // Directories. // dxAcross = dxDirDrive / BMPHIOFFSET; if (lpdis->itemID > pOFI->idirSub) { nShift = (SHORT)pOFI->idirSub; } else { nShift = (SHORT)lpdis->itemID; } // // Must be at least 1. // nShift++; BltItem = 1 + Signum(lpdis->itemID + 1 - pOFI->idirSub); if (bSel & ODS_FOCUS) { BltItem += BMPHIOFFSET; } } else if (lpdis->CtlID == lst1) { // // Prep for TextOut below. // dxAcross = -dxSpace; } if (bSave && (lpdis->CtlID == lst1) && !rgbText) { HBRUSH hBrush = CreateSolidBrush(rgbBack); HBRUSH hOldBrush; nBackMode = SetBkMode(hdcList, TRANSPARENT); hOldBrush = SelectObject( lpdis->hDC, hBrush ? hBrush : GetStockObject(WHITE_BRUSH) ); FillRect(lpdis->hDC, (LPRECT)(&(lpdis->rcItem)), hBrush); SelectObject(lpdis->hDC, hOldBrush); if (hBrush) { DeleteObject(hBrush); } GrayString( lpdis->hDC, GetStockObject(BLACK_BRUSH), NULL, (LPARAM)szText, 0, lpdis->rcItem.left + dxSpace, lpdis->rcItem.top, 0, 0 ); SetBkMode(hdcList, nBackMode); } else { // // Draw the name. // ExtTextOut( hdcList, rc.left + (WORD)(dxSpace + dxAcross) + dxSpace * nShift, rc.top + (nHeight - dyText) / 2, ETO_OPAQUE | ETO_CLIPPED, (LPRECT)&rc, szText, lstrlen(szText), NULL ); } // // Draw the picture. // if (lpdis->CtlID != lst1) { BitBlt( hdcList, rc.left + dxSpace * nShift, rc.top + (dyItem - dyDirDrive) / 2, dxAcross, dyDirDrive, hdcMemory, BltItem * dxAcross, 0, SRCCOPY ); } SetTextColor(hdcList, rgbOldText); SetBkColor(hdcList, rgbBack); if (lpdis->itemState & ODS_FOCUS) { DrawFocusRect(hdcList, (LPRECT)&lpdis->rcItem); } } //////////////////////////////////////////////////////////////////////////// // // SpacesExist // //////////////////////////////////////////////////////////////////////////// BOOL SpacesExist( LPTSTR szFileName) { while (*szFileName) { if (*szFileName == CHAR_SPACE) { return (TRUE); } else { szFileName++; } } return (FALSE); } //////////////////////////////////////////////////////////////////////////// // // StripFileName // // Removes all but the filename from editbox contents. // This is to be called before the user makes directory or drive // changes by selecting them instead of typing them. // //////////////////////////////////////////////////////////////////////////// void StripFileName( HANDLE hDlg, BOOL bWowApp) { TCHAR szText[MAX_FULLPATHNAME]; SHORT nFileOffset, cb; if (GetDlgItemText(hDlg, edt1, szText, MAX_FULLPATHNAME - 1)) { DWORD lRet; lRet = ParseFile(szText, IsLFNDriveX(hDlg, szText), bWowApp, FALSE); nFileOffset = (SHORT)LOWORD(lRet); cb = HIWORD(lRet); if (nFileOffset < 0) { // // If there was a parsing error, check for CHAR_SEMICOLON // delimeter. // if (szText[cb] == CHAR_SEMICOLON) { szText[cb] = CHAR_NULL; lRet = ParseFile( szText, IsLFNDriveX(hDlg, szText), bWowApp, FALSE ); nFileOffset = (SHORT)LOWORD(lRet); szText[cb] = CHAR_SEMICOLON; if (nFileOffset < 0) { // // Still trouble, so Exit. // szText[0] = CHAR_NULL; } } else { szText[0] = CHAR_NULL; } } if (nFileOffset > 0) { StringCopyOverlap(szText, szText + nFileOffset); } if (nFileOffset) { SetDlgItemText(hDlg, edt1, szText); } } } //////////////////////////////////////////////////////////////////////////// // // lstrtok // //////////////////////////////////////////////////////////////////////////// LPTSTR lstrtok( LPTSTR lpStr, LPCTSTR lpDelim) { static LPTSTR lpString; LPTSTR lpRetVal, lpTemp; // // If we are passed new string skip leading delimiters. // if (lpStr) { lpString = lpStr; while (*lpString && StrChr(lpDelim, *lpString)) { lpString = CharNext(lpString); } } // // If there are no more tokens, return NULL. // if (!*lpString) { return (CHAR_NULL); } // // Save head of token. // lpRetVal = lpString; // // Find delimiter or end of string. // while (*lpString && !StrChr(lpDelim, *lpString)) { lpString = CharNext(lpString); } // // If we found a delimiter insert string terminator and skip. // if (*lpString) { lpTemp = CharNext(lpString); *lpString = CHAR_NULL; lpString = lpTemp; } // // Return token. // return (lpRetVal); } //////////////////////////////////////////////////////////////////////////// // // ChopText // //////////////////////////////////////////////////////////////////////////// LPTSTR ChopText( HWND hwndDlg, int idStatic, LPTSTR lpch) { RECT rc; register int cxField; BOOL fChop = FALSE; HWND hwndStatic; HDC hdc; TCHAR chDrv; HANDLE hOldFont; LPTSTR lpstrStart = lpch; SIZE Size; BOOL bRet; // // Get length of static field. // hwndStatic = GetDlgItem(hwndDlg, idStatic); GetClientRect(hwndStatic, (LPRECT)&rc); cxField = rc.right - rc.left; // // Chop characters off front end of text until short enough. // hdc = GetDC(hwndStatic); hOldFont = NULL; while ((bRet = GetTextExtentPoint(hdc, lpch, lstrlen(lpch), &Size)) && (cxField < Size.cx)) { if (!fChop) { chDrv = *lpch; // // Proportional font support. // if (bRet = GetTextExtentPoint(hdc, lpch, 7, &Size)) { cxField -= Size.cx; } else { break; } if (cxField <= 0) { break; } lpch += 7; } while (*lpch && (!ISBACKSLASH_P(lpstrStart, lpch))) { lpch++; } //Skip the backslash lpch++; fChop = TRUE; } ReleaseDC(hwndStatic, hdc); // // If any characters chopped off, replace first three characters in // remaining text string with ellipsis. // if (fChop) { //Skip back to include the backslash lpch--; *--lpch = CHAR_DOT; *--lpch = CHAR_DOT; *--lpch = CHAR_DOT; *--lpch = *(lpstrStart + 2); *--lpch = *(lpstrStart + 1); *--lpch = *lpstrStart; } return (lpch); } //////////////////////////////////////////////////////////////////////////// // // FillOutPath // // Fills out lst2 given that the current directory has been set. // // Returns: TRUE if they DO NOT match // FALSE if match // //////////////////////////////////////////////////////////////////////////// BOOL FillOutPath( HWND hList, POPENFILEINFO pOFI) { TCHAR szPath[CCHNETPATH]; LPTSTR lpCurDir; LPTSTR lpB, lpF; TCHAR wc; int cchPathOffset; LPCURDLG lpCurDlg; if(!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) || !(lpCurDir = lpCurDlg->lpstrCurDir)) { return (FALSE); } EVAL(SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), lpCurDir))); lpF = szPath; // // Wow apps started from lfn dirs will set the current directory to an // lfn, but only in the case where it is less than 8 chars. // if (pOFI->pOFN->Flags & OFN_NOLONGNAMES) { ShortenThePath(lpF); } *lpF = (TCHAR)CharLower((LPTSTR)*lpF); cchPathOffset = GetPathOffset(lpF); if (cchPathOffset == -1) { cchPathOffset = 0; } lpB = (lpF + cchPathOffset); // lpB now points to c:[here]\bar or \\foo\bar[here]\foo // // Hack to retain Winball display functionality. // Drived disks are displayed as C:\ (the root dir). // whereas unc disks are displayed as \\server\share (the disk). // Hence, extend display of drived disks by one char. // if (*(lpF + 1) == CHAR_COLON) { ++lpB; wc = *(lpB); // For "c:\foo", wc = 'f', and lpF is now "c:\" *lpB = CHAR_NULL; } else { // // Since we use lpF over and over again to speed things // up, and since GetCurrentDirectory returns the disk name // for unc, but the root path for drives, we have the following hack // for when we are at the root of the unc directory, and lpF // contains old stuff out past cchPathOffset. // EVAL(PathAddBackslash(lpF)); // Make sure there is a backslash... wc = 0; *lpB = CHAR_NULL; lpB++; // For "\\foo\bar\hoo" lpF is now "\\foo\bar" (no final backslash), lpB is "hoo" } // // Insert the items for the path to the current dir // Insert the root... // pOFI->idirSub = 0; SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF); // Inserting the root ("c:\" or "\\foo\bar") if (wc) { *lpB = wc; // Replace any missing character - so for "c:\foo", lpB is now "foo". For "\\foo\bar\hoo", lpB is now still 0 (followed by "hoo") } // For "\\foo\bar\hoo", lpB is now "hoo" // For "c:\foo", lpB is now "foo" for (lpF = lpB; *lpB; lpB++) { if ((ISBACKSLASH_P(szPath, lpB)) || (*lpB == CHAR_SLASH)) { *lpB = CHAR_NULL; SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF); lpF = lpB + 1; *lpB = CHAR_BSLASH; } } // // Assumes that a path always ends with one last un-delimited dir name. // Check to make sure we have at least one. // if (lpF != lpB) { SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF); } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // ShortenThePath // // Takes a pathname and converts all dirs to shortnames if they are // not valid DOS 8.3 names. // // Returns: TRUE if pathname converted // FALSE if ran out of space, buffer left alone // Note: pPath is assumed to be at least MAX_PATH in length. // //////////////////////////////////////////////////////////////////////////// BOOL ShortenThePath( LPTSTR pPath) { TCHAR szDest[MAX_PATH]; LPTSTR pSrcNextSpec, pReplaceSpec; LPTSTR pDest, p; LPTSTR pSrc; int cchPathOffset; HANDLE hFind; WIN32_FIND_DATA FindData; UINT i; int nSpaceLeft = ARRAYSIZE(szDest) - 1; UNICODE_STRING Name; BOOLEAN fSpace = FALSE; // // Save pointer to beginning of buffer. // pSrc = pPath; // // Eliminate double quotes. // for (p = pDest = pSrc; *p; p++, pDest++) { if (*p == CHAR_QUOTE) { p++; } *pDest = *p; } *pDest = CHAR_NULL; // // Strip out leading spaces. // while (*pSrc == CHAR_SPACE) { pSrc++; } // // Skip past \\foo\bar or : // pDest = szDest; pSrcNextSpec = pSrc; // // Reuse shell32 internal api that calculates path offset. // The cchPathOffset variable will be the offset that when added to // the pointer will result in a pointer to the backslash before the // first part of the path. // // NOTE: UNICODE only call. // cchPathOffset = GetPathOffset(pSrc); // // Check to see if it's valid. If pSrc is not of the \\foo\bar // or : form we just do nothing. // if (cchPathOffset == -1) { return (TRUE); } // // cchPathOffset will always be at least 1 and is the number of // characters - 1 that we want to copy (that is, if 0 was // permissible, it would denote 1 character). // do { *pDest++ = *pSrcNextSpec++; if (!--nSpaceLeft) { return (FALSE); } } while (cchPathOffset--); // // At this point, we have just the filenames that we can shorten: // \\foo\bar\it\is\here -> it\is\here // c:\angry\lions -> angry\lions // while (pSrcNextSpec) { // // pReplaceSpec holds the current spec we need to replace. // By default, if we can't find the altname, then just use this. // pReplaceSpec = pSrcNextSpec; // // Search for trailing "\" // pSrcNextSpec will point to the next spec to fix. // (*pSrcNextSpec = NULL if done) // while (*pSrcNextSpec && (!ISBACKSLASH_P(pReplaceSpec, pSrcNextSpec))) { pSrcNextSpec++; } if (*pSrcNextSpec) { // // If there is more, then pSrcNextSpec should point to it. // Also delimit this spec. // *pSrcNextSpec = CHAR_NULL; } else { pSrcNextSpec = NULL; } hFind = FindFirstFile(pSrc, &FindData); // // We could exit as soon as this FindFirstFileFails, // but there's the special case of having execute // without read permission. This would fail since the lfn // is valid for lfn apps. // if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); // // See if it's not a legal 8.3 name or if there are spaces // in the name. If either is true, use the alternate name. // if (NT_SUCCESS(RtlInitUnicodeStringEx(&Name, FindData.cFileName))) { if (!RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) || fSpace) { if (FindData.cAlternateFileName[0]) { pReplaceSpec = FindData.cAlternateFileName; } } } } i = lstrlen(pReplaceSpec); nSpaceLeft -= i; if (nSpaceLeft <= 0) { return (FALSE); } EVAL(SUCCEEDED(StringCchCopy(pDest, nSpaceLeft + 1, pReplaceSpec))); pDest += i; // // Now replace the CHAR_NULL with a slash if necessary. // if (pSrcNextSpec) { *pSrcNextSpec++ = CHAR_BSLASH; // // Also add backslash to destination. // *pDest++ = CHAR_BSLASH; nSpaceLeft--; } } EVAL(SUCCEEDED(StringCchCopy(pPath, MAX_PATH, szDest))); return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // FListAll // // Given a file pattern, it changes the directory to that of the spec, // and updates the display. // // notes: pszSpec must be a string less than MAX_FULLPATHNAME in size. // cchSpec is the length of the buffer that contains pszSpec. It may // have an extension appended to it. // //////////////////////////////////////////////////////////////////////////// int FListAll( POPENFILEINFO pOFI, HWND hDlg, LPTSTR pszSpec, int cchSpec) { LPTSTR pszPattern; TCHAR chSave; int nRet = 0; TCHAR szDirBuf[MAX_FULLPATHNAME + 1]; BOOL bPattern = TRUE; if (!bCasePreserved) { CharLower(pszSpec); } // // No directory. // pszPattern = StrRChr(pszSpec, pszSpec + lstrlen(pszSpec), CHAR_BSLASH); if (!pszPattern && !StrChr(pszSpec, CHAR_COLON)) { EVAL(SUCCEEDED(StringCchCopy(pOFI->szSpecCur, ARRAYSIZE(pOFI->szSpecCur), pszSpec))); // Should always be enough room. if (!bInitializing) { UpdateListBoxes(hDlg, pOFI, pszSpec, mskDirectory); } } else { *szDirBuf = CHAR_NULL; // // Just root + pattern. // if (pszPattern == StrChr(pszSpec, CHAR_BSLASH)) { if (!pszPattern) { // // Didn't find a slash, must have drive. // pszPattern = CharNext(CharNext(pszSpec)); } else if ((pszPattern == pszSpec) || ((pszPattern - 2 == pszSpec) && (*(pszSpec + 1) == CHAR_COLON))) { pszPattern = CharNext(pszPattern); } else { goto KillSlash; } chSave = *pszPattern; if (chSave != CHAR_DOT) { // // If not c:.. or c:. // *pszPattern = CHAR_NULL; } EVAL(SUCCEEDED(StringCchCopy(szDirBuf, ARRAYSIZE(szDirBuf), pszSpec))); if (chSave == CHAR_DOT) { int lenSpec = lstrlen(pszSpec); pszPattern = pszSpec + lenSpec; // Could potentially get truncated, bPattern reflects this: bPattern = AppendExt(pszPattern, cchSpec - lenSpec, pOFI->pOFN->lpstrDefExt, TRUE); } else { *pszPattern = chSave; } } else { KillSlash: *pszPattern++ = 0; EVAL(SUCCEEDED(StringCchCopy(szDirBuf, ARRAYSIZE(szDirBuf), pszSpec))); } if ((nRet = ChangeDir(hDlg, szDirBuf, TRUE, FALSE)) < 0) { return (nRet); } if (bPattern) { EVAL(SUCCEEDED(StringCchCopy(pOFI->szSpecCur, ARRAYSIZE(pOFI->szSpecCur), pszPattern))); SetDlgItemText(hDlg, edt1, pOFI->szSpecCur); SelDisk(hDlg, NULL); if (!bInitializing) { SendMessage( hDlg, WM_COMMAND, GET_WM_COMMAND_MPS( cmb2, GetDlgItem(hDlg, cmb2), MYCBN_DRAW ) ); } } } return (nRet); } //////////////////////////////////////////////////////////////////////////// // // ChangeDir // // Changes the current directory and/or resource. // // lpszDir - Fully qualified, or partially qualified names. // To change to another disk and cd automatically to the // last directory as set in the shell's environment, specify // only a disk name (i.e. c: or \\triskal\scratch - must not end // in backslash). // bForce - If True, then caller requires that ChangeDir successfully cd // somewhere. Order of cding is as follows: // 1. lpszDir // 2. current dir for the current thread // 3. root of current dir for the current thread // 4. c: // bError - if TRUE, then pop up an AccessDenied dialog at every step // in the force. // // Returns an index into gaDiskInfo for new disk chosen or, // the ADDDISK_error code. // Returns ADDDISK_NOCHANGE in the event that it cannot cd to the root // directory of the specific file. // //////////////////////////////////////////////////////////////////////////// int ChangeDir( HWND hDlg, LPCTSTR lpszDir, BOOL bForce, BOOL bError) { TCHAR szCurDir[CCHNETPATH]; LPTSTR lpCurDir; int cchDirLen; TCHAR wcDrive = 0; int nIndex; BOOL nRet; LPCURDLG lpCurDlg; // // SheChangeDirEx will call GetCurrentDir, but will use what it // gets only in the case where the path passed in was no good. // // // 1st, try request. // if (lpszDir && *lpszDir) { if (SUCCEEDED(StringCchCopy(szCurDir, ARRAYSIZE(szCurDir), lpszDir))) { // // Remove trailing spaces. // lpCurDir = szCurDir + lstrlen(szCurDir) - 1; while (*lpCurDir && (*lpCurDir == CHAR_SPACE)) { *lpCurDir-- = CHAR_NULL; } nRet = SheChangeDirEx(szCurDir); if (nRet == ERROR_ACCESS_DENIED) { if (bError) { // // Casting to LPTSTR is ok below - InvalidFileWarning will // not change this string because the path is always // guaranteed to be <= MAX_FULLPATHNAME. // InvalidFileWarning( hDlg, (LPTSTR)lpszDir, ERROR_DIR_ACCESS_DENIED, 0 ); } if (!bForce) { return (CHANGEDIR_FAILED); } } else { goto ChangeDir_OK; } } } // // 2nd, try lpCurDlg->lpstrCurDir value (which we got above). // // !!! need to check for a null return value ??? // lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL); nRet = SheChangeDirEx(lpCurDir); if (nRet == ERROR_ACCESS_DENIED) { if (bError) { InvalidFileWarning( hDlg, lpCurDir, ERROR_DIR_ACCESS_DENIED, 0 ); } } else { goto ChangeDir_OK; } // // 3rd, try root of lpCurDlg->lpstrCurDir or GetCurrentDir (sanity). // EVAL(SUCCEEDED(StringCchCopy(szCurDir, ARRAYSIZE(szCurDir), lpCurDir))); cchDirLen = GetPathOffset(szCurDir); // // Sanity check - it's guaranteed not to fail ... // if (cchDirLen != -1) { szCurDir[cchDirLen] = CHAR_BSLASH; szCurDir[cchDirLen + 1] = CHAR_NULL; nRet = SheChangeDirEx(szCurDir); if (nRet == ERROR_ACCESS_DENIED) { if (bError) { InvalidFileWarning( hDlg, (LPTSTR)lpszDir, ERROR_DIR_ACCESS_DENIED, 0 ); } } else { goto ChangeDir_OK; } } // // 4th, try c: // StringCchCopy(szCurDir, ARRAYSIZE(szCurDir), L"c:"); nRet = SheChangeDirEx(szCurDir); if (nRet == ERROR_ACCESS_DENIED) { if (bError) { InvalidFileWarning( hDlg, (LPTSTR)lpszDir, ERROR_DIR_ACCESS_DENIED, 0 ); } } else { goto ChangeDir_OK; } return (CHANGEDIR_FAILED); ChangeDir_OK: GetCurrentDirectory(ARRAYSIZE(szCurDir), szCurDir); nIndex = DiskAddedPreviously(0, szCurDir); // // If the disk doesn't exist, add it. // if (nIndex == -1) { HWND hCmb2 = GetDlgItem(hDlg, cmb2); LPTSTR lpszDisk = NULL; DWORD dwType; TCHAR wc1, wc2; if (szCurDir[1] == CHAR_COLON) { wcDrive = szCurDir[0]; } else { lpszDisk = &szCurDir[0]; } cchDirLen = GetPathOffset(szCurDir); if (cchDirLen != -1) { wc1 = szCurDir[cchDirLen]; wc2 = szCurDir[cchDirLen + 1]; szCurDir[cchDirLen] = CHAR_BSLASH; szCurDir[cchDirLen + 1] = CHAR_NULL; } dwType = GetDiskIndex(GetDiskType(szCurDir)); if (cchDirLen != -1) { szCurDir[cchDirLen] = CHAR_NULL; } nIndex = AddDisk(wcDrive, lpszDisk, NULL, dwType); SendMessage(hCmb2, WM_SETREDRAW, FALSE, 0L); wNoRedraw |= 1; SendMessage( hCmb2, CB_SETITEMDATA, (WPARAM)SendMessage( hCmb2, CB_ADDSTRING, (WPARAM)0, (LPARAM)(LPTSTR)gaDiskInfo[nIndex].lpAbbrName ), (LPARAM)gaDiskInfo[nIndex].dwType ); if ((dwType != NETDRVBMP) && (dwType != REMDRVBMP)) { gaDiskInfo[nIndex].bCasePreserved = IsFileSystemCasePreserving(gaDiskInfo[nIndex].lpPath); } wNoRedraw &= ~1; SendMessage(hCmb2, WM_SETREDRAW, TRUE, 0L); if (cchDirLen != -1) { szCurDir[cchDirLen] = wc1; szCurDir[cchDirLen + 1] = wc2; } } else { // // Validate the disk if it has been seen before. // // For unc names that fade away, refresh the cmb2 box. // if (!gaDiskInfo[nIndex].bValid) { gaDiskInfo[nIndex].bValid = TRUE; SendDlgItemMessage( hDlg, cmb2, CB_SETITEMDATA, (WPARAM)SendDlgItemMessage( hDlg, cmb2, CB_ADDSTRING, (WPARAM)0, (LPARAM)(LPTSTR)gaDiskInfo[nIndex].lpAbbrName ), (LPARAM)gaDiskInfo[nIndex].dwType ); } } // // Update our global concept of Case. // if (nIndex >= 0) { // // Send special WOW message to indicate the directory has // changed. // SendMessage(hDlg, msgWOWDIRCHANGE, 0, 0); // // Get pointer to current directory. // lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL); if (!lpCurDlg || !lpCurDir) { return (CHANGEDIR_FAILED); } bCasePreserved = gaDiskInfo[nIndex].bCasePreserved; // // In case the unc name already has a drive letter, correct // lst2 display. // cchDirLen = 0; // // Compare with szCurDir since it's been lowercased. // if (DBL_BSLASH(szCurDir) && (*gaDiskInfo[nIndex].lpAbbrName != szCurDir[0])) { if ((cchDirLen = GetPathOffset(szCurDir)) != -1) { szCurDir[--cchDirLen] = CHAR_COLON; szCurDir[--cchDirLen] = *gaDiskInfo[nIndex].lpAbbrName; } } if ((gaDiskInfo[nIndex].dwType == CDDRVBMP) || (gaDiskInfo[nIndex].dwType == FLOPPYBMP)) { if (*lpCurDir != gaDiskInfo[nIndex].wcDrive) { TCHAR szDrive[5]; // // Get new volume info - should always succeed. // szDrive[0] = gaDiskInfo[nIndex].wcDrive; szDrive[1] = CHAR_COLON; szDrive[2] = CHAR_BSLASH; szDrive[3] = CHAR_NULL; UpdateLocalDrive(szDrive, TRUE); // // Flush to the cmb before selecting the disk. // if ( lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg) ) { gahDlg[lpCurDlg->dwCurDlgNum] = hDlg; FlushDiskInfoToCmb2(); } } } // lpCurDir came from lpstrCurDir, which is of length CCHNETPATH EVAL(SUCCEEDED(StringCchCopy(lpCurDir, CCHNETPATH, (LPTSTR)&szCurDir[cchDirLen]))); // szCurDir not longer than CCH_NETPATH PathAddBackslash(lpCurDir); // // If the worker thread is running, then trying to select here // will just render the cmb2 blank, which is what we want; // otherwise, it should successfully select it. // SelDisk(hDlg, gaDiskInfo[nIndex].lpPath); } return (nIndex); } //////////////////////////////////////////////////////////////////////////// // // IsFileSystemCasePreserving // //////////////////////////////////////////////////////////////////////////// BOOL IsFileSystemCasePreserving( LPTSTR lpszDisk) { TCHAR szPath[MAX_FULLPATHNAME]; DWORD dwFlags; if (!lpszDisk) { return (FALSE); } if (SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), lpszDisk))) { if (SUCCEEDED(StringCchCat(szPath, ARRAYSIZE(szPath), L"\\"))) { if (GetVolumeInformation( szPath, NULL, 0, NULL, NULL, &dwFlags, NULL, 0 )) { return ((dwFlags & FS_CASE_IS_PRESERVED)); } } } // // Default to FALSE if there is an error. // return (FALSE); } //////////////////////////////////////////////////////////////////////////// // // IsLFNDriveX // //////////////////////////////////////////////////////////////////////////// BOOL IsLFNDriveX( HWND hDlg, LPTSTR pszPath) { TCHAR szRootPath[MAX_FULLPATHNAME]; DWORD dwVolumeSerialNumber; DWORD dwMaximumComponentLength; DWORD dwFileSystemFlags; LPTSTR lpCurDir; LPCURDLG lpCurDlg; if (!pszPath[0] || !pszPath[1] || (pszPath[1] != CHAR_COLON && !(DBL_BSLASH(pszPath)))) { // // If the path is not a full path then get the directory path // from the TLS current directory. // lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL); EVAL(SUCCEEDED(StringCchCopy(szRootPath, ARRAYSIZE(szRootPath), lpCurDir))); } else { EVAL(SUCCEEDED(StringCchCopy(szRootPath, ARRAYSIZE(szRootPath), pszPath))); } if (szRootPath[1] == CHAR_COLON) { szRootPath[2] = CHAR_BSLASH; szRootPath[3] = 0; } else if (DBL_BSLASH(szRootPath)) { int i; LPTSTR p; // // Stop at "\\foo\bar". // for (i = 0, p = szRootPath + 2; *p && i < 2; p++) { if (ISBACKSLASH_P(szRootPath, p)) { i++; } } switch (i) { case ( 0 ) : { return (FALSE); } case ( 1 ) : { if (lstrlen(szRootPath) < MAX_FULLPATHNAME - 2) { *p = CHAR_BSLASH; *(p + 1) = CHAR_NULL; } else { return (FALSE); } break; } case ( 2 ) : { *p = CHAR_NULL; break; } } } if (GetVolumeInformation( szRootPath, NULL, 0, &dwVolumeSerialNumber, &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0 )) { if (dwMaximumComponentLength == (MAXDOSFILENAMELEN - 1)) { return (FALSE); } else { return (TRUE); } } return (FALSE); } //////////////////////////////////////////////////////////////////////////// // // DiskAddedPreviously // // This routine checks to see if a disk resource has been previously // added to the global structure. // // wcDrive - if this is set, then there is no lpszName comparison // lpszName - if wcDrive is not set, but the lpszName is of the form // "c:\" then set wcDrive = *lpszName and index by drive letter // else assume lpszName is a unc name // // Returns: 0xFFFFFFFF failure (disk doesn't exist in list) // 0 - 128 number of disk in list // //////////////////////////////////////////////////////////////////////////// int DiskAddedPreviously( TCHAR wcDrive, LPTSTR lpszName) { WORD i; // // There are two index schemes (by drive or by unc \\server\share). // If it doesn't have a drive letter, assume unc. // if (wcDrive || (lpszName && (*(lpszName + 1) == CHAR_COLON))) { if (!wcDrive) { wcDrive = *lpszName; wcDrive = (TCHAR)CharLower((LPTSTR)wcDrive); } for (i = 0; i < dwNumDisks; i++) { // // See if the drive letters are the same. // if (wcDrive) { if (wcDrive == (TCHAR)CharLower((LPTSTR)gaDiskInfo[i].wcDrive)) { return (i); } } } } else if (lpszName) { DWORD cchDirLen; TCHAR wc; // // Check remote name (\\server\share). // cchDirLen = GetPathOffset(lpszName); // // If we're given a unc path, get the disk name. // Otherwise, assume the whole thing is a disk name. // if (cchDirLen != -1) { wc = *(lpszName + cchDirLen); *(lpszName + cchDirLen) = CHAR_NULL; } for (i = 0; i < dwNumDisks; i++) { if (!lstrcmpi(gaDiskInfo[i].lpName, lpszName)) { if (cchDirLen != -1) { *(lpszName + cchDirLen) = wc; } return (i); } } if (cchDirLen != -1) { *(lpszName + cchDirLen) = wc; } } return (0xFFFFFFFF); } //////////////////////////////////////////////////////////////////////////// // // AddDisk // // Adds a disk to one of the global structures: // gaNetDiskInfo // gaLocalDiskInfo // // wcDrive - the drive to attach to (this parm should be 0 for unc) // lpName - \\server\share name for remote disks // volume name for local disks // lpProvider - used for remote disks only, the name of the provider // used with WNetFormatNetworkName api // dwType - type of the bitmap to display // except when we are adding a drive letter temporarily // at startup this parameter can equal TMPNETDRV in which // case we set the bitmap to NETDRVBMP // // Returns: -2 Cannot Add Disk // -1 DiskInfo did not change // 0 dwNumDisks - DiskInfo changed // //////////////////////////////////////////////////////////////////////////// int AddDisk( TCHAR wcDrive, LPTSTR lpName, LPTSTR lpProvider, DWORD dwType) { int nIndex, nRet; DWORD cchMultiLen = 0; DWORD cchAbbrLen = 0; DWORD cchLen; DWORD dwRet = 0; LPTSTR lpBuff; OFN_DISKINFO *pofndiDisk = NULL, *pgDI; // // Sanity check - wcDrive and/or lpName must be set. // if (!wcDrive && (!lpName || !*lpName)) { return (ADDDISK_INVALIDPARMS); } nIndex = DiskAddedPreviously(wcDrive, lpName); if (nIndex != 0xFFFFFFFF) { // // Do not add a temporary drive letter if we already // have something better (added, for example, in a previous call). // if (dwType == TMPNETDRV) { gaDiskInfo[nIndex].bValid = TRUE; return (ADDDISK_NOCHANGE); } // Using a floating profile, there can be collisions between // local and network drives in which case we take the former. // // Note: If the drive is remembered, we assume that getdrivetype // will return false and that the drive is not added. // But if it was added, then we overwrite anyway, // since it's the desired behavior. // if ((dwType == REMDRVBMP) && (dwType != gaDiskInfo[nIndex].dwType)) { return (ADDDISK_NOCHANGE); } // // Update previous connections. // if (!lstrcmpi(lpName, gaDiskInfo[nIndex].lpName)) { // // Don't update a connection as remembered, unless it's been // invalidated. // if (dwType != REMDRVBMP) { gaDiskInfo[nIndex].dwType = dwType; } gaDiskInfo[nIndex].bValid = TRUE; return (ADDDISK_NOCHANGE); } else if (!*lpName && ((dwType == CDDRVBMP) || (dwType == FLOPPYBMP))) { // // Guard against lazy calls to updatelocaldrive erasing current // changed dir volume name (set via changedir). // return (ADDDISK_NOCHANGE); } } if (dwNumDisks >= MAX_DISKS) { return (ADDDISK_MAXNUMDISKS); } // // If there is a drive, then lpPath needs only 4. // If it's unc, then lpPath just equals lpName. // if (wcDrive) { cchLen = 4; } else { cchLen = 0; } if (lpName && *lpName) { // // Get the length of the standard (Remote/Local) name. // cchLen += (lstrlen(lpName) + 1); if (lpProvider && *lpProvider && ((dwType == NETDRVBMP) || (dwType == REMDRVBMP))) { // // Get the length for the multiline name. // dwRet = WNetFormatNetworkName( lpProvider, lpName, NULL, &cchMultiLen, WNFMT_MULTILINE, dwAveCharPerLine ); if (dwRet != ERROR_MORE_DATA) { return (ADDDISK_NETFORMATFAILED); } // // Add 4 for :\ and NULL (safeguard) // if (wcDrive) { cchMultiLen += 4; } dwRet = WNetFormatNetworkName( lpProvider, lpName, NULL, &cchAbbrLen, WNFMT_ABBREVIATED, dwAveCharPerLine ); if (dwRet != ERROR_MORE_DATA) { return (ADDDISK_NETFORMATFAILED); } // // Add 4 for :\ and NULL (safeguard). // if (wcDrive) { cchAbbrLen += 4; } } else { // // Make enough room so that lpMulti and lpAbbr can point to // 4 characters (drive letter + : + space + null) ahead of // lpremote. // if (wcDrive) { cchLen += 4; } } } else { // // Make enough room so that lpMulti and lpAbbr can point to // 4 characters (drive letter + : + space + null) ahead of // lpremote. // if (wcDrive) { cchLen += 4; } } // // Allocate a temp OFN_DISKINFO object to work with. // When we are finished, we'll request the critical section // and update the global array. // pofndiDisk = (OFN_DISKINFO *)LocalAlloc(LPTR, sizeof(OFN_DISKINFO)); if (!pofndiDisk) { // // Can't alloc or realloc memory, return error. // nRet = ADDDISK_ALLOCFAILED; goto AddDisk_Error; } lpBuff = (LPTSTR)LocalAlloc( LPTR, (cchLen + cchMultiLen + cchAbbrLen) * sizeof(TCHAR)); if (!lpBuff) { // // Can't alloc or realloc memory, return error. // nRet = ADDDISK_ALLOCFAILED; goto AddDisk_Error; } if (dwType == TMPNETDRV) { pofndiDisk->dwType = NETDRVBMP; } else { pofndiDisk->dwType = dwType; } // // Always set these slots, even though wcDrive can equal 0. // pofndiDisk->wcDrive = wcDrive; pofndiDisk->bValid = TRUE; pofndiDisk->cchLen = cchLen + cchAbbrLen + cchMultiLen; // // NOTE: lpAbbrName must always point to the head of lpBuff // so that we can free the block later at DLL_PROCESS_DETACH // if (lpName && *lpName && lpProvider && *lpProvider && ((dwType == NETDRVBMP) || (dwType == REMDRVBMP))) { // // Create an entry for a network disk. // pofndiDisk->lpAbbrName = lpBuff; if (wcDrive) { *lpBuff++ = wcDrive; *lpBuff++ = CHAR_COLON; *lpBuff++ = CHAR_SPACE; cchAbbrLen -= 3; } dwRet = WNetFormatNetworkName( lpProvider, lpName, lpBuff, &cchAbbrLen, WNFMT_ABBREVIATED, dwAveCharPerLine ); if (dwRet != WN_SUCCESS) { nRet = ADDDISK_NETFORMATFAILED; LocalFree(lpBuff); goto AddDisk_Error; } lpBuff += cchAbbrLen; pofndiDisk->lpMultiName = lpBuff; if (wcDrive) { *lpBuff++ = wcDrive; *lpBuff++ = CHAR_COLON; *lpBuff++ = CHAR_SPACE; cchMultiLen -= 3; } dwRet = WNetFormatNetworkName(lpProvider, lpName, lpBuff, &cchMultiLen, WNFMT_MULTILINE, dwAveCharPerLine); if (dwRet != WN_SUCCESS) { nRet = ADDDISK_NETFORMATFAILED; LocalFree(lpBuff); goto AddDisk_Error; } // // Note: this assumes that the lpRemoteName // returned by WNetEnumResources is always in // the form \\server\share (without a trailing bslash). // pofndiDisk->lpPath = lpBuff; // // if it's not unc. // if (wcDrive) { *lpBuff++ = wcDrive; *lpBuff++ = CHAR_COLON; *lpBuff++ = CHAR_NULL; cchLen -= 3; } EVAL(SUCCEEDED(StringCchCopy(lpBuff, cchLen, lpName))); // Should always be enough room. pofndiDisk->lpName = lpBuff; pofndiDisk->bCasePreserved = IsFileSystemCasePreserving(pofndiDisk->lpPath); } else { // // Create entry for a local name, or a network one with // no name yet. // pofndiDisk->lpAbbrName = pofndiDisk->lpMultiName = lpBuff; if (wcDrive) { *lpBuff++ = wcDrive; *lpBuff++ = CHAR_COLON; *lpBuff++ = CHAR_SPACE; cchLen -= 3; } if (lpName) { EVAL(SUCCEEDED(StringCchCopy(lpBuff, cchLen, lpName))); // Should always be enough room. } else { *lpBuff = CHAR_NULL; } pofndiDisk->lpName = lpBuff; if (wcDrive) { lpBuff += lstrlen(lpBuff) + 1; *lpBuff = wcDrive; *(lpBuff + 1) = CHAR_COLON; *(lpBuff + 2) = CHAR_NULL; } pofndiDisk->lpPath = lpBuff; if ((dwType == NETDRVBMP) || (dwType == REMDRVBMP)) { pofndiDisk->bCasePreserved = IsFileSystemCasePreserving(pofndiDisk->lpPath); } else { pofndiDisk->bCasePreserved = FALSE; } } // // Now we need to update the global array. // if (nIndex == 0xFFFFFFFF) { nIndex = dwNumDisks; } pgDI = &gaDiskInfo[nIndex]; // // Enter critical section and update data. // EnterCriticalSection(&g_csLocal); pgDI->cchLen = pofndiDisk->cchLen; pgDI->lpAbbrName = pofndiDisk->lpAbbrName; pgDI->lpMultiName = pofndiDisk->lpMultiName; pgDI->lpName = pofndiDisk->lpName; pgDI->lpPath = pofndiDisk->lpPath; pgDI->wcDrive = pofndiDisk->wcDrive; pgDI->bCasePreserved = pofndiDisk->bCasePreserved; pgDI->dwType = pofndiDisk->dwType; pgDI->bValid = pofndiDisk->bValid; LeaveCriticalSection(&g_csLocal); if ((DWORD)nIndex == dwNumDisks) { dwNumDisks++; } nRet = nIndex; AddDisk_Error: if (pofndiDisk) { LocalFree(pofndiDisk); } return (nRet); } //////////////////////////////////////////////////////////////////////////// // // EnableDiskInfo // //////////////////////////////////////////////////////////////////////////// VOID EnableDiskInfo( BOOL bValid, BOOL bDoUnc) { DWORD dwCnt = dwNumDisks; EnterCriticalSection(&g_csLocal); while (dwCnt--) { if (gaDiskInfo[dwCnt].dwType == NETDRVBMP) { if (!(DBL_BSLASH(gaDiskInfo[dwCnt].lpAbbrName)) || bDoUnc) { gaDiskInfo[dwCnt].bValid = bValid; } // // Always re-invalidate remembered just in case someone // escapes from fileopen, removes a connection // overriding a remembered and comes back expecting to see // the original remembered. // } } LeaveCriticalSection(&g_csLocal); } //////////////////////////////////////////////////////////////////////////// // // FlushDiskInfoToCmb2 // //////////////////////////////////////////////////////////////////////////// VOID FlushDiskInfoToCmb2() { DWORD dwDisk; DWORD dwDlg; for (dwDlg = 0; dwDlg < dwNumDlgs; dwDlg++) { if (gahDlg[dwDlg]) { HWND hCmb2; if (hCmb2 = GetDlgItem(gahDlg[dwDlg], cmb2)) { wNoRedraw |= 1; SendMessage(hCmb2, WM_SETREDRAW, FALSE, 0L); SendMessage(hCmb2, CB_RESETCONTENT, 0, 0); dwDisk = dwNumDisks; while (dwDisk--) { if (gaDiskInfo[dwDisk].bValid) { SendMessage( hCmb2, CB_SETITEMDATA, (WPARAM)SendMessage( hCmb2, CB_ADDSTRING, (WPARAM)0, (LPARAM)(LPTSTR)gaDiskInfo[dwDisk].lpAbbrName ), (LPARAM)gaDiskInfo[dwDisk].dwType ); } } wNoRedraw &= ~1; SendMessage(hCmb2, WM_SETREDRAW, TRUE, 0L); InvalidateRect(hCmb2, NULL, FALSE); SendMessage( gahDlg[dwDlg], WM_COMMAND, GET_WM_COMMAND_MPS(cmb2, hCmb2, MYCBN_REPAINT) ); } gahDlg[dwDlg] = NULL; } } } //////////////////////////////////////////////////////////////////////////// // // CallNetDlg // // Calls the appropriate network dialog in winnet driver. // // hwndParent - parent window of network dialog // // Returns: TRUE there are new drives to display // FALSE there are no new drives to display // //////////////////////////////////////////////////////////////////////////// BOOL CallNetDlg( HWND hWnd) { DWORD wRet; HourGlass(TRUE); wRet = WNetConnectionDialog(hWnd, WNTYPE_DRIVE); if ((wRet != WN_SUCCESS) && (wRet != WN_CANCEL) && (wRet != 0xFFFFFFFF)) { if (CDLoadString( g_hinst, iszNoNetButtonResponse, szCaption, ARRAYSIZE(szCaption))) { EVAL(SUCCEEDED(StringCchPrintf(szWarning, ARRAYSIZE(szWarning), szCaption))); GetWindowText(hWnd, szCaption, WARNINGMSGLENGTH); MessageBox( hWnd, szWarning, szCaption, MB_OK | MB_ICONEXCLAMATION ); } } HourGlass(FALSE); return (wRet == WN_SUCCESS); } //////////////////////////////////////////////////////////////////////////// // // GetDiskType // //////////////////////////////////////////////////////////////////////////// UINT GetDiskType( LPTSTR lpszDisk) { // // Unfortunately GetDriveType is not for deviceless connections. // So assume all unc stuff is just "remote" - no way of telling // if it's a cdrom or not. // if (DBL_BSLASH(lpszDisk)) { return (DRIVE_REMOTE); } else { return (GetDriveType(lpszDisk)); } } //////////////////////////////////////////////////////////////////////////// // // GetUNCDirectoryFromLB // // If lb contains a UNC listing, the function returns the full UNC path. // // Returns: 0 if no UNC listing in lb // length of UNC listing string // Note: this also fills in pOFI->szPath with the *full* UNC path (if UNC), // or the name of the drive (if not UNC) // //////////////////////////////////////////////////////////////////////////// DWORD GetUNCDirectoryFromLB( HWND hDlg, WORD nLB, POPENFILEINFO pOFI) { DWORD cch; DWORD idir; DWORD idirCurrent; cch = (DWORD)SendDlgItemMessage( hDlg, nLB, LB_GETTEXT, 0, (LPARAM)(LPTSTR)pOFI->szPath ); // // If not UNC listing, return 0. // if (pOFI->szPath[0] != CHAR_BSLASH) { return (0); } idirCurrent = (WORD)(DWORD)SendDlgItemMessage( hDlg, nLB, LB_GETCURSEL, 0, 0L ); if (idirCurrent < (pOFI->idirSub - 1)) { pOFI->idirSub = idirCurrent; } pOFI->szPath[cch++] = CHAR_BSLASH; for (idir = 1; idir < pOFI->idirSub; ++idir) { cch += (DWORD)SendDlgItemMessage( hDlg, nLB, LB_GETTEXT, (WPARAM)idir, (LPARAM)(LPTSTR)&pOFI->szPath[cch] ); pOFI->szPath[cch++] = CHAR_BSLASH; } // // Only add the subdirectory if it's not the \\server\share point. // if (idirCurrent && (idirCurrent >= pOFI->idirSub)) { cch += (DWORD)SendDlgItemMessage( hDlg, nLB, LB_GETTEXT, (WPARAM)idirCurrent, (LPARAM)(LPTSTR)&pOFI->szPath[cch] ); pOFI->szPath[cch++] = CHAR_BSLASH; } pOFI->szPath[cch] = CHAR_NULL; return (cch); } //////////////////////////////////////////////////////////////////////////// // // SelDisk // // Selects the given disk in the combo drive list. Works for unc names, // too. // //////////////////////////////////////////////////////////////////////////// VOID SelDisk( HWND hDlg, LPTSTR lpszDisk) { HWND hCmb = GetDlgItem(hDlg, cmb2); if (lpszDisk) { CharLower(lpszDisk); SendMessage( hCmb, CB_SETCURSEL, (WPARAM)SendMessage( hCmb, CB_FINDSTRING, (WPARAM)-1, (LPARAM)lpszDisk ), 0L ); } else { TCHAR szChangeSel[CCHNETPATH]; LPTSTR lpCurDir; LPCURDLG lpCurDlg; if ((lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) && (lpCurDir = lpCurDlg->lpstrCurDir)) { int cch; if (!GetCurrentDirectory(ARRAYSIZE(szChangeSel), szChangeSel)) { EVAL(SUCCEEDED(StringCchCopy(szChangeSel, ARRAYSIZE(szChangeSel), lpCurDir))); } cch = GetPathOffset(szChangeSel); if (cch != -1) { szChangeSel[cch] = CHAR_NULL; } SendMessage( hCmb, CB_SETCURSEL, (WPARAM)SendMessage( hCmb, CB_FINDSTRING, (WPARAM)-1, (LPARAM)szChangeSel ), 0L ); } } } //////////////////////////////////////////////////////////////////////////// // // LNDSetEvent // //////////////////////////////////////////////////////////////////////////// VOID LNDSetEvent( HWND hDlg) { LPCURDLG lpCurDlg; lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); if ( lpCurDlg && hLNDEvent && !wNoRedraw && hLNDThread && bNetworkInstalled) { gahDlg[lpCurDlg->dwCurDlgNum] = hDlg; SetEvent(hLNDEvent); } } //////////////////////////////////////////////////////////////////////////// // // UpdateLocalDrive // //////////////////////////////////////////////////////////////////////////// VOID UpdateLocalDrive( LPTSTR szDrive, BOOL bGetVolName) { DWORD dwFlags = 0; DWORD dwDriveType; TCHAR szVolLabel[MAX_PATH]; // // No unc here - so bypass extra call to GetDiskType and call // GetDriveType directly. // dwDriveType = GetDriveType(szDrive); if ((dwDriveType != 0) && (dwDriveType != 1)) { BOOL bRet = TRUE; szVolLabel[0] = CHAR_NULL; szDrive[1] = CHAR_COLON; szDrive[2] = CHAR_NULL; if ( bGetVolName || ((dwDriveType != DRIVE_REMOVABLE) && (dwDriveType != DRIVE_CDROM) && (dwDriveType != DRIVE_REMOTE)) ) { // // Removing call to CharUpper since it causes trouble on // turkish machines. // // CharUpper(szDrive); if (GetFileAttributes(szDrive) != (DWORD)0xffffffff) { if (dwDriveType != DRIVE_REMOTE) { szDrive[2] = CHAR_BSLASH; bRet = GetVolumeInformation( szDrive, szVolLabel, MAX_PATH, NULL, NULL, &dwFlags, NULL, (DWORD)0 ); // // The adddisk hack to prevent lazy loading from // overwriting the current removable media's label // with "" (because it never calls getvolumeinfo) // is to not allow null lpnames to overwrite, so when // the volume label really is null, we make it a space. // if (!szVolLabel[0]) { szVolLabel[0] = CHAR_SPACE; szVolLabel[1] = CHAR_NULL; } } } } if (bRet) { int nIndex; CharLower(szDrive); if (dwDriveType == DRIVE_REMOTE) { nIndex = AddDisk( szDrive[0], szVolLabel, NULL, TMPNETDRV ); } else { nIndex = AddDisk( szDrive[0], szVolLabel, NULL, GetDiskIndex(dwDriveType) ); } if (nIndex != ADDDISK_NOCHANGE) { gaDiskInfo[nIndex].bCasePreserved = (dwFlags & FS_CASE_IS_PRESERVED); } } } } //////////////////////////////////////////////////////////////////////////// // // GetNetDrives // // Enumerates network disk resources and updates the global disk info // structure. // // dwScope RESOURCE_CONNECTED or RESOURCE_REMEMBERED // // Returns the last connection that did not previously exist. // //////////////////////////////////////////////////////////////////////////// VOID GetNetDrives( DWORD dwScope) { DWORD dwRet; HANDLE hEnum = NULL; // // Guard against termination with the enum handle open. // dwRet = WNetOpenEnum( dwScope, RESOURCETYPE_DISK, RESOURCEUSAGE_CONNECTABLE, NULL, &hEnum ); if (dwRet == WN_SUCCESS) { while (dwRet == WN_SUCCESS) { DWORD dwCount = 0xffffffff; DWORD cbSize = cbNetEnumBuf; if (bLNDExit) { WNetCloseEnum(hEnum); return; } dwRet = WNetEnumResource(hEnum, &dwCount, gpcNetEnumBuf, &cbSize); switch (dwRet) { case ( WN_SUCCESS ) : { // // Add the Entries to the listbox. // TCHAR wcDrive = 0; NETRESOURCE *pNetRes; WORD i; pNetRes = (LPNETRESOURCE)gpcNetEnumBuf; for (i = 0; dwCount; dwCount--, i++) { if (pNetRes[i].lpLocalName) { CharLower(pNetRes[i].lpLocalName); wcDrive = *pNetRes[i].lpLocalName; } else { // // Skip deviceless names that are not // LanMan provided (or, in the case where there // is no LanMan provider name, skip deviceless // always). // wcDrive = 0; } if (!DBL_BSLASH(pNetRes[i].lpRemoteName)) { continue; } // // When bGetNetDrivesSync is TRUE, we are coming back // from the Network button, so we want to cd to the // last connected drive. // (see last command in this routine) // if (bGetNetDrivesSync) { int nIndex; WORD k; nIndex = AddDisk( wcDrive, pNetRes[i].lpRemoteName, pNetRes[i].lpProvider, (dwScope == RESOURCE_REMEMBERED) ? REMDRVBMP : NETDRVBMP ); // // If it's a new connection, update global state. // if (nIndex >= 0) { // // Since flushdiskinfotocmb2 will clear out // the array below, remember it's state here. // It's a hack, but a nice way to find out // exactly which of the many threads // completed a net dlg operation. // for (k = 0; k < dwNumDlgs; k++) { if (gahDlg[k]) { // Could encounter small problems with // preemption here, but assume that // user cannot simultaneously return // from two different net dlg calls. // lpNetDriveSync = gaDiskInfo[nIndex].lpPath; SendMessage( gahDlg[k], WM_COMMAND, GET_WM_COMMAND_MPS( cmb2, GetDlgItem(gahDlg[k], cmb2), MYCBN_CHANGEDIR ) ); } } } } else { AddDisk( wcDrive, pNetRes[i].lpRemoteName, pNetRes[i].lpProvider, (dwScope == RESOURCE_REMEMBERED) ? REMDRVBMP : NETDRVBMP ); } } break; } case ( WN_MORE_DATA ) : { LPTSTR pcTemp; pcTemp = (LPTSTR)LocalReAlloc( gpcNetEnumBuf, cbSize, LMEM_MOVEABLE ); if (!pcTemp) { cbNetEnumBuf = 0; } else { gpcNetEnumBuf = pcTemp; cbNetEnumBuf = cbSize; dwRet = WN_SUCCESS; break; } } case ( WN_NO_MORE_ENTRIES ) : case ( WN_EXTENDED_ERROR ) : case ( WN_NO_NETWORK ) : { // // WN_NO_MORE_ENTRIES is a success error code. // It is special cased when we fall out of the loop. // break; } case ( WN_BAD_HANDLE ) : default : { break; } } } WNetCloseEnum(hEnum); // // Flush once per event - there will always be a call with // dwscope = connected. // if (dwScope == RESOURCE_CONNECTED) { FlushDiskInfoToCmb2(); } if (bGetNetDrivesSync) { bGetNetDrivesSync = FALSE; } } } //////////////////////////////////////////////////////////////////////////// // // ListNetDrivesHandler // //////////////////////////////////////////////////////////////////////////// VOID ListNetDrivesHandler() { BOOL bInit = TRUE; HANDLE hEnum = NULL; if (!gpcNetEnumBuf && !(gpcNetEnumBuf = (LPTSTR)LocalAlloc(LPTR, cbNetEnumBuf))) { hLNDThread = NULL; return; } if (bLNDExit) { goto LNDExitThread1; } EnterCriticalSection(&g_csNetThread); while (1) { if (bLNDExit) { goto LNDExitThread; } // // hLNDEvent will always be valid since we have loaded ourself // and FreeLibrary will not produce a DLL_PROCESS_DETACH. // WaitForSingleObject(hLNDEvent, INFINITE); // // In case this is the exit event. // if (bLNDExit) { goto LNDExitThread; } EnableDiskInfo(FALSE, FALSE); if (bInit) { GetNetDrives(RESOURCE_REMEMBERED); // // In case this is the exit event. // if (bLNDExit) { goto LNDExitThread; } GetNetDrives(RESOURCE_CONNECTED); // // In case this is the exit event. // if (bLNDExit) { goto LNDExitThread; } bInit = FALSE; } else { // // In case this is the exit event. // if (bLNDExit) { goto LNDExitThread; } GetNetDrives(RESOURCE_CONNECTED); // // In case this is the exit event. // if (bLNDExit) { goto LNDExitThread; } } ResetEvent(hLNDEvent); } LNDExitThread: bLNDExit = FALSE; LeaveCriticalSection(&g_csNetThread); LNDExitThread1: FreeLibraryAndExitThread(g_hinst, 1); // // The ExitThread is implicit in this return. // return; } //////////////////////////////////////////////////////////////////////////// // // LoadDrives // // Lists the current drives (connected) in the combo box. // //////////////////////////////////////////////////////////////////////////// VOID LoadDrives( HWND hDlg) { // // Hard-code this - It's internal && always cmb2/psh14. // HWND hCmb = GetDlgItem(hDlg, cmb2); DWORD dwThreadID; LPCURDLG lpCurDlg; BOOL bFirstAttach = FALSE; WORD wCurDrive; TCHAR szDrive[5]; if (!hLNDEvent) { // // Don't check if this succeeds since we can run without the net. // hLNDEvent = CreateEvent(NULL, TRUE, FALSE, NULL); bFirstAttach = TRUE; } else { // // Assume all previous connections (except unc) are valid // for first display - but only when they exist. // EnableDiskInfo(TRUE, FALSE); } // // Set the hDlg into the refresh array before initially // creating the thread so that the worker thread can hide/disable // the net button in the event that there is no network. // lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); // sanity check if (!lpCurDlg) { return; } gahDlg[lpCurDlg->dwCurDlgNum] = hDlg; // // If there is no worker thread for network disk enumeration, // start up here rather than in the dll, since it's only // for the fileopen dlg. // // Always start a thread if the number of active fileopen dialogs // goes from 0 to 1 // if ((lpCurDlg->dwCurDlgNum == 0) && (!hLNDThread)) { if (hLNDEvent && (bNetworkInstalled = IsNetworkInstalled())) { TCHAR szModule[MAX_PATH]; // // Do this once when dialog thread count goes from 0 to 1. // GetModuleFileName(g_hinst, szModule, ARRAYSIZE(szModule)); if (LoadLibrary(szModule)) { hLNDThread = CreateThread( NULL, (DWORD)0, (LPTHREAD_START_ROUTINE)ListNetDrivesHandler, (LPVOID)NULL, (DWORD_PTR)NULL, &dwThreadID ); } } else { HWND hNet = GetDlgItem(hDlg, psh14); EnableWindow(hNet, FALSE); ShowWindow(hNet, SW_HIDE); } } // Fix for Millenium BUG #113035 // Putting the get drives information code instead of in the // ListNetDriveHandler thread. // // Get the drive information for all drives. // // NOTE: If we don't redo all volume info, then a change in a volume // label will never be caught by wow apps unless wowexec is // killed and restarted. Therefore, information for all drives // should be retrieved here. // for (wCurDrive = 0; wCurDrive <= 25; wCurDrive++) { szDrive[0] = (CHAR_A + (TCHAR)wCurDrive); szDrive[1] = CHAR_COLON; szDrive[2] = CHAR_BSLASH; szDrive[3] = CHAR_NULL; UpdateLocalDrive(szDrive, FALSE); } FlushDiskInfoToCmb2(); // // Now invalidate all net conns and re-enum, but only if there is // indeed a worker thread too. // if (!bFirstAttach) { EnableDiskInfo(FALSE, FALSE); } LNDSetEvent(hDlg); } //////////////////////////////////////////////////////////////////////////// // // GetDiskIndex // //////////////////////////////////////////////////////////////////////////// DWORD GetDiskIndex( DWORD dwDriveType) { if (dwDriveType == 1) { // // Drive doesn't exist! // return (0); } else if (dwDriveType == DRIVE_CDROM) { return (CDDRVBMP); } else if (dwDriveType == DRIVE_REMOVABLE) { return (FLOPPYBMP); } else if (dwDriveType == DRIVE_REMOTE) { return (NETDRVBMP); } else if (dwDriveType == DRIVE_RAMDISK) { return (RAMDRVBMP); } return (HARDDRVBMP); } //////////////////////////////////////////////////////////////////////////// // // CleanUpFile // // This releases the memory used by the system dialog bitmaps. // //////////////////////////////////////////////////////////////////////////// VOID CleanUpFile() { // // Check if anyone else is around. // if (--cLock) { return; } // // Select the null bitmap into our memory DC so that the // DirDrive bitmap can be discarded. // SelectObject(hdcMemory, hbmpOrigMemBmp); } //////////////////////////////////////////////////////////////////////////// // // FileOpenAbort // //////////////////////////////////////////////////////////////////////////// VOID FileOpenAbort() { LPCURDLG lpCurDlg; lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg); if (lpCurDlg) { EnterCriticalSection(&g_csLocal); if (dwNumDlgs > 0) { dwNumDlgs--; } if (dwNumDlgs == 0) { // // If there are no more fileopen dialogs for this process, // then signal the worker thread it's all over. // if (hLNDEvent && hLNDThread) { bLNDExit = TRUE; SetEvent(hLNDEvent); CloseHandle(hLNDThread); hLNDThread = NULL; } } LeaveCriticalSection(&g_csLocal); } } //////////////////////////////////////////////////////////////////////////// // // TermFile // //////////////////////////////////////////////////////////////////////////// VOID TermFile() { vDeleteDirDriveBitmap(); if (hdcMemory) { DeleteDC(hdcMemory); } if (hLNDEvent) { CloseHandle(hLNDEvent); hLNDEvent = NULL; } if (gpcNetEnumBuf) { LocalFree(gpcNetEnumBuf); } while (dwNumDisks) { dwNumDisks--; if (gaDiskInfo[dwNumDisks].lpAbbrName) { LocalFree(gaDiskInfo[dwNumDisks].lpAbbrName); } } } /*========================================================================*/ /* Ansi->Unicode Thunk routines */ /*========================================================================*/ #ifdef UNICODE //////////////////////////////////////////////////////////////////////////// // // ThunkOpenFileNameA2WDelayed // //////////////////////////////////////////////////////////////////////////// VOID ThunkOpenFileNameA2WDelayed( POPENFILEINFO pOFI) { LPOPENFILENAMEA pOFNA = pOFI->pOFNA; LPOPENFILENAMEW pOFNW = pOFI->pOFN; if (pOFNA->lpstrDefExt) { // // Make sure the default extension buffer is at least 4 characters // in length. // DWORD cbLen = max(lstrlenA(pOFNA->lpstrDefExt) + 1, 4); if (pOFNW->lpstrDefExt) { LocalFree((HLOCAL)pOFNW->lpstrDefExt); } if (!(pOFNW->lpstrDefExt = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); return; } else { if (pOFNA->lpstrDefExt) { SHAnsiToUnicode(pOFNA->lpstrDefExt,(LPWSTR)pOFNW->lpstrDefExt,cbLen ); } } } // // Need to thunk back to A value since Claris Filemaker side effects // this in an ID_OK subclass without hooking at the very last moment. // Do an |= instead of an = to preserve internal flags. // pOFNW->Flags &= OFN_ALL_INTERNAL_FLAGS; pOFNW->Flags |= pOFNA->Flags; } //////////////////////////////////////////////////////////////////////////// // // ThunkOpenFileNameA2W // //////////////////////////////////////////////////////////////////////////// BOOL ThunkOpenFileNameA2W( POPENFILEINFO pOFI) { int nRet; LPOPENFILENAMEA pOFNA = pOFI->pOFNA; LPOPENFILENAMEW pOFNW = pOFI->pOFN; pOFNW->Flags = pOFNA->Flags; pOFNW->lCustData = pOFNA->lCustData; // we actually can have the original ver1 structure passed in here // so we need to check and make sure to only copy over the valid data if ((pOFNA->lStructSize == SIZEOF(OPENFILENAMEA) && pOFNW->lStructSize == SIZEOF(OPENFILENAMEW)) ) { pOFNW->pvReserved = pOFNA->pvReserved ; pOFNW->dwReserved = pOFNA->dwReserved; pOFNW->FlagsEx = pOFNA->FlagsEx; } // // Various WOW apps change the strings and *ptrs* to the strings in the // OPENFILENAME struct while processing messages with their hook procs. // Handle that silliness here. (We probably don't want to promote this // beyond WOW). // if (pOFNA->Flags & CD_WOWAPP) { pOFNW->lpstrFilter = (LPCWSTR) ThunkMultiANSIStrToWIDE( (LPWSTR)pOFNW->lpstrFilter, (LPSTR)pOFNA->lpstrFilter, 0 ); pOFNW->lpstrCustomFilter = ThunkMultiANSIStrToWIDE( pOFNW->lpstrCustomFilter, pOFNA->lpstrCustomFilter, pOFNA->nMaxCustFilter ); pOFNW->lpstrFile = ThunkANSIStrToWIDE( pOFNW->lpstrFile, pOFNA->lpstrFile, pOFNA->nMaxFile ); pOFNW->lpstrFileTitle = ThunkANSIStrToWIDE( pOFNW->lpstrFileTitle, pOFNA->lpstrFileTitle, pOFNA->nMaxFileTitle ); pOFNW->lpstrInitialDir = (LPCWSTR) ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrInitialDir, (LPSTR)pOFNA->lpstrInitialDir, 0 ); pOFNW->lpstrTitle = (LPCWSTR) ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrTitle, (LPSTR)pOFNA->lpstrTitle, 0 ); pOFNW->lpstrDefExt = (LPCWSTR) ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrDefExt, (LPSTR)pOFNA->lpstrDefExt, 0 ); pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter; pOFNW->nMaxFile = pOFNA->nMaxFile; pOFNW->nMaxFileTitle = pOFNA->nMaxFileTitle; pOFNW->nFileOffset = pOFNA->nFileOffset; pOFNW->nFileExtension = pOFNA->nFileExtension; } else { if (pOFNW->lpstrFile) { if (pOFNA->lpstrFile) { nRet = SHAnsiToUnicode(pOFNA->lpstrFile,pOFNW->lpstrFile,pOFNW->nMaxFile ); if (nRet == 0) { return (FALSE); } } } if (pOFNW->lpstrFileTitle && pOFNW->nMaxFileTitle) { if (pOFNA->lpstrFileTitle) { nRet= MultiByteToWideChar(CP_ACP, 0, pOFNA->lpstrFileTitle, pOFNA->nMaxFileTitle, pOFNW->lpstrFileTitle, pOFNW->nMaxFileTitle); if (nRet == 0) { return (FALSE); } } } if (pOFNW->lpstrCustomFilter) { if (pOFI->pasCustomFilter) { LPSTR psz = pOFI->pasCustomFilter->Buffer; DWORD cch = 0; if (*psz || *(psz + 1)) { cch = 2; while (*psz || *(psz + 1)) { psz++; cch++; } } if (cch) { pOFI->pasCustomFilter->Length = cch; nRet = MultiByteToWideChar(CP_ACP, 0, pOFI->pasCustomFilter->Buffer, pOFI->pasCustomFilter->Length, pOFI->pusCustomFilter->Buffer, pOFI->pusCustomFilter->MaximumLength ); if (nRet == 0) { return (FALSE); } } } } } pOFNW->nFilterIndex = pOFNA->nFilterIndex; return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // ThunkOpenFileNameW2A // //////////////////////////////////////////////////////////////////////////// BOOL ThunkOpenFileNameW2A( POPENFILEINFO pOFI) { int nRet; LPOPENFILENAMEW pOFNW = pOFI->pOFN; LPOPENFILENAMEA pOFNA = pOFI->pOFNA; LPWSTR pszW; USHORT cch; // // Supposedly invariant, but not necessarily. // Definition: invariant - changed by 16-bit apps frequently // pOFNA->Flags = pOFNW->Flags; pOFNA->lCustData = pOFNW->lCustData; // this way we can assert that we are covered. DEBUG_CODE(pOFNA->nFileOffset = 0 ); // we actually can have the original ver1 structure passed in here // so we need to check and make sure to only copy over the valid data if (pOFNA->lStructSize == SIZEOF(OPENFILENAMEA) && pOFNW->lStructSize == SIZEOF(OPENFILENAMEW) ) { pOFNA->pvReserved = pOFNW->pvReserved; pOFNA->dwReserved = pOFNW->dwReserved; pOFNA->FlagsEx = pOFNW->FlagsEx; } if (pOFNA->lpstrFileTitle && pOFNA->nMaxFileTitle) { nRet = SHUnicodeToAnsi(pOFNW->lpstrFileTitle,pOFNA->lpstrFileTitle,pOFNA->nMaxFileTitle); if (nRet == 0) { return (FALSE); } } if (pOFNA->lpstrCustomFilter) { pszW = pOFI->pusCustomFilter->Buffer; cch = 0; if (*pszW || *(pszW + 1)) { cch = 2; while (*pszW || *(pszW + 1)) { pszW++; cch++; } } if (cch) { pOFI->pusCustomFilter->Length = cch; nRet = WideCharToMultiByte(CP_ACP, 0, pOFI->pusCustomFilter->Buffer, pOFI->pusCustomFilter->Length, pOFI->pasCustomFilter->Buffer, pOFI->pasCustomFilter->MaximumLength, NULL, NULL); if (nRet == 0) { return (FALSE); } } } pOFNA->nFilterIndex = pOFNW->nFilterIndex; if (pOFNA->lpstrFile && pOFNW->lpstrFile) { if (GetStoredExtendedError() == FNERR_BUFFERTOOSMALL) { // // In the case where the lpstrFile buffer is too small, // lpstrFile contains the size of the buffer needed for // the string rather than the string itself. // pszW = pOFNW->lpstrFile; switch (pOFNA->nMaxFile) { case ( 3 ) : default : { pOFNA->lpstrFile[2] = CHAR_NULL; // fall thru... } case ( 2 ) : { pOFNA->lpstrFile[1] = HIBYTE(*pszW); // fall thru... } case ( 1 ) : { pOFNA->lpstrFile[0] = LOBYTE(*pszW); // fall thru... } case ( 0 ) : { break; } } } else { LPWSTR pFileW = pOFNW->lpstrFile; DWORD cchFile = 0; // Find the length of string to be converted. This takes care of both single select (there will be only string) // and multiselect case (there will multiple strings with double null termination) while (*pFileW) { DWORD cch = lstrlenW(pFileW) +1; cchFile +=cch; pFileW += cch; } if (pOFNW->Flags & OFN_ALLOWMULTISELECT) { // for the double null terminator cchFile++; } // need to copy the whole buffer after the initial directory nRet = WideCharToMultiByte(CP_ACP, 0, pOFNW->lpstrFile, cchFile, pOFNA->lpstrFile, pOFNA->nMaxFile, NULL, NULL); if (nRet == 0) { return (FALSE); } if ((SHORT)pOFNW->nFileOffset > 0) { pOFNA->nFileOffset = (WORD) WideCharToMultiByte( CP_ACP, 0, pOFNW->lpstrFile, pOFNW->nFileOffset, NULL, 0, NULL, NULL ); } else { pOFNA->nFileOffset = pOFNW->nFileOffset; } if ((SHORT)pOFNW->nFileExtension > 0) { pOFNA->nFileExtension = (WORD) WideCharToMultiByte( CP_ACP, 0, pOFNW->lpstrFile, pOFNW->nFileExtension, NULL, 0, NULL, NULL ); } else { pOFNA->nFileExtension = pOFNW->nFileExtension; } } } else { pOFNA->nFileOffset = pOFNW->nFileOffset; pOFNA->nFileExtension = pOFNW->nFileExtension; } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // GenericGetFileNameA // //////////////////////////////////////////////////////////////////////////// BOOL GenericGetFileNameA( LPOPENFILENAMEA pOFNA, DLGPROC qfnDlgProc) { LPOPENFILENAMEW pOFNW; BOOL bRet = FALSE; OFN_UNICODE_STRING usCustomFilter; OFN_ANSI_STRING asCustomFilter; DWORD cbLen; LPSTR pszA; DWORD cch; LPBYTE pStrMem = NULL; OPENFILEINFO OFI = {0}; if (!pOFNA) { StoreExtendedError(CDERR_INITIALIZATION); return (FALSE); } //Set the Open File Version OFI.iVersion = OPENFILEVERSION; if (pOFNA->lStructSize == OPENFILENAME_SIZE_VERSION_400) { OFI.iVersion = OPENFILEVERSION_NT4; } // we allow both sizes because we allocate a full size one anyway // and we want to preserve the original structure for notifies if ((pOFNA->lStructSize != OPENFILENAME_SIZE_VERSION_400) && (pOFNA->lStructSize != sizeof(OPENFILENAMEA)) ) { StoreExtendedError(CDERR_STRUCTSIZE); return (FALSE); } if (!(pOFNW = (LPOPENFILENAMEW)LocalAlloc(LPTR, sizeof(OPENFILENAMEW)))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); return (FALSE); } // // Constant stuff. // pOFNW->lStructSize = sizeof(OPENFILENAMEW); pOFNW->hwndOwner = pOFNA->hwndOwner; pOFNW->hInstance = pOFNA->hInstance; pOFNW->lpfnHook = pOFNA->lpfnHook; // it will always be a valid structsize at this point if (pOFNA->lStructSize != OPENFILENAME_SIZE_VERSION_400) { pOFNW->pvReserved = pOFNA->pvReserved; pOFNW->dwReserved = pOFNA->dwReserved; pOFNW->FlagsEx = pOFNA->FlagsEx; } // // Init TemplateName constant. // if (pOFNA->Flags & OFN_ENABLETEMPLATE) { if (!IS_INTRESOURCE(pOFNA->lpTemplateName)) { cbLen = lstrlenA(pOFNA->lpTemplateName) + 1; if (!(pOFNW->lpTemplateName = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } else { SHAnsiToUnicode(pOFNA->lpTemplateName,(LPWSTR)pOFNW->lpTemplateName,cbLen); } } else { (DWORD_PTR)pOFNW->lpTemplateName = (DWORD_PTR)pOFNA->lpTemplateName; } } else { pOFNW->lpTemplateName = NULL; } // // Initialize Initial Dir constant. // if (pOFNA->lpstrInitialDir) { cbLen = lstrlenA(pOFNA->lpstrInitialDir) + 1; if (!(pOFNW->lpstrInitialDir = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } else { SHAnsiToUnicode(pOFNA->lpstrInitialDir,(LPWSTR)pOFNW->lpstrInitialDir,cbLen); } } else { pOFNW->lpstrInitialDir = NULL; } // // Initialize Title constant. // if (pOFNA->lpstrTitle) { cbLen = lstrlenA(pOFNA->lpstrTitle) + 1; if (!(pOFNW->lpstrTitle = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } else { SHAnsiToUnicode(pOFNA->lpstrTitle,(LPWSTR)pOFNW->lpstrTitle,cbLen ); } } else { pOFNW->lpstrTitle = NULL; } // // Initialize Def Ext constant. // if (pOFNA->lpstrDefExt) { // // Make sure the default extension buffer is at least 4 characters // in length. // cbLen = max(lstrlenA(pOFNA->lpstrDefExt) + 1, 4); if (!(pOFNW->lpstrDefExt = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } else { SHAnsiToUnicode(pOFNA->lpstrDefExt,(LPWSTR)pOFNW->lpstrDefExt,cbLen ); } } else { pOFNW->lpstrDefExt = NULL; } // // Initialize Filter constant. Note: 16-bit apps change this. // if (pOFNA->lpstrFilter) { pszA = (LPSTR)pOFNA->lpstrFilter; cch = 0; if (*pszA || *(pszA + 1)) { // // Pick up trailing nulls. // cch = 2; try { while (*pszA || *(pszA + 1)) { pszA++; cch++; } } except (EXCEPTION_EXECUTE_HANDLER) { StoreExtendedError(CDERR_INITIALIZATION); goto GenericExit; } } // // Need to do cch + 1 in the Local Alloc rather than just cch. // This is to make sure there is at least one extra null in the // string so that if a filter does not have the second part of // the pair, three nulls will be placed in the wide string. // // Example: "Print File (*.prn)\0\0\0" // if (!(pOFNW->lpstrFilter = (LPWSTR)LocalAlloc(LPTR, ((cch + 1) * sizeof(WCHAR))))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } else { MultiByteToWideChar(CP_ACP, 0, pOFNA->lpstrFilter, cch, (LPWSTR)pOFNW->lpstrFilter, cch); } } else { pOFNW->lpstrFilter = NULL; } // // Initialize File strings. // if (pOFNA->lpstrFile) { if (pOFNA->nMaxFile <= (DWORD)lstrlenA(pOFNA->lpstrFile)) { StoreExtendedError(CDERR_INITIALIZATION); goto GenericExit; } pOFNW->nMaxFile = pOFNA->nMaxFile; if (!(pOFNW->lpstrFile = (LPWSTR)LocalAlloc(LPTR, pOFNW->nMaxFile * sizeof(WCHAR)))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } } else { // // Conversion done in thunkofna2w. // pOFNW->nMaxFile = 0; pOFNW->lpstrFile = NULL; } // // Initialize File Title strings. // if (pOFNA->lpstrFileTitle && pOFNA->nMaxFileTitle) { // // Calculate length of lpstrFileTitle. // pszA = pOFNA->lpstrFileTitle; cch = 0; try { while (*pszA++) { cch++; } } except (EXCEPTION_EXECUTE_HANDLER) { if (cch) { cch--; } (pOFNA->lpstrFileTitle)[cch] = CHAR_NULL; } if (pOFNA->nMaxFileTitle < cch) { // // Override the incorrect length from the app. // Make room for the null. // pOFNW->nMaxFileTitle = cch + 1; } else { pOFNW->nMaxFileTitle = pOFNA->nMaxFileTitle; } if (!(pOFNW->lpstrFileTitle = (LPWSTR)LocalAlloc(LPTR, pOFNW->nMaxFileTitle * sizeof(WCHAR)))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } } else { // // Conversion done in thunkofna2w. // pOFNW->nMaxFileTitle = 0; pOFNW->lpstrFileTitle = NULL; } // // Initialize custom filter strings. // if ((asCustomFilter.Buffer = pOFNA->lpstrCustomFilter)) { pszA = pOFNA->lpstrCustomFilter; cch = 0; if (*pszA || *(pszA + 1)) { cch = 2; try { while (*pszA || *(pszA + 1)) { pszA++; cch++; } } except (EXCEPTION_EXECUTE_HANDLER) { StoreExtendedError(CDERR_INITIALIZATION); goto GenericExit; } } // // JVert-inspired-wow-compatibility-hack-to-make-vbasic2.0-makeexe // save-as-dialog-box-work-even-though-they-didn't-fill-in- // the-whole-structure(nMaxCustFilter)-according-to-winhelp-spec fix // if (!(pOFNA->Flags & OFN_NOLONGNAMES)) { if (((DWORD)cch >= pOFNA->nMaxCustFilter) || (pOFNA->nMaxCustFilter < 40)) { StoreExtendedError(CDERR_INITIALIZATION); goto GenericExit; } asCustomFilter.Length = cch; asCustomFilter.MaximumLength = pOFNA->nMaxCustFilter; pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter; } else { asCustomFilter.Length = cch; if (pOFNA->nMaxCustFilter < cch) { asCustomFilter.MaximumLength = cch; pOFNW->nMaxCustFilter = cch; } else { asCustomFilter.MaximumLength = pOFNA->nMaxCustFilter; pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter; } } usCustomFilter.MaximumLength = (asCustomFilter.MaximumLength + 1) * sizeof(WCHAR); usCustomFilter.Length = asCustomFilter.Length * sizeof(WCHAR); } else { pOFNW->nMaxCustFilter = usCustomFilter.MaximumLength = 0; pOFNW->lpstrCustomFilter = NULL; } if (usCustomFilter.MaximumLength > 0) { if (!(pStrMem = (LPBYTE)LocalAlloc(LPTR, usCustomFilter.MaximumLength))) { StoreExtendedError(CDERR_MEMALLOCFAILURE); goto GenericExit; } else { pOFNW->lpstrCustomFilter = usCustomFilter.Buffer = (LPWSTR)pStrMem; } } else { pStrMem = NULL; } OFI.pOFN = pOFNW; OFI.pOFNA = pOFNA; OFI.pasCustomFilter = &asCustomFilter; OFI.pusCustomFilter = &usCustomFilter; OFI.ApiType = COMDLG_ANSI; // // The following should always succeed. // if (!ThunkOpenFileNameA2W(&OFI)) { StoreExtendedError(CDERR_INITIALIZATION); goto GenericExit; } bRet = GetFileName(&OFI, qfnDlgProc); if (g_bUserPressedCancel == FALSE) { ThunkOpenFileNameW2A(&OFI); } GenericExit: if (pStrMem) { LocalFree(pStrMem); } if (!IS_INTRESOURCE(pOFNW->lpstrFile)) { LocalFree((HLOCAL)pOFNW->lpstrFile); } if (!IS_INTRESOURCE(pOFNW->lpstrFileTitle)) { LocalFree((HLOCAL)pOFNW->lpstrFileTitle); } if (!IS_INTRESOURCE(pOFNW->lpstrFilter)) { LocalFree((HLOCAL)pOFNW->lpstrFilter); } if (!IS_INTRESOURCE(pOFNW->lpstrDefExt)) { LocalFree((HLOCAL)pOFNW->lpstrDefExt); } if (!IS_INTRESOURCE(pOFNW->lpstrTitle)) { LocalFree((HLOCAL)pOFNW->lpstrTitle); } if (!IS_INTRESOURCE(pOFNW->lpstrInitialDir)) { LocalFree((HLOCAL)pOFNW->lpstrInitialDir); } if (!IS_INTRESOURCE(pOFNW->lpTemplateName)) { LocalFree((HLOCAL)pOFNW->lpTemplateName); } LocalFree(pOFNW); return (bRet); } //////////////////////////////////////////////////////////////////////////// // // Multi_strlenA // // This is a strlen for ANSI string lists that have several strings that // are *separated* by a NULL char and are *terminated* by two NULL chars. // // Returns length of string including all NULL *separators* but not the // 2nd NULL *terminator*. (ie. cat0dog00 would return length = 8) // //////////////////////////////////////////////////////////////////////////// int Multi_strlenA( LPCSTR str) { int ctr = 0; if (str) { while (*str) { while (*str++) { ctr++; } ctr++; // count the NULL separator } } return (ctr); } //////////////////////////////////////////////////////////////////////////// // // Multi_strcpyAtoW // // This is a strcpy for string lists that have several strings that are // *separated* by a NULL char and are *terminated* by two NULL chars. // Returns FALSE if: // 1. the wide buffer is determined to be too small // 2. the ptr to either buffer is NULL // Returns TRUE if the copy was successful. // //////////////////////////////////////////////////////////////////////////// BOOL Multi_strcpyAtoW( LPWSTR pDestW, LPCSTR pSrcA, int cChars) { int off = 0; int cb; if (!pSrcA || !pDestW) { return (FALSE); } cChars = max(cChars, (Multi_strlenA(pSrcA) + 1)); if (LocalSize((HLOCAL)pDestW) < (cChars * sizeof(WCHAR))) { return (FALSE); } while (*pSrcA) { cb = lstrlenA(pSrcA) + 1; off += MultiByteToWideChar(CP_ACP,0,pSrcA,cb,pDestW + off, cb); pSrcA += cb; } pDestW[off] = L'\0'; return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // ThunkMultiANSIStrToWIDE // // Thunks an ANSI multi-string (a list of NULL *separated* strings with // two NULLs *terminating* the list) to the equivalent WIDE multi-string. // // Note: If the original wide buffer is too small to contain the new list, // it will be free'd and a new wide buffer will be allocated. If a // new wide buffer can't be allocated, the ptr to the original wide // buffer is returned with no changes to the contents. // // Returns: ptr to the original WIDE buffer // OR ptr to a new wide buffer if original buffer was too small // OR NULL if pSrcA is NULL. // //////////////////////////////////////////////////////////////////////////// LPWSTR ThunkMultiANSIStrToWIDE( LPWSTR pDestW, LPSTR pSrcA, int cChars) { int size; HLOCAL hBufW; if (!pSrcA) { // // The app doesn't want a buffer for this anymore. // if (pDestW) { LocalFree((HLOCAL)pDestW); } return (NULL); } // // First try to copy to the existing wide buffer since most of the time // there will be no change to the buffer ptr anyway. // if (!(Multi_strcpyAtoW(pDestW, pSrcA, cChars))) { // // If the wide buffer is too small (or NULL or invalid), allocate // a bigger buffer. // size = max(cChars, (Multi_strlenA(pSrcA) + 1)); cChars = size; if (hBufW = LocalAlloc(LPTR, (size * sizeof(WCHAR)))) { // // Try to copy to the new wide buffer. // if ((Multi_strcpyAtoW((LPWSTR)hBufW, pSrcA, cChars))) { if (pDestW) { LocalFree((HLOCAL)pDestW); } pDestW = (LPWSTR)hBufW; } else { // // Don't change anything. // LocalFree(hBufW); } } } return (pDestW); } //////////////////////////////////////////////////////////////////////////// // // ThunkANSIStrToWIDE // // Thunks an ANSI string to WIDE. // // Note: If the original wide buffer is too small to contain the new // string, it will be free'd and a new wide buffer will be allocated. // If a new wide buffer can't be allocated, the ptr to the original // wide buffer is returned with no changes to the contents. // // Returns: ptr to the original WIDE buffer // OR ptr to a new wide buffer if original buffer was too small // OR NULL if pSrcA is NULL. // //////////////////////////////////////////////////////////////////////////// LPWSTR ThunkANSIStrToWIDE( LPWSTR pDestW, LPSTR pSrcA, int cChars) { HLOCAL hBufW; int size; if (!pSrcA) { // // The app doesn't want a buffer for this anymore. // if (pDestW) { LocalFree((HLOCAL)pDestW); } return (NULL); } size = max(cChars, (lstrlenA(pSrcA) + 1)); cChars = size; // // If the wide buffer is too small (or NULL or invalid), allocate a // bigger buffer. // if (LocalSize((HLOCAL)pDestW) < (size * sizeof(WCHAR))) { if (hBufW = LocalAlloc(LPTR, (size * sizeof(WCHAR)))) { // // Try to copy to the new wide buffer. // if (SHAnsiToUnicode(pSrcA,(LPWSTR)hBufW,cChars )) { if (pDestW) { LocalFree((HLOCAL)pDestW); } pDestW = (LPWSTR)hBufW; } else { // // Don't change anything. // LocalFree(hBufW); } } } else { // // Just use the original wide buffer. // SHAnsiToUnicode(pSrcA,pDestW, cChars); } return (pDestW); } #ifdef WINNT //////////////////////////////////////////////////////////////////////////// // // Ssync_ANSI_UNICODE_OFN_For_WOW // // Function to allow NT WOW to keep the ANSI & UNICODE versions of // the OPENFILENAME structure in ssync as required by many 16-bit apps. // See notes for Ssync_ANSI_UNICODE_Struct_For_WOW() in dlgs.c. // //////////////////////////////////////////////////////////////////////////// VOID Ssync_ANSI_UNICODE_OFN_For_WOW( HWND hDlg, BOOL f_ANSI_to_UNICODE) { POPENFILEINFO pOFI; if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP)) { if (pOFI->pOFN && pOFI->pOFNA) { if (f_ANSI_to_UNICODE) { ThunkOpenFileNameA2W(pOFI); } else { ThunkOpenFileNameW2A(pOFI); } } } } #endif #endif