Leaked source code of windows server 2003
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.

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