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

4288 lines
124 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995.
  5. //
  6. // FILE: SHCOMPUI.C
  7. //
  8. // DESCRIPTION:
  9. //
  10. // This module provides the code for supporting NTFS file compression
  11. // in the NT Explorer user interface. There are two interfaces to the
  12. // compression features.
  13. //
  14. // The first interface is a shell context menu extension that adds
  15. // the options "Compress" and "Uncompress" to the context/file menu of
  16. // an object that has registered the extension. This interface
  17. // uses the standard shell extension protocol of QueryContextMenu,
  18. // InvokeCommand etc. InvokeCommand calls ShellChangeCompressionAttribute()
  19. // to do the actual compression/uncompression.
  20. //
  21. // The shell extension CLSID is {764BF0E1-F219-11ce-972D-00AA00A14F56}
  22. // and is represented by the symbol CLSID_CompressMenuExt.
  23. //
  24. // The second interface is provided for handling compression
  25. // requests through object property pages. Property page action
  26. // code calls ShellChangeCompressionAttribute( ).
  27. //
  28. // This way, through either interface, compression appears the same
  29. // to the user.
  30. //
  31. // Note that a good portion of the actual compression code was taken
  32. // from the WinFile implementation. Some changes were made to
  33. // eliminate redundant code and to produce the desired Explorer
  34. // compression behavior.
  35. //
  36. // The comment string "WARNING" points out areas that are sensitive to maintenance activity.
  37. //
  38. // This module is applicable only to the NT version of the shell.
  39. //
  40. // REVISIONS:
  41. //
  42. // Date Description Programmer
  43. // ---------- --------------------------------------------------- ----------
  44. // 09/15/95 Initial creation. brianau
  45. // 09/20/95 Incorporated changes from 1st code review. brianau
  46. // 10/02/95 Added SCCA context structure. brianau
  47. // Changed all __TEXT() macros to TEXT()
  48. // 10/13/95 Removed function Int64ToString and moved it to brianau
  49. // util.c.
  50. // 02/22/96 Check for shift-key before adding context menu brianau
  51. // items. No shift key, no items.
  52. // Also added call to SHChangeNotify to notify shell
  53. // that compression attribute changed on each file.
  54. // 03/20/96 Added invalidation of drive type flags cache. brianau
  55. // Replaced imported function declarations with
  56. // #include <shellprv.h>
  57. //
  58. ///////////////////////////////////////////////////////////////////////////////
  59. #ifdef WINNT
  60. #include <nt.h>
  61. #include <ntrtl.h>
  62. #include <nturtl.h>
  63. #include <windows.h>
  64. #include <windowsx.h>
  65. #include <tchar.h>
  66. #include <shlobj.h>
  67. #include <shellapi.h>
  68. #include <shlobjp.h>
  69. #define INITGUID
  70. #include <initguid.h>
  71. #include "resids.h" // SHCOMPUI Resource IDs.
  72. #include "debug.h" // DbgOut and ASSERT.
  73. #include "shcompui.h"
  74. #define Assert(f)
  75. #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
  76. //
  77. // Debug message control.
  78. //
  79. //#define TRACE_SHEXT 1 // Un-comment for shell extension tracing.
  80. //#define TRACE_DLL 1 // Un-comment for DLL load/unload tracing.
  81. //#define TRACE_COMPRESSION 1 // Un-comment for compression code tracing.
  82. //#define SIM_DISK_FULL 1 // Un-comment to test disk-full condition.
  83. //
  84. // Define context menu position offsets for each option.
  85. //
  86. #define MENUOFS_FIRST 0 // For index range checking.
  87. #define MENUOFS_COMPRESS 0 // Command index for "Compress" menu opt..
  88. #define MENUOFS_UNCOMPRESS 1 // Command index for "Uncompress" menu opt.
  89. #define MENUOFS_LAST 1 // For index range checking.
  90. //
  91. // Return values for compression confirmation dialog.
  92. //
  93. #define COMPRESS_CANCELLED 0 // User pressed Cancel.
  94. #define COMPRESS_SUBSNO 1 // User pressed OK without box checked.
  95. #define COMPRESS_SUBSYES 2 // User pressed OK with box checked.
  96. //
  97. // Control values for DisplayCompressProgress( ) and
  98. // DisplayUncompressProgress( )
  99. //
  100. #define PROGRESS_UPD_FILENAME 1
  101. #define PROGRESS_UPD_DIRECTORY 2
  102. #define PROGRESS_UPD_FILEANDDIR 3
  103. #define PROGRESS_UPD_DIRCNT 4
  104. #define PROGRESS_UPD_FILECNT 5
  105. #define PROGRESS_UPD_COMPRESSEDSIZE 6
  106. #define PROGRESS_UPD_FILESIZE 7
  107. #define PROGRESS_UPD_PERCENTAGE 8
  108. #define PROGRESS_UPD_FILENUMBERS 9
  109. #define PROGRESS_UPD_FINAL 10
  110. //
  111. // Return values for CompressErrMessageBox routine.
  112. //
  113. #define RETRY_CREATE 1
  114. #define RETRY_DEVIO 2
  115. //
  116. // Some text string and character constants.
  117. // FEATURE: These should probably come from some locale info.
  118. //
  119. const TCHAR c_szSTAR[] = TEXT("*");
  120. const TCHAR c_szDOT[] = TEXT(".");
  121. const TCHAR c_szDOTDOT[] = TEXT("..");
  122. const TCHAR c_szNTLDR[] = TEXT("NTLDR");
  123. const TCHAR c_szBACKSLASH[] = TEXT("\\");
  124. #define CH_NULL TEXT('\0')
  125. //
  126. // String length constants.
  127. //
  128. #define MAX_DLGTITLE_LEN 128 // Max length of dialog title.
  129. #define MAX_MESSAGE_LEN (_MAX_PATH * 3) // General dialog text message.
  130. #define MAX_MENUITEM_LEN 40 // Max length of context menu item.
  131. #define MAX_CMDVERB_LEN 40 // Max length of cmd verb.
  132. typedef HRESULT (CALLBACK FAR * LPFNCREATEINSTANCE)(LPUNKNOWN pUnkOuter,
  133. REFIID riid, LPVOID FAR* ppvObject);
  134. static INT_PTR CALLBACK CompressSubsConfirmDlgProc(HWND hDlg, UINT uMsg,
  135. WPARAM wParam, LPARAM lParam);
  136. static BOOL DoCompress(HWND hwndParent,
  137. LPTSTR DirectorySpec, LPTSTR FileSpec);
  138. static BOOL DoUncompress(HWND hwndParent,
  139. LPTSTR DirectorySpec, LPTSTR FileSpec);
  140. static int CompressErrMessageBox(HWND hwndActive,
  141. LPTSTR szFile, PHANDLE phFile);
  142. static INT_PTR CALLBACK CompressErrDialogProc(HWND hDlg, UINT uMsg,
  143. WPARAM wParam, LPARAM lParam);
  144. static BOOL OpenFileForCompress(PHANDLE phFile, LPTSTR szFile);
  145. static DWORD FormatStringWithArgs(LPCTSTR pszFormat, LPTSTR pszBuffer,
  146. DWORD nSize, ...);
  147. static DWORD LoadStringWithArgs(HINSTANCE hInstance, UINT uId,
  148. LPTSTR pszBuffer, DWORD nSize, ...);
  149. static VOID CompressProgressYield(void);
  150. static VOID DisplayUncompressProgress(int iType);
  151. static void UncompressDiskFullError(HWND hwndParent, HANDLE hFile);
  152. //
  153. // Structure used to communicate with the compression confirmation dialog.
  154. //
  155. typedef struct {
  156. BOOL bCompress; // TRUE = compress, FALSE = uncompress
  157. TCHAR szFileName[_MAX_PATH+1]; // File to be acted on.
  158. } CompressionDesc;
  159. //
  160. // Macros for converting between interface and class pointers.
  161. //
  162. #define CMX_OFFSETOF(x) ((UINT_PTR)(&((CContextMenuExt *)0)->x))
  163. #define PVOID2PCMX(pv,offset) ((CContextMenuExt *)(((LPBYTE)pv)-offset))
  164. #define PCM2PCMX(pcmx) PVOID2PCMX(pcmx, CMX_OFFSETOF(ctm))
  165. #define PSEI2PCMX(psei) PVOID2PCMX(psei, CMX_OFFSETOF(sei))
  166. INT g_cRefThisDll = 0; // Reference count for this DLL.
  167. HINSTANCE g_hmodThisDll = NULL; // Handle to the DLL.
  168. HANDLE g_hProcessHeap = NULL; // Handle to the process heap.
  169. HANDLE g_hdlgProgress = NULL; // Operation progress dialog.
  170. TCHAR szMessage[MAX_MESSAGE_LEN+1]; // FEATURE: This need not be global.
  171. TCHAR g_szByteCntFmt[10]; // Byte cnt disp fmt str ( "%1 bytes" ).
  172. #define SZ_SEMAPHORE_NAME TEXT("SHCOMPUI_SEMAPHORE")
  173. HANDLE g_hSemaphore = NULL; // Re-entrancy semaphore.
  174. LPSCCA_CONTEXT g_pContext = NULL; // Ptr to current context structure.
  175. INT g_iRecursionLevel = 0; // Used to control shell change notifications.
  176. //
  177. // Global variables to hold the User option information.
  178. //
  179. BOOL g_bDoSubdirectories = FALSE; // Include all subdirectories ?
  180. BOOL g_bShowProgress = FALSE; // Show operation progress dialog ?
  181. BOOL g_bIgnoreAllErrors = FALSE; // User wants to ignore all errors ?
  182. BOOL g_bDiskFull = FALSE; // Is disk full on uncompression ?
  183. //
  184. // Global variables to hold compression statistics.
  185. //
  186. LONGLONG g_cTotalDirectories = 0;
  187. LONGLONG g_cTotalFiles = 0;
  188. //
  189. // Compression ratio statistics values.
  190. //
  191. unsigned _int64 g_iTotalFileSize = 0;
  192. unsigned _int64 g_iTotalCompressedSize = 0;
  193. //
  194. // "Current" file and directory names are global.
  195. //
  196. TCHAR g_szFile[_MAX_PATH + 1];
  197. TCHAR g_szDirectory[_MAX_PATH + 1];
  198. //
  199. // Directory text control in progress dialog.
  200. //
  201. HDC g_hdcDirectoryTextCtrl = NULL; // Control handle.
  202. DWORD g_cDirectoryTextCtrlWd = 0; // Width of control.
  203. //
  204. // Number format locale information.
  205. //
  206. NUMBERFMT g_NumberFormat;
  207. TCHAR g_szDecimalSep[5];
  208. TCHAR g_szThousandSep[5];
  209. //
  210. // Context menu extension GUID generated with GUIDGEN.
  211. //
  212. // {764BF0E1-F219-11ce-972D-00AA00A14F56}
  213. DEFINE_GUID(CLSID_CompressMenuExt,
  214. 0x764bf0e1, 0xf219, 0x11ce, 0x97, 0x2d, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56);
  215. //
  216. // Structure representing the class factory for this in-process server.
  217. //
  218. typedef struct
  219. {
  220. IClassFactory cf; // Pointer to class factory vtbl.
  221. UINT cRef; // Interface reference counter.
  222. LPFNCREATEINSTANCE pfnCI; // Pointer to instance generator function.
  223. } CClassFactory;
  224. //
  225. // Structure representing the context menu extension.
  226. //
  227. typedef struct
  228. {
  229. IContextMenu ctm; // Pointer to context menu interface.
  230. IShellExtInit sei; // Pointer to shell extension init interface.
  231. UINT cRef; // Interface reference counter.
  232. STGMEDIUM medium; // OLE data xfer storage medium descriptor.
  233. INT cSelectedFiles; // Cnt of files selected. Must be signed.
  234. BOOL bDriveSelected; // Are drives selected ?
  235. LPDATAOBJECT pDataObj; // Saved pointer to Data Object.
  236. } CContextMenuExt;
  237. ///////////////////////////////////////////////////////////////////////////////
  238. //
  239. // FUNCTION: PathRemoveTheBackslash
  240. //
  241. // DESCRIPTION:
  242. //
  243. // Removes trailing backslash from path string if it exists.
  244. // This code was cut directly from path.c in shell32.dll and
  245. // renamed from PathRemoveBackslash to avoid linkage naming
  246. // conflicts.
  247. //
  248. // in:
  249. // lpszPath (A:\, C:\foo\, etc)
  250. //
  251. // out:
  252. // lpszPath (A:\, C:\foo, etc)
  253. //
  254. // returns:
  255. // ponter to NULL that replaced the backslash
  256. // or the pointer to the last character if it isn't a backslash.
  257. //
  258. ///////////////////////////////////////////////////////////////////////////////
  259. LPTSTR WINAPI PathRemoveTheBackslash(LPTSTR lpszPath)
  260. {
  261. int len = lstrlen(lpszPath)-1;
  262. if (IsDBCSLeadByte(*((LPSTR)CharPrev(lpszPath,lpszPath+len+1))))
  263. len--;
  264. if (!PathIsRoot(lpszPath) && lpszPath[len] == TEXT('\\'))
  265. lpszPath[len] = TEXT('\0');
  266. return lpszPath + len;
  267. }
  268. ///////////////////////////////////////////////////////////////////////////////
  269. //
  270. // FUNCTION: FileAttribString
  271. //
  272. // DESCRIPTION:
  273. //
  274. // Formats a file's attribute word into a string of characters.
  275. // Used for program tracing and debugging only.
  276. //
  277. // ARGUMENTS:
  278. //
  279. // dwAttrib
  280. // Attribute bits to be decoded.
  281. //
  282. // pszDest
  283. // Address of destination string.
  284. // String must be at least 8 characters long.
  285. //
  286. // RETURNS:
  287. //
  288. // Pointer to destination string.
  289. //
  290. ///////////////////////////////////////////////////////////////////////////////
  291. #ifdef TRACE_COMPRESSION
  292. static LPTSTR FileAttribString(DWORD dwAttrib, LPTSTR pszDest)
  293. {
  294. if (dwAttrib != (DWORD)-1)
  295. {
  296. wsprintf(pszDest, TEXT("%c%c%c%c%c%c%c"),
  297. dwAttrib & FILE_ATTRIBUTE_ARCHIVE ? TEXT('A') : TEXT('.'),
  298. dwAttrib & FILE_ATTRIBUTE_COMPRESSED ? TEXT('C') : TEXT('.'),
  299. dwAttrib & FILE_ATTRIBUTE_DIRECTORY ? TEXT('D') : TEXT('.'),
  300. dwAttrib & FILE_ATTRIBUTE_HIDDEN ? TEXT('H') : TEXT('.'),
  301. dwAttrib & FILE_ATTRIBUTE_NORMAL ? TEXT('N') : TEXT('.'),
  302. dwAttrib & FILE_ATTRIBUTE_READONLY ? TEXT('R') : TEXT('.'),
  303. dwAttrib & FILE_ATTRIBUTE_SYSTEM ? TEXT('S') : TEXT('.'));
  304. }
  305. else
  306. lstrcpy(pszDest, TEXT("INVALID"));
  307. return pszDest;
  308. }
  309. #endif
  310. ///////////////////////////////////////////////////////////////////////////////
  311. //
  312. // FUNCTION: ChgDllRefCnt
  313. //
  314. // DESCRIPTION:
  315. //
  316. // Adds reference count tracking to the incrementing and decrementing of the
  317. // module reference count.
  318. //
  319. // ARGUMENTS:
  320. //
  321. // n
  322. // Should be +1 or -1.
  323. //
  324. // RETURNS:
  325. //
  326. // Nothing.
  327. //
  328. ///////////////////////////////////////////////////////////////////////////////
  329. __inline void ChgDllRefCnt(INT n)
  330. {
  331. ASSERT(g_cRefThisDll >= 0 && g_cRefThisDll+(n) >= 0);
  332. g_cRefThisDll += (n);
  333. #ifdef TRACE_DLL
  334. DbgOut(TEXT("SHCOMPUI: ChgDllRefCnt. Count = %d"), g_cRefThisDll);
  335. #endif
  336. }
  337. ///////////////////////////////////////////////////////////////////////////////
  338. //
  339. // FUNCTION: CClassFactory_QueryInterface
  340. //
  341. // DESCRIPTION:
  342. //
  343. // Class factory support for IUnknown::QueryInterface( ).
  344. // Queries class factory object for a specific interface.
  345. //
  346. // ARGUMENTS:
  347. //
  348. // pcf
  349. // Pointer to class factory interface.
  350. //
  351. // riid
  352. // Reference to ID of interface being requested.
  353. //
  354. // ppvOut
  355. // Destination for address of vtable for requested interface.
  356. //
  357. // RETURNS:
  358. //
  359. // NOERROR = Success.
  360. // E_NOINTERFACE = Requested interface not supported.
  361. //
  362. ///////////////////////////////////////////////////////////////////////////////
  363. STDMETHODIMP CClassFactory_QueryInterface(IClassFactory *pcf, REFIID riid,
  364. LPVOID *ppvOut)
  365. {
  366. CClassFactory *this = IToClass(CClassFactory, cf, pcf);
  367. HRESULT hResult = E_NOINTERFACE;
  368. ASSERT(NULL != this);
  369. ASSERT(NULL != ppvOut);
  370. #ifdef TRACE_SHEXT
  371. DbgOut(TEXT("SHCOMPUI: CClassFactory::QueryInterface"));
  372. #endif
  373. *ppvOut = NULL;
  374. if (IsEqualIID(riid, &IID_IClassFactory) ||
  375. IsEqualIID(riid, &IID_IUnknown))
  376. {
  377. (LPCLASSFACTORY)*ppvOut = &this->cf;
  378. this->cRef++;
  379. hResult = NOERROR;
  380. }
  381. return hResult;
  382. }
  383. ///////////////////////////////////////////////////////////////////////////////
  384. //
  385. // FUNCTION: CClassFactory_AddRef
  386. //
  387. // DESCRIPTION:
  388. //
  389. // Class factory support for IUnknown::AddRef( ).
  390. // Increments object reference count.
  391. //
  392. // ARGUMENTS:
  393. //
  394. // pcf
  395. // Pointer to class factory interface.
  396. //
  397. // RETURNS:
  398. //
  399. // Returns object reference count after it is incremented.
  400. //
  401. ///////////////////////////////////////////////////////////////////////////////
  402. STDMETHODIMP_(ULONG) CClassFactory_AddRef(IClassFactory *pcf)
  403. {
  404. CClassFactory *this = IToClass(CClassFactory, cf, pcf);
  405. #ifdef TRACE_SHEXT
  406. DbgOut(TEXT("SHCOMPUI: CClassFactory::AddRef"));
  407. #endif
  408. ASSERT(NULL != this);
  409. return ++this->cRef;
  410. }
  411. ///////////////////////////////////////////////////////////////////////////////
  412. //
  413. // FUNCTION: CClassFactory_Release
  414. //
  415. // DESCRIPTION:
  416. //
  417. // Class factory support for IUnknown::Release( ).
  418. // Decrements object reference count.
  419. // Deletes object from memory when reference count reaches 0.
  420. //
  421. // ARGUMENTS:
  422. //
  423. // pcf
  424. // Pointer to class factory interface.
  425. //
  426. // RETURNS:
  427. //
  428. // Returns object reference count after it is incremented.
  429. //
  430. ///////////////////////////////////////////////////////////////////////////////
  431. STDMETHODIMP_(ULONG) CClassFactory_Release(IClassFactory *pcf)
  432. {
  433. CClassFactory *this = IToClass(CClassFactory, cf, pcf);
  434. ULONG refCnt = 0;
  435. #ifdef TRACE_SHEXT
  436. DbgOut(TEXT("SHCOMPUI: CClassFactory::Release"));
  437. #endif
  438. ASSERT(NULL != this);
  439. if ((refCnt = --this->cRef) == 0)
  440. {
  441. LocalFree((HLOCAL)this);
  442. ChgDllRefCnt(-1);
  443. }
  444. return refCnt;
  445. }
  446. ///////////////////////////////////////////////////////////////////////////////
  447. //
  448. // FUNCTION: CClassFactory_CreateInstance
  449. //
  450. // DESCRIPTION:
  451. //
  452. // Generates an instance of the class factory.
  453. //
  454. // ARGUMENTS:
  455. //
  456. // pcf
  457. // Pointer to class factory interface.
  458. //
  459. // punkOuter
  460. // Pointer to outer object's IUnknown interface. Only used when
  461. // object aggregation is requested.
  462. //
  463. // riid
  464. // Reference to requested interface ID.
  465. //
  466. // ppv
  467. // Destination for address of requested interface.
  468. //
  469. // RETURNS:
  470. //
  471. //
  472. //
  473. ///////////////////////////////////////////////////////////////////////////////
  474. STDMETHODIMP CClassFactory_CreateInstance(IClassFactory *pcf, IUnknown *punkOuter,
  475. REFIID riid, LPVOID *ppv)
  476. {
  477. CClassFactory *this = IToClass(CClassFactory, cf, pcf);
  478. HRESULT hResult = CLASS_E_NOAGGREGATION;
  479. #ifdef TRACE_SHEXT
  480. DbgOut(TEXT("SHCOMPUI: CClassFactory::CreateInstance"));
  481. #endif
  482. ASSERT(NULL != this);
  483. if (NULL == punkOuter)
  484. {
  485. hResult = this->pfnCI(punkOuter, riid, ppv);
  486. }
  487. return hResult;
  488. }
  489. ///////////////////////////////////////////////////////////////////////////////
  490. //
  491. // FUNCTION: CClassFactory_LockServer
  492. //
  493. // DESCRIPTION:
  494. //
  495. // Explicitly locks the server from unloading regardless of the module
  496. // reference count.
  497. // Not used for DLL servers. Therefore, this implementation is nul.
  498. //
  499. //
  500. // ARGUMENTS:
  501. // pcf
  502. // Pointer to class factory object.
  503. //
  504. // fLock
  505. // TRUE = Lock server.
  506. // FALSE = Unlock server.
  507. //
  508. //
  509. // RETURNS:
  510. //
  511. // E_NOTIMPL = Server locking not required for DLL server.
  512. //
  513. ///////////////////////////////////////////////////////////////////////////////
  514. STDMETHODIMP CClassFactory_LockServer(IClassFactory *pcf, BOOL fLock)
  515. {
  516. #ifdef TRACE_SHEXT
  517. DbgOut(TEXT("SHCOMPUI: CClassFactory::LockServer"));
  518. #endif
  519. return E_NOTIMPL;
  520. }
  521. ///////////////////////////////////////////////////////////////////////////////
  522. //
  523. // FUNCTION: CContextMenuExt_QueryInterface
  524. //
  525. // DESCRIPTION:
  526. //
  527. // Context menu extension support for IUnknown::QueryInterface( ).
  528. // Queries context menu extension object for a specific interface.
  529. //
  530. // ARGUMENTS:
  531. //
  532. // pctm
  533. // Pointer to context menu interface.
  534. //
  535. // riid
  536. // Reference to ID of interface being requested.
  537. //
  538. // ppvOut
  539. // Destination for address of vtable for requested interface.
  540. //
  541. // RETURNS:
  542. //
  543. // NOERROR = Success.
  544. // E_NOINTERFACE = Requested interface not supported.
  545. //
  546. ///////////////////////////////////////////////////////////////////////////////
  547. STDMETHODIMP CContextMenuExt_QueryInterface(IContextMenu *pctm, REFIID riid,
  548. LPVOID *ppvOut)
  549. {
  550. CContextMenuExt *this = IToClass(CContextMenuExt, ctm, pctm);
  551. HRESULT hResult = NOERROR;
  552. #ifdef TRACE_SHEXT
  553. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::QueryInterface"));
  554. #endif
  555. ASSERT(NULL != this);
  556. ASSERT(NULL != ppvOut);
  557. *ppvOut = NULL;
  558. if (IsEqualIID(riid, &IID_IContextMenu) || IsEqualIID(riid, &IID_IUnknown))
  559. {
  560. (IContextMenu *)*ppvOut = &this->ctm;
  561. this->cRef++;
  562. }
  563. else if (IsEqualIID(riid, &IID_IShellExtInit))
  564. {
  565. (IShellExtInit *)*ppvOut = &this->sei;
  566. this->cRef++;
  567. }
  568. else
  569. hResult = E_NOINTERFACE;
  570. return hResult;
  571. }
  572. ///////////////////////////////////////////////////////////////////////////////
  573. //
  574. // FUNCTION: CContextMenuExt_AddRef
  575. //
  576. // DESCRIPTION:
  577. //
  578. // Context menu extension support for IUnknown::AddRef( ).
  579. // Increments object reference count.
  580. //
  581. // ARGUMENTS:
  582. //
  583. // pctm
  584. // Pointer to context menu interface.
  585. //
  586. // RETURNS:
  587. //
  588. // Returns object reference count after it is incremented.
  589. //
  590. ///////////////////////////////////////////////////////////////////////////////
  591. STDMETHODIMP_(ULONG) CContextMenuExt_AddRef(IContextMenu *pctm)
  592. {
  593. CContextMenuExt *this = IToClass(CContextMenuExt, ctm, pctm);
  594. #ifdef TRACE_SHEXT
  595. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::AddRef"));
  596. #endif
  597. ASSERT(NULL != this);
  598. return ++this->cRef;
  599. }
  600. ///////////////////////////////////////////////////////////////////////////////
  601. //
  602. // FUNCTION: CContextMenuExt_Cleanup
  603. //
  604. // DESCRIPTION:
  605. //
  606. // Does the cleanup associated with a previous IShellExtInit_Initialize call
  607. //
  608. // ARGUMENTS:
  609. //
  610. // this
  611. // Pointer to context menu extension
  612. //
  613. // RETURNS:
  614. //
  615. // -nothing-
  616. //
  617. ///////////////////////////////////////////////////////////////////////////////
  618. void CContextMenu_Cleanup( CContextMenuExt *this )
  619. {
  620. if (this->pDataObj)
  621. {
  622. this->pDataObj->lpVtbl->Release(this->pDataObj);
  623. }
  624. //
  625. // Now release the stgmedium (FEATURE - Replace this with OLE's ReleaseStgMedium
  626. //
  627. if (this->medium.pUnkForRelease)
  628. {
  629. this->medium.pUnkForRelease->lpVtbl->Release(this->medium.pUnkForRelease);
  630. }
  631. else
  632. {
  633. switch(this->medium.tymed)
  634. {
  635. case TYMED_HGLOBAL:
  636. GlobalFree(this->medium.hGlobal);
  637. break;
  638. case TYMED_ISTORAGE: // depends on pstm/pstg overlap in union
  639. case TYMED_ISTREAM:
  640. this->medium.pstm->lpVtbl->Release(this->medium.pstm);
  641. break;
  642. default:
  643. Assert(0); // unknown type
  644. }
  645. }
  646. }
  647. ///////////////////////////////////////////////////////////////////////////////
  648. //
  649. // FUNCTION: CContextMenuExt_Release
  650. //
  651. // DESCRIPTION:
  652. //
  653. // Context menu extension support for IUnknown::Release( ).
  654. // Decrements object reference count.
  655. // Deletes object from memory when reference count reaches 0.
  656. //
  657. // ARGUMENTS:
  658. //
  659. // pctm
  660. // Pointer to context menu interface.
  661. //
  662. // RETURNS:
  663. //
  664. // Returns object reference count after it is decremented.
  665. //
  666. ///////////////////////////////////////////////////////////////////////////////
  667. STDMETHODIMP_(ULONG) CContextMenuExt_Release(IContextMenu *pctm)
  668. {
  669. CContextMenuExt *this = IToClass(CContextMenuExt, ctm, pctm);
  670. ULONG refCnt = 0;
  671. #ifdef TRACE_SHEXT
  672. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::Release"));
  673. #endif
  674. ASSERT(NULL != this);
  675. if ((refCnt = --this->cRef) == 0)
  676. {
  677. CContextMenu_Cleanup(this);
  678. LocalFree((HLOCAL)this);
  679. ChgDllRefCnt(-1);
  680. }
  681. return refCnt;
  682. }
  683. ///////////////////////////////////////////////////////////////////////////////
  684. //
  685. // FUNCTION: CContextMenuExt_QueryContextMenu
  686. //
  687. // DESCRIPTION:
  688. //
  689. // Called by NT Shell when requesting menu option text and command numbers.
  690. //
  691. // ARGUMENTS:
  692. //
  693. // pctm
  694. // Pointer to context menu interface.
  695. //
  696. // hMenu
  697. // Handle to context menu to be modified.
  698. //
  699. // indexMenu
  700. // Index where first menu item may be inserted.
  701. //
  702. // idCmdFirst
  703. // Lower bound of available menu command IDs.
  704. //
  705. // idCmdLast
  706. // Upper bound of available menu command IDs.
  707. //
  708. // uFlags
  709. // Flag indicating context of function call.
  710. //
  711. // RETURNS:
  712. //
  713. // Returns the number of menu items added (excluding separators).
  714. //
  715. //
  716. ///////////////////////////////////////////////////////////////////////////////
  717. STDMETHODIMP CContextMenuExt_QueryContextMenu(IContextMenu *pctm, HMENU hMenu,
  718. UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  719. {
  720. CContextMenuExt *this = IToClass(CContextMenuExt, ctm, pctm);
  721. BOOL bDisableCompress = FALSE;
  722. BOOL bDisableUncompress = FALSE;
  723. BOOL bDirectorySelected = FALSE;
  724. BOOL bNonCompressibleVol = FALSE;
  725. DWORD dwAttribTalley = 0;
  726. INT i = 0;
  727. INT cMenuItemsAdded = 0;
  728. HCURSOR hCursor = NULL;
  729. DRAGINFO di; // Drag information.
  730. LPTSTR pDragFileName = NULL; // Ptr into list of drag info names.
  731. LPTSTR pNextName = NULL; // Lookahead pointer into name list.
  732. #ifdef TRACE_SHEXT
  733. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::QueryContextMenu"));
  734. #endif
  735. ASSERT(NULL != this);
  736. //
  737. // Only allow addition of Compress/Uncompress when user is pressing
  738. // shift key.
  739. //
  740. // cSelectedFiles will be -1 if user selected only a virtual object.
  741. // i.e. Control Panel, Printer etc.
  742. //
  743. if (GetAsyncKeyState(VK_SHIFT) >= 0 || this->cSelectedFiles <= 0)
  744. return 0;
  745. //
  746. // Determine if we should hide both of the context menu items or
  747. // disable one of them.
  748. // If no selected device supports compression, HIDE both items.
  749. // If a directory is selected, show both menu items.
  750. // If a directory is not in the selected list, disable a menu item
  751. // if all of the selected files have the same compression state.
  752. // i.e.: All items compressed - disable the "Compress" item.
  753. // A mix of compression states activates both items.
  754. //
  755. dwAttribTalley = 0; // Attribute talley mask.
  756. //
  757. // Attribute talley mask bits.
  758. //
  759. #define TALLEY_UNCOMPRESSED 0x0001 // Object is an uncompressed file.
  760. #define TALLEY_COMPRESSED 0x0002 // Object is a compressed file.
  761. #define TALLEY_DIRECTORY 0x0004 // Object is a directory.
  762. #define TALLEY_NOCOMPSUPT 0x0008 // At least 1 file from FAT/HPFS drive.
  763. //
  764. // Display the hourglass cursor so that the user knows something is
  765. // happening. This loop can take a long time on large selections.
  766. //
  767. if (hCursor = LoadCursor(NULL, IDC_WAIT))
  768. hCursor = SetCursor(hCursor);
  769. ShowCursor(TRUE);
  770. //
  771. // Determine if the user has selected drives.
  772. // Since you can't select a combination of drive(s) and folders/files,
  773. // if there are ANY drives selected, the first one must be a drive.
  774. //
  775. {
  776. TCHAR szFileName[_MAX_PATH + 1];
  777. TCHAR szRootName[_MAX_PATH + 1];
  778. DragQueryFile((HDROP)this->medium.hGlobal, 0, szFileName,
  779. ARRAYSIZE(szFileName));
  780. lstrcpy(szRootName, szFileName);
  781. PathStripToRoot(szRootName);
  782. this->bDriveSelected = (lstrcmpi(szRootName, szFileName) == 0);
  783. }
  784. //
  785. // Get list of file names selected.
  786. // The string contains nul-terminated names.
  787. // The entire list is terminated with a double-nul.
  788. //
  789. // FEATURE: We need to write ANSI/UNICODE thunking layer for
  790. // DragQueryInfo().
  791. //
  792. di.uSize = sizeof(DRAGINFO);
  793. DragQueryInfo((HDROP)this->medium.hGlobal, &di);
  794. pDragFileName = pNextName = di.lpFileList;
  795. for (i = 0; i < this->cSelectedFiles; i++)
  796. {
  797. DWORD dwFlags = 0;
  798. DWORD dwAttrib = 0;
  799. //
  800. // Set bits in the attribute "talley" word. This signals the existence of
  801. // each desired quantity for all selected files.
  802. //
  803. if ((dwAttrib = GetFileAttributes(pDragFileName)) != (DWORD)-1)
  804. {
  805. if ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
  806. dwAttribTalley |= TALLEY_DIRECTORY;
  807. if ((dwAttrib & FILE_ATTRIBUTE_COMPRESSED))
  808. dwAttribTalley |= TALLEY_COMPRESSED;
  809. else
  810. dwAttribTalley |= TALLEY_UNCOMPRESSED;
  811. }
  812. //
  813. // Save pointer to next name.
  814. //
  815. pNextName += lstrlen(pNextName) + 1;
  816. if (!(dwAttribTalley & TALLEY_NOCOMPSUPT))
  817. {
  818. //
  819. // Do we have at least one drive that DOES NOT support compression?
  820. // If so, we don't show the menu items. This provides the INTERSECTION
  821. // of capabilities for the selected files per the UI design guide.
  822. // Note that if GetVolumeInformation fails for a drive, the drive is empty
  823. // and is therefore uncompressible.
  824. //
  825. PathStripToRoot(pDragFileName);
  826. // GetVolumeInformation requires a trailing backslash. Append
  827. // one if this is a UNC path.
  828. if (PathIsUNC(pDragFileName))
  829. {
  830. lstrcat(pDragFileName, c_szBACKSLASH);
  831. }
  832. if (GetVolumeInformation(pDragFileName, NULL, 0, NULL, NULL, &dwFlags, NULL, 0))
  833. dwAttribTalley |= (dwFlags & FS_FILE_COMPRESSION) == 0 ?
  834. TALLEY_NOCOMPSUPT : 0;
  835. else
  836. dwAttribTalley |= TALLEY_NOCOMPSUPT;
  837. }
  838. //
  839. // If all of the flag bits are set, no need to check more files.
  840. // We have all the info we need to properly configure the UI.
  841. //
  842. if ( (dwAttribTalley & ((DWORD)-1) ) == (TALLEY_DIRECTORY |
  843. TALLEY_COMPRESSED |
  844. TALLEY_UNCOMPRESSED |
  845. TALLEY_NOCOMPSUPT))
  846. {
  847. break;
  848. }
  849. //
  850. // Advance name pointer to next name.
  851. //
  852. pDragFileName = pNextName;
  853. }
  854. //
  855. // Free the file name list we got through DragQueryInfo().
  856. //
  857. if (di.lpFileList)
  858. SHFree(di.lpFileList);
  859. //
  860. // Convert the settings of the talley flag bits to more meaningful names.
  861. //
  862. bNonCompressibleVol = (dwAttribTalley & TALLEY_NOCOMPSUPT) != 0;
  863. bDirectorySelected = (dwAttribTalley & TALLEY_DIRECTORY) != 0;
  864. if (!bDirectorySelected)
  865. {
  866. bDisableUncompress = (dwAttribTalley &
  867. (TALLEY_COMPRESSED | TALLEY_UNCOMPRESSED)) == TALLEY_UNCOMPRESSED;
  868. bDisableCompress = (dwAttribTalley &
  869. (TALLEY_COMPRESSED | TALLEY_UNCOMPRESSED)) == TALLEY_COMPRESSED;
  870. }
  871. switch(uFlags & 0x0F) // Upper 28 bits are reserved.
  872. {
  873. case CMF_EXPLORE:
  874. //
  875. // Win32 SDK says we should only get this flag bit set when the user
  876. // has selected an object in the left pane of the Explorer. This is a
  877. // doc bug in the SDK verified by "satona". We get it via selection
  878. // in either pane.
  879. //
  880. case CMF_NORMAL:
  881. if (!bNonCompressibleVol)
  882. {
  883. INT cchLoaded = 0;
  884. TCHAR szMenuItem[MAX_MENUITEM_LEN + 1];
  885. //
  886. // Regarding item separators; can we always count on there being
  887. // an item above and below our new items? If not, we're
  888. // in danger of adding a separator at the top or bottom of
  889. // the menu. BobDay says we'll always have something above
  890. // and below us.
  891. //
  892. cchLoaded = LoadString(g_hmodThisDll,
  893. bDirectorySelected ? IDS_COMPRESS_MENUITEM_ELLIP :
  894. IDS_COMPRESS_MENUITEM,
  895. szMenuItem, ARRAYSIZE(szMenuItem));
  896. ASSERT(cchLoaded > 0);
  897. InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
  898. InsertMenu(hMenu, indexMenu++, MF_STRING | MF_BYPOSITION | (bDisableCompress ? MF_GRAYED : 0),
  899. idCmdFirst + MENUOFS_COMPRESS, szMenuItem);
  900. cMenuItemsAdded++;
  901. cchLoaded = LoadString(g_hmodThisDll,
  902. bDirectorySelected ? IDS_UNCOMPRESS_MENUITEM_ELLIP :
  903. IDS_UNCOMPRESS_MENUITEM,
  904. szMenuItem, ARRAYSIZE(szMenuItem));
  905. ASSERT(cchLoaded > 0);
  906. InsertMenu(hMenu, indexMenu++, MF_STRING | MF_BYPOSITION | (bDisableUncompress ? MF_GRAYED : 0),
  907. idCmdFirst + MENUOFS_UNCOMPRESS, szMenuItem);
  908. cMenuItemsAdded++;
  909. InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
  910. }
  911. break;
  912. case CMF_DEFAULTONLY:
  913. case CMF_VERBSONLY:
  914. //
  915. // SDK Docs say Context Menu Extensions should ignore these.
  916. //
  917. break;
  918. default:
  919. break;
  920. }
  921. //
  922. // Restore original cursor and return number of menu items added.
  923. //
  924. if (hCursor)
  925. SetCursor(hCursor);
  926. ShowCursor(FALSE);
  927. return cMenuItemsAdded;
  928. }
  929. ///////////////////////////////////////////////////////////////////////////////
  930. //
  931. // FUNCTION: CContextMenuExt_InvokeCommand
  932. //
  933. // DESCRIPTION:
  934. //
  935. // Called by the shell whenever the user selects one of the registered
  936. // items on the context menu.
  937. //
  938. // Or may be called programmatically with one of the following verb
  939. // strings:
  940. // "COMPRESS" to compress files.
  941. // "UNCOMPRESS" to uncompress files.
  942. //
  943. // These verb names are case-sensitive and language-
  944. // insensitive.
  945. //
  946. // ARGUMENTS:
  947. //
  948. // pctm
  949. // Pointer to the context menu interface.
  950. //
  951. // pici
  952. // Pointer to the command info structure associated with the selected
  953. // menu command.
  954. //
  955. // RETURNS:
  956. //
  957. // NOERROR = Success
  958. // OLEOBJ_E_INVALIDVERB = Invalid verb was specified in programatic
  959. // invocation.
  960. // E_FAIL = User aborted operation or an error occured during
  961. // compression/uncompression. We should have more
  962. // descriptive failure return info. The original
  963. // implementation of compression in WinFile only
  964. // returned TRUE/FALSE. In the interest of time,
  965. // that "feature" was retained.
  966. //
  967. ///////////////////////////////////////////////////////////////////////////////
  968. STDMETHODIMP CContextMenuExt_InvokeCommand(IContextMenu *pctm,
  969. LPCMINVOKECOMMANDINFO pici)
  970. {
  971. HRESULT hResult = NOERROR;
  972. INT i = 0;
  973. CContextMenuExt *this = IToClass(CContextMenuExt, ctm, pctm);
  974. BOOL bCompressing = FALSE;
  975. BOOL bShowUI = FALSE;
  976. HWND hwndParent = NULL;
  977. #ifdef TRACE_SHEXT
  978. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::InvokeCommand"));
  979. #endif
  980. ASSERT(NULL != pici);
  981. //
  982. // Caller can request that no UI be activated.
  983. //
  984. bShowUI = (pici->fMask & CMIC_MASK_FLAG_NO_UI) == 0;
  985. //
  986. // Parent all displayed dialogs with the handle passed in from the caller.
  987. // Use the desktop as a default if one wasn't provided.
  988. //
  989. if ((hwndParent = pici->hwnd) == NULL)
  990. hwndParent = GetDesktopWindow();
  991. if (HIWORD(pici->lpVerb) == 0)
  992. {
  993. //
  994. // InvokeCommand was called through the context menu extension protocol.
  995. //
  996. bCompressing = LOWORD(pici->lpVerb) == MENUOFS_COMPRESS;
  997. }
  998. else
  999. {
  1000. //
  1001. // InvokeCommand was called programatically.
  1002. // If lpVerb is "COMPRESS", compress the file(s).
  1003. // If lpVerb is "UNCOMPRESS", uncompress the file(s).
  1004. // Otherwise, do nothing and return OLE error code.
  1005. //
  1006. TCHAR szValidVerb[MAX_CMDVERB_LEN + 1];
  1007. TCHAR szVerb[MAX_CMDVERB_LEN + 1];
  1008. INT cchLoaded = 0;
  1009. //
  1010. // Convert verb string to unicode.
  1011. //
  1012. MultiByteToWideChar(CP_ACP, 0L, pici->lpVerb, -1, szVerb, ARRAYSIZE(szVerb));
  1013. bCompressing = FALSE;
  1014. cchLoaded = LoadString(g_hmodThisDll, IDS_COMPRESS_CMDVERB, szValidVerb,
  1015. ARRAYSIZE(szValidVerb));
  1016. ASSERT(cchLoaded > 0);
  1017. if (!lstrcmp(szValidVerb, szVerb))
  1018. {
  1019. bCompressing = TRUE;
  1020. }
  1021. else
  1022. {
  1023. cchLoaded = LoadString(g_hmodThisDll, IDS_UNCOMPRESS_CMDVERB, szValidVerb,
  1024. ARRAYSIZE(szValidVerb));
  1025. ASSERT(cchLoaded > 0);
  1026. if (!lstrcmp(szValidVerb, szVerb))
  1027. {
  1028. bCompressing = FALSE;
  1029. }
  1030. else
  1031. {
  1032. //
  1033. // Verb isn't COMPRESS or UNCOMPRESS.
  1034. //
  1035. hResult = OLEOBJ_E_INVALIDVERB;
  1036. }
  1037. }
  1038. }
  1039. //
  1040. // Do the compression/uncompression.
  1041. //
  1042. if (hResult == NOERROR)
  1043. {
  1044. SCCA_CONTEXT Context; // Compression context structure.
  1045. SCCA_CONTEXT_INIT(&Context); // Initialize context.
  1046. for (i = 0; i < this->cSelectedFiles; i++)
  1047. {
  1048. TCHAR szFileName[_MAX_PATH + 1];
  1049. //
  1050. // Get the name of the file to compress.
  1051. //
  1052. DragQueryFile((HDROP)this->medium.hGlobal, i, szFileName, ARRAYSIZE(szFileName));
  1053. //
  1054. // Compress/uncompress file. Return value of FALSE indicates either an error
  1055. // occured or the user cancelled the operation. In either case, don't process
  1056. // any more files.
  1057. //
  1058. if (!ShellChangeCompressionAttribute(hwndParent, szFileName, &Context,
  1059. bCompressing, bShowUI))
  1060. {
  1061. hResult = E_FAIL;
  1062. break;
  1063. }
  1064. }
  1065. }
  1066. return hResult;
  1067. }
  1068. ///////////////////////////////////////////////////////////////////////////////
  1069. //
  1070. // FUNCTION: CContextMenuExt_GetCommandString
  1071. //
  1072. // DESCRIPTION:
  1073. //
  1074. // Called by the shell when it wants a text string associated with a menu
  1075. // item. Status bar text for example or a menu command verb.
  1076. //
  1077. // ARGUMENTS:
  1078. //
  1079. // pctm
  1080. // Pointer to the context menu interface.
  1081. //
  1082. // idCmd
  1083. // Integer identifier of the command in question. The number is the
  1084. // offset of the menu item in the set of added menu items, based 0.
  1085. //
  1086. // uFlags
  1087. // Indicates what type of service the shell is requesting.
  1088. //
  1089. // pwReserved
  1090. // Unused.
  1091. //
  1092. // pszName
  1093. // Destination for menu item character string. Provided by shell.
  1094. //
  1095. // cchMax
  1096. // Max size of destination buffer. Provided by shell.
  1097. //
  1098. // RETURNS:
  1099. //
  1100. // NOERROR = Success.
  1101. // E_FAIL = Requested menu idCmd not recognized.
  1102. //
  1103. ///////////////////////////////////////////////////////////////////////////////
  1104. STDMETHODIMP CContextMenuExt_GetCommandString(IContextMenu *pctm, UINT_PTR idCmd,
  1105. UINT uFlags, UINT *pwReserved, LPSTR pszName,
  1106. UINT cchMax)
  1107. {
  1108. CContextMenuExt *this = IToClass(CContextMenuExt, ctm, pctm);
  1109. HRESULT hResult = NOERROR;
  1110. #ifdef TRACE_SHEXT
  1111. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::GetCommandString idCmd = %d"), idCmd);
  1112. #endif
  1113. ASSERT(NULL != this);
  1114. ASSERT(NULL != pszName);
  1115. //
  1116. // Start with destination buffer blank.
  1117. //
  1118. if (cchMax > 0)
  1119. pszName[0] = TEXT('\0');
  1120. if (idCmd >= MENUOFS_FIRST && idCmd <= MENUOFS_LAST)
  1121. {
  1122. DWORD *pStrIdArray = NULL;
  1123. BOOL bUnicode = FALSE;
  1124. DWORD dwCmdVerbIds[] = { IDS_COMPRESS_CMDVERB,
  1125. IDS_UNCOMPRESS_CMDVERB };
  1126. DWORD dwSbarTextIds[] = { IDS_COMPRESS_SBARTEXT,
  1127. IDS_UNCOMPRESS_SBARTEXT };
  1128. DWORD dwSbarTextMultIds[] = { IDS_COMPRESS_SBARTEXT_M,
  1129. IDS_UNCOMPRESS_SBARTEXT_M };
  1130. DWORD dwSbarDrvTextIds[] = { IDS_COMPRESS_SBARTEXT_DRV,
  1131. IDS_UNCOMPRESS_SBARTEXT_DRV };
  1132. DWORD dwSbarDrvTextMultIds[] = { IDS_COMPRESS_SBARTEXT_DRV_M,
  1133. IDS_UNCOMPRESS_SBARTEXT_DRV_M };
  1134. switch(uFlags)
  1135. {
  1136. //
  1137. // Provide help text for menu item.
  1138. //
  1139. case GCS_HELPTEXTW:
  1140. case GCS_HELPTEXTA:
  1141. //
  1142. // If drives selected, use "Drive" strings. Otherwise, use "File"
  1143. // strings. Also address multiplicity.
  1144. //
  1145. if (this->cSelectedFiles == 1)
  1146. pStrIdArray = this->bDriveSelected ? dwSbarDrvTextIds : dwSbarTextIds;
  1147. else
  1148. pStrIdArray = this->bDriveSelected ? dwSbarDrvTextMultIds : dwSbarTextMultIds;
  1149. bUnicode = uFlags == GCS_HELPTEXTW;
  1150. break;
  1151. //
  1152. // Provide command verb recognized by InvokeCommand( ).
  1153. //
  1154. case GCS_VERBW:
  1155. case GCS_VERBA:
  1156. pStrIdArray = dwCmdVerbIds;
  1157. bUnicode = uFlags == GCS_VERBW;
  1158. break;
  1159. //
  1160. // Validate that the menu cmd exists.
  1161. //
  1162. case GCS_VALIDATE:
  1163. hResult = NOERROR;
  1164. break;
  1165. default:
  1166. break;
  1167. }
  1168. //
  1169. // If we've identified what array to get the string resource ID from, load
  1170. // the ANSI or UNICODE version of the string related to the command id.
  1171. //
  1172. if (NULL != pStrIdArray)
  1173. {
  1174. INT cchLoaded = 0;
  1175. if (bUnicode)
  1176. cchLoaded = LoadStringW(g_hmodThisDll, *(pStrIdArray + idCmd), (LPWSTR)pszName, cchMax);
  1177. else
  1178. cchLoaded = LoadStringA(g_hmodThisDll, *(pStrIdArray + idCmd), pszName, cchMax);
  1179. ASSERT(cchLoaded > 0);
  1180. }
  1181. }
  1182. else
  1183. hResult = E_FAIL;
  1184. return hResult;
  1185. }
  1186. ///////////////////////////////////////////////////////////////////////////////
  1187. //
  1188. // FUNCTION: CShellExtInit_QueryInterface
  1189. //
  1190. // DESCRIPTION:
  1191. //
  1192. // Called by the shell to obtain an interface from the shell extension.
  1193. //
  1194. // ARGUMENTS:
  1195. //
  1196. // psei
  1197. // Pointer to the shell extension interface.
  1198. //
  1199. // riid
  1200. // Reference to the requested interface ID.
  1201. //
  1202. // ppvOut
  1203. // Address of destination for resulting interface pointer.
  1204. //
  1205. //
  1206. // RETURNS:
  1207. //
  1208. // NOERROR
  1209. // E_NOINTERFACE
  1210. //
  1211. ///////////////////////////////////////////////////////////////////////////////
  1212. STDMETHODIMP CShellExtInit_QueryInterface(IShellExtInit *psei, REFIID riid,
  1213. LPVOID *ppvOut)
  1214. {
  1215. CContextMenuExt *this = PSEI2PCMX(psei);
  1216. #ifdef TRACE_SHEXT
  1217. DbgOut(TEXT("SHCOMPUI: CShellExtInit::QueryInterface"));
  1218. #endif
  1219. ASSERT(NULL != this);
  1220. ASSERT(NULL != ppvOut);
  1221. return CContextMenuExt_QueryInterface(&this->ctm, riid, ppvOut);
  1222. }
  1223. ///////////////////////////////////////////////////////////////////////////////
  1224. //
  1225. // FUNCTION: CShellExtInit_AddRef
  1226. //
  1227. // DESCRIPTION:
  1228. //
  1229. // Increments the reference count for the shell extension init
  1230. // interface.
  1231. //
  1232. // ARGUMENTS:
  1233. //
  1234. // psei
  1235. // Pointer to the shell extension interface.
  1236. //
  1237. // RETURNS:
  1238. //
  1239. // New reference counter value.
  1240. //
  1241. ///////////////////////////////////////////////////////////////////////////////
  1242. STDMETHODIMP_(ULONG) CShellExtInit_AddRef(IShellExtInit *psei)
  1243. {
  1244. CContextMenuExt *this = PSEI2PCMX(psei);
  1245. #ifdef TRACE_SHEXT
  1246. DbgOut(TEXT("SHCOMPUI: CShellExtInit::AddRef"));
  1247. #endif
  1248. ASSERT(NULL != this);
  1249. return CContextMenuExt_AddRef(&this->ctm);
  1250. }
  1251. ///////////////////////////////////////////////////////////////////////////////
  1252. //
  1253. // FUNCTION: CShellExtInit_Release
  1254. //
  1255. // DESCRIPTION:
  1256. //
  1257. // Decrements the reference count for the shell extension init
  1258. // interface. When count reaches 0, the interface object is
  1259. // deleted.
  1260. //
  1261. // ARGUMENTS:
  1262. //
  1263. // psei
  1264. // Pointer to the shell extension interface.
  1265. //
  1266. // RETURNS:
  1267. //
  1268. // New reference counter value.
  1269. //
  1270. ///////////////////////////////////////////////////////////////////////////////
  1271. STDMETHODIMP_(ULONG) CShellExtInit_Release(IShellExtInit *psei)
  1272. {
  1273. CContextMenuExt *this = PSEI2PCMX(psei);
  1274. ULONG refCnt = 0;
  1275. #ifdef TRACE_SHEXT
  1276. DbgOut(TEXT("SHCOMPUI: CShellExtInit::Release"));
  1277. #endif
  1278. ASSERT(NULL != this);
  1279. return CContextMenuExt_Release(&this->ctm);
  1280. }
  1281. ///////////////////////////////////////////////////////////////////////////////
  1282. //
  1283. // CShellExtInit::Initialize
  1284. //
  1285. // Called by the shell to initialize the shell extension.
  1286. //
  1287. // Arguments:
  1288. //
  1289. // psei
  1290. // Pointer to the IShellExtInit interface.
  1291. //
  1292. // pidlFolder
  1293. // Pointer to item ID list of parent folder.
  1294. //
  1295. // lpdobj
  1296. // Pointer to data object containing selected file names.
  1297. //
  1298. // hkeyProgID
  1299. // Registry class of the file object that has focus.
  1300. //
  1301. // RETURNS:
  1302. //
  1303. // S_OK
  1304. // E_FAIL
  1305. // E_INVALIDARG
  1306. // E_UNEXPECTED
  1307. // E_OUTOFMEMORY
  1308. // DV_E_LINDEX
  1309. // DV_E_FORMATETC
  1310. // DV_E_TYMED
  1311. // DV_E_DVASPECT
  1312. // OLE_E_NOTRUNNING
  1313. // STG_E_MEDIUMFULL
  1314. //
  1315. ///////////////////////////////////////////////////////////////////////////////
  1316. static STDMETHODIMP CShellExtInit_Initialize(IShellExtInit *psei,
  1317. LPCITEMIDLIST pidlFolder, LPDATAOBJECT lpdobj, HKEY hkeyProgID)
  1318. {
  1319. CContextMenuExt *this = PSEI2PCMX(psei);
  1320. FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  1321. HRESULT hResult = NOERROR;
  1322. #ifdef TRACE_SHEXT
  1323. DbgOut(TEXT("SHCOMPUI: CShellExtInit::Initialize"));
  1324. #endif
  1325. ASSERT(NULL != this);
  1326. //
  1327. // According to Win32 SDK, Initialize can be called more than once.
  1328. //
  1329. CContextMenu_Cleanup(this);
  1330. if (NULL != lpdobj)
  1331. {
  1332. this->pDataObj = lpdobj;
  1333. lpdobj->lpVtbl->AddRef(lpdobj);
  1334. //
  1335. // If we are allowed to get data from the data object, save the medium
  1336. // descriptor in our context menu extension object. We'll use it to
  1337. // iterate through the names of the selected files in
  1338. // CContextMenuExt::InvokeCommand( ).
  1339. //
  1340. hResult = lpdobj->lpVtbl->GetData(lpdobj, &fe, &this->medium);
  1341. if (NOERROR == hResult)
  1342. this->cSelectedFiles = DragQueryFile((HDROP)this->medium.hGlobal, (DWORD)-1, NULL, 0);
  1343. else
  1344. this->cSelectedFiles = 0;
  1345. }
  1346. else
  1347. hResult = E_FAIL;
  1348. return hResult;
  1349. }
  1350. ///////////////////////////////////////////////////////////////////////////////
  1351. // CLASS VTABLES
  1352. ///////////////////////////////////////////////////////////////////////////////
  1353. #pragma data_seg(".text")
  1354. //
  1355. // Create the class factory vtbl in a read-only segment.
  1356. //
  1357. IClassFactoryVtbl c_vtblCClassFactory = {
  1358. CClassFactory_QueryInterface,
  1359. CClassFactory_AddRef,
  1360. CClassFactory_Release,
  1361. CClassFactory_CreateInstance,
  1362. CClassFactory_LockServer
  1363. };
  1364. //
  1365. // Create the context menu extension vtbl in a read-only segment.
  1366. //
  1367. IContextMenuVtbl c_vtblContextMenuExt = {
  1368. CContextMenuExt_QueryInterface,
  1369. CContextMenuExt_AddRef,
  1370. CContextMenuExt_Release,
  1371. CContextMenuExt_QueryContextMenu,
  1372. CContextMenuExt_InvokeCommand,
  1373. CContextMenuExt_GetCommandString
  1374. };
  1375. //
  1376. // Create the shell extension initialization vtbl in a read-only segment.
  1377. //
  1378. IShellExtInitVtbl c_vtblShellExtInit = {
  1379. CShellExtInit_QueryInterface,
  1380. CShellExtInit_AddRef,
  1381. CShellExtInit_Release,
  1382. CShellExtInit_Initialize
  1383. };
  1384. #pragma data_seg()
  1385. ///////////////////////////////////////////////////////////////////////////////
  1386. //
  1387. // CContextMenuExt_CreateInstance
  1388. //
  1389. // Context menu extension instance generator. Creates a context menu extension
  1390. // object and returns a pointer to the requested interface.
  1391. //
  1392. // Arguments:
  1393. //
  1394. // punkOuter
  1395. // Not used for objects that don't support aggregation. We don't support
  1396. // aggregation so we don't use it.
  1397. //
  1398. // riid
  1399. // Reference to the requested interface ID.
  1400. //
  1401. // ppvOut
  1402. // Address of the destination for the interface pointer.
  1403. //
  1404. // RETURNS:
  1405. //
  1406. // NOERROR = Success.
  1407. // E_OUTOFMEMORY = Can't allocate extension object.
  1408. // E_NOINTERFACE = Interface not supported.
  1409. // CLASS_E_NOAGGREGATION = Aggregation not supported.
  1410. //
  1411. ///////////////////////////////////////////////////////////////////////////////
  1412. static HRESULT CContextMenuExt_CreateInstance(IUnknown *punkOuter,
  1413. REFIID riid, void **ppvOut)
  1414. {
  1415. CContextMenuExt *pcmx = NULL;
  1416. HRESULT hResult = NOERROR;
  1417. #ifdef TRACE_SHEXT
  1418. DbgOut(TEXT("SHCOMPUI: CContextMenuExt::CreateInstance"));
  1419. #endif
  1420. ASSERT(NULL != ppvOut);
  1421. *ppvOut = NULL;
  1422. if (NULL == punkOuter)
  1423. {
  1424. pcmx = (CContextMenuExt *)LocalAlloc(LPTR, sizeof(CContextMenuExt));
  1425. if (NULL != pcmx)
  1426. {
  1427. pcmx->ctm.lpVtbl = &c_vtblContextMenuExt; // Context menu ext vtable ptr.
  1428. pcmx->sei.lpVtbl = &c_vtblShellExtInit; // Shell extention int vtable ptr.
  1429. pcmx->cRef = 0; // Initialize reference counter.
  1430. pcmx->medium.tymed = TYMED_NULL; // Not yet initialized by shell.
  1431. pcmx->medium.hGlobal = (HGLOBAL)NULL; // Not yet initialized by shell.
  1432. pcmx->medium.pUnkForRelease = NULL; // Not yet initialized by shell.
  1433. pcmx->cSelectedFiles = 0; // Not yet initialized by shell.
  1434. pcmx->bDriveSelected = FALSE; // No drives selected yet.
  1435. pcmx->pDataObj = NULL;
  1436. hResult = c_vtblContextMenuExt.QueryInterface(&pcmx->ctm, riid, ppvOut);
  1437. ChgDllRefCnt(+1);
  1438. }
  1439. else
  1440. hResult = E_OUTOFMEMORY;
  1441. }
  1442. else
  1443. hResult = CLASS_E_NOAGGREGATION; // Extension doesn't support aggregation.
  1444. return hResult;
  1445. }
  1446. ///////////////////////////////////////////////////////////////////////////////
  1447. //
  1448. // CreateClassObject
  1449. //
  1450. // Creates a class factory object returning a pointer to its IUnknown interface.
  1451. //
  1452. // Arguments:
  1453. //
  1454. // riid
  1455. // Reference to interface on class factory object.
  1456. //
  1457. // pfcnCI
  1458. // Pointer to instance creation function.
  1459. // In this application, this is CContextMenuExt_CreateInstance.
  1460. //
  1461. // ppvOut
  1462. // Destination for pointer to class factory interface (Vtable).
  1463. //
  1464. // RETURNS:
  1465. //
  1466. // NOERROR = Success.
  1467. // E_NOINTERFACE = Interface not supported.
  1468. // E_OUTOFMEMORY = Can't create class factory object.
  1469. //
  1470. ///////////////////////////////////////////////////////////////////////////////
  1471. STDAPI CreateClassObject(REFIID riid, LPFNCREATEINSTANCE pfnCI, LPVOID *ppvOut)
  1472. {
  1473. HRESULT hResult = NOERROR;
  1474. #ifdef TRACE_SHEXT
  1475. DbgOut(TEXT("SHCOMPUI: CreateClassObject"));
  1476. #endif
  1477. ASSERT(NULL != ppvOut);
  1478. ASSERT(NULL != pfnCI);
  1479. *ppvOut = NULL; // Initialize pointer transfer buffer.
  1480. if (IsEqualIID(riid, &IID_IClassFactory))
  1481. {
  1482. //
  1483. // Allocate the class factory structure.
  1484. //
  1485. CClassFactory *pcf = (CClassFactory *)LocalAlloc(LPTR, sizeof(CClassFactory));
  1486. if (NULL != pcf)
  1487. {
  1488. pcf->cf.lpVtbl = &c_vtblCClassFactory; // Assign ptr to vtbl.
  1489. pcf->cRef++; // Increment interface ref count.
  1490. pcf->pfnCI = pfnCI; // Assign ptr instance creation proc.
  1491. (IClassFactory *)*ppvOut = &pcf->cf; // Return ptr to vtbl (interface).
  1492. ChgDllRefCnt(+1);
  1493. }
  1494. else
  1495. hResult = E_OUTOFMEMORY; // Cannot get or use shell memory allocator interface.
  1496. }
  1497. else
  1498. hResult = E_NOINTERFACE; // Cannot produce requested interface.
  1499. return hResult;
  1500. }
  1501. ///////////////////////////////////////////////////////////////////////////////
  1502. //
  1503. // FUNCTION: DllGetClassObject
  1504. //
  1505. // DESCRIPTION:
  1506. //
  1507. // Called by NT Shell to retrieve the interface to the Class factory.
  1508. //
  1509. // ARGUMENTS:
  1510. //
  1511. // rclsid
  1512. // Reference to class ID that identifies the type of object that the
  1513. // class factory will be asked to create.
  1514. //
  1515. // riid
  1516. // Reference to interface ID on the class factory object.
  1517. //
  1518. // ppvOut
  1519. // Destination location for class factory object pointer after instantiation.
  1520. //
  1521. // RETURNS:
  1522. //
  1523. // NOERROR = Success.
  1524. // E_OUTOFMEMORY = Can't create class factory object.
  1525. // E_NOINTERFACE = Interface not supported.
  1526. // CLASS_E_CLASSNOTAVAILABLE = Context menu extension not available.
  1527. //
  1528. ///////////////////////////////////////////////////////////////////////////////
  1529. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
  1530. {
  1531. HRESULT hResult = CLASS_E_CLASSNOTAVAILABLE;
  1532. #ifdef TRACE_SHEXT
  1533. DbgOut(TEXT("SHCOMPUI: DllGetClassObject"));
  1534. #endif
  1535. ASSERT(NULL != ppvOut);
  1536. *ppvOut = NULL;
  1537. //
  1538. // Call the extension-provided creation function corresponding
  1539. // to the class ID of the requested extension.
  1540. //
  1541. if (IsEqualIID(rclsid, &CLSID_CompressMenuExt ))
  1542. {
  1543. hResult = CreateClassObject(riid,
  1544. (LPFNCREATEINSTANCE)CContextMenuExt_CreateInstance, ppvOut);
  1545. }
  1546. #ifdef DBG
  1547. if (hResult != NOERROR)
  1548. DbgOut(TEXT("SHCOMPUI: Context menu extension [CLSID_CompressMenuExt] creation failed."));
  1549. #endif
  1550. return hResult;
  1551. }
  1552. ///////////////////////////////////////////////////////////////////////////////
  1553. //
  1554. // FUNCTION: DllCanUnloadNow
  1555. //
  1556. // DESCRIPTION:
  1557. //
  1558. // Called by NT to determine if DLL can be unloaded.
  1559. //
  1560. // ARGUMENTS:
  1561. //
  1562. // None.
  1563. //
  1564. // RETURNS:
  1565. //
  1566. // S_FALSE = Can't unload.
  1567. // S_OK = OK to unload.
  1568. //
  1569. ///////////////////////////////////////////////////////////////////////////////
  1570. STDAPI DllCanUnloadNow(void)
  1571. {
  1572. #ifdef TRACE_SHEXT
  1573. DbgOut(TEXT("SHCOMPUI: DllCanUnloadNow. Dll reference count = %d"), g_cRefThisDll);
  1574. #endif
  1575. ASSERT(g_cRefThisDll >= 0);
  1576. //
  1577. // I test for <= 0 so that the DLL can be unloaded even if the ref
  1578. // count drops below 0 (reference count error). The preceding
  1579. // ASSERT( ) catches this during development. This means that
  1580. // the ref counter has to be signed.
  1581. //
  1582. return ResultFromScode((g_cRefThisDll <= 0) ? S_OK : S_FALSE);
  1583. }
  1584. ///////////////////////////////////////////////////////////////////////////////
  1585. //
  1586. // FUNCTION: DllMain
  1587. //
  1588. // DESCRIPTION:
  1589. //
  1590. // Dll entry point.
  1591. //
  1592. ///////////////////////////////////////////////////////////////////////////////
  1593. int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
  1594. {
  1595. TCHAR szLocaleInfo[20];
  1596. switch(dwReason)
  1597. {
  1598. case DLL_PROCESS_ATTACH:
  1599. #ifdef TRACE_DLL
  1600. DbgOut(TEXT("SHCOMPUI: DLL_PROCESS_ATTACH"));
  1601. #endif
  1602. g_hmodThisDll = hInstance;
  1603. g_hProcessHeap = GetProcessHeap();
  1604. DisableThreadLibraryCalls(hInstance);
  1605. //
  1606. // Create/Open semaphore to prevent re-entrancy.
  1607. //
  1608. g_hSemaphore = CreateSemaphore(NULL, 1, 1, SZ_SEMAPHORE_NAME);
  1609. if (GetLastError() == ERROR_ALREADY_EXISTS)
  1610. g_hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, SZ_SEMAPHORE_NAME);
  1611. if (NULL == g_hSemaphore)
  1612. return FALSE; // Can't create/open semaphore object.
  1613. //
  1614. // Prepare number format info for current locale.
  1615. // Used in progress dialog display of 64-bit integers.
  1616. //
  1617. g_NumberFormat.NumDigits = 0; // This is locale-insensitive.
  1618. g_NumberFormat.LeadingZero = 0; // So is this.
  1619. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szLocaleInfo, ARRAYSIZE(szLocaleInfo));
  1620. g_NumberFormat.Grouping = StrToLong(szLocaleInfo);
  1621. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, g_szDecimalSep, ARRAYSIZE(g_szDecimalSep));
  1622. g_NumberFormat.lpDecimalSep = g_szDecimalSep;
  1623. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, g_szThousandSep, ARRAYSIZE(g_szThousandSep));
  1624. g_NumberFormat.lpThousandSep = g_szThousandSep;
  1625. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szLocaleInfo, ARRAYSIZE(szLocaleInfo));
  1626. g_NumberFormat.NegativeOrder = StrToLong(szLocaleInfo);
  1627. break;
  1628. case DLL_PROCESS_DETACH:
  1629. #ifdef TRACE_DLL
  1630. DbgOut(TEXT("SHCOMPUI: DLL_PROCESS_DETACH"));
  1631. #endif
  1632. CloseHandle(g_hSemaphore);
  1633. break;
  1634. case DLL_THREAD_ATTACH:
  1635. #ifdef TRACE_DLL
  1636. DbgOut(TEXT("SHCOMPUI: DLL_THREAD_ATTACH"));
  1637. #endif
  1638. break;
  1639. case DLL_THREAD_DETACH:
  1640. #ifdef TRACE_DLL
  1641. DbgOut(TEXT("SHCOMPUI: DLL_THREAD_DETACH"));
  1642. #endif
  1643. break;
  1644. default:
  1645. break;
  1646. }
  1647. return TRUE;
  1648. }
  1649. ///////////////////////////////////////////////////////////////////////////////
  1650. //
  1651. // FUNCTION: FormatStringWithArgs
  1652. //
  1653. // DESCRIPTION:
  1654. //
  1655. // Formats a text string with variable arguments.
  1656. //
  1657. // ARGUMENTS:
  1658. //
  1659. // pszFormat
  1660. // Address of format text string using %1,%2 etc. format specifiers.
  1661. //
  1662. // pszBuffer
  1663. // Address of destination buffer.
  1664. //
  1665. // nSize
  1666. // Number of characters in destination buffer.
  1667. //
  1668. // ...
  1669. // Variable length list of replacement parameters.
  1670. //
  1671. // RETURNS:
  1672. //
  1673. // Returns number of characters copied to buffer.
  1674. // 0 = Error. GetLastError() if you're interested in why.
  1675. //
  1676. ///////////////////////////////////////////////////////////////////////////////
  1677. static DWORD FormatStringWithArgs(LPCTSTR pszFormat, LPTSTR pszBuffer,
  1678. DWORD nSize, ...)
  1679. {
  1680. DWORD dwCharCount = 0;
  1681. va_list args;
  1682. ASSERT(NULL != pszBuffer);
  1683. ASSERT(NULL != pszFormat);
  1684. //
  1685. // Format the resource string by replacing parameters if present.
  1686. //
  1687. va_start(args, nSize);
  1688. dwCharCount = FormatMessage(FORMAT_MESSAGE_FROM_STRING,
  1689. pszFormat,
  1690. 0,
  1691. 0,
  1692. pszBuffer,
  1693. nSize,
  1694. &args);
  1695. va_end(args);
  1696. return dwCharCount;
  1697. }
  1698. ///////////////////////////////////////////////////////////////////////////////
  1699. //
  1700. // FUNCTION: LoadStringWithArgs
  1701. //
  1702. // DESCRIPTION:
  1703. //
  1704. // Formats a resource string with variable arguments.
  1705. //
  1706. // ARGUMENTS:
  1707. //
  1708. // hInstance
  1709. // Instance handle for module containing resource string.
  1710. //
  1711. // uId
  1712. // Resource string ID. String may contain embedded formatting characters
  1713. // for replaceable parameters (i.e. "Delete file %1 ?")
  1714. //
  1715. // szBuffer
  1716. // Destination buffer.
  1717. //
  1718. // nSize
  1719. // Number of characters in destination buffer.
  1720. //
  1721. // ...
  1722. // Variable length list of replacement parameters.
  1723. //
  1724. // RETURNS:
  1725. //
  1726. // Returns number of characters copied to buffer.
  1727. // 0 = Error. GetLastError() if you're interested in why.
  1728. // Function does set last error to E_OUTOFMEMORY on LocalAlloc fail.
  1729. //
  1730. ///////////////////////////////////////////////////////////////////////////////
  1731. static DWORD LoadStringWithArgs(HINSTANCE hInstance, UINT uId,
  1732. LPTSTR pszBuffer, DWORD nSize, ...)
  1733. {
  1734. DWORD dwCharCount = 0;
  1735. LPTSTR pszFormat = NULL;
  1736. va_list args;
  1737. ASSERT(NULL != pszBuffer);
  1738. //
  1739. // Allocate a buffer for the resource string.
  1740. //
  1741. if ((pszFormat = LocalAlloc(LMEM_FIXED, nSize * sizeof(TCHAR))) != NULL)
  1742. {
  1743. //
  1744. // Load the resource string from the specified module.
  1745. //
  1746. if (LoadString(hInstance, uId, pszFormat, nSize) != 0)
  1747. {
  1748. va_start(args, nSize);
  1749. dwCharCount = FormatMessage(FORMAT_MESSAGE_FROM_STRING,
  1750. pszFormat,
  1751. 0,
  1752. 0,
  1753. pszBuffer,
  1754. nSize,
  1755. &args);
  1756. va_end(args);
  1757. }
  1758. LocalFree(pszFormat);
  1759. }
  1760. else
  1761. SetLastError((DWORD)E_OUTOFMEMORY);
  1762. return dwCharCount;
  1763. }
  1764. ///////////////////////////////////////////////////////////////////////////////
  1765. //
  1766. // FUNCTION: CenterWindowInParent
  1767. //
  1768. // DESCRIPTION:
  1769. //
  1770. // Positions a window centered in its parent.
  1771. // This function was taken from WinFile.
  1772. //
  1773. // ARGUMENTS:
  1774. //
  1775. // hwnd
  1776. // Handle of window to be centered.
  1777. //
  1778. //
  1779. // RETURNS:
  1780. //
  1781. // Nothing.
  1782. //
  1783. ///////////////////////////////////////////////////////////////////////////////
  1784. static VOID CenterWindowInParent(HWND hwnd)
  1785. {
  1786. RECT rect;
  1787. RECT rectParent;
  1788. HWND hwndParent;
  1789. LONG Style;
  1790. //
  1791. // Get window rect.
  1792. //
  1793. GetWindowRect(hwnd, &rect);
  1794. //
  1795. // Get parent rect.
  1796. //
  1797. Style = GetWindowLong(hwnd, GWL_STYLE);
  1798. if ((Style & WS_CHILD) == 0)
  1799. {
  1800. hwndParent = GetDesktopWindow();
  1801. }
  1802. else
  1803. {
  1804. hwndParent = GetParent(hwnd);
  1805. if (hwndParent == NULL)
  1806. {
  1807. hwndParent = GetDesktopWindow();
  1808. }
  1809. }
  1810. GetWindowRect(hwndParent, &rectParent);
  1811. //
  1812. // Center the child in the parent.
  1813. //
  1814. rect.left = rectParent.left + (((rectParent.right - rectParent.left) - (rect.right - rect.left)) >> 1);
  1815. rect.top = rectParent.top + (((rectParent.bottom - rectParent.top) - (rect.bottom - rect.top)) >> 1);
  1816. //
  1817. // Move the child into position.
  1818. //
  1819. SetWindowPos( hwnd,
  1820. NULL,
  1821. rect.left,
  1822. rect.top,
  1823. 0,
  1824. 0,
  1825. SWP_NOSIZE | SWP_NOZORDER );
  1826. SetForegroundWindow(hwnd);
  1827. }
  1828. ///////////////////////////////////////////////////////////////////////////////
  1829. //
  1830. // FUNCTION: CompressSubsConfirmDlgProc
  1831. //
  1832. // DESCRIPTION:
  1833. //
  1834. // Message proc for compression UI confirmation dialog. The dialog is
  1835. // displayed whenever a selected directory is about to be compressed or
  1836. // uncompressed. The dialog includes a message stating that all files
  1837. // in the selected directory are about to be compressed/uncompressed.
  1838. // Included is a checkbox that may be checked to approve compression/
  1839. // uncompression of all sub-folders.
  1840. //
  1841. // ARGUMENTS:
  1842. //
  1843. // wParam
  1844. // Unused.
  1845. //
  1846. // lParam
  1847. // Address of a "Compression Descriptor" (type CompressionDesc).
  1848. // The descriptor contains the name of the file to be compresssed/
  1849. // uncompressed along with a flag value indicating which operation
  1850. // is being performed.
  1851. //
  1852. //
  1853. // RETURNS:
  1854. //
  1855. // 0 = User pressed Cancel button. Don't compress this folder.
  1856. // (COMPRESS_CANCELLED)
  1857. // 1 = User pressed OK button. Sub-folder checkbox is unchecked.
  1858. // (COMPRESS_SUBSNO)
  1859. // 2 = User pressed OK button. Sub-folder checkbox is checked.
  1860. // (COMPRESS_SUBSYES)
  1861. //
  1862. ///////////////////////////////////////////////////////////////////////////////
  1863. static INT_PTR CALLBACK CompressSubsConfirmDlgProc(HWND hDlg, UINT uMsg,
  1864. WPARAM wParam, LPARAM lParam)
  1865. {
  1866. //
  1867. // Dialog message string resource IDs. Array indexes map directly to values
  1868. // of lParam.
  1869. //
  1870. UINT uDlgTextIds[] = { IDS_UNCOMPRESS_CONFIRMATION, IDS_COMPRESS_CONFIRMATION };
  1871. UINT uCbxTextIds[] = { IDS_UNCOMPRESS_ALSO, IDS_COMPRESS_ALSO };
  1872. UINT uActionTextIds[] = { IDS_UNCOMPRESS_ACTION, IDS_COMPRESS_ACTION };
  1873. TCHAR szDlgText[40 + _MAX_PATH]; // Dialog text resource string buffer.
  1874. CompressionDesc *cd = (CompressionDesc *)lParam;
  1875. UINT cchLoaded = 0;
  1876. switch(uMsg)
  1877. {
  1878. case WM_INITDIALOG:
  1879. ASSERT(NULL != (void *)lParam);
  1880. //
  1881. // Initialize the "Compress/Uncompress all files in..." message.
  1882. //
  1883. LoadStringWithArgs(g_hmodThisDll, uDlgTextIds[cd->bCompress], szDlgText,
  1884. ARRAYSIZE(szDlgText), cd->szFileName);
  1885. SetDlgItemText(hDlg, IDC_COMPRESS_CONFIRM_TEXT, szDlgText);
  1886. //
  1887. // Initialize the "This action compresses..." message.
  1888. //
  1889. cchLoaded = LoadString(g_hmodThisDll, uActionTextIds[cd->bCompress],
  1890. szDlgText, ARRAYSIZE(szDlgText));
  1891. ASSERT(cchLoaded > 0);
  1892. SetDlgItemText(hDlg, IDC_COMPRESS_ACTION_TEXT, szDlgText);
  1893. //
  1894. // Initialize the "also compress/uncompress subfolders" checkbox message.
  1895. //
  1896. cchLoaded = LoadString(g_hmodThisDll, uCbxTextIds[cd->bCompress], szDlgText,
  1897. ARRAYSIZE(szDlgText));
  1898. ASSERT(cchLoaded > 0);
  1899. SetDlgItemText(hDlg, IDC_COMPRESS_SUBFOLDERS, szDlgText);
  1900. return TRUE;
  1901. case WM_COMMAND:
  1902. //
  1903. // Handle user button selections.
  1904. //
  1905. switch(wParam)
  1906. {
  1907. case IDOK:
  1908. EndDialog(hDlg, Button_GetCheck(GetDlgItem(hDlg, IDC_COMPRESS_SUBFOLDERS)) ?
  1909. COMPRESS_SUBSYES :
  1910. COMPRESS_SUBSNO);
  1911. return TRUE;
  1912. case IDCANCEL:
  1913. g_pContext->uCompletionReason = SCCA_REASON_USERCANCEL;
  1914. EndDialog(hDlg, COMPRESS_CANCELLED);
  1915. //
  1916. // Fall through to return TRUE.
  1917. //
  1918. case IDC_COMPRESS_SUBFOLDERS:
  1919. //
  1920. // Do nothing when the checkbox is selected.
  1921. //
  1922. return TRUE;
  1923. }
  1924. }
  1925. return FALSE;
  1926. }
  1927. ///////////////////////////////////////////////////////////////////////////////
  1928. //
  1929. // FUNCTION: CompressProgressYield
  1930. //
  1931. // DESCRIPTION:
  1932. //
  1933. // Allow other messages including Dialog messages for Modeless dialog to be
  1934. // processed while we are Compressing and Uncompressing files. This message
  1935. // loop is similar to "wfYield" in treectl.c except that it allows for the
  1936. // processing of Modeless dialog messages also (specifically for the Progress
  1937. // Dialogs).
  1938. //
  1939. // Since the file/directory Compression/Uncompression is done on a single
  1940. // thread (in order to keep it synchronous with the existing Set Attributes
  1941. // processing) we need to provide a mechanism that will allow a user to
  1942. // Cancel out of the operation and also allow window messages, like WM_PAINT,
  1943. // to be processed by other Window Procedures.
  1944. //
  1945. // Taken from WinFile. Removed MDI-related processing.
  1946. //
  1947. // ARGUMENTS:
  1948. //
  1949. // None.
  1950. //
  1951. // RETURNS:
  1952. //
  1953. // Nothing.
  1954. //
  1955. ///////////////////////////////////////////////////////////////////////////////
  1956. static VOID CompressProgressYield(void)
  1957. {
  1958. MSG msg;
  1959. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1960. {
  1961. if (!g_hdlgProgress || !IsDialogMessage(g_hdlgProgress, &msg))
  1962. {
  1963. TranslateMessage(&msg);
  1964. DispatchMessage(&msg);
  1965. }
  1966. }
  1967. }
  1968. ///////////////////////////////////////////////////////////////////////////////
  1969. //
  1970. // FUNCTION: DisplayUncompressProgress
  1971. //
  1972. // DESCRIPTION:
  1973. //
  1974. // Update the progress of uncompressing files.
  1975. //
  1976. // This routine uses the global variables to update the Dialog box items
  1977. // which display the progress through the uncompression process. The global
  1978. // variables are updated by individual routines. An ordinal value is sent
  1979. // to this routine which determines which dialog box item to update.
  1980. // Taken from WinFile.
  1981. //
  1982. //
  1983. // ARGUMENTS:
  1984. //
  1985. // iType
  1986. // Control value to determine what is to be updated.
  1987. // Value names are self-descriptive.
  1988. //
  1989. // RETURNS:
  1990. //
  1991. // Nothing.
  1992. //
  1993. ///////////////////////////////////////////////////////////////////////////////
  1994. static VOID DisplayUncompressProgress(int iType)
  1995. {
  1996. TCHAR szNum[30];
  1997. if (!g_bShowProgress)
  1998. {
  1999. return;
  2000. }
  2001. switch (iType)
  2002. {
  2003. case ( PROGRESS_UPD_FILEANDDIR ) :
  2004. case ( PROGRESS_UPD_FILENAME ) :
  2005. {
  2006. SetDlgItemText(g_hdlgProgress, IDC_UNCOMPRESS_FILE, g_szFile);
  2007. if (iType != PROGRESS_UPD_FILEANDDIR)
  2008. {
  2009. break;
  2010. }
  2011. // else...fall thru
  2012. }
  2013. case ( PROGRESS_UPD_DIRECTORY ) :
  2014. {
  2015. RECT rect;
  2016. //
  2017. // Preprocess the directory name to shorten it to fit
  2018. // into the alloted space.
  2019. //
  2020. GetWindowRect(GetDlgItem(g_hdlgProgress, IDC_UNCOMPRESS_DIR), &rect);
  2021. DrawTextEx(g_hdcDirectoryTextCtrl, g_szDirectory, lstrlen(g_szDirectory), &rect, DT_MODIFYSTRING | DT_PATH_ELLIPSIS, NULL);
  2022. SetDlgItemText(g_hdlgProgress, IDC_UNCOMPRESS_DIR, g_szDirectory);
  2023. break;
  2024. }
  2025. case ( PROGRESS_UPD_DIRCNT ) :
  2026. {
  2027. AddCommas((DWORD)g_cTotalDirectories, szNum);
  2028. SetDlgItemText(g_hdlgProgress, IDC_UNCOMPRESS_DIRCNT, szNum);
  2029. break;
  2030. }
  2031. case ( PROGRESS_UPD_FILENUMBERS ) :
  2032. case ( PROGRESS_UPD_FILECNT ) :
  2033. {
  2034. AddCommas((DWORD)g_cTotalFiles, szNum);
  2035. SetDlgItemText(g_hdlgProgress, IDC_UNCOMPRESS_FILECNT, szNum);
  2036. break;
  2037. }
  2038. }
  2039. CompressProgressYield();
  2040. }
  2041. ///////////////////////////////////////////////////////////////////////////////
  2042. //
  2043. // FUNCTION: UncompressProgDlg
  2044. //
  2045. // DESCRIPTION:
  2046. //
  2047. // Display progress information.
  2048. // Taken from WinFile.
  2049. //
  2050. // NOTE: This is a modeless dialog and must be terminated with DestroyWindow
  2051. // and NOT EndDialog
  2052. //
  2053. // ARGUMENTS:
  2054. //
  2055. // Standard dialog proc args.
  2056. //
  2057. // RETURNS:
  2058. //
  2059. // TRUE = Message handled.
  2060. // FALSE = Message not handled.
  2061. //
  2062. ///////////////////////////////////////////////////////////////////////////////
  2063. INT_PTR APIENTRY UncompressProgDlg(
  2064. HWND hDlg,
  2065. UINT nMsg,
  2066. WPARAM wParam,
  2067. LPARAM lParam)
  2068. {
  2069. TCHAR szTemp[120];
  2070. RECT rect;
  2071. switch (nMsg)
  2072. {
  2073. case ( WM_INITDIALOG ) :
  2074. {
  2075. CenterWindowInParent(hDlg);
  2076. g_hdlgProgress = hDlg;
  2077. //
  2078. // Clear Dialog items.
  2079. //
  2080. szTemp[0] = TEXT('\0');
  2081. SetDlgItemText(hDlg, IDC_UNCOMPRESS_FILE, szTemp);
  2082. SetDlgItemText(hDlg, IDC_UNCOMPRESS_DIR, szTemp);
  2083. SetDlgItemText(hDlg, IDC_UNCOMPRESS_DIRCNT, szTemp);
  2084. SetDlgItemText(hDlg, IDC_UNCOMPRESS_FILECNT, szTemp);
  2085. g_hdcDirectoryTextCtrl = GetDC(GetDlgItem(hDlg, IDC_UNCOMPRESS_DIR));
  2086. GetClientRect(GetDlgItem(hDlg, IDC_UNCOMPRESS_DIR), &rect);
  2087. g_cDirectoryTextCtrlWd = rect.right;
  2088. EnableWindow(hDlg, TRUE);
  2089. break;
  2090. }
  2091. case ( WM_COMMAND ) :
  2092. {
  2093. switch (LOWORD(wParam))
  2094. {
  2095. case ( IDOK ) :
  2096. case ( IDCANCEL ) :
  2097. {
  2098. if (LOWORD(wParam) == IDCANCEL)
  2099. g_pContext->uCompletionReason = SCCA_REASON_USERCANCEL;
  2100. if (g_hdcDirectoryTextCtrl)
  2101. {
  2102. ReleaseDC(GetDlgItem(hDlg, IDC_UNCOMPRESS_DIR), g_hdcDirectoryTextCtrl);
  2103. g_hdcDirectoryTextCtrl = NULL;
  2104. }
  2105. DestroyWindow(hDlg);
  2106. g_hdlgProgress = NULL;
  2107. break;
  2108. }
  2109. default :
  2110. {
  2111. return (FALSE);
  2112. }
  2113. }
  2114. break;
  2115. }
  2116. default :
  2117. {
  2118. return (FALSE);
  2119. }
  2120. }
  2121. return (TRUE);
  2122. }
  2123. ///////////////////////////////////////////////////////////////////////////////
  2124. //
  2125. // FUNCTION: DisplayCompressProgress
  2126. //
  2127. // DESCRIPTION:
  2128. //
  2129. // Update the progress of compressing files.
  2130. //
  2131. // This routine uses the global variables to update the Dialog box items
  2132. // which display the progress through the compression process. The global
  2133. // variables are updated by individual routines. An ordinal value is sent
  2134. // to this routine which determines which dialog box item to update.
  2135. // Taken from WinFile.
  2136. //
  2137. //
  2138. // ARGUMENTS:
  2139. //
  2140. // iType
  2141. // Control value to determine what is to be updated.
  2142. // Value names are self-descriptive.
  2143. //
  2144. // RETURNS:
  2145. //
  2146. // Nothing.
  2147. //
  2148. ///////////////////////////////////////////////////////////////////////////////
  2149. void DisplayCompressProgress(int iType)
  2150. {
  2151. TCHAR szTemp[120];
  2152. TCHAR szNum[30];
  2153. unsigned _int64 Percentage;
  2154. if (!g_bShowProgress)
  2155. {
  2156. return;
  2157. }
  2158. switch (iType)
  2159. {
  2160. case ( PROGRESS_UPD_FILEANDDIR ) :
  2161. case ( PROGRESS_UPD_FILENAME ) :
  2162. {
  2163. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_FILE, g_szFile);
  2164. if (iType != PROGRESS_UPD_FILEANDDIR)
  2165. {
  2166. break;
  2167. }
  2168. // else...fall thru
  2169. }
  2170. case ( PROGRESS_UPD_DIRECTORY ) :
  2171. {
  2172. RECT rect;
  2173. //
  2174. // Preprocess the directory name to shorten it to fit
  2175. // into the alloted space.
  2176. //
  2177. GetWindowRect(GetDlgItem(g_hdlgProgress, IDC_COMPRESS_DIR), &rect);
  2178. DrawTextEx(g_hdcDirectoryTextCtrl, g_szDirectory, lstrlen(g_szDirectory), &rect, DT_MODIFYSTRING | DT_PATH_ELLIPSIS, NULL);
  2179. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_DIR, g_szDirectory);
  2180. break;
  2181. }
  2182. case ( PROGRESS_UPD_DIRCNT ) :
  2183. {
  2184. AddCommas((DWORD)g_cTotalDirectories, szNum);
  2185. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_DIRCNT, szNum);
  2186. break;
  2187. }
  2188. case ( PROGRESS_UPD_FILENUMBERS ) :
  2189. case ( PROGRESS_UPD_FILECNT ) :
  2190. {
  2191. AddCommas((DWORD)g_cTotalFiles, szNum);
  2192. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_FILECNT, szNum);
  2193. if (iType != PROGRESS_UPD_FILENUMBERS)
  2194. {
  2195. break;
  2196. }
  2197. // else...fall thru
  2198. }
  2199. case ( PROGRESS_UPD_COMPRESSEDSIZE ) :
  2200. {
  2201. Int64ToString(g_iTotalCompressedSize, szTemp, ARRAYSIZE(szTemp), TRUE, &g_NumberFormat, NUMFMT_ALL);
  2202. FormatStringWithArgs(g_szByteCntFmt, szNum, ARRAYSIZE(szNum), szTemp);
  2203. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_CSIZE, szNum);
  2204. if (iType != PROGRESS_UPD_FILENUMBERS)
  2205. {
  2206. break;
  2207. }
  2208. // else...fall thru
  2209. }
  2210. case ( PROGRESS_UPD_FILESIZE ) :
  2211. {
  2212. Int64ToString(g_iTotalFileSize, szTemp, ARRAYSIZE(szTemp), TRUE, &g_NumberFormat, NUMFMT_ALL);
  2213. FormatStringWithArgs(g_szByteCntFmt, szNum, ARRAYSIZE(szNum), szTemp);
  2214. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_USIZE, szNum);
  2215. if (iType != PROGRESS_UPD_FILENUMBERS)
  2216. {
  2217. break;
  2218. }
  2219. // else...fall thru
  2220. }
  2221. case ( PROGRESS_UPD_PERCENTAGE ) :
  2222. {
  2223. if (g_iTotalFileSize != 0)
  2224. {
  2225. //
  2226. // Percentage = 100 - ((CompressSize * 100) / FileSize)
  2227. //
  2228. Percentage = (g_iTotalCompressedSize * 100) / g_iTotalFileSize;
  2229. if (Percentage > 100)
  2230. {
  2231. Percentage = 100;
  2232. }
  2233. else
  2234. Percentage = 100 - Percentage;
  2235. }
  2236. else
  2237. {
  2238. Percentage = 0;
  2239. }
  2240. //
  2241. // Note that percentage string is not formatted.
  2242. // i.e. no commas or decimal places.
  2243. //
  2244. Int64ToString(Percentage, szTemp, ARRAYSIZE(szTemp), FALSE, NULL, 0);
  2245. wsprintf(szNum, TEXT("%s%%"), szTemp);
  2246. SetDlgItemText(g_hdlgProgress, IDC_COMPRESS_RATIO, szNum);
  2247. break;
  2248. }
  2249. }
  2250. CompressProgressYield();
  2251. }
  2252. ///////////////////////////////////////////////////////////////////////////////
  2253. //
  2254. // FUNCTION: CompressProgDlg
  2255. //
  2256. // DESCRIPTION:
  2257. //
  2258. // Display progress information.
  2259. // Taken from WinFile.
  2260. //
  2261. // NOTE: This is a modeless dialog and must be terminated with DestroyWindow
  2262. // and NOT EndDialog
  2263. //
  2264. // ARGUMENTS:
  2265. //
  2266. // Standard dialog proc args.
  2267. //
  2268. // RETURNS:
  2269. //
  2270. // TRUE = Message handled.
  2271. // FALSE = Message not handled.
  2272. //
  2273. ///////////////////////////////////////////////////////////////////////////////
  2274. INT_PTR APIENTRY CompressProgDlg(
  2275. HWND hDlg,
  2276. UINT nMsg,
  2277. WPARAM wParam,
  2278. LPARAM lParam)
  2279. {
  2280. TCHAR szTemp[120];
  2281. RECT rect;
  2282. switch (nMsg)
  2283. {
  2284. case ( WM_INITDIALOG ) :
  2285. {
  2286. CenterWindowInParent(hDlg);
  2287. g_hdlgProgress = hDlg;
  2288. //
  2289. // Clear Dialog items.
  2290. //
  2291. szTemp[0] = TEXT('\0');
  2292. SetDlgItemText(hDlg, IDC_COMPRESS_FILE, szTemp);
  2293. SetDlgItemText(hDlg, IDC_COMPRESS_DIR, szTemp);
  2294. SetDlgItemText(hDlg, IDC_COMPRESS_DIRCNT, szTemp);
  2295. SetDlgItemText(hDlg, IDC_COMPRESS_FILECNT, szTemp);
  2296. SetDlgItemText(hDlg, IDC_COMPRESS_CSIZE, szTemp);
  2297. SetDlgItemText(hDlg, IDC_COMPRESS_USIZE, szTemp);
  2298. SetDlgItemText(hDlg, IDC_COMPRESS_RATIO, szTemp);
  2299. g_hdcDirectoryTextCtrl = GetDC(GetDlgItem(hDlg, IDC_COMPRESS_DIR));
  2300. GetClientRect(GetDlgItem(hDlg, IDC_COMPRESS_DIR), &rect);
  2301. g_cDirectoryTextCtrlWd = rect.right;
  2302. //
  2303. // Set Dialog message text.
  2304. //
  2305. LoadString(g_hmodThisDll, IDS_COMPRESS_DIR, szTemp, ARRAYSIZE(szTemp));
  2306. EnableWindow(hDlg, TRUE);
  2307. break;
  2308. }
  2309. case ( WM_COMMAND ) :
  2310. {
  2311. switch (LOWORD(wParam))
  2312. {
  2313. case ( IDOK ) :
  2314. case ( IDCANCEL ) :
  2315. {
  2316. if (LOWORD(wParam) == IDCANCEL)
  2317. g_pContext->uCompletionReason = SCCA_REASON_USERCANCEL;
  2318. if (g_hdcDirectoryTextCtrl)
  2319. {
  2320. ReleaseDC(GetDlgItem(hDlg, IDC_COMPRESS_DIR), g_hdcDirectoryTextCtrl);
  2321. g_hdcDirectoryTextCtrl = NULL;
  2322. }
  2323. DestroyWindow(hDlg);
  2324. g_hdlgProgress = NULL;
  2325. break;
  2326. }
  2327. default :
  2328. {
  2329. return (FALSE);
  2330. }
  2331. }
  2332. break;
  2333. }
  2334. default :
  2335. {
  2336. return (FALSE);
  2337. }
  2338. }
  2339. return (TRUE);
  2340. }
  2341. ///////////////////////////////////////////////////////////////////////////////
  2342. //
  2343. // FUNCTION: NotifyShellOfAttribChange
  2344. //
  2345. // DESCRIPTION:
  2346. //
  2347. // If the item is visible or is a directory, tell the shell that it's
  2348. // attributes have changed. This will cause the shell to update any
  2349. // compression-related display characteristics.
  2350. //
  2351. // ARGUMENTS:
  2352. //
  2353. // pszPath
  2354. // Fully-qualified path to file/directory that changed.
  2355. //
  2356. // bIsDirectory
  2357. // If TRUE, shell is notified.
  2358. // If FALSE, shell is notified only if global recursion level counter
  2359. // is 1. This ensures that we don't send unnecessary notifications to
  2360. // the shell for items that definitely are not visible in the shell view.
  2361. //
  2362. // RETURNS:
  2363. //
  2364. // Nothing.
  2365. //
  2366. ///////////////////////////////////////////////////////////////////////////////
  2367. void NotifyShellOfAttribChange(LPTSTR pszPath, BOOL bIsDirectory)
  2368. {
  2369. //
  2370. // Only notify shell if item is visible.
  2371. // All items handled at recursion level 1 are visible by default.
  2372. // Subdirectories must always be updated because they may be visible
  2373. // the Explorer tree view.
  2374. //
  2375. if (1 == g_iRecursionLevel || bIsDirectory)
  2376. {
  2377. if (PathIsRoot(pszPath))
  2378. {
  2379. //
  2380. // Invalidate the drive type flags cache for this drive.
  2381. // Cache will be updated with new information on next call to
  2382. // RealDriveTypeFlags( ).
  2383. //
  2384. InvalidateDriveType(PathGetDriveNumber(pszPath));
  2385. }
  2386. else
  2387. {
  2388. PathRemoveTheBackslash(pszPath);
  2389. }
  2390. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pszPath, NULL);
  2391. #ifdef TRACE_COMPRESSION
  2392. DbgOut(TEXT("SHCOMPUI: Shell notified. %s"), pszPath);
  2393. #endif
  2394. }
  2395. }
  2396. ///////////////////////////////////////////////////////////////////////////////
  2397. //
  2398. // FUNCTION: ShellChangeCompressionAttribute
  2399. //
  2400. // DESCRIPTION:
  2401. //
  2402. // Main entry point for file compression/uncompression.
  2403. //
  2404. // ARGUMENTS:
  2405. //
  2406. // hwndParent
  2407. // Handle to window for parenting dialogs.
  2408. //
  2409. // szFileSpec
  2410. // Fully-qualified path of file to be compressed/uncompressed.
  2411. //
  2412. // pContext
  2413. // Address of a context structure as defined in shcompui.h
  2414. // This structure is created by the caller so that we can maintain
  2415. // information about the compression process across a series of
  2416. // calls to this function. In particular, the user can press the
  2417. // "Ignore All Errors" button and we must remember this during
  2418. // subsequent calls. The structure also maintains error count
  2419. // information and completion status. This may be used to supplement
  2420. // the TRUE/FALSE return mechanism to further discriminate a FALSE
  2421. // return value.
  2422. //
  2423. // bCompressing
  2424. // Control variable. TRUE = compress file. FALSE = uncompress file.
  2425. //
  2426. // bShowUI
  2427. // Control of message box and dialog displays.
  2428. // TRUE = Show all dialogs and messages.
  2429. // FALSE = Hide all dialogs and messages. Used when compression is desired
  2430. // without any user interactiion.
  2431. //
  2432. // ***********************************************************
  2433. // NOTE
  2434. //
  2435. // The bShowUI argument has been introduced to support
  2436. // programmatic invocation of shell compression in cases
  2437. // where a UI display is not wanted. The functionality
  2438. // of preventing UI display is not complete at this time.
  2439. // To meet the SUR beta deadline, this parameter is ignored.
  2440. //
  2441. // ***********************************************************
  2442. //
  2443. // RETURNS:
  2444. //
  2445. // TRUE = Operation successful. Continue if iterating
  2446. // through set of files/directories.
  2447. // FALSE = User aborted compression/uncompression or error occurred.
  2448. // Stop if iterating through set of files/directories.
  2449. // Query the context structure for additional completion information.
  2450. //
  2451. ///////////////////////////////////////////////////////////////////////////////
  2452. BOOL ShellChangeCompressionAttribute(
  2453. HWND hwndParent,
  2454. LPTSTR szNameSpec,
  2455. LPSCCA_CONTEXT pContext,
  2456. BOOL bCompressing,
  2457. BOOL bShowUI)
  2458. {
  2459. TCHAR szTitle[MAX_DLGTITLE_LEN+1];
  2460. TCHAR szTemp[MAX_MESSAGE_LEN+1];
  2461. TCHAR szFilespec[_MAX_PATH+1];
  2462. BOOL bCompressionAttrChange;
  2463. BOOL bIsDir = FALSE;
  2464. BOOL bRet = TRUE;
  2465. HCURSOR hCursor = NULL;
  2466. DWORD dwAttribs = 0;
  2467. DWORD dwNewAttribs = 0;
  2468. DWORD dwFlags = 0;
  2469. ASSERT(hwndParent != NULL);
  2470. ASSERT(szNameSpec != NULL);
  2471. //
  2472. // Make sure we're not in the middle of another compression operation.
  2473. // If so, put up an error box warning the user that they need to wait
  2474. // to do another compression operation.
  2475. //
  2476. if (WaitForSingleObject(g_hSemaphore, 0L) == WAIT_TIMEOUT)
  2477. {
  2478. //
  2479. // REARCHITECT: Shouldn't assume the UI is being displayed on behalf of
  2480. // Explorer. The code should be modified so that the app
  2481. // name is passed in through ShellChangeCompressionAtttribute.
  2482. //
  2483. LoadString(g_hmodThisDll, IDS_APP_NAME, szTitle, ARRAYSIZE(szTitle));
  2484. LoadString(g_hmodThisDll, IDS_MULTI_COMPRESS_ERR, szMessage, ARRAYSIZE(szMessage));
  2485. MessageBox(hwndParent, szMessage, szTitle, MB_OK | MB_ICONEXCLAMATION);
  2486. #ifdef TRACE_COMPRESSION
  2487. DbgOut(TEXT("SHCOMPUI: Re-entrancy attempted and denied"));
  2488. #endif
  2489. return (TRUE);
  2490. }
  2491. //
  2492. // Give the context structure "file" scope.
  2493. //
  2494. g_pContext = pContext;
  2495. //
  2496. // Reset recursion level counter.
  2497. //
  2498. g_iRecursionLevel = 0;
  2499. //
  2500. // Make sure the volume supports File Compression.
  2501. //
  2502. lstrcpy(szTemp, szNameSpec);
  2503. PathStripToRoot(szTemp);
  2504. // GetVolumeInformation requires a trailing backslash. Append
  2505. // one if this is a UNC path.
  2506. if (PathIsUNC(szTemp))
  2507. {
  2508. lstrcat(szTemp, c_szBACKSLASH);
  2509. }
  2510. if (!GetVolumeInformation (szTemp, NULL, 0L, NULL, NULL, &dwFlags, NULL, 0L)
  2511. || !(dwFlags & FS_FILE_COMPRESSION))
  2512. {
  2513. //
  2514. // The volume does not support file compression, so just
  2515. // quit out. Do not return FALSE, since that will not
  2516. // allow any other attributes to be changed.
  2517. //
  2518. #ifdef TRACE_COMPRESSION
  2519. DbgOut(TEXT("SHCOMPUI: Volume %s doesn't support compression."), szTemp);
  2520. #endif
  2521. ReleaseSemaphore(g_hSemaphore, 1, NULL);
  2522. return (TRUE);
  2523. }
  2524. //
  2525. // Show the hour glass cursor.
  2526. //
  2527. if (hCursor = LoadCursor(NULL, IDC_WAIT))
  2528. {
  2529. hCursor = SetCursor(hCursor);
  2530. }
  2531. ShowCursor(TRUE);
  2532. //
  2533. // Get the file attributes (current and new).
  2534. // On error, don't change the attribute.
  2535. //
  2536. if ((dwAttribs = GetFileAttributes(szNameSpec)) != (DWORD)-1)
  2537. {
  2538. if (bCompressing)
  2539. dwNewAttribs = dwAttribs | FILE_ATTRIBUTE_COMPRESSED;
  2540. else
  2541. dwNewAttribs = dwAttribs & ~FILE_ATTRIBUTE_COMPRESSED;
  2542. }
  2543. //
  2544. // Determine if ATTR_COMPRESSED is changing state.
  2545. //
  2546. bCompressionAttrChange = ( (dwAttribs & FILE_ATTRIBUTE_COMPRESSED) !=
  2547. (dwNewAttribs & FILE_ATTRIBUTE_COMPRESSED) );
  2548. #ifdef TRACE_COMPRESSION
  2549. {
  2550. TCHAR szAttribStr[40];
  2551. TCHAR szNewAttribStr[40];
  2552. DbgOut(TEXT("SHCOMPUI: File = \"%-30s\" Attr = [%s] New = [%s]"), szNameSpec,
  2553. FileAttribString(dwAttribs, szAttribStr),
  2554. FileAttribString(dwNewAttribs, szNewAttribStr));
  2555. }
  2556. #endif
  2557. g_bShowProgress = FALSE;
  2558. g_bIgnoreAllErrors = g_pContext->bIgnoreAllErrors;
  2559. g_bDiskFull = FALSE;
  2560. g_pContext->cErrors = 0; // Clear "current" error counter.
  2561. //
  2562. // If the Compression attribute changed or if we're dealing with
  2563. // a directory, perform action.
  2564. //
  2565. bIsDir = PathIsDirectory(szNameSpec);
  2566. if (bCompressionAttrChange || bIsDir)
  2567. {
  2568. INT cchLoaded = 0;
  2569. //
  2570. // Reset globals before progress display.
  2571. //
  2572. g_cTotalDirectories = 0;
  2573. g_cTotalFiles = 0;
  2574. g_iTotalFileSize = 0;
  2575. g_iTotalCompressedSize = 0;
  2576. g_szFile[0] = CH_NULL;
  2577. g_szDirectory[0] = CH_NULL;
  2578. cchLoaded = LoadString(g_hmodThisDll, IDS_BYTECNT_FMT, g_szByteCntFmt, ARRAYSIZE(g_szByteCntFmt));
  2579. ASSERT(cchLoaded > 0);
  2580. if (bIsDir)
  2581. {
  2582. BOOL bIgnoreAll = FALSE;
  2583. UINT_PTR uDlgResult = 0;
  2584. CompressionDesc compdesc = { bCompressing, TEXT("") };
  2585. lstrcpy(compdesc.szFileName, szNameSpec);
  2586. uDlgResult = DialogBoxParam(g_hmodThisDll, MAKEINTRESOURCE(DLG_COMPRESS_CONFIRMATION),
  2587. hwndParent, CompressSubsConfirmDlgProc, (LPARAM)&compdesc);
  2588. lstrcpy(szFilespec, c_szSTAR);
  2589. g_bShowProgress = TRUE;
  2590. switch(uDlgResult)
  2591. {
  2592. case COMPRESS_SUBSYES:
  2593. g_bDoSubdirectories = TRUE;
  2594. break;
  2595. case COMPRESS_SUBSNO:
  2596. g_bDoSubdirectories = FALSE;
  2597. break;
  2598. case COMPRESS_CANCELLED:
  2599. bRet = FALSE;
  2600. goto CancelCompress;
  2601. break;
  2602. default:
  2603. ASSERT(0);
  2604. break;
  2605. }
  2606. if (g_bShowProgress)
  2607. {
  2608. g_hdlgProgress = CreateDialog(
  2609. g_hmodThisDll,
  2610. MAKEINTRESOURCE(bCompressing ? DLG_COMPRESS_PROGRESS : DLG_UNCOMPRESS_PROGRESS),
  2611. hwndParent,
  2612. (bCompressing ? CompressProgDlg :
  2613. UncompressProgDlg));
  2614. ShowWindow(g_hdlgProgress, SW_SHOW);
  2615. }
  2616. PathAddBackslash(szNameSpec);
  2617. lstrcpy(szTemp, szNameSpec);
  2618. bRet = bCompressing ? DoCompress(hwndParent, szNameSpec, szFilespec) :
  2619. DoUncompress(hwndParent, szNameSpec, szFilespec);
  2620. //
  2621. // Set attribute on Directory if last call was successful.
  2622. //
  2623. if (bRet)
  2624. {
  2625. szFilespec[0] = TEXT('\0');
  2626. g_bDoSubdirectories = FALSE;
  2627. lstrcpy(szNameSpec, szTemp);
  2628. bRet = bCompressing ? DoCompress(hwndParent, szNameSpec, szFilespec) :
  2629. DoUncompress(hwndParent, szNameSpec, szFilespec);
  2630. }
  2631. //
  2632. // If the progress dialog was being displayed, destroy it.
  2633. //
  2634. if (g_hdlgProgress)
  2635. {
  2636. if (g_hdcDirectoryTextCtrl)
  2637. {
  2638. ReleaseDC( GetDlgItem(g_hdlgProgress,
  2639. (bCompressing ? IDC_COMPRESS_DIR : IDC_UNCOMPRESS_DIR)),
  2640. g_hdcDirectoryTextCtrl);
  2641. g_hdcDirectoryTextCtrl = NULL;
  2642. }
  2643. DestroyWindow(g_hdlgProgress);
  2644. g_hdlgProgress = NULL;
  2645. }
  2646. }
  2647. else
  2648. {
  2649. //
  2650. // Compress single file.
  2651. //
  2652. g_bDoSubdirectories = FALSE;
  2653. lstrcpy(szFilespec, szNameSpec);
  2654. PathStripPath(szFilespec);
  2655. PathRemoveFileSpec(szNameSpec);
  2656. PathAddBackslash(szNameSpec);
  2657. #ifdef TRACE_COMPRESSION
  2658. DbgOut(TEXT("Compress/Uncompress single file %s%s"),szNameSpec,szFilespec);
  2659. #endif
  2660. bRet = bCompressing ? DoCompress(hwndParent, szNameSpec, szFilespec) :
  2661. DoUncompress(hwndParent, szNameSpec, szFilespec);
  2662. }
  2663. }
  2664. CancelCompress:
  2665. //
  2666. // Reset the cursor.
  2667. //
  2668. if (hCursor)
  2669. {
  2670. SetCursor(hCursor);
  2671. }
  2672. ShowCursor(FALSE);
  2673. //
  2674. // Tell the shell that the free space on this volume has changed.
  2675. //
  2676. lstrcpy(szTemp, szNameSpec);
  2677. PathStripToRoot(szTemp);
  2678. if (PathIsUNC(szTemp))
  2679. {
  2680. lstrcat(szTemp, c_szBACKSLASH);
  2681. }
  2682. SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szTemp, NULL);
  2683. //
  2684. // Return the appropriate value.
  2685. //
  2686. g_pContext->bIgnoreAllErrors = g_bIgnoreAllErrors;
  2687. ReleaseSemaphore(g_hSemaphore, 1, NULL);
  2688. return (bRet);
  2689. }
  2690. ///////////////////////////////////////////////////////////////////////////////
  2691. //
  2692. // FUNCTION: CompressFile
  2693. //
  2694. // DESCRIPTION:
  2695. //
  2696. // Compress a single file.
  2697. // Originally take from WinFile.
  2698. //
  2699. // ARGUMENTS:
  2700. //
  2701. // Handle
  2702. // Handle to file to be compressed.
  2703. //
  2704. // FileSpec
  2705. // Full file specification.
  2706. // Required to obtain compressed file size.
  2707. // FindData->cFileName[] doesn't include the path.
  2708. //
  2709. // FindData
  2710. // Pointer to file search context data. Contains file name.
  2711. //
  2712. // RETURNS:
  2713. //
  2714. // TRUE = Success.
  2715. // FALSE = Device IO error during compression.
  2716. //
  2717. ///////////////////////////////////////////////////////////////////////////////
  2718. BOOL CompressFile(
  2719. HANDLE Handle,
  2720. LPTSTR FileSpec,
  2721. PWIN32_FIND_DATA FindData)
  2722. {
  2723. USHORT State;
  2724. ULONG Length;
  2725. LARGE_INTEGER TempLarge;
  2726. //
  2727. // Print out the file name and then do the Ioctl to compress the
  2728. // file. When we are done we'll print the okay message.
  2729. //
  2730. lstrcpy(g_szFile, FindData->cFileName);
  2731. DisplayCompressProgress(PROGRESS_UPD_FILENAME);
  2732. State = 1;
  2733. if (!DeviceIoControl( Handle,
  2734. FSCTL_SET_COMPRESSION,
  2735. &State,
  2736. sizeof(USHORT),
  2737. NULL,
  2738. 0,
  2739. &Length,
  2740. FALSE ))
  2741. {
  2742. g_pContext->cErrors++;
  2743. g_pContext->cCummErrors++;
  2744. #ifdef TRACE_COMPRESSION
  2745. DbgOut(TEXT("SHCOMPUI: CompressFile, DeviceIoControl failed with error 0x%08X"),
  2746. GetLastError());
  2747. #endif
  2748. return (FALSE);
  2749. }
  2750. //
  2751. // Gather statistics (File size, compressed size and count).
  2752. //
  2753. TempLarge.LowPart = FindData->nFileSizeLow;
  2754. TempLarge.HighPart = FindData->nFileSizeHigh;
  2755. g_iTotalFileSize += TempLarge.QuadPart;
  2756. TempLarge.LowPart = GetCompressedFileSize(FileSpec, &(TempLarge.HighPart));
  2757. g_iTotalCompressedSize += TempLarge.QuadPart;
  2758. g_cTotalFiles++;
  2759. DisplayCompressProgress(PROGRESS_UPD_FILENUMBERS);
  2760. return (TRUE);
  2761. }
  2762. ///////////////////////////////////////////////////////////////////////////////
  2763. //
  2764. // FUNCTION: DoCompress
  2765. //
  2766. // DESCRIPTION:
  2767. //
  2768. // Compress a directory and its subdirectories if necessary.
  2769. // Originally take from WinFile.
  2770. //
  2771. // ARGUMENTS:
  2772. //
  2773. // hwndParent
  2774. // Window handle for parenting dialogs and message boxes.
  2775. //
  2776. // DirectorySpec
  2777. // Fully-qualified directory specification with backslash appended.
  2778. //
  2779. // FileSpec
  2780. // File name with directory path removed.
  2781. //
  2782. // RETURNS:
  2783. //
  2784. // TRUE = Success.
  2785. // FALSE = Device IO error or user aborted compression.
  2786. //
  2787. ///////////////////////////////////////////////////////////////////////////////
  2788. BOOL DoCompress(
  2789. HWND hwndParent,
  2790. LPTSTR DirectorySpec,
  2791. LPTSTR FileSpec)
  2792. {
  2793. LPTSTR DirectorySpecEnd;
  2794. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  2795. USHORT State;
  2796. ULONG Length;
  2797. HANDLE FindHandle;
  2798. WIN32_FIND_DATA FindData;
  2799. int MBRet;
  2800. TCHAR szTitle[128];
  2801. g_iRecursionLevel++;
  2802. #ifdef TRACE_COMPRESSION
  2803. DbgOut(TEXT("SHCOMPUI: DoCompress with %s%s"), DirectorySpec, FileSpec);
  2804. DbgOut(TEXT(" Recursion Level: %d -> %d"), g_iRecursionLevel-1, g_iRecursionLevel);
  2805. #endif
  2806. //
  2807. // If the file spec is null, then set the compression bit for
  2808. // the directory spec and get out.
  2809. //
  2810. lstrcpy(g_szDirectory, DirectorySpec);
  2811. g_szFile[0] = CH_NULL;
  2812. DisplayCompressProgress(PROGRESS_UPD_FILEANDDIR);
  2813. if (lstrlen(FileSpec) == 0)
  2814. {
  2815. DoCompressRetryCreate:
  2816. if (!OpenFileForCompress(&FileHandle, DirectorySpec))
  2817. {
  2818. #ifdef TRACE_COMPRESSION
  2819. DbgOut(TEXT("SHCOMPUI: DoCompress, OpenFileForCompress failed."));
  2820. #endif
  2821. goto DoCompressError;
  2822. }
  2823. DoCompressRetryDevIo:
  2824. State = 1;
  2825. if (!DeviceIoControl( FileHandle,
  2826. FSCTL_SET_COMPRESSION,
  2827. &State,
  2828. sizeof(USHORT),
  2829. NULL,
  2830. 0,
  2831. &Length,
  2832. FALSE ))
  2833. {
  2834. g_pContext->cErrors++;
  2835. g_pContext->cCummErrors++;
  2836. DoCompressError:
  2837. #ifdef TRACE_COMPRESSION
  2838. DbgOut(TEXT("SHCOMPUI: DoCompress, DeviceIoControl failed with error 0x%08X"),
  2839. GetLastError());
  2840. #endif
  2841. if (!g_bIgnoreAllErrors)
  2842. {
  2843. MBRet = CompressErrMessageBox( hwndParent,
  2844. DirectorySpec,
  2845. &FileHandle );
  2846. if (MBRet == RETRY_CREATE)
  2847. {
  2848. goto DoCompressRetryCreate;
  2849. }
  2850. else if (MBRet == RETRY_DEVIO)
  2851. {
  2852. goto DoCompressRetryDevIo;
  2853. }
  2854. else if (MBRet == IDABORT)
  2855. {
  2856. //
  2857. // Return error.
  2858. // File handle was closed by CompressErrMessageBox( ).
  2859. //
  2860. g_pContext->uCompletionReason = SCCA_REASON_USERABORT;
  2861. g_iRecursionLevel--;
  2862. return (FALSE);
  2863. }
  2864. //
  2865. // Else (MBRet == IDIGNORE)
  2866. // Continue on as if the error did not occur.
  2867. //
  2868. }
  2869. }
  2870. if (INVALID_HANDLE_VALUE != FileHandle)
  2871. {
  2872. CloseHandle(FileHandle);
  2873. FileHandle = INVALID_HANDLE_VALUE;
  2874. }
  2875. g_cTotalDirectories++;
  2876. g_cTotalFiles++;
  2877. DisplayCompressProgress(PROGRESS_UPD_DIRCNT);
  2878. DisplayCompressProgress(PROGRESS_UPD_FILECNT);
  2879. NotifyShellOfAttribChange(DirectorySpec, TRUE);
  2880. g_iRecursionLevel--;
  2881. return (TRUE);
  2882. }
  2883. //
  2884. // Get a pointer to the end of the directory spec, so that we can
  2885. // keep appending names to the end of it.
  2886. //
  2887. DirectorySpecEnd = DirectorySpec + lstrlen(DirectorySpec);
  2888. //
  2889. // List the directory that is being compressed and display
  2890. // its current compress attribute.
  2891. //
  2892. g_cTotalDirectories++;
  2893. DisplayCompressProgress(PROGRESS_UPD_DIRCNT);
  2894. //
  2895. // For every file in the directory that matches the file spec,
  2896. // open the file and compress it.
  2897. //
  2898. // Setup the template for findfirst/findnext.
  2899. //
  2900. lstrcpy(DirectorySpecEnd, FileSpec);
  2901. if ((FindHandle = FindFirstFile(DirectorySpec, &FindData)) != INVALID_HANDLE_VALUE)
  2902. {
  2903. do
  2904. {
  2905. DWORD dwAttrib = FindData.dwFileAttributes;
  2906. //
  2907. // Make sure the user hasn't hit cancel.
  2908. //
  2909. if (g_bShowProgress && !g_hdlgProgress)
  2910. {
  2911. g_iRecursionLevel--;
  2912. FindClose(FindHandle);
  2913. return FALSE;
  2914. }
  2915. //
  2916. // Skip over the . and .. entries.
  2917. //
  2918. if ( !lstrcmp(FindData.cFileName, c_szDOT) ||
  2919. !lstrcmp(FindData.cFileName, c_szDOTDOT) )
  2920. {
  2921. continue;
  2922. }
  2923. else if ((DirectorySpecEnd == (DirectorySpec + 3)) &&
  2924. !lstrcmpi(FindData.cFileName, c_szNTLDR))
  2925. {
  2926. //
  2927. // Do not allow \NTLDR to be compressed.
  2928. // Put up OK message box and then continue.
  2929. //
  2930. lstrcpy(DirectorySpecEnd, FindData.cFileName);
  2931. LoadString(g_hmodThisDll, IDS_NTLDR_COMPRESS_ERR, szTitle, ARRAYSIZE(szTitle));
  2932. wsprintf(szMessage, szTitle, DirectorySpec);
  2933. LoadString(g_hmodThisDll, IDS_APP_NAME, szTitle, ARRAYSIZE(szTitle));
  2934. MessageBox(g_hdlgProgress ? g_hdlgProgress : hwndParent, szMessage,
  2935. szTitle, MB_OK | MB_ICONEXCLAMATION);
  2936. continue;
  2937. }
  2938. else
  2939. {
  2940. //
  2941. // Append the found file to the directory spec and
  2942. // open the file.
  2943. //
  2944. lstrcpy(DirectorySpecEnd, FindData.cFileName);
  2945. if ((dwAttrib & FILE_ATTRIBUTE_COMPRESSED) ||
  2946. (!g_bDoSubdirectories && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)))
  2947. {
  2948. //
  2949. // File is a directory or is already compressed.
  2950. // So just skip it.
  2951. //
  2952. continue;
  2953. }
  2954. CompressFileRetryCreate:
  2955. if (!OpenFileForCompress(&FileHandle, DirectorySpec))
  2956. {
  2957. #ifdef TRACE_COMPRESSION
  2958. DbgOut(TEXT("SHCOMPUI: DoCompress, OpenFileForCompress failed."),
  2959. GetLastError());
  2960. #endif
  2961. goto CompressFileError;
  2962. }
  2963. CompressFileRetryDevIo:
  2964. //
  2965. // Compress the file.
  2966. //
  2967. if (!CompressFile(FileHandle, DirectorySpec, &FindData))
  2968. {
  2969. CompressFileError:
  2970. if (!g_bIgnoreAllErrors)
  2971. {
  2972. MBRet = CompressErrMessageBox( hwndParent,
  2973. DirectorySpec,
  2974. &FileHandle );
  2975. if (MBRet == RETRY_CREATE)
  2976. {
  2977. goto CompressFileRetryCreate;
  2978. }
  2979. else if (MBRet == RETRY_DEVIO)
  2980. {
  2981. goto CompressFileRetryDevIo;
  2982. }
  2983. else if (MBRet == IDABORT)
  2984. {
  2985. //
  2986. // Return error.
  2987. // File handle was closed by CompressErrMessageBox( ).
  2988. //
  2989. g_pContext->uCompletionReason = SCCA_REASON_USERABORT;
  2990. g_iRecursionLevel--;
  2991. FindClose(FindHandle);
  2992. return (FALSE);
  2993. }
  2994. //
  2995. // Else (MBRet == IDIGNORE)
  2996. // Continue on as if the error did not occur.
  2997. //
  2998. }
  2999. }
  3000. if (INVALID_HANDLE_VALUE != FileHandle)
  3001. {
  3002. CloseHandle(FileHandle);
  3003. FileHandle = INVALID_HANDLE_VALUE;
  3004. }
  3005. }
  3006. NotifyShellOfAttribChange(DirectorySpec,
  3007. (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
  3008. CompressProgressYield();
  3009. } while (FindNextFile(FindHandle, &FindData));
  3010. FindClose(FindHandle);
  3011. }
  3012. //
  3013. // If we are to do subdirectores, then look for every subdirectory
  3014. // and recursively call ourselves to list the subdirectory.
  3015. //
  3016. if (g_bDoSubdirectories)
  3017. {
  3018. //
  3019. // Setup findfirst/findnext to search the entire directory.
  3020. //
  3021. lstrcpy(DirectorySpecEnd, c_szSTAR);
  3022. if ((FindHandle = FindFirstFile(DirectorySpec, &FindData)) != INVALID_HANDLE_VALUE)
  3023. {
  3024. do
  3025. {
  3026. //
  3027. // Skip over the . and .. entries, otherwise recurse.
  3028. //
  3029. if ( !lstrcmp(FindData.cFileName, c_szDOT) ||
  3030. !lstrcmp(FindData.cFileName, c_szDOTDOT) )
  3031. {
  3032. continue;
  3033. }
  3034. else
  3035. {
  3036. //
  3037. // If the entry is for a directory, then tack
  3038. // on the subdirectory name to the directory spec
  3039. // and recurse.
  3040. //
  3041. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  3042. {
  3043. lstrcpy(DirectorySpecEnd, FindData.cFileName);
  3044. lstrcat(DirectorySpecEnd, c_szBACKSLASH);
  3045. if (!DoCompress(hwndParent, DirectorySpec, FileSpec))
  3046. {
  3047. g_iRecursionLevel--;
  3048. FindClose(FindHandle);
  3049. return (FALSE || g_bIgnoreAllErrors);
  3050. }
  3051. }
  3052. }
  3053. } while (FindNextFile(FindHandle, &FindData));
  3054. FindClose(FindHandle);
  3055. }
  3056. }
  3057. g_iRecursionLevel--;
  3058. return (TRUE);
  3059. }
  3060. ///////////////////////////////////////////////////////////////////////////////
  3061. //
  3062. // FUNCTION: UncompressFile
  3063. //
  3064. // DESCRIPTION:
  3065. //
  3066. // Uncompress a single file.
  3067. // Originally take from WinFile.
  3068. //
  3069. // ARGUMENTS:
  3070. //
  3071. // hwndParent
  3072. // Handle to window for parenting any dialogs.
  3073. //
  3074. // hFile
  3075. // Handle to file to be compressed.
  3076. //
  3077. // FindData
  3078. // Pointer to file search context data. Contains file name.
  3079. //
  3080. // RETURNS:
  3081. //
  3082. // TRUE = Success.
  3083. // FALSE = Device IO error during compression.
  3084. //
  3085. ///////////////////////////////////////////////////////////////////////////////
  3086. BOOL UncompressFile(HWND hwndParent, HANDLE hFile, PWIN32_FIND_DATA FindData)
  3087. {
  3088. USHORT State;
  3089. ULONG Length;
  3090. //
  3091. // Print out the file name and then do the Ioctl to uncompress the
  3092. // file. When we are done we'll print the okay message.
  3093. //
  3094. lstrcpy(g_szFile, FindData->cFileName);
  3095. DisplayUncompressProgress(PROGRESS_UPD_FILENAME);
  3096. State = 0;
  3097. if (!DeviceIoControl( hFile,
  3098. FSCTL_SET_COMPRESSION,
  3099. &State,
  3100. sizeof(USHORT),
  3101. NULL,
  3102. 0,
  3103. &Length,
  3104. FALSE )
  3105. #ifdef SIM_DISK_FULL
  3106. || TRUE
  3107. #endif
  3108. )
  3109. {
  3110. g_pContext->cErrors++;
  3111. g_pContext->cCummErrors++;
  3112. #ifdef TRACE_COMPRESSION
  3113. DbgOut(TEXT("SHCOMPUI: UncompressFile, DeviceIoControl failed with error 0x%08X"),
  3114. GetLastError());
  3115. #endif
  3116. #ifdef SIM_DISK_FULL
  3117. SetLastError((DWORD)STATUS_DISK_FULL);
  3118. #endif
  3119. if (GetLastError() == STATUS_DISK_FULL)
  3120. {
  3121. UncompressDiskFullError(hwndParent, hFile);
  3122. g_bDiskFull = TRUE;
  3123. }
  3124. return (FALSE);
  3125. }
  3126. //
  3127. // Increment the running total.
  3128. //
  3129. g_cTotalFiles++;
  3130. DisplayUncompressProgress(PROGRESS_UPD_FILENUMBERS);
  3131. return (TRUE);
  3132. }
  3133. ///////////////////////////////////////////////////////////////////////////////
  3134. //
  3135. // FUNCTION: DoUncompress
  3136. //
  3137. // DESCRIPTION:
  3138. //
  3139. // Uncompress a directory and its subdirectories if necessary.
  3140. // Originally take from WinFile.
  3141. //
  3142. // ARGUMENTS:
  3143. //
  3144. // hwndParent
  3145. // Window handle for parenting dialogs and message boxes.
  3146. //
  3147. // DirectorySpec
  3148. // Fully-qualified directory specification with backslash appended.
  3149. //
  3150. // FileSpec
  3151. // File name with directory path removed.
  3152. //
  3153. // RETURNS:
  3154. //
  3155. // TRUE = Success.
  3156. // FALSE = Device IO error or user aborted uncompression.
  3157. //
  3158. ///////////////////////////////////////////////////////////////////////////////
  3159. BOOL DoUncompress(
  3160. HWND hwndParent,
  3161. LPTSTR DirectorySpec,
  3162. LPTSTR FileSpec)
  3163. {
  3164. LPTSTR DirectorySpecEnd;
  3165. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  3166. USHORT State;
  3167. ULONG Length;
  3168. HANDLE FindHandle;
  3169. WIN32_FIND_DATA FindData;
  3170. int MBRet;
  3171. g_iRecursionLevel++;
  3172. #ifdef TRACE_COMPRESSION
  3173. DbgOut(TEXT("SHCOMPUI: DoUncompress with %s%s"), DirectorySpec, FileSpec);
  3174. DbgOut(TEXT(" Recursion Level: %d -> %d"), g_iRecursionLevel-1, g_iRecursionLevel);
  3175. #endif
  3176. //
  3177. // If the file spec is null, then clear the compression bit for
  3178. // the directory spec and get out.
  3179. //
  3180. lstrcpy(g_szDirectory, DirectorySpec);
  3181. g_szFile[0] = CH_NULL;
  3182. DisplayUncompressProgress(PROGRESS_UPD_FILEANDDIR);
  3183. if (lstrlen(FileSpec) == 0)
  3184. {
  3185. DoUncompressRetryCreate:
  3186. if (!OpenFileForCompress(&FileHandle, DirectorySpec))
  3187. {
  3188. #ifdef TRACE_COMPRESSION
  3189. DbgOut(TEXT("SHCOMPUI: UncompressDirecory, OpenFileForCompress failed."));
  3190. #endif
  3191. goto DoUncompressError;
  3192. }
  3193. DoUncompressRetryDevIo:
  3194. State = 0;
  3195. if (!DeviceIoControl( FileHandle,
  3196. FSCTL_SET_COMPRESSION,
  3197. &State,
  3198. sizeof(USHORT),
  3199. NULL,
  3200. 0,
  3201. &Length,
  3202. FALSE )
  3203. #ifdef SIM_DISK_FULL
  3204. || TRUE
  3205. #endif
  3206. )
  3207. {
  3208. g_pContext->cErrors++;
  3209. g_pContext->cCummErrors++;
  3210. DoUncompressError:
  3211. //
  3212. // Handle disk-full error.
  3213. //
  3214. #ifdef TRACE_COMPRESSION
  3215. DbgOut(TEXT("SHCOMPUI: DoUncompress, DeviceIoControl failed with error 0x%08X"),
  3216. GetLastError());
  3217. #endif
  3218. #ifdef SIM_DISK_FULL
  3219. SetLastError((DWORD)STATUS_DISK_FULL);
  3220. #endif
  3221. if (GetLastError() == STATUS_DISK_FULL)
  3222. {
  3223. UncompressDiskFullError(hwndParent, FileHandle);
  3224. g_bDiskFull = TRUE;
  3225. CloseHandle(FileHandle);
  3226. g_iRecursionLevel--;
  3227. return FALSE;
  3228. }
  3229. if (!g_bIgnoreAllErrors)
  3230. {
  3231. MBRet = CompressErrMessageBox( hwndParent,
  3232. DirectorySpec,
  3233. &FileHandle );
  3234. if (MBRet == RETRY_CREATE)
  3235. {
  3236. goto DoUncompressRetryCreate;
  3237. }
  3238. else if (MBRet == RETRY_DEVIO)
  3239. {
  3240. goto DoUncompressRetryDevIo;
  3241. }
  3242. else if (MBRet == IDABORT)
  3243. {
  3244. //
  3245. // Return error.
  3246. // File handle was closed by CompressErrMessageBox.
  3247. //
  3248. g_pContext->uCompletionReason = SCCA_REASON_USERABORT;
  3249. g_iRecursionLevel--;
  3250. return (FALSE);
  3251. }
  3252. //
  3253. // Else (MBRet == IDIGNORE)
  3254. // Continue on as if the error did not occur.
  3255. //
  3256. }
  3257. }
  3258. if (INVALID_HANDLE_VALUE != FileHandle)
  3259. {
  3260. CloseHandle(FileHandle);
  3261. FileHandle = INVALID_HANDLE_VALUE;
  3262. }
  3263. g_cTotalDirectories++;
  3264. g_cTotalFiles++;
  3265. DisplayUncompressProgress(PROGRESS_UPD_DIRCNT);
  3266. DisplayUncompressProgress(PROGRESS_UPD_FILECNT);
  3267. NotifyShellOfAttribChange(DirectorySpec, TRUE);
  3268. g_iRecursionLevel--;
  3269. return (TRUE);
  3270. }
  3271. //
  3272. // Get a pointer to the end of the directory spec, so that we can
  3273. // keep appending names to the end of it.
  3274. //
  3275. DirectorySpecEnd = DirectorySpec + lstrlen(DirectorySpec);
  3276. g_cTotalDirectories++;
  3277. DisplayUncompressProgress(PROGRESS_UPD_DIRCNT);
  3278. //
  3279. // For every file in the directory that matches the file spec,
  3280. // open the file and uncompress it.
  3281. //
  3282. // Setup the template for findfirst/findnext.
  3283. //
  3284. lstrcpy(DirectorySpecEnd, FileSpec);
  3285. if ((FindHandle = FindFirstFile(DirectorySpec, &FindData)) != INVALID_HANDLE_VALUE)
  3286. {
  3287. do
  3288. {
  3289. DWORD dwAttrib = FindData.dwFileAttributes;
  3290. //
  3291. // Make sure the user hasn't hit cancel.
  3292. //
  3293. if (g_bShowProgress && !g_hdlgProgress)
  3294. {
  3295. g_iRecursionLevel--;
  3296. FindClose(FindHandle);
  3297. return FALSE;
  3298. }
  3299. //
  3300. // Skip over the . and .. entries.
  3301. //
  3302. if ( !lstrcmp(FindData.cFileName, c_szDOT) ||
  3303. !lstrcmp(FindData.cFileName, c_szDOTDOT) )
  3304. {
  3305. continue;
  3306. }
  3307. else
  3308. {
  3309. //
  3310. // Append the found file to the directory spec and
  3311. // open the file.
  3312. //
  3313. lstrcpy(DirectorySpecEnd, FindData.cFileName);
  3314. if (!(dwAttrib & FILE_ATTRIBUTE_COMPRESSED) ||
  3315. (!g_bDoSubdirectories && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)))
  3316. {
  3317. //
  3318. // File is a directory or already uncompressed.
  3319. // So skip it.
  3320. //
  3321. continue;
  3322. }
  3323. UncompressFileRetryCreate:
  3324. if (!OpenFileForCompress(&FileHandle, DirectorySpec))
  3325. {
  3326. #ifdef TRACE_COMPRESSION
  3327. DbgOut(TEXT("SHCOMPUI: OpenFileForCompress failed."));
  3328. #endif
  3329. goto UncompressFileError;
  3330. }
  3331. UncompressFileRetryDevIo:
  3332. //
  3333. // Uncompress the file.
  3334. //
  3335. if (!UncompressFile(hwndParent, FileHandle, &FindData))
  3336. {
  3337. UncompressFileError:
  3338. //
  3339. // If disk is full, UncompressFile( ) already handled the error.
  3340. // Don't handle it again. Just return.
  3341. //
  3342. if (g_bDiskFull)
  3343. {
  3344. g_pContext->uCompletionReason = SCCA_REASON_DISKFULL;
  3345. CloseHandle(FileHandle);
  3346. FindClose(FindHandle);
  3347. g_iRecursionLevel--;
  3348. return FALSE;
  3349. }
  3350. if (!g_bIgnoreAllErrors)
  3351. {
  3352. MBRet = CompressErrMessageBox( hwndParent,
  3353. DirectorySpec,
  3354. &FileHandle );
  3355. if (MBRet == RETRY_CREATE)
  3356. {
  3357. goto UncompressFileRetryCreate;
  3358. }
  3359. else if (MBRet == RETRY_DEVIO)
  3360. {
  3361. goto UncompressFileRetryDevIo;
  3362. }
  3363. else if (MBRet == IDABORT)
  3364. {
  3365. //
  3366. // Return error.
  3367. // File handle was closed by CompressErrMessageBox.
  3368. //
  3369. g_pContext->uCompletionReason = SCCA_REASON_USERABORT;
  3370. g_iRecursionLevel--;
  3371. FindClose(FindHandle);
  3372. return (FALSE);
  3373. }
  3374. //
  3375. // Else (MBRet == IDIGNORE)
  3376. // Continue on as if the error did not occur.
  3377. //
  3378. }
  3379. }
  3380. if (INVALID_HANDLE_VALUE != FileHandle)
  3381. {
  3382. CloseHandle(FileHandle);
  3383. FileHandle = INVALID_HANDLE_VALUE;
  3384. }
  3385. }
  3386. NotifyShellOfAttribChange(DirectorySpec,
  3387. (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
  3388. CompressProgressYield();
  3389. } while (FindNextFile(FindHandle, &FindData));
  3390. FindClose(FindHandle);
  3391. }
  3392. //
  3393. // If we are to do subdirectores, then look for every subdirectory
  3394. // and recursively call ourselves to list the subdirectory.
  3395. //
  3396. if (g_bDoSubdirectories)
  3397. {
  3398. //
  3399. // Setup findfirst/findnext to search the entire directory.
  3400. //
  3401. lstrcpy(DirectorySpecEnd, c_szSTAR);
  3402. if ((FindHandle = FindFirstFile(DirectorySpec, &FindData)) != INVALID_HANDLE_VALUE)
  3403. {
  3404. do
  3405. {
  3406. //
  3407. // Skip over the . and .. entries, otherwise recurse.
  3408. //
  3409. if ( !lstrcmp(FindData.cFileName, c_szDOT) ||
  3410. !lstrcmp(FindData.cFileName, c_szDOTDOT) )
  3411. {
  3412. continue;
  3413. }
  3414. else
  3415. {
  3416. //
  3417. // If the entry is for a directory, then tack
  3418. // on the subdirectory name to the directory spec
  3419. // and recurse.
  3420. //
  3421. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  3422. {
  3423. lstrcpy(DirectorySpecEnd, FindData.cFileName);
  3424. lstrcat(DirectorySpecEnd, c_szBACKSLASH);
  3425. if (!DoUncompress(hwndParent, DirectorySpec, FileSpec))
  3426. {
  3427. g_iRecursionLevel--;
  3428. FindClose(FindHandle);
  3429. return (FALSE || g_bIgnoreAllErrors);
  3430. }
  3431. }
  3432. }
  3433. } while (FindNextFile(FindHandle, &FindData));
  3434. FindClose(FindHandle);
  3435. }
  3436. }
  3437. g_iRecursionLevel--;
  3438. return (TRUE);
  3439. }
  3440. ///////////////////////////////////////////////////////////////////////////////
  3441. //
  3442. // FUNCTION: CompressErrMessageBox
  3443. //
  3444. // DESCRIPTION:
  3445. //
  3446. // Puts up the error message box when a file cannot be compressed or
  3447. // uncompressed. It also returns the user preference.
  3448. //
  3449. // NOTE: The file handle is closed if the abort or ignore option is
  3450. // chosen by the user.
  3451. //
  3452. // ARGUMENTS:
  3453. //
  3454. //
  3455. // RETURNS:
  3456. //
  3457. // RETRY_CREATE = User selected "Retry"
  3458. // RETRY_DEVIO = User selected "Retry"
  3459. // IDABORT
  3460. // IDIGNORE
  3461. // IDC_COMPRESS_IGNOREALL
  3462. //
  3463. ///////////////////////////////////////////////////////////////////////////////
  3464. int CompressErrMessageBox(
  3465. HWND hwndActive,
  3466. LPTSTR szFile,
  3467. PHANDLE phFile)
  3468. {
  3469. int rc;
  3470. //
  3471. // Put up the error message box - ABORT, RETRY, IGNORE, IGNORE ALL.
  3472. //
  3473. rc = (int)DialogBoxParam( g_hmodThisDll,
  3474. (LPTSTR) MAKEINTRESOURCE(DLG_COMPRESS_ERROR),
  3475. g_hdlgProgress ? g_hdlgProgress : hwndActive,
  3476. CompressErrDialogProc,
  3477. (LPARAM)szFile );
  3478. //
  3479. // Return the user preference.
  3480. //
  3481. if (rc == IDRETRY)
  3482. {
  3483. if (*phFile == INVALID_HANDLE_VALUE)
  3484. {
  3485. return (RETRY_CREATE);
  3486. }
  3487. else
  3488. {
  3489. return (RETRY_DEVIO);
  3490. }
  3491. }
  3492. else
  3493. {
  3494. //
  3495. // IDABORT or IDIGNORE or IDC_COMPRESS_IGNOREALL
  3496. //
  3497. // Close the file handle and return the message box result.
  3498. //
  3499. if (*phFile != INVALID_HANDLE_VALUE)
  3500. {
  3501. CloseHandle(*phFile);
  3502. *phFile = INVALID_HANDLE_VALUE;
  3503. }
  3504. return (rc);
  3505. }
  3506. }
  3507. ///////////////////////////////////////////////////////////////////////////////
  3508. //
  3509. // FUNCTION: CompressErrDialogProc
  3510. //
  3511. // DESCRIPTION:
  3512. //
  3513. // Puts up a dialog to allow the user to Abort, Retry, Ignore, or
  3514. // Ignore All when an error occurs during compression.
  3515. //
  3516. //
  3517. // Taken from WinFile source wffile.c. Modified control resource IDs to
  3518. // be consistent with other Explorer ID naming conventions.
  3519. //
  3520. // ARGUMENTS:
  3521. //
  3522. // Standard Dialog Proc args.
  3523. //
  3524. // RETURNS:
  3525. //
  3526. // Standard Dialog Proc return values..
  3527. //
  3528. // Returns through EndDialog( ):
  3529. //
  3530. // IDABORT
  3531. // IDRETRY
  3532. // IDIGNORE
  3533. // IDC_COMPRESS_IGNOREALL
  3534. //
  3535. ///////////////////////////////////////////////////////////////////////////////
  3536. INT_PTR CALLBACK CompressErrDialogProc(
  3537. HWND hDlg,
  3538. UINT uMsg,
  3539. WPARAM wParam,
  3540. LPARAM lParam)
  3541. {
  3542. WORD IdControl = TRUE;
  3543. TCHAR szTitle[MAX_DLGTITLE_LEN + 1];
  3544. switch (uMsg)
  3545. {
  3546. case ( WM_INITDIALOG ) :
  3547. {
  3548. //
  3549. // Modify very long path names so that they fit into the message box.
  3550. // They are formatted as "c:\dir1\dir2\dir3\...\dir8\filename.ext"
  3551. // DrawTextEx isn't drawing on anything. Only it's formatting capabilities are
  3552. // being used. The DT_CALCRECT flag prevents drawing.
  3553. //
  3554. HDC hDC = GetDC(hDlg);
  3555. LONG iBaseUnits = GetDialogBaseUnits();
  3556. RECT rc;
  3557. TCHAR szFilePath[MAX_PATH]; // Local copy of path name string.
  3558. const int MAX_PATH_DISPLAY_WD = 50; // Max characters to display in path name.
  3559. const int MAX_PATH_DISPLAY_HT = 1; // Path name is 1 character high.
  3560. rc.left = 0;
  3561. rc.top = 0;
  3562. rc.right = MAX_PATH_DISPLAY_WD * LOWORD(iBaseUnits);
  3563. rc.bottom = MAX_PATH_DISPLAY_HT * HIWORD(iBaseUnits);
  3564. lstrcpyn(szFilePath, (LPCTSTR)lParam, ARRAYSIZE(szFilePath));
  3565. DrawTextEx(hDC, szFilePath, ARRAYSIZE(szFilePath), &rc,
  3566. DT_CALCRECT | DT_PATH_ELLIPSIS | DT_MODIFYSTRING, NULL);
  3567. ReleaseDC(hDlg, hDC);
  3568. //
  3569. // Set the dialog message text.
  3570. //
  3571. LoadString( g_hmodThisDll,
  3572. IDS_COMPRESS_ATTRIB_ERR,
  3573. szTitle,
  3574. ARRAYSIZE(szTitle) );
  3575. wsprintf(szMessage, szTitle, szFilePath);
  3576. SetDlgItemText(hDlg, IDC_COMPRESS_ERRTEXT, szMessage);
  3577. EnableWindow (hDlg, TRUE);
  3578. break;
  3579. }
  3580. case ( WM_COMMAND ) :
  3581. {
  3582. IdControl = GET_WM_COMMAND_ID(wParam, lParam);
  3583. switch (IdControl)
  3584. {
  3585. case ( IDC_COMPRESS_IGNOREALL ) :
  3586. {
  3587. g_bIgnoreAllErrors = TRUE;
  3588. // fall thru...
  3589. }
  3590. case ( IDABORT ) :
  3591. case ( IDRETRY ) :
  3592. case ( IDIGNORE ) :
  3593. {
  3594. EndDialog(hDlg, IdControl);
  3595. break;
  3596. }
  3597. default :
  3598. {
  3599. return (FALSE);
  3600. }
  3601. }
  3602. break;
  3603. }
  3604. default :
  3605. {
  3606. return (FALSE);
  3607. }
  3608. }
  3609. return (IdControl);
  3610. }
  3611. ///////////////////////////////////////////////////////////////////////////////
  3612. //
  3613. // FUNCTION: OpenFileForCompress
  3614. //
  3615. // DESCRIPTION:
  3616. //
  3617. // Opens the file for compression. It handles the case where a READONLY
  3618. // file is trying to be compressed or uncompressed. Since read only files
  3619. // cannot be opened for WRITE_DATA, it temporarily resets the file to NOT
  3620. // be READONLY in order to open the file, and then sets it back once the
  3621. // file has been compressed.
  3622. //
  3623. // Taken from WinFile module wffile.c without change. Originally from
  3624. // G. Kimura's compact.c.
  3625. //
  3626. // ARGUMENTS:
  3627. //
  3628. // phFile
  3629. // Address of file handle variable for handle of open file if
  3630. // successful.
  3631. //
  3632. // szFile
  3633. // Name string of file to be opened.
  3634. //
  3635. // RETURNS:
  3636. //
  3637. // TRUE = File successfully opened. Handle in *phFile.
  3638. // FALSE = File couldn't be opened. *phFile == INVALID_HANDLE_VALUE
  3639. //
  3640. ///////////////////////////////////////////////////////////////////////////////
  3641. BOOL OpenFileForCompress(
  3642. PHANDLE phFile,
  3643. LPTSTR szFile)
  3644. {
  3645. HANDLE hAttr;
  3646. BY_HANDLE_FILE_INFORMATION fi;
  3647. //
  3648. // Try to open the file - READ_DATA | WRITE_DATA.
  3649. //
  3650. if ((*phFile = CreateFile( szFile,
  3651. FILE_READ_DATA | FILE_WRITE_DATA,
  3652. FILE_SHARE_READ | FILE_SHARE_WRITE,
  3653. NULL,
  3654. OPEN_EXISTING,
  3655. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  3656. NULL )) != INVALID_HANDLE_VALUE)
  3657. {
  3658. //
  3659. // Successfully opened the file.
  3660. //
  3661. return (TRUE);
  3662. }
  3663. if (GetLastError() != ERROR_ACCESS_DENIED)
  3664. {
  3665. return (FALSE);
  3666. }
  3667. //
  3668. // Try to open the file - READ_ATTRIBUTES | WRITE_ATTRIBUTES.
  3669. //
  3670. if ((hAttr = CreateFile( szFile,
  3671. FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
  3672. FILE_SHARE_READ | FILE_SHARE_WRITE,
  3673. NULL,
  3674. OPEN_EXISTING,
  3675. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  3676. NULL )) == INVALID_HANDLE_VALUE)
  3677. {
  3678. return (FALSE);
  3679. }
  3680. //
  3681. // See if the READONLY attribute is set.
  3682. //
  3683. if ( (!GetFileInformationByHandle(hAttr, &fi)) ||
  3684. (!(fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) )
  3685. {
  3686. //
  3687. // If the file could not be open for some reason other than that
  3688. // the readonly attribute was set, then fail.
  3689. //
  3690. CloseHandle(hAttr);
  3691. return (FALSE);
  3692. }
  3693. //
  3694. // Turn OFF the READONLY attribute.
  3695. //
  3696. fi.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
  3697. if (!SetFileAttributes(szFile, fi.dwFileAttributes))
  3698. {
  3699. CloseHandle(hAttr);
  3700. return (FALSE);
  3701. }
  3702. //
  3703. // Try again to open the file - READ_DATA | WRITE_DATA.
  3704. //
  3705. *phFile = CreateFile( szFile,
  3706. FILE_READ_DATA | FILE_WRITE_DATA,
  3707. FILE_SHARE_READ | FILE_SHARE_WRITE,
  3708. NULL,
  3709. OPEN_EXISTING,
  3710. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  3711. NULL );
  3712. //
  3713. // Close the file handle opened for READ_ATTRIBUTE | WRITE_ATTRIBUTE.
  3714. //
  3715. CloseHandle(hAttr);
  3716. //
  3717. // Make sure the open succeeded. If it still couldn't be opened with
  3718. // the readonly attribute turned off, then fail.
  3719. //
  3720. if (*phFile == INVALID_HANDLE_VALUE)
  3721. {
  3722. return (FALSE);
  3723. }
  3724. //
  3725. // Turn the READONLY attribute back ON.
  3726. //
  3727. fi.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
  3728. if (!SetFileAttributes(szFile, fi.dwFileAttributes))
  3729. {
  3730. CloseHandle(*phFile);
  3731. *phFile = INVALID_HANDLE_VALUE;
  3732. return (FALSE);
  3733. }
  3734. //
  3735. // Return success. A valid file handle is in *phFile.
  3736. //
  3737. return (TRUE);
  3738. }
  3739. ///////////////////////////////////////////////////////////////////////////////
  3740. //
  3741. // FUNCTION: UncompressDiskFullError
  3742. //
  3743. // DESCRIPTION:
  3744. //
  3745. // To be called when disk space is exhausted during uncompression.
  3746. // The function displays a message box with an OK button.
  3747. // NTFS leaves a file partially compressed when DeviceIoControl( )
  3748. // returns STATUS_DISK_FULL in GetLastError( ). It also leaves the
  3749. // compressed attribute as "compressed".
  3750. // Once the user acknowledges the message, we attempt to re-compress the
  3751. // file so that the entire file is compressed and matches its
  3752. // attribute setting.
  3753. //
  3754. // ARGUMENTS:
  3755. //
  3756. // hFile
  3757. // Handle to file being uncompressed at the time of the error.
  3758. //
  3759. // RETURNS:
  3760. //
  3761. // Nothing.
  3762. //
  3763. ///////////////////////////////////////////////////////////////////////////////
  3764. static void UncompressDiskFullError(HWND hwndParent, HANDLE hFile)
  3765. {
  3766. TCHAR szTitle[MAX_DLGTITLE_LEN + 1];
  3767. USHORT State = 1; // 1 = Compress.
  3768. ULONG Length = 0;
  3769. LoadString(g_hmodThisDll, IDS_APP_NAME, szTitle, ARRAYSIZE(szTitle));
  3770. LoadString(g_hmodThisDll, IDS_UNCOMPRESS_DISKFULL, szMessage, ARRAYSIZE(szMessage));
  3771. MessageBox(hwndParent, szMessage, szTitle, MB_OK | MB_ICONSTOP);
  3772. //
  3773. // Try to compress the file.
  3774. // Don't worry about errors on the compression attempt.
  3775. // At worst case, the file is left partially compressed with the attribute
  3776. // set as "compressed". According to the NTFS people, this is not
  3777. // harmful and the file is still usable. Besides, there isn't much else
  3778. // we could do at this point.
  3779. //
  3780. DeviceIoControl( hFile,
  3781. FSCTL_SET_COMPRESSION,
  3782. &State,
  3783. sizeof(USHORT),
  3784. NULL,
  3785. 0,
  3786. &Length,
  3787. FALSE );
  3788. }
  3789. #endif // ifdef WINNT