Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10527 lines
290 KiB

  1. /*++
  2. Copyright (c) 1990-1998, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. fileopen.c
  5. Abstract:
  6. This module implements the Win32 fileopen dialogs.
  7. Revision History:
  8. --*/
  9. // precompiled headers
  10. #include "precomp.h"
  11. #pragma hdrstop
  12. #include "fileopen.h"
  13. #include "util.h"
  14. //
  15. // Constant Declarations.
  16. //
  17. #define WNTYPE_DRIVE 1
  18. #define MIN_DEFEXT_LEN 4
  19. #define BMPHIOFFSET 9
  20. //
  21. // hbmpDirs array index values.
  22. // Note: Two copies: for standard background, and hilite.
  23. // Relative order is important.
  24. //
  25. #define OPENDIRBMP 0
  26. #define CURDIRBMP 1
  27. #define STDDIRBMP 2
  28. #define FLOPPYBMP 3
  29. #define HARDDRVBMP 4
  30. #define CDDRVBMP 5
  31. #define NETDRVBMP 6
  32. #define RAMDRVBMP 7
  33. #define REMDRVBMP 8
  34. //
  35. // If the following disktype is passed to AddDisk, then bTmp will be
  36. // set to true in the DISKINFO structure (if the disk is new).
  37. //
  38. #define TMPNETDRV 9
  39. #define MAXDOSFILENAMELEN (12 + 1) // 8.3 filename + 1 for NULL
  40. //
  41. // Maximum number of filters on one filter line.
  42. //
  43. #define MAXFILTERS 36
  44. //
  45. // File exclusion bits (don't show files of these types).
  46. //
  47. #define EXCLBITS (FILE_ATTRIBUTE_HIDDEN)
  48. //
  49. // Global Variables.
  50. //
  51. //
  52. // Caching drive list.
  53. //
  54. extern DWORD dwNumDisks;
  55. extern OFN_DISKINFO gaDiskInfo[MAX_DISKS];
  56. extern TCHAR g_szInitialCurDir[MAX_PATH];
  57. DWORD dwNumDlgs = 0;
  58. //
  59. // Used to update the dialogs after coming back from the net dlg button.
  60. //
  61. BOOL bGetNetDrivesSync = FALSE;
  62. LPTSTR lpNetDriveSync = NULL;
  63. BOOL bNetworkInstalled = TRUE;
  64. //
  65. // Following array is used to send messages to all dialog box threads
  66. // that have requested enumeration updating from the worker
  67. // thread. The worker thread sends off a message to each slot
  68. // in the array that is non-NULL.
  69. //
  70. HWND gahDlg[MAX_THREADS];
  71. //
  72. // Strings for Filter Parsing.
  73. //
  74. const static TCHAR szSemiColonSpaceTab[] = TEXT("; \t");
  75. const static TCHAR szSemiColonTab[] = TEXT(";\t");
  76. //
  77. // For WNet apis.
  78. //
  79. HANDLE hLNDThread = NULL;
  80. WNDPROC lpLBProc = NULL;
  81. WNDPROC lpOKProc = NULL;
  82. //
  83. // Drive/Dir bitmap dimensions.
  84. //
  85. LONG dxDirDrive = 0;
  86. LONG dyDirDrive = 0;
  87. //
  88. // REARCHITECT: This needs to be on a per dialog basis for multi-threaded apps.
  89. //
  90. WORD wNoRedraw = 0;
  91. UINT msgWOWDIRCHANGE;
  92. UINT msgLBCHANGEA;
  93. UINT msgSHAREVIOLATIONA;
  94. UINT msgFILEOKA;
  95. UINT msgLBCHANGEW;
  96. UINT msgSHAREVIOLATIONW;
  97. UINT msgFILEOKW;
  98. BOOL bInChildDlg;
  99. BOOL bFirstTime;
  100. BOOL bInitializing;
  101. //
  102. // Used by the worker thread to enumerate network disk resources.
  103. //
  104. extern DWORD cbNetEnumBuf;
  105. extern LPTSTR gpcNetEnumBuf;
  106. //
  107. // List Net Drives global variables.
  108. //
  109. extern HANDLE hLNDEvent;
  110. BOOL bLNDExit = FALSE;
  111. extern CRITICAL_SECTION g_csLocal;
  112. extern CRITICAL_SECTION g_csNetThread;
  113. extern DWORD g_tlsiCurDlg;
  114. extern HDC hdcMemory;
  115. extern HBITMAP hbmpOrigMemBmp;
  116. HBITMAP hbmpDirDrive = HNULL;
  117. //
  118. // Static Declarations.
  119. //
  120. static WORD cLock = 0;
  121. //
  122. // Not valid RGB color.
  123. //
  124. static DWORD rgbWindowColor = 0xFF000000;
  125. static DWORD rgbHiliteColor = 0xFF000000;
  126. static DWORD rgbWindowText = 0xFF000000;
  127. static DWORD rgbHiliteText = 0xFF000000;
  128. static DWORD rgbGrayText = 0xFF000000;
  129. static DWORD rgbDDWindow = 0xFF000000;
  130. static DWORD rgbDDHilite = 0xFF000000;
  131. TCHAR szCaption[TOOLONGLIMIT + WARNINGMSGLENGTH];
  132. TCHAR szWarning[TOOLONGLIMIT + WARNINGMSGLENGTH];
  133. LPOFNHOOKPROC glpfnFileHook = 0;
  134. //
  135. // REARCHITECT:
  136. // Of course, in the case where there is a multi-threaded process
  137. // that has > 1 threads simultaneously calling GetFileOpen, the
  138. // following globals may cause problems.
  139. //
  140. static LONG dyItem = 0;
  141. static LONG dyText;
  142. static BOOL bChangeDir = FALSE;
  143. static BOOL bCasePreserved;
  144. //
  145. // Used for formatting long unc names (ex. banyan).
  146. //
  147. static DWORD dwAveCharPerLine = 10;
  148. //
  149. // Context Help IDs.
  150. //
  151. const static DWORD aFileOpenHelpIDs[] =
  152. {
  153. edt1, IDH_OPEN_FILENAME,
  154. stc3, IDH_OPEN_FILENAME,
  155. lst1, IDH_OPEN_FILENAME,
  156. stc1, IDH_OPEN_PATH,
  157. lst2, IDH_OPEN_PATH,
  158. stc2, IDH_OPEN_FILETYPE,
  159. cmb1, IDH_OPEN_FILETYPE,
  160. stc4, IDH_OPEN_DRIVES,
  161. cmb2, IDH_OPEN_DRIVES,
  162. chx1, IDH_OPEN_READONLY,
  163. pshHelp, IDH_HELP,
  164. psh14, IDH_PRINT_NETWORK,
  165. 0, 0
  166. };
  167. const static DWORD aFileSaveHelpIDs[] =
  168. {
  169. edt1, IDH_OPEN_FILENAME,
  170. stc3, IDH_OPEN_FILENAME,
  171. lst1, IDH_OPEN_FILENAME,
  172. stc1, IDH_OPEN_PATH,
  173. lst2, IDH_OPEN_PATH,
  174. stc2, IDH_SAVE_FILETYPE,
  175. cmb1, IDH_SAVE_FILETYPE,
  176. stc4, IDH_OPEN_DRIVES,
  177. cmb2, IDH_OPEN_DRIVES,
  178. chx1, IDH_OPEN_READONLY,
  179. pshHelp, IDH_HELP,
  180. psh14, IDH_PRINT_NETWORK,
  181. 0, 0
  182. };
  183. //
  184. // Function Prototypes.
  185. //
  186. SHORT
  187. GetFileTitleX(
  188. LPTSTR lpszFile,
  189. LPTSTR lpszTitle,
  190. WORD wBufSize);
  191. BOOL
  192. GetFileName(
  193. POPENFILEINFO pOFI,
  194. DLGPROC qfnDlgProc);
  195. BOOL_PTR CALLBACK
  196. FileOpenDlgProc(
  197. HWND hDlg,
  198. UINT wMsg,
  199. WPARAM wParam,
  200. LPARAM lParam);
  201. BOOL_PTR CALLBACK
  202. FileSaveDlgProc(
  203. HWND hDlg,
  204. UINT wMsg,
  205. WPARAM wParam,
  206. LPARAM lParam);
  207. BOOL_PTR
  208. InitFileDlg(
  209. HWND hDlg,
  210. WPARAM wParam,
  211. POPENFILEINFO pOFI);
  212. int
  213. InitTlsValues(
  214. POPENFILEINFO pOFI);
  215. DWORD
  216. InitFilterBox(
  217. HANDLE hDlg,
  218. LPCTSTR lpszFilter);
  219. VOID
  220. InitCurrentDisk(
  221. HWND hDlg,
  222. POPENFILEINFO pOFI,
  223. WORD cmb);
  224. VOID
  225. vDeleteDirDriveBitmap();
  226. BOOL
  227. LoadDirDriveBitmap();
  228. void
  229. SetRGBValues();
  230. BOOL
  231. FSetUpFile();
  232. BOOL_PTR
  233. FileOpenCmd(
  234. HANDLE hDlg,
  235. WPARAM wParam,
  236. LPARAM lParam,
  237. POPENFILEINFO pOFI,
  238. BOOL bSave);
  239. BOOL
  240. UpdateListBoxes(
  241. HWND hDlg,
  242. POPENFILEINFO pOFI,
  243. LPTSTR lpszFilter,
  244. WORD wMask);
  245. BOOL
  246. OKButtonPressed(
  247. HWND hDlg,
  248. POPENFILEINFO pOFI,
  249. BOOL bSave);
  250. BOOL
  251. MultiSelectOKButton(
  252. HWND hDlg,
  253. POPENFILEINFO pOFI,
  254. BOOL bSave);
  255. LRESULT WINAPI
  256. dwOKSubclass(
  257. HWND hOK,
  258. UINT msg,
  259. WPARAM wParam,
  260. LPARAM lParam);
  261. LRESULT WINAPI
  262. dwLBSubclass(
  263. HWND hLB,
  264. UINT msg,
  265. WPARAM wParam,
  266. LPARAM lParam);
  267. int
  268. InvalidFileWarning(
  269. HWND hDlg,
  270. LPTSTR szFile,
  271. DWORD wErrCode,
  272. UINT mbType);
  273. VOID
  274. MeasureItem(
  275. HWND hDlg,
  276. LPMEASUREITEMSTRUCT mis);
  277. int
  278. Signum(
  279. int nTest);
  280. VOID
  281. DrawItem(
  282. POPENFILEINFO pOFI,
  283. HWND hDlg,
  284. WPARAM wParam,
  285. LPDRAWITEMSTRUCT lpdis,
  286. BOOL bSave);
  287. BOOL
  288. SpacesExist(
  289. LPTSTR szFileName);
  290. void
  291. StripFileName(
  292. HANDLE hDlg,
  293. BOOL bWowApp);
  294. LPTSTR
  295. lstrtok(
  296. LPTSTR lpStr,
  297. LPCTSTR lpDelim);
  298. LPTSTR
  299. ChopText(
  300. HWND hwndDlg,
  301. int idStatic,
  302. LPTSTR lpch);
  303. BOOL
  304. FillOutPath(
  305. HWND hList,
  306. POPENFILEINFO pOFI);
  307. BOOL
  308. ShortenThePath(
  309. LPTSTR pPath);
  310. int
  311. FListAll(
  312. POPENFILEINFO pOFI,
  313. HWND hDlg,
  314. LPTSTR szSpec);
  315. int
  316. ChangeDir(
  317. HWND hDlg,
  318. LPCTSTR lpszDir,
  319. BOOL bForce,
  320. BOOL bError);
  321. BOOL
  322. IsFileSystemCasePreserving(
  323. LPTSTR lpszDisk);
  324. BOOL
  325. IsLFNDriveX(
  326. HWND hDlg,
  327. LPTSTR szPath);
  328. int
  329. DiskAddedPreviously(
  330. TCHAR wcDrive,
  331. LPTSTR lpszName);
  332. int
  333. AddDisk(
  334. TCHAR wcDrive,
  335. LPTSTR lpName,
  336. LPTSTR lpProvider,
  337. DWORD dwType);
  338. VOID
  339. EnableDiskInfo(
  340. BOOL bValid,
  341. BOOL bDoUnc);
  342. VOID
  343. FlushDiskInfoToCmb2();
  344. BOOL
  345. CallNetDlg(
  346. HWND hWnd);
  347. UINT
  348. GetDiskType(
  349. LPTSTR lpszDisk);
  350. DWORD
  351. GetUNCDirectoryFromLB(
  352. HWND hDlg,
  353. WORD nLB,
  354. POPENFILEINFO pOFI);
  355. VOID
  356. SelDisk(
  357. HWND hDlg,
  358. LPTSTR lpszDisk);
  359. VOID
  360. LNDSetEvent(
  361. HWND hDlg);
  362. VOID
  363. UpdateLocalDrive(
  364. LPTSTR szDrive,
  365. BOOL bGetVolName);
  366. VOID
  367. GetNetDrives(
  368. DWORD dwScope);
  369. VOID
  370. ListNetDrivesHandler();
  371. VOID
  372. LoadDrives(
  373. HWND hDlg);
  374. DWORD
  375. GetDiskIndex(
  376. DWORD dwDriveType);
  377. VOID
  378. CleanUpFile();
  379. VOID
  380. FileOpenAbort();
  381. VOID
  382. TermFile();
  383. #ifdef UNICODE
  384. //VOID // prototype in fileopen.h
  385. //ThunkOpenFileNameA2WDelayed(
  386. // POPENFILEINFO pOFI);
  387. //BOOL // prototype in fileopen.h
  388. //ThunkOpenFileNameA2W(
  389. // POPENFILEINFO pOFI);
  390. //BOOL // prototype in fileopen.h
  391. //ThunkOpenFileNameW2A(
  392. // POPENFILEINFO pOFI);
  393. BOOL
  394. GenericGetFileNameA(
  395. LPOPENFILENAMEA pOFNA,
  396. DLGPROC qfnDlgProc);
  397. LPWSTR
  398. ThunkANSIStrToWIDE(
  399. LPWSTR pDestW,
  400. LPSTR pSrcA,
  401. int cChars);
  402. LPWSTR
  403. ThunkMultiANSIStrToWIDE(
  404. LPWSTR pDestW,
  405. LPSTR pSrcA,
  406. int cChars);
  407. BOOL
  408. Multi_strcpyAtoW(
  409. LPWSTR pDestW,
  410. LPCSTR pSrcA,
  411. int cChars);
  412. INT
  413. Multi_strlenA(
  414. LPCSTR str);
  415. #endif
  416. // The Win9x code relies on calling SetCurrentDirectory wherever SheChangeDirEx is
  417. // called. (Ideally SheChangeDirExA should be implemented).
  418. // Ref: NT5 bug 161292 and Millenium bug 95478
  419. #ifndef UNICODE
  420. #undef SheChangeDirEx
  421. #define SheChangeDirEx SetCurrentDirectory
  422. #endif
  423. #ifdef UNICODE
  424. ////////////////////////////////////////////////////////////////////////////
  425. //
  426. // GetFileTitleA
  427. //
  428. // ANSI entry point for GetFileTitle when this code is built UNICODE.
  429. //
  430. ////////////////////////////////////////////////////////////////////////////
  431. SHORT WINAPI GetFileTitleA(
  432. LPCSTR lpszFileA,
  433. LPSTR lpszTitleA,
  434. WORD cbBuf)
  435. {
  436. LPWSTR lpszFileW;
  437. LPWSTR lpszTitleW;
  438. BOOL fResult;
  439. DWORD cbLen;
  440. //
  441. // Init File string.
  442. //
  443. if (lpszFileA)
  444. {
  445. cbLen = lstrlenA(lpszFileA) + 1;
  446. if (!(lpszFileW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
  447. {
  448. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  449. return (FALSE);
  450. }
  451. else
  452. {
  453. SHAnsiToUnicode((LPSTR)lpszFileA,lpszFileW,cbLen );
  454. }
  455. }
  456. else
  457. {
  458. lpszFileW = NULL;
  459. }
  460. if (!(lpszTitleW = (LPWSTR)LocalAlloc(LPTR, (cbBuf * sizeof(WCHAR)))))
  461. {
  462. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  463. if (lpszFileW)
  464. {
  465. LocalFree(lpszFileW);
  466. }
  467. return (FALSE);
  468. }
  469. if (!(fResult = GetFileTitleW(lpszFileW, lpszTitleW, cbBuf)))
  470. {
  471. SHUnicodeToAnsi(lpszTitleW,lpszTitleA,cbBuf);
  472. }
  473. else if (fResult > 0)
  474. {
  475. //
  476. // Buffer is too small - Ansi size needed (including null terminator).
  477. // Get the offset to the filename.
  478. //
  479. SHORT nNeeded = (SHORT)(INT)LOWORD(ParseFile(lpszFileW, TRUE, FALSE, FALSE));
  480. LPSTR lpA = (LPSTR)lpszFileA;
  481. lpA += WideCharToMultiByte( CP_ACP,
  482. 0,
  483. lpszFileW,
  484. nNeeded,
  485. NULL,
  486. 0,
  487. NULL,
  488. NULL );
  489. fResult = lstrlenA(lpA) + 1;
  490. if (fResult <= cbBuf)
  491. {
  492. lstrcpyA(lpszTitleA, lpA);
  493. fResult = 0;
  494. }
  495. }
  496. //
  497. // Clean up memory.
  498. //
  499. LocalFree(lpszTitleW);
  500. if (lpszFileW)
  501. {
  502. LocalFree(lpszFileW);
  503. }
  504. return ((SHORT)fResult);
  505. }
  506. #else
  507. ////////////////////////////////////////////////////////////////////////////
  508. //
  509. // GetFileTitleW
  510. //
  511. // Stub UNICODE function for GetFileTitle when this code is built ANSI.
  512. //
  513. ////////////////////////////////////////////////////////////////////////////
  514. SHORT WINAPI GetFileTitleW(
  515. LPCWSTR lpszFileW,
  516. LPWSTR lpszTitleW,
  517. WORD cbBuf)
  518. {
  519. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  520. return (FALSE);
  521. }
  522. #endif
  523. ////////////////////////////////////////////////////////////////////////////
  524. //
  525. // GetFileTitle
  526. //
  527. // The GetFileTitle function returns the name of the file identified
  528. // by the lpCFile parameter. This is useful if the file name was
  529. // received via some method other than GetOpenFileName
  530. // (e.g. command line, drag drop).
  531. //
  532. // Returns: 0 on success
  533. // < 0, Parsing failure (invalid file name)
  534. // > 0, buffer too small, size needed (including NULL terminator)
  535. //
  536. ////////////////////////////////////////////////////////////////////////////
  537. SHORT WINAPI GetFileTitle(
  538. LPCTSTR lpCFile,
  539. LPTSTR lpTitle,
  540. WORD cbBuf)
  541. {
  542. LPTSTR lpFile;
  543. DWORD cbLen;
  544. SHORT fResult;
  545. //
  546. // Init File string.
  547. //
  548. if (lpCFile)
  549. {
  550. cbLen = lstrlen(lpCFile) + 1;
  551. if (!(lpFile = (LPTSTR)LocalAlloc(LPTR, (cbLen * sizeof(TCHAR)))))
  552. {
  553. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  554. return (FALSE);
  555. }
  556. else
  557. {
  558. lstrcpy(lpFile, lpCFile);
  559. }
  560. }
  561. else
  562. {
  563. lpFile = NULL;
  564. }
  565. fResult = GetFileTitleX(lpFile, lpTitle, cbBuf);
  566. //
  567. // Clean up memory.
  568. //
  569. if (lpFile)
  570. {
  571. LocalFree(lpFile);
  572. }
  573. return (fResult);
  574. }
  575. ////////////////////////////////////////////////////////////////////////////
  576. //
  577. // GetFileTitleX
  578. //
  579. // Worker routine for the GetFileTitle api.
  580. //
  581. // Assumes: lpszFile points to NULL terminated DOS filename (may have path)
  582. // lpszTitle points to buffer to receive NULL terminated file title
  583. // wBufSize is the size of buffer pointed to by lpszTitle
  584. //
  585. // Returns: 0 on success
  586. // < 0, Parsing failure (invalid file name)
  587. // > 0, buffer too small, size needed (including NULL terminator)
  588. //
  589. ////////////////////////////////////////////////////////////////////////////
  590. SHORT GetFileTitleX(
  591. LPTSTR lpszFile,
  592. LPTSTR lpszTitle,
  593. WORD wBufSize)
  594. {
  595. SHORT nNeeded;
  596. LPTSTR lpszPtr;
  597. //
  598. // New 32 bit apps will get a title based on the user's preferences.
  599. //
  600. if ((GetProcessVersion(0) >= 0x040000) && !(CDGetAppCompatFlags() & CDACF_FILETITLE))
  601. {
  602. SHFILEINFO info;
  603. DWORD_PTR result;
  604. if (!lpszFile || !*lpszFile)
  605. {
  606. return (PARSE_EMPTYSTRING);
  607. }
  608. //
  609. // If we have a root directory name (eg. c:\), then we need to go
  610. // to the old implementation so that it will return -1.
  611. // SHGetFileInfo will return the display name for the directory
  612. // (which is the volume name). This is incompatible with Win95
  613. // and previous versions of NT.
  614. //
  615. if ((lstrlen(lpszFile) != 3) ||
  616. (lpszFile[1] != CHAR_COLON) || (!ISBACKSLASH(lpszFile, 2)))
  617. {
  618. result = SHGetFileInfo( lpszFile,
  619. FILE_ATTRIBUTE_NORMAL,
  620. &info,
  621. sizeof(info),
  622. SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES );
  623. if (result && (*info.szDisplayName))
  624. {
  625. UINT uDisplayLen = lstrlen(info.szDisplayName);
  626. //
  627. // If no buffer or insufficient size, return the required chars.
  628. // Original GetFileTitle API did not copy on failure.
  629. //
  630. if (!lpszTitle || (uDisplayLen >= (UINT)wBufSize))
  631. {
  632. return ( (SHORT)(uDisplayLen + 1) );
  633. }
  634. //
  635. // We already know it fits, so we don't need lstrcpyn.
  636. //
  637. lstrcpy(lpszTitle, info.szDisplayName);
  638. return (0);
  639. }
  640. }
  641. }
  642. //
  643. // Use the old implementation.
  644. //
  645. nNeeded = (SHORT)(int)LOWORD(ParseFile(lpszFile, TRUE, FALSE, FALSE));
  646. if (nNeeded >= 0)
  647. {
  648. //
  649. // Is the filename valid?
  650. //
  651. lpszPtr = lpszFile + nNeeded;
  652. if ((nNeeded = (SHORT)lstrlen(lpszPtr) + 1) <= (int)wBufSize)
  653. {
  654. //
  655. // ParseFile() fails if wildcards in directory, but OK if in name.
  656. // Since they arent OK here, the check is needed here.
  657. //
  658. if (StrChr(lpszPtr, CHAR_STAR) || StrChr(lpszPtr, CHAR_QMARK))
  659. {
  660. nNeeded = PARSE_WILDCARDINFILE;
  661. }
  662. else
  663. {
  664. lstrcpy(lpszTitle, lpszPtr);
  665. //
  666. // Remove trailing spaces.
  667. //
  668. lpszPtr = lpszTitle + lstrlen(lpszTitle) - 1;
  669. while (*lpszPtr && *lpszPtr == CHAR_SPACE)
  670. {
  671. *lpszPtr-- = CHAR_NULL;
  672. }
  673. nNeeded = 0;
  674. }
  675. }
  676. }
  677. return (nNeeded);
  678. }
  679. #ifdef UNICODE
  680. ////////////////////////////////////////////////////////////////////////////
  681. //
  682. // GetOpenFileNameA
  683. //
  684. // ANSI entry point for GetOpenFileName when this code is built UNICODE.
  685. //
  686. ////////////////////////////////////////////////////////////////////////////
  687. BOOL WINAPI GetOpenFileNameA(
  688. LPOPENFILENAMEA pOFNA)
  689. {
  690. if (!pOFNA)
  691. {
  692. StoreExtendedError(CDERR_INITIALIZATION);
  693. return (FALSE);
  694. }
  695. return ( GenericGetFileNameA(pOFNA, FileOpenDlgProc) );
  696. }
  697. #else
  698. ////////////////////////////////////////////////////////////////////////////
  699. //
  700. // GetOpenFileNameW
  701. //
  702. // Stub UNICODE function for GetOpenFileName when this code is built ANSI.
  703. //
  704. ////////////////////////////////////////////////////////////////////////////
  705. BOOL WINAPI GetOpenFileNameW(
  706. LPOPENFILENAMEW pOFNW)
  707. {
  708. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  709. return (FALSE);
  710. }
  711. #endif
  712. ////////////////////////////////////////////////////////////////////////////
  713. //
  714. // GetOpenFileName
  715. //
  716. // The GetOpenFileName function creates a system-defined dialog box
  717. // that enables the user to select a file to open.
  718. //
  719. // Returns: TRUE if user specified name
  720. // FALSE if not
  721. //
  722. ////////////////////////////////////////////////////////////////////////////
  723. BOOL WINAPI GetOpenFileName(
  724. LPOPENFILENAME pOFN)
  725. {
  726. OPENFILEINFO OFI;
  727. ZeroMemory(&OFI, sizeof(OPENFILEINFO));
  728. if (!pOFN)
  729. {
  730. StoreExtendedError(CDERR_INITIALIZATION);
  731. return (FALSE);
  732. }
  733. OFI.pOFN = pOFN;
  734. OFI.ApiType = COMDLG_WIDE;
  735. OFI.iVersion = OPENFILEVERSION;
  736. return (GetFileName(&OFI, FileOpenDlgProc));
  737. }
  738. #ifdef UNICODE
  739. ////////////////////////////////////////////////////////////////////////////
  740. //
  741. // GetSaveFileNameA
  742. //
  743. // ANSI entry point for GetSaveFileName when this code is built UNICODE.
  744. //
  745. ////////////////////////////////////////////////////////////////////////////
  746. BOOL WINAPI GetSaveFileNameA(
  747. LPOPENFILENAMEA pOFNA)
  748. {
  749. return (GenericGetFileNameA(pOFNA, FileSaveDlgProc));
  750. }
  751. #else
  752. ////////////////////////////////////////////////////////////////////////////
  753. //
  754. // GetSaveFileNameW
  755. //
  756. // Stub UNICODE function for GetSaveFileName when this code is built ANSI.
  757. //
  758. ////////////////////////////////////////////////////////////////////////////
  759. BOOL WINAPI GetSaveFileNameW(
  760. LPOPENFILENAMEW pOFNW)
  761. {
  762. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  763. return (FALSE);
  764. }
  765. #endif
  766. ////////////////////////////////////////////////////////////////////////////
  767. //
  768. // GetSaveFileName
  769. //
  770. // The GetSaveFileName function creates a system-defined dialog box
  771. // that enables the user to select a file to save.
  772. //
  773. // Returns: TRUE if user desires to save file and gave a proper name
  774. // FALSE if not
  775. //
  776. ////////////////////////////////////////////////////////////////////////////
  777. BOOL WINAPI GetSaveFileName(
  778. LPOPENFILENAME pOFN)
  779. {
  780. OPENFILEINFO OFI;
  781. ZeroMemory(&OFI, sizeof(OPENFILEINFO));
  782. OFI.pOFN = pOFN;
  783. OFI.ApiType = COMDLG_WIDE;
  784. OFI.iVersion = OPENFILEVERSION;
  785. return ( GetFileName(&OFI, FileSaveDlgProc) );
  786. }
  787. ////////////////////////////////////////////////////////////////////////////
  788. //
  789. // GetFileName
  790. //
  791. // This is the meat of both GetOpenFileName and GetSaveFileName.
  792. //
  793. // Returns: TRUE if user specified name
  794. // FALSE if not
  795. //
  796. ////////////////////////////////////////////////////////////////////////////
  797. BOOL GetFileName(
  798. POPENFILEINFO pOFI,
  799. DLGPROC qfnDlgProc)
  800. {
  801. LPOPENFILENAME pOFN = pOFI->pOFN;
  802. INT_PTR iRet = 0;
  803. LPTSTR lpDlg;
  804. HANDLE hRes, hDlgTemplate;
  805. WORD wErrorMode;
  806. HDC hdcScreen;
  807. HBITMAP hbmpTemp;
  808. LPCURDLG lpCurDlg;
  809. static fFirstTime = TRUE;
  810. #ifdef UNICODE
  811. UINT uiWOWFlag = 0;
  812. #endif
  813. LANGID LangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  814. if (!pOFN)
  815. {
  816. StoreExtendedError(CDERR_INITIALIZATION);
  817. return (FALSE);
  818. }
  819. if (pOFN->lStructSize == OPENFILENAME_SIZE_VERSION_400)
  820. {
  821. // Note: We do not want to make a copy of the OFN structure passed in.
  822. // This confuses all MFC based apps since they query
  823. // MFC and end up getting stale data if we make a copy and endup updating only the
  824. // copy until Comdlg api returns.
  825. pOFI->iVersion = OPENFILEVERSION_NT4;
  826. }
  827. if ((pOFN->lStructSize != sizeof(OPENFILENAME)) &&
  828. (pOFN->lStructSize != OPENFILENAME_SIZE_VERSION_400)
  829. )
  830. {
  831. StoreExtendedError(CDERR_STRUCTSIZE);
  832. return (FALSE);
  833. }
  834. if (pOFN->nMaxFile == 0)
  835. {
  836. //Bail out for NULL lpstrFile Only for NT5 and above applications
  837. if (!IS16BITWOWAPP(pOFN) && (pOFI->iVersion >= OPENFILEVERSION_NT5))
  838. {
  839. StoreExtendedError(CDERR_INITIALIZATION);
  840. return (FALSE);
  841. }
  842. }
  843. //
  844. // See if the application should get the new look.
  845. //
  846. // Do not allow the new look if they have hooks, templates, or
  847. // multi select without the OFN_EXPLORER bit.
  848. //
  849. // Also don't allow the new look if we are in the context of
  850. // a 16 bit process.
  851. //
  852. if ( ((pOFN->Flags & OFN_EXPLORER) ||
  853. (!(pOFN->Flags & (OFN_ENABLEHOOK |
  854. OFN_ENABLETEMPLATE |
  855. OFN_ENABLETEMPLATEHANDLE |
  856. OFN_ALLOWMULTISELECT)))) &&
  857. (!IS16BITWOWAPP(pOFN)) )
  858. {
  859. BOOL fRet;
  860. #ifdef UNICODE
  861. //
  862. // To be used by the thunking routines for multi selection.
  863. //
  864. pOFI->bUseNewDialog = TRUE;
  865. #endif
  866. //
  867. // Show the new explorer look.
  868. //
  869. StoreExtendedError(0);
  870. g_bUserPressedCancel = FALSE;
  871. if (qfnDlgProc == FileOpenDlgProc)
  872. {
  873. fRet = (NewGetOpenFileName(pOFI));
  874. }
  875. else
  876. {
  877. fRet = (NewGetSaveFileName(pOFI));
  878. }
  879. return fRet;
  880. }
  881. if (fFirstTime)
  882. {
  883. //
  884. // Create a DC that is compatible with the screen and find the
  885. // handle of the null bitmap.
  886. //
  887. hdcScreen = GetDC(HNULL);
  888. if (!hdcScreen)
  889. {
  890. goto CantInit;
  891. }
  892. hdcMemory = CreateCompatibleDC(hdcScreen);
  893. if (!hdcMemory)
  894. {
  895. goto ReleaseScreenDC;
  896. }
  897. hbmpTemp = CreateCompatibleBitmap(hdcMemory, 1, 1);
  898. if (!hbmpTemp)
  899. {
  900. goto ReleaseMemDC;
  901. }
  902. hbmpOrigMemBmp = SelectObject(hdcMemory, hbmpTemp);
  903. if (!hbmpOrigMemBmp)
  904. {
  905. goto ReleaseMemDC;
  906. }
  907. SelectObject(hdcMemory, hbmpOrigMemBmp);
  908. DeleteObject(hbmpTemp);
  909. ReleaseDC(HNULL, hdcScreen);
  910. fFirstTime = FALSE;
  911. }
  912. if (pOFN->Flags & OFN_ENABLEHOOK)
  913. {
  914. if (!pOFN->lpfnHook)
  915. {
  916. StoreExtendedError(CDERR_NOHOOK);
  917. return (FALSE);
  918. }
  919. }
  920. else
  921. {
  922. pOFN->lpfnHook = NULL;
  923. }
  924. HourGlass(TRUE);
  925. StoreExtendedError(0);
  926. //
  927. // Force re-compute for font changes between calls.
  928. //
  929. dyItem = dyText = 0;
  930. g_bUserPressedCancel = FALSE;
  931. if (!FSetUpFile())
  932. {
  933. StoreExtendedError(CDERR_INITIALIZATION);
  934. goto TERMINATE;
  935. }
  936. if (pOFN->Flags & OFN_ENABLETEMPLATE)
  937. {
  938. if (!(hRes = FindResource( pOFN->hInstance,
  939. pOFN->lpTemplateName,
  940. RT_DIALOG )))
  941. {
  942. StoreExtendedError(CDERR_FINDRESFAILURE);
  943. goto TERMINATE;
  944. }
  945. if (!(hDlgTemplate = LoadResource(pOFN->hInstance, hRes)))
  946. {
  947. StoreExtendedError(CDERR_LOADRESFAILURE);
  948. goto TERMINATE;
  949. }
  950. LangID = GetDialogLanguage(pOFN->hwndOwner, hDlgTemplate);
  951. }
  952. else if (pOFN->Flags & OFN_ENABLETEMPLATEHANDLE)
  953. {
  954. hDlgTemplate = pOFN->hInstance;
  955. LangID = GetDialogLanguage(pOFN->hwndOwner, hDlgTemplate);
  956. }
  957. else
  958. {
  959. if (pOFN->Flags & OFN_ALLOWMULTISELECT)
  960. {
  961. lpDlg = MAKEINTRESOURCE(MULTIFILEOPENORD);
  962. }
  963. else
  964. {
  965. lpDlg = MAKEINTRESOURCE(FILEOPENORD);
  966. }
  967. LangID = GetDialogLanguage(pOFN->hwndOwner, NULL);
  968. if (!(hRes = FindResourceExFallback(g_hinst, RT_DIALOG, lpDlg, LangID)))
  969. {
  970. StoreExtendedError(CDERR_FINDRESFAILURE);
  971. goto TERMINATE;
  972. }
  973. if (!(hDlgTemplate = LoadResource(g_hinst, hRes)))
  974. {
  975. StoreExtendedError(CDERR_LOADRESFAILURE);
  976. goto TERMINATE;
  977. }
  978. }
  979. //
  980. // Warning! Warning! Warning!
  981. //
  982. // We have to set g_tlsLangID before any call for CDLoadString
  983. //
  984. TlsSetValue(g_tlsLangID, (LPVOID) LangID);
  985. //
  986. // No kernel network error dialogs.
  987. //
  988. wErrorMode = (WORD)SetErrorMode(SEM_NOERROR);
  989. SetErrorMode(SEM_NOERROR | wErrorMode);
  990. if (LockResource(hDlgTemplate))
  991. {
  992. if (pOFN->Flags & OFN_ENABLEHOOK)
  993. {
  994. glpfnFileHook = GETHOOKFN(pOFN);
  995. }
  996. #ifdef UNICODE
  997. if (IS16BITWOWAPP(pOFN))
  998. {
  999. uiWOWFlag = SCDLG_16BIT;
  1000. }
  1001. iRet = DialogBoxIndirectParamAorW( g_hinst,
  1002. (LPDLGTEMPLATE)hDlgTemplate,
  1003. pOFN->hwndOwner,
  1004. qfnDlgProc,
  1005. (DWORD_PTR)pOFI,
  1006. uiWOWFlag );
  1007. #else
  1008. iRet = DialogBoxIndirectParam( g_hinst,
  1009. (LPDLGTEMPLATE)hDlgTemplate,
  1010. pOFN->hwndOwner,
  1011. qfnDlgProc,
  1012. (DWORD)pOFI );
  1013. #endif
  1014. if (iRet == -1 || ((iRet == 0) && (!g_bUserPressedCancel) && (!GetStoredExtendedError())))
  1015. {
  1016. StoreExtendedError(CDERR_DIALOGFAILURE);
  1017. }
  1018. else
  1019. {
  1020. FileOpenAbort();
  1021. }
  1022. glpfnFileHook = 0;
  1023. }
  1024. else
  1025. {
  1026. StoreExtendedError(CDERR_LOCKRESFAILURE);
  1027. goto TERMINATE;
  1028. }
  1029. SetErrorMode(wErrorMode);
  1030. if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
  1031. {
  1032. // restore the thread list to the previous dialog (if any)
  1033. TlsSetValue(g_tlsiCurDlg, (LPVOID)lpCurDlg->next);
  1034. LocalFree(lpCurDlg->lpstrCurDir);
  1035. LocalFree(lpCurDlg);
  1036. }
  1037. TERMINATE:
  1038. CleanUpFile();
  1039. HourGlass(FALSE);
  1040. return (iRet == IDOK);
  1041. ReleaseMemDC:
  1042. DeleteDC(hdcMemory);
  1043. ReleaseScreenDC:
  1044. ReleaseDC(HNULL, hdcScreen);
  1045. CantInit:
  1046. return (FALSE);
  1047. }
  1048. ////////////////////////////////////////////////////////////////////////////
  1049. //
  1050. // FileHookCmd
  1051. //
  1052. // Called when a hook function processes a WM_COMMAND message.
  1053. // Called by FileOpenDlgProc and FileSaveDlgProc.
  1054. //
  1055. ////////////////////////////////////////////////////////////////////////////
  1056. BOOL FileHookCmd(
  1057. HANDLE hDlg,
  1058. WPARAM wParam,
  1059. LPARAM lParam,
  1060. POPENFILEINFO pOFI)
  1061. {
  1062. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1063. {
  1064. case ( IDCANCEL ) :
  1065. {
  1066. //
  1067. // Set global flag stating that the
  1068. // user pressed cancel.
  1069. //
  1070. g_bUserPressedCancel = TRUE;
  1071. // Fall Thru...
  1072. }
  1073. case ( IDOK ) :
  1074. case ( IDABORT ) :
  1075. {
  1076. #ifdef UNICODE
  1077. //
  1078. // Apps that side-effect these messages may
  1079. // not have their internal unicode strings
  1080. // updated. They may also forget to gracefully
  1081. // exit the network enum'ing worker thread.
  1082. //
  1083. if (pOFI->ApiType == COMDLG_ANSI)
  1084. {
  1085. ThunkOpenFileNameA2W(pOFI);
  1086. }
  1087. #endif
  1088. break;
  1089. }
  1090. case ( cmb1 ) :
  1091. case ( cmb2 ) :
  1092. {
  1093. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  1094. {
  1095. case ( MYCBN_DRAW ) :
  1096. case ( MYCBN_LIST ) :
  1097. case ( MYCBN_REPAINT ) :
  1098. case ( MYCBN_CHANGEDIR ) :
  1099. {
  1100. //
  1101. // In case an app has a hook, and returns
  1102. // true for processing WM_COMMAND messages,
  1103. // we still have to worry about our
  1104. // internal message that came through via
  1105. // WM_COMMAND.
  1106. //
  1107. FileOpenCmd( hDlg,
  1108. wParam,
  1109. lParam,
  1110. pOFI,
  1111. FALSE );
  1112. break;
  1113. }
  1114. }
  1115. break;
  1116. }
  1117. }
  1118. return (TRUE);
  1119. }
  1120. ////////////////////////////////////////////////////////////////////////////
  1121. //
  1122. // FileOpenDlgProc
  1123. //
  1124. // Gets the name of a file to open from the user.
  1125. //
  1126. // edt1 = file name
  1127. // lst1 = list of files in current directory matching current pattern
  1128. // cmb1 = lists file patterns
  1129. // stc1 = is current directory
  1130. // lst2 = lists directories on current drive
  1131. // cmb2 = lists drives
  1132. // IDOK = is Open pushbutton
  1133. // IDCANCEL = is Cancel pushbutton
  1134. // chx1 = is for opening read only files
  1135. //
  1136. // Returns the normal dialog proc values.
  1137. //
  1138. ////////////////////////////////////////////////////////////////////////////
  1139. BOOL_PTR CALLBACK FileOpenDlgProc(
  1140. HWND hDlg,
  1141. UINT wMsg,
  1142. WPARAM wParam,
  1143. LPARAM lParam)
  1144. {
  1145. POPENFILEINFO pOFI;
  1146. BOOL_PTR bRet, bHookRet;
  1147. if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
  1148. {
  1149. if (pOFI->pOFN->lpfnHook)
  1150. {
  1151. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFI->pOFN);
  1152. bHookRet = (*lpfnHook)(hDlg, wMsg, wParam, lParam);
  1153. if (bHookRet)
  1154. {
  1155. if (wMsg == WM_COMMAND)
  1156. {
  1157. return (FileHookCmd(hDlg, wParam, lParam, pOFI));
  1158. }
  1159. return (bHookRet);
  1160. }
  1161. }
  1162. }
  1163. else if (glpfnFileHook &&
  1164. (wMsg != WM_INITDIALOG) &&
  1165. (bHookRet = (*glpfnFileHook)(hDlg, wMsg, wParam, lParam)))
  1166. {
  1167. return (bHookRet);
  1168. }
  1169. switch (wMsg)
  1170. {
  1171. case ( WM_INITDIALOG ) :
  1172. {
  1173. pOFI = (POPENFILEINFO)lParam;
  1174. SetProp(hDlg, FILEPROP, (HANDLE)pOFI);
  1175. glpfnFileHook = 0;
  1176. //
  1177. // If we are being called from a Unicode app, turn off
  1178. // the ES_OEMCONVERT style on the filename edit control.
  1179. //
  1180. // if (pOFI->ApiType == COMDLG_WIDE)
  1181. {
  1182. LONG lStyle;
  1183. HWND hEdit = GetDlgItem(hDlg, edt1);
  1184. //
  1185. // Grab the window style.
  1186. //
  1187. lStyle = GetWindowLong(hEdit, GWL_STYLE);
  1188. //
  1189. // If the window style bits include ES_OEMCONVERT,
  1190. // remove this flag and reset the style.
  1191. //
  1192. if (lStyle & ES_OEMCONVERT)
  1193. {
  1194. lStyle &= ~ES_OEMCONVERT;
  1195. SetWindowLong(hEdit, GWL_STYLE, lStyle);
  1196. }
  1197. }
  1198. bInitializing = TRUE;
  1199. bRet = InitFileDlg(hDlg, wParam, pOFI);
  1200. bInitializing = FALSE;
  1201. HourGlass(FALSE);
  1202. return (bRet);
  1203. break;
  1204. }
  1205. case ( WM_ACTIVATE ) :
  1206. {
  1207. if (!bInChildDlg)
  1208. {
  1209. if (bFirstTime == TRUE)
  1210. {
  1211. bFirstTime = FALSE;
  1212. }
  1213. else if (wParam)
  1214. {
  1215. //
  1216. // If becoming active.
  1217. //
  1218. LNDSetEvent(hDlg);
  1219. }
  1220. }
  1221. return (FALSE);
  1222. break;
  1223. }
  1224. case ( WM_MEASUREITEM ) :
  1225. {
  1226. MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
  1227. break;
  1228. }
  1229. case ( WM_DRAWITEM ) :
  1230. {
  1231. if (wNoRedraw < 2)
  1232. {
  1233. DrawItem(pOFI, hDlg, wParam, (LPDRAWITEMSTRUCT)lParam, FALSE);
  1234. }
  1235. break;
  1236. }
  1237. case ( WM_SYSCOLORCHANGE ) :
  1238. {
  1239. SetRGBValues();
  1240. LoadDirDriveBitmap();
  1241. break;
  1242. }
  1243. case ( WM_COMMAND ) :
  1244. {
  1245. return (FileOpenCmd(hDlg, wParam, lParam, pOFI, FALSE));
  1246. break;
  1247. }
  1248. case ( WM_SETFOCUS ) :
  1249. {
  1250. //
  1251. // This logic used to be in CBN_SETFOCUS in fileopencmd,
  1252. // but CBN_SETFOCUS is called whenever there is a click on
  1253. // the List Drives combo. This causes the worker thread
  1254. // to start up and flicker when the combo box is refreshed.
  1255. //
  1256. // But, refreshes are only needed when someone focuses out of
  1257. // the common dialog and then back in (unless someone is logged
  1258. // in remote, or there is a background thread busy connecting!)
  1259. // so fix the flicker by moving the logic here.
  1260. //
  1261. if (!wNoRedraw)
  1262. {
  1263. LNDSetEvent(hDlg);
  1264. }
  1265. return (FALSE);
  1266. break;
  1267. }
  1268. case ( WM_HELP ) :
  1269. {
  1270. if (IsWindowEnabled(hDlg))
  1271. {
  1272. WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
  1273. NULL,
  1274. HELP_WM_HELP,
  1275. (ULONG_PTR)(LPTSTR)aFileOpenHelpIDs );
  1276. }
  1277. break;
  1278. }
  1279. case ( WM_CONTEXTMENU ) :
  1280. {
  1281. if (IsWindowEnabled(hDlg))
  1282. {
  1283. WinHelp( (HWND)wParam,
  1284. NULL,
  1285. HELP_CONTEXTMENU,
  1286. (ULONG_PTR)(LPVOID)aFileOpenHelpIDs );
  1287. }
  1288. break;
  1289. }
  1290. default :
  1291. {
  1292. return (FALSE);
  1293. }
  1294. }
  1295. return (TRUE);
  1296. }
  1297. ////////////////////////////////////////////////////////////////////////////
  1298. //
  1299. // FileSaveDlgProc
  1300. //
  1301. // Obtains the name of the file that the user wants to save.
  1302. //
  1303. // Returns the normal dialog proc values.
  1304. //
  1305. ////////////////////////////////////////////////////////////////////////////
  1306. BOOL_PTR CALLBACK FileSaveDlgProc(
  1307. HWND hDlg,
  1308. UINT wMsg,
  1309. WPARAM wParam,
  1310. LPARAM lParam)
  1311. {
  1312. POPENFILEINFO pOFI;
  1313. BOOL_PTR bRet, bHookRet;
  1314. TCHAR szTitle[cbCaption];
  1315. if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
  1316. {
  1317. if (pOFI->pOFN->lpfnHook)
  1318. {
  1319. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFI->pOFN);
  1320. bHookRet = (*lpfnHook)(hDlg, wMsg, wParam, lParam);
  1321. if (bHookRet)
  1322. {
  1323. if (wMsg == WM_COMMAND)
  1324. {
  1325. return (FileHookCmd(hDlg, wParam, lParam, pOFI));
  1326. }
  1327. return (bHookRet);
  1328. }
  1329. }
  1330. }
  1331. else if (glpfnFileHook &&
  1332. (wMsg != WM_INITDIALOG) &&
  1333. (bHookRet = (*glpfnFileHook)(hDlg, wMsg, wParam, lParam)))
  1334. {
  1335. return (bHookRet);
  1336. }
  1337. switch (wMsg)
  1338. {
  1339. case ( WM_INITDIALOG ) :
  1340. {
  1341. pOFI = (POPENFILEINFO)lParam;
  1342. if (!(pOFI->pOFN->Flags &
  1343. (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
  1344. {
  1345. CDLoadString(g_hinst, iszFileSaveTitle, szTitle, cbCaption);
  1346. SetWindowText(hDlg, szTitle);
  1347. CDLoadString(g_hinst, iszSaveFileAsType, szTitle, cbCaption);
  1348. SetDlgItemText(hDlg, stc2, szTitle);
  1349. }
  1350. glpfnFileHook = 0;
  1351. SetProp(hDlg, FILEPROP, (HANDLE)pOFI);
  1352. //
  1353. // If we are being called from a Unicode app, turn off
  1354. // the ES_OEMCONVERT style on the filename edit control.
  1355. //
  1356. // if (pOFI->ApiType == COMDLG_WIDE)
  1357. {
  1358. LONG lStyle;
  1359. HWND hEdit = GetDlgItem(hDlg, edt1);
  1360. //
  1361. // Grab the window style.
  1362. //
  1363. lStyle = GetWindowLong(hEdit, GWL_STYLE);
  1364. //
  1365. // If the window style bits include ES_OEMCONVERT,
  1366. // remove this flag and reset the style.
  1367. //
  1368. if (lStyle & ES_OEMCONVERT)
  1369. {
  1370. lStyle &= ~ES_OEMCONVERT;
  1371. SetWindowLong (hEdit, GWL_STYLE, lStyle);
  1372. }
  1373. }
  1374. bInitializing = TRUE;
  1375. bRet = InitFileDlg(hDlg, wParam, pOFI);
  1376. bInitializing = FALSE;
  1377. HourGlass(FALSE);
  1378. return (bRet);
  1379. break;
  1380. }
  1381. case ( WM_ACTIVATE ) :
  1382. {
  1383. if (!bInChildDlg)
  1384. {
  1385. if (bFirstTime == TRUE)
  1386. {
  1387. bFirstTime = FALSE;
  1388. }
  1389. else if (wParam)
  1390. {
  1391. //
  1392. // If becoming active.
  1393. //
  1394. if (!wNoRedraw)
  1395. {
  1396. LNDSetEvent(hDlg);
  1397. }
  1398. }
  1399. }
  1400. return (FALSE);
  1401. break;
  1402. }
  1403. case ( WM_MEASUREITEM ) :
  1404. {
  1405. MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
  1406. break;
  1407. }
  1408. case ( WM_DRAWITEM ) :
  1409. {
  1410. if (wNoRedraw < 2)
  1411. {
  1412. DrawItem(pOFI, hDlg, wParam, (LPDRAWITEMSTRUCT)lParam, TRUE);
  1413. }
  1414. break;
  1415. }
  1416. case ( WM_SYSCOLORCHANGE ) :
  1417. {
  1418. SetRGBValues();
  1419. LoadDirDriveBitmap();
  1420. break;
  1421. }
  1422. case ( WM_COMMAND ) :
  1423. {
  1424. return (FileOpenCmd(hDlg, wParam, lParam, pOFI, TRUE));
  1425. break;
  1426. }
  1427. case ( WM_SETFOCUS ) :
  1428. {
  1429. //
  1430. // This logic used to be in CBN_SETFOCUS in fileopencmd,
  1431. // but CBN_SETFOCUS is called whenever there is a click on
  1432. // the List Drives combo. This causes the worker thread
  1433. // to start up and flicker when the combo box is refreshed.
  1434. //
  1435. // But, refreshes are only needed when someone focuses out of
  1436. // the common dialog and then back in (unless someone is logged
  1437. // in remote, or there is a background thread busy connecting!)
  1438. // so fix the flicker by moving the logic here.
  1439. //
  1440. if (!wNoRedraw)
  1441. {
  1442. LNDSetEvent(hDlg);
  1443. }
  1444. return (FALSE);
  1445. break;
  1446. }
  1447. case ( WM_HELP ) :
  1448. {
  1449. if (IsWindowEnabled(hDlg))
  1450. {
  1451. WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
  1452. NULL,
  1453. HELP_WM_HELP,
  1454. (ULONG_PTR)(LPTSTR)aFileSaveHelpIDs );
  1455. }
  1456. break;
  1457. }
  1458. case ( WM_CONTEXTMENU ) :
  1459. {
  1460. if (IsWindowEnabled(hDlg))
  1461. {
  1462. WinHelp( (HWND)wParam,
  1463. NULL,
  1464. HELP_CONTEXTMENU,
  1465. (ULONG_PTR)(LPVOID)aFileSaveHelpIDs );
  1466. }
  1467. break;
  1468. }
  1469. default :
  1470. {
  1471. return (FALSE);
  1472. }
  1473. }
  1474. return (TRUE);
  1475. }
  1476. ////////////////////////////////////////////////////////////////////////////
  1477. //
  1478. // InitFileDlg
  1479. //
  1480. ////////////////////////////////////////////////////////////////////////////
  1481. BOOL_PTR InitFileDlg(
  1482. HWND hDlg,
  1483. WPARAM wParam,
  1484. POPENFILEINFO pOFI)
  1485. {
  1486. DWORD lRet, nFilterIndex;
  1487. LPOPENFILENAME pOFN = pOFI->pOFN;
  1488. int nFileOffset, nExtOffset;
  1489. RECT rRect;
  1490. RECT rLbox;
  1491. BOOL_PTR bRet;
  1492. if (!InitTlsValues(pOFI))
  1493. {
  1494. //
  1495. // The extended error is set inside of the above call.
  1496. //
  1497. EndDialog(hDlg, FALSE);
  1498. return (FALSE);
  1499. }
  1500. lpLBProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hDlg, lst2), GWLP_WNDPROC);
  1501. lpOKProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hDlg, IDOK), GWLP_WNDPROC);
  1502. if (!lpLBProc || !lpOKProc)
  1503. {
  1504. StoreExtendedError(FNERR_SUBCLASSFAILURE);
  1505. EndDialog(hDlg, FALSE);
  1506. return (FALSE);
  1507. }
  1508. //
  1509. // Save original directory for later restoration if necessary.
  1510. //
  1511. *pOFI->szCurDir = 0;
  1512. GetCurrentDirectory(MAX_FULLPATHNAME + 1, pOFI->szCurDir);
  1513. //
  1514. // Check out if the filename contains a path. If so, override whatever
  1515. // is contained in lpstrInitialDir. Chop off the path and put up only
  1516. // the filename.
  1517. //
  1518. if ( pOFN->lpstrFile &&
  1519. *pOFN->lpstrFile &&
  1520. !(pOFN->Flags & OFN_NOVALIDATE) )
  1521. {
  1522. if (DBL_BSLASH(pOFN->lpstrFile + 2) &&
  1523. ((*(pOFN->lpstrFile + 1) == CHAR_COLON)))
  1524. {
  1525. lstrcpy(pOFN->lpstrFile , pOFN->lpstrFile + sizeof(TCHAR));
  1526. }
  1527. lRet = ParseFile(pOFN->lpstrFile, TRUE, IS16BITWOWAPP(pOFN), FALSE);
  1528. nFileOffset = (int)(SHORT)LOWORD(lRet);
  1529. nExtOffset = (int)(SHORT)HIWORD(lRet);
  1530. //
  1531. // Is the filename invalid?
  1532. //
  1533. if ( (nFileOffset < 0) &&
  1534. (nFileOffset != PARSE_EMPTYSTRING) &&
  1535. (pOFN->lpstrFile[nExtOffset] != CHAR_SEMICOLON) )
  1536. {
  1537. StoreExtendedError(FNERR_INVALIDFILENAME);
  1538. EndDialog(hDlg, FALSE);
  1539. return (FALSE);
  1540. }
  1541. }
  1542. pOFN->Flags &= ~(OFN_FILTERDOWN | OFN_DRIVEDOWN | OFN_DIRSELCHANGED);
  1543. pOFI->idirSub = 0;
  1544. if (!(pOFN->Flags & OFN_SHOWHELP))
  1545. {
  1546. HWND hHelp;
  1547. EnableWindow(hHelp = GetDlgItem(hDlg, pshHelp), FALSE);
  1548. //
  1549. // Move the window out of this spot so that no overlap will be
  1550. // detected.
  1551. //
  1552. MoveWindow(hHelp, -8000, -8000, 20, 20, FALSE);
  1553. ShowWindow(hHelp, SW_HIDE);
  1554. }
  1555. if (pOFN->Flags & OFN_CREATEPROMPT)
  1556. {
  1557. pOFN->Flags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);
  1558. }
  1559. else if (pOFN->Flags & OFN_FILEMUSTEXIST)
  1560. {
  1561. pOFN->Flags |= OFN_PATHMUSTEXIST;
  1562. }
  1563. if (pOFN->Flags & OFN_HIDEREADONLY)
  1564. {
  1565. HWND hReadOnly;
  1566. EnableWindow(hReadOnly = GetDlgItem(hDlg, chx1), FALSE);
  1567. //
  1568. // Move the window out of this spot so that no overlap will be
  1569. // detected.
  1570. //
  1571. MoveWindow(hReadOnly, -8000, -8000, 20, 20, FALSE);
  1572. ShowWindow(hReadOnly, SW_HIDE);
  1573. }
  1574. else
  1575. {
  1576. CheckDlgButton(hDlg, chx1, (pOFN->Flags & OFN_READONLY) != 0);
  1577. }
  1578. SendDlgItemMessage(hDlg, edt1, EM_LIMITTEXT, (WPARAM)MAX_PATH, 0L);
  1579. //
  1580. // Insert file specs into cmb1.
  1581. // Custom filter first.
  1582. // Must also check if filter contains anything.
  1583. //
  1584. if ( pOFN->lpstrFile &&
  1585. (StrChr(pOFN->lpstrFile, CHAR_STAR) ||
  1586. StrChr(pOFN->lpstrFile, CHAR_QMARK)) )
  1587. {
  1588. lstrcpy(pOFI->szLastFilter, pOFN->lpstrFile);
  1589. }
  1590. else
  1591. {
  1592. pOFI->szLastFilter[0] = CHAR_NULL;
  1593. }
  1594. if (pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter)
  1595. {
  1596. SHORT nLength;
  1597. SendDlgItemMessage( hDlg,
  1598. cmb1,
  1599. CB_INSERTSTRING,
  1600. 0,
  1601. (LONG_PTR)pOFN->lpstrCustomFilter );
  1602. nLength = (SHORT)(lstrlen(pOFN->lpstrCustomFilter) + 1);
  1603. SendDlgItemMessage( hDlg,
  1604. cmb1,
  1605. CB_SETITEMDATA,
  1606. 0,
  1607. (LONG)(nLength) );
  1608. SendDlgItemMessage( hDlg,
  1609. cmb1,
  1610. CB_LIMITTEXT,
  1611. (WPARAM)(pOFN->nMaxCustFilter),
  1612. 0L );
  1613. if (pOFI->szLastFilter[0] == CHAR_NULL)
  1614. {
  1615. lstrcpy(pOFI->szLastFilter, pOFN->lpstrCustomFilter + nLength);
  1616. }
  1617. }
  1618. else
  1619. {
  1620. //
  1621. // Given no custom filter, the index will be off by one.
  1622. //
  1623. if (pOFN->nFilterIndex != 0)
  1624. {
  1625. pOFN->nFilterIndex--;
  1626. }
  1627. }
  1628. //
  1629. // Listed filters next.
  1630. //
  1631. if (pOFN->lpstrFilter && *pOFN->lpstrFilter)
  1632. {
  1633. if (pOFN->nFilterIndex > InitFilterBox(hDlg, pOFN->lpstrFilter))
  1634. {
  1635. pOFN->nFilterIndex = 0;
  1636. }
  1637. }
  1638. else
  1639. {
  1640. pOFN->nFilterIndex = 0;
  1641. }
  1642. pOFI->szSpecCur[0] = CHAR_NULL;
  1643. //
  1644. // If an entry exists, select the one indicated by nFilterIndex.
  1645. //
  1646. if ((pOFN->lpstrFilter && *pOFN->lpstrFilter) ||
  1647. (pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter))
  1648. {
  1649. LPCTSTR lpFilter;
  1650. SendDlgItemMessage( hDlg,
  1651. cmb1,
  1652. CB_SETCURSEL,
  1653. (WPARAM)(pOFN->nFilterIndex),
  1654. 0L );
  1655. nFilterIndex = pOFN->nFilterIndex;
  1656. SendMessage( hDlg,
  1657. WM_COMMAND,
  1658. GET_WM_COMMAND_MPS( cmb1,
  1659. GetDlgItem(hDlg, cmb1),
  1660. MYCBN_DRAW ) );
  1661. pOFN->nFilterIndex = nFilterIndex;
  1662. if (pOFN->nFilterIndex ||
  1663. !(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter))
  1664. {
  1665. lpFilter = pOFN->lpstrFilter +
  1666. SendDlgItemMessage( hDlg,
  1667. cmb1,
  1668. CB_GETITEMDATA,
  1669. (WPARAM)pOFN->nFilterIndex,
  1670. 0L );
  1671. }
  1672. else
  1673. {
  1674. lpFilter = pOFN->lpstrCustomFilter +
  1675. lstrlen(pOFN->lpstrCustomFilter) + 1;
  1676. }
  1677. if (*lpFilter)
  1678. {
  1679. TCHAR szText[MAX_FULLPATHNAME];
  1680. // lpFilter can be longer than MAX_FULLPATHNAME!!!
  1681. StrCpyN(szText, lpFilter, ARRAYSIZE(szText));
  1682. //
  1683. // Filtering is case-insensitive.
  1684. //
  1685. CharLower(szText);
  1686. if (pOFI->szLastFilter[0] == CHAR_NULL)
  1687. {
  1688. lstrcpy(pOFI->szLastFilter, szText);
  1689. }
  1690. if (!(pOFN->lpstrFile && *pOFN->lpstrFile))
  1691. {
  1692. SetDlgItemText(hDlg, edt1, szText);
  1693. }
  1694. }
  1695. }
  1696. InitCurrentDisk(hDlg, pOFI, cmb2);
  1697. bFirstTime = TRUE;
  1698. bInChildDlg = FALSE;
  1699. SendMessage( hDlg,
  1700. WM_COMMAND,
  1701. GET_WM_COMMAND_MPS(cmb2, GetDlgItem(hDlg, cmb2), MYCBN_DRAW) );
  1702. SendMessage( hDlg,
  1703. WM_COMMAND,
  1704. GET_WM_COMMAND_MPS(cmb2, GetDlgItem(hDlg, cmb2), MYCBN_LIST) );
  1705. if (pOFN->lpstrFile && *pOFN->lpstrFile)
  1706. {
  1707. TCHAR szText[MAX_FULLPATHNAME];
  1708. lRet = ParseFile( pOFN->lpstrFile,
  1709. IsLFNDriveX(hDlg, pOFN->lpstrFile),
  1710. IS16BITWOWAPP(pOFN),
  1711. FALSE );
  1712. nFileOffset = (int)(SHORT)LOWORD(lRet);
  1713. nExtOffset = (int)(SHORT)HIWORD(lRet);
  1714. //
  1715. // Is the filename invalid?
  1716. //
  1717. if ( !(pOFN->Flags & OFN_NOVALIDATE) &&
  1718. (nFileOffset < 0) &&
  1719. (nFileOffset != PARSE_EMPTYSTRING) &&
  1720. (pOFN->lpstrFile[nExtOffset] != CHAR_SEMICOLON) )
  1721. {
  1722. StoreExtendedError(FNERR_INVALIDFILENAME);
  1723. EndDialog(hDlg, FALSE);
  1724. return (FALSE);
  1725. }
  1726. lstrcpy(szText, pOFN->lpstrFile);
  1727. SetDlgItemText(hDlg, edt1, szText);
  1728. }
  1729. SetWindowLongPtr(GetDlgItem(hDlg, lst2), GWLP_WNDPROC, (LONG_PTR)dwLBSubclass);
  1730. SetWindowLongPtr(GetDlgItem(hDlg, IDOK), GWLP_WNDPROC, (LONG_PTR)dwOKSubclass);
  1731. if (pOFN->lpstrTitle && *pOFN->lpstrTitle)
  1732. {
  1733. SetWindowText(hDlg, pOFN->lpstrTitle);
  1734. }
  1735. //
  1736. // By setting dyText to rRect.bottom/8, dyText defaults to 8 items showing
  1737. // in the listbox. This only matters if the applications hook function
  1738. // steals all WM_MEASUREITEM messages. Otherwise, dyText will be set in
  1739. // the MeasureItem() routine. Check for !dyItem in case message ordering
  1740. // has already sent WM_MEASUREITEM and dyText is already initialized.
  1741. //
  1742. if (!dyItem)
  1743. {
  1744. GetClientRect(GetDlgItem(hDlg, lst1), (LPRECT) &rRect);
  1745. if (!(dyText = (rRect.bottom / 8)))
  1746. {
  1747. //
  1748. // If no size to rectangle.
  1749. //
  1750. dyText = 8;
  1751. }
  1752. }
  1753. // The template has changed to make it extremely clear that
  1754. // this is not a combobox, but rather an edit control and a listbox. The
  1755. // problem is that the new templates try to align the edit box and listbox.
  1756. // Unfortunately, when listboxes add borders, they expand beyond their
  1757. // borders. When edit controls add borders, they stay within their
  1758. // borders. This makes it impossible to align the two controls strictly
  1759. // within the template. The code below will align the controls, but only
  1760. // if they are using the standard dialog template.
  1761. //
  1762. if (!(pOFN->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
  1763. {
  1764. GetWindowRect(GetDlgItem(hDlg, lst1), (LPRECT)&rLbox);
  1765. GetWindowRect(GetDlgItem(hDlg, edt1), (LPRECT)&rRect);
  1766. rRect.left = rLbox.left;
  1767. rRect.right = rLbox.right;
  1768. MapWindowPoints(NULL, hDlg, (LPPOINT)&rRect, 2);
  1769. SetWindowPos( GetDlgItem(hDlg, edt1),
  1770. 0,
  1771. rRect.left,
  1772. rRect.top,
  1773. rRect.right - rRect.left,
  1774. rRect.bottom - rRect.top,
  1775. SWP_NOZORDER );
  1776. }
  1777. if (pOFN->lpfnHook)
  1778. {
  1779. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  1780. #ifdef UNICODE
  1781. if (pOFI->ApiType == COMDLG_ANSI)
  1782. {
  1783. ThunkOpenFileNameW2A(pOFI);
  1784. bRet = ((*lpfnHook)( hDlg,
  1785. WM_INITDIALOG,
  1786. wParam,
  1787. (LPARAM)pOFI->pOFNA ));
  1788. //
  1789. // Strange win 31 example uses lCustData to
  1790. // hold a temporary variable that it passes back to
  1791. // calling function.
  1792. //
  1793. ThunkOpenFileNameA2W(pOFI);
  1794. }
  1795. else
  1796. #endif
  1797. {
  1798. bRet = ((*lpfnHook)( hDlg,
  1799. WM_INITDIALOG,
  1800. wParam,
  1801. (LPARAM)pOFN ));
  1802. }
  1803. }
  1804. else
  1805. {
  1806. #ifdef UNICODE
  1807. //
  1808. // Have to thunk A version even when there isn't a hook proc so it
  1809. // doesn't reset W version on delayed thunk back.
  1810. //
  1811. if (pOFI->ApiType == COMDLG_ANSI)
  1812. {
  1813. pOFI->pOFNA->Flags = pOFN->Flags;
  1814. }
  1815. #endif
  1816. bRet = TRUE;
  1817. }
  1818. //
  1819. // At first, assume there is net support !
  1820. //
  1821. if ((pOFN->Flags & OFN_NONETWORKBUTTON))
  1822. {
  1823. HWND hNet;
  1824. if (hNet = GetDlgItem(hDlg, psh14))
  1825. {
  1826. EnableWindow(hNet = GetDlgItem(hDlg, psh14), FALSE);
  1827. ShowWindow(hNet, SW_HIDE);
  1828. }
  1829. }
  1830. else
  1831. {
  1832. AddNetButton( hDlg,
  1833. ((pOFN->Flags & OFN_ENABLETEMPLATE)
  1834. ? pOFN->hInstance
  1835. : g_hinst),
  1836. FILE_BOTTOM_MARGIN,
  1837. (pOFN->Flags & (OFN_ENABLETEMPLATE |
  1838. OFN_ENABLETEMPLATEHANDLE))
  1839. ? FALSE
  1840. : TRUE,
  1841. (pOFN->Flags & OFN_NOLONGNAMES)
  1842. ? FALSE
  1843. : TRUE,
  1844. FALSE);
  1845. }
  1846. return (bRet);
  1847. }
  1848. ////////////////////////////////////////////////////////////////////////////
  1849. //
  1850. // InitTlsValues
  1851. //
  1852. ////////////////////////////////////////////////////////////////////////////
  1853. int InitTlsValues(
  1854. POPENFILEINFO pOFI)
  1855. {
  1856. //
  1857. // As long as we do not call TlsGetValue before this,
  1858. // everything should be ok.
  1859. //
  1860. LPCURDLG lpCurDlg, lpPrevDlg;
  1861. DWORD dwError;
  1862. LPTSTR lpCurDir;
  1863. if (dwNumDlgs == MAX_THREADS)
  1864. {
  1865. dwError = CDERR_INITIALIZATION;
  1866. goto ErrorExit0;
  1867. }
  1868. // alloc for the current directory
  1869. if (lpCurDir = (LPTSTR)LocalAlloc(LPTR, CCHNETPATH * sizeof(TCHAR)))
  1870. {
  1871. GetCurrentDirectory(CCHNETPATH, lpCurDir);
  1872. if ( (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) &&
  1873. (StrChr(lpCurDir, CHAR_SPACE)) )
  1874. {
  1875. GetShortPathName(lpCurDir, lpCurDir, CCHNETPATH);
  1876. }
  1877. }
  1878. else
  1879. {
  1880. dwError = CDERR_MEMALLOCFAILURE;
  1881. goto ErrorExit0;
  1882. }
  1883. // add a CurDlg struct to the list for this thread
  1884. if (lpCurDlg = (LPCURDLG)LocalAlloc(LPTR, sizeof(CURDLG)))
  1885. {
  1886. // get start of CURDLG list for this thread
  1887. // Note: lpPrevDlg will be NULL if there wasn't a previous dialog
  1888. lpPrevDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  1889. // make sure TlsGetValue() actually succeeded (a NULL return could
  1890. // mean there wasn't a previous dialog in the list)
  1891. if (GetLastError() != NO_ERROR)
  1892. {
  1893. dwError = CDERR_INITIALIZATION;
  1894. goto ErrorExit2;
  1895. }
  1896. // push the new dlg to the front of the list
  1897. lpCurDlg->next = lpPrevDlg;
  1898. lpCurDlg->lpstrCurDir = lpCurDir;
  1899. PathAddBackslash(lpCurDlg->lpstrCurDir);
  1900. EnterCriticalSection(&g_csLocal);
  1901. lpCurDlg->dwCurDlgNum = dwNumDlgs++;
  1902. LeaveCriticalSection(&g_csLocal);
  1903. // save the new head of the list for the thread
  1904. if (!TlsSetValue(g_tlsiCurDlg, (LPVOID)lpCurDlg))
  1905. {
  1906. dwError = CDERR_INITIALIZATION;
  1907. goto ErrorExit2;
  1908. }
  1909. }
  1910. else
  1911. {
  1912. dwError = CDERR_MEMALLOCFAILURE;
  1913. goto ErrorExit1;
  1914. }
  1915. return(TRUE);
  1916. ErrorExit2:
  1917. LocalFree(lpCurDlg);
  1918. ErrorExit1:
  1919. LocalFree(lpCurDir);
  1920. ErrorExit0:
  1921. StoreExtendedError(dwError);
  1922. return (FALSE);
  1923. }
  1924. ////////////////////////////////////////////////////////////////////////////
  1925. //
  1926. // InitFilterBox
  1927. //
  1928. // Places the double null terminated list of filters in the combo box.
  1929. // The list should consist of pairs of null terminated strings, with
  1930. // an additional null terminating the list.
  1931. //
  1932. ////////////////////////////////////////////////////////////////////////////
  1933. DWORD InitFilterBox(
  1934. HANDLE hDlg,
  1935. LPCTSTR lpszFilter)
  1936. {
  1937. DWORD nOffset = 0;
  1938. DWORD nIndex = 0;
  1939. register WORD nLen;
  1940. while (*lpszFilter)
  1941. {
  1942. //
  1943. // First string put in as string to show.
  1944. //
  1945. nIndex = (DWORD) SendDlgItemMessage( hDlg,
  1946. cmb1,
  1947. CB_ADDSTRING,
  1948. 0,
  1949. (LPARAM)lpszFilter );
  1950. nLen = (WORD)(lstrlen(lpszFilter) + 1);
  1951. (LPTSTR)lpszFilter += nLen;
  1952. nOffset += nLen;
  1953. //
  1954. // Second string put in as itemdata.
  1955. //
  1956. SendDlgItemMessage( hDlg,
  1957. cmb1,
  1958. CB_SETITEMDATA,
  1959. (WPARAM)nIndex,
  1960. nOffset );
  1961. //
  1962. // Advance to next element.
  1963. //
  1964. nLen = (WORD)(lstrlen(lpszFilter) + 1);
  1965. (LPTSTR)lpszFilter += nLen;
  1966. nOffset += nLen;
  1967. }
  1968. return (nIndex);
  1969. }
  1970. void TokenizeFilterString(LPTSTR pszFilterString, LPTSTR *ppszFilterArray, int cFilterArray, BOOL bLFN)
  1971. {
  1972. LPCTSTR pszDelim = bLFN ? szSemiColonTab : szSemiColonSpaceTab;
  1973. int nFilters = 0;
  1974. cFilterArray--; // Need one for the NULL at the end.
  1975. //
  1976. // Find the first filter in the string, and add it to the
  1977. // array.
  1978. //
  1979. ppszFilterArray[nFilters] = lstrtok(pszFilterString, pszDelim);
  1980. //
  1981. // Now we are going to loop through all the filters in the string
  1982. // parsing the one we already have, and then finding the next one
  1983. // and starting the loop over again.
  1984. //
  1985. while (ppszFilterArray[nFilters] && (nFilters < cFilterArray))
  1986. {
  1987. //
  1988. // Check to see if the first character is a space. If so, remove
  1989. // the spaces, and save the pointer back into the same spot. We
  1990. // need to do this because the FindFirstFile/Next api will still
  1991. // work on filenames that begin with a space since they also
  1992. // look at the short names. The short names will begin with the
  1993. // same first real letter as the long filename. For example, the
  1994. // long filename is " my document" the first letter of this short
  1995. // name is "m", so searching on "m*.*" or " m*.*" will yield the
  1996. // same results.
  1997. //
  1998. if (bLFN && (*ppszFilterArray[nFilters] == CHAR_SPACE))
  1999. {
  2000. LPTSTR pszTemp = ppszFilterArray[nFilters];
  2001. while ((*pszTemp == CHAR_SPACE) && *pszTemp)
  2002. {
  2003. pszTemp = CharNext(pszTemp);
  2004. }
  2005. ppszFilterArray[nFilters] = pszTemp;
  2006. }
  2007. //
  2008. // Ready to move on to the next filter. Find the next
  2009. // filter based upon the type of file system we're using.
  2010. //
  2011. ppszFilterArray[++nFilters] = lstrtok(NULL, pszDelim);
  2012. //
  2013. // In case we found a pointer to NULL, then look for the
  2014. // next filter.
  2015. //
  2016. while (ppszFilterArray[nFilters] && !*ppszFilterArray[nFilters])
  2017. {
  2018. ppszFilterArray[nFilters] = lstrtok(NULL, pszDelim);
  2019. }
  2020. }
  2021. }
  2022. BOOL FoundFilterMatch(LPCTSTR pszIn, BOOL bLFN)
  2023. {
  2024. TCHAR szFilter[MAX_FULLPATHNAME];
  2025. LPTSTR pszF[MAXFILTERS + 1];
  2026. BOOL fFoundMatches = FALSE;
  2027. int i;
  2028. StrCpyN(szFilter, pszIn, SIZECHARS(szFilter));
  2029. TokenizeFilterString(szFilter, pszF, ARRAYSIZE(pszF), bLFN);
  2030. for (i = 0; i < ARRAYSIZE(pszF) && pszF[i] && !fFoundMatches; i++)
  2031. {
  2032. HANDLE hff;
  2033. WIN32_FIND_DATA FindFileData;
  2034. //
  2035. // Find First for each filter.
  2036. //
  2037. hff = FindFirstFile(pszF[i], &FindFileData);
  2038. if (hff == INVALID_HANDLE_VALUE)
  2039. {
  2040. continue;
  2041. }
  2042. do
  2043. {
  2044. if ((FindFileData.dwFileAttributes & EXCLBITS) ||
  2045. (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  2046. {
  2047. continue;
  2048. }
  2049. fFoundMatches = TRUE;
  2050. break;
  2051. } while (FindNextFile(hff, &FindFileData));
  2052. FindClose(hff);
  2053. }
  2054. return fFoundMatches;
  2055. }
  2056. ////////////////////////////////////////////////////////////////////////////
  2057. //
  2058. // GetAppOpenDir
  2059. //
  2060. ////////////////////////////////////////////////////////////////////////////
  2061. void GetAppOpenDir(LPCTSTR pszDir, LPTSTR pszOut, LPITEMIDLIST *ppidl)
  2062. {
  2063. BOOL fUseMyDocs = FALSE;
  2064. TCHAR szPersonal[MAX_PATH];
  2065. *pszOut = 0; // prepare to return empty string
  2066. if (ppidl)
  2067. *ppidl = NULL;
  2068. if (SHGetSpecialFolderPath(NULL, szPersonal, CSIDL_PERSONAL, FALSE))
  2069. {
  2070. if (*pszDir)
  2071. {
  2072. //
  2073. // if the current directory is a temp dir
  2074. // or is the mydocs dir, then use my docs
  2075. // otherwise we should just use this directory
  2076. //
  2077. if ((0 == lstrcmpi(pszDir, szPersonal) || PathIsTemporary(pszDir)))
  2078. fUseMyDocs = TRUE;
  2079. }
  2080. else
  2081. {
  2082. TCHAR szPath[MAX_FULLPATHNAME];
  2083. if (GetCurrentDirectory(ARRAYSIZE(szPath), szPath)
  2084. && (PathIsTemporary(szPath) || (0 == lstrcmpi(szPath, szPersonal))))
  2085. fUseMyDocs = TRUE;
  2086. }
  2087. }
  2088. if (fUseMyDocs)
  2089. {
  2090. lstrcpy(pszOut, szPersonal);
  2091. if (ppidl)
  2092. {
  2093. SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, ppidl);
  2094. }
  2095. }
  2096. else
  2097. {
  2098. lstrcpy(pszOut, pszDir);
  2099. }
  2100. }
  2101. ////////////////////////////////////////////////////////////////////////////
  2102. //
  2103. // InitCurrentDisk
  2104. //
  2105. ////////////////////////////////////////////////////////////////////////////
  2106. VOID InitCurrentDisk(HWND hDlg, POPENFILEINFO pOFI, WORD cmb)
  2107. {
  2108. TCHAR szPath[MAX_FULLPATHNAME];
  2109. //
  2110. // Clear out stale unc stuff from disk info.
  2111. // Unc \\server\shares are persistent through one popup session
  2112. // and then we resync with the system. This is to fix a bug
  2113. // where a user's startup dir is unc but the system no longer has
  2114. // a connection and hence the cmb2 appears blank.
  2115. //
  2116. EnableDiskInfo(FALSE, TRUE);
  2117. if (pOFI->pOFN->lpstrInitialDir)
  2118. {
  2119. //
  2120. // Notice that we force ChangeDir to succeed here
  2121. // but that TlsGetValue(g_tlsiCurDlg)->lpstrCurDir will return "" which
  2122. // when fed to SheChangeDirEx means GetCurrentDir will be called.
  2123. // So, the default cd behavior at startup is:
  2124. // 1. lpstrInitialDir
  2125. // 2. GetCurrentDir
  2126. //
  2127. szPath[0] = 0;
  2128. if ( (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) &&
  2129. (StrChr(pOFI->pOFN->lpstrInitialDir, CHAR_SPACE)) &&
  2130. (GetShortPathName( pOFI->pOFN->lpstrInitialDir,
  2131. szPath,
  2132. MAX_FULLPATHNAME )) &&
  2133. (szPath[0] != 0) )
  2134. {
  2135. ChangeDir(hDlg, szPath, TRUE, FALSE);
  2136. }
  2137. else
  2138. {
  2139. ChangeDir(hDlg, pOFI->pOFN->lpstrInitialDir, TRUE, FALSE);
  2140. }
  2141. }
  2142. else
  2143. {
  2144. GetAppOpenDir(TEXT(""),szPath, NULL);
  2145. ChangeDir(hDlg, szPath, TRUE, FALSE);
  2146. }
  2147. }
  2148. ////////////////////////////////////////////////////////////////////////////
  2149. //
  2150. // vDeleteDirDriveBitmap
  2151. //
  2152. // Gets rid of bitmaps, if they exist.
  2153. //
  2154. ////////////////////////////////////////////////////////////////////////////
  2155. VOID vDeleteDirDriveBitmap()
  2156. {
  2157. if (hbmpOrigMemBmp)
  2158. {
  2159. SelectObject(hdcMemory, hbmpOrigMemBmp);
  2160. if (hbmpDirDrive != HNULL)
  2161. {
  2162. DeleteObject(hbmpDirDrive);
  2163. hbmpDirDrive = HNULL;
  2164. }
  2165. }
  2166. }
  2167. ////////////////////////////////////////////////////////////////////////////
  2168. //
  2169. // LoadDirDriveBitmap
  2170. //
  2171. // Creates the drive/directory bitmap. If an appropriate bitmap
  2172. // already exists, it just returns immediately. Otherwise, it
  2173. // loads the bitmap and creates a larger bitmap with both regular
  2174. // and highlight colors.
  2175. //
  2176. ////////////////////////////////////////////////////////////////////////////
  2177. BOOL LoadDirDriveBitmap()
  2178. {
  2179. BITMAP bmp;
  2180. HANDLE hbmp, hbmpOrig;
  2181. HDC hdcTemp;
  2182. BOOL bWorked = FALSE;
  2183. if ( (hbmpDirDrive != HNULL) &&
  2184. (rgbWindowColor == rgbDDWindow) &&
  2185. (rgbHiliteColor == rgbDDHilite))
  2186. {
  2187. if (SelectObject(hdcMemory, hbmpDirDrive))
  2188. {
  2189. return (TRUE);
  2190. }
  2191. }
  2192. vDeleteDirDriveBitmap();
  2193. rgbDDWindow = rgbWindowColor;
  2194. rgbDDHilite = rgbHiliteColor;
  2195. if (!(hdcTemp = CreateCompatibleDC(hdcMemory)))
  2196. {
  2197. goto LoadExit;
  2198. }
  2199. if (!(hbmp = LoadAlterBitmap(bmpDirDrive, rgbSolidBlue, rgbWindowColor)))
  2200. {
  2201. goto DeleteTempDC;
  2202. }
  2203. GetObject(hbmp, sizeof(BITMAP), (LPTSTR)&bmp);
  2204. dyDirDrive = bmp.bmHeight;
  2205. dxDirDrive = bmp.bmWidth;
  2206. hbmpOrig = SelectObject(hdcTemp, hbmp);
  2207. hbmpDirDrive = CreateDiscardableBitmap(hdcTemp, dxDirDrive * 2, dyDirDrive);
  2208. if (!hbmpDirDrive)
  2209. {
  2210. goto DeleteTempBmp;
  2211. }
  2212. if (!SelectObject(hdcMemory, hbmpDirDrive))
  2213. {
  2214. vDeleteDirDriveBitmap();
  2215. goto DeleteTempBmp;
  2216. }
  2217. BitBlt(hdcMemory, 0, 0, dxDirDrive, dyDirDrive, hdcTemp, 0, 0, SRCCOPY);
  2218. SelectObject(hdcTemp, hbmpOrig);
  2219. DeleteObject(hbmp);
  2220. if (!(hbmp = LoadAlterBitmap(bmpDirDrive, rgbSolidBlue, rgbHiliteColor)))
  2221. {
  2222. goto DeleteTempDC;
  2223. }
  2224. hbmpOrig = SelectObject(hdcTemp, hbmp);
  2225. BitBlt(hdcMemory, dxDirDrive, 0, dxDirDrive, dyDirDrive, hdcTemp, 0, 0, SRCCOPY);
  2226. SelectObject(hdcTemp, hbmpOrig);
  2227. bWorked = TRUE;
  2228. DeleteTempBmp:
  2229. DeleteObject(hbmp);
  2230. DeleteTempDC:
  2231. DeleteDC(hdcTemp);
  2232. LoadExit:
  2233. return (bWorked);
  2234. }
  2235. ////////////////////////////////////////////////////////////////////////////
  2236. //
  2237. // SetRGBValues
  2238. //
  2239. // This sets the various system colors in static variables. It's
  2240. // called at init time and when system colors change.
  2241. //
  2242. ////////////////////////////////////////////////////////////////////////////
  2243. void SetRGBValues()
  2244. {
  2245. rgbWindowColor = GetSysColor(COLOR_WINDOW);
  2246. rgbHiliteColor = GetSysColor(COLOR_HIGHLIGHT);
  2247. rgbWindowText = GetSysColor(COLOR_WINDOWTEXT);
  2248. rgbHiliteText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  2249. rgbGrayText = GetSysColor(COLOR_GRAYTEXT);
  2250. }
  2251. ////////////////////////////////////////////////////////////////////////////
  2252. //
  2253. // FSetUpFile
  2254. //
  2255. // This loads in the resources & initializes the data used by the
  2256. // file dialogs.
  2257. //
  2258. // Returns: TRUE if successful
  2259. // FALSE if any bitmap fails
  2260. //
  2261. ////////////////////////////////////////////////////////////////////////////
  2262. BOOL FSetUpFile()
  2263. {
  2264. if (cLock++)
  2265. {
  2266. return (TRUE);
  2267. }
  2268. SetRGBValues();
  2269. return (LoadDirDriveBitmap());
  2270. }
  2271. ////////////////////////////////////////////////////////////////////////////
  2272. //
  2273. // GetPathOffset
  2274. //
  2275. ////////////////////////////////////////////////////////////////////////////
  2276. int GetPathOffset(LPTSTR lpszDir)
  2277. {
  2278. LPTSTR lpszSkipRoot;
  2279. if (!lpszDir || !*lpszDir)
  2280. {
  2281. return (-1);
  2282. }
  2283. lpszSkipRoot = PathSkipRoot(lpszDir);
  2284. if (lpszSkipRoot)
  2285. {
  2286. return (int)((lpszSkipRoot - 1) - lpszDir);
  2287. }
  2288. else
  2289. {
  2290. //
  2291. // Unrecognized format.
  2292. //
  2293. return (-1);
  2294. }
  2295. }
  2296. ////////////////////////////////////////////////////////////////////////////
  2297. //
  2298. // FileOpenCmd
  2299. //
  2300. // Handles WM_COMMAND for Open & Save dlgs.
  2301. //
  2302. // edt1 = file name
  2303. // lst1 = list of files in current directory matching current pattern
  2304. // cmb1 = lists file patterns
  2305. // stc1 = is current directory
  2306. // lst2 = lists directories on current drive
  2307. // cmb2 = lists drives
  2308. // IDOK = is Open pushbutton
  2309. // IDCANCEL = is Cancel pushbutton
  2310. // chx1 = is for opening read only files
  2311. //
  2312. // Returns the normal dialog proc values.
  2313. //
  2314. ////////////////////////////////////////////////////////////////////////////
  2315. BOOL_PTR FileOpenCmd(
  2316. HANDLE hDlg,
  2317. WPARAM wParam,
  2318. LPARAM lParam,
  2319. POPENFILEINFO pOFI,
  2320. BOOL bSave)
  2321. {
  2322. LPOPENFILENAME pOFN;
  2323. LPTSTR pch, pch2;
  2324. WORD i, sCount, len;
  2325. LRESULT wFlag;
  2326. BOOL_PTR bRet, bHookRet;
  2327. TCHAR szText[MAX_FULLPATHNAME];
  2328. HWND hwnd;
  2329. LPCURDLG lpCurDlg;
  2330. if (!pOFI)
  2331. {
  2332. return (FALSE);
  2333. }
  2334. pOFN = pOFI->pOFN;
  2335. switch (GET_WM_COMMAND_ID(wParam, lParam))
  2336. {
  2337. case ( IDOK ) :
  2338. {
  2339. #ifdef UNICODE
  2340. //
  2341. // Apps that side-effect this message may not have their
  2342. // internal unicode strings updated (eg. Corel Mosaic).
  2343. //
  2344. // NOTE: Must preserve the internal flags.
  2345. //
  2346. if (pOFI->ApiType == COMDLG_ANSI)
  2347. {
  2348. DWORD InternalFlags = pOFN->Flags & OFN_ALL_INTERNAL_FLAGS;
  2349. ThunkOpenFileNameA2W(pOFI);
  2350. pOFN->Flags |= InternalFlags;
  2351. }
  2352. #endif
  2353. //
  2354. // If the focus is on the directory box, or if the selection
  2355. // within the box has changed since the last listing, give a
  2356. // new listing.
  2357. //
  2358. if (bChangeDir || ((GetFocus() == GetDlgItem(hDlg, lst2)) &&
  2359. (pOFN->Flags & OFN_DIRSELCHANGED)))
  2360. {
  2361. bChangeDir = FALSE;
  2362. goto ChangingDir;
  2363. }
  2364. else if ((GetFocus() == (hwnd = GetDlgItem(hDlg, cmb2))) &&
  2365. (pOFN->Flags & OFN_DRIVEDOWN))
  2366. {
  2367. //
  2368. // If the focus is on the drive or filter combobox, give
  2369. // a new listing.
  2370. //
  2371. SendDlgItemMessage(hDlg, cmb2, CB_SHOWDROPDOWN, FALSE, 0L);
  2372. break;
  2373. }
  2374. else if ((GetFocus() == (hwnd = GetDlgItem(hDlg, cmb1))) &&
  2375. (pOFN->Flags & OFN_FILTERDOWN))
  2376. {
  2377. SendDlgItemMessage(hDlg, cmb1, CB_SHOWDROPDOWN, FALSE, 0L);
  2378. lParam = (LPARAM)hwnd;
  2379. goto ChangingFilter;
  2380. }
  2381. else
  2382. {
  2383. #ifdef UNICODE
  2384. //
  2385. // Visual Basic passes in an uninitialized lpstrDefExt string.
  2386. // Since we only have to use it in OKButtonPressed, update
  2387. // lpstrDefExt here along with whatever else is only needed
  2388. // in OKButtonPressed.
  2389. //
  2390. if (pOFI->ApiType == COMDLG_ANSI)
  2391. {
  2392. ThunkOpenFileNameA2WDelayed(pOFI);
  2393. }
  2394. #endif
  2395. if (OKButtonPressed(hDlg, pOFI, bSave))
  2396. {
  2397. bRet = TRUE;
  2398. if (pOFN->lpstrFile)
  2399. {
  2400. if (!(pOFN->Flags & OFN_NOVALIDATE))
  2401. {
  2402. if (pOFN->nMaxFile >= 3)
  2403. {
  2404. if ((pOFN->lpstrFile[0] == 0) ||
  2405. (pOFN->lpstrFile[1] == 0) ||
  2406. (pOFN->lpstrFile[2] == 0))
  2407. {
  2408. bRet = FALSE;
  2409. StoreExtendedError(FNERR_BUFFERTOOSMALL);
  2410. }
  2411. }
  2412. else
  2413. {
  2414. bRet = FALSE;
  2415. StoreExtendedError(FNERR_BUFFERTOOSMALL);
  2416. }
  2417. }
  2418. }
  2419. goto AbortDialog;
  2420. }
  2421. }
  2422. SendDlgItemMessage(hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
  2423. return (TRUE);
  2424. break;
  2425. }
  2426. case ( IDCANCEL ) :
  2427. {
  2428. bRet = FALSE;
  2429. g_bUserPressedCancel = TRUE;
  2430. goto AbortDialog;
  2431. }
  2432. case ( IDABORT ) :
  2433. {
  2434. bRet = (BYTE)lParam;
  2435. AbortDialog:
  2436. //
  2437. // Return the most recently used filter.
  2438. //
  2439. pOFN->nFilterIndex = (WORD)SendDlgItemMessage( hDlg,
  2440. cmb1,
  2441. CB_GETCURSEL,
  2442. (WPARAM)0,
  2443. (LPARAM)0 );
  2444. if (pOFN->lpstrCustomFilter)
  2445. {
  2446. len = (WORD)(lstrlen(pOFN->lpstrCustomFilter) + 1);
  2447. sCount = (WORD)lstrlen(pOFI->szLastFilter);
  2448. if (pOFN->nMaxCustFilter > (DWORD)(sCount + len))
  2449. {
  2450. lstrcpy(pOFN->lpstrCustomFilter + len, pOFI->szLastFilter);
  2451. }
  2452. }
  2453. if (!pOFN->lpstrCustomFilter ||
  2454. (*pOFN->lpstrCustomFilter == CHAR_NULL))
  2455. {
  2456. pOFN->nFilterIndex++;
  2457. }
  2458. if (((GET_WM_COMMAND_ID(wParam, lParam)) == IDOK) && pOFN->lpfnHook)
  2459. {
  2460. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  2461. #ifdef UNICODE
  2462. if (pOFI->ApiType == COMDLG_ANSI)
  2463. {
  2464. ThunkOpenFileNameW2A(pOFI);
  2465. bHookRet = (*lpfnHook)( hDlg,
  2466. msgFILEOKA,
  2467. 0,
  2468. (LPARAM)pOFI->pOFNA );
  2469. //
  2470. // For apps that side-effect pOFNA stuff and expect it to
  2471. // be preserved through dialog exit, update internal
  2472. // struct after the hook proc is called.
  2473. //
  2474. ThunkOpenFileNameA2W(pOFI);
  2475. }
  2476. else
  2477. #endif
  2478. {
  2479. bHookRet = (*lpfnHook)( hDlg,
  2480. msgFILEOKW,
  2481. 0,
  2482. (LPARAM)pOFI->pOFN );
  2483. }
  2484. if (bHookRet)
  2485. {
  2486. HourGlass(FALSE);
  2487. break;
  2488. }
  2489. }
  2490. wNoRedraw = 0;
  2491. if (pOFI->pOFN->Flags & OFN_ENABLEHOOK)
  2492. {
  2493. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  2494. glpfnFileHook = lpfnHook;
  2495. }
  2496. RemoveProp(hDlg, FILEPROP);
  2497. EndDialog(hDlg, bRet);
  2498. if (pOFI)
  2499. {
  2500. if ((pOFN->Flags & OFN_NOCHANGEDIR) && *pOFI->szCurDir)
  2501. {
  2502. ChangeDir(hDlg, pOFI->szCurDir, TRUE, FALSE);
  2503. }
  2504. }
  2505. //
  2506. // WARNING:
  2507. // If the app subclasses ID_ABORT, the worker thread will never
  2508. // get exited. This will cause problems. Currently, there are
  2509. // no apps that do this, though.
  2510. //
  2511. return (TRUE);
  2512. break;
  2513. }
  2514. case ( edt1 ) :
  2515. {
  2516. if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE )
  2517. {
  2518. int iIndex, iCount;
  2519. HWND hLBox = GetDlgItem(hDlg, lst1);
  2520. WORD wIndex = (WORD)SendMessage(hLBox, LB_GETCARETINDEX, 0, 0);
  2521. szText[0] = CHAR_NULL;
  2522. if (wIndex == (WORD)LB_ERR)
  2523. {
  2524. break;
  2525. }
  2526. SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
  2527. WM_GETTEXT,
  2528. (WPARAM)MAX_FULLPATHNAME,
  2529. (LPARAM)szText );
  2530. if ((iIndex = (int)SendMessage( hLBox,
  2531. LB_FINDSTRING,
  2532. (WPARAM)(wIndex - 1),
  2533. (LPARAM)szText )) != LB_ERR)
  2534. {
  2535. RECT rRect;
  2536. iCount = (int)SendMessage(hLBox, LB_GETTOPINDEX, 0, 0L);
  2537. GetClientRect(hLBox, (LPRECT)&rRect);
  2538. if ((iIndex < iCount) ||
  2539. (iIndex >= (iCount + rRect.bottom / dyText)))
  2540. {
  2541. SendMessage(hLBox, LB_SETCARETINDEX, (WPARAM)iIndex, 0);
  2542. SendMessage(hLBox, LB_SETTOPINDEX, (WPARAM)iIndex, 0);
  2543. }
  2544. }
  2545. return (TRUE);
  2546. }
  2547. else if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_SETFOCUS )
  2548. {
  2549. SetModeBias(MODEBIASMODE_FILENAME);
  2550. }
  2551. else if ( GET_WM_COMMAND_CMD(wParam, lParam)== EN_KILLFOCUS )
  2552. {
  2553. SetModeBias(MODEBIASMODE_DEFAULT);
  2554. }
  2555. break;
  2556. }
  2557. case ( lst1 ) :
  2558. {
  2559. //
  2560. // A double click means OK.
  2561. //
  2562. if (GET_WM_COMMAND_CMD(wParam, lParam)== LBN_DBLCLK)
  2563. {
  2564. SendMessage(hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(IDOK, 0, 0));
  2565. return (TRUE);
  2566. }
  2567. else if (pOFN && (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE))
  2568. {
  2569. if (pOFN->Flags & OFN_ALLOWMULTISELECT)
  2570. {
  2571. int *pSelIndex;
  2572. //
  2573. // Muliselection allowed.
  2574. //
  2575. sCount = (SHORT)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam),
  2576. LB_GETSELCOUNT,
  2577. 0,
  2578. 0L );
  2579. if (!sCount)
  2580. {
  2581. //
  2582. // If nothing selected, clear edit control.
  2583. //
  2584. SetDlgItemText(hDlg, edt1, szNull);
  2585. }
  2586. else
  2587. {
  2588. DWORD cchMemBlockSize = 2048;
  2589. DWORD cchTotalLength = 0;
  2590. pSelIndex = (int *)LocalAlloc(LPTR, sCount * sizeof(int));
  2591. if (!pSelIndex)
  2592. {
  2593. goto LocalFailure1;
  2594. }
  2595. sCount = (SHORT)SendMessage(
  2596. GET_WM_COMMAND_HWND(wParam, lParam),
  2597. LB_GETSELITEMS,
  2598. (WPARAM)sCount,
  2599. (LONG_PTR)(LPTSTR)pSelIndex );
  2600. pch2 = pch = (LPTSTR)
  2601. LocalAlloc(LPTR, cchMemBlockSize * sizeof(TCHAR));
  2602. if (!pch)
  2603. {
  2604. goto LocalFailure2;
  2605. }
  2606. for (*pch = CHAR_NULL, i = 0; i < sCount; i++)
  2607. {
  2608. len = (WORD)SendMessage(
  2609. GET_WM_COMMAND_HWND(wParam, lParam),
  2610. LB_GETTEXTLEN,
  2611. (WPARAM)(*(pSelIndex + i)),
  2612. (LPARAM)0 );
  2613. //
  2614. // Add the length of the selected file to the
  2615. // total length of selected files. + 2 for the
  2616. // space that goes in between files and for the
  2617. // possible dot added at the end of the filename
  2618. // if the file does not have an extension.
  2619. //
  2620. cchTotalLength += (len + 2);
  2621. if (cchTotalLength > cchMemBlockSize)
  2622. {
  2623. LPTSTR pTemp;
  2624. UINT cchPrevLen = cchTotalLength - (len + 2);
  2625. cchMemBlockSize = cchMemBlockSize << 1;
  2626. pTemp = (LPTSTR)LocalReAlloc(
  2627. pch,
  2628. cchMemBlockSize * sizeof(TCHAR),
  2629. LMEM_MOVEABLE );
  2630. if (pTemp)
  2631. {
  2632. pch = pTemp;
  2633. pch2 = pch + cchPrevLen;
  2634. }
  2635. else
  2636. {
  2637. LocalFree(pch);
  2638. goto LocalFailure2;
  2639. }
  2640. }
  2641. SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
  2642. LB_GETTEXT,
  2643. (WPARAM)(*(pSelIndex + i)),
  2644. (LONG_PTR)pch2 );
  2645. if (!StrChr(pch2, CHAR_DOT))
  2646. {
  2647. *(pch2 + len++) = CHAR_DOT;
  2648. }
  2649. pch2 += len;
  2650. *pch2++ = CHAR_SPACE;
  2651. }
  2652. if (pch2 != pch)
  2653. {
  2654. *--pch2 = CHAR_NULL;
  2655. }
  2656. SetDlgItemText(hDlg, edt1, pch);
  2657. LocalFree((HANDLE)pch);
  2658. LocalFailure2:
  2659. LocalFree((HANDLE)pSelIndex);
  2660. }
  2661. LocalFailure1:
  2662. if (pOFN->lpfnHook)
  2663. {
  2664. i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
  2665. LB_GETCARETINDEX,
  2666. 0,
  2667. 0L );
  2668. if (!(i & 0x8000))
  2669. {
  2670. wFlag = (SendMessage(
  2671. GET_WM_COMMAND_HWND(wParam, lParam),
  2672. LB_GETSEL,
  2673. (WPARAM)i,
  2674. 0L )
  2675. ? CD_LBSELADD
  2676. : CD_LBSELSUB);
  2677. }
  2678. else
  2679. {
  2680. wFlag = CD_LBSELNOITEMS;
  2681. }
  2682. }
  2683. }
  2684. else
  2685. {
  2686. //
  2687. // Multiselection is not allowed.
  2688. // Put the file name in the edit control.
  2689. //
  2690. szText[0] = CHAR_NULL;
  2691. i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
  2692. LB_GETCURSEL,
  2693. 0,
  2694. 0L );
  2695. if (i != (WORD)LB_ERR)
  2696. {
  2697. i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
  2698. LB_GETTEXT,
  2699. (WPARAM)i,
  2700. (LONG_PTR)szText );
  2701. if (!StrChr(szText, CHAR_DOT))
  2702. {
  2703. if (i < MAX_FULLPATHNAME - 1)
  2704. {
  2705. szText[i] = CHAR_DOT;
  2706. szText[i + 1] = CHAR_NULL;
  2707. }
  2708. }
  2709. if (!bCasePreserved)
  2710. {
  2711. CharLower(szText);
  2712. }
  2713. SetDlgItemText(hDlg, edt1, szText);
  2714. if (pOFN->lpfnHook)
  2715. {
  2716. i = (WORD)SendMessage(
  2717. GET_WM_COMMAND_HWND(wParam, lParam),
  2718. LB_GETCURSEL,
  2719. 0,
  2720. 0L );
  2721. wFlag = CD_LBSELCHANGE;
  2722. }
  2723. }
  2724. }
  2725. if (pOFN->lpfnHook)
  2726. {
  2727. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  2728. #ifdef UNICODE
  2729. if (pOFI->ApiType == COMDLG_ANSI)
  2730. {
  2731. (*lpfnHook)( hDlg,
  2732. msgLBCHANGEA,
  2733. lst1,
  2734. MAKELONG(i, wFlag) );
  2735. }
  2736. else
  2737. #endif
  2738. {
  2739. (*lpfnHook)( hDlg,
  2740. msgLBCHANGEW,
  2741. lst1,
  2742. MAKELONG(i, wFlag) );
  2743. }
  2744. }
  2745. SendDlgItemMessage(hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
  2746. return (TRUE);
  2747. }
  2748. break;
  2749. }
  2750. case ( cmb1 ) :
  2751. {
  2752. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  2753. {
  2754. case ( CBN_DROPDOWN ) :
  2755. {
  2756. if (wWinVer >= 0x030A)
  2757. {
  2758. pOFN->Flags |= OFN_FILTERDOWN;
  2759. }
  2760. return (TRUE);
  2761. break;
  2762. }
  2763. case ( CBN_CLOSEUP ) :
  2764. {
  2765. PostMessage( hDlg,
  2766. WM_COMMAND,
  2767. GET_WM_COMMAND_MPS(cmb1, lParam, MYCBN_DRAW) );
  2768. return (TRUE);
  2769. break;
  2770. }
  2771. case ( CBN_SELCHANGE ) :
  2772. {
  2773. //
  2774. // Need to change the file listing in lst1.
  2775. //
  2776. if (pOFN->Flags & OFN_FILTERDOWN)
  2777. {
  2778. return (TRUE);
  2779. break;
  2780. }
  2781. }
  2782. case ( MYCBN_DRAW ) :
  2783. {
  2784. SHORT nIndex;
  2785. LPCTSTR lpFilter;
  2786. HourGlass(TRUE);
  2787. pOFN->Flags &= ~OFN_FILTERDOWN;
  2788. ChangingFilter:
  2789. nIndex = (SHORT)SendDlgItemMessage( hDlg,
  2790. cmb1,
  2791. CB_GETCURSEL,
  2792. 0,
  2793. 0L );
  2794. if (nIndex < 0)
  2795. {
  2796. //
  2797. // No current selection.
  2798. //
  2799. break;
  2800. }
  2801. //
  2802. // Must also check if filter contains anything.
  2803. //
  2804. if (nIndex ||
  2805. !(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter))
  2806. {
  2807. lpFilter = pOFN->lpstrFilter +
  2808. SendDlgItemMessage( hDlg,
  2809. cmb1,
  2810. CB_GETITEMDATA,
  2811. (WPARAM)nIndex,
  2812. 0L );
  2813. }
  2814. else
  2815. {
  2816. lpFilter = pOFN->lpstrCustomFilter +
  2817. lstrlen(pOFN->lpstrCustomFilter) + 1;
  2818. }
  2819. if (*lpFilter)
  2820. {
  2821. GetDlgItemText( hDlg,
  2822. edt1,
  2823. szText,
  2824. MAX_FULLPATHNAME - 1 );
  2825. bRet = (!szText[0] ||
  2826. (StrChr(szText, CHAR_STAR)) ||
  2827. (StrChr(szText, CHAR_QMARK)));
  2828. lstrcpy(szText, lpFilter);
  2829. if (bRet)
  2830. {
  2831. CharLower(szText);
  2832. SetDlgItemText(hDlg, edt1, szText);
  2833. SendDlgItemMessage( hDlg,
  2834. edt1,
  2835. EM_SETSEL,
  2836. (WPARAM)0,
  2837. (LPARAM)-1 );
  2838. }
  2839. FListAll(pOFI, hDlg, szText);
  2840. if (!bInitializing)
  2841. {
  2842. lstrcpy(pOFI->szLastFilter, szText);
  2843. #ifdef WINNT
  2844. //
  2845. // Provide dynamic lpstrDefExt updating
  2846. // when lpstrDefExt is user initialized.
  2847. //
  2848. if (StrChr((LPTSTR)lpFilter, CHAR_DOT) &&
  2849. pOFN->lpstrDefExt)
  2850. {
  2851. DWORD cbLen = MIN_DEFEXT_LEN - 1; // only 1st 3
  2852. LPTSTR lpTemp = (LPTSTR)(pOFN->lpstrDefExt);
  2853. while (*lpFilter++ != CHAR_DOT);
  2854. if (!(StrChr((LPTSTR)lpFilter, CHAR_STAR)) &&
  2855. !(StrChr((LPTSTR)lpFilter, CHAR_QMARK)))
  2856. {
  2857. while (cbLen--)
  2858. {
  2859. *lpTemp++ = *lpFilter++;
  2860. }
  2861. *lpTemp = CHAR_NULL;
  2862. }
  2863. }
  2864. #endif
  2865. }
  2866. }
  2867. if (pOFN->lpfnHook)
  2868. {
  2869. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  2870. #ifdef UNICODE
  2871. if (pOFI->ApiType == COMDLG_ANSI)
  2872. {
  2873. (*lpfnHook)( hDlg,
  2874. msgLBCHANGEA,
  2875. cmb1,
  2876. MAKELONG(nIndex, CD_LBSELCHANGE) );
  2877. }
  2878. else
  2879. #endif
  2880. {
  2881. (*lpfnHook)( hDlg,
  2882. msgLBCHANGEW,
  2883. cmb1,
  2884. MAKELONG(nIndex, CD_LBSELCHANGE) );
  2885. }
  2886. }
  2887. HourGlass(FALSE);
  2888. return (TRUE);
  2889. break;
  2890. }
  2891. default :
  2892. {
  2893. break;
  2894. }
  2895. }
  2896. break;
  2897. }
  2898. case ( lst2 ) :
  2899. {
  2900. if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)
  2901. {
  2902. if (!(pOFN->Flags & OFN_DIRSELCHANGED))
  2903. {
  2904. if ((DWORD)SendDlgItemMessage( hDlg,
  2905. lst2,
  2906. LB_GETCURSEL,
  2907. 0,
  2908. 0L ) != pOFI->idirSub - 1)
  2909. {
  2910. StripFileName(hDlg, IS16BITWOWAPP(pOFN));
  2911. pOFN->Flags |= OFN_DIRSELCHANGED;
  2912. }
  2913. }
  2914. return (TRUE);
  2915. }
  2916. else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SETFOCUS)
  2917. {
  2918. EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
  2919. SendMessage( GetDlgItem(hDlg, IDCANCEL),
  2920. BM_SETSTYLE,
  2921. (WPARAM)BS_PUSHBUTTON,
  2922. (LPARAM)TRUE );
  2923. }
  2924. else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_KILLFOCUS)
  2925. {
  2926. if (pOFN && (pOFN->Flags & OFN_DIRSELCHANGED))
  2927. {
  2928. pOFN->Flags &= ~OFN_DIRSELCHANGED;
  2929. }
  2930. else
  2931. {
  2932. bChangeDir = FALSE;
  2933. }
  2934. }
  2935. else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_DBLCLK)
  2936. {
  2937. TCHAR szNextDir[CCHNETPATH];
  2938. LPTSTR lpCurDir;
  2939. DWORD idir;
  2940. DWORD idirNew;
  2941. int cb;
  2942. LPTSTR pstrPath;
  2943. ChangingDir:
  2944. bChangeDir = FALSE;
  2945. pOFN->Flags &= ~OFN_DIRSELCHANGED;
  2946. idirNew = (DWORD)SendDlgItemMessage( hDlg,
  2947. lst2,
  2948. LB_GETCURSEL,
  2949. 0,
  2950. 0L );
  2951. //
  2952. // Can use relative path name.
  2953. //
  2954. *pOFI->szPath = 0;
  2955. if (idirNew >= pOFI->idirSub)
  2956. {
  2957. cb = (int) SendDlgItemMessage( hDlg,
  2958. lst2,
  2959. LB_GETTEXT,
  2960. (WPARAM)idirNew,
  2961. (LPARAM)pOFI->szPath );
  2962. //
  2963. // sanity check
  2964. //
  2965. if (!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
  2966. !(lpCurDir = lpCurDlg->lpstrCurDir))
  2967. {
  2968. break;
  2969. }
  2970. lstrcpy(szNextDir, lpCurDir);
  2971. //
  2972. // Fix phenom with c:\\foobar - because of inconsistency
  2973. // in directory display guaranteed to have a valid
  2974. // lpCurDir here, right?
  2975. //
  2976. PathAddBackslash(szNextDir);
  2977. lstrcat(szNextDir, pOFI->szPath);
  2978. pstrPath = szNextDir;
  2979. idirNew = pOFI->idirSub; // for msgLBCHANGE message
  2980. }
  2981. else
  2982. {
  2983. //
  2984. // Need full path name.
  2985. //
  2986. cb = (int) SendDlgItemMessage( hDlg,
  2987. lst2,
  2988. LB_GETTEXT,
  2989. 0,
  2990. (LPARAM)pOFI->szPath );
  2991. //
  2992. // The following condition is necessary because wb displays
  2993. // \\server\share (the disk resource name) for unc, but
  2994. // for root paths (eg. c:\) for device conns, this in-
  2995. // consistency is hacked around here and in FillOutPath.
  2996. //
  2997. if (DBL_BSLASH(pOFI->szPath))
  2998. {
  2999. lstrcat(pOFI->szPath, TEXT("\\"));
  3000. cb++;
  3001. }
  3002. for (idir = 1; idir <= idirNew; ++idir)
  3003. {
  3004. cb += (int) SendDlgItemMessage(
  3005. hDlg,
  3006. lst2,
  3007. LB_GETTEXT,
  3008. (WPARAM)idir,
  3009. (LPARAM)&pOFI->szPath[cb] );
  3010. pOFI->szPath[cb++] = CHAR_BSLASH;
  3011. }
  3012. //
  3013. // The root is a special case.
  3014. //
  3015. if (idirNew)
  3016. {
  3017. pOFI->szPath[cb - 1] = CHAR_NULL;
  3018. }
  3019. pstrPath = pOFI->szPath;
  3020. }
  3021. if (!*pstrPath ||
  3022. (ChangeDir(hDlg, pstrPath, FALSE, TRUE) == CHANGEDIR_FAILED))
  3023. {
  3024. break;
  3025. }
  3026. //
  3027. // List all directories under this one.
  3028. //
  3029. UpdateListBoxes(hDlg, pOFI, NULL, mskDirectory);
  3030. if (pOFN->lpfnHook)
  3031. {
  3032. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  3033. #ifdef UNICODE
  3034. if (pOFI->ApiType == COMDLG_ANSI)
  3035. {
  3036. (*lpfnHook)( hDlg,
  3037. msgLBCHANGEA,
  3038. lst2,
  3039. MAKELONG(LOWORD(idirNew), CD_LBSELCHANGE) );
  3040. }
  3041. else
  3042. #endif
  3043. {
  3044. (*lpfnHook)( hDlg,
  3045. msgLBCHANGEW,
  3046. lst2,
  3047. MAKELONG(LOWORD(idirNew), CD_LBSELCHANGE) );
  3048. }
  3049. }
  3050. return (TRUE);
  3051. }
  3052. break;
  3053. }
  3054. case ( cmb2 ) :
  3055. {
  3056. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  3057. {
  3058. case ( CBN_DROPDOWN ) :
  3059. {
  3060. pOFN->Flags |= OFN_DRIVEDOWN;
  3061. return (TRUE);
  3062. break;
  3063. }
  3064. case ( CBN_CLOSEUP ) :
  3065. {
  3066. //
  3067. // It would seem reasonable to merely do the update
  3068. // at this point, but that would rely on message
  3069. // ordering, which isnt a smart move. In fact, if
  3070. // you hit ALT-DOWNARROW, DOWNARROW, ALT-DOWNARROW,
  3071. // you receive CBN_DROPDOWN, CBN_SELCHANGE, and then
  3072. // CBN_CLOSEUP. But if you use the mouse to choose
  3073. // the same element, the last two messages trade
  3074. // places. PostMessage allows all messages in the
  3075. // sequence to be processed, and then updates are
  3076. // done as needed.
  3077. //
  3078. PostMessage( hDlg,
  3079. WM_COMMAND,
  3080. GET_WM_COMMAND_MPS(
  3081. cmb2,
  3082. GET_WM_COMMAND_HWND(wParam, lParam),
  3083. MYCBN_DRAW ) );
  3084. return (TRUE);
  3085. break;
  3086. }
  3087. case ( MYCBN_LIST ) :
  3088. {
  3089. LoadDrives(hDlg);
  3090. break;
  3091. }
  3092. case ( MYCBN_REPAINT ) :
  3093. {
  3094. int cchCurDir;
  3095. LPTSTR lpCurDir;
  3096. // sanity
  3097. if (!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
  3098. !(lpCurDir = lpCurDlg->lpstrCurDir))
  3099. {
  3100. break;
  3101. }
  3102. cchCurDir = GetPathOffset(lpCurDir);
  3103. if (cchCurDir != -1)
  3104. {
  3105. TCHAR szRepaintDir[CCHNETPATH];
  3106. HWND hCmb2 = (HWND)lParam;
  3107. lstrcpy(szRepaintDir, lpCurDir);
  3108. szRepaintDir[cchCurDir] = CHAR_NULL;
  3109. SendMessage( hCmb2,
  3110. CB_SELECTSTRING,
  3111. (WPARAM)-1,
  3112. (LPARAM)szRepaintDir );
  3113. }
  3114. break;
  3115. }
  3116. case ( CBN_SELCHANGE ) :
  3117. {
  3118. StripFileName(hDlg, IS16BITWOWAPP(pOFN));
  3119. //
  3120. // Version check not needed, since flag never set
  3121. // for versions not supporting CBN_CLOSEUP. Putting
  3122. // check at CBN_DROPDOWN is more efficient since it
  3123. // is less frequent than CBN_SELCHANGE.
  3124. if (pOFN->Flags & OFN_DRIVEDOWN)
  3125. {
  3126. //
  3127. // Don't fill lst2 while the combobox is down.
  3128. //
  3129. return (TRUE);
  3130. break;
  3131. }
  3132. }
  3133. case ( MYCBN_CHANGEDIR ) :
  3134. case ( MYCBN_DRAW ) :
  3135. {
  3136. TCHAR szTitle[WARNINGMSGLENGTH];
  3137. LPTSTR lpFilter;
  3138. int nDiskInd, nInd;
  3139. DWORD dwType = 0;
  3140. LPTSTR lpszPath = NULL;
  3141. LPTSTR lpszDisk = NULL;
  3142. HWND hCmb2;
  3143. OFN_DISKINFO *pofndiDisk = NULL;
  3144. static szDrawDir[CCHNETPATH];
  3145. int nRet;
  3146. HourGlass(TRUE);
  3147. //
  3148. // Clear Flag for future CBN_SELCHANGE messeges.
  3149. //
  3150. pOFN->Flags &= ~OFN_DRIVEDOWN;
  3151. //
  3152. // Change the drive.
  3153. //
  3154. szText[0] = CHAR_NULL;
  3155. hCmb2 = (HWND)lParam;
  3156. if (hCmb2 != NULL)
  3157. {
  3158. nInd = (int) SendMessage(hCmb2, CB_GETCURSEL, 0, 0L);
  3159. if (nInd != CB_ERR)
  3160. {
  3161. SendMessage( hCmb2,
  3162. CB_GETLBTEXT,
  3163. nInd,
  3164. (LPARAM)(LPTSTR)szDrawDir );
  3165. }
  3166. if ((nInd == CB_ERR) || ((INT_PTR)pofndiDisk == CB_ERR))
  3167. {
  3168. if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
  3169. {
  3170. if (lpCurDlg->lpstrCurDir)
  3171. {
  3172. lstrcpy((LPTSTR)szDrawDir,
  3173. lpCurDlg->lpstrCurDir);
  3174. }
  3175. }
  3176. }
  3177. CharLower((LPTSTR)szDrawDir);
  3178. //
  3179. // Should always succeed.
  3180. //
  3181. nDiskInd = DiskAddedPreviously(0, (LPTSTR)szDrawDir);
  3182. if (nDiskInd != 0xFFFFFFFF)
  3183. {
  3184. pofndiDisk = &gaDiskInfo[nDiskInd];
  3185. }
  3186. else
  3187. {
  3188. //
  3189. // Skip update in the case where it fails.
  3190. //
  3191. return (TRUE);
  3192. }
  3193. dwType = pofndiDisk->dwType;
  3194. lpszDisk = pofndiDisk->lpPath;
  3195. }
  3196. if ((GET_WM_COMMAND_CMD(wParam, lParam)) == MYCBN_CHANGEDIR)
  3197. {
  3198. if (lpNetDriveSync)
  3199. {
  3200. lpszPath = lpNetDriveSync;
  3201. lpNetDriveSync = NULL;
  3202. }
  3203. else
  3204. {
  3205. if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
  3206. {
  3207. if (lpCurDlg->lpstrCurDir)
  3208. {
  3209. lstrcpy((LPTSTR)szDrawDir,
  3210. lpCurDlg->lpstrCurDir);
  3211. lpszPath = (LPTSTR)szDrawDir;
  3212. }
  3213. }
  3214. }
  3215. }
  3216. else
  3217. {
  3218. lpszPath = lpszDisk;
  3219. }
  3220. if (bInitializing)
  3221. {
  3222. lpFilter = szTitle;
  3223. if (pOFN->lpstrFile &&
  3224. (StrChr(pOFN->lpstrFile, CHAR_STAR) ||
  3225. StrChr(pOFN->lpstrFile, CHAR_QMARK)))
  3226. {
  3227. lstrcpy(lpFilter, pOFN->lpstrFile);
  3228. }
  3229. else
  3230. {
  3231. HWND hcmb1 = GetDlgItem(hDlg, cmb1);
  3232. nInd = (int) SendMessage(hcmb1, CB_GETCURSEL, 0, 0L);
  3233. if (nInd == CB_ERR)
  3234. {
  3235. //
  3236. // No current selection.
  3237. //
  3238. goto NullSearch;
  3239. }
  3240. //
  3241. // Must also check if filter contains anything.
  3242. //
  3243. if (nInd ||
  3244. !(pOFN->lpstrCustomFilter &&
  3245. *pOFN->lpstrCustomFilter))
  3246. {
  3247. lpFilter = (LPTSTR)(pOFN->lpstrFilter);
  3248. lpFilter += SendMessage( hcmb1,
  3249. CB_GETITEMDATA,
  3250. (WPARAM)nInd,
  3251. 0 );
  3252. }
  3253. else
  3254. {
  3255. lpFilter = pOFN->lpstrCustomFilter;
  3256. lpFilter += lstrlen(pOFN->lpstrCustomFilter) + 1;
  3257. }
  3258. }
  3259. }
  3260. else
  3261. {
  3262. NullSearch:
  3263. lpFilter = NULL;
  3264. }
  3265. //
  3266. // UpdateListBoxes cuts up filter string in place.
  3267. //
  3268. if (lpFilter)
  3269. {
  3270. lstrcpy(szTitle, lpFilter);
  3271. CharLower(szTitle);
  3272. }
  3273. if (dwType == REMDRVBMP)
  3274. {
  3275. DWORD err = WNetRestoreConnection(hDlg, lpszDisk);
  3276. if (err != WN_SUCCESS)
  3277. {
  3278. HourGlass(FALSE);
  3279. return (TRUE);
  3280. }
  3281. pofndiDisk->dwType = NETDRVBMP;
  3282. SendMessage(
  3283. hCmb2,
  3284. CB_SETITEMDATA,
  3285. (WPARAM)SendMessage(
  3286. hCmb2,
  3287. CB_SELECTSTRING,
  3288. (WPARAM)-1,
  3289. (LPARAM)(LPTSTR)pofndiDisk->lpAbbrName ),
  3290. (LPARAM)NETDRVBMP );
  3291. }
  3292. //
  3293. // Calls to ChangeDir will call SelDisk, so no need
  3294. // to update cmb2 on our own here (used to be after
  3295. // updatelistboxes).
  3296. //
  3297. if ((nRet = ChangeDir( hDlg,
  3298. lpszPath,
  3299. FALSE,
  3300. FALSE )) == CHANGEDIR_FAILED)
  3301. {
  3302. int mbRet;
  3303. while (nRet == CHANGEDIR_FAILED)
  3304. {
  3305. if (dwType == FLOPPYBMP)
  3306. {
  3307. mbRet = InvalidFileWarning(
  3308. hDlg,
  3309. lpszPath,
  3310. ERROR_NO_DISK_IN_DRIVE,
  3311. (UINT)(MB_RETRYCANCEL |
  3312. MB_ICONEXCLAMATION));
  3313. }
  3314. else if (dwType == CDDRVBMP)
  3315. {
  3316. mbRet = InvalidFileWarning(
  3317. hDlg,
  3318. lpszPath,
  3319. ERROR_NO_DISK_IN_CDROM,
  3320. (UINT)(MB_RETRYCANCEL |
  3321. MB_ICONEXCLAMATION) );
  3322. }
  3323. else
  3324. {
  3325. //
  3326. // See if it's a RAW volume.
  3327. //
  3328. if (dwType == HARDDRVBMP &&
  3329. GetLastError() == ERROR_UNRECOGNIZED_VOLUME)
  3330. {
  3331. mbRet = InvalidFileWarning(
  3332. hDlg,
  3333. lpszPath,
  3334. ERROR_UNRECOGNIZED_VOLUME,
  3335. (UINT)(MB_OK |
  3336. MB_ICONEXCLAMATION) );
  3337. }
  3338. else
  3339. {
  3340. mbRet = InvalidFileWarning(
  3341. hDlg,
  3342. lpszPath,
  3343. ERROR_DIR_ACCESS_DENIED,
  3344. (UINT)(MB_RETRYCANCEL |
  3345. MB_ICONEXCLAMATION) );
  3346. }
  3347. }
  3348. if (bFirstTime || (mbRet != IDRETRY))
  3349. {
  3350. lpszPath = NULL;
  3351. nRet = ChangeDir(hDlg, lpszPath, TRUE, FALSE);
  3352. }
  3353. else
  3354. {
  3355. nRet = ChangeDir(hDlg, lpszPath, FALSE, FALSE);
  3356. }
  3357. }
  3358. }
  3359. UpdateListBoxes( hDlg,
  3360. pOFI,
  3361. lpFilter ? szTitle : lpFilter,
  3362. (WORD)(mskDrives | mskDirectory) );
  3363. if (pOFN->lpfnHook)
  3364. {
  3365. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  3366. nInd = (int) SendDlgItemMessage( hDlg,
  3367. cmb2,
  3368. CB_GETCURSEL,
  3369. 0,
  3370. 0 );
  3371. #ifdef UNICODE
  3372. if (pOFI->ApiType == COMDLG_ANSI)
  3373. {
  3374. (*lpfnHook)( hDlg,
  3375. msgLBCHANGEA,
  3376. cmb2,
  3377. MAKELONG(LOWORD(nInd),
  3378. CD_LBSELCHANGE) );
  3379. }
  3380. else
  3381. #endif
  3382. {
  3383. (*lpfnHook)( hDlg,
  3384. msgLBCHANGEW,
  3385. cmb2,
  3386. MAKELONG(LOWORD(nInd),
  3387. CD_LBSELCHANGE) );
  3388. }
  3389. }
  3390. HourGlass(FALSE);
  3391. return (TRUE);
  3392. break;
  3393. }
  3394. default :
  3395. {
  3396. break;
  3397. }
  3398. }
  3399. break;
  3400. }
  3401. case ( pshHelp ) :
  3402. {
  3403. #ifdef UNICODE
  3404. if (pOFI->ApiType == COMDLG_ANSI)
  3405. {
  3406. if (msgHELPA && pOFN->hwndOwner)
  3407. {
  3408. SendMessage( pOFN->hwndOwner,
  3409. msgHELPA,
  3410. (WPARAM)hDlg,
  3411. (DWORD_PTR)pOFN );
  3412. }
  3413. }
  3414. else
  3415. #endif
  3416. {
  3417. if (msgHELPW && pOFN->hwndOwner)
  3418. {
  3419. SendMessage( pOFN->hwndOwner,
  3420. msgHELPW,
  3421. (WPARAM)hDlg,
  3422. (DWORD_PTR)pOFN );
  3423. }
  3424. }
  3425. break;
  3426. }
  3427. case ( psh14 ) :
  3428. {
  3429. bGetNetDrivesSync = TRUE;
  3430. if (CallNetDlg(hDlg))
  3431. {
  3432. LNDSetEvent(hDlg);
  3433. }
  3434. else
  3435. {
  3436. bGetNetDrivesSync = FALSE;
  3437. }
  3438. break;
  3439. }
  3440. default :
  3441. {
  3442. break;
  3443. }
  3444. }
  3445. return (FALSE);
  3446. }
  3447. ////////////////////////////////////////////////////////////////////////////
  3448. //
  3449. // UpdateListBoxes
  3450. //
  3451. // Fills out File and Directory List Boxes in a single pass
  3452. // given (potentially) multiple filters
  3453. //
  3454. // It assumes the string of extensions are delimited by semicolons.
  3455. //
  3456. // hDlg Handle to File Open/Save dialog
  3457. // pOFI pointer to OPENFILEINFO structure
  3458. // lpszFilter pointer to filter, if NULL, use pOFI->szSpecCur
  3459. // wMask mskDirectory and/or mskDrives, or NULL
  3460. //
  3461. // Returns: TRUE if match
  3462. // FALSE if not
  3463. //
  3464. ////////////////////////////////////////////////////////////////////////////
  3465. BOOL UpdateListBoxes(
  3466. HWND hDlg,
  3467. POPENFILEINFO pOFI,
  3468. LPTSTR lpszFilter,
  3469. WORD wMask)
  3470. {
  3471. LPTSTR lpszF[MAXFILTERS + 1];
  3472. LPTSTR lpszTemp;
  3473. SHORT i, nFilters;
  3474. HWND hFileList = GetDlgItem(hDlg, lst1);
  3475. HWND hDirList = GetDlgItem(hDlg, lst2);
  3476. BOOL bRet = FALSE;
  3477. TCHAR szSpec[MAX_FULLPATHNAME];
  3478. BOOL bDriveChange;
  3479. BOOL bFindAll = FALSE;
  3480. RECT rDirLBox;
  3481. BOOL bLFN;
  3482. HANDLE hff;
  3483. DWORD dwErr;
  3484. WIN32_FIND_DATA FindFileData;
  3485. TCHAR szBuffer[MAX_FULLPATHNAME]; // add one for CHAR_DOT
  3486. WORD wCount;
  3487. LPCURDLG lpCurDlg;
  3488. //
  3489. // Save the drive bit and then clear it out.
  3490. //
  3491. bDriveChange = wMask & mskDrives;
  3492. wMask &= ~mskDrives;
  3493. if (!lpszFilter)
  3494. {
  3495. GetDlgItemText( hDlg,
  3496. edt1,
  3497. lpszFilter = szSpec,
  3498. MAX_FULLPATHNAME - 1 );
  3499. //
  3500. // If any directory or drive characters are in there, or if there
  3501. // are no wildcards, use the default spec.
  3502. //
  3503. if ( StrChr(szSpec, CHAR_BSLASH) ||
  3504. StrChr(szSpec, CHAR_SLASH) ||
  3505. StrChr(szSpec, CHAR_COLON) ||
  3506. (!((StrChr(szSpec, CHAR_STAR)) ||
  3507. (StrChr(szSpec, CHAR_QMARK)))) )
  3508. {
  3509. lstrcpy(szSpec, pOFI->szSpecCur);
  3510. }
  3511. else
  3512. {
  3513. lstrcpy(pOFI->szLastFilter, szSpec);
  3514. }
  3515. }
  3516. //
  3517. // We need to find out what kind of a drive we are running
  3518. // on in order to determine if spaces are valid in a filename
  3519. // or not.
  3520. //
  3521. bLFN = IsLFNDriveX(hDlg, TEXT("\0"));
  3522. //
  3523. // Find the first filter in the string, and add it to the
  3524. // array.
  3525. //
  3526. if (bLFN)
  3527. {
  3528. lpszF[nFilters = 0] = lstrtok(lpszFilter, szSemiColonTab);
  3529. }
  3530. else
  3531. {
  3532. lpszF[nFilters = 0] = lstrtok(lpszFilter, szSemiColonSpaceTab);
  3533. }
  3534. //
  3535. // Now we are going to loop through all the filters in the string
  3536. // parsing the one we already have, and then finding the next one
  3537. // and starting the loop over again.
  3538. //
  3539. while (lpszF[nFilters] && (nFilters < MAXFILTERS))
  3540. {
  3541. //
  3542. // Check to see if the first character is a space.
  3543. // If so, remove the spaces, and save the pointer
  3544. // back into the same spot. Why? because the
  3545. // FindFirstFile/Next api will _still_ work on
  3546. // filenames that begin with a space because
  3547. // they also look at the short names. The
  3548. // short names will begin with the same first
  3549. // real letter as the long filename. For
  3550. // example, the long filename is " my document"
  3551. // the first letter of this short name is "m",
  3552. // so searching on "m*.*" or " m*.*" will yield
  3553. // the same results.
  3554. //
  3555. if (bLFN && (*lpszF[nFilters] == CHAR_SPACE))
  3556. {
  3557. lpszTemp = lpszF[nFilters];
  3558. while ((*lpszTemp == CHAR_SPACE) && *lpszTemp)
  3559. {
  3560. lpszTemp = CharNext(lpszTemp);
  3561. }
  3562. lpszF[nFilters] = lpszTemp;
  3563. }
  3564. //
  3565. // The original code used to do a CharUpper here to put the
  3566. // filter strings in upper case. EG: *.TXT However, this
  3567. // is not a good thing to do for Turkish. Capital 'i' does
  3568. // not equal 'I', so the CharUpper is being removed.
  3569. //
  3570. // CharUpper(lpszF[nFilters]);
  3571. //
  3572. // Compare the filter with *.*. If we find *.* then
  3573. // set the boolean bFindAll, and this will cause the
  3574. // files listbox to be filled in at the same time the
  3575. // directories listbox is filled. This saves time
  3576. // from walking the directory twice (once for the directory
  3577. // names and once for the filenames).
  3578. //
  3579. if (!lstrcmpi(lpszF[nFilters], szStarDotStar))
  3580. {
  3581. bFindAll = TRUE;
  3582. }
  3583. //
  3584. // Now we need to check if this filter is a duplicate
  3585. // of an already existing filter.
  3586. //
  3587. for (wCount = 0; wCount < nFilters; wCount++)
  3588. {
  3589. //
  3590. // If we find a duplicate, decrement the current
  3591. // index pointer by one so that the last location
  3592. // is written over (thus removing the duplicate),
  3593. // and break out of this loop.
  3594. //
  3595. if (!lstrcmpi(lpszF[nFilters], lpszF[wCount]))
  3596. {
  3597. nFilters--;
  3598. break;
  3599. }
  3600. }
  3601. //
  3602. // Ready to move on to the next filter. Find the next
  3603. // filter based upon the type of file system we're using.
  3604. //
  3605. if (bLFN)
  3606. {
  3607. lpszF[++nFilters] = lstrtok(NULL, szSemiColonTab);
  3608. }
  3609. else
  3610. {
  3611. lpszF[++nFilters] = lstrtok(NULL, szSemiColonSpaceTab);
  3612. }
  3613. //
  3614. // In case we found a pointer to NULL, then look for the
  3615. // next filter.
  3616. //
  3617. while (lpszF[nFilters] && !*lpszF[nFilters])
  3618. {
  3619. if (bLFN)
  3620. {
  3621. lpszF[nFilters] = lstrtok(NULL, szSemiColonTab);
  3622. }
  3623. else
  3624. {
  3625. lpszF[nFilters] = lstrtok(NULL, szSemiColonSpaceTab);
  3626. }
  3627. }
  3628. }
  3629. //
  3630. // Add NULL terminator only if needed.
  3631. //
  3632. if (nFilters >= MAXFILTERS)
  3633. {
  3634. lpszF[MAXFILTERS] = 0;
  3635. }
  3636. HourGlass(TRUE);
  3637. SendMessage(hFileList, WM_SETREDRAW, FALSE, 0L);
  3638. SendMessage(hFileList, LB_RESETCONTENT, 0, 0L);
  3639. if (wMask & mskDirectory)
  3640. {
  3641. wNoRedraw |= 2; // HACK!!! WM_SETREDRAW isn't complete
  3642. SendMessage(hDirList, WM_SETREDRAW, FALSE, 0L);
  3643. //
  3644. // LB_RESETCONTENT causes InvalidateRect(hDirList, 0, TRUE) to be
  3645. // sent as well as repositioning the scrollbar thumb and drawing
  3646. // it immediately. This causes flicker when the LB_SETCURSEL is
  3647. // made, as it clears out the listbox by erasing the background of
  3648. // each item.
  3649. //
  3650. SendMessage(hDirList, LB_RESETCONTENT, 0, 0L);
  3651. }
  3652. //
  3653. // Always open enumeration for *.*
  3654. //
  3655. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  3656. SetCurrentDirectory(lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
  3657. hff = FindFirstFile(szStarDotStar, &FindFileData);
  3658. if ( hff == INVALID_HANDLE_VALUE)
  3659. {
  3660. //
  3661. // Error. Call GetLastError to determine what happened.
  3662. //
  3663. dwErr = GetLastError();
  3664. //
  3665. // With the ChangeDir logic handling AccessDenied for cds,
  3666. // if we are not allowed to enum files, that's ok, just get out.
  3667. //
  3668. if (dwErr == ERROR_ACCESS_DENIED)
  3669. {
  3670. wMask = mskDirectory;
  3671. goto Func4EFailure;
  3672. }
  3673. //
  3674. // For bad path of bad filename.
  3675. //
  3676. if (dwErr != ERROR_FILE_NOT_FOUND)
  3677. {
  3678. wMask = mskDrives;
  3679. goto Func4EFailure;
  3680. }
  3681. }
  3682. //
  3683. // A listing was made, even if empty.
  3684. //
  3685. bRet = TRUE;
  3686. wMask &= mskDirectory;
  3687. //
  3688. // GetLastError says no more files.
  3689. //
  3690. if (hff == INVALID_HANDLE_VALUE && dwErr == ERROR_FILE_NOT_FOUND)
  3691. {
  3692. //
  3693. // Things went well, but there are no files.
  3694. //
  3695. goto NoMoreFilesFound;
  3696. }
  3697. do
  3698. {
  3699. if (pOFI->pOFN->Flags & OFN_NOLONGNAMES)
  3700. {
  3701. #ifdef UNICODE
  3702. UNICODE_STRING Name;
  3703. BOOLEAN fSpace = FALSE;
  3704. RtlInitUnicodeString(&Name, FindFileData.cFileName);
  3705. if (RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace)
  3706. {
  3707. //
  3708. // Legal 8.3 name and no spaces, so use the principal
  3709. // file name.
  3710. //
  3711. lstrcpy(szBuffer, (LPTSTR)FindFileData.cFileName);
  3712. }
  3713. else
  3714. #endif
  3715. {
  3716. #ifdef WINNT
  3717. if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
  3718. {
  3719. continue;
  3720. }
  3721. //
  3722. // Use the alternate file name.
  3723. //
  3724. lstrcpy(szBuffer, (LPTSTR)FindFileData.cAlternateFileName);
  3725. #else
  3726. if (FindFileData.cAlternateFileName[0])
  3727. {
  3728. //
  3729. // Use the alternate file name.
  3730. //
  3731. lstrcpy(szBuffer, (LPTSTR)FindFileData.cAlternateFileName);
  3732. }
  3733. else
  3734. {
  3735. //
  3736. // Use the main file name.
  3737. //
  3738. lstrcpy(szBuffer, (LPTSTR)FindFileData.cFileName);
  3739. }
  3740. #endif
  3741. }
  3742. }
  3743. else
  3744. {
  3745. lstrcpy(szBuffer, (LPTSTR)FindFileData.cFileName);
  3746. }
  3747. if ((FindFileData.dwFileAttributes & EXCLBITS))
  3748. {
  3749. continue;
  3750. }
  3751. if ((pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT))
  3752. {
  3753. if (StrChr(szBuffer, CHAR_SPACE))
  3754. {
  3755. //
  3756. // HPFS does not support alternate filenames
  3757. // for multiselect, bump all spacey filenames.
  3758. //
  3759. if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
  3760. {
  3761. continue;
  3762. }
  3763. lstrcpy(szBuffer, (LPTSTR)FindFileData.cAlternateFileName);
  3764. }
  3765. }
  3766. if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  3767. {
  3768. if (wMask & mskDirectory)
  3769. {
  3770. //
  3771. // Don't include the subdirectories "." and "..".
  3772. //
  3773. if (szBuffer[0] == CHAR_DOT)
  3774. {
  3775. if ((szBuffer[1] == CHAR_NULL) ||
  3776. ((szBuffer[1] == CHAR_DOT) && (szBuffer[2] == CHAR_NULL)))
  3777. {
  3778. continue;
  3779. }
  3780. }
  3781. if (!bCasePreserved)
  3782. {
  3783. CharLower(szBuffer);
  3784. }
  3785. i = (WORD)SendMessage( hDirList,
  3786. LB_ADDSTRING,
  3787. 0,
  3788. (DWORD_PTR)szBuffer );
  3789. }
  3790. }
  3791. else if (bFindAll)
  3792. {
  3793. if (!bCasePreserved)
  3794. {
  3795. CharLower(szBuffer);
  3796. }
  3797. SendMessage(hFileList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer);
  3798. }
  3799. } while (FindNextFile(hff, &FindFileData));
  3800. if (hff == INVALID_HANDLE_VALUE)
  3801. {
  3802. goto Func4EFailure;
  3803. }
  3804. FindClose(hff);
  3805. if (!bFindAll)
  3806. {
  3807. for (i = 0; lpszF[i]; i++)
  3808. {
  3809. if (!lstrcmpi(lpszF[i], szStarDotStar))
  3810. {
  3811. continue;
  3812. }
  3813. //
  3814. // Find First for each filter.
  3815. //
  3816. hff = FindFirstFile(lpszF[i], &FindFileData);
  3817. if (hff == INVALID_HANDLE_VALUE)
  3818. {
  3819. DWORD dwErr = GetLastError();
  3820. if ((dwErr == ERROR_FILE_NOT_FOUND) ||
  3821. (dwErr == ERROR_INVALID_NAME))
  3822. {
  3823. //
  3824. // Things went well, but there are no files.
  3825. //
  3826. continue;
  3827. }
  3828. else
  3829. {
  3830. wMask = mskDrives;
  3831. goto Func4EFailure;
  3832. }
  3833. }
  3834. do
  3835. {
  3836. if (pOFI->pOFN->Flags & OFN_NOLONGNAMES)
  3837. {
  3838. #ifdef UNICODE
  3839. UNICODE_STRING Name;
  3840. BOOLEAN fSpace = FALSE;
  3841. RtlInitUnicodeString(&Name, FindFileData.cFileName);
  3842. if (RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace)
  3843. {
  3844. //
  3845. // Legal 8.3 name and no spaces, so use the principal
  3846. // file name.
  3847. //
  3848. lstrcpy(szBuffer, (LPTSTR)FindFileData.cFileName);
  3849. }
  3850. else
  3851. #endif
  3852. {
  3853. #ifdef WINNT
  3854. if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
  3855. {
  3856. continue;
  3857. }
  3858. //
  3859. // Use the alternate file name.
  3860. //
  3861. lstrcpy( szBuffer,
  3862. (LPTSTR)FindFileData.cAlternateFileName );
  3863. #else
  3864. if (FindFileData.cAlternateFileName[0])
  3865. {
  3866. //
  3867. // Use the alternate file name.
  3868. //
  3869. lstrcpy( szBuffer,
  3870. (LPTSTR)FindFileData.cAlternateFileName );
  3871. }
  3872. else
  3873. {
  3874. //
  3875. // Use the main file name.
  3876. //
  3877. lstrcpy(szBuffer, (LPTSTR)FindFileData.cFileName);
  3878. }
  3879. #endif
  3880. }
  3881. }
  3882. else
  3883. {
  3884. lstrcpy(szBuffer, (LPTSTR)FindFileData.cFileName);
  3885. if (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT)
  3886. {
  3887. if (StrChr(szBuffer, CHAR_SPACE))
  3888. {
  3889. //
  3890. // HPFS does not support alternate filenames
  3891. // for multiselect, bump all spacey filenames.
  3892. //
  3893. if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
  3894. {
  3895. continue;
  3896. }
  3897. lstrcpy( szBuffer,
  3898. (LPTSTR)FindFileData.cAlternateFileName );
  3899. }
  3900. }
  3901. }
  3902. if ((FindFileData.dwFileAttributes & EXCLBITS) ||
  3903. (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  3904. {
  3905. continue;
  3906. }
  3907. if (!bCasePreserved)
  3908. {
  3909. CharLower(szBuffer);
  3910. }
  3911. SendMessage(hFileList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer);
  3912. } while (FindNextFile(hff, &FindFileData));
  3913. if (hff != INVALID_HANDLE_VALUE)
  3914. {
  3915. FindClose(hff);
  3916. }
  3917. }
  3918. }
  3919. NoMoreFilesFound:
  3920. Func4EFailure:
  3921. if (wMask)
  3922. {
  3923. if (wMask == mskDirectory)
  3924. {
  3925. LPTSTR lpCurDir = NULL;
  3926. if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
  3927. {
  3928. lpCurDir = lpCurDlg->lpstrCurDir;
  3929. }
  3930. FillOutPath(hDirList, pOFI);
  3931. //
  3932. // The win31 way of chopping the text by just passing
  3933. // it on to user doesn't work for unc names since user
  3934. // doesn't see the drivelessness of them (thinks drive is
  3935. // a bslash char). So, special case it here.
  3936. //
  3937. lstrcpy(pOFI->szPath, lpCurDir);
  3938. if (DBL_BSLASH(pOFI->szPath))
  3939. {
  3940. SetDlgItemText(hDlg, stc1, ChopText(hDlg, stc1, pOFI->szPath));
  3941. }
  3942. else
  3943. {
  3944. DlgDirList(hDlg, pOFI->szPath, 0, stc1, DDL_READONLY);
  3945. }
  3946. SendMessage(hDirList, LB_SETCURSEL, pOFI->idirSub - 1, 0L);
  3947. if (bDriveChange)
  3948. {
  3949. //
  3950. // The design here is to show the selected drive whenever the
  3951. // user changes drives, or whenever the number of
  3952. // subdirectories is sufficiently low to allow them to be
  3953. // shown along with the drive. Otherwise, show the
  3954. // immediate parent and all the children that can be shown.
  3955. // This all was done to meet the UITF spec.
  3956. //
  3957. i = 0;
  3958. }
  3959. else
  3960. {
  3961. //
  3962. // Show as many children as possible.
  3963. //
  3964. if ((i = (SHORT)(pOFI->idirSub - 2)) < 0)
  3965. {
  3966. i = 0;
  3967. }
  3968. }
  3969. //
  3970. // LB_SETTOPINDEX must be after LB_SETCURSEL, as LB_SETCURSEL will
  3971. // alter the top index to bring the current selection into view.
  3972. //
  3973. SendMessage(hDirList, LB_SETTOPINDEX, (WPARAM)i, 0L);
  3974. }
  3975. else
  3976. {
  3977. SetDlgItemText(hDlg, stc1, szNull);
  3978. }
  3979. wNoRedraw &= ~2;
  3980. SendMessage(hDirList, WM_SETREDRAW, TRUE, 0L);
  3981. GetWindowRect(hDirList, (LPRECT)&rDirLBox);
  3982. rDirLBox.left++, rDirLBox.top++;
  3983. rDirLBox.right--, rDirLBox.bottom--;
  3984. MapWindowPoints(NULL, hDlg, (LPPOINT)&rDirLBox, 2);
  3985. //
  3986. // If there are less than enough directories to fill the listbox,
  3987. // Win 3.0 doesn't clear out the bottom. Pass TRUE as the last
  3988. // parameter to demand a WM_ERASEBACKGROUND message.
  3989. //
  3990. InvalidateRect(hDlg, (LPRECT)&rDirLBox, (BOOL)(wWinVer < 0x030A));
  3991. }
  3992. SendMessage(hFileList, WM_SETREDRAW, TRUE, 0L);
  3993. InvalidateRect(hFileList, (LPRECT)0, (BOOL)TRUE);
  3994. #ifndef WIN32
  3995. ResetDTAAddress();
  3996. #endif
  3997. HourGlass(FALSE);
  3998. return (bRet);
  3999. }
  4000. ////////////////////////////////////////////////////////////////////////////
  4001. //
  4002. // OKButtonPressed
  4003. //
  4004. // Note: There are 4 cases for validation of a file name:
  4005. // 1) OFN_NOVALIDATE allows invalid characters
  4006. // 2) No validation flags No invalid characters, but path need not exist
  4007. // 3) OFN_PATHMUSTEXIST No invalid characters, path must exist
  4008. // 4) OFN_FILEMUSTEXIST No invalid characters, path & file must exist
  4009. //
  4010. ////////////////////////////////////////////////////////////////////////////
  4011. BOOL OKButtonPressed(
  4012. HWND hDlg,
  4013. POPENFILEINFO pOFI,
  4014. BOOL bSave)
  4015. {
  4016. DWORD nErrCode = 0;
  4017. DWORD cch;
  4018. DWORD cchSearchPath;
  4019. LPOPENFILENAME pOFN = pOFI->pOFN;
  4020. int nFileOffset, nExtOffset;
  4021. HANDLE hFile;
  4022. BOOL bAddExt = FALSE;
  4023. BOOL bUNCName = FALSE;
  4024. int nTempOffset;
  4025. TCHAR szPathName[MAX_FULLPATHNAME];
  4026. DWORD lRet;
  4027. BOOL blfn;
  4028. LPCURDLG lpCurDlg;
  4029. TCHAR ch = 0;
  4030. if (cch = GetUNCDirectoryFromLB(hDlg, lst2, pOFI))
  4031. {
  4032. nTempOffset = (WORD)(DWORD)SendDlgItemMessage( hDlg,
  4033. lst2,
  4034. LB_GETTEXTLEN,
  4035. 0,
  4036. 0 );
  4037. }
  4038. else
  4039. {
  4040. nTempOffset = 0;
  4041. }
  4042. GetDlgItemText(hDlg, edt1, pOFI->szPath + cch, MAX_FULLPATHNAME - 1);
  4043. if (cch)
  4044. {
  4045. //
  4046. // If a drive or new UNC was specified, forget the old UNC.
  4047. //
  4048. if ((pOFI->szPath[cch + 1] == CHAR_COLON) ||
  4049. (DBL_BSLASH(pOFI->szPath + cch)) )
  4050. {
  4051. lstrcpy(pOFI->szPath, pOFI->szPath + cch);
  4052. }
  4053. else if ((ISBACKSLASH(pOFI->szPath, cch)) ||
  4054. (pOFI->szPath[cch] == CHAR_SLASH))
  4055. {
  4056. //
  4057. // If a directory from the root is given, put it immediately
  4058. // after the \\server\share listing.
  4059. //
  4060. lstrcpy(pOFI->szPath + nTempOffset, pOFI->szPath + cch);
  4061. }
  4062. }
  4063. if (pOFN->Flags & OFN_NOLONGNAMES)
  4064. {
  4065. blfn = FALSE;
  4066. }
  4067. else
  4068. {
  4069. blfn = IsLFNDriveX(hDlg, pOFI->szPath);
  4070. }
  4071. lRet = ParseFile(pOFI->szPath, blfn, IS16BITWOWAPP(pOFN), FALSE);
  4072. nFileOffset = (int)(SHORT)LOWORD(lRet);
  4073. nExtOffset = (int)(SHORT)HIWORD(lRet);
  4074. if (nFileOffset == PARSE_EMPTYSTRING)
  4075. {
  4076. UpdateListBoxes(hDlg, pOFI, NULL, 0);
  4077. return (FALSE);
  4078. }
  4079. else if ((nFileOffset != PARSE_DIRECTORYNAME) &&
  4080. (pOFN->Flags & OFN_NOVALIDATE))
  4081. {
  4082. pOFN->nFileOffset = (WORD)nFileOffset;
  4083. pOFN->nFileExtension = (WORD)nExtOffset;
  4084. if (pOFN->lpstrFile)
  4085. {
  4086. cch = lstrlen(pOFI->szPath);
  4087. if (cch < pOFN->nMaxFile)
  4088. {
  4089. lstrcpy(pOFN->lpstrFile, pOFI->szPath);
  4090. }
  4091. else
  4092. {
  4093. //
  4094. // For single file requests, we will never go over 64K
  4095. // because the filesystem is limited to 256.
  4096. //
  4097. if (cch > 0x0000FFFF)
  4098. {
  4099. pOFN->lpstrFile[0] = (TCHAR)0xFFFF;
  4100. }
  4101. else
  4102. {
  4103. pOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
  4104. }
  4105. pOFN->lpstrFile[1] = CHAR_NULL;
  4106. }
  4107. }
  4108. return (TRUE);
  4109. }
  4110. else if ((pOFN->Flags & OFN_ALLOWMULTISELECT) &&
  4111. SpacesExist(pOFI->szPath))
  4112. {
  4113. return (MultiSelectOKButton(hDlg, pOFI, bSave));
  4114. }
  4115. else if (pOFI->szPath[nExtOffset] == CHAR_SEMICOLON)
  4116. {
  4117. pOFI->szPath[nExtOffset] = CHAR_NULL;
  4118. nFileOffset = (int)(SHORT)LOWORD(ParseFile( pOFI->szPath,
  4119. blfn,
  4120. IS16BITWOWAPP(pOFN),
  4121. FALSE ));
  4122. pOFI->szPath[nExtOffset] = CHAR_SEMICOLON;
  4123. if ( (nFileOffset >= 0) &&
  4124. (StrChr(pOFI->szPath + nFileOffset, CHAR_STAR) ||
  4125. StrChr(pOFI->szPath + nFileOffset, CHAR_QMARK)) )
  4126. {
  4127. lstrcpy(pOFI->szLastFilter, pOFI->szPath + nFileOffset);
  4128. if (FListAll(pOFI, hDlg, pOFI->szPath) == CHANGEDIR_FAILED)
  4129. {
  4130. //
  4131. // Conform with cchSearchPath error code settings in
  4132. // PathCheck.
  4133. //
  4134. cchSearchPath = 2;
  4135. goto PathCheck;
  4136. }
  4137. return (FALSE);
  4138. }
  4139. else
  4140. {
  4141. nFileOffset = PARSE_INVALIDCHAR;
  4142. goto Warning;
  4143. }
  4144. }
  4145. else if (nFileOffset == PARSE_DIRECTORYNAME)
  4146. {
  4147. //
  4148. // End with slash?
  4149. //
  4150. if ((ISBACKSLASH(pOFI->szPath, nExtOffset - 1)) ||
  4151. (pOFI->szPath[nExtOffset - 1] == CHAR_SLASH))
  4152. {
  4153. //
  4154. // ... and is not the root, get rid of the slash.
  4155. //
  4156. if ( (nExtOffset != 1) &&
  4157. (pOFI->szPath[nExtOffset - 2] != CHAR_COLON) &&
  4158. (nExtOffset != nTempOffset + 1) )
  4159. {
  4160. pOFI->szPath[nExtOffset - 1] = CHAR_NULL;
  4161. }
  4162. }
  4163. else if ((pOFI->szPath[nExtOffset - 1] == CHAR_DOT) &&
  4164. ((pOFI->szPath[nExtOffset - 2] == CHAR_DOT) ||
  4165. (ISBACKSLASH(pOFI->szPath, nExtOffset - 2)) ||
  4166. (pOFI->szPath[nExtOffset - 2] == CHAR_SLASH)) &&
  4167. ((DBL_BSLASH(pOFI->szPath)) ||
  4168. ((*(pOFI->szPath + 1) == CHAR_COLON) &&
  4169. (DBL_BSLASH(pOFI->szPath + 2)))))
  4170. {
  4171. pOFI->szPath[nExtOffset] = CHAR_BSLASH;
  4172. pOFI->szPath[nExtOffset + 1] = CHAR_NULL;
  4173. }
  4174. //
  4175. // Fall through to Directory Checking.
  4176. //
  4177. }
  4178. else if (nFileOffset < 0)
  4179. {
  4180. //
  4181. // Put in nErrCode so that call can be used from other points.
  4182. //
  4183. nErrCode = (DWORD)nFileOffset;
  4184. Warning:
  4185. //
  4186. // If the disk is not a floppy and they tell me there's no
  4187. // disk in the drive, dont believe it. Instead, put up the error
  4188. // message that they should have given us.
  4189. // (Note that the error message is checked first since checking
  4190. // the drive type is slower.)
  4191. //
  4192. if (nErrCode == ERROR_ACCESS_DENIED)
  4193. {
  4194. if (bUNCName)
  4195. {
  4196. nErrCode = ERROR_NETWORK_ACCESS_DENIED;
  4197. }
  4198. else
  4199. {
  4200. szPathName[0] = CharLowerChar(szPathName[0]);
  4201. if (GetDiskType(szPathName) == DRIVE_REMOTE)
  4202. {
  4203. nErrCode = ERROR_NETWORK_ACCESS_DENIED;
  4204. }
  4205. else if (GetDiskType(szPathName) == DRIVE_REMOVABLE)
  4206. {
  4207. nErrCode = ERROR_NO_DISK_IN_DRIVE;
  4208. }
  4209. else if (GetDiskType(szPathName) == DRIVE_CDROM)
  4210. {
  4211. nErrCode = ERROR_NO_DISK_IN_CDROM;
  4212. }
  4213. }
  4214. }
  4215. if ((nErrCode == ERROR_WRITE_PROTECT) ||
  4216. (nErrCode == ERROR_CANNOT_MAKE) ||
  4217. (nErrCode == ERROR_NO_DISK_IN_DRIVE) ||
  4218. (nErrCode == ERROR_NO_DISK_IN_CDROM))
  4219. {
  4220. pOFI->szPath[0] = szPathName[0];
  4221. }
  4222. InvalidFileWarning(hDlg, pOFI->szPath, nErrCode, 0);
  4223. //
  4224. // Can't cd case (don't want WM_ACTIVATE to setevent to GetNetDrives!).
  4225. // Reset wNoRedraw.
  4226. //
  4227. wNoRedraw &= ~1;
  4228. return (FALSE);
  4229. }
  4230. bUNCName = ((DBL_BSLASH(pOFI->szPath)) ||
  4231. ((*(pOFI->szPath + 1) == CHAR_COLON) &&
  4232. (DBL_BSLASH(pOFI->szPath + 2))));
  4233. nTempOffset = nFileOffset;
  4234. //
  4235. // Get the fully-qualified path.
  4236. //
  4237. {
  4238. BOOL bSlash;
  4239. BOOL bRet;
  4240. WORD nNullOffset;
  4241. if (nFileOffset != PARSE_DIRECTORYNAME)
  4242. {
  4243. ch = *(pOFI->szPath + nFileOffset);
  4244. *(pOFI->szPath + nFileOffset) = CHAR_NULL;
  4245. nNullOffset = (WORD) nFileOffset;
  4246. }
  4247. //
  4248. // For files of the format c:filename where c is not the
  4249. // current directory, SearchPath does not return the curdir of c
  4250. // so, prefetch it - should searchpath be changed?
  4251. //
  4252. if (nFileOffset)
  4253. {
  4254. if (*(pOFI->szPath + nFileOffset - 1) == CHAR_COLON)
  4255. {
  4256. //
  4257. // If it fails, fall through to the error generated below.
  4258. //
  4259. if (ChangeDir(hDlg, pOFI->szPath, FALSE, FALSE) != CHANGEDIR_FAILED)
  4260. {
  4261. //
  4262. // Replace old null offset.
  4263. //
  4264. *(pOFI->szPath + nFileOffset) = ch;
  4265. ch = *pOFI->szPath;
  4266. //
  4267. // Don't pass drive-colon into search path.
  4268. //
  4269. *pOFI->szPath = CHAR_NULL;
  4270. nNullOffset = 0;
  4271. }
  4272. }
  4273. }
  4274. if (bSlash = (*pOFI->szPath == CHAR_SLASH))
  4275. {
  4276. *pOFI->szPath = CHAR_BSLASH;
  4277. }
  4278. szPathName[0] = CHAR_NULL;
  4279. HourGlass(TRUE);
  4280. //
  4281. // REARCHITECT:
  4282. // Each wow thread can change the current directory.
  4283. // Since searchpath doesn't check current dirs on a per thread basis,
  4284. // reset it here and hope that we don't get interrupted between
  4285. // setting and searching...
  4286. //
  4287. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  4288. SetCurrentDirectory(lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
  4289. if (pOFI->szPath[0] == TEXT('\0')) // space for name (pretend it's valid for now)
  4290. {
  4291. lstrcpy(szPathName, (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL));
  4292. bRet = 1;
  4293. }
  4294. else
  4295. {
  4296. bRet = GetFullPathName( pOFI->szPath,
  4297. MAX_FULLPATHNAME,
  4298. szPathName,
  4299. NULL );
  4300. }
  4301. if (!bRet && (pOFI->szPath[1] == CHAR_COLON))
  4302. {
  4303. int nDriveIndex = DiskAddedPreviously(pOFI->szPath[0], NULL);
  4304. //
  4305. // If it's a remembered connection, try to reconnect it.
  4306. //
  4307. if (nDriveIndex != 0xFFFFFFFF &&
  4308. gaDiskInfo[nDriveIndex].dwType == REMDRVBMP)
  4309. {
  4310. DWORD err = WNetRestoreConnection( hDlg,
  4311. gaDiskInfo[nDriveIndex].lpPath );
  4312. if (err == WN_SUCCESS)
  4313. {
  4314. gaDiskInfo[nDriveIndex].dwType = NETDRVBMP;
  4315. nDriveIndex = (int) SendDlgItemMessage(
  4316. hDlg,
  4317. cmb2,
  4318. CB_SELECTSTRING,
  4319. (WPARAM)-1,
  4320. (LPARAM)(LPTSTR)gaDiskInfo[nDriveIndex].lpPath );
  4321. SendDlgItemMessage( hDlg,
  4322. cmb2,
  4323. CB_SETITEMDATA,
  4324. (WPARAM)nDriveIndex,
  4325. (LPARAM)NETDRVBMP );
  4326. bRet = GetFullPathName( pOFI->szPath,
  4327. MAX_FULLPATHNAME,
  4328. szPathName,
  4329. NULL);
  4330. }
  4331. }
  4332. }
  4333. HourGlass(FALSE);
  4334. if (nFileOffset != PARSE_DIRECTORYNAME)
  4335. {
  4336. *(pOFI->szPath + nNullOffset) = ch;
  4337. }
  4338. if (bSlash)
  4339. {
  4340. *pOFI->szPath = CHAR_SLASH;
  4341. }
  4342. if (bRet)
  4343. {
  4344. cchSearchPath = 0;
  4345. if (nFileOffset != PARSE_DIRECTORYNAME)
  4346. {
  4347. ch = *(szPathName + lstrlen(szPathName) - 1);
  4348. if (!ISBACKSLASH(szPathName, lstrlen(szPathName) - 1))
  4349. {
  4350. lstrcat(szPathName, TEXT("\\"));
  4351. }
  4352. lstrcat(szPathName, (LPTSTR)(pOFI->szPath + nFileOffset));
  4353. }
  4354. else
  4355. {
  4356. //
  4357. // Hack to get around SearchPath inconsistencies.
  4358. //
  4359. // searching for c: returns c:
  4360. // searching for server share dir1 .. returns server share
  4361. // in these two cases bypass the regular ChangeDir call that
  4362. // uses szPathName and use the original pOFI->szPath instead
  4363. // OKButtonPressed needs to be simplified!
  4364. //
  4365. int cch = GetPathOffset(pOFI->szPath);
  4366. if (cch > 0)
  4367. {
  4368. if (bUNCName)
  4369. {
  4370. //
  4371. // If this fails, how is szPathName used?
  4372. // szPathName's disk should equal pOFI->szPath's
  4373. // so the cch will be valid.
  4374. //
  4375. szPathName[cch] = CHAR_BSLASH;
  4376. szPathName[cch + 1] = CHAR_NULL;
  4377. if (ChangeDir( hDlg,
  4378. pOFI->szPath,
  4379. FALSE,
  4380. TRUE ) != CHANGEDIR_FAILED)
  4381. {
  4382. goto ChangedDir;
  4383. }
  4384. }
  4385. else
  4386. {
  4387. if (!pOFI->szPath[cch])
  4388. {
  4389. if (ChangeDir( hDlg,
  4390. pOFI->szPath,
  4391. FALSE,
  4392. TRUE) != CHANGEDIR_FAILED)
  4393. {
  4394. goto ChangedDir;
  4395. }
  4396. }
  4397. }
  4398. }
  4399. }
  4400. }
  4401. else
  4402. {
  4403. if (!(pOFN->Flags & OFN_PATHMUSTEXIST))
  4404. {
  4405. lstrcpy(szPathName, pOFI->szPath);
  4406. }
  4407. if (((nErrCode = GetLastError()) == ERROR_INVALID_DRIVE) ||
  4408. (pOFI->szPath[1] == CHAR_COLON))
  4409. {
  4410. cchSearchPath = 1;
  4411. }
  4412. else
  4413. {
  4414. cchSearchPath = 2;
  4415. }
  4416. }
  4417. }
  4418. //
  4419. // Full pattern?
  4420. //
  4421. if ( !cchSearchPath &&
  4422. ((StrChr(pOFI->szPath + nFileOffset, CHAR_STAR)) ||
  4423. (StrChr(pOFI->szPath + nFileOffset, CHAR_QMARK))) )
  4424. {
  4425. TCHAR szSameDirFile[MAX_FULLPATHNAME];
  4426. if (nTempOffset)
  4427. {
  4428. //
  4429. // Must restore character in case it is part of the filename,
  4430. // e.g. nTempOffset is 1 for "\foo.txt".
  4431. //
  4432. ch = pOFI->szPath[nTempOffset];
  4433. pOFI->szPath[nTempOffset] = 0;
  4434. ChangeDir(hDlg, pOFI->szPath, FALSE, TRUE);
  4435. pOFI->szPath[nTempOffset] = ch;
  4436. }
  4437. if (!nExtOffset)
  4438. {
  4439. lstrcat(pOFI->szPath + nFileOffset, TEXT("."));
  4440. }
  4441. lstrcpy(szSameDirFile, pOFI->szPath + nFileOffset);
  4442. lstrcpy(pOFI->szLastFilter, pOFI->szPath + nFileOffset);
  4443. if (FListAll(pOFI, hDlg, szSameDirFile) < 0)
  4444. {
  4445. MessageBeep(0);
  4446. }
  4447. return (FALSE);
  4448. }
  4449. //
  4450. // We either have a file pattern or a real file.
  4451. // If its a directory
  4452. // (1) Add on default pattern
  4453. // (2) Act like its a pattern (goto pattern (1))
  4454. // Else if its a pattern
  4455. // (1) Update everything
  4456. // (2) display files in whatever dir were now in
  4457. // Else if its a file name!
  4458. // (1) Check out the syntax
  4459. // (2) End the dialog given OK
  4460. // (3) Beep/message otherwise
  4461. //
  4462. //
  4463. // Drive-letter:\dirpath ??
  4464. //
  4465. if (!cchSearchPath)
  4466. {
  4467. DWORD dwFileAttr;
  4468. if ((dwFileAttr = GetFileAttributes(szPathName)) != 0xFFFFFFFF)
  4469. {
  4470. if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY)
  4471. {
  4472. if (ChangeDir(hDlg, szPathName, FALSE, TRUE) != CHANGEDIR_FAILED)
  4473. {
  4474. ChangedDir:
  4475. SendDlgItemMessage(hDlg, edt1, WM_SETREDRAW, FALSE, 0L);
  4476. if (*pOFI->szLastFilter)
  4477. {
  4478. SetDlgItemText(hDlg, edt1, pOFI->szLastFilter);
  4479. }
  4480. else
  4481. {
  4482. SetDlgItemText(hDlg, edt1, szStarDotStar);
  4483. }
  4484. SendMessage( hDlg,
  4485. WM_COMMAND,
  4486. GET_WM_COMMAND_MPS( cmb1,
  4487. GetDlgItem(hDlg, cmb1),
  4488. CBN_CLOSEUP ) );
  4489. SendMessage( hDlg,
  4490. WM_COMMAND,
  4491. GET_WM_COMMAND_MPS( cmb2,
  4492. GetDlgItem(hDlg, cmb2),
  4493. MYCBN_CHANGEDIR ) );
  4494. SendDlgItemMessage(hDlg, edt1, WM_SETREDRAW, TRUE, 0L);
  4495. InvalidateRect(GetDlgItem(hDlg, edt1), NULL, FALSE);
  4496. }
  4497. return (FALSE);
  4498. }
  4499. }
  4500. }
  4501. //
  4502. // Was there a path and did it fail?
  4503. //
  4504. if (nFileOffset && cchSearchPath && (pOFN->Flags & OFN_PATHMUSTEXIST))
  4505. {
  4506. PathCheck:
  4507. if (cchSearchPath == 2)
  4508. {
  4509. nErrCode = ERROR_PATH_NOT_FOUND;
  4510. }
  4511. else if (cchSearchPath == 1)
  4512. {
  4513. int nDriveIndex;
  4514. //
  4515. // Lowercase drive letters since DiskAddedPreviously is case
  4516. // sensitive.
  4517. //
  4518. CharLower(pOFI->szPath);
  4519. // We can get here without performing an OpenFile call. As such
  4520. // the szPathName can be filled with random garbage. Since we
  4521. // only need one character for the error message, set
  4522. // szPathName[0] to the drive letter.
  4523. //
  4524. if (pOFI->szPath[1] == CHAR_COLON)
  4525. {
  4526. nDriveIndex = DiskAddedPreviously(pOFI->szPath[0], NULL);
  4527. }
  4528. else
  4529. {
  4530. nDriveIndex = DiskAddedPreviously(0, pOFI->szPath);
  4531. }
  4532. if (nDriveIndex == 0xFFFFFFFF)
  4533. {
  4534. nErrCode = ERROR_NO_DRIVE;
  4535. }
  4536. else
  4537. {
  4538. if (bUNCName)
  4539. {
  4540. nErrCode = ERROR_NO_DRIVE;
  4541. }
  4542. else
  4543. {
  4544. switch (GetDiskType(pOFI->szPath))
  4545. {
  4546. case ( DRIVE_REMOVABLE ) :
  4547. {
  4548. szPathName[0] = pOFI->szPath[0];
  4549. nErrCode = ERROR_NO_DISK_IN_DRIVE;
  4550. break;
  4551. }
  4552. case ( DRIVE_CDROM ) :
  4553. {
  4554. szPathName[0] = pOFI->szPath[0];
  4555. nErrCode = ERROR_NO_DISK_IN_CDROM;
  4556. break;
  4557. }
  4558. default :
  4559. {
  4560. nErrCode = ERROR_PATH_NOT_FOUND;
  4561. }
  4562. }
  4563. }
  4564. }
  4565. }
  4566. else
  4567. {
  4568. nErrCode = ERROR_FILE_NOT_FOUND;
  4569. }
  4570. //
  4571. // If we don't set wNoRedraw here, then WM_ACTIVATE will set the
  4572. // GetNetDrives event.
  4573. //
  4574. wNoRedraw |= 1;
  4575. goto Warning;
  4576. }
  4577. if (PortName(pOFI->szPath + nFileOffset))
  4578. {
  4579. nErrCode = ERROR_PORTNAME;
  4580. goto Warning;
  4581. }
  4582. #if 0
  4583. //
  4584. // Check if we've received a string in the form "C:filename.ext".
  4585. // If we have, convert it to the form "C:.\filename.ext". This is done
  4586. // because the kernel will search the entire path, ignoring the drive
  4587. // specification after the initial search. Making it include a slash
  4588. // causes kernel to only search at that location.
  4589. // Note: Only increment nExtOffset, not nFileOffset. This is done
  4590. // because only nExtOffset is used later, and nFileOffset can then be
  4591. // used at the Warning: label to determine if this hack has occurred,
  4592. // and thus it can strip out the ".\" when putting out the error.
  4593. //
  4594. if ((nFileOffset == 2) && (pOFI->szPath[1] == CHAR_COLON))
  4595. {
  4596. lstrcpy(szWarning, pOFI->szPath + 2);
  4597. lstrcpy(pOFI->szPath + 4, szWarning);
  4598. pOFI->szPath[2] = CHAR_DOT;
  4599. pOFI->szPath[3] = CHAR_BSLASH;
  4600. nExtOffset += 2;
  4601. }
  4602. #endif
  4603. //
  4604. // Add the default extension unless filename ends with period or no
  4605. // default extension exists. If the file exists, consider asking
  4606. // permission to overwrite the file.
  4607. //
  4608. // NOTE: When no extension given, default extension is tried 1st.
  4609. //
  4610. if ( (nFileOffset != PARSE_DIRECTORYNAME) &&
  4611. nExtOffset &&
  4612. !pOFI->szPath[nExtOffset] &&
  4613. pOFN->lpstrDefExt &&
  4614. *pOFN->lpstrDefExt &&
  4615. (((DWORD)nExtOffset + lstrlen(pOFN->lpstrDefExt)) < pOFN->nMaxFile) )
  4616. {
  4617. DWORD dwFileAttr;
  4618. int nExtOffset2 = lstrlen(szPathName);
  4619. bAddExt = TRUE;
  4620. AppendExt(pOFI->szPath, pOFN->lpstrDefExt, FALSE);
  4621. AppendExt(szPathName, pOFN->lpstrDefExt, FALSE);
  4622. //
  4623. // Directory may match default extension. Change to it as if it had
  4624. // been typed in. A dir w/o the extension would have been switched
  4625. // to in the logic above.
  4626. //
  4627. if ((dwFileAttr = GetFileAttributes(pOFI->szPath)) != 0xFFFFFFFF)
  4628. {
  4629. if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY)
  4630. {
  4631. if (ChangeDir(hDlg, szPathName, FALSE, TRUE) != CHANGEDIR_FAILED)
  4632. {
  4633. goto ChangedDir;
  4634. }
  4635. }
  4636. }
  4637. hFile = CreateFile( szPathName,
  4638. GENERIC_READ,
  4639. FILE_SHARE_READ | FILE_SHARE_WRITE,
  4640. NULL,
  4641. OPEN_EXISTING,
  4642. FILE_ATTRIBUTE_NORMAL,
  4643. NULL );
  4644. if (hFile == INVALID_HANDLE_VALUE)
  4645. {
  4646. nErrCode = GetLastError();
  4647. //
  4648. // Fix bug where progman cannot OK a file being browsed for new
  4649. // item because it has Execute only permission.
  4650. //
  4651. if (nErrCode == ERROR_ACCESS_DENIED)
  4652. {
  4653. hFile = CreateFile( szPathName,
  4654. GENERIC_EXECUTE,
  4655. FILE_SHARE_READ | FILE_SHARE_WRITE,
  4656. NULL,
  4657. OPEN_EXISTING,
  4658. FILE_ATTRIBUTE_NORMAL,
  4659. NULL );
  4660. if (hFile == INVALID_HANDLE_VALUE)
  4661. {
  4662. nErrCode = GetLastError();
  4663. }
  4664. }
  4665. }
  4666. if (nErrCode == ERROR_SHARING_VIOLATION)
  4667. {
  4668. goto SharingViolationInquiry;
  4669. }
  4670. if (hFile != INVALID_HANDLE_VALUE)
  4671. {
  4672. if (!CloseHandle(hFile))
  4673. {
  4674. nErrCode = GetLastError();
  4675. goto Warning;
  4676. }
  4677. AskPermission:
  4678. //
  4679. // Is the file read-only?
  4680. //
  4681. if (pOFN->Flags & OFN_NOREADONLYRETURN)
  4682. {
  4683. int nRet;
  4684. if ((nRet = GetFileAttributes(szPathName)) != -1)
  4685. {
  4686. if (nRet & ATTR_READONLY)
  4687. {
  4688. nErrCode = ERROR_LAZY_READONLY;
  4689. goto Warning;
  4690. }
  4691. }
  4692. else
  4693. {
  4694. nErrCode = GetLastError();
  4695. goto Warning;
  4696. }
  4697. }
  4698. if ((bSave || (pOFN->Flags & OFN_NOREADONLYRETURN)) &&
  4699. (nErrCode == ERROR_ACCESS_DENIED))
  4700. {
  4701. goto Warning;
  4702. }
  4703. if (pOFN->Flags & OFN_OVERWRITEPROMPT)
  4704. {
  4705. if (bSave && !FOkToWriteOver(hDlg, szPathName))
  4706. {
  4707. PostMessage( hDlg,
  4708. WM_NEXTDLGCTL,
  4709. (WPARAM)GetDlgItem(hDlg, edt1),
  4710. (LPARAM)1L );
  4711. return (FALSE);
  4712. }
  4713. }
  4714. if (nErrCode == ERROR_SHARING_VIOLATION)
  4715. {
  4716. goto SharingViolationInquiry;
  4717. }
  4718. goto FileNameAccepted;
  4719. }
  4720. else
  4721. {
  4722. *(pOFI->szPath + nExtOffset) = CHAR_NULL;
  4723. szPathName[nExtOffset2] = CHAR_NULL;
  4724. }
  4725. }
  4726. else
  4727. {
  4728. //
  4729. // Extension should not be added.
  4730. //
  4731. bAddExt = FALSE;
  4732. }
  4733. hFile = CreateFile( szPathName,
  4734. GENERIC_READ,
  4735. FILE_SHARE_READ | FILE_SHARE_WRITE,
  4736. NULL,
  4737. OPEN_EXISTING,
  4738. FILE_ATTRIBUTE_NORMAL,
  4739. NULL );
  4740. if (hFile == INVALID_HANDLE_VALUE)
  4741. {
  4742. nErrCode = GetLastError();
  4743. //
  4744. // Fix bug where progman cannot OK a file being browsed for new item
  4745. // because it has Execute only permission.
  4746. //
  4747. if (nErrCode == ERROR_ACCESS_DENIED)
  4748. {
  4749. hFile = CreateFile( szPathName,
  4750. GENERIC_EXECUTE,
  4751. FILE_SHARE_READ | FILE_SHARE_WRITE,
  4752. NULL,
  4753. OPEN_EXISTING,
  4754. FILE_ATTRIBUTE_NORMAL,
  4755. NULL );
  4756. if (hFile == INVALID_HANDLE_VALUE)
  4757. {
  4758. nErrCode = GetLastError();
  4759. }
  4760. }
  4761. }
  4762. if (hFile != INVALID_HANDLE_VALUE)
  4763. {
  4764. if (!CloseHandle(hFile))
  4765. {
  4766. nErrCode = GetLastError();
  4767. goto Warning;
  4768. }
  4769. goto AskPermission;
  4770. }
  4771. else
  4772. {
  4773. if ((nErrCode == ERROR_FILE_NOT_FOUND) ||
  4774. (nErrCode == ERROR_PATH_NOT_FOUND))
  4775. {
  4776. //
  4777. // Figure out if the default extension should be tacked on.
  4778. //
  4779. if (bAddExt)
  4780. {
  4781. AppendExt(pOFI->szPath, pOFN->lpstrDefExt, FALSE);
  4782. AppendExt(szPathName, pOFN->lpstrDefExt, FALSE);
  4783. }
  4784. }
  4785. else if (nErrCode == ERROR_SHARING_VIOLATION)
  4786. {
  4787. SharingViolationInquiry:
  4788. //
  4789. // If the app is "share aware", fall through.
  4790. // Otherwise, ask the hook function.
  4791. //
  4792. if (!(pOFN->Flags & OFN_SHAREAWARE))
  4793. {
  4794. if (pOFN->lpfnHook)
  4795. {
  4796. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  4797. #ifdef UNICODE
  4798. if (pOFI->ApiType == COMDLG_ANSI)
  4799. {
  4800. CHAR szPathNameA[MAX_FULLPATHNAME];
  4801. RtlUnicodeToMultiByteSize(
  4802. &cch,
  4803. szPathName,
  4804. lstrlenW(szPathName) * sizeof(TCHAR) );
  4805. SHUnicodeToAnsi(szPathName,(LPSTR)&szPathNameA[0],cch + 1);
  4806. cch = (DWORD)(*lpfnHook)( hDlg,
  4807. msgSHAREVIOLATIONA,
  4808. 0,
  4809. (LONG_PTR)(LPSTR)szPathNameA );
  4810. }
  4811. else
  4812. #endif
  4813. {
  4814. cch = (DWORD)(*lpfnHook)( hDlg,
  4815. msgSHAREVIOLATIONW,
  4816. 0,
  4817. (LONG_PTR)szPathName );
  4818. }
  4819. if (cch == OFN_SHARENOWARN)
  4820. {
  4821. return (FALSE);
  4822. }
  4823. else if (cch != OFN_SHAREFALLTHROUGH)
  4824. {
  4825. goto Warning;
  4826. }
  4827. }
  4828. else
  4829. {
  4830. goto Warning;
  4831. }
  4832. }
  4833. goto FileNameAccepted;
  4834. }
  4835. if (!bSave)
  4836. {
  4837. if ((nErrCode == ERROR_FILE_NOT_FOUND) ||
  4838. (nErrCode == ERROR_PATH_NOT_FOUND))
  4839. {
  4840. if (pOFN->Flags & OFN_FILEMUSTEXIST)
  4841. {
  4842. if (pOFN->Flags & OFN_CREATEPROMPT)
  4843. {
  4844. //
  4845. // Don't alter pOFI->szPath.
  4846. //
  4847. bInChildDlg = TRUE;
  4848. cch = (DWORD)CreateFileDlg(hDlg, pOFI->szPath);
  4849. bInChildDlg = FALSE;
  4850. if (cch == IDYES)
  4851. {
  4852. goto TestCreation;
  4853. }
  4854. else
  4855. {
  4856. return (FALSE);
  4857. }
  4858. }
  4859. goto Warning;
  4860. }
  4861. }
  4862. else
  4863. {
  4864. goto Warning;
  4865. }
  4866. }
  4867. //
  4868. // The file doesn't exist. Can it be created? This is needed because
  4869. // there are many extended characters which are invalid that won't be
  4870. // caught by ParseFile.
  4871. // Two more good reasons: Write-protected disks & full disks.
  4872. //
  4873. // BUT, if they dont want the test creation, they can request that we
  4874. // not do it using the OFN_NOTESTFILECREATE flag. If they want to
  4875. // create files on a share that has create-but-no-modify privileges,
  4876. // they should set this flag but be ready for failures that couldn't
  4877. // be caught, such as no create privileges, invalid extended
  4878. // characters, a full disk, etc.
  4879. //
  4880. TestCreation:
  4881. if ((pOFN->Flags & OFN_PATHMUSTEXIST) &&
  4882. (!(pOFN->Flags & OFN_NOTESTFILECREATE)))
  4883. {
  4884. //
  4885. // Must use the FILE_FLAG_DELETE_ON_CLOSE flag so that the
  4886. // file is automatically deleted when the handle is closed
  4887. // (no need to call DeleteFile). This is necessary in the
  4888. // event that the directory only has Add & Read access.
  4889. // The CreateFile call will succeed, but the DeleteFile call
  4890. // will fail. By adding the above flag to the CreateFile
  4891. // call, it overrides the access rights and deletes the file
  4892. // during the call to CloseHandle.
  4893. //
  4894. #ifdef WINNT
  4895. hFile = CreateFile( szPathName,
  4896. FILE_ADD_FILE,
  4897. 0,
  4898. NULL,
  4899. CREATE_NEW,
  4900. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
  4901. NULL );
  4902. #else
  4903. // Win95/Memphis don't support FILE_ADD_FILE flag, use GENERIC_READ instead.
  4904. hFile = CreateFile( szPathName,
  4905. GENERIC_READ,
  4906. 0,
  4907. NULL,
  4908. CREATE_NEW,
  4909. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
  4910. NULL );
  4911. #endif
  4912. if (hFile == INVALID_HANDLE_VALUE)
  4913. {
  4914. nErrCode = GetLastError();
  4915. }
  4916. if (hFile != INVALID_HANDLE_VALUE)
  4917. {
  4918. if (!CloseHandle(hFile))
  4919. {
  4920. nErrCode = GetLastError();
  4921. goto Warning;
  4922. }
  4923. }
  4924. else
  4925. {
  4926. //
  4927. // Unable to create it.
  4928. //
  4929. // If it's not write-protection, a full disk,
  4930. // network protection, or the user popping the drive door
  4931. // open, assume that the filename is invalid.
  4932. //
  4933. if ( (nErrCode != ERROR_WRITE_PROTECT) &&
  4934. (nErrCode != ERROR_CANNOT_MAKE) &&
  4935. (nErrCode != ERROR_NETWORK_ACCESS_DENIED) &&
  4936. (nErrCode != ERROR_ACCESS_DENIED) )
  4937. {
  4938. nErrCode = 0;
  4939. }
  4940. goto Warning;
  4941. }
  4942. }
  4943. }
  4944. FileNameAccepted:
  4945. HourGlass(TRUE);
  4946. lRet = ParseFile(szPathName, blfn, IS16BITWOWAPP(pOFN), FALSE);
  4947. nFileOffset = (int)(SHORT)LOWORD(lRet);
  4948. cch = (DWORD)HIWORD(lRet);
  4949. pOFN->nFileOffset = (WORD)nFileOffset;
  4950. if (nExtOffset || bAddExt)
  4951. {
  4952. pOFN->nFileExtension = LOWORD(cch);
  4953. }
  4954. else
  4955. {
  4956. pOFN->nFileExtension = 0;
  4957. }
  4958. pOFN->Flags &= ~OFN_EXTENSIONDIFFERENT;
  4959. if (pOFN->lpstrDefExt && pOFN->nFileExtension)
  4960. {
  4961. TCHAR szPrivateExt[4];
  4962. SHORT i;
  4963. for (i = 0; i < 3; i++)
  4964. {
  4965. szPrivateExt[i] = *(pOFN->lpstrDefExt + i);
  4966. }
  4967. szPrivateExt[3] = CHAR_NULL;
  4968. if (lstrcmpi(szPrivateExt, szPathName + cch))
  4969. {
  4970. pOFN->Flags |= OFN_EXTENSIONDIFFERENT;
  4971. }
  4972. }
  4973. //
  4974. // If we're called from wow, and the user hasn't changed
  4975. // directories, shorten the path to abbreviated 8.3 format.
  4976. //
  4977. if (pOFN->Flags & OFN_NOLONGNAMES)
  4978. {
  4979. ShortenThePath(szPathName);
  4980. //
  4981. // If the path was shortened, the offset might have changed so
  4982. // we must parse the file again.
  4983. //
  4984. lRet = ParseFile(szPathName, blfn, IS16BITWOWAPP(pOFN), FALSE);
  4985. nFileOffset = (int)(SHORT)LOWORD(lRet);
  4986. cch = (DWORD)HIWORD(lRet);
  4987. //
  4988. // When in Save dialog, the file may not exist yet, so the file
  4989. // name cannot be shortened. So, we need to test if it's an
  4990. // 8.3 filename and popup an error message if not.
  4991. //
  4992. if (bSave)
  4993. {
  4994. LPTSTR lptmp;
  4995. LPTSTR lpExt = NULL;
  4996. for (lptmp = szPathName + nFileOffset; *lptmp; lptmp++)
  4997. {
  4998. if (*lptmp == CHAR_DOT)
  4999. {
  5000. if (lpExt)
  5001. {
  5002. //
  5003. // There's more than one dot in the file, so it is
  5004. // invalid.
  5005. //
  5006. nErrCode = FNERR_INVALIDFILENAME;
  5007. goto Warning;
  5008. }
  5009. lpExt = lptmp;
  5010. }
  5011. if (*lptmp == CHAR_SPACE)
  5012. {
  5013. nErrCode = FNERR_INVALIDFILENAME;
  5014. goto Warning;
  5015. }
  5016. }
  5017. if (lpExt)
  5018. {
  5019. //
  5020. // There's an extension.
  5021. //
  5022. *lpExt = 0;
  5023. }
  5024. if ((lstrlen(szPathName + nFileOffset) > 8) ||
  5025. (lpExt && lstrlen(lpExt + 1) > 3))
  5026. {
  5027. if (lpExt)
  5028. {
  5029. *lpExt = CHAR_DOT;
  5030. }
  5031. nErrCode = FNERR_INVALIDFILENAME;
  5032. goto Warning;
  5033. }
  5034. if (lpExt)
  5035. {
  5036. *lpExt = CHAR_DOT;
  5037. }
  5038. }
  5039. }
  5040. if (pOFN->lpstrFile)
  5041. {
  5042. DWORD cchLen = lstrlen(szPathName);
  5043. if (cchLen < pOFN->nMaxFile)
  5044. {
  5045. lstrcpy(pOFN->lpstrFile, szPathName);
  5046. }
  5047. else
  5048. {
  5049. //
  5050. // Buffer is too small, so return the size of the buffer
  5051. // required to hold the string.
  5052. //
  5053. // For single file requests, we will never go over 64K
  5054. // because the filesystem is limited to 256.
  5055. //
  5056. #ifdef UNICODE
  5057. pOFN->lpstrFile[0] = (TCHAR)LOWORD(cchLen);
  5058. if (pOFN->nMaxFile >= 2)
  5059. {
  5060. pOFN->lpstrFile[1] = CHAR_NULL;
  5061. }
  5062. #else
  5063. pOFN->lpstrFile[0] = LOBYTE(cchLen);
  5064. pOFN->lpstrFile[1] = HIBYTE(cchLen);
  5065. pOFN->lpstrFile[2] = CHAR_NULL;
  5066. #endif
  5067. }
  5068. }
  5069. //
  5070. // File Title. Note that it's cut off at whatever the buffer length
  5071. // is, so if the buffer is too small, no notice is given.
  5072. //
  5073. if (pOFN->lpstrFileTitle && pOFN->nMaxFileTitle)
  5074. {
  5075. cch = lstrlen(szPathName + nFileOffset);
  5076. if (cch > pOFN->nMaxFileTitle)
  5077. {
  5078. szPathName[nFileOffset + pOFN->nMaxFileTitle - 1] = CHAR_NULL;
  5079. }
  5080. lstrcpy(pOFN->lpstrFileTitle, szPathName + nFileOffset);
  5081. }
  5082. if (!(pOFN->Flags & OFN_HIDEREADONLY))
  5083. {
  5084. if (IsDlgButtonChecked(hDlg, chx1))
  5085. {
  5086. pOFN->Flags |= OFN_READONLY;
  5087. }
  5088. else
  5089. {
  5090. pOFN->Flags &= ~OFN_READONLY;
  5091. }
  5092. }
  5093. return (TRUE);
  5094. }
  5095. ////////////////////////////////////////////////////////////////////////////
  5096. //
  5097. // MultiSelectOKButton
  5098. //
  5099. ////////////////////////////////////////////////////////////////////////////
  5100. BOOL MultiSelectOKButton(
  5101. HWND hDlg,
  5102. POPENFILEINFO pOFI,
  5103. BOOL bSave)
  5104. {
  5105. DWORD nErrCode;
  5106. LPTSTR lpCurDir;
  5107. LPTSTR lpchStart; // start of an individual filename
  5108. LPTSTR lpchEnd; // end of an individual filename
  5109. DWORD cch;
  5110. HANDLE hFile;
  5111. LPOPENFILENAME pOFN;
  5112. BOOL EOS = FALSE; // end of string flag
  5113. BOOL bRet;
  5114. TCHAR szPathName[MAX_FULLPATHNAME - 1];
  5115. LPCURDLG lpCurDlg;
  5116. pOFN = pOFI->pOFN;
  5117. //
  5118. // Check for space for first full path element.
  5119. //
  5120. if(!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
  5121. !(lpCurDir = lpCurDlg->lpstrCurDir))
  5122. {
  5123. return (FALSE);
  5124. }
  5125. lstrcpy(pOFI->szPath, lpCurDir);
  5126. if (StrChr(pOFI->szPath, CHAR_SPACE))
  5127. {
  5128. GetShortPathName(pOFI->szPath, pOFI->szPath, MAX_FULLPATHNAME);
  5129. }
  5130. if (!bCasePreserved)
  5131. {
  5132. CharLower(pOFI->szPath);
  5133. }
  5134. cch = (DWORD) ( lstrlen(pOFI->szPath) +
  5135. sizeof(TCHAR) +
  5136. SendDlgItemMessage(hDlg, edt1, WM_GETTEXTLENGTH, 0, 0L) );
  5137. if (pOFN->lpstrFile)
  5138. {
  5139. if (cch > pOFN->nMaxFile)
  5140. {
  5141. //
  5142. // Buffer is too small, so return the size of the buffer
  5143. // required to hold the string (if possible).
  5144. //
  5145. if (pOFN->nMaxFile >= 3)
  5146. {
  5147. #ifdef UNICODE
  5148. pOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
  5149. pOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
  5150. #else
  5151. pOFN->lpstrFile[0] = (TCHAR)LOBYTE(cch);
  5152. pOFN->lpstrFile[1] = (TCHAR)HIBYTE(cch);
  5153. #endif
  5154. pOFN->lpstrFile[2] = CHAR_NULL;
  5155. }
  5156. else
  5157. {
  5158. #ifdef UNICODE
  5159. pOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
  5160. if (pOFN->nMaxFile == 2)
  5161. {
  5162. pOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
  5163. }
  5164. #else
  5165. pOFN->lpstrFile[0] = LOBYTE(cch);
  5166. pOFN->lpstrFile[1] = HIBYTE(cch);
  5167. pOFN->lpstrFile[2] = CHAR_NULL;
  5168. #endif
  5169. }
  5170. }
  5171. else
  5172. {
  5173. //
  5174. // Copy in the full path as the first element.
  5175. //
  5176. lstrcpy(pOFN->lpstrFile, pOFI->szPath);
  5177. lstrcat(pOFN->lpstrFile, TEXT(" "));
  5178. //
  5179. // Get the other files here.
  5180. //
  5181. cch = lstrlen(pOFN->lpstrFile);
  5182. //
  5183. // The path is guaranteed to be less than 64K (actually, < 260).
  5184. //
  5185. pOFN->nFileOffset = LOWORD(cch);
  5186. lpchStart = pOFN->lpstrFile + cch;
  5187. GetDlgItemText( hDlg,
  5188. edt1,
  5189. lpchStart,
  5190. (int)(pOFN->nMaxFile - cch - 1) );
  5191. while (*lpchStart == CHAR_SPACE)
  5192. {
  5193. lpchStart = CharNext(lpchStart);
  5194. }
  5195. if (*lpchStart == CHAR_NULL)
  5196. {
  5197. return (FALSE);
  5198. }
  5199. //
  5200. // Go along file path looking for multiple filenames delimited by
  5201. // spaces. For each filename found, try to open it to make sure
  5202. // it's a valid file.
  5203. //
  5204. while (!EOS)
  5205. {
  5206. //
  5207. // Find the end of the filename.
  5208. //
  5209. lpchEnd = lpchStart;
  5210. while (*lpchEnd && *lpchEnd != CHAR_SPACE)
  5211. {
  5212. lpchEnd = CharNext(lpchEnd);
  5213. }
  5214. //
  5215. // Mark the end of the filename with a NULL.
  5216. //
  5217. if (*lpchEnd == CHAR_SPACE)
  5218. {
  5219. *lpchEnd = CHAR_NULL;
  5220. }
  5221. else
  5222. {
  5223. //
  5224. // Already NULL, found the end of the string.
  5225. //
  5226. EOS = TRUE;
  5227. }
  5228. //
  5229. // Check that the filename is valid.
  5230. //
  5231. bRet = GetFullPathName( lpchStart,
  5232. ARRAYSIZE(szPathName),
  5233. szPathName,
  5234. NULL);
  5235. if (!bRet)
  5236. {
  5237. nErrCode = ERROR_FILE_NOT_FOUND;
  5238. goto MultiFileNotFound;
  5239. }
  5240. hFile = CreateFile( szPathName,
  5241. GENERIC_READ,
  5242. FILE_SHARE_READ | FILE_SHARE_WRITE,
  5243. NULL,
  5244. OPEN_EXISTING,
  5245. FILE_ATTRIBUTE_NORMAL,
  5246. NULL );
  5247. //
  5248. // Fix bug where progman cannot OK a file being browsed for
  5249. // new item because it has Execute only permission.
  5250. //
  5251. if (hFile == INVALID_HANDLE_VALUE)
  5252. {
  5253. nErrCode = GetLastError();
  5254. if (nErrCode == ERROR_ACCESS_DENIED)
  5255. {
  5256. hFile = CreateFile( szPathName,
  5257. GENERIC_EXECUTE,
  5258. FILE_SHARE_READ | FILE_SHARE_WRITE,
  5259. NULL,
  5260. OPEN_EXISTING,
  5261. FILE_ATTRIBUTE_NORMAL,
  5262. NULL );
  5263. }
  5264. else
  5265. {
  5266. goto MultiFileNotFound;
  5267. }
  5268. }
  5269. if (hFile == INVALID_HANDLE_VALUE)
  5270. {
  5271. nErrCode = GetLastError();
  5272. MultiFileNotFound:
  5273. if ( ((pOFN->Flags & OFN_FILEMUSTEXIST) ||
  5274. (nErrCode != ERROR_FILE_NOT_FOUND)) &&
  5275. ((pOFN->Flags & OFN_PATHMUSTEXIST) ||
  5276. (nErrCode != ERROR_PATH_NOT_FOUND)) &&
  5277. (!(pOFN->Flags & OFN_SHAREAWARE) ||
  5278. (nErrCode != ERROR_SHARING_VIOLATION)) )
  5279. {
  5280. if ( (nErrCode == ERROR_SHARING_VIOLATION) &&
  5281. pOFN->lpfnHook )
  5282. {
  5283. LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
  5284. #ifdef UNICODE
  5285. if (pOFI->ApiType == COMDLG_ANSI)
  5286. {
  5287. CHAR szPathNameA[MAX_FULLPATHNAME];
  5288. RtlUnicodeToMultiByteSize(
  5289. &cch,
  5290. szPathName,
  5291. lstrlenW(szPathName) * sizeof(TCHAR) );
  5292. SHUnicodeToAnsi(szPathName,(LPSTR)&szPathNameA[0],cch + 1);
  5293. cch = (DWORD)(*lpfnHook)( hDlg,
  5294. msgSHAREVIOLATIONA,
  5295. 0,
  5296. (LONG_PTR)(LPSTR)szPathNameA );
  5297. }
  5298. else
  5299. #endif
  5300. {
  5301. cch = (DWORD)(*lpfnHook)( hDlg,
  5302. msgSHAREVIOLATIONW,
  5303. 0,
  5304. (LONG_PTR)szPathName );
  5305. }
  5306. if (cch == OFN_SHARENOWARN)
  5307. {
  5308. return (FALSE);
  5309. }
  5310. else if (cch == OFN_SHAREFALLTHROUGH)
  5311. {
  5312. goto EscapedThroughShare;
  5313. }
  5314. }
  5315. else if (nErrCode == ERROR_ACCESS_DENIED)
  5316. {
  5317. szPathName[0] = CharLowerChar(szPathName[0]);
  5318. if (GetDiskType(szPathName) != DRIVE_REMOVABLE)
  5319. {
  5320. nErrCode = ERROR_NETWORK_ACCESS_DENIED;
  5321. }
  5322. }
  5323. if ((nErrCode == ERROR_WRITE_PROTECT) ||
  5324. (nErrCode == ERROR_CANNOT_MAKE) ||
  5325. (nErrCode == ERROR_ACCESS_DENIED))
  5326. {
  5327. *lpchStart = szPathName[0];
  5328. }
  5329. MultiWarning:
  5330. InvalidFileWarning(hDlg, lpchStart, nErrCode, 0);
  5331. return (FALSE);
  5332. }
  5333. }
  5334. EscapedThroughShare:
  5335. if (hFile != INVALID_HANDLE_VALUE)
  5336. {
  5337. if (!CloseHandle(hFile))
  5338. {
  5339. nErrCode = GetLastError();
  5340. goto MultiWarning;
  5341. }
  5342. if ((pOFN->Flags & OFN_NOREADONLYRETURN) &&
  5343. (GetFileAttributes(szPathName) & FILE_ATTRIBUTE_READONLY))
  5344. {
  5345. nErrCode = ERROR_LAZY_READONLY;
  5346. goto MultiWarning;
  5347. }
  5348. if ((bSave || (pOFN->Flags & OFN_NOREADONLYRETURN)) &&
  5349. (nErrCode == ERROR_ACCESS_DENIED))
  5350. {
  5351. goto MultiWarning;
  5352. }
  5353. if (pOFN->Flags & OFN_OVERWRITEPROMPT)
  5354. {
  5355. if (bSave && !FOkToWriteOver(hDlg, szPathName))
  5356. {
  5357. PostMessage( hDlg,
  5358. WM_NEXTDLGCTL,
  5359. (WPARAM)GetDlgItem(hDlg, edt1),
  5360. (LPARAM)1L );
  5361. return (FALSE);
  5362. }
  5363. }
  5364. }
  5365. //
  5366. // This file is valid, so check the next one.
  5367. //
  5368. if (!EOS)
  5369. {
  5370. lpchStart = lpchEnd + 1;
  5371. while (*lpchStart == CHAR_SPACE)
  5372. {
  5373. lpchStart = CharNext(lpchStart);
  5374. }
  5375. if (*lpchStart == CHAR_NULL)
  5376. {
  5377. EOS = TRUE;
  5378. }
  5379. else
  5380. {
  5381. //
  5382. // Not at end, replace NULL with SPACE.
  5383. //
  5384. *lpchEnd = CHAR_SPACE;
  5385. }
  5386. }
  5387. }
  5388. //
  5389. // Limit String.
  5390. //
  5391. *lpchEnd = CHAR_NULL;
  5392. }
  5393. }
  5394. //
  5395. // This doesn't really mean anything for multiselection.
  5396. //
  5397. pOFN->nFileExtension = 0;
  5398. pOFN->nFilterIndex = (int) SendDlgItemMessage(hDlg, cmb1, CB_GETCURSEL, 0, 0L);
  5399. return (TRUE);
  5400. }
  5401. ////////////////////////////////////////////////////////////////////////////
  5402. //
  5403. // dwOKSubclass
  5404. //
  5405. // Simulates a double click if the user presses OK with the mouse
  5406. // and the focus was on the directory listbox.
  5407. //
  5408. // The problem is that the UITF demands that when the directory
  5409. // listbox loses the focus, the selected directory should return
  5410. // to the current directory. But when the user changes the item
  5411. // selected with a single click, and then clicks the OK button to
  5412. // have the change take effect, the focus is lost before the OK button
  5413. // knows it was pressed. By setting the global flag bChangeDir
  5414. // when the directory listbox loses the focus and clearing it when
  5415. // the OK button loses the focus, we can check whether a mouse
  5416. // click should update the directory.
  5417. //
  5418. // Returns: Return value from default listbox proceedure.
  5419. //
  5420. ////////////////////////////////////////////////////////////////////////////
  5421. LRESULT WINAPI dwOKSubclass(
  5422. HWND hOK,
  5423. UINT msg,
  5424. WPARAM wParam,
  5425. LPARAM lParam)
  5426. {
  5427. HANDLE hDlg;
  5428. POPENFILEINFO pOFI;
  5429. if (msg == WM_KILLFOCUS)
  5430. {
  5431. if (bChangeDir)
  5432. {
  5433. if (pOFI = (POPENFILEINFO)GetProp(hDlg = GetParent(hOK), FILEPROP))
  5434. {
  5435. SendDlgItemMessage( hDlg,
  5436. lst2,
  5437. LB_SETCURSEL,
  5438. (WPARAM)(pOFI->idirSub - 1),
  5439. 0L );
  5440. }
  5441. bChangeDir = FALSE;
  5442. }
  5443. }
  5444. return (CallWindowProc(lpOKProc, hOK, msg, wParam, lParam));
  5445. }
  5446. ////////////////////////////////////////////////////////////////////////////
  5447. //
  5448. // dwLBSubclass
  5449. //
  5450. // Simulates a double click if the user presses OK with the mouse.
  5451. //
  5452. // The problem is that the UITF demands that when the directory
  5453. // listbox loses the focus, the selected directory should return
  5454. // to the current directory. But when the user changes the item
  5455. // selected with a single click, and then clicks the OK button to
  5456. // have the change take effect, the focus is lost before the OK button
  5457. // knows it was pressed. By simulating a double click, the change
  5458. // takes place.
  5459. //
  5460. // Returns: Return value from default listbox proceedure.
  5461. //
  5462. ////////////////////////////////////////////////////////////////////////////
  5463. LRESULT WINAPI dwLBSubclass(
  5464. HWND hLB,
  5465. UINT msg,
  5466. WPARAM wParam,
  5467. LPARAM lParam)
  5468. {
  5469. HANDLE hDlg;
  5470. POPENFILEINFO pOFI;
  5471. if (msg == WM_KILLFOCUS)
  5472. {
  5473. hDlg = GetParent(hLB);
  5474. bChangeDir = (GetDlgItem(hDlg, IDOK) == (HWND)wParam) ? TRUE : FALSE;
  5475. if (!bChangeDir)
  5476. {
  5477. if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
  5478. {
  5479. SendMessage( hLB,
  5480. LB_SETCURSEL,
  5481. (WPARAM)(pOFI->idirSub - 1),
  5482. 0L );
  5483. }
  5484. }
  5485. }
  5486. return (CallWindowProc(lpLBProc, hLB, msg, wParam, lParam));
  5487. }
  5488. ////////////////////////////////////////////////////////////////////////////
  5489. //
  5490. // InvalidFileWarning
  5491. //
  5492. ////////////////////////////////////////////////////////////////////////////
  5493. int InvalidFileWarning(
  5494. HWND hDlg,
  5495. LPTSTR szFile,
  5496. DWORD wErrCode,
  5497. UINT mbType)
  5498. {
  5499. SHORT isz;
  5500. BOOL bDriveLetter = FALSE;
  5501. int nRet = 0;
  5502. if (lstrlen(szFile) > TOOLONGLIMIT)
  5503. {
  5504. *(szFile + TOOLONGLIMIT) = CHAR_NULL;
  5505. }
  5506. switch (wErrCode)
  5507. {
  5508. case ( ERROR_NO_DISK_IN_DRIVE ) :
  5509. {
  5510. isz = iszNoDiskInDrive;
  5511. bDriveLetter = TRUE;
  5512. break;
  5513. }
  5514. case ( ERROR_NO_DISK_IN_CDROM ) :
  5515. {
  5516. isz = iszNoDiskInCDRom;
  5517. bDriveLetter = TRUE;
  5518. break;
  5519. }
  5520. case ( ERROR_NO_DRIVE ) :
  5521. {
  5522. isz = iszDriveDoesNotExist;
  5523. bDriveLetter = TRUE;
  5524. break;
  5525. }
  5526. case ( ERROR_TOO_MANY_OPEN_FILES ) :
  5527. {
  5528. isz = iszNoFileHandles;
  5529. break;
  5530. }
  5531. case ( ERROR_PATH_NOT_FOUND ) :
  5532. {
  5533. isz = iszPathNotFound;
  5534. break;
  5535. }
  5536. case ( ERROR_FILE_NOT_FOUND ) :
  5537. {
  5538. isz = iszFileNotFound;
  5539. break;
  5540. }
  5541. case ( ERROR_CANNOT_MAKE ) :
  5542. case ( ERROR_DISK_FULL ) :
  5543. {
  5544. isz = iszDiskFull;
  5545. bDriveLetter = TRUE;
  5546. break;
  5547. }
  5548. case ( ERROR_WRITE_PROTECT ) :
  5549. {
  5550. isz = iszWriteProtection;
  5551. bDriveLetter = TRUE;
  5552. break;
  5553. }
  5554. case ( ERROR_SHARING_VIOLATION ) :
  5555. {
  5556. isz = iszSharingViolation;
  5557. break;
  5558. }
  5559. case ( ERROR_CREATE_NO_MODIFY ) :
  5560. {
  5561. isz = iszCreateNoModify;
  5562. break;
  5563. }
  5564. case ( ERROR_NETWORK_ACCESS_DENIED ) :
  5565. {
  5566. isz = iszNetworkAccessDenied;
  5567. break;
  5568. }
  5569. case ( ERROR_PORTNAME ) :
  5570. {
  5571. isz = iszPortName;
  5572. break;
  5573. }
  5574. case ( ERROR_LAZY_READONLY ) :
  5575. {
  5576. isz = iszReadOnly;
  5577. break;
  5578. }
  5579. case ( ERROR_DIR_ACCESS_DENIED ) :
  5580. {
  5581. isz = iszDirAccessDenied;
  5582. break;
  5583. }
  5584. case ( ERROR_FILE_ACCESS_DENIED ) :
  5585. case ( ERROR_ACCESS_DENIED ) :
  5586. {
  5587. isz = iszFileAccessDenied;
  5588. break;
  5589. }
  5590. case ( ERROR_UNRECOGNIZED_VOLUME ) :
  5591. {
  5592. FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
  5593. FORMAT_MESSAGE_IGNORE_INSERTS |
  5594. FORMAT_MESSAGE_MAX_WIDTH_MASK,
  5595. NULL,
  5596. wErrCode,
  5597. GetUserDefaultLCID(),
  5598. szWarning,
  5599. WARNINGMSGLENGTH,
  5600. NULL );
  5601. goto DisplayError;
  5602. }
  5603. default :
  5604. {
  5605. isz = iszInvalidFileName;
  5606. break;
  5607. }
  5608. }
  5609. if (!CDLoadString( g_hinst,
  5610. isz,
  5611. szCaption,
  5612. WARNINGMSGLENGTH ))
  5613. {
  5614. wsprintf( szWarning,
  5615. TEXT("Error occurred, but error resource cannot be loaded.") );
  5616. }
  5617. else
  5618. {
  5619. wsprintf( szWarning,
  5620. szCaption,
  5621. bDriveLetter ? (LPTSTR)(CHAR)*szFile : szFile );
  5622. DisplayError:
  5623. GetWindowText(hDlg, szCaption, WARNINGMSGLENGTH);
  5624. if (!mbType)
  5625. {
  5626. mbType = MB_OK | MB_ICONEXCLAMATION;
  5627. }
  5628. nRet = MessageBox(hDlg, szWarning, szCaption, mbType);
  5629. }
  5630. if (isz == iszInvalidFileName)
  5631. {
  5632. PostMessage( hDlg,
  5633. WM_NEXTDLGCTL,
  5634. (WPARAM)GetDlgItem(hDlg, edt1),
  5635. (LPARAM)1L );
  5636. }
  5637. return (nRet);
  5638. }
  5639. ////////////////////////////////////////////////////////////////////////////
  5640. //
  5641. // MeasureItem
  5642. //
  5643. ////////////////////////////////////////////////////////////////////////////
  5644. VOID MeasureItem(
  5645. HWND hDlg,
  5646. LPMEASUREITEMSTRUCT mis)
  5647. {
  5648. if (!dyItem)
  5649. {
  5650. HDC hDC = GetDC(hDlg);
  5651. TEXTMETRIC TM;
  5652. HANDLE hFont;
  5653. hFont = (HANDLE)SendMessage(hDlg, WM_GETFONT, 0, 0L);
  5654. if (!hFont)
  5655. {
  5656. hFont = GetStockObject(SYSTEM_FONT);
  5657. }
  5658. hFont = SelectObject(hDC, hFont);
  5659. GetTextMetrics(hDC, &TM);
  5660. SelectObject(hDC, hFont);
  5661. ReleaseDC(hDlg, hDC);
  5662. dyText = TM.tmHeight;
  5663. dyItem = max(dyDirDrive, dyText);
  5664. }
  5665. if (mis->CtlID == lst1)
  5666. {
  5667. mis->itemHeight = dyText;
  5668. }
  5669. else
  5670. {
  5671. mis->itemHeight = dyItem;
  5672. }
  5673. }
  5674. ////////////////////////////////////////////////////////////////////////////
  5675. //
  5676. // Signum
  5677. //
  5678. // Returns the sign of an integer:
  5679. // -1 if integer < 0
  5680. // 0 if integer = 0
  5681. // 1 if integer > 0
  5682. //
  5683. // Note: Signum *could* be defined as an inline macro, but that causes
  5684. // the C compiler to disable Loop optimization, Global register
  5685. // optimization, and Global optimizations for common subexpressions
  5686. // in any function that the macro would appear. The cost of a call
  5687. // to the function seemed worth the optimizations.
  5688. //
  5689. ////////////////////////////////////////////////////////////////////////////
  5690. int Signum(
  5691. int nTest)
  5692. {
  5693. return ((nTest == 0) ? 0 : (nTest > 0) ? 1 : -1);
  5694. }
  5695. ////////////////////////////////////////////////////////////////////////////
  5696. //
  5697. // DrawItem
  5698. //
  5699. // Draws the drive/directory pictures in the respective combo list boxes.
  5700. //
  5701. // lst1 is listbox for files
  5702. // lst2 is listbox for directories
  5703. // cmb1 is combobox for filters
  5704. // cmb2 is combobox for drives
  5705. //
  5706. ////////////////////////////////////////////////////////////////////////////
  5707. VOID DrawItem(
  5708. POPENFILEINFO pOFI,
  5709. HWND hDlg,
  5710. WPARAM wParam,
  5711. LPDRAWITEMSTRUCT lpdis,
  5712. BOOL bSave)
  5713. {
  5714. HDC hdcList;
  5715. RECT rc;
  5716. // RECT rcCmb2;
  5717. TCHAR szText[MAX_FULLPATHNAME + 1];
  5718. int dxAcross;
  5719. LONG nHeight;
  5720. LONG rgbBack, rgbText, rgbOldBack, rgbOldText;
  5721. SHORT nShift = 1; // to shift directories right in lst2
  5722. BOOL bSel;
  5723. int BltItem;
  5724. int nBackMode;
  5725. if ((int)lpdis->itemID < 0)
  5726. {
  5727. DefWindowProc(hDlg, WM_DRAWITEM, wParam, (LPARAM)lpdis);
  5728. return;
  5729. }
  5730. *szText = CHAR_NULL;
  5731. if (lpdis->CtlID != lst1 && lpdis->CtlID != lst2 && lpdis->CtlID != cmb2)
  5732. {
  5733. return;
  5734. }
  5735. if (!pOFI)
  5736. {
  5737. return;
  5738. }
  5739. hdcList = lpdis->hDC;
  5740. if (lpdis->CtlID != cmb2)
  5741. {
  5742. SendDlgItemMessage( hDlg,
  5743. (int)lpdis->CtlID,
  5744. LB_GETTEXT ,
  5745. (WPARAM)lpdis->itemID,
  5746. (LONG_PTR)szText );
  5747. if (*szText == 0)
  5748. {
  5749. //
  5750. // If empty listing.
  5751. //
  5752. DefWindowProc(hDlg, WM_DRAWITEM, wParam, (LONG_PTR)lpdis);
  5753. return;
  5754. }
  5755. if (!bCasePreserved)
  5756. {
  5757. CharLower(szText);
  5758. }
  5759. }
  5760. nHeight = (lpdis->CtlID == lst1) ? dyText : dyItem;
  5761. CopyRect((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
  5762. rc.bottom = rc.top + nHeight;
  5763. if (bSave && (lpdis->CtlID == lst1))
  5764. {
  5765. rgbBack = rgbWindowColor;
  5766. rgbText = rgbGrayText;
  5767. }
  5768. else
  5769. {
  5770. //
  5771. // Careful checking of bSel is needed here. Since the file
  5772. // listbox (lst1) can allow multiselect, only ODS_SELECTED needs
  5773. // to be set. But for the directory listbox (lst2), ODS_FOCUS
  5774. // also needs to be set.
  5775. //
  5776. bSel = (lpdis->itemState & (ODS_SELECTED | ODS_FOCUS));
  5777. if ((bSel & ODS_SELECTED) &&
  5778. ((lpdis->CtlID != lst2) || (bSel & ODS_FOCUS)))
  5779. {
  5780. rgbBack = rgbHiliteColor;
  5781. rgbText = rgbHiliteText;
  5782. }
  5783. else
  5784. {
  5785. rgbBack = rgbWindowColor;
  5786. rgbText = rgbWindowText;
  5787. }
  5788. }
  5789. rgbOldBack = SetBkColor(hdcList, rgbBack);
  5790. rgbOldText = SetTextColor(hdcList, rgbText);
  5791. //
  5792. // Drives -- text is now in UI style, c: VolumeName/Server-Sharename.
  5793. //
  5794. if (lpdis->CtlID == cmb2)
  5795. {
  5796. HANDLE hCmb2 = GetDlgItem(hDlg, cmb2);
  5797. dxAcross = dxDirDrive / BMPHIOFFSET;
  5798. BltItem = (int) SendMessage(hCmb2, CB_GETITEMDATA, lpdis->itemID, 0);
  5799. SendMessage(hCmb2, CB_GETLBTEXT, lpdis->itemID, (LPARAM)szText);
  5800. if (bSel & ODS_SELECTED)
  5801. {
  5802. BltItem += BMPHIOFFSET;
  5803. }
  5804. }
  5805. else if (lpdis->CtlID == lst2)
  5806. {
  5807. //
  5808. // Directories.
  5809. //
  5810. dxAcross = dxDirDrive / BMPHIOFFSET;
  5811. if (lpdis->itemID > pOFI->idirSub)
  5812. {
  5813. nShift = (SHORT)pOFI->idirSub;
  5814. }
  5815. else
  5816. {
  5817. nShift = (SHORT)lpdis->itemID;
  5818. }
  5819. //
  5820. // Must be at least 1.
  5821. //
  5822. nShift++;
  5823. BltItem = 1 + Signum(lpdis->itemID + 1 - pOFI->idirSub);
  5824. if (bSel & ODS_FOCUS)
  5825. {
  5826. BltItem += BMPHIOFFSET;
  5827. }
  5828. }
  5829. else if (lpdis->CtlID == lst1)
  5830. {
  5831. //
  5832. // Prep for TextOut below.
  5833. //
  5834. dxAcross = -dxSpace;
  5835. }
  5836. if (bSave && (lpdis->CtlID == lst1) && !rgbText)
  5837. {
  5838. HBRUSH hBrush = CreateSolidBrush(rgbBack);
  5839. HBRUSH hOldBrush;
  5840. nBackMode = SetBkMode(hdcList, TRANSPARENT);
  5841. hOldBrush = SelectObject( lpdis->hDC,
  5842. hBrush
  5843. ? hBrush
  5844. : GetStockObject(WHITE_BRUSH) );
  5845. FillRect(lpdis->hDC, (LPRECT)(&(lpdis->rcItem)), hBrush);
  5846. SelectObject(lpdis->hDC, hOldBrush);
  5847. if (hBrush)
  5848. {
  5849. DeleteObject(hBrush);
  5850. }
  5851. GrayString( lpdis->hDC,
  5852. GetStockObject(BLACK_BRUSH),
  5853. NULL,
  5854. (LPARAM)szText,
  5855. 0,
  5856. lpdis->rcItem.left + dxSpace,
  5857. lpdis->rcItem.top,
  5858. 0,
  5859. 0 );
  5860. SetBkMode(hdcList, nBackMode);
  5861. }
  5862. #if 0
  5863. else if (lpdis->CtlID == cmb2)
  5864. {
  5865. rcCmb2.right = rc.right;
  5866. rcCmb2.left = rc.left + (WORD)(dxSpace + dxAcross) + (dxSpace * nShift);
  5867. rcCmb2.top = rc.top + (dyItem - dyText) / 2;
  5868. rcCmb2.bottom = rc.top + nHeight;
  5869. DrawText( hdcList,
  5870. szText,
  5871. -1,
  5872. &rcCmb2,
  5873. DT_LEFT | DT_EXPANDTABS | DT_NOPREFIX );
  5874. }
  5875. #endif
  5876. else
  5877. {
  5878. //
  5879. // Draw the name.
  5880. //
  5881. ExtTextOut( hdcList,
  5882. rc.left + (WORD)(dxSpace + dxAcross) + dxSpace * nShift,
  5883. rc.top + (nHeight - dyText) / 2,
  5884. ETO_OPAQUE | ETO_CLIPPED,
  5885. (LPRECT)&rc,
  5886. szText,
  5887. lstrlen(szText),
  5888. NULL );
  5889. }
  5890. //
  5891. // Draw the picture.
  5892. //
  5893. if (lpdis->CtlID != lst1)
  5894. {
  5895. BitBlt( hdcList,
  5896. rc.left + dxSpace * nShift,
  5897. rc.top + (dyItem - dyDirDrive) / 2,
  5898. dxAcross,
  5899. dyDirDrive,
  5900. hdcMemory,
  5901. BltItem * dxAcross,
  5902. 0,
  5903. SRCCOPY );
  5904. }
  5905. SetTextColor(hdcList, rgbOldText);
  5906. SetBkColor(hdcList, rgbBack);
  5907. if (lpdis->itemState & ODS_FOCUS)
  5908. {
  5909. DrawFocusRect(hdcList, (LPRECT)&lpdis->rcItem);
  5910. }
  5911. }
  5912. ////////////////////////////////////////////////////////////////////////////
  5913. //
  5914. // SpacesExist
  5915. //
  5916. ////////////////////////////////////////////////////////////////////////////
  5917. BOOL SpacesExist(
  5918. LPTSTR szFileName)
  5919. {
  5920. while (*szFileName)
  5921. {
  5922. if (*szFileName == CHAR_SPACE)
  5923. {
  5924. return (TRUE);
  5925. }
  5926. else
  5927. {
  5928. szFileName++;
  5929. }
  5930. }
  5931. return (FALSE);
  5932. }
  5933. ////////////////////////////////////////////////////////////////////////////
  5934. //
  5935. // StripFileName
  5936. //
  5937. // Removes all but the filename from editbox contents.
  5938. // This is to be called before the user makes directory or drive
  5939. // changes by selecting them instead of typing them.
  5940. //
  5941. ////////////////////////////////////////////////////////////////////////////
  5942. void StripFileName(
  5943. HANDLE hDlg,
  5944. BOOL bWowApp)
  5945. {
  5946. TCHAR szText[MAX_FULLPATHNAME];
  5947. SHORT nFileOffset, cb;
  5948. if (GetDlgItemText(hDlg, edt1, szText, MAX_FULLPATHNAME - 1))
  5949. {
  5950. DWORD lRet;
  5951. lRet = ParseFile(szText, IsLFNDriveX(hDlg, szText), bWowApp, FALSE);
  5952. nFileOffset = LOWORD(lRet);
  5953. cb = HIWORD(lRet);
  5954. if (nFileOffset < 0)
  5955. {
  5956. //
  5957. // If there was a parsing error, check for CHAR_SEMICOLON
  5958. // delimeter.
  5959. //
  5960. if (szText[cb] == CHAR_SEMICOLON)
  5961. {
  5962. szText[cb] = CHAR_NULL;
  5963. nFileOffset = (WORD)ParseFile( szText,
  5964. IsLFNDriveX(hDlg, szText),
  5965. bWowApp,
  5966. FALSE );
  5967. szText[cb] = CHAR_SEMICOLON;
  5968. if (nFileOffset < 0)
  5969. {
  5970. //
  5971. // Still trouble, so Exit.
  5972. //
  5973. szText[0] = CHAR_NULL;
  5974. }
  5975. }
  5976. else
  5977. {
  5978. szText[0] = CHAR_NULL;
  5979. }
  5980. }
  5981. if (nFileOffset > 0)
  5982. {
  5983. lstrcpy(szText, (LPTSTR)(szText + nFileOffset));
  5984. }
  5985. if (nFileOffset)
  5986. {
  5987. SetDlgItemText(hDlg, edt1, szText);
  5988. }
  5989. }
  5990. }
  5991. ////////////////////////////////////////////////////////////////////////////
  5992. //
  5993. // lstrtok
  5994. //
  5995. ////////////////////////////////////////////////////////////////////////////
  5996. LPTSTR lstrtok(
  5997. LPTSTR lpStr,
  5998. LPCTSTR lpDelim)
  5999. {
  6000. static LPTSTR lpString;
  6001. LPTSTR lpRetVal, lpTemp;
  6002. //
  6003. // If we are passed new string skip leading delimiters.
  6004. //
  6005. if (lpStr)
  6006. {
  6007. lpString = lpStr;
  6008. while (*lpString && StrChr(lpDelim, *lpString))
  6009. {
  6010. lpString = CharNext(lpString);
  6011. }
  6012. }
  6013. //
  6014. // If there are no more tokens, return NULL.
  6015. //
  6016. if (!*lpString)
  6017. {
  6018. return (CHAR_NULL);
  6019. }
  6020. //
  6021. // Save head of token.
  6022. //
  6023. lpRetVal = lpString;
  6024. //
  6025. // Find delimiter or end of string.
  6026. //
  6027. while (*lpString && !StrChr(lpDelim, *lpString))
  6028. {
  6029. lpString = CharNext(lpString);
  6030. }
  6031. //
  6032. // If we found a delimiter insert string terminator and skip.
  6033. //
  6034. if (*lpString)
  6035. {
  6036. lpTemp = CharNext(lpString);
  6037. *lpString = CHAR_NULL;
  6038. lpString = lpTemp;
  6039. }
  6040. //
  6041. // Return token.
  6042. //
  6043. return (lpRetVal);
  6044. }
  6045. ////////////////////////////////////////////////////////////////////////////
  6046. //
  6047. // ChopText
  6048. //
  6049. ////////////////////////////////////////////////////////////////////////////
  6050. LPTSTR ChopText(
  6051. HWND hwndDlg,
  6052. int idStatic,
  6053. LPTSTR lpch)
  6054. {
  6055. RECT rc;
  6056. register int cxField;
  6057. BOOL fChop = FALSE;
  6058. HWND hwndStatic;
  6059. HDC hdc;
  6060. TCHAR chDrv;
  6061. HANDLE hOldFont;
  6062. LPTSTR lpstrStart = lpch;
  6063. SIZE Size;
  6064. BOOL bRet;
  6065. //
  6066. // Get length of static field.
  6067. //
  6068. hwndStatic = GetDlgItem(hwndDlg, idStatic);
  6069. GetClientRect(hwndStatic, (LPRECT)&rc);
  6070. cxField = rc.right - rc.left;
  6071. //
  6072. // Chop characters off front end of text until short enough.
  6073. //
  6074. hdc = GetDC(hwndStatic);
  6075. hOldFont = NULL;
  6076. while ((bRet = GetTextExtentPoint(hdc, lpch, lstrlen(lpch), &Size)) &&
  6077. (cxField < Size.cx))
  6078. {
  6079. if (!fChop)
  6080. {
  6081. chDrv = *lpch;
  6082. //
  6083. // Proportional font support.
  6084. //
  6085. if (bRet = GetTextExtentPoint(hdc, lpch, 7, &Size))
  6086. {
  6087. cxField -= Size.cx;
  6088. }
  6089. else
  6090. {
  6091. break;
  6092. }
  6093. if (cxField <= 0)
  6094. {
  6095. break;
  6096. }
  6097. lpch += 7;
  6098. }
  6099. while (*lpch && (!ISBACKSLASH_P(lpstrStart, lpch)))
  6100. {
  6101. lpch++;
  6102. }
  6103. //Skip the backslash
  6104. lpch++;
  6105. fChop = TRUE;
  6106. }
  6107. ReleaseDC(hwndStatic, hdc);
  6108. //
  6109. // If any characters chopped off, replace first three characters in
  6110. // remaining text string with ellipsis.
  6111. //
  6112. if (fChop)
  6113. {
  6114. //Skip back to include the backslash
  6115. lpch--;
  6116. *--lpch = CHAR_DOT;
  6117. *--lpch = CHAR_DOT;
  6118. *--lpch = CHAR_DOT;
  6119. *--lpch = *(lpstrStart + 2);
  6120. *--lpch = *(lpstrStart + 1);
  6121. *--lpch = *lpstrStart;
  6122. }
  6123. return (lpch);
  6124. }
  6125. ////////////////////////////////////////////////////////////////////////////
  6126. //
  6127. // FillOutPath
  6128. //
  6129. // Fills out lst2 given that the current directory has been set.
  6130. //
  6131. // Returns: TRUE if they DO NOT match
  6132. // FALSE if match
  6133. //
  6134. ////////////////////////////////////////////////////////////////////////////
  6135. BOOL FillOutPath(
  6136. HWND hList,
  6137. POPENFILEINFO pOFI)
  6138. {
  6139. TCHAR szPath[CCHNETPATH];
  6140. LPTSTR lpCurDir;
  6141. LPTSTR lpB, lpF;
  6142. TCHAR wc;
  6143. int cchPathOffset;
  6144. LPCURDLG lpCurDlg;
  6145. if(!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
  6146. !(lpCurDir = lpCurDlg->lpstrCurDir))
  6147. {
  6148. return (FALSE);
  6149. }
  6150. lpF = szPath;
  6151. lstrcpy(lpF, lpCurDir);
  6152. //
  6153. // Wow apps started from lfn dirs will set the current directory to an
  6154. // lfn, but only in the case where it is less than 8 chars.
  6155. //
  6156. if (pOFI->pOFN->Flags & OFN_NOLONGNAMES)
  6157. {
  6158. ShortenThePath(lpF);
  6159. }
  6160. *lpF = (TCHAR)CharLower((LPTSTR)*lpF);
  6161. cchPathOffset = GetPathOffset(lpF);
  6162. if (cchPathOffset == -1)
  6163. {
  6164. cchPathOffset = 0;
  6165. }
  6166. lpB = (lpF + cchPathOffset);
  6167. //
  6168. // Hack to retain Winball display functionality.
  6169. // Drived disks are displayed as C:\ (the root dir).
  6170. // whereas unc disks are displayed as \\server\share (the disk).
  6171. // Hence, extend display of drived disks by one char.
  6172. //
  6173. if (*(lpF + 1) == CHAR_COLON)
  6174. {
  6175. wc = *(++lpB);
  6176. *lpB = CHAR_NULL;
  6177. }
  6178. else
  6179. {
  6180. //
  6181. // Since we use lpF over and over again to speed things
  6182. // up, and since GetCurrentDirectory returns the disk name
  6183. // for unc, but the root path for drives, we have the following hack
  6184. // for when we are at the root of the unc directory, and lpF
  6185. // contains old stuff out past cchPathOffset.
  6186. //
  6187. PathAddBackslash(lpF);
  6188. wc = 0;
  6189. *lpB++ = CHAR_NULL;
  6190. }
  6191. //
  6192. // Insert the items for the path to the current dir
  6193. // Insert the root...
  6194. //
  6195. pOFI->idirSub = 0;
  6196. SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF);
  6197. if (wc)
  6198. {
  6199. *lpB = wc;
  6200. }
  6201. for (lpF = lpB; *lpB; lpB++)
  6202. {
  6203. if ((ISBACKSLASH_P(szPath, lpB)) || (*lpB == CHAR_SLASH))
  6204. {
  6205. *lpB = CHAR_NULL;
  6206. SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF);
  6207. lpF = lpB + 1;
  6208. *lpB = CHAR_BSLASH;
  6209. }
  6210. }
  6211. //
  6212. // Assumes that a path always ends with one last un-delimited dir name.
  6213. // Check to make sure we have at least one.
  6214. //
  6215. if (lpF != lpB)
  6216. {
  6217. SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF);
  6218. }
  6219. return (TRUE);
  6220. }
  6221. ////////////////////////////////////////////////////////////////////////////
  6222. //
  6223. // ShortenThePath
  6224. //
  6225. // Takes a pathname and converts all dirs to shortnames if they are
  6226. // not valid DOS 8.3 names.
  6227. //
  6228. // Returns: TRUE if pathname converted
  6229. // FALSE if ran out of space, buffer left alone
  6230. //
  6231. ////////////////////////////////////////////////////////////////////////////
  6232. BOOL ShortenThePath(
  6233. LPTSTR pPath)
  6234. {
  6235. TCHAR szDest[MAX_PATH];
  6236. LPTSTR pSrcNextSpec, pReplaceSpec;
  6237. LPTSTR pDest, p;
  6238. LPTSTR pSrc;
  6239. int cchPathOffset;
  6240. HANDLE hFind;
  6241. WIN32_FIND_DATA FindData;
  6242. UINT i;
  6243. int nSpaceLeft = MAX_PATH - 1;
  6244. #ifdef UNICODE
  6245. UNICODE_STRING Name;
  6246. BOOLEAN fSpace = FALSE;
  6247. #endif
  6248. //
  6249. // Save pointer to beginning of buffer.
  6250. //
  6251. pSrc = pPath;
  6252. //
  6253. // Eliminate double quotes.
  6254. //
  6255. for (p = pDest = pSrc; *p; p++, pDest++)
  6256. {
  6257. if (*p == CHAR_QUOTE)
  6258. {
  6259. p++;
  6260. }
  6261. *pDest = *p;
  6262. }
  6263. *pDest = CHAR_NULL;
  6264. //
  6265. // Strip out leading spaces.
  6266. //
  6267. while (*pSrc == CHAR_SPACE)
  6268. {
  6269. pSrc++;
  6270. }
  6271. //
  6272. // Skip past \\foo\bar or <drive>:
  6273. //
  6274. pDest = szDest;
  6275. pSrcNextSpec = pSrc;
  6276. //
  6277. // Reuse shell32 internal api that calculates path offset.
  6278. // The cchPathOffset variable will be the offset that when added to
  6279. // the pointer will result in a pointer to the backslash before the
  6280. // first part of the path.
  6281. //
  6282. // NOTE: UNICODE only call.
  6283. //
  6284. cchPathOffset = GetPathOffset(pSrc);
  6285. //
  6286. // Check to see if it's valid. If pSrc is not of the \\foo\bar
  6287. // or <drive>: form we just do nothing.
  6288. //
  6289. if (cchPathOffset == -1)
  6290. {
  6291. return (TRUE);
  6292. }
  6293. //
  6294. // cchPathOffset will always be at least 1 and is the number of
  6295. // characters - 1 that we want to copy (that is, if 0 was
  6296. // permissible, it would denote 1 character).
  6297. //
  6298. do
  6299. {
  6300. *pDest++ = *pSrcNextSpec++;
  6301. if (!--nSpaceLeft)
  6302. {
  6303. return (FALSE);
  6304. }
  6305. } while (cchPathOffset--);
  6306. //
  6307. // At this point, we have just the filenames that we can shorten:
  6308. // \\foo\bar\it\is\here -> it\is\here
  6309. // c:\angry\lions -> angry\lions
  6310. //
  6311. while (pSrcNextSpec)
  6312. {
  6313. //
  6314. // pReplaceSpec holds the current spec we need to replace.
  6315. // By default, if we can't find the altname, then just use this.
  6316. //
  6317. pReplaceSpec = pSrcNextSpec;
  6318. //
  6319. // Search for trailing "\"
  6320. // pSrcNextSpec will point to the next spec to fix.
  6321. // (*pSrcNextSpec = NULL if done)
  6322. //
  6323. while (*pSrcNextSpec && (!ISBACKSLASH_P(pReplaceSpec, pSrcNextSpec)))
  6324. {
  6325. pSrcNextSpec++;
  6326. }
  6327. if (*pSrcNextSpec)
  6328. {
  6329. //
  6330. // If there is more, then pSrcNextSpec should point to it.
  6331. // Also delimit this spec.
  6332. //
  6333. *pSrcNextSpec = CHAR_NULL;
  6334. }
  6335. else
  6336. {
  6337. pSrcNextSpec = NULL;
  6338. }
  6339. hFind = FindFirstFile(pSrc, &FindData);
  6340. //
  6341. // We could exit as soon as this FindFirstFileFails,
  6342. // but there's the special case of having execute
  6343. // without read permission. This would fail since the lfn
  6344. // is valid for lfn apps.
  6345. //
  6346. if (hFind != INVALID_HANDLE_VALUE)
  6347. {
  6348. FindClose(hFind);
  6349. #ifdef UNICODE
  6350. //
  6351. // See if it's not a legal 8.3 name or if there are spaces
  6352. // in the name. If either is true, use the alternate name.
  6353. //
  6354. RtlInitUnicodeString(&Name, FindData.cFileName);
  6355. if (!RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) || fSpace)
  6356. #endif
  6357. {
  6358. if (FindData.cAlternateFileName[0])
  6359. {
  6360. pReplaceSpec = FindData.cAlternateFileName;
  6361. }
  6362. }
  6363. }
  6364. i = lstrlen(pReplaceSpec);
  6365. nSpaceLeft -= i;
  6366. if (nSpaceLeft <= 0)
  6367. {
  6368. return (FALSE);
  6369. }
  6370. lstrcpy(pDest, pReplaceSpec);
  6371. pDest += i;
  6372. //
  6373. // Now replace the CHAR_NULL with a slash if necessary.
  6374. //
  6375. if (pSrcNextSpec)
  6376. {
  6377. *pSrcNextSpec++ = CHAR_BSLASH;
  6378. //
  6379. // Also add backslash to destination.
  6380. //
  6381. *pDest++ = CHAR_BSLASH;
  6382. nSpaceLeft--;
  6383. }
  6384. }
  6385. lstrcpy(pPath, szDest);
  6386. return (TRUE);
  6387. }
  6388. ////////////////////////////////////////////////////////////////////////////
  6389. //
  6390. // FListAll
  6391. //
  6392. // Given a file pattern, it changes the directory to that of the spec,
  6393. // and updates the display.
  6394. //
  6395. ////////////////////////////////////////////////////////////////////////////
  6396. int FListAll(
  6397. POPENFILEINFO pOFI,
  6398. HWND hDlg,
  6399. LPTSTR szSpec)
  6400. {
  6401. LPTSTR szPattern;
  6402. TCHAR chSave;
  6403. int nRet = 0;
  6404. TCHAR szDirBuf[MAX_FULLPATHNAME + 1];
  6405. if (!bCasePreserved)
  6406. {
  6407. CharLower(szSpec);
  6408. }
  6409. //
  6410. // No directory.
  6411. //
  6412. if (!(szPattern = StrRChr( szSpec,
  6413. szSpec + lstrlen(szSpec),
  6414. CHAR_BSLASH )) &&
  6415. !StrChr(szSpec, CHAR_COLON))
  6416. {
  6417. lstrcpy(pOFI->szSpecCur, szSpec);
  6418. if (!bInitializing)
  6419. {
  6420. UpdateListBoxes(hDlg, pOFI, szSpec, mskDirectory);
  6421. }
  6422. }
  6423. else
  6424. {
  6425. *szDirBuf = CHAR_NULL;
  6426. //
  6427. // Just root + pattern.
  6428. //
  6429. if (szPattern == StrChr(szSpec, CHAR_BSLASH))
  6430. {
  6431. if (!szPattern)
  6432. {
  6433. //
  6434. // Didn't find a slash, must have drive.
  6435. //
  6436. szPattern = CharNext(CharNext(szSpec));
  6437. }
  6438. else if ((szPattern == szSpec) ||
  6439. ((szPattern - 2 == szSpec) &&
  6440. (*(szSpec + 1) == CHAR_COLON)))
  6441. {
  6442. szPattern = CharNext(szPattern);
  6443. }
  6444. else
  6445. {
  6446. goto KillSlash;
  6447. }
  6448. chSave = *szPattern;
  6449. if (chSave != CHAR_DOT)
  6450. {
  6451. //
  6452. // If not c:.. or c:.
  6453. //
  6454. *szPattern = CHAR_NULL;
  6455. }
  6456. lstrcpy(szDirBuf, szSpec);
  6457. if (chSave == CHAR_DOT)
  6458. {
  6459. szPattern = szSpec + lstrlen(szSpec);
  6460. AppendExt(szPattern, pOFI->pOFN->lpstrDefExt, TRUE);
  6461. }
  6462. else
  6463. {
  6464. *szPattern = chSave;
  6465. }
  6466. }
  6467. else
  6468. {
  6469. KillSlash:
  6470. *szPattern++ = 0;
  6471. lstrcpy(szDirBuf, szSpec);
  6472. }
  6473. if ((nRet = ChangeDir(hDlg, szDirBuf, TRUE, FALSE)) < 0)
  6474. {
  6475. return (nRet);
  6476. }
  6477. lstrcpy(pOFI->szSpecCur, szPattern);
  6478. SetDlgItemText(hDlg, edt1, pOFI->szSpecCur);
  6479. SelDisk(hDlg, NULL);
  6480. if (!bInitializing)
  6481. {
  6482. SendMessage( hDlg,
  6483. WM_COMMAND,
  6484. GET_WM_COMMAND_MPS( cmb2,
  6485. GetDlgItem(hDlg, cmb2),
  6486. MYCBN_DRAW ) );
  6487. }
  6488. }
  6489. return (nRet);
  6490. }
  6491. ////////////////////////////////////////////////////////////////////////////
  6492. //
  6493. // ChangeDir
  6494. //
  6495. // Changes the current directory and/or resource.
  6496. //
  6497. // lpszDir - Fully qualified, or partially qualified names.
  6498. // To change to another disk and cd automatically to the
  6499. // last directory as set in the shell's environment, specify
  6500. // only a disk name (i.e. c: or \\triskal\scratch - must not end
  6501. // in backslash).
  6502. // bForce - If True, then caller requires that ChangeDir successfully cd
  6503. // somewhere. Order of cding is as follows:
  6504. // 1. lpszDir
  6505. // 2. current dir for the current thread
  6506. // 3. root of current dir for the current thread
  6507. // 4. c:
  6508. // bError - if TRUE, then pop up an AccessDenied dialog at every step
  6509. // in the force.
  6510. //
  6511. // Returns an index into gaDiskInfo for new disk chosen or,
  6512. // the ADDDISK_error code.
  6513. // Returns ADDDISK_NOCHANGE in the event that it cannot cd to the root
  6514. // directory of the specific file.
  6515. //
  6516. ////////////////////////////////////////////////////////////////////////////
  6517. int ChangeDir(
  6518. HWND hDlg,
  6519. LPCTSTR lpszDir,
  6520. BOOL bForce,
  6521. BOOL bError)
  6522. {
  6523. TCHAR szCurDir[CCHNETPATH];
  6524. LPTSTR lpCurDir;
  6525. int cchDirLen;
  6526. TCHAR wcDrive = 0;
  6527. int nIndex;
  6528. BOOL nRet;
  6529. LPCURDLG lpCurDlg;
  6530. //
  6531. // SheChangeDirEx will call GetCurrentDir, but will use what it
  6532. // gets only in the case where the path passed in was no good.
  6533. //
  6534. //
  6535. // 1st, try request.
  6536. //
  6537. if (lpszDir && *lpszDir)
  6538. {
  6539. lstrcpyn(szCurDir, lpszDir, CCHNETPATH);
  6540. //
  6541. // Remove trailing spaces.
  6542. //
  6543. lpCurDir = szCurDir + lstrlen(szCurDir) - 1;
  6544. while (*lpCurDir && (*lpCurDir == CHAR_SPACE))
  6545. {
  6546. *lpCurDir-- = CHAR_NULL;
  6547. }
  6548. nRet = SheChangeDirEx(szCurDir);
  6549. #ifdef UNICODE
  6550. if (nRet == ERROR_ACCESS_DENIED)
  6551. #else
  6552. if (!nRet)
  6553. #endif
  6554. {
  6555. if (bError)
  6556. {
  6557. //
  6558. // Casting to LPTSTR is ok below - InvalidFileWarning will
  6559. // not change this string because the path is always
  6560. // guaranteed to be <= MAX_FULLPATHNAME.
  6561. //
  6562. InvalidFileWarning( hDlg,
  6563. (LPTSTR)lpszDir,
  6564. ERROR_DIR_ACCESS_DENIED,
  6565. 0 );
  6566. }
  6567. if (!bForce)
  6568. {
  6569. return (CHANGEDIR_FAILED);
  6570. }
  6571. }
  6572. else
  6573. {
  6574. goto ChangeDir_OK;
  6575. }
  6576. }
  6577. //
  6578. // 2nd, try lpCurDlg->lpstrCurDir value (which we got above).
  6579. //
  6580. // !!! need to check for a null return value ???
  6581. //
  6582. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  6583. lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
  6584. nRet = SheChangeDirEx(lpCurDir);
  6585. #ifdef UNICODE
  6586. if (nRet == ERROR_ACCESS_DENIED)
  6587. #else
  6588. if (!nRet)
  6589. #endif
  6590. {
  6591. if (bError)
  6592. {
  6593. InvalidFileWarning( hDlg,
  6594. lpCurDir,
  6595. ERROR_DIR_ACCESS_DENIED,
  6596. 0 );
  6597. }
  6598. }
  6599. else
  6600. {
  6601. goto ChangeDir_OK;
  6602. }
  6603. //
  6604. // 3rd, try root of lpCurDlg->lpstrCurDir or GetCurrentDir (sanity).
  6605. //
  6606. lstrcpy(szCurDir, lpCurDir);
  6607. cchDirLen = GetPathOffset(szCurDir);
  6608. //
  6609. // Sanity check - it's guaranteed not to fail ...
  6610. //
  6611. if (cchDirLen != -1)
  6612. {
  6613. szCurDir[cchDirLen] = CHAR_BSLASH;
  6614. szCurDir[cchDirLen + 1] = CHAR_NULL;
  6615. nRet = SheChangeDirEx(szCurDir);
  6616. #ifdef UNICODE
  6617. if (nRet == ERROR_ACCESS_DENIED)
  6618. #else
  6619. if (!nRet)
  6620. #endif
  6621. {
  6622. if (bError)
  6623. {
  6624. InvalidFileWarning( hDlg,
  6625. (LPTSTR)lpszDir,
  6626. ERROR_DIR_ACCESS_DENIED,
  6627. 0 );
  6628. }
  6629. }
  6630. else
  6631. {
  6632. goto ChangeDir_OK;
  6633. }
  6634. }
  6635. //
  6636. // 4th, try c:
  6637. //
  6638. lstrcpy(szCurDir, TEXT("c:"));
  6639. nRet = SheChangeDirEx(szCurDir);
  6640. #ifdef UNICODE
  6641. if (nRet == ERROR_ACCESS_DENIED)
  6642. #else
  6643. if (!nRet)
  6644. #endif
  6645. {
  6646. if (bError)
  6647. {
  6648. InvalidFileWarning( hDlg,
  6649. (LPTSTR)lpszDir,
  6650. ERROR_DIR_ACCESS_DENIED,
  6651. 0 );
  6652. }
  6653. }
  6654. else
  6655. {
  6656. goto ChangeDir_OK;
  6657. }
  6658. return (CHANGEDIR_FAILED);
  6659. ChangeDir_OK:
  6660. GetCurrentDirectory(CCHNETPATH, szCurDir);
  6661. nIndex = DiskAddedPreviously(0, szCurDir);
  6662. //
  6663. // If the disk doesn't exist, add it.
  6664. //
  6665. if (nIndex == -1)
  6666. {
  6667. HWND hCmb2 = GetDlgItem(hDlg, cmb2);
  6668. LPTSTR lpszDisk = NULL;
  6669. DWORD dwType;
  6670. TCHAR wc1, wc2;
  6671. if (szCurDir[1] == CHAR_COLON)
  6672. {
  6673. wcDrive = szCurDir[0];
  6674. }
  6675. else
  6676. {
  6677. lpszDisk = &szCurDir[0];
  6678. }
  6679. cchDirLen = GetPathOffset(szCurDir);
  6680. if (cchDirLen != -1)
  6681. {
  6682. wc1 = szCurDir[cchDirLen];
  6683. wc2 = szCurDir[cchDirLen + 1];
  6684. szCurDir[cchDirLen] = CHAR_BSLASH;
  6685. szCurDir[cchDirLen + 1] = CHAR_NULL;
  6686. }
  6687. dwType = GetDiskIndex(GetDiskType(szCurDir));
  6688. if (cchDirLen != -1)
  6689. {
  6690. szCurDir[cchDirLen] = CHAR_NULL;
  6691. }
  6692. nIndex = AddDisk(wcDrive, lpszDisk, NULL, dwType);
  6693. SendMessage(hCmb2, WM_SETREDRAW, FALSE, 0L);
  6694. wNoRedraw |= 1;
  6695. SendMessage( hCmb2,
  6696. CB_SETITEMDATA,
  6697. (WPARAM)SendMessage(
  6698. hCmb2,
  6699. CB_ADDSTRING,
  6700. (WPARAM)0,
  6701. (LPARAM)(LPTSTR)gaDiskInfo[nIndex].lpAbbrName ),
  6702. (LPARAM)gaDiskInfo[nIndex].dwType );
  6703. if ((dwType != NETDRVBMP) && (dwType != REMDRVBMP))
  6704. {
  6705. gaDiskInfo[nIndex].bCasePreserved =
  6706. IsFileSystemCasePreserving(gaDiskInfo[nIndex].lpPath);
  6707. }
  6708. wNoRedraw &= ~1;
  6709. SendMessage(hCmb2, WM_SETREDRAW, TRUE, 0L);
  6710. if (cchDirLen != -1)
  6711. {
  6712. szCurDir[cchDirLen] = wc1;
  6713. szCurDir[cchDirLen + 1] = wc2;
  6714. }
  6715. }
  6716. else
  6717. {
  6718. //
  6719. // Validate the disk if it has been seen before.
  6720. //
  6721. // For unc names that fade away, refresh the cmb2 box.
  6722. //
  6723. if (!gaDiskInfo[nIndex].bValid)
  6724. {
  6725. gaDiskInfo[nIndex].bValid = TRUE;
  6726. SendDlgItemMessage(
  6727. hDlg,
  6728. cmb2,
  6729. CB_SETITEMDATA,
  6730. (WPARAM)SendDlgItemMessage(
  6731. hDlg,
  6732. cmb2,
  6733. CB_ADDSTRING,
  6734. (WPARAM)0,
  6735. (LPARAM)(LPTSTR)gaDiskInfo[nIndex].lpAbbrName ),
  6736. (LPARAM)gaDiskInfo[nIndex].dwType );
  6737. }
  6738. }
  6739. //
  6740. // Update our global concept of Case.
  6741. //
  6742. if (nIndex >= 0)
  6743. {
  6744. //
  6745. // Send special WOW message to indicate the directory has
  6746. // changed.
  6747. //
  6748. SendMessage(hDlg, msgWOWDIRCHANGE, 0, 0);
  6749. //
  6750. // Get pointer to current directory.
  6751. //
  6752. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  6753. lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
  6754. if (!lpCurDlg || !lpCurDir)
  6755. {
  6756. return (CHANGEDIR_FAILED);
  6757. }
  6758. bCasePreserved = gaDiskInfo[nIndex].bCasePreserved;
  6759. //
  6760. // In case the unc name already has a drive letter, correct
  6761. // lst2 display.
  6762. //
  6763. cchDirLen = 0;
  6764. //
  6765. // Compare with szCurDir since it's been lowercased.
  6766. //
  6767. if (DBL_BSLASH(szCurDir) &&
  6768. (*gaDiskInfo[nIndex].lpAbbrName != szCurDir[0]))
  6769. {
  6770. if ((cchDirLen = GetPathOffset(szCurDir)) != -1)
  6771. {
  6772. szCurDir[--cchDirLen] = CHAR_COLON;
  6773. szCurDir[--cchDirLen] = *gaDiskInfo[nIndex].lpAbbrName;
  6774. }
  6775. }
  6776. if ((gaDiskInfo[nIndex].dwType == CDDRVBMP) ||
  6777. (gaDiskInfo[nIndex].dwType == FLOPPYBMP))
  6778. {
  6779. if (*lpCurDir != gaDiskInfo[nIndex].wcDrive)
  6780. {
  6781. TCHAR szDrive[5];
  6782. //
  6783. // Get new volume info - should always succeed.
  6784. //
  6785. szDrive[0] = gaDiskInfo[nIndex].wcDrive;
  6786. szDrive[1] = CHAR_COLON;
  6787. szDrive[2] = CHAR_BSLASH;
  6788. szDrive[3] = CHAR_NULL;
  6789. UpdateLocalDrive(szDrive, TRUE);
  6790. //
  6791. // Flush to the cmb before selecting the disk.
  6792. //
  6793. if ( lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg) )
  6794. {
  6795. gahDlg[lpCurDlg->dwCurDlgNum] = hDlg;
  6796. FlushDiskInfoToCmb2();
  6797. }
  6798. }
  6799. }
  6800. lstrcpy(lpCurDir, (LPTSTR)&szCurDir[cchDirLen]);
  6801. PathAddBackslash(lpCurDir);
  6802. //
  6803. // If the worker thread is running, then trying to select here
  6804. // will just render the cmb2 blank, which is what we want;
  6805. // otherwise, it should successfully select it.
  6806. //
  6807. SelDisk(hDlg, gaDiskInfo[nIndex].lpPath);
  6808. }
  6809. // else
  6810. // {
  6811. // print out error message returned from AddDisk ...
  6812. // }
  6813. return (nIndex);
  6814. }
  6815. ////////////////////////////////////////////////////////////////////////////
  6816. //
  6817. // IsFileSystemCasePreserving
  6818. //
  6819. ////////////////////////////////////////////////////////////////////////////
  6820. BOOL IsFileSystemCasePreserving(
  6821. LPTSTR lpszDisk)
  6822. {
  6823. TCHAR szPath[MAX_FULLPATHNAME];
  6824. DWORD dwFlags;
  6825. if (!lpszDisk)
  6826. {
  6827. return (FALSE);
  6828. }
  6829. lstrcpy(szPath, lpszDisk);
  6830. lstrcat(szPath, TEXT("\\"));
  6831. if (GetVolumeInformation( szPath,
  6832. NULL,
  6833. 0,
  6834. NULL,
  6835. NULL,
  6836. &dwFlags,
  6837. NULL,
  6838. 0 ))
  6839. {
  6840. return ((dwFlags & FS_CASE_IS_PRESERVED));
  6841. }
  6842. //
  6843. // Default to FALSE if there is an error.
  6844. //
  6845. return (FALSE);
  6846. }
  6847. ////////////////////////////////////////////////////////////////////////////
  6848. //
  6849. // IsLFNDriveX
  6850. //
  6851. ////////////////////////////////////////////////////////////////////////////
  6852. BOOL IsLFNDriveX(
  6853. HWND hDlg,
  6854. LPTSTR szPath)
  6855. {
  6856. TCHAR szRootPath[MAX_FULLPATHNAME];
  6857. DWORD dwVolumeSerialNumber;
  6858. DWORD dwMaximumComponentLength;
  6859. DWORD dwFileSystemFlags;
  6860. LPTSTR lpCurDir;
  6861. LPCURDLG lpCurDlg;
  6862. if (!szPath[0] || !szPath[1] ||
  6863. (szPath[1] != CHAR_COLON && !(DBL_BSLASH(szPath))))
  6864. {
  6865. //
  6866. // If the path is not a full path then get the directory path
  6867. // from the TLS current directory.
  6868. //
  6869. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  6870. lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
  6871. lstrcpy(szRootPath, lpCurDir);
  6872. }
  6873. else
  6874. {
  6875. lstrcpy(szRootPath, szPath);
  6876. }
  6877. if (szRootPath[1] == CHAR_COLON)
  6878. {
  6879. szRootPath[2] = CHAR_BSLASH;
  6880. szRootPath[3] = 0;
  6881. }
  6882. else if (DBL_BSLASH(szRootPath))
  6883. {
  6884. int i;
  6885. LPTSTR p;
  6886. //
  6887. // Stop at "\\foo\bar".
  6888. //
  6889. for (i = 0, p = szRootPath + 2; *p && i < 2; p++)
  6890. {
  6891. if (ISBACKSLASH_P(szRootPath, p))
  6892. {
  6893. i++;
  6894. }
  6895. }
  6896. switch (i)
  6897. {
  6898. case ( 0 ) :
  6899. {
  6900. return (FALSE);
  6901. }
  6902. case ( 1 ) :
  6903. {
  6904. if (lstrlen(szRootPath) < MAX_FULLPATHNAME - 2)
  6905. {
  6906. *p = CHAR_BSLASH;
  6907. *(p + 1) = CHAR_NULL;
  6908. }
  6909. else
  6910. {
  6911. return (FALSE);
  6912. }
  6913. break;
  6914. }
  6915. case ( 2 ) :
  6916. {
  6917. *p = CHAR_NULL;
  6918. break;
  6919. }
  6920. }
  6921. }
  6922. if (GetVolumeInformation( szRootPath,
  6923. NULL,
  6924. 0,
  6925. &dwVolumeSerialNumber,
  6926. &dwMaximumComponentLength,
  6927. &dwFileSystemFlags,
  6928. NULL,
  6929. 0 ))
  6930. {
  6931. if (dwMaximumComponentLength == (MAXDOSFILENAMELEN - 1))
  6932. {
  6933. return (FALSE);
  6934. }
  6935. else
  6936. {
  6937. return (TRUE);
  6938. }
  6939. }
  6940. return (FALSE);
  6941. }
  6942. ////////////////////////////////////////////////////////////////////////////
  6943. //
  6944. // DiskAddedPreviously
  6945. //
  6946. // This routine checks to see if a disk resource has been previously
  6947. // added to the global structure.
  6948. //
  6949. // wcDrive - if this is set, then there is no lpszName comparison
  6950. // lpszName - if wcDrive is not set, but the lpszName is of the form
  6951. // "c:\" then set wcDrive = *lpszName and index by drive letter
  6952. // else assume lpszName is a unc name
  6953. //
  6954. // Returns: 0xFFFFFFFF failure (disk doesn't exist in list)
  6955. // 0 - 128 number of disk in list
  6956. //
  6957. ////////////////////////////////////////////////////////////////////////////
  6958. int DiskAddedPreviously(
  6959. TCHAR wcDrive,
  6960. LPTSTR lpszName)
  6961. {
  6962. WORD i;
  6963. //
  6964. // There are two index schemes (by drive or by unc \\server\share).
  6965. // If it doesn't have a drive letter, assume unc.
  6966. //
  6967. if (wcDrive || (lpszName && (*(lpszName + 1) == CHAR_COLON)))
  6968. {
  6969. if (!wcDrive)
  6970. {
  6971. wcDrive = *lpszName;
  6972. wcDrive = (TCHAR)CharLower((LPTSTR)wcDrive);
  6973. }
  6974. for (i = 0; i < dwNumDisks; i++)
  6975. {
  6976. //
  6977. // See if the drive letters are the same.
  6978. //
  6979. if (wcDrive)
  6980. {
  6981. if (wcDrive == (TCHAR)CharLower((LPTSTR)gaDiskInfo[i].wcDrive))
  6982. {
  6983. return (i);
  6984. }
  6985. }
  6986. }
  6987. }
  6988. else if (lpszName)
  6989. {
  6990. DWORD cchDirLen;
  6991. TCHAR wc;
  6992. //
  6993. // Check remote name (\\server\share).
  6994. //
  6995. cchDirLen = GetPathOffset(lpszName);
  6996. //
  6997. // If we're given a unc path, get the disk name.
  6998. // Otherwise, assume the whole thing is a disk name.
  6999. //
  7000. if (cchDirLen != -1)
  7001. {
  7002. wc = *(lpszName + cchDirLen);
  7003. *(lpszName + cchDirLen) = CHAR_NULL;
  7004. }
  7005. for (i = 0; i < dwNumDisks; i++)
  7006. {
  7007. if (!lstrcmpi(gaDiskInfo[i].lpName, lpszName))
  7008. {
  7009. if (cchDirLen != -1)
  7010. {
  7011. *(lpszName + cchDirLen) = wc;
  7012. }
  7013. return (i);
  7014. }
  7015. }
  7016. if (cchDirLen != -1)
  7017. {
  7018. *(lpszName + cchDirLen) = wc;
  7019. }
  7020. }
  7021. return (0xFFFFFFFF);
  7022. }
  7023. ////////////////////////////////////////////////////////////////////////////
  7024. //
  7025. // AddDisk
  7026. //
  7027. // Adds a disk to one of the global structures:
  7028. // gaNetDiskInfo
  7029. // gaLocalDiskInfo
  7030. //
  7031. // wcDrive - the drive to attach to (this parm should be 0 for unc)
  7032. // lpName - \\server\share name for remote disks
  7033. // volume name for local disks
  7034. // lpProvider - used for remote disks only, the name of the provider
  7035. // used with WNetFormatNetworkName api
  7036. // dwType - type of the bitmap to display
  7037. // except when we are adding a drive letter temporarily
  7038. // at startup this parameter can equal TMPNETDRV in which
  7039. // case we set the bitmap to NETDRVBMP
  7040. //
  7041. // Returns: -2 Cannot Add Disk
  7042. // -1 DiskInfo did not change
  7043. // 0 dwNumDisks - DiskInfo changed
  7044. //
  7045. ////////////////////////////////////////////////////////////////////////////
  7046. int AddDisk(
  7047. TCHAR wcDrive,
  7048. LPTSTR lpName,
  7049. LPTSTR lpProvider,
  7050. DWORD dwType)
  7051. {
  7052. int nIndex, nRet;
  7053. DWORD cchMultiLen = 0;
  7054. DWORD cchAbbrLen = 0;
  7055. DWORD cchLen;
  7056. DWORD dwRet = 0;
  7057. LPTSTR lpBuff;
  7058. OFN_DISKINFO *pofndiDisk = NULL, *pgDI;
  7059. //
  7060. // Sanity check - wcDrive and/or lpName must be set.
  7061. //
  7062. if (!wcDrive && (!lpName || !*lpName))
  7063. {
  7064. return (ADDDISK_INVALIDPARMS);
  7065. }
  7066. nIndex = DiskAddedPreviously(wcDrive, lpName);
  7067. if (nIndex != 0xFFFFFFFF)
  7068. {
  7069. //
  7070. // Do not add a temporary drive letter if we already
  7071. // have something better (added, for example, in a previous call).
  7072. //
  7073. if (dwType == TMPNETDRV)
  7074. {
  7075. gaDiskInfo[nIndex].bValid = TRUE;
  7076. return (ADDDISK_NOCHANGE);
  7077. }
  7078. // Using a floating profile, there can be collisions between
  7079. // local and network drives in which case we take the former.
  7080. //
  7081. // Note: If the drive is remembered, we assume that getdrivetype
  7082. // will return false and that the drive is not added.
  7083. // But if it was added, then we overwrite anyway,
  7084. // since it's the desired behavior.
  7085. //
  7086. if ((dwType == REMDRVBMP) &&
  7087. (dwType != gaDiskInfo[nIndex].dwType))
  7088. {
  7089. return (ADDDISK_NOCHANGE);
  7090. }
  7091. //
  7092. // Update previous connections.
  7093. //
  7094. if (!lstrcmpi(lpName, gaDiskInfo[nIndex].lpName))
  7095. {
  7096. //
  7097. // Don't update a connection as remembered, unless it's been
  7098. // invalidated.
  7099. //
  7100. if (dwType != REMDRVBMP)
  7101. {
  7102. gaDiskInfo[nIndex].dwType = dwType;
  7103. }
  7104. gaDiskInfo[nIndex].bValid = TRUE;
  7105. return (ADDDISK_NOCHANGE);
  7106. }
  7107. else if (!*lpName && ((dwType == CDDRVBMP) || (dwType == FLOPPYBMP)))
  7108. {
  7109. //
  7110. // Guard against lazy calls to updatelocaldrive erasing current
  7111. // changed dir volume name (set via changedir).
  7112. //
  7113. return (ADDDISK_NOCHANGE);
  7114. }
  7115. }
  7116. if (dwNumDisks >= MAX_DISKS)
  7117. {
  7118. return (ADDDISK_MAXNUMDISKS);
  7119. }
  7120. //
  7121. // If there is a drive, then lpPath needs only 4.
  7122. // If it's unc, then lpPath just equals lpName.
  7123. //
  7124. if (wcDrive)
  7125. {
  7126. cchLen = 4;
  7127. }
  7128. else
  7129. {
  7130. cchLen = 0;
  7131. }
  7132. if (lpName && *lpName)
  7133. {
  7134. //
  7135. // Get the length of the standard (Remote/Local) name.
  7136. //
  7137. cchLen += (lstrlen(lpName) + 1);
  7138. if (lpProvider && *lpProvider &&
  7139. ((dwType == NETDRVBMP) || (dwType == REMDRVBMP)))
  7140. {
  7141. //
  7142. // Get the length for the multiline name.
  7143. //
  7144. dwRet = WNetFormatNetworkName( lpProvider,
  7145. lpName,
  7146. NULL,
  7147. &cchMultiLen,
  7148. WNFMT_MULTILINE,
  7149. dwAveCharPerLine );
  7150. if (dwRet != ERROR_MORE_DATA)
  7151. {
  7152. return (ADDDISK_NETFORMATFAILED);
  7153. }
  7154. //
  7155. // Add 4 for <drive-letter>:\ and NULL (safeguard)
  7156. //
  7157. if (wcDrive)
  7158. {
  7159. cchMultiLen += 4;
  7160. }
  7161. dwRet = WNetFormatNetworkName( lpProvider,
  7162. lpName,
  7163. NULL,
  7164. &cchAbbrLen,
  7165. WNFMT_ABBREVIATED,
  7166. dwAveCharPerLine );
  7167. if (dwRet != ERROR_MORE_DATA)
  7168. {
  7169. return (ADDDISK_NETFORMATFAILED);
  7170. }
  7171. //
  7172. // Add 4 for <drive-letter>:\ and NULL (safeguard).
  7173. //
  7174. if (wcDrive)
  7175. {
  7176. cchAbbrLen += 4;
  7177. }
  7178. }
  7179. else
  7180. {
  7181. //
  7182. // Make enough room so that lpMulti and lpAbbr can point to
  7183. // 4 characters (drive letter + : + space + null) ahead of
  7184. // lpremote.
  7185. //
  7186. if (wcDrive)
  7187. {
  7188. cchLen += 4;
  7189. }
  7190. }
  7191. }
  7192. else
  7193. {
  7194. //
  7195. // Make enough room so that lpMulti and lpAbbr can point to
  7196. // 4 characters (drive letter + : + space + null) ahead of
  7197. // lpremote.
  7198. //
  7199. if (wcDrive)
  7200. {
  7201. cchLen += 4;
  7202. }
  7203. }
  7204. //
  7205. // Allocate a temp OFN_DISKINFO object to work with.
  7206. // When we are finished, we'll request the critical section
  7207. // and update the global array.
  7208. //
  7209. pofndiDisk = (OFN_DISKINFO *)LocalAlloc(LPTR, sizeof(OFN_DISKINFO));
  7210. if (!pofndiDisk)
  7211. {
  7212. //
  7213. // Can't alloc or realloc memory, return error.
  7214. //
  7215. nRet = ADDDISK_ALLOCFAILED;
  7216. goto AddDisk_Error;
  7217. }
  7218. lpBuff = (LPTSTR)LocalAlloc( LPTR,
  7219. (cchLen + cchMultiLen + cchAbbrLen) * sizeof(TCHAR));
  7220. if (!lpBuff)
  7221. {
  7222. //
  7223. // Can't alloc or realloc memory, return error.
  7224. //
  7225. nRet = ADDDISK_ALLOCFAILED;
  7226. goto AddDisk_Error;
  7227. }
  7228. if (dwType == TMPNETDRV)
  7229. {
  7230. pofndiDisk->dwType = NETDRVBMP;
  7231. }
  7232. else
  7233. {
  7234. pofndiDisk->dwType = dwType;
  7235. }
  7236. //
  7237. // Always set these slots, even though wcDrive can equal 0.
  7238. //
  7239. pofndiDisk->wcDrive = wcDrive;
  7240. pofndiDisk->bValid = TRUE;
  7241. pofndiDisk->cchLen = cchLen + cchAbbrLen + cchMultiLen;
  7242. //
  7243. // NOTE: lpAbbrName must always point to the head of lpBuff
  7244. // so that we can free the block later at DLL_PROCESS_DETACH
  7245. //
  7246. if (lpName && *lpName && lpProvider && *lpProvider &&
  7247. ((dwType == NETDRVBMP) || (dwType == REMDRVBMP)))
  7248. {
  7249. //
  7250. // Create an entry for a network disk.
  7251. //
  7252. pofndiDisk->lpAbbrName = lpBuff;
  7253. if (wcDrive)
  7254. {
  7255. *lpBuff++ = wcDrive;
  7256. *lpBuff++ = CHAR_COLON;
  7257. *lpBuff++ = CHAR_SPACE;
  7258. cchAbbrLen -= 3;
  7259. }
  7260. dwRet = WNetFormatNetworkName( lpProvider,
  7261. lpName,
  7262. lpBuff,
  7263. &cchAbbrLen,
  7264. WNFMT_ABBREVIATED,
  7265. dwAveCharPerLine );
  7266. if (dwRet != WN_SUCCESS)
  7267. {
  7268. nRet = ADDDISK_NETFORMATFAILED;
  7269. LocalFree(lpBuff);
  7270. goto AddDisk_Error;
  7271. }
  7272. lpBuff += cchAbbrLen;
  7273. pofndiDisk->lpMultiName = lpBuff;
  7274. if (wcDrive)
  7275. {
  7276. *lpBuff++ = wcDrive;
  7277. *lpBuff++ = CHAR_COLON;
  7278. *lpBuff++ = CHAR_SPACE;
  7279. cchMultiLen -= 3;
  7280. }
  7281. dwRet = WNetFormatNetworkName(lpProvider, lpName,
  7282. lpBuff, &cchMultiLen, WNFMT_MULTILINE, dwAveCharPerLine);
  7283. if (dwRet != WN_SUCCESS)
  7284. {
  7285. nRet = ADDDISK_NETFORMATFAILED;
  7286. LocalFree(lpBuff);
  7287. goto AddDisk_Error;
  7288. }
  7289. //
  7290. // Note: this assumes that the lpRemoteName
  7291. // returned by WNetEnumResources is always in
  7292. // the form \\server\share (without a trailing bslash).
  7293. //
  7294. pofndiDisk->lpPath = lpBuff;
  7295. //
  7296. // if it's not unc.
  7297. //
  7298. if (wcDrive)
  7299. {
  7300. *lpBuff++ = wcDrive;
  7301. *lpBuff++ = CHAR_COLON;
  7302. *lpBuff++ = CHAR_NULL;
  7303. }
  7304. lstrcpy(lpBuff, lpName);
  7305. pofndiDisk->lpName = lpBuff;
  7306. pofndiDisk->bCasePreserved =
  7307. IsFileSystemCasePreserving(pofndiDisk->lpPath);
  7308. }
  7309. else
  7310. {
  7311. //
  7312. // Create entry for a local name, or a network one with
  7313. // no name yet.
  7314. //
  7315. pofndiDisk->lpAbbrName = pofndiDisk->lpMultiName = lpBuff;
  7316. if (wcDrive)
  7317. {
  7318. *lpBuff++ = wcDrive;
  7319. *lpBuff++ = CHAR_COLON;
  7320. *lpBuff++ = CHAR_SPACE;
  7321. }
  7322. if (lpName)
  7323. {
  7324. lstrcpy(lpBuff, lpName);
  7325. }
  7326. else
  7327. {
  7328. *lpBuff = CHAR_NULL;
  7329. }
  7330. pofndiDisk->lpName = lpBuff;
  7331. if (wcDrive)
  7332. {
  7333. lpBuff += lstrlen(lpBuff) + 1;
  7334. *lpBuff = wcDrive;
  7335. *(lpBuff + 1) = CHAR_COLON;
  7336. *(lpBuff + 2) = CHAR_NULL;
  7337. }
  7338. pofndiDisk->lpPath = lpBuff;
  7339. if ((dwType == NETDRVBMP) || (dwType == REMDRVBMP))
  7340. {
  7341. pofndiDisk->bCasePreserved =
  7342. IsFileSystemCasePreserving(pofndiDisk->lpPath);
  7343. }
  7344. else
  7345. {
  7346. pofndiDisk->bCasePreserved = FALSE;
  7347. }
  7348. }
  7349. //
  7350. // Now we need to update the global array.
  7351. //
  7352. if (nIndex == 0xFFFFFFFF)
  7353. {
  7354. nIndex = dwNumDisks;
  7355. }
  7356. pgDI = &gaDiskInfo[nIndex];
  7357. //
  7358. // Enter critical section and update data.
  7359. //
  7360. EnterCriticalSection(&g_csLocal);
  7361. pgDI->cchLen = pofndiDisk->cchLen;
  7362. pgDI->lpAbbrName = pofndiDisk->lpAbbrName;
  7363. pgDI->lpMultiName = pofndiDisk->lpMultiName;
  7364. pgDI->lpName = pofndiDisk->lpName;
  7365. pgDI->lpPath = pofndiDisk->lpPath;
  7366. pgDI->wcDrive = pofndiDisk->wcDrive;
  7367. pgDI->bCasePreserved = pofndiDisk->bCasePreserved;
  7368. pgDI->dwType = pofndiDisk->dwType;
  7369. pgDI->bValid = pofndiDisk->bValid;
  7370. LeaveCriticalSection(&g_csLocal);
  7371. if ((DWORD)nIndex == dwNumDisks)
  7372. {
  7373. dwNumDisks++;
  7374. }
  7375. nRet = nIndex;
  7376. AddDisk_Error:
  7377. if (pofndiDisk)
  7378. {
  7379. LocalFree(pofndiDisk);
  7380. }
  7381. return (nRet);
  7382. }
  7383. ////////////////////////////////////////////////////////////////////////////
  7384. //
  7385. // EnableDiskInfo
  7386. //
  7387. ////////////////////////////////////////////////////////////////////////////
  7388. VOID EnableDiskInfo(
  7389. BOOL bValid,
  7390. BOOL bDoUnc)
  7391. {
  7392. DWORD dwCnt = dwNumDisks;
  7393. EnterCriticalSection(&g_csLocal);
  7394. while (dwCnt--)
  7395. {
  7396. if (gaDiskInfo[dwCnt].dwType == NETDRVBMP)
  7397. {
  7398. if (!(DBL_BSLASH(gaDiskInfo[dwCnt].lpAbbrName)) || bDoUnc)
  7399. {
  7400. gaDiskInfo[dwCnt].bValid = bValid;
  7401. }
  7402. //
  7403. // Always re-invalidate remembered just in case someone
  7404. // escapes from fileopen, removes a connection
  7405. // overriding a remembered and comes back expecting to see
  7406. // the original remembered.
  7407. //
  7408. }
  7409. }
  7410. LeaveCriticalSection(&g_csLocal);
  7411. }
  7412. ////////////////////////////////////////////////////////////////////////////
  7413. //
  7414. // FlushDiskInfoToCmb2
  7415. //
  7416. ////////////////////////////////////////////////////////////////////////////
  7417. VOID FlushDiskInfoToCmb2()
  7418. {
  7419. DWORD dwDisk;
  7420. DWORD dwDlg;
  7421. for (dwDlg = 0; dwDlg < dwNumDlgs; dwDlg++)
  7422. {
  7423. if (gahDlg[dwDlg])
  7424. {
  7425. HWND hCmb2;
  7426. if (hCmb2 = GetDlgItem(gahDlg[dwDlg], cmb2))
  7427. {
  7428. wNoRedraw |= 1;
  7429. SendMessage(hCmb2, WM_SETREDRAW, FALSE, 0L);
  7430. SendMessage(hCmb2, CB_RESETCONTENT, 0, 0);
  7431. dwDisk = dwNumDisks;
  7432. while (dwDisk--)
  7433. {
  7434. if (gaDiskInfo[dwDisk].bValid)
  7435. {
  7436. SendMessage(
  7437. hCmb2,
  7438. CB_SETITEMDATA,
  7439. (WPARAM)SendMessage(
  7440. hCmb2,
  7441. CB_ADDSTRING,
  7442. (WPARAM)0,
  7443. (LPARAM)(LPTSTR)gaDiskInfo[dwDisk].lpAbbrName ),
  7444. (LPARAM)gaDiskInfo[dwDisk].dwType );
  7445. }
  7446. }
  7447. wNoRedraw &= ~1;
  7448. SendMessage(hCmb2, WM_SETREDRAW, TRUE, 0L);
  7449. InvalidateRect(hCmb2, NULL, FALSE);
  7450. SendMessage( gahDlg[dwDlg],
  7451. WM_COMMAND,
  7452. GET_WM_COMMAND_MPS(cmb2, hCmb2, MYCBN_REPAINT) );
  7453. }
  7454. gahDlg[dwDlg] = NULL;
  7455. }
  7456. }
  7457. }
  7458. ////////////////////////////////////////////////////////////////////////////
  7459. //
  7460. // CallNetDlg
  7461. //
  7462. // Calls the appropriate network dialog in winnet driver.
  7463. //
  7464. // hwndParent - parent window of network dialog
  7465. //
  7466. // Returns: TRUE there are new drives to display
  7467. // FALSE there are no new drives to display
  7468. //
  7469. ////////////////////////////////////////////////////////////////////////////
  7470. BOOL CallNetDlg(
  7471. HWND hWnd)
  7472. {
  7473. DWORD wRet;
  7474. HourGlass(TRUE);
  7475. wRet = WNetConnectionDialog(hWnd, WNTYPE_DRIVE);
  7476. if ((wRet != WN_SUCCESS) && (wRet != WN_CANCEL) && (wRet != 0xFFFFFFFF))
  7477. {
  7478. if (!CDLoadString( g_hinst,
  7479. iszNoNetButtonResponse,
  7480. szCaption,
  7481. WARNINGMSGLENGTH ))
  7482. {
  7483. //
  7484. // !!!!! CAUTION
  7485. // The following is not portable between code pages.
  7486. //
  7487. wsprintf( szWarning,
  7488. TEXT("Error occurred, but error resource cannot be loaded.") );
  7489. }
  7490. else
  7491. {
  7492. wsprintf(szWarning, szCaption);
  7493. GetWindowText(hWnd, szCaption, WARNINGMSGLENGTH);
  7494. MessageBox( hWnd,
  7495. szWarning,
  7496. szCaption,
  7497. MB_OK | MB_ICONEXCLAMATION );
  7498. }
  7499. }
  7500. HourGlass(FALSE);
  7501. return (wRet == WN_SUCCESS);
  7502. }
  7503. ////////////////////////////////////////////////////////////////////////////
  7504. //
  7505. // GetDiskType
  7506. //
  7507. ////////////////////////////////////////////////////////////////////////////
  7508. UINT GetDiskType(
  7509. LPTSTR lpszDisk)
  7510. {
  7511. //
  7512. // Unfortunately GetDriveType is not for deviceless connections.
  7513. // So assume all unc stuff is just "remote" - no way of telling
  7514. // if it's a cdrom or not.
  7515. //
  7516. if (DBL_BSLASH(lpszDisk))
  7517. {
  7518. return (DRIVE_REMOTE);
  7519. }
  7520. else
  7521. {
  7522. return (GetDriveType(lpszDisk));
  7523. }
  7524. }
  7525. ////////////////////////////////////////////////////////////////////////////
  7526. //
  7527. // GetUNCDirectoryFromLB
  7528. //
  7529. // If lb contains a UNC listing, the function returns the full UNC path.
  7530. //
  7531. // Returns: 0 if no UNC listing in lb
  7532. // length of UNC listing string
  7533. //
  7534. ////////////////////////////////////////////////////////////////////////////
  7535. DWORD GetUNCDirectoryFromLB(
  7536. HWND hDlg,
  7537. WORD nLB,
  7538. POPENFILEINFO pOFI)
  7539. {
  7540. DWORD cch;
  7541. DWORD idir;
  7542. DWORD idirCurrent;
  7543. cch = (DWORD)SendDlgItemMessage( hDlg,
  7544. nLB,
  7545. LB_GETTEXT,
  7546. 0,
  7547. (LPARAM)(LPTSTR)pOFI->szPath );
  7548. //
  7549. // If not UNC listing, return 0.
  7550. //
  7551. if (pOFI->szPath[0] != CHAR_BSLASH)
  7552. {
  7553. return (0);
  7554. }
  7555. idirCurrent = (WORD)(DWORD)SendDlgItemMessage( hDlg,
  7556. nLB,
  7557. LB_GETCURSEL,
  7558. 0,
  7559. 0L );
  7560. if (idirCurrent < (pOFI->idirSub - 1))
  7561. {
  7562. pOFI->idirSub = idirCurrent;
  7563. }
  7564. pOFI->szPath[cch++] = CHAR_BSLASH;
  7565. for (idir = 1; idir < pOFI->idirSub; ++idir)
  7566. {
  7567. cch += (DWORD)SendDlgItemMessage( hDlg,
  7568. nLB,
  7569. LB_GETTEXT,
  7570. (WPARAM)idir,
  7571. (LPARAM)(LPTSTR)&pOFI->szPath[cch] );
  7572. pOFI->szPath[cch++] = CHAR_BSLASH;
  7573. }
  7574. //
  7575. // Only add the subdirectory if it's not the \\server\share point.
  7576. //
  7577. if (idirCurrent && (idirCurrent >= pOFI->idirSub))
  7578. {
  7579. cch += (DWORD)SendDlgItemMessage( hDlg,
  7580. nLB,
  7581. LB_GETTEXT,
  7582. (WPARAM)idirCurrent,
  7583. (LPARAM)(LPTSTR)&pOFI->szPath[cch] );
  7584. pOFI->szPath[cch++] = CHAR_BSLASH;
  7585. }
  7586. pOFI->szPath[cch] = CHAR_NULL;
  7587. return (cch);
  7588. }
  7589. ////////////////////////////////////////////////////////////////////////////
  7590. //
  7591. // SelDisk
  7592. //
  7593. // Selects the given disk in the combo drive list. Works for unc names,
  7594. // too.
  7595. //
  7596. ////////////////////////////////////////////////////////////////////////////
  7597. VOID SelDisk(
  7598. HWND hDlg,
  7599. LPTSTR lpszDisk)
  7600. {
  7601. HWND hCmb = GetDlgItem(hDlg, cmb2);
  7602. if (lpszDisk)
  7603. {
  7604. CharLower(lpszDisk);
  7605. SendMessage( hCmb,
  7606. CB_SETCURSEL,
  7607. (WPARAM)SendMessage( hCmb,
  7608. CB_FINDSTRING,
  7609. (WPARAM)-1,
  7610. (LPARAM)lpszDisk ),
  7611. 0L );
  7612. }
  7613. else
  7614. {
  7615. TCHAR szChangeSel[CCHNETPATH];
  7616. LPTSTR lpCurDir;
  7617. LPCURDLG lpCurDlg;
  7618. int cch = CCHNETPATH;
  7619. if ((lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) &&
  7620. (lpCurDir = lpCurDlg->lpstrCurDir))
  7621. {
  7622. lstrcpy(szChangeSel, lpCurDir);
  7623. GetCurrentDirectory(ARRAYSIZE(szChangeSel), szChangeSel);
  7624. if ((cch = GetPathOffset(szChangeSel)) != -1)
  7625. {
  7626. szChangeSel[cch] = CHAR_NULL;
  7627. }
  7628. SendMessage( hCmb,
  7629. CB_SETCURSEL,
  7630. (WPARAM)SendMessage( hCmb,
  7631. CB_FINDSTRING,
  7632. (WPARAM)-1,
  7633. (LPARAM)szChangeSel ),
  7634. 0L );
  7635. }
  7636. }
  7637. }
  7638. ////////////////////////////////////////////////////////////////////////////
  7639. //
  7640. // LNDSetEvent
  7641. //
  7642. ////////////////////////////////////////////////////////////////////////////
  7643. VOID LNDSetEvent(
  7644. HWND hDlg)
  7645. {
  7646. LPCURDLG lpCurDlg;
  7647. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  7648. if ( lpCurDlg &&
  7649. hLNDEvent &&
  7650. !wNoRedraw &&
  7651. hLNDThread &&
  7652. bNetworkInstalled)
  7653. {
  7654. gahDlg[lpCurDlg->dwCurDlgNum] = hDlg;
  7655. SetEvent(hLNDEvent);
  7656. }
  7657. }
  7658. ////////////////////////////////////////////////////////////////////////////
  7659. //
  7660. // UpdateLocalDrive
  7661. //
  7662. ////////////////////////////////////////////////////////////////////////////
  7663. VOID UpdateLocalDrive(
  7664. LPTSTR szDrive,
  7665. BOOL bGetVolName)
  7666. {
  7667. DWORD dwFlags = 0;
  7668. DWORD dwDriveType;
  7669. TCHAR szVolLabel[MAX_PATH];
  7670. //
  7671. // No unc here - so bypass extra call to GetDiskType and call
  7672. // GetDriveType directly.
  7673. //
  7674. dwDriveType = GetDriveType(szDrive);
  7675. if ((dwDriveType != 0) && (dwDriveType != 1))
  7676. {
  7677. BOOL bRet = TRUE;
  7678. szVolLabel[0] = CHAR_NULL;
  7679. szDrive[1] = CHAR_COLON;
  7680. szDrive[2] = CHAR_NULL;
  7681. if ( bGetVolName ||
  7682. ((dwDriveType != DRIVE_REMOVABLE) &&
  7683. (dwDriveType != DRIVE_CDROM) &&
  7684. (dwDriveType != DRIVE_REMOTE)) )
  7685. {
  7686. //
  7687. // Removing call to CharUpper since it causes trouble on
  7688. // turkish machines.
  7689. //
  7690. // CharUpper(szDrive);
  7691. if (GetFileAttributes(szDrive) != (DWORD)0xffffffff)
  7692. {
  7693. if (dwDriveType != DRIVE_REMOTE)
  7694. {
  7695. szDrive[2] = CHAR_BSLASH;
  7696. bRet = GetVolumeInformation( szDrive,
  7697. szVolLabel,
  7698. MAX_PATH,
  7699. NULL,
  7700. NULL,
  7701. &dwFlags,
  7702. NULL,
  7703. (DWORD)0 );
  7704. //
  7705. // The adddisk hack to prevent lazy loading from
  7706. // overwriting the current removable media's label
  7707. // with "" (because it never calls getvolumeinfo)
  7708. // is to not allow null lpnames to overwrite, so when
  7709. // the volume label really is null, we make it a space.
  7710. //
  7711. if (!szVolLabel[0])
  7712. {
  7713. szVolLabel[0] = CHAR_SPACE;
  7714. szVolLabel[1] = CHAR_NULL;
  7715. }
  7716. }
  7717. }
  7718. }
  7719. if (bRet)
  7720. {
  7721. int nIndex;
  7722. CharLower(szDrive);
  7723. if (dwDriveType == DRIVE_REMOTE)
  7724. {
  7725. nIndex = AddDisk( szDrive[0],
  7726. szVolLabel,
  7727. NULL,
  7728. TMPNETDRV );
  7729. }
  7730. else
  7731. {
  7732. nIndex = AddDisk( szDrive[0],
  7733. szVolLabel,
  7734. NULL,
  7735. GetDiskIndex(dwDriveType) );
  7736. }
  7737. if (nIndex != ADDDISK_NOCHANGE)
  7738. {
  7739. gaDiskInfo[nIndex].bCasePreserved =
  7740. (dwFlags & FS_CASE_IS_PRESERVED);
  7741. }
  7742. }
  7743. }
  7744. }
  7745. ////////////////////////////////////////////////////////////////////////////
  7746. //
  7747. // GetNetDrives
  7748. //
  7749. // Enumerates network disk resources and updates the global disk info
  7750. // structure.
  7751. //
  7752. // dwScope RESOURCE_CONNECTED or RESOURCE_REMEMBERED
  7753. //
  7754. // Returns the last connection that did not previously exist.
  7755. //
  7756. ////////////////////////////////////////////////////////////////////////////
  7757. VOID GetNetDrives(
  7758. DWORD dwScope)
  7759. {
  7760. DWORD dwRet;
  7761. HANDLE hEnum = NULL;
  7762. //
  7763. // Guard against termination with the enum handle open.
  7764. //
  7765. dwRet = WNetOpenEnum( dwScope,
  7766. RESOURCETYPE_DISK,
  7767. RESOURCEUSAGE_CONNECTABLE,
  7768. NULL,
  7769. &hEnum );
  7770. if (dwRet == WN_SUCCESS)
  7771. {
  7772. while (dwRet == WN_SUCCESS)
  7773. {
  7774. DWORD dwCount = 0xffffffff;
  7775. DWORD cbSize = cbNetEnumBuf;
  7776. if (bLNDExit)
  7777. {
  7778. WNetCloseEnum(hEnum);
  7779. return;
  7780. }
  7781. dwRet = WNetEnumResource(hEnum, &dwCount, gpcNetEnumBuf, &cbSize);
  7782. switch (dwRet)
  7783. {
  7784. case ( WN_SUCCESS ) :
  7785. {
  7786. //
  7787. // Add the Entries to the listbox.
  7788. //
  7789. TCHAR wcDrive = 0;
  7790. NETRESOURCE *pNetRes;
  7791. WORD i;
  7792. pNetRes = (LPNETRESOURCE)gpcNetEnumBuf;
  7793. for (i = 0; dwCount; dwCount--, i++)
  7794. {
  7795. if (pNetRes[i].lpLocalName)
  7796. {
  7797. CharLower(pNetRes[i].lpLocalName);
  7798. wcDrive = *pNetRes[i].lpLocalName;
  7799. }
  7800. else
  7801. {
  7802. //
  7803. // Skip deviceless names that are not
  7804. // LanMan provided (or, in the case where there
  7805. // is no LanMan provider name, skip deviceless
  7806. // always).
  7807. //
  7808. wcDrive = 0;
  7809. }
  7810. if (!DBL_BSLASH(pNetRes[i].lpRemoteName))
  7811. {
  7812. continue;
  7813. }
  7814. //
  7815. // When bGetNetDrivesSync is TRUE, we are coming back
  7816. // from the Network button, so we want to cd to the
  7817. // last connected drive.
  7818. // (see last command in this routine)
  7819. //
  7820. if (bGetNetDrivesSync)
  7821. {
  7822. int nIndex;
  7823. WORD k;
  7824. nIndex = AddDisk( wcDrive,
  7825. pNetRes[i].lpRemoteName,
  7826. pNetRes[i].lpProvider,
  7827. (dwScope == RESOURCE_REMEMBERED)
  7828. ? REMDRVBMP
  7829. : NETDRVBMP );
  7830. //
  7831. // If it's a new connection, update global state.
  7832. //
  7833. if (nIndex >= 0)
  7834. {
  7835. //
  7836. // Since flushdiskinfotocmb2 will clear out
  7837. // the array below, remember it's state here.
  7838. // It's a hack, but a nice way to find out
  7839. // exactly which of the many threads
  7840. // completed a net dlg operation.
  7841. //
  7842. for (k = 0; k < dwNumDlgs; k++)
  7843. {
  7844. if (gahDlg[k])
  7845. {
  7846. // Could encounter small problems with
  7847. // preemption here, but assume that
  7848. // user cannot simultaneously return
  7849. // from two different net dlg calls.
  7850. //
  7851. lpNetDriveSync = gaDiskInfo[nIndex].lpPath;
  7852. SendMessage(
  7853. gahDlg[k],
  7854. WM_COMMAND,
  7855. GET_WM_COMMAND_MPS(
  7856. cmb2,
  7857. GetDlgItem(gahDlg[k], cmb2),
  7858. MYCBN_CHANGEDIR ) );
  7859. }
  7860. }
  7861. }
  7862. }
  7863. else
  7864. {
  7865. AddDisk( wcDrive,
  7866. pNetRes[i].lpRemoteName,
  7867. pNetRes[i].lpProvider,
  7868. (dwScope == RESOURCE_REMEMBERED)
  7869. ? REMDRVBMP
  7870. : NETDRVBMP );
  7871. }
  7872. }
  7873. break;
  7874. }
  7875. case ( WN_MORE_DATA ) :
  7876. {
  7877. LPTSTR pcTemp;
  7878. pcTemp = (LPTSTR)LocalReAlloc( gpcNetEnumBuf,
  7879. cbSize,
  7880. LMEM_MOVEABLE );
  7881. if (!pcTemp)
  7882. {
  7883. cbNetEnumBuf = 0;
  7884. }
  7885. else
  7886. {
  7887. gpcNetEnumBuf = pcTemp;
  7888. cbNetEnumBuf = cbSize;
  7889. dwRet = WN_SUCCESS;
  7890. break;
  7891. }
  7892. }
  7893. case ( WN_NO_MORE_ENTRIES ) :
  7894. case ( WN_EXTENDED_ERROR ) :
  7895. case ( WN_NO_NETWORK ) :
  7896. {
  7897. //
  7898. // WN_NO_MORE_ENTRIES is a success error code.
  7899. // It is special cased when we fall out of the loop.
  7900. //
  7901. break;
  7902. }
  7903. case ( WN_BAD_HANDLE ) :
  7904. default :
  7905. {
  7906. break;
  7907. }
  7908. }
  7909. }
  7910. WNetCloseEnum(hEnum);
  7911. //
  7912. // Flush once per event - there will always be a call with
  7913. // dwscope = connected.
  7914. //
  7915. if (dwScope == RESOURCE_CONNECTED)
  7916. {
  7917. FlushDiskInfoToCmb2();
  7918. }
  7919. if (bGetNetDrivesSync)
  7920. {
  7921. bGetNetDrivesSync = FALSE;
  7922. }
  7923. }
  7924. }
  7925. #if 0
  7926. // See comments in ListNetDrivesHandler
  7927. ////////////////////////////////////////////////////////////////////////////
  7928. //
  7929. // HideNetButton
  7930. //
  7931. ////////////////////////////////////////////////////////////////////////////
  7932. VOID HideNetButton()
  7933. {
  7934. DWORD dwDlg;
  7935. HWND hNet;
  7936. for (dwDlg = 0; dwDlg < dwNumDlgs; dwDlg++)
  7937. {
  7938. hNet = GetDlgItem(gahDlg[dwDlg], psh14);
  7939. EnableWindow(hNet, FALSE);
  7940. ShowWindow(hNet, SW_HIDE);
  7941. }
  7942. }
  7943. #endif
  7944. ////////////////////////////////////////////////////////////////////////////
  7945. //
  7946. // ListNetDrivesHandler
  7947. //
  7948. ////////////////////////////////////////////////////////////////////////////
  7949. VOID ListNetDrivesHandler()
  7950. {
  7951. BOOL bInit = TRUE;
  7952. HANDLE hEnum = NULL;
  7953. if (!gpcNetEnumBuf &&
  7954. !(gpcNetEnumBuf = (LPTSTR)LocalAlloc(LPTR, cbNetEnumBuf)))
  7955. {
  7956. hLNDThread = NULL;
  7957. return;
  7958. }
  7959. if (bLNDExit)
  7960. {
  7961. goto LNDExitThread1;
  7962. }
  7963. EnterCriticalSection(&g_csNetThread);
  7964. while (1)
  7965. {
  7966. if (bLNDExit)
  7967. {
  7968. goto LNDExitThread;
  7969. }
  7970. //
  7971. // hLNDEvent will always be valid since we have loaded ourself
  7972. // and FreeLibrary will not produce a DLL_PROCESS_DETACH.
  7973. //
  7974. WaitForSingleObject(hLNDEvent, INFINITE);
  7975. //
  7976. // In case this is the exit event.
  7977. //
  7978. if (bLNDExit)
  7979. {
  7980. goto LNDExitThread;
  7981. }
  7982. EnableDiskInfo(FALSE, FALSE);
  7983. if (bInit)
  7984. {
  7985. GetNetDrives(RESOURCE_REMEMBERED);
  7986. //
  7987. // In case this is the exit event.
  7988. //
  7989. if (bLNDExit)
  7990. {
  7991. goto LNDExitThread;
  7992. }
  7993. GetNetDrives(RESOURCE_CONNECTED);
  7994. //
  7995. // In case this is the exit event.
  7996. //
  7997. if (bLNDExit)
  7998. {
  7999. goto LNDExitThread;
  8000. }
  8001. bInit = FALSE;
  8002. }
  8003. else
  8004. {
  8005. //
  8006. // In case this is the exit event.
  8007. //
  8008. if (bLNDExit)
  8009. {
  8010. goto LNDExitThread;
  8011. }
  8012. GetNetDrives(RESOURCE_CONNECTED);
  8013. //
  8014. // In case this is the exit event.
  8015. //
  8016. if (bLNDExit)
  8017. {
  8018. goto LNDExitThread;
  8019. }
  8020. }
  8021. ResetEvent(hLNDEvent);
  8022. }
  8023. LNDExitThread:
  8024. bLNDExit = FALSE;
  8025. LeaveCriticalSection(&g_csNetThread);
  8026. LNDExitThread1:
  8027. FreeLibraryAndExitThread(g_hinst, 1);
  8028. //
  8029. // The ExitThread is implicit in this return.
  8030. //
  8031. return;
  8032. }
  8033. ////////////////////////////////////////////////////////////////////////////
  8034. //
  8035. // LoadDrives
  8036. //
  8037. // Lists the current drives (connected) in the combo box.
  8038. //
  8039. ////////////////////////////////////////////////////////////////////////////
  8040. VOID LoadDrives(
  8041. HWND hDlg)
  8042. {
  8043. //
  8044. // Hard-code this - It's internal && always cmb2/psh14.
  8045. //
  8046. HWND hCmb = GetDlgItem(hDlg, cmb2);
  8047. DWORD dwThreadID;
  8048. LPCURDLG lpCurDlg;
  8049. BOOL bFirstAttach = FALSE;
  8050. WORD wCurDrive;
  8051. TCHAR szDrive[5];
  8052. if (!hLNDEvent)
  8053. {
  8054. //
  8055. // Don't check if this succeeds since we can run without the net.
  8056. //
  8057. hLNDEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  8058. bFirstAttach = TRUE;
  8059. }
  8060. else
  8061. {
  8062. //
  8063. // Assume all previous connections (except unc) are valid
  8064. // for first display - but only when they exist.
  8065. //
  8066. EnableDiskInfo(TRUE, FALSE);
  8067. }
  8068. //
  8069. // Set the hDlg into the refresh array before initially
  8070. // creating the thread so that the worker thread can hide/disable
  8071. // the net button in the event that there is no network.
  8072. //
  8073. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  8074. // sanity check
  8075. if (!lpCurDlg)
  8076. {
  8077. return;
  8078. }
  8079. gahDlg[lpCurDlg->dwCurDlgNum] = hDlg;
  8080. //
  8081. // If there is no worker thread for network disk enumeration,
  8082. // start up here rather than in the dll, since it's only
  8083. // for the fileopen dlg.
  8084. //
  8085. // Always start a thread if the number of active fileopen dialogs
  8086. // goes from 0 to 1
  8087. //
  8088. if ((lpCurDlg->dwCurDlgNum == 0) && (!hLNDThread))
  8089. {
  8090. if (hLNDEvent && (bNetworkInstalled = IsNetworkInstalled()))
  8091. {
  8092. TCHAR szModule[MAX_PATH];
  8093. //
  8094. // Do this once when dialog thread count goes from 0 to 1.
  8095. //
  8096. GetModuleFileName(g_hinst, szModule, ARRAYSIZE(szModule));
  8097. if (LoadLibrary(szModule))
  8098. {
  8099. hLNDThread = CreateThread(
  8100. NULL,
  8101. (DWORD)0,
  8102. (LPTHREAD_START_ROUTINE)ListNetDrivesHandler,
  8103. (LPVOID)NULL,
  8104. (DWORD_PTR)NULL,
  8105. &dwThreadID );
  8106. }
  8107. }
  8108. else
  8109. {
  8110. HWND hNet = GetDlgItem(hDlg, psh14);
  8111. EnableWindow(hNet, FALSE);
  8112. ShowWindow(hNet, SW_HIDE);
  8113. }
  8114. }
  8115. // Fix for Millenium BUG #113035
  8116. // Putting the get drives information code instead of in the
  8117. // ListNetDriveHandler thread.
  8118. //
  8119. // Get the drive information for all drives.
  8120. //
  8121. // NOTE: If we don't redo all volume info, then a change in a volume
  8122. // label will never be caught by wow apps unless wowexec is
  8123. // killed and restarted. Therefore, information for all drives
  8124. // should be retrieved here.
  8125. //
  8126. for (wCurDrive = 0; wCurDrive <= 25; wCurDrive++)
  8127. {
  8128. szDrive[0] = (CHAR_A + (TCHAR)wCurDrive);
  8129. szDrive[1] = CHAR_COLON;
  8130. szDrive[2] = CHAR_BSLASH;
  8131. szDrive[3] = CHAR_NULL;
  8132. UpdateLocalDrive(szDrive, FALSE);
  8133. }
  8134. FlushDiskInfoToCmb2();
  8135. //
  8136. // Now invalidate all net conns and re-enum, but only if there is
  8137. // indeed a worker thread too.
  8138. //
  8139. if (!bFirstAttach)
  8140. {
  8141. EnableDiskInfo(FALSE, FALSE);
  8142. }
  8143. LNDSetEvent(hDlg);
  8144. }
  8145. ////////////////////////////////////////////////////////////////////////////
  8146. //
  8147. // GetDiskIndex
  8148. //
  8149. ////////////////////////////////////////////////////////////////////////////
  8150. DWORD GetDiskIndex(
  8151. DWORD dwDriveType)
  8152. {
  8153. if (dwDriveType == 1)
  8154. {
  8155. //
  8156. // Drive doesn't exist!
  8157. //
  8158. return (0);
  8159. }
  8160. else if (dwDriveType == DRIVE_CDROM)
  8161. {
  8162. return (CDDRVBMP);
  8163. }
  8164. else if (dwDriveType == DRIVE_REMOVABLE)
  8165. {
  8166. return (FLOPPYBMP);
  8167. }
  8168. else if (dwDriveType == DRIVE_REMOTE)
  8169. {
  8170. return (NETDRVBMP);
  8171. }
  8172. else if (dwDriveType == DRIVE_RAMDISK)
  8173. {
  8174. return (RAMDRVBMP);
  8175. }
  8176. return (HARDDRVBMP);
  8177. }
  8178. ////////////////////////////////////////////////////////////////////////////
  8179. //
  8180. // CleanUpFile
  8181. //
  8182. // This releases the memory used by the system dialog bitmaps.
  8183. //
  8184. ////////////////////////////////////////////////////////////////////////////
  8185. VOID CleanUpFile()
  8186. {
  8187. //
  8188. // Check if anyone else is around.
  8189. //
  8190. if (--cLock)
  8191. {
  8192. return;
  8193. }
  8194. //
  8195. // Select the null bitmap into our memory DC so that the
  8196. // DirDrive bitmap can be discarded.
  8197. //
  8198. SelectObject(hdcMemory, hbmpOrigMemBmp);
  8199. }
  8200. ////////////////////////////////////////////////////////////////////////////
  8201. //
  8202. // FileOpenAbort
  8203. //
  8204. ////////////////////////////////////////////////////////////////////////////
  8205. VOID FileOpenAbort()
  8206. {
  8207. LPCURDLG lpCurDlg;
  8208. lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
  8209. if (lpCurDlg)
  8210. {
  8211. EnterCriticalSection(&g_csLocal);
  8212. if (dwNumDlgs > 0)
  8213. {
  8214. dwNumDlgs--;
  8215. }
  8216. if (dwNumDlgs == 0)
  8217. {
  8218. //
  8219. // If there are no more fileopen dialogs for this process,
  8220. // then signal the worker thread it's all over.
  8221. //
  8222. if (hLNDEvent && hLNDThread)
  8223. {
  8224. bLNDExit = TRUE;
  8225. SetEvent(hLNDEvent);
  8226. CloseHandle(hLNDThread);
  8227. hLNDThread = NULL;
  8228. }
  8229. }
  8230. LeaveCriticalSection(&g_csLocal);
  8231. }
  8232. }
  8233. ////////////////////////////////////////////////////////////////////////////
  8234. //
  8235. // TermFile
  8236. //
  8237. ////////////////////////////////////////////////////////////////////////////
  8238. VOID TermFile()
  8239. {
  8240. vDeleteDirDriveBitmap();
  8241. if (hdcMemory)
  8242. {
  8243. DeleteDC(hdcMemory);
  8244. }
  8245. if (hLNDEvent)
  8246. {
  8247. CloseHandle(hLNDEvent);
  8248. hLNDEvent = NULL;
  8249. }
  8250. if (gpcNetEnumBuf)
  8251. {
  8252. LocalFree(gpcNetEnumBuf);
  8253. }
  8254. while (dwNumDisks)
  8255. {
  8256. dwNumDisks--;
  8257. if (gaDiskInfo[dwNumDisks].lpAbbrName)
  8258. {
  8259. LocalFree(gaDiskInfo[dwNumDisks].lpAbbrName);
  8260. }
  8261. }
  8262. }
  8263. /*========================================================================*/
  8264. /* Ansi->Unicode Thunk routines */
  8265. /*========================================================================*/
  8266. #ifdef UNICODE
  8267. ////////////////////////////////////////////////////////////////////////////
  8268. //
  8269. // ThunkOpenFileNameA2WDelayed
  8270. //
  8271. ////////////////////////////////////////////////////////////////////////////
  8272. VOID ThunkOpenFileNameA2WDelayed(
  8273. POPENFILEINFO pOFI)
  8274. {
  8275. LPOPENFILENAMEA pOFNA = pOFI->pOFNA;
  8276. LPOPENFILENAMEW pOFNW = pOFI->pOFN;
  8277. if (pOFNA->lpstrDefExt)
  8278. {
  8279. //
  8280. // Make sure the default extension buffer is at least 4 characters
  8281. // in length.
  8282. //
  8283. DWORD cbLen = max(lstrlenA(pOFNA->lpstrDefExt) + 1, 4);
  8284. if (pOFNW->lpstrDefExt)
  8285. {
  8286. LocalFree((HLOCAL)pOFNW->lpstrDefExt);
  8287. }
  8288. if (!(pOFNW->lpstrDefExt = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
  8289. {
  8290. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8291. return;
  8292. }
  8293. else
  8294. {
  8295. if (pOFNA->lpstrDefExt)
  8296. {
  8297. SHAnsiToUnicode(pOFNA->lpstrDefExt,(LPWSTR)pOFNW->lpstrDefExt,cbLen );
  8298. }
  8299. }
  8300. }
  8301. //
  8302. // Need to thunk back to A value since Claris Filemaker side effects
  8303. // this in an ID_OK subclass without hooking at the very last moment.
  8304. // Do an |= instead of an = to preserve internal flags.
  8305. //
  8306. pOFNW->Flags &= OFN_ALL_INTERNAL_FLAGS;
  8307. pOFNW->Flags |= pOFNA->Flags;
  8308. }
  8309. ////////////////////////////////////////////////////////////////////////////
  8310. //
  8311. // ThunkOpenFileNameA2W
  8312. //
  8313. ////////////////////////////////////////////////////////////////////////////
  8314. BOOL ThunkOpenFileNameA2W(
  8315. POPENFILEINFO pOFI)
  8316. {
  8317. int nRet;
  8318. LPOPENFILENAMEA pOFNA = pOFI->pOFNA;
  8319. LPOPENFILENAMEW pOFNW = pOFI->pOFN;
  8320. pOFNW->Flags = pOFNA->Flags;
  8321. pOFNW->lCustData = pOFNA->lCustData;
  8322. // we actually can have the original ver1 structure passed in here
  8323. // so we need to check and make sure to only copy over the valid data
  8324. if ((pOFNA->lStructSize == SIZEOF(OPENFILENAMEA) && pOFNW->lStructSize == SIZEOF(OPENFILENAMEW))
  8325. )
  8326. {
  8327. pOFNW->pvReserved = pOFNA->pvReserved ;
  8328. pOFNW->dwReserved = pOFNA->dwReserved;
  8329. pOFNW->FlagsEx = pOFNA->FlagsEx;
  8330. }
  8331. //
  8332. // Various WOW apps change the strings and *ptrs* to the strings in the
  8333. // OPENFILENAME struct while processing messages with their hook procs.
  8334. // Handle that silliness here. (We probably don't want to promote this
  8335. // beyond WOW).
  8336. //
  8337. if (pOFNA->Flags & CD_WOWAPP)
  8338. {
  8339. pOFNW->lpstrFilter = (LPCWSTR)
  8340. ThunkMultiANSIStrToWIDE( (LPWSTR)pOFNW->lpstrFilter,
  8341. (LPSTR)pOFNA->lpstrFilter,
  8342. 0 );
  8343. pOFNW->lpstrCustomFilter =
  8344. ThunkMultiANSIStrToWIDE( pOFNW->lpstrCustomFilter,
  8345. pOFNA->lpstrCustomFilter,
  8346. pOFNA->nMaxCustFilter );
  8347. pOFNW->lpstrFile =
  8348. ThunkANSIStrToWIDE( pOFNW->lpstrFile,
  8349. pOFNA->lpstrFile,
  8350. pOFNA->nMaxFile );
  8351. pOFNW->lpstrFileTitle =
  8352. ThunkANSIStrToWIDE( pOFNW->lpstrFileTitle,
  8353. pOFNA->lpstrFileTitle,
  8354. pOFNA->nMaxFileTitle );
  8355. pOFNW->lpstrInitialDir = (LPCWSTR)
  8356. ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrInitialDir,
  8357. (LPSTR)pOFNA->lpstrInitialDir,
  8358. 0 );
  8359. pOFNW->lpstrTitle = (LPCWSTR)
  8360. ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrTitle,
  8361. (LPSTR)pOFNA->lpstrTitle,
  8362. 0 );
  8363. pOFNW->lpstrDefExt = (LPCWSTR)
  8364. ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrDefExt,
  8365. (LPSTR)pOFNA->lpstrDefExt,
  8366. 0 );
  8367. pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter;
  8368. pOFNW->nMaxFile = pOFNA->nMaxFile;
  8369. pOFNW->nMaxFileTitle = pOFNA->nMaxFileTitle;
  8370. pOFNW->nFileOffset = pOFNA->nFileOffset;
  8371. pOFNW->nFileExtension = pOFNA->nFileExtension;
  8372. }
  8373. else
  8374. {
  8375. if (pOFNW->lpstrFile)
  8376. {
  8377. if (pOFNA->lpstrFile)
  8378. {
  8379. nRet = SHAnsiToUnicode(pOFNA->lpstrFile,pOFNW->lpstrFile,pOFNW->nMaxFile );
  8380. if (nRet == 0)
  8381. {
  8382. return (FALSE);
  8383. }
  8384. }
  8385. }
  8386. if (pOFNW->lpstrFileTitle && pOFNW->nMaxFileTitle)
  8387. {
  8388. if (pOFNA->lpstrFileTitle)
  8389. {
  8390. nRet=MultiByteToWideChar(CP_ACP,
  8391. 0,
  8392. pOFNA->lpstrFileTitle,
  8393. pOFNA->nMaxFileTitle,
  8394. pOFNW->lpstrFileTitle,
  8395. pOFNW->nMaxFileTitle);
  8396. if (nRet == 0)
  8397. {
  8398. return (FALSE);
  8399. }
  8400. }
  8401. }
  8402. if (pOFNW->lpstrCustomFilter)
  8403. {
  8404. if (pOFI->pasCustomFilter)
  8405. {
  8406. LPSTR psz = pOFI->pasCustomFilter->Buffer;
  8407. DWORD cch = 0;
  8408. if (*psz || *(psz + 1))
  8409. {
  8410. cch = 2;
  8411. while (*psz || *(psz + 1))
  8412. {
  8413. psz++;
  8414. cch++;
  8415. }
  8416. }
  8417. if (cch)
  8418. {
  8419. pOFI->pasCustomFilter->Length = cch;
  8420. nRet = MultiByteToWideChar(CP_ACP,
  8421. 0,
  8422. pOFI->pasCustomFilter->Buffer,
  8423. pOFI->pasCustomFilter->Length,
  8424. pOFI->pusCustomFilter->Buffer,
  8425. pOFI->pusCustomFilter->MaximumLength );
  8426. if (nRet == 0)
  8427. {
  8428. return (FALSE);
  8429. }
  8430. }
  8431. }
  8432. }
  8433. }
  8434. pOFNW->nFilterIndex = pOFNA->nFilterIndex;
  8435. return (TRUE);
  8436. }
  8437. ////////////////////////////////////////////////////////////////////////////
  8438. //
  8439. // ThunkOpenFileNameW2A
  8440. //
  8441. ////////////////////////////////////////////////////////////////////////////
  8442. BOOL ThunkOpenFileNameW2A(
  8443. POPENFILEINFO pOFI)
  8444. {
  8445. int nRet;
  8446. LPOPENFILENAMEW pOFNW = pOFI->pOFN;
  8447. LPOPENFILENAMEA pOFNA = pOFI->pOFNA;
  8448. LPWSTR pszW;
  8449. USHORT cch;
  8450. //
  8451. // Supposedly invariant, but not necessarily.
  8452. // Definition: invariant - changed by 16-bit apps frequently
  8453. //
  8454. pOFNA->Flags = pOFNW->Flags;
  8455. pOFNA->lCustData = pOFNW->lCustData;
  8456. // this way we can assert that we are covered.
  8457. DEBUG_CODE(pOFNA->nFileOffset = 0 );
  8458. // we actually can have the original ver1 structure passed in here
  8459. // so we need to check and make sure to only copy over the valid data
  8460. if (pOFNA->lStructSize == SIZEOF(OPENFILENAMEA) && pOFNW->lStructSize == SIZEOF(OPENFILENAMEW)
  8461. )
  8462. {
  8463. pOFNA->pvReserved = pOFNW->pvReserved;
  8464. pOFNA->dwReserved = pOFNW->dwReserved;
  8465. pOFNA->FlagsEx = pOFNW->FlagsEx;
  8466. }
  8467. if (pOFNA->lpstrFileTitle && pOFNA->nMaxFileTitle)
  8468. {
  8469. nRet = SHUnicodeToAnsi(pOFNW->lpstrFileTitle,pOFNA->lpstrFileTitle,pOFNA->nMaxFileTitle);
  8470. if (nRet == 0)
  8471. {
  8472. return (FALSE);
  8473. }
  8474. }
  8475. if (pOFNA->lpstrCustomFilter)
  8476. {
  8477. pszW = pOFI->pusCustomFilter->Buffer;
  8478. cch = 0;
  8479. if (*pszW || *(pszW + 1))
  8480. {
  8481. cch = 2;
  8482. while (*pszW || *(pszW + 1))
  8483. {
  8484. pszW++;
  8485. cch++;
  8486. }
  8487. }
  8488. if (cch)
  8489. {
  8490. pOFI->pusCustomFilter->Length = cch;
  8491. nRet = WideCharToMultiByte(CP_ACP,
  8492. 0,
  8493. pOFI->pusCustomFilter->Buffer,
  8494. pOFI->pusCustomFilter->Length,
  8495. pOFI->pasCustomFilter->Buffer,
  8496. pOFI->pasCustomFilter->MaximumLength,
  8497. NULL,
  8498. NULL);
  8499. if (nRet == 0)
  8500. {
  8501. return (FALSE);
  8502. }
  8503. }
  8504. }
  8505. pOFNA->nFilterIndex = pOFNW->nFilterIndex;
  8506. if (pOFNA->lpstrFile && pOFNW->lpstrFile)
  8507. {
  8508. if (GetStoredExtendedError() == FNERR_BUFFERTOOSMALL)
  8509. {
  8510. //
  8511. // In the case where the lpstrFile buffer is too small,
  8512. // lpstrFile contains the size of the buffer needed for
  8513. // the string rather than the string itself.
  8514. //
  8515. pszW = pOFNW->lpstrFile;
  8516. switch (pOFNA->nMaxFile)
  8517. {
  8518. case ( 3 ) :
  8519. default :
  8520. {
  8521. pOFNA->lpstrFile[2] = CHAR_NULL;
  8522. // fall thru...
  8523. }
  8524. case ( 2 ) :
  8525. {
  8526. pOFNA->lpstrFile[1] = HIBYTE(*pszW);
  8527. // fall thru...
  8528. }
  8529. case ( 1 ) :
  8530. {
  8531. pOFNA->lpstrFile[0] = LOBYTE(*pszW);
  8532. // fall thru...
  8533. }
  8534. case ( 0 ) :
  8535. {
  8536. break;
  8537. }
  8538. }
  8539. }
  8540. else
  8541. {
  8542. LPWSTR pFileW = pOFNW->lpstrFile;
  8543. DWORD cchFile = 0;
  8544. // Find the length of string to be converted. This takes care of both single select (there will be only string)
  8545. // and multiselect case (there will multiple strings with double null termination)
  8546. while (*pFileW)
  8547. {
  8548. DWORD cch = lstrlenW(pFileW) +1;
  8549. cchFile +=cch;
  8550. pFileW += cch;
  8551. }
  8552. if (pOFNW->Flags & OFN_ALLOWMULTISELECT)
  8553. {
  8554. // for the double null terminator
  8555. cchFile++;
  8556. }
  8557. // need to copy the whole buffer after the initial directory
  8558. nRet =WideCharToMultiByte(CP_ACP,
  8559. 0,
  8560. pOFNW->lpstrFile,
  8561. cchFile,
  8562. pOFNA->lpstrFile, pOFNA->nMaxFile,
  8563. NULL, NULL);
  8564. if (nRet == 0)
  8565. {
  8566. return (FALSE);
  8567. }
  8568. if ((SHORT)pOFNW->nFileOffset > 0)
  8569. {
  8570. pOFNA->nFileOffset = (WORD) WideCharToMultiByte( CP_ACP,
  8571. 0,
  8572. pOFNW->lpstrFile,
  8573. pOFNW->nFileOffset,
  8574. NULL,
  8575. 0,
  8576. NULL,
  8577. NULL );
  8578. }
  8579. else
  8580. {
  8581. pOFNA->nFileOffset = pOFNW->nFileOffset;
  8582. }
  8583. if ((SHORT)pOFNW->nFileExtension > 0)
  8584. {
  8585. pOFNA->nFileExtension = (WORD) WideCharToMultiByte( CP_ACP,
  8586. 0,
  8587. pOFNW->lpstrFile,
  8588. pOFNW->nFileExtension,
  8589. NULL,
  8590. 0,
  8591. NULL,
  8592. NULL );
  8593. }
  8594. else
  8595. {
  8596. pOFNA->nFileExtension = pOFNW->nFileExtension;
  8597. }
  8598. }
  8599. }
  8600. else
  8601. {
  8602. pOFNA->nFileOffset = pOFNW->nFileOffset;
  8603. pOFNA->nFileExtension = pOFNW->nFileExtension;
  8604. }
  8605. return (TRUE);
  8606. }
  8607. ////////////////////////////////////////////////////////////////////////////
  8608. //
  8609. // GenericGetFileNameA
  8610. //
  8611. ////////////////////////////////////////////////////////////////////////////
  8612. BOOL GenericGetFileNameA(
  8613. LPOPENFILENAMEA pOFNA,
  8614. DLGPROC qfnDlgProc)
  8615. {
  8616. LPOPENFILENAMEW pOFNW;
  8617. BOOL bRet = FALSE;
  8618. OFN_UNICODE_STRING usCustomFilter;
  8619. OFN_ANSI_STRING asCustomFilter;
  8620. DWORD cbLen;
  8621. LPSTR pszA;
  8622. DWORD cch;
  8623. LPBYTE pStrMem = NULL;
  8624. OPENFILEINFO OFI = {0};
  8625. if (!pOFNA)
  8626. {
  8627. StoreExtendedError(CDERR_INITIALIZATION);
  8628. return (FALSE);
  8629. }
  8630. //Set the Open File Version
  8631. OFI.iVersion = OPENFILEVERSION;
  8632. if (pOFNA->lStructSize == OPENFILENAME_SIZE_VERSION_400)
  8633. {
  8634. OFI.iVersion = OPENFILEVERSION_NT4;
  8635. }
  8636. // we allow both sizes because we allocate a full size one anyway
  8637. // and we want to preserve the original structure for notifies
  8638. if ((pOFNA->lStructSize != OPENFILENAME_SIZE_VERSION_400) &&
  8639. (pOFNA->lStructSize != sizeof(OPENFILENAMEA))
  8640. )
  8641. {
  8642. StoreExtendedError(CDERR_STRUCTSIZE);
  8643. return (FALSE);
  8644. }
  8645. if (!(pOFNW = (LPOPENFILENAMEW)LocalAlloc(LPTR, sizeof(OPENFILENAMEW))))
  8646. {
  8647. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8648. return (FALSE);
  8649. }
  8650. //
  8651. // Constant stuff.
  8652. //
  8653. pOFNW->lStructSize = sizeof(OPENFILENAMEW);
  8654. pOFNW->hwndOwner = pOFNA->hwndOwner;
  8655. pOFNW->hInstance = pOFNA->hInstance;
  8656. pOFNW->lpfnHook = pOFNA->lpfnHook;
  8657. // it will always be a valid structsize at this point
  8658. if (pOFNA->lStructSize != OPENFILENAME_SIZE_VERSION_400)
  8659. {
  8660. pOFNW->pvReserved = pOFNA->pvReserved;
  8661. pOFNW->dwReserved = pOFNA->dwReserved;
  8662. pOFNW->FlagsEx = pOFNA->FlagsEx;
  8663. }
  8664. //
  8665. // Init TemplateName constant.
  8666. //
  8667. if (pOFNA->Flags & OFN_ENABLETEMPLATE)
  8668. {
  8669. if (!IS_INTRESOURCE(pOFNA->lpTemplateName))
  8670. {
  8671. cbLen = lstrlenA(pOFNA->lpTemplateName) + 1;
  8672. if (!(pOFNW->lpTemplateName = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
  8673. {
  8674. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8675. goto GenericExit;
  8676. }
  8677. else
  8678. {
  8679. SHAnsiToUnicode(pOFNA->lpTemplateName,(LPWSTR)pOFNW->lpTemplateName,cbLen);
  8680. }
  8681. }
  8682. else
  8683. {
  8684. (DWORD_PTR)pOFNW->lpTemplateName = (DWORD_PTR)pOFNA->lpTemplateName;
  8685. }
  8686. }
  8687. else
  8688. {
  8689. pOFNW->lpTemplateName = NULL;
  8690. }
  8691. //
  8692. // Initialize Initial Dir constant.
  8693. //
  8694. if (pOFNA->lpstrInitialDir)
  8695. {
  8696. cbLen = lstrlenA(pOFNA->lpstrInitialDir) + 1;
  8697. if (!(pOFNW->lpstrInitialDir = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
  8698. {
  8699. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8700. goto GenericExit;
  8701. }
  8702. else
  8703. {
  8704. SHAnsiToUnicode(pOFNA->lpstrInitialDir,(LPWSTR)pOFNW->lpstrInitialDir,cbLen);
  8705. }
  8706. }
  8707. else
  8708. {
  8709. pOFNW->lpstrInitialDir = NULL;
  8710. }
  8711. //
  8712. // Initialize Title constant.
  8713. //
  8714. if (pOFNA->lpstrTitle)
  8715. {
  8716. cbLen = lstrlenA(pOFNA->lpstrTitle) + 1;
  8717. if (!(pOFNW->lpstrTitle = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
  8718. {
  8719. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8720. goto GenericExit;
  8721. }
  8722. else
  8723. {
  8724. SHAnsiToUnicode(pOFNA->lpstrTitle,(LPWSTR)pOFNW->lpstrTitle,cbLen );
  8725. }
  8726. }
  8727. else
  8728. {
  8729. pOFNW->lpstrTitle = NULL;
  8730. }
  8731. //
  8732. // Initialize Def Ext constant.
  8733. //
  8734. if (pOFNA->lpstrDefExt)
  8735. {
  8736. //
  8737. // Make sure the default extension buffer is at least 4 characters
  8738. // in length.
  8739. //
  8740. cbLen = max(lstrlenA(pOFNA->lpstrDefExt) + 1, 4);
  8741. if (!(pOFNW->lpstrDefExt = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
  8742. {
  8743. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8744. goto GenericExit;
  8745. }
  8746. else
  8747. {
  8748. SHAnsiToUnicode(pOFNA->lpstrDefExt,(LPWSTR)pOFNW->lpstrDefExt,cbLen );
  8749. }
  8750. }
  8751. else
  8752. {
  8753. pOFNW->lpstrDefExt = NULL;
  8754. }
  8755. //
  8756. // Initialize Filter constant. Note: 16-bit apps change this.
  8757. //
  8758. if (pOFNA->lpstrFilter)
  8759. {
  8760. pszA = (LPSTR)pOFNA->lpstrFilter;
  8761. cch = 0;
  8762. if (*pszA || *(pszA + 1))
  8763. {
  8764. //
  8765. // Pick up trailing nulls.
  8766. //
  8767. cch = 2;
  8768. try
  8769. {
  8770. while (*pszA || *(pszA + 1))
  8771. {
  8772. pszA++;
  8773. cch++;
  8774. }
  8775. }
  8776. except (EXCEPTION_EXECUTE_HANDLER)
  8777. {
  8778. StoreExtendedError(CDERR_INITIALIZATION);
  8779. goto GenericExit;
  8780. }
  8781. }
  8782. //
  8783. // Need to do cch + 1 in the Local Alloc rather than just cch.
  8784. // This is to make sure there is at least one extra null in the
  8785. // string so that if a filter does not have the second part of
  8786. // the pair, three nulls will be placed in the wide string.
  8787. //
  8788. // Example: "Print File (*.prn)\0\0\0"
  8789. //
  8790. if (!(pOFNW->lpstrFilter = (LPWSTR)LocalAlloc(LPTR, ((cch + 1) * sizeof(WCHAR)))))
  8791. {
  8792. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8793. goto GenericExit;
  8794. }
  8795. else
  8796. {
  8797. MultiByteToWideChar(CP_ACP,
  8798. 0,
  8799. pOFNA->lpstrFilter,
  8800. cch,
  8801. (LPWSTR)pOFNW->lpstrFilter,
  8802. cch);
  8803. }
  8804. }
  8805. else
  8806. {
  8807. pOFNW->lpstrFilter = NULL;
  8808. }
  8809. //
  8810. // Initialize File strings.
  8811. //
  8812. if (pOFNA->lpstrFile)
  8813. {
  8814. if (pOFNA->nMaxFile <= (DWORD)lstrlenA(pOFNA->lpstrFile))
  8815. {
  8816. StoreExtendedError(CDERR_INITIALIZATION);
  8817. goto GenericExit;
  8818. }
  8819. pOFNW->nMaxFile = pOFNA->nMaxFile;
  8820. if (!(pOFNW->lpstrFile = (LPWSTR)LocalAlloc(LPTR, pOFNW->nMaxFile * sizeof(WCHAR))))
  8821. {
  8822. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8823. goto GenericExit;
  8824. }
  8825. }
  8826. else
  8827. {
  8828. //
  8829. // Conversion done in thunkofna2w.
  8830. //
  8831. pOFNW->nMaxFile = 0;
  8832. pOFNW->lpstrFile = NULL;
  8833. }
  8834. //
  8835. // Initialize File Title strings.
  8836. //
  8837. if (pOFNA->lpstrFileTitle && pOFNA->nMaxFileTitle)
  8838. {
  8839. //
  8840. // Calculate length of lpstrFileTitle.
  8841. //
  8842. pszA = pOFNA->lpstrFileTitle;
  8843. cch = 0;
  8844. try
  8845. {
  8846. while (*pszA++)
  8847. {
  8848. cch++;
  8849. }
  8850. }
  8851. except (EXCEPTION_EXECUTE_HANDLER)
  8852. {
  8853. if (cch)
  8854. {
  8855. cch--;
  8856. }
  8857. (pOFNA->lpstrFileTitle)[cch] = CHAR_NULL;
  8858. }
  8859. if (pOFNA->nMaxFileTitle < cch)
  8860. {
  8861. //
  8862. // Override the incorrect length from the app.
  8863. // Make room for the null.
  8864. //
  8865. pOFNW->nMaxFileTitle = cch + 1;
  8866. }
  8867. else
  8868. {
  8869. pOFNW->nMaxFileTitle = pOFNA->nMaxFileTitle;
  8870. }
  8871. if (!(pOFNW->lpstrFileTitle = (LPWSTR)LocalAlloc(LPTR, pOFNW->nMaxFileTitle * sizeof(WCHAR))))
  8872. {
  8873. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8874. goto GenericExit;
  8875. }
  8876. }
  8877. else
  8878. {
  8879. //
  8880. // Conversion done in thunkofna2w.
  8881. //
  8882. pOFNW->nMaxFileTitle = 0;
  8883. pOFNW->lpstrFileTitle = NULL;
  8884. }
  8885. //
  8886. // Initialize custom filter strings.
  8887. //
  8888. if ((asCustomFilter.Buffer = pOFNA->lpstrCustomFilter))
  8889. {
  8890. pszA = pOFNA->lpstrCustomFilter;
  8891. cch = 0;
  8892. if (*pszA || *(pszA + 1))
  8893. {
  8894. cch = 2;
  8895. try
  8896. {
  8897. while (*pszA || *(pszA + 1))
  8898. {
  8899. pszA++;
  8900. cch++;
  8901. }
  8902. }
  8903. except (EXCEPTION_EXECUTE_HANDLER)
  8904. {
  8905. StoreExtendedError(CDERR_INITIALIZATION);
  8906. goto GenericExit;
  8907. }
  8908. }
  8909. //
  8910. // JVert-inspired-wow-compatibility-hack-to-make-vbasic2.0-makeexe
  8911. // save-as-dialog-box-work-even-though-they-didn't-fill-in-
  8912. // the-whole-structure(nMaxCustFilter)-according-to-winhelp-spec fix
  8913. //
  8914. if (!(pOFNA->Flags & OFN_NOLONGNAMES))
  8915. {
  8916. if (((DWORD)cch >= pOFNA->nMaxCustFilter) ||
  8917. (pOFNA->nMaxCustFilter < 40))
  8918. {
  8919. StoreExtendedError(CDERR_INITIALIZATION);
  8920. goto GenericExit;
  8921. }
  8922. asCustomFilter.Length = cch;
  8923. asCustomFilter.MaximumLength = pOFNA->nMaxCustFilter;
  8924. pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter;
  8925. }
  8926. else
  8927. {
  8928. asCustomFilter.Length = cch;
  8929. if (pOFNA->nMaxCustFilter < cch)
  8930. {
  8931. asCustomFilter.MaximumLength = cch;
  8932. pOFNW->nMaxCustFilter = cch;
  8933. }
  8934. else
  8935. {
  8936. asCustomFilter.MaximumLength = pOFNA->nMaxCustFilter;
  8937. pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter;
  8938. }
  8939. }
  8940. usCustomFilter.MaximumLength = (asCustomFilter.MaximumLength + 1) * sizeof(WCHAR);
  8941. usCustomFilter.Length = asCustomFilter.Length * sizeof(WCHAR);
  8942. }
  8943. else
  8944. {
  8945. pOFNW->nMaxCustFilter = usCustomFilter.MaximumLength = 0;
  8946. pOFNW->lpstrCustomFilter = NULL;
  8947. }
  8948. if (usCustomFilter.MaximumLength > 0)
  8949. {
  8950. if (!(pStrMem = (LPBYTE)LocalAlloc(LPTR, usCustomFilter.MaximumLength)))
  8951. {
  8952. StoreExtendedError(CDERR_MEMALLOCFAILURE);
  8953. goto GenericExit;
  8954. }
  8955. else
  8956. {
  8957. pOFNW->lpstrCustomFilter = usCustomFilter.Buffer = (LPWSTR)pStrMem;
  8958. }
  8959. }
  8960. else
  8961. {
  8962. pStrMem = NULL;
  8963. }
  8964. OFI.pOFN = pOFNW;
  8965. OFI.pOFNA = pOFNA;
  8966. OFI.pasCustomFilter = &asCustomFilter;
  8967. OFI.pusCustomFilter = &usCustomFilter;
  8968. OFI.ApiType = COMDLG_ANSI;
  8969. //
  8970. // The following should always succeed.
  8971. //
  8972. if (!ThunkOpenFileNameA2W(&OFI))
  8973. {
  8974. StoreExtendedError(CDERR_INITIALIZATION);
  8975. goto GenericExit;
  8976. }
  8977. bRet = GetFileName(&OFI, qfnDlgProc);
  8978. if (g_bUserPressedCancel == FALSE)
  8979. {
  8980. ThunkOpenFileNameW2A(&OFI);
  8981. }
  8982. GenericExit:
  8983. if (pStrMem)
  8984. {
  8985. LocalFree(pStrMem);
  8986. }
  8987. if (!IS_INTRESOURCE(pOFNW->lpstrFile))
  8988. {
  8989. LocalFree((HLOCAL)pOFNW->lpstrFile);
  8990. }
  8991. if (!IS_INTRESOURCE(pOFNW->lpstrFileTitle))
  8992. {
  8993. LocalFree((HLOCAL)pOFNW->lpstrFileTitle);
  8994. }
  8995. if (!IS_INTRESOURCE(pOFNW->lpstrFilter))
  8996. {
  8997. LocalFree((HLOCAL)pOFNW->lpstrFilter);
  8998. }
  8999. if (!IS_INTRESOURCE(pOFNW->lpstrDefExt))
  9000. {
  9001. LocalFree((HLOCAL)pOFNW->lpstrDefExt);
  9002. }
  9003. if (!IS_INTRESOURCE(pOFNW->lpstrTitle))
  9004. {
  9005. LocalFree((HLOCAL)pOFNW->lpstrTitle);
  9006. }
  9007. if (!IS_INTRESOURCE(pOFNW->lpstrInitialDir))
  9008. {
  9009. LocalFree((HLOCAL)pOFNW->lpstrInitialDir);
  9010. }
  9011. if (!IS_INTRESOURCE(pOFNW->lpTemplateName))
  9012. {
  9013. LocalFree((HLOCAL)pOFNW->lpTemplateName);
  9014. }
  9015. LocalFree(pOFNW);
  9016. return (bRet);
  9017. }
  9018. ////////////////////////////////////////////////////////////////////////////
  9019. //
  9020. // Multi_strlenA
  9021. //
  9022. // This is a strlen for ANSI string lists that have several strings that
  9023. // are *separated* by a NULL char and are *terminated* by two NULL chars.
  9024. //
  9025. // Returns length of string including all NULL *separators* but not the
  9026. // 2nd NULL *terminator*. (ie. cat0dog00 would return length = 8)
  9027. //
  9028. ////////////////////////////////////////////////////////////////////////////
  9029. int Multi_strlenA(
  9030. LPCSTR str)
  9031. {
  9032. int ctr = 0;
  9033. if (str)
  9034. {
  9035. while (*str)
  9036. {
  9037. while (*str++)
  9038. {
  9039. ctr++;
  9040. }
  9041. ctr++; // count the NULL separator
  9042. }
  9043. }
  9044. return (ctr);
  9045. }
  9046. ////////////////////////////////////////////////////////////////////////////
  9047. //
  9048. // Multi_strcpyAtoW
  9049. //
  9050. // This is a strcpy for string lists that have several strings that are
  9051. // *separated* by a NULL char and are *terminated* by two NULL chars.
  9052. // Returns FALSE if:
  9053. // 1. the wide buffer is determined to be too small
  9054. // 2. the ptr to either buffer is NULL
  9055. // Returns TRUE if the copy was successful.
  9056. //
  9057. ////////////////////////////////////////////////////////////////////////////
  9058. BOOL Multi_strcpyAtoW(
  9059. LPWSTR pDestW,
  9060. LPCSTR pSrcA,
  9061. int cChars)
  9062. {
  9063. int off = 0;
  9064. int cb;
  9065. if (!pSrcA || !pDestW)
  9066. {
  9067. return (FALSE);
  9068. }
  9069. cChars = max(cChars, (Multi_strlenA(pSrcA) + 1));
  9070. if (LocalSize((HLOCAL)pDestW) < (cChars * sizeof(WCHAR)))
  9071. {
  9072. return (FALSE);
  9073. }
  9074. while (*pSrcA)
  9075. {
  9076. cb = lstrlenA(pSrcA) + 1;
  9077. off += MultiByteToWideChar(CP_ACP,0,pSrcA,cb,pDestW + off, cb);
  9078. pSrcA += cb;
  9079. }
  9080. pDestW[off] = L'\0';
  9081. return (TRUE);
  9082. }
  9083. ////////////////////////////////////////////////////////////////////////////
  9084. //
  9085. // ThunkMultiANSIStrToWIDE
  9086. //
  9087. // Thunks an ANSI multi-string (a list of NULL *separated* strings with
  9088. // two NULLs *terminating* the list) to the equivalent WIDE multi-string.
  9089. //
  9090. // Note: If the original wide buffer is too small to contain the new list,
  9091. // it will be free'd and a new wide buffer will be allocated. If a
  9092. // new wide buffer can't be allocated, the ptr to the original wide
  9093. // buffer is returned with no changes to the contents.
  9094. //
  9095. // Returns: ptr to the original WIDE buffer
  9096. // OR ptr to a new wide buffer if original buffer was too small
  9097. // OR NULL if pSrcA is NULL.
  9098. //
  9099. ////////////////////////////////////////////////////////////////////////////
  9100. LPWSTR ThunkMultiANSIStrToWIDE(
  9101. LPWSTR pDestW,
  9102. LPSTR pSrcA,
  9103. int cChars)
  9104. {
  9105. int size;
  9106. HLOCAL hBufW;
  9107. if (!pSrcA)
  9108. {
  9109. //
  9110. // The app doesn't want a buffer for this anymore.
  9111. //
  9112. if (pDestW)
  9113. {
  9114. LocalFree((HLOCAL)pDestW);
  9115. }
  9116. return (NULL);
  9117. }
  9118. //
  9119. // First try to copy to the existing wide buffer since most of the time
  9120. // there will be no change to the buffer ptr anyway.
  9121. //
  9122. if (!(Multi_strcpyAtoW(pDestW, pSrcA, cChars)))
  9123. {
  9124. //
  9125. // If the wide buffer is too small (or NULL or invalid), allocate
  9126. // a bigger buffer.
  9127. //
  9128. size = max(cChars, (Multi_strlenA(pSrcA) + 1));
  9129. cChars = size;
  9130. if (hBufW = LocalAlloc(LPTR, (size * sizeof(WCHAR))))
  9131. {
  9132. //
  9133. // Try to copy to the new wide buffer.
  9134. //
  9135. if ((Multi_strcpyAtoW((LPWSTR)hBufW, pSrcA, cChars)))
  9136. {
  9137. if (pDestW)
  9138. {
  9139. LocalFree((HLOCAL)pDestW);
  9140. }
  9141. pDestW = (LPWSTR)hBufW;
  9142. }
  9143. else
  9144. {
  9145. //
  9146. // Don't change anything.
  9147. //
  9148. LocalFree(hBufW);
  9149. }
  9150. }
  9151. }
  9152. return (pDestW);
  9153. }
  9154. ////////////////////////////////////////////////////////////////////////////
  9155. //
  9156. // ThunkANSIStrToWIDE
  9157. //
  9158. // Thunks an ANSI string to WIDE.
  9159. //
  9160. // Note: If the original wide buffer is too small to contain the new
  9161. // string, it will be free'd and a new wide buffer will be allocated.
  9162. // If a new wide buffer can't be allocated, the ptr to the original
  9163. // wide buffer is returned with no changes to the contents.
  9164. //
  9165. // Returns: ptr to the original WIDE buffer
  9166. // OR ptr to a new wide buffer if original buffer was too small
  9167. // OR NULL if pSrcA is NULL.
  9168. //
  9169. ////////////////////////////////////////////////////////////////////////////
  9170. LPWSTR ThunkANSIStrToWIDE(
  9171. LPWSTR pDestW,
  9172. LPSTR pSrcA,
  9173. int cChars)
  9174. {
  9175. HLOCAL hBufW;
  9176. int size;
  9177. if (!pSrcA)
  9178. {
  9179. //
  9180. // The app doesn't want a buffer for this anymore.
  9181. //
  9182. if (pDestW)
  9183. {
  9184. LocalFree((HLOCAL)pDestW);
  9185. }
  9186. return (NULL);
  9187. }
  9188. size = max(cChars, (lstrlenA(pSrcA) + 1));
  9189. cChars = size;
  9190. //
  9191. // If the wide buffer is too small (or NULL or invalid), allocate a
  9192. // bigger buffer.
  9193. //
  9194. if (LocalSize((HLOCAL)pDestW) < (size * sizeof(WCHAR)))
  9195. {
  9196. if (hBufW = LocalAlloc(LPTR, (size * sizeof(WCHAR))))
  9197. {
  9198. //
  9199. // Try to copy to the new wide buffer.
  9200. //
  9201. if (SHAnsiToUnicode(pSrcA,(LPWSTR)hBufW,cChars ))
  9202. {
  9203. if (pDestW)
  9204. {
  9205. LocalFree((HLOCAL)pDestW);
  9206. }
  9207. pDestW = (LPWSTR)hBufW;
  9208. }
  9209. else
  9210. {
  9211. //
  9212. // Don't change anything.
  9213. //
  9214. LocalFree(hBufW);
  9215. }
  9216. }
  9217. }
  9218. else
  9219. {
  9220. //
  9221. // Just use the original wide buffer.
  9222. //
  9223. SHAnsiToUnicode(pSrcA,pDestW, cChars);
  9224. }
  9225. return (pDestW);
  9226. }
  9227. #ifdef WINNT
  9228. ////////////////////////////////////////////////////////////////////////////
  9229. //
  9230. // Ssync_ANSI_UNICODE_OFN_For_WOW
  9231. //
  9232. // Function to allow NT WOW to keep the ANSI & UNICODE versions of
  9233. // the OPENFILENAME structure in ssync as required by many 16-bit apps.
  9234. // See notes for Ssync_ANSI_UNICODE_Struct_For_WOW() in dlgs.c.
  9235. //
  9236. ////////////////////////////////////////////////////////////////////////////
  9237. VOID Ssync_ANSI_UNICODE_OFN_For_WOW(
  9238. HWND hDlg,
  9239. BOOL f_ANSI_to_UNICODE)
  9240. {
  9241. POPENFILEINFO pOFI;
  9242. if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
  9243. {
  9244. if (pOFI->pOFN && pOFI->pOFNA)
  9245. {
  9246. if (f_ANSI_to_UNICODE)
  9247. {
  9248. ThunkOpenFileNameA2W(pOFI);
  9249. }
  9250. else
  9251. {
  9252. ThunkOpenFileNameW2A(pOFI);
  9253. }
  9254. }
  9255. }
  9256. }
  9257. #endif
  9258. #endif