Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.   Copyright (c) 1990-2003 Info-ZIP.  All rights reserved.
  3.  
  4.   See the accompanying file LICENSE, version 2000-Apr-09 or later
  5.   (the contents of which are also included in unzip.h) for terms of use.
  6.   If, for some reason, all these files are missing, the Info-ZIP license
  7.   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
  8. */
  9. //******************************************************************************
  10. //
  11. // File:        WINMAIN.CPP
  12. //
  13. // Description: This module contains all the Windows specific code for Pocket
  14. //              UnZip.  It contains the entire user interface.  This code knows
  15. //              almost nothing about the Info-ZIP code.  All Info-ZIP related
  16. //              functions are wrapped by helper functions in INTRFACE.CPP.  The
  17. //              code in this module only calls those wrapper functions and
  18. //              INTRFACE.CPP handles all the details and callbacks of the
  19. //              Info-ZIP code.
  20. //
  21. // Copyright:   All the source files for Pocket UnZip, except for components
  22. //              written by the Info-ZIP group, are copyrighted 1997 by Steve P.
  23. //              Miller.  The product "Pocket UnZip" itself is property of the
  24. //              author and cannot be altered in any way without written consent
  25. //              from Steve P. Miller.
  26. //
  27. // Disclaimer:  All project files are provided "as is" with no guarantee of
  28. //              their correctness.  The authors are not liable for any outcome
  29. //              that is the result of using this source.  The source for Pocket
  30. //              UnZip has been placed in the public domain to help provide an
  31. //              understanding of its implementation.  You are hereby granted
  32. //              full permission to use this source in any way you wish, except
  33. //              to alter Pocket UnZip itself.  For comments, suggestions, and
  34. //              bug reports, please write to stevemil@pobox.com.
  35. //
  36. // Functions:   WinMain
  37. //              InitializeApplication
  38. //              ShutdownApplication
  39. //              RegisterUnzip
  40. //              BuildImageList
  41. //              WndProc
  42. //              OnCreate
  43. //              OnFileOpen
  44. //              OnActionView
  45. //              OnActionSelectAll
  46. //              OnViewExpandedView
  47. //              OnHelp
  48. //              OnGetDispInfo
  49. //              OnDeleteItem
  50. //              OnItemChanged
  51. //              Sort
  52. //              CompareFunc
  53. //              SetCaptionText
  54. //              DrawBanner
  55. //              AddDeleteColumns
  56. //              ResizeColumns
  57. //              GetZipErrorString
  58. //              AddFileToListView
  59. //              EnableAllMenuItems
  60. //              CheckAllMenuItems
  61. //              CenterWindow
  62. //              AddTextToEdit
  63. //              FormatValue
  64. //              BuildAttributesString
  65. //              BuildTypeString
  66. //              GetFileFromPath
  67. //              ForwardSlashesToBackSlashesA
  68. //              ForwardSlashesToBackSlashesW
  69. //              DeleteDirectory(LPTSTR szPath);
  70. //              RegWriteKey
  71. //              RegReadKey
  72. //              WriteOptionString
  73. //              WriteOptionInt
  74. //              GetOptionString
  75. //              GetOptionInt
  76. //              DisableEditing
  77. //              EditSubclassProc
  78. //              GetMenuString
  79. //              InitializeMRU
  80. //              AddFileToMRU
  81. //              RemoveFileFromMRU
  82. //              ActivateMRU
  83. //              ReadZipFileList
  84. //              DlgProcProperties
  85. //              MergeValues
  86. //              CheckThreeStateBox
  87. //              ExtractOrTestFiles
  88. //              DlgProcExtractOrTest
  89. //              FolderBrowser
  90. //              DlgProcBrowser
  91. //              SubclassSaveAsDlg
  92. //              DlgProcExtractProgress
  93. //              DlgProcViewProgress
  94. //              UpdateProgress
  95. //              PromptToReplace
  96. //              DlgProcReplace
  97. //              DlgProcPassword
  98. //              DlgProcViewAssociation
  99. //              DlgProcComment
  100. //              DlgProcAbout
  101. //
  102. //
  103. // Date      Name          History
  104. // --------  ------------  -----------------------------------------------------
  105. // 02/01/97  Steve Miller  Created (Version 1.0 using Info-ZIP UnZip 5.30)
  106. //
  107. //******************************************************************************
  108.  
  109. extern "C" {
  110. #define __WINMAIN_CPP__
  111. #define UNZIP_INTERNAL
  112.  
  113. #include "unzip.h"
  114.  
  115. #include "crypt.h"     // Needed to pick up CRYPT define setting and return values.
  116.  
  117. #include "unzvers.h"   // Only needed by consts.h (VERSION_DATE & VersionDate)
  118. #include "consts.h"    // Only include once - defines constant string messages.
  119.  
  120. #include <commctrl.h>  // Common controls - mainly ListView and ImageList
  121. #include <commdlg.h>   // Common dialogs - OpenFile dialog
  122.  
  123. #ifndef _WIN32_WCE
  124. #include <shlobj.h>    // On NT, we use the SHBrowseForFolder() stuff.
  125. #include <shellapi.h>  // CommandLineToArgvW() and ExtractIconEx()
  126. #endif
  127.  
  128. #include "intrface.h"  // Interface between Info-ZIP and us
  129. #include "winmain.h"   // Us
  130. }
  131. #include <tchar.h>     // Must be outside of extern "C" block
  132.  
  133.  
  134. //******************************************************************************
  135. //***** "Local" Global Variables
  136. //******************************************************************************
  137.  
  138. static LPCTSTR         g_szAppName     = TEXT("Pocket UnZip");
  139. static LPCTSTR         g_szClass       = TEXT("PocketUnZip");
  140. static LPCTSTR         g_szRegKey      = TEXT("Software\\Pocket UnZip");
  141. static LPCTSTR         g_szTempDir     = NULL;
  142. static HWND            g_hWndList      = NULL;
  143. static HWND            g_hWndCmdBar    = NULL;
  144. static int             g_cyCmdBar      = 0;
  145. static HFONT           g_hFontBanner   = NULL;
  146. static HICON           g_hIconMain     = NULL;
  147. static WNDPROC         g_wpSaveAsDlg   = NULL;
  148. static WNDPROC         g_wpEdit        = NULL;
  149. static int             g_sortColumn    = -1;
  150. static BOOL            g_fExpandedView = FALSE;
  151. static BOOL            g_fLoading      = FALSE;
  152. static BOOL            g_fSkipped      = FALSE;
  153. static BOOL            g_fViewing      = FALSE;
  154. static HWND            g_hWndWaitFor   = NULL;
  155. static FILE_TYPE_NODE *g_pftHead       = NULL;
  156.  
  157. #ifdef _WIN32_WCE
  158. static LPCTSTR         g_szHelpFile    = TEXT("\\windows\\punzip.htp");
  159. #else
  160. static TCHAR           g_szTempDirPath[_MAX_PATH];
  161. static LPCTSTR         g_szHelpFile    = TEXT("punzip.html");
  162. #endif
  163.  
  164. static COLUMN g_columns[] = {
  165.    { TEXT("Name"),       LVCFMT_LEFT  },
  166.    { TEXT("Size"),       LVCFMT_RIGHT },
  167.    { TEXT("Type"),       LVCFMT_LEFT  },
  168.    { TEXT("Modified"),   LVCFMT_LEFT  },
  169.    { TEXT("Attributes"), LVCFMT_LEFT  },
  170.    { TEXT("Compressed"), LVCFMT_RIGHT },
  171.    { TEXT("Ratio"),      LVCFMT_RIGHT },
  172.    { TEXT("Method"),     LVCFMT_LEFT  },
  173.    { TEXT("CRC"),        LVCFMT_LEFT  },
  174.    { TEXT("Comment"),    LVCFMT_LEFT  }
  175. };
  176.  
  177.  
  178. //******************************************************************************
  179. //***** Local Function Prototypes
  180. //******************************************************************************
  181.  
  182. // Startup and Shutdown Functions
  183. void InitializeApplication(LPCTSTR szZipFile);
  184. void ShutdownApplication();
  185. void RegisterUnzip();
  186. void BuildImageList();
  187.  
  188. // Our Main Window's Message Handler
  189. LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  190.  
  191. // Event Handlers for our Main Window
  192. int OnCreate();
  193. void OnFileOpen();
  194. void OnActionView();
  195. void OnActionSelectAll();
  196. void OnViewExpandedView();
  197. void OnHelp();
  198.  
  199. // Event Handlers for our List View
  200. void OnGetDispInfo(LV_DISPINFO *plvdi);
  201. void OnDeleteItem(NM_LISTVIEW *pnmlv);
  202. void OnItemChanged(NM_LISTVIEW *pnmlv);
  203.  
  204. // List View Sort Functions
  205. void Sort(int sortColumn, BOOL fForce);
  206. int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM sortColumn);
  207.  
  208. // Helper/Utility Functions
  209. void SetCaptionText(LPCTSTR szPrefix);
  210. void DrawBanner(HDC hdc);
  211. void AddDeleteColumns();
  212. void ResizeColumns();
  213. LPCTSTR GetZipErrorString(int error);
  214. void AddFileToListView(FILE_NODE *pFile);
  215. void EnableAllMenuItems(UINT uMenuItem, BOOL fEnabled);
  216. void CheckAllMenuItems(UINT uMenuItem, BOOL fChecked);
  217. void CenterWindow(HWND hWnd);
  218. void AddTextToEdit(LPCSTR szText);
  219. LPTSTR FormatValue(LPTSTR szValue, zusz_t uzValue);
  220. LPTSTR BuildAttributesString(LPTSTR szBuffer, DWORD dwAttributes);
  221. LPCSTR BuildTypeString(FILE_NODE *pFile, LPSTR szType);
  222. LPCSTR GetFileFromPath(LPCSTR szPath);
  223. void ForwardSlashesToBackSlashesA(LPSTR szBuffer);
  224. #ifdef UNICODE
  225.    void ForwardSlashesToBackSlashesW(LPWSTR szBuffer);
  226. #  define ForwardSlashesToBackSlashes ForwardSlashesToBackSlashesW
  227. #else
  228. #  define ForwardSlashesToBackSlashes ForwardSlashesToBackSlashesA
  229. #endif
  230. void DeleteDirectory(LPTSTR szPath);
  231.  
  232. // Registry Functions
  233. void RegWriteKey(HKEY hKeyRoot, LPCTSTR szSubKey, LPCTSTR szValue);
  234. BOOL RegReadKey(HKEY hKeyRoot, LPCTSTR szSubKey, LPTSTR szValue, DWORD cBytes);
  235. void WriteOptionString(LPCTSTR szOption, LPCTSTR szValue);
  236. void WriteOptionInt(LPCTSTR szOption, DWORD dwValue);
  237. LPTSTR GetOptionString(LPCTSTR szOption, LPCTSTR szDefault, LPTSTR szValue, DWORD nSize);
  238. DWORD GetOptionInt(LPCTSTR szOption, DWORD dwDefault);
  239.  
  240. // EDIT Control Subclass Functions
  241. void DisableEditing(HWND hWndEdit);
  242. LRESULT CALLBACK EditSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  243.  
  244. // MRU Functions
  245. void InitializeMRU();
  246. void AddFileToMRU(LPCSTR szFile);
  247. void RemoveFileFromMRU(LPCTSTR szFile);
  248. void ActivateMRU(UINT uIDItem);
  249.  
  250. // Open Zip File Functions
  251. void ReadZipFileList(LPCTSTR wszPath);
  252.  
  253. // Zip File Properties Dialog Functions
  254. BOOL CALLBACK DlgProcProperties(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  255. void MergeValues(int *p1, int p2);
  256. void CheckThreeStateBox(HWND hDlg, int nIDButton, int state);
  257.  
  258. // Extract/Test Dialog Functions
  259. void ExtractOrTestFiles(BOOL fExtract);
  260. BOOL CALLBACK DlgProcExtractOrTest(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  261.  
  262. // Folder Browsing Dialog Functions
  263. BOOL FolderBrowser(LPTSTR szPath, DWORD dwLength);
  264. BOOL CALLBACK DlgProcBrowser(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  265. void SubclassSaveAsDlg();
  266.  
  267. // Extraction/Test/View Progress Dialog Functions
  268. BOOL CALLBACK DlgProcExtractProgress(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  269. BOOL CALLBACK DlgProcViewProgress(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  270. void UpdateProgress(EXTRACT_INFO *pei, BOOL fFull);
  271.  
  272. // Replace File Dialog Functions
  273. int PromptToReplace(LPCSTR szPath);
  274. BOOL CALLBACK DlgProcReplace(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  275.  
  276. // Password Dialog Functions
  277. BOOL CALLBACK DlgProcPassword(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  278.  
  279. // View Association Dialog Functions
  280. BOOL CALLBACK DlgProcViewAssociation(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  281.  
  282. // Comment Dialog Functions
  283. BOOL CALLBACK DlgProcComment(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  284.  
  285. // About Dialog Functions
  286. BOOL CALLBACK DlgProcAbout(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  287.  
  288.  
  289. //******************************************************************************
  290. //***** WinMain - Our one and only entry point
  291. //******************************************************************************
  292.  
  293. // Entrypoint is a tiny bit different on Windows CE - UNICODE command line.
  294. #ifdef _WIN32_WCE
  295. extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  296.                               LPTSTR lpCmdLine, int nCmdShow)
  297. #else
  298. extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  299.                               LPSTR lpCmdLine, int nCmdShow)
  300. #endif
  301. {
  302.    // Wrap the whole ball of wax in a big exception handler.
  303.    __try {
  304.  
  305.       // Store global instance handle.
  306.       g_hInst = hInstance;
  307.  
  308.       // Create our banner font.  We need to do this before creating our window.
  309.       // This font handle will be deleted in ShutdownApplication().
  310.       LOGFONT lf;
  311.       ZeroMemory(&lf, sizeof(lf));
  312.       lf.lfHeight = 16;
  313.       lf.lfWeight = FW_BOLD;
  314.       lf.lfCharSet = ANSI_CHARSET;
  315.       lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
  316.       lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  317.       lf.lfQuality = DEFAULT_QUALITY;
  318.       lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;
  319.       _tcscpy(lf.lfFaceName, TEXT("MS Sans Serif"));
  320.       g_hFontBanner = CreateFontIndirect(&lf);
  321.  
  322.       // Define the window class for our application's main window.
  323.       WNDCLASS wc;
  324.       ZeroMemory(&wc, sizeof(wc));
  325.       wc.lpszClassName = g_szClass;
  326.       wc.hInstance     = hInstance;
  327.       wc.lpfnWndProc   = WndProc;
  328.       wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  329.  
  330.       TCHAR *szZipPath = NULL;
  331.  
  332. #ifdef _WIN32_WCE
  333.  
  334.       // Get our main window's small icon.  On Windows CE, we need to send ourself
  335.       // a WM_SETICON in order for our task bar to update itself.
  336.       g_hIconMain = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_UNZIP),
  337.                                      IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
  338.       wc.hIcon = g_hIconMain;
  339.  
  340.       // On Windows CE, we only need the WS_VISIBLE flag.
  341.       DWORD dwStyle = WS_VISIBLE;
  342.  
  343.       // Get and store command line file (if any).
  344.       if (lpCmdLine && *lpCmdLine) {
  345.          szZipPath = lpCmdLine;
  346.       }
  347.  
  348. #else
  349.  
  350.       // On NT we add a cursor, icon, and menu to our application's window class.
  351.       wc.hCursor      = LoadCursor(NULL, IDC_ARROW);
  352.       wc.hIcon        = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_UNZIP));
  353.       wc.lpszMenuName = MAKEINTRESOURCE(IDR_UNZIP);
  354.  
  355.       // On Windows NT, we use the standard overlapped window style.
  356.       DWORD dwStyle = WS_OVERLAPPEDWINDOW;
  357.  
  358.       TCHAR szBuffer[_MAX_PATH];
  359.  
  360.       // Get and store command line file (if any).
  361.       if (lpCmdLine && *lpCmdLine) {
  362.          MBSTOTSTR(szBuffer, lpCmdLine, countof(szBuffer));
  363.          szZipPath = szBuffer;
  364.       }
  365.  
  366. #endif
  367.  
  368.       // Register our window class with the OS.
  369.       if (!RegisterClass(&wc)) {
  370.          DebugOut(TEXT("RegisterClass() failed [%u]"), GetLastError());
  371.       }
  372.  
  373.       // Create our main window using our registered window class.
  374.       g_hWndMain = CreateWindow(wc.lpszClassName, g_szAppName, dwStyle,
  375.                                 CW_USEDEFAULT, CW_USEDEFAULT,
  376.                                 CW_USEDEFAULT, CW_USEDEFAULT,
  377.                                 NULL, NULL, hInstance, NULL);
  378.  
  379.       // Quit now if we failed to create our main window.
  380.       if (!g_hWndMain) {
  381.          DebugOut(TEXT("CreateWindow() failed [%u]"), GetLastError());
  382.          ShutdownApplication();
  383.          return 0;
  384.       }
  385.  
  386.       // Make sure our window is visible.  Really only needed for NT.
  387.       ShowWindow(g_hWndMain, nCmdShow);
  388.  
  389.       // Load our keyboard accelerator shortcuts.
  390.       MSG    msg;
  391.       HACCEL hAccel = LoadAccelerators(g_hInst, MAKEINTRESOURCE(IDR_UNZIP));
  392.       DWORD  dwPaintFlags = 0;
  393.  
  394.       // The message pump.  Loop until we get a WM_QUIT message.
  395.       while (GetMessage(&msg, NULL, 0, 0)) {
  396.  
  397.          // Check to see if this is an accelerator and handle it if neccessary.
  398.          if (!TranslateAccelerator(g_hWndMain, hAccel, &msg)) {
  399.  
  400.             // If a normal message, then dispatch it to the correct window.
  401.             TranslateMessage(&msg);
  402.             DispatchMessage(&msg);
  403.  
  404.             // Wait until our application is up and visible before trying to
  405.             // initialize some of our structures and load any command line file.
  406.             if ((msg.message == WM_PAINT) && (dwPaintFlags != 0x11)) {
  407.                if (msg.hwnd == g_hWndWaitFor) {
  408.                   dwPaintFlags |= 0x01;
  409.                } else if (msg.hwnd == g_hWndList) {
  410.                   dwPaintFlags |= 0x10;
  411.                }
  412.                if (dwPaintFlags == 0x11) {
  413.                   InitializeApplication((szZipPath && *szZipPath) ?
  414.                                         szZipPath : NULL);
  415.                }
  416.             }
  417.          }
  418.       }
  419.  
  420.       // Clean up code.
  421.       ShutdownApplication();
  422.  
  423.       // Nice clean finish - were out of here.
  424.       return msg.wParam;
  425.  
  426.  
  427.    } __except(EXCEPTION_EXECUTE_HANDLER) {
  428.  
  429.       // Something very bad happened.  Try our best to appear somewhat graceful.
  430.       MessageBox(NULL,
  431.          TEXT("An internal error occurred.  Possible causes are that you are ")
  432.          TEXT("out of memory, a ZIP file (if one is loaded) contains an ")
  433.          TEXT("unexpected error, or there is a bug in our program (that's why ")
  434.          TEXT("it's free).  Pocket UnZip cannot continue.  It will exit now, ")
  435.          TEXT("but you may restart it and try again.\n\n")
  436.          TEXT("If the problem persists, please write to stevemil@pobox.com with ")
  437.          TEXT("any information that might help track down the problem."),
  438.          g_szAppName, MB_ICONERROR | MB_OK);
  439.    }
  440.  
  441.    return 1;
  442. }
  443.  
  444.  
  445. //******************************************************************************
  446. //***** Startup and Shutdown Functions
  447. //******************************************************************************
  448.  
  449. void InitializeApplication(LPCTSTR szZipFile) {
  450.  
  451.    // This function is called after our class is registered and all our windows
  452.    // are created and visible to the user.
  453.  
  454.    // Show hour glass cursor.
  455.    HCURSOR hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  456.  
  457.    // Register UnZip in the registry to handle ".ZIP" files.
  458.    RegisterUnzip();
  459.  
  460.    // Enumerate the system file assoications and build an image list.
  461.    BuildImageList();
  462.  
  463.    // Load our initial MRU into our menu.
  464.    InitializeMRU();
  465.  
  466.    // Restore/remove our cursor.
  467.    SetCursor(hCur);
  468.  
  469.    // Clear our initialization window handle.
  470.    g_hWndWaitFor = NULL;
  471.  
  472.    // Load our command line file if one was specified. Otherwise, just update
  473.    // our banner to show that no file is loaded.
  474.    if (szZipFile) {
  475.       ReadZipFileList(szZipFile);
  476.    } else {
  477.       DrawBanner(NULL);
  478.    }
  479.  
  480.    // Enable some controls.
  481.    EnableAllMenuItems(IDM_FILE_OPEN,          TRUE);
  482.    EnableAllMenuItems(IDM_FILE_CLOSE,         TRUE);
  483.    EnableAllMenuItems(IDM_VIEW_EXPANDED_VIEW, TRUE);
  484.    EnableAllMenuItems(IDM_HELP_ABOUT,         TRUE);
  485.  
  486.    // Set our temporary directory.
  487. #ifdef _WIN32_WCE
  488.    g_szTempDir = TEXT("\\Temporary Pocket UnZip Files");
  489. #else
  490.    g_szTempDir = TEXT("C:\\Temporary Pocket UnZip Files");
  491.  
  492.    // Set the drive to be the same drive as the OS installation is on.
  493.    if (GetWindowsDirectory(g_szTempDirPath, countof(g_szTempDirPath))) {
  494.       lstrcpy(g_szTempDirPath + 3, TEXT("Temporary Pocket UnZip Files"));
  495.       g_szTempDir  = g_szTempDirPath;
  496.    }
  497. #endif
  498. }
  499.  
  500. //******************************************************************************
  501. void ShutdownApplication() {
  502.  
  503.    // Free our banner font.
  504.    if (g_hFontBanner) {
  505.       DeleteObject(g_hFontBanner);
  506.       g_hFontBanner = NULL;
  507.    }
  508.  
  509.    // Delete our FILE_TYPE_NODE linked list.
  510.    for (FILE_TYPE_NODE *pft = g_pftHead; pft; ) {
  511.       FILE_TYPE_NODE *pftNext = pft->pNext;
  512.       delete[] (BYTE*)pft;
  513.       pft = pftNext;
  514.    }
  515.    g_pftHead = NULL;
  516.  
  517.    // If there are no other instances of our application open, then delete our
  518.    // temporary directory and all the files in it.  Any files opened for viewing
  519.    // should be locked and will fail to delete.  This is to be expected.
  520.    if (g_szTempDir && (FindWindow(g_szClass, NULL) == NULL)) {
  521.       TCHAR szPath[_MAX_PATH];
  522.       _tcscpy(szPath, g_szTempDir);
  523.       DeleteDirectory(szPath);
  524.    }
  525. }
  526.  
  527. //******************************************************************************
  528. void RegisterUnzip() {
  529.  
  530. #ifdef _WIN32_WCE
  531.  
  532.    // WARNING!  Since Windows CE does not support any way to get your binary's
  533.    // name at runtime, we have to hard-code in "punzip.exe".  If our binary is
  534.    // not named this or is in a non-path directory, then we will fail to
  535.    // register ourself with the system as the default application to handle
  536.    // ".zip" files.
  537.    TCHAR szPath[32] = TEXT("punzip.exe");
  538.    TCHAR szTstPath[32];
  539.  
  540. #else
  541.  
  542.    // Get our module's path and file name.  We use the short path name for the
  543.    // registry because it is guaranteed to contain no spaces.
  544.    TCHAR szLongPath[_MAX_PATH];
  545.    TCHAR szPath[_MAX_PATH];
  546.    TCHAR szTstPath[_MAX_PATH];
  547.    GetModuleFileName(NULL, szLongPath, countof(szLongPath));
  548.    GetShortPathName(szLongPath, szPath, countof(szPath));
  549.  
  550. #endif
  551.  
  552.    // Store a pointer to the end of our path for easy appending.
  553.    LPTSTR szEnd = szPath + _tcslen(szPath);
  554.  
  555.    BOOL fDoRegisterPUnZip = TRUE;
  556.  
  557.    // Associate "ZIP" file extensions to our application
  558.    if (RegReadKey(HKEY_CLASSES_ROOT, TEXT(".zip"), szTstPath, sizeof(szTstPath)))
  559.    {
  560.       if (_tcscmp(szTstPath, TEXT("zipfile")) != 0)
  561.          fDoRegisterPUnZip = FALSE;
  562.       else if (RegReadKey(HKEY_CLASSES_ROOT, TEXT("zipfile\\shell\\Open\\command"),
  563.                           szTstPath, sizeof(szTstPath)) &&
  564.                (_tcsncmp(szTstPath, szPath, _tcslen(szPath)) != 0))
  565.          fDoRegisterPUnZip = FALSE;
  566.  
  567.       if (!fDoRegisterPUnZip)
  568.       {
  569.          fDoRegisterPUnZip =
  570.             (IDOK == MessageBox(g_hWndMain,
  571.                                 TEXT("Currently, Pocket UnZip is not registered as default ")
  572.                                 TEXT("handler for Zip archives.\n\n")
  573.                                 TEXT("Please, confirm that Pocket UnZip should now register itself ")
  574.                                 TEXT("as default application for handling Zip archives (.zip files)"),
  575.                                 g_szAppName,
  576.                                 MB_ICONQUESTION | MB_OKCANCEL));
  577.       }
  578.    }
  579.    if (fDoRegisterPUnZip) {
  580.       RegWriteKey(HKEY_CLASSES_ROOT, TEXT(".zip"), TEXT("zipfile"));
  581.       RegWriteKey(HKEY_CLASSES_ROOT, TEXT("zipfile"), TEXT("ZIP File"));
  582.       RegWriteKey(HKEY_CLASSES_ROOT, TEXT("zipfile\\shell"), NULL);
  583.       RegWriteKey(HKEY_CLASSES_ROOT, TEXT("zipfile\\shell\\Open"), NULL);
  584.       _tcscpy(szEnd, TEXT(" %1"));
  585.       RegWriteKey(HKEY_CLASSES_ROOT, TEXT("zipfile\\shell\\Open\\command"), szPath);
  586.  
  587.       // Register our program icon for all ZIP files.
  588.       _stprintf(szEnd, TEXT(",-%u"), IDI_ZIPFILE);
  589.       RegWriteKey(HKEY_CLASSES_ROOT, TEXT("zipfile\\DefaultIcon"), szPath);
  590.    }
  591.  
  592.    // Create our application option location.
  593.    RegWriteKey(HKEY_CURRENT_USER, TEXT("Software"), NULL);
  594.    RegWriteKey(HKEY_CURRENT_USER, g_szRegKey, NULL);
  595. }
  596.  
  597. //******************************************************************************
  598. void BuildImageList() {
  599.  
  600.    // Create our global image list.
  601. #ifdef _WIN32_WCE
  602.  
  603.    // On Windows CE, we can't spare a color for the mask, so we have to create
  604.    // the mask in a separate monochrome bitmap.
  605.  
  606.    HIMAGELIST hil = ImageList_Create(16, 16, ILC_COLOR | ILC_MASK, 8, 8);
  607.  
  608.    // Load our default bitmaps into the image list.
  609.    HBITMAP hBmpImageList = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_IMAGELIST));
  610.    HBITMAP hBmpMask = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_IMAGELIST_MASK));
  611.    ImageList_Add(hil, hBmpImageList, hBmpMask);
  612.    DeleteObject(hBmpImageList);
  613.    DeleteObject(hBmpMask);
  614.  
  615. #else
  616.  
  617.    // On Windows NT, we use magenta as a transparency mask color.
  618.    HIMAGELIST hil = ImageList_LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_IMAGELIST),
  619.                                          16, 8, RGB(255, 0, 255));
  620. #endif
  621.  
  622.    // Set up for our registry file type enumeration.
  623.    FILE_TYPE_NODE *pftLast = NULL;
  624.    TCHAR szExtension[128], szKey[128], szDescription[_MAX_PATH], szIconFile[_MAX_PATH + 16];
  625.    DWORD dwIndex = 0, dwCount = countof(szExtension);
  626.  
  627.    // Enumerate all the keys immediately under HKEY_CLASSES_ROOT.
  628.    while (ERROR_SUCCESS == RegEnumKeyEx(HKEY_CLASSES_ROOT, dwIndex++, szExtension,
  629.                                         &dwCount, NULL, NULL, NULL, NULL))
  630.    {
  631.       dwCount = countof(szExtension);
  632.  
  633.       // Check to see if we read an extension key (starts with a period)
  634.       if (*szExtension != TEXT('.')) {
  635.          continue;
  636.       }
  637.  
  638.       // Read the actual key name for this extension.
  639.       if (!RegReadKey(HKEY_CLASSES_ROOT, szExtension, szKey, sizeof(szKey))) {
  640.          continue;
  641.       }
  642.  
  643.       // Read the Description for this extension.
  644.       RegReadKey(HKEY_CLASSES_ROOT, szKey, szDescription, sizeof(szDescription));
  645.  
  646.       HICON hIcon = NULL;
  647.       LPTSTR szEnd = szKey + _tcslen(szKey);
  648.  
  649.       // Attempt to get an icon for this extension from the "DefaultIcon" key.
  650.       _tcscpy(szEnd, TEXT("\\DefaultIcon"));
  651.       if (RegReadKey(HKEY_CLASSES_ROOT, szKey, szIconFile, sizeof(szIconFile))) {
  652.  
  653.          // Look for the comma between the file name and the image.
  654.          LPTSTR szImageId = _tcschr(szIconFile, TEXT(','));
  655.          if (szImageId) {
  656.  
  657.             // NULL terminate the file name portion of szIconFile.
  658.             *(szImageId++) = TEXT('\0');
  659.  
  660.             // Get the image ID value from szIconFile.
  661.             int imageId = _ttoi(szImageId);
  662.  
  663.             // Extract the icon from the module specified in szIconFile.
  664.             ExtractIconEx(szIconFile, imageId, NULL, &hIcon, 1);
  665.             if (hIcon == NULL) {
  666.                ExtractIconEx(szIconFile, imageId, &hIcon, NULL, 1);
  667.             }
  668.          }
  669.       }
  670.  
  671.       // If we failed to get the icon using the "DefaultIcon" key, then try
  672.       // using the "shell\Open\command" key.
  673.       if (hIcon == NULL) {
  674.  
  675.          _tcscpy(szEnd, TEXT("\\shell\\Open\\command"));
  676.          if (RegReadKey(HKEY_CLASSES_ROOT, szKey, szIconFile, sizeof(szIconFile))) {
  677.  
  678.             // Get a pointer to just the binary - strip quotes and spaces.
  679.             LPTSTR szPath;
  680.             if (*szIconFile == TEXT('\"')) {
  681.                szPath = szIconFile + 1;
  682.                if (szEnd = _tcschr(szPath, TEXT('\"'))) {
  683.                   *szEnd = TEXT('\0');
  684.                }
  685.             } else {
  686.                szPath = szIconFile;
  687.                if (szEnd = _tcschr(szPath, TEXT(' '))) {
  688.                   *szEnd = TEXT('\0');
  689.                }
  690.             }
  691.  
  692.             // Extract the icon from the module specified in szIconFile.
  693.             ExtractIconEx(szPath, 0, NULL, &hIcon, 1);
  694.             if (hIcon == NULL) {
  695.                ExtractIconEx(szPath, 0, &hIcon, NULL, 1);
  696.             }
  697.          }
  698.       }
  699.  
  700.       // If we found an icon, add it to our image list.
  701.       int image = -1;
  702.       if (hIcon) {
  703.          image = ImageList_AddIcon(hil, hIcon);
  704.       }
  705.  
  706.       // If no icon could be found, then check to see if this is an executable.
  707.       if ((image == -1) && (
  708. #ifndef _WIN32_WCE // Windows CE only recognizes EXE's as executable.
  709.          !_tcsicmp(szExtension + 1, TEXT("bat")) ||
  710.          !_tcsicmp(szExtension + 1, TEXT("cmd")) ||
  711.          !_tcsicmp(szExtension + 1, TEXT("com")) ||
  712. #endif
  713.          !_tcsicmp(szExtension + 1, TEXT("exe"))))
  714.       {
  715.          image = IMAGE_APPLICATION;
  716.       }
  717.  
  718.       // If we don't have a description or a icon, then bail on this extension.
  719.       if (!*szDescription && (image < 0)) {
  720.          continue;
  721.       }
  722.  
  723.       // Create our FILE_TYPE_NODE.
  724.       size_t length = _tcslen(szExtension) - 1 + _tcslen(szDescription);
  725.       FILE_TYPE_NODE *pft = (FILE_TYPE_NODE*) new BYTE[
  726.          sizeof(FILE_TYPE_NODE) + (sizeof(TCHAR) * length)];
  727.  
  728.       // Bail out if we could not create our node.
  729.       if (!pft) {
  730.          DebugOut(TEXT("Not enough memory to create a FILE_TYPE_NODE."));
  731.          continue;
  732.       }
  733.  
  734.       // Fill in the node.
  735.       pft->pNext = NULL;
  736.       pft->image = (image >= 0) ? image : IMAGE_GENERIC;
  737.       TSTRTOMBS(pft->szExtAndDesc, szExtension + 1, length + 2);
  738.       size_t sizext = (strlen(pft->szExtAndDesc) + 1);
  739.       TSTRTOMBS(pft->szExtAndDesc + sizext,
  740.                 szDescription, length - sizext + 2);
  741.  
  742.       // Add the node to our list.
  743.       if (pftLast) {
  744.          pftLast->pNext = pft;
  745.       } else {
  746.          g_pftHead = pft;
  747.       }
  748.       pftLast = pft;
  749.    }
  750.  
  751.    // Assign this image list to our tree control.
  752.    ListView_SetImageList(g_hWndList, hil, LVSIL_SMALL);
  753. }
  754.  
  755.  
  756. //******************************************************************************
  757. //***** Our Main Window's Message Handler
  758. //******************************************************************************
  759.  
  760. LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  761.  
  762.    switch(uMsg) {
  763.       case WM_CREATE:
  764.          g_hWndMain = hWnd;
  765.          return OnCreate();
  766.  
  767.       case WM_ERASEBKGND:
  768.          DrawBanner((HDC)wParam);
  769.          return 0;
  770.  
  771.       case WM_SIZE:
  772.          // Resize our list view control to match our client area.
  773.          MoveWindow(g_hWndList, 0, g_cyCmdBar + 22, LOWORD(lParam),
  774.                     HIWORD(lParam) - (g_cyCmdBar + 22), TRUE);
  775.  
  776. #ifndef _WIN32_WCE
  777.          // On NT we have to resize our toolbar as well.
  778.          MoveWindow(g_hWndCmdBar, 0, 0, LOWORD(lParam), g_cyCmdBar, TRUE);
  779. #endif
  780.          return 0;
  781.  
  782.       case WM_SETFOCUS:
  783.          // Always direct focus to our list control.
  784.          SetFocus(g_hWndList);
  785.          return 0;
  786.  
  787.       case WM_DESTROY:
  788.          PostQuitMessage(0);
  789.          return 0;
  790.  
  791.       case WM_HELP:
  792.          OnHelp();
  793.          return 0;
  794.  
  795.       case WM_PRIVATE:
  796.          switch (wParam) {
  797.  
  798. #ifdef _WIN32_WCE
  799.             case MSG_SUBCLASS_DIALOG:
  800.                SubclassSaveAsDlg();
  801.                return 0;
  802. #endif
  803.             case MSG_ADD_TEXT_TO_EDIT:
  804.                AddTextToEdit((LPCSTR)lParam);
  805.                return 0;
  806.  
  807.             case MSG_PROMPT_TO_REPLACE:
  808.                return PromptToReplace((LPCSTR)lParam);
  809.  
  810. #if CRYPT
  811.             case MSG_PROMPT_FOR_PASSWORD:
  812.                return DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_PASSWORD),
  813.                                      g_hDlgProgress, (DLGPROC)DlgProcPassword,
  814.                                      lParam);
  815. #endif
  816.  
  817.             case MSG_UPDATE_PROGRESS_PARTIAL:
  818.                UpdateProgress((EXTRACT_INFO*)lParam, FALSE);
  819.                return 0;
  820.  
  821.             case MSG_UPDATE_PROGRESS_COMPLETE:
  822.                UpdateProgress((EXTRACT_INFO*)lParam, TRUE);
  823.                return 0;
  824.          }
  825.          return 0;
  826.  
  827.       case WM_NOTIFY:
  828.          switch (((LPNMHDR)lParam)->code) {
  829.  
  830.             case LVN_GETDISPINFO:
  831.                OnGetDispInfo((LV_DISPINFO*)lParam);
  832.                return 0;
  833.  
  834.             case LVN_DELETEITEM:
  835.                OnDeleteItem((NM_LISTVIEW*)lParam);
  836.                return 0;
  837.  
  838.             case LVN_COLUMNCLICK:
  839.                Sort(((NM_LISTVIEW*)lParam)->iSubItem, FALSE);
  840.                return 0;
  841.  
  842.             case LVN_ITEMCHANGED:
  843.                OnItemChanged((NM_LISTVIEW*)lParam);
  844.                return 0;
  845.  
  846.             case NM_DBLCLK:
  847.             case NM_RETURN:
  848.                OnActionView();
  849.                return 0;
  850.          }
  851.  
  852.          return 0;
  853.  
  854.       case WM_COMMAND:
  855.          switch (LOWORD(wParam)) {
  856.  
  857.             case IDM_FILE_OPEN:
  858.                OnFileOpen();
  859.                return 0;
  860.  
  861.             case IDM_FILE_PROPERTIES:
  862.                DialogBox(g_hInst, MAKEINTRESOURCE(IDD_PROPERTIES), hWnd, (DLGPROC)DlgProcProperties);
  863.                return 0;
  864.  
  865.             case IDM_FILE_CLOSE:
  866.                SendMessage(hWnd, WM_CLOSE, 0, 0);
  867.                return 0;
  868.  
  869.             case IDM_ACTION_EXTRACT_ALL:
  870.                OnActionSelectAll();
  871.                // Fall through to IDM_ACTION_EXTRACT
  872.  
  873.             case IDM_ACTION_EXTRACT:
  874.                ExtractOrTestFiles(TRUE);
  875.                return 0;
  876.  
  877.             case IDM_ACTION_TEST_ALL:
  878.                OnActionSelectAll();
  879.                // Fall through to IDM_ACTION_TEST
  880.  
  881.             case IDM_ACTION_TEST:
  882.                ExtractOrTestFiles(FALSE);
  883.                return 0;
  884.  
  885.             case IDM_ACTION_VIEW:
  886.                OnActionView();
  887.                return 0;
  888.  
  889.             case IDM_ACTION_SELECT_ALL:
  890.                OnActionSelectAll();
  891.                return 0;
  892.  
  893.             case IDM_VIEW_EXPANDED_VIEW:
  894.                OnViewExpandedView();
  895.                return 0;
  896.  
  897.             case IDM_VIEW_COMMENT:
  898.                DialogBox(g_hInst, MAKEINTRESOURCE(IDD_COMMENT), hWnd, (DLGPROC)DlgProcComment);
  899.                return 0;
  900.  
  901.             case IDM_HELP_ABOUT:
  902.                DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)DlgProcAbout);
  903.                return 0;
  904.  
  905.             case IDHELP:
  906.                return SendMessage(hWnd, WM_HELP, 0, 0);
  907.  
  908.             default:
  909.                // Check to see if a MRU file was selected.
  910.                if ((LOWORD(wParam) >= MRU_START_ID) &&
  911.                    (LOWORD(wParam) < (MRU_START_ID + MRU_MAX_FILE)))
  912.                {
  913.                   ActivateMRU(LOWORD(wParam));
  914.                }
  915.          }
  916.     }
  917.     return DefWindowProc(hWnd, uMsg, wParam, lParam);
  918. }
  919.  
  920. //******************************************************************************
  921. //***** Event Handlers for our Main Window
  922. //******************************************************************************
  923.  
  924. int OnCreate() {
  925.  
  926.    // Our toolbar buttons.
  927.    static TBBUTTON tbButton[] = {
  928.       { 0, 0,                      0, TBSTYLE_SEP,    0, 0, 0, -1 },
  929.       { 0, IDM_FILE_OPEN,          0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  930.       { 0, 0,                      0, TBSTYLE_SEP,    0, 0, 0, -1 },
  931.       { 1, IDM_FILE_PROPERTIES,    0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  932.       { 0, 0,                      0, TBSTYLE_SEP,    0, 0, 0, -1 },
  933.       { 2, IDM_ACTION_EXTRACT,     0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  934.       { 3, IDM_ACTION_EXTRACT_ALL, 0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  935.       { 0, 0,                      0, TBSTYLE_SEP,    0, 0, 0, -1 },
  936.       { 4, IDM_ACTION_TEST,        0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  937.       { 5, IDM_ACTION_TEST_ALL,    0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  938.       { 0, 0,                      0, TBSTYLE_SEP,    0, 0, 0, -1 },
  939.       { 6, IDM_ACTION_VIEW,        0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  940.       { 0, 0,                      0, TBSTYLE_SEP,    0, 0, 0, -1 },
  941.       { 7, IDM_VIEW_EXPANDED_VIEW, 0, TBSTYLE_BUTTON, 0, 0, 0, -1 },
  942.       { 8, IDM_VIEW_COMMENT,       0, TBSTYLE_BUTTON, 0, 0, 0, -1 }
  943.    };
  944.  
  945.    // Our toolbar buttons' tool tip text.
  946.    static LPTSTR szToolTips[] = {
  947.        TEXT(""),  // Menu
  948.        TEXT("Open (Ctrl+O)"),
  949.        TEXT("Properties (Alt+Enter)"),
  950.        TEXT("Extract Selected Files"),
  951.        TEXT("Extract All Files"),
  952.        TEXT("Test Selected Files"),
  953.        TEXT("Test All Files"),
  954.        TEXT("View Selected File"),
  955.        TEXT("Expanded View"),
  956.        TEXT("View Zip File Comment")
  957.    };
  958.  
  959.    // Initialize the common controls.
  960.    InitCommonControls();
  961.  
  962.    // Check to see if we have a help file.
  963.    BOOL fHelp = (GetFileAttributes(g_szHelpFile) != 0xFFFFFFFF);
  964.  
  965.    // Set our window's icon so it can update the task bar.
  966.    if (g_hIconMain) {
  967.       SendMessage(g_hWndMain, WM_SETICON, FALSE, (LPARAM)g_hIconMain);
  968.    }
  969.  
  970.    // Create the tree control.  Our main window will resize it to fit.
  971.    g_hWndList = CreateWindow(WC_LISTVIEW, TEXT(""),
  972.                              WS_VSCROLL | WS_CHILD | WS_VISIBLE |
  973.                              LVS_REPORT | LVS_SHOWSELALWAYS,
  974.                              0, 0, 0, 0, g_hWndMain, NULL, g_hInst, NULL);
  975.  
  976. #ifdef _WIN32_WCE
  977.  
  978.    // Create a command bar and add the toolbar bitmaps to it.
  979.    g_hWndCmdBar = CommandBar_Create(g_hInst, g_hWndMain, 1);
  980.    CommandBar_AddBitmap(g_hWndCmdBar, g_hInst, IDB_TOOLBAR, 9, 16, 16);
  981.    CommandBar_InsertMenubar(g_hWndCmdBar, g_hInst, IDR_UNZIP, 0);
  982.    CommandBar_AddButtons(g_hWndCmdBar, countof(tbButton), tbButton);
  983.    CommandBar_AddAdornments(g_hWndCmdBar, fHelp ? CMDBAR_HELP : 0, 0);
  984.  
  985.    // Add tool tips to the tool bar.
  986.    CommandBar_AddToolTips(g_hWndCmdBar, countof(szToolTips), szToolTips);
  987.  
  988.    // Store the height of the command bar for later calculations.
  989.    g_cyCmdBar = CommandBar_Height(g_hWndCmdBar);
  990.  
  991.    // We set our wait window handle to our menu window within our command bar.
  992.    // This is the last window that will be painted during startup of our app.
  993.    g_hWndWaitFor = GetWindow(g_hWndCmdBar, GW_CHILD);
  994.  
  995.    // Add the help item to our help menu if we have a help file.
  996.    if (fHelp) {
  997.       HMENU hMenu = GetSubMenu(CommandBar_GetMenu(g_hWndCmdBar, 0), 3);
  998.       InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  999.       InsertMenu(hMenu, 0, MF_BYPOSITION | MF_ENABLED, IDHELP, TEXT("&Help"));
  1000.    }
  1001.  
  1002. #else
  1003.  
  1004.    // Create a tool bar and add the toolbar bitmaps to it.
  1005.    g_hWndCmdBar = CreateToolbarEx(g_hWndMain, WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS,
  1006.                                   1, 9, g_hInst, IDB_TOOLBAR, tbButton,
  1007.                                   countof(tbButton), 16, 16, 16, 16,
  1008.                                   sizeof(TBBUTTON));
  1009.  
  1010.    // Get our tool tip control.
  1011.    HWND hWndTT = (HWND)SendMessage(g_hWndCmdBar, TB_GETTOOLTIPS, 0, 0);
  1012.  
  1013.    // Set our tool tip strings.
  1014.    TOOLINFO ti;
  1015.    ti.cbSize = sizeof(ti);
  1016.    int tip = 0, button;
  1017.    while (SendMessage(hWndTT, TTM_ENUMTOOLS, tip++, (LPARAM)&ti)) {
  1018.       for (button = 0; button < countof(tbButton); button++) {
  1019.          if (tbButton[button].idCommand == (int)ti.uId) {
  1020.             ti.lpszText = szToolTips[tbButton[button].iBitmap + 1];
  1021.             SendMessage(hWndTT, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  1022.             break;
  1023.          }
  1024.       }
  1025.    }
  1026.  
  1027.    // Store the height of the tool bar for later calculations.
  1028.    RECT rc;
  1029.    GetWindowRect(g_hWndCmdBar, &rc);
  1030.    g_cyCmdBar = rc.bottom - rc.top;
  1031.  
  1032.    // We set our wait window handle to our toolbar.
  1033.    // This is the last window that will be painted during the startup of our app.
  1034.    g_hWndWaitFor = g_hWndCmdBar;
  1035.  
  1036.    // Add the help item to our help menu if we have a help file.
  1037.    if (fHelp) {
  1038.       HMENU hMenu = GetSubMenu(GetMenu(g_hWndMain), 3);
  1039.       InsertMenu(hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  1040.       InsertMenu(hMenu, 0, MF_BYPOSITION | MF_ENABLED, IDHELP, TEXT("&Help\tF1"));
  1041.    }
  1042.  
  1043. #endif // _WIN32_WCE
  1044.  
  1045.    // Enable Full Row Select - This feature is supported on Windows CE and was
  1046.    // introduced to Win95/NT with IE 3.0.  If the user does not have a
  1047.    // COMCTL32.DLL that supports this feature, then they will just see the
  1048.    // old standard First Column Select.
  1049.    SendMessage(g_hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT |
  1050.                SendMessage(g_hWndList, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0));
  1051.  
  1052.    // Get our expanded view option from the registry.
  1053.    g_fExpandedView = GetOptionInt(TEXT("ExpandedView"), FALSE);
  1054.  
  1055.    // Show or remove menu check for expanded view option.
  1056.    CheckAllMenuItems(IDM_VIEW_EXPANDED_VIEW, g_fExpandedView);
  1057.  
  1058.    // Create our columns.
  1059.    AddDeleteColumns();
  1060.  
  1061.    // Set our current sort column to our name column
  1062.    Sort(0, TRUE);
  1063.  
  1064.    return 0;
  1065. }
  1066.  
  1067. //******************************************************************************
  1068. void OnFileOpen() {
  1069.  
  1070.    TCHAR szPath[_MAX_PATH] = TEXT("");
  1071.  
  1072.    OPENFILENAME ofn;
  1073.    ZeroMemory(&ofn, sizeof(ofn));
  1074.  
  1075.    ofn.lStructSize  = sizeof(ofn);
  1076.    ofn.hwndOwner    = g_hWndMain;
  1077.    ofn.hInstance    = g_hInst;
  1078.    ofn.lpstrFilter  = TEXT("ZIP files (*.zip)\0*.zip\0SFX files (*.exe)\0*.exe\0All Files (*.*)\0*.*\0");
  1079.    ofn.nFilterIndex = 1;
  1080.    ofn.lpstrFile    = szPath;
  1081.    ofn.nMaxFile     = countof(szPath);
  1082.    ofn.Flags        = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
  1083.    ofn.lpstrDefExt  = TEXT("zip");
  1084.  
  1085.    if (GetOpenFileName(&ofn)) {
  1086.       ReadZipFileList(szPath);
  1087.    }
  1088. }
  1089.  
  1090. //******************************************************************************
  1091. void OnActionView() {
  1092.  
  1093.    // We only allow a view if one item is selected.
  1094.    int count = ListView_GetSelectedCount(g_hWndList);
  1095.    if (count != 1) {
  1096.       return;
  1097.    }
  1098.  
  1099.    // Query the selected item for its FILE_NODE.
  1100.    LV_ITEM lvi;
  1101.    ZeroMemory(&lvi, sizeof(lvi));
  1102.    lvi.mask = LVIF_IMAGE | LVIF_PARAM;
  1103.    lvi.iItem = ListView_GetNextItem(g_hWndList, -1, LVNI_SELECTED);
  1104.    ListView_GetItem(g_hWndList, &lvi);
  1105.    FILE_NODE *pfn = (FILE_NODE*)lvi.lParam;
  1106.  
  1107.    // Bail out if the selected item is a folder or volume label.
  1108.    if (pfn->dwAttributes & (FILE_ATTRIBUTE_DIRECTORY | ZFILE_ATTRIBUTE_VOLUME)) {
  1109.       MessageBox(g_hWndMain, TEXT("You cannot view folders or volume labels."),
  1110.                  g_szAppName, MB_ICONINFORMATION | MB_OK);
  1111.       return;
  1112.    }
  1113.  
  1114.    // Make sure our temporary directory exists.
  1115.    CreateDirectory(g_szTempDir, NULL);
  1116.  
  1117.    TCHAR szPath[_MAX_PATH + 256];
  1118.  
  1119.    // Set our extraction directory to our temporary directory.
  1120.    if (!SetExtractToDirectory((LPTSTR)g_szTempDir)) {
  1121.  
  1122.       // Create error message.  Use szPath buffer because it is handy.
  1123.       _stprintf(szPath,
  1124.          TEXT("Could not create \"%s\"\n\n")
  1125.          TEXT("Most likely cause is that your drive is full."),
  1126.          g_szTempDir);
  1127.  
  1128.       // Display error message.
  1129.       MessageBox(g_hWndMain, szPath, g_szAppName, MB_ICONERROR | MB_OK);
  1130.  
  1131.       return;
  1132.    }
  1133.  
  1134.    // Create our single item file array.
  1135.    CHAR *argv[2] = { pfn->szPathAndMethod, NULL };
  1136.  
  1137.    // Create a buffer to store the mapped name of the file.  If the has to be
  1138.    // renamed to be compatible with our file system, then we need to know that
  1139.    // new name in order to open it correctly.
  1140.    CHAR szMappedPath[_MAX_PATH];
  1141.    *szMappedPath = '\0';
  1142.  
  1143.    // Configure our extract structure.
  1144.    EXTRACT_INFO ei;
  1145.    ZeroMemory(&ei, sizeof(ei));
  1146.    ei.fExtract      = TRUE;
  1147.    ei.dwFileCount   = 1;
  1148.    ei.uzByteCount   = pfn->uzSize;
  1149.    ei.szFileList    = argv;
  1150.    ei.fRestorePaths = FALSE;
  1151.    ei.overwriteMode = OM_PROMPT;
  1152.    ei.szMappedPath  = szMappedPath;
  1153.  
  1154.    // Clear our skipped flag and set our viewing flag.
  1155.    g_fSkipped = FALSE;
  1156.    g_fViewing = TRUE;
  1157.  
  1158.    // Display our progress dialog and do the extraction.
  1159.    DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_VIEW_PROGRESS), g_hWndMain,
  1160.                   (DLGPROC)DlgProcViewProgress, (LPARAM)&ei);
  1161.  
  1162.    // Clear our viewing flag.
  1163.    g_fViewing = FALSE;
  1164.  
  1165.    // Check to see if the user skipped the file by aborting the decryption or
  1166.    // overwrite prompts.  The only other case that causes us to skip a file
  1167.    // is when the user enters the incorrect password too many times.  In this
  1168.    // case, IZ_BADPWD will be returned.
  1169.    if (g_fSkipped) {
  1170.       return;
  1171.    }
  1172.    if (ei.result == IZ_BADPWD) {
  1173.       MessageBox(g_hWndMain, TEXT("Password was incorrect.  The file has been skipped."),
  1174.                  g_szAppName, MB_ICONWARNING | MB_OK);
  1175.       return;
  1176.    }
  1177.  
  1178.    // Check to see if the extraction failed.
  1179.    if (ei.result != PK_OK) {
  1180.  
  1181.       if (ei.result == PK_ABORTED) {
  1182.          _tcscpy(szPath, GetZipErrorString(ei.result));
  1183.  
  1184.       } else {
  1185.          // Create error message.  Use szPath buffer because it is handy.
  1186.          _stprintf(szPath,
  1187. #ifdef UNICODE
  1188.             TEXT("Could not extract \"%S\".\n\n%s\n\nTry using the Test or ")
  1189. #else
  1190.             TEXT("Could not extract \"%s\".\n\n%s\n\nTry using the Test or ")
  1191. #endif
  1192.             TEXT("Extract action on the file for more details."),
  1193.             *szMappedPath ? szMappedPath : pfn->szPathAndMethod,
  1194.             GetZipErrorString(ei.result));
  1195.       }
  1196.  
  1197.       // Display error message.
  1198.       MessageBox(g_hWndMain, szPath, g_szAppName, MB_ICONERROR | MB_OK);
  1199.  
  1200.       // If we managed to create a bad file, then delete it.
  1201.       if (*szMappedPath) {
  1202.          MBSTOTSTR(szPath, szMappedPath, countof(szPath));
  1203.          SetFileAttributes(szPath, FILE_ATTRIBUTE_NORMAL);
  1204.          if (!DeleteFile(szPath)) {
  1205.             SetFileAttributes(szPath, FILE_ATTRIBUTE_READONLY);
  1206.          }
  1207.       }
  1208.  
  1209.       return;
  1210.    }
  1211.  
  1212.    // Convert the file name to UNICODE.
  1213.    MBSTOTSTR(szPath, szMappedPath, countof(szPath));
  1214.  
  1215.    // Prepare to launch the file.
  1216.    SHELLEXECUTEINFO sei;
  1217.    ZeroMemory(&sei, sizeof(sei));
  1218.    sei.cbSize      = sizeof(sei);
  1219.    sei.hwnd        = g_hWndMain;
  1220.    sei.lpDirectory = g_szTempDir;
  1221.    sei.nShow       = SW_SHOWNORMAL;
  1222.  
  1223. #ifdef _WIN32_WCE
  1224.  
  1225.    TCHAR szApp[_MAX_PATH];
  1226.  
  1227.    // On Windows CE, there is no default file association dialog that appears
  1228.    // when ShellExecuteEx() is given an unknown file type.  We check to see if
  1229.    // file is unknown, and display our own file association prompt.
  1230.  
  1231.    // Check our file image to see if this file has no associated viewer.
  1232.    if (lvi.iImage == IMAGE_GENERIC) {
  1233.  
  1234.       // Display our file association prompt dialog.
  1235.       if (IDOK != DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_VIEW_ASSOCIATION),
  1236.                                  g_hWndMain, (DLGPROC)DlgProcViewAssociation,
  1237.                                  (LPARAM)szApp))
  1238.       {
  1239.          // If the user aborted the association prompt, then delete file and exit.
  1240.          SetFileAttributes(szPath, FILE_ATTRIBUTE_NORMAL);
  1241.          if (!DeleteFile(szPath)) {
  1242.             SetFileAttributes(szPath, FILE_ATTRIBUTE_READONLY);
  1243.          }
  1244.          return;
  1245.       }
  1246.       // Set the file to be the viewer app and the parameters to be the file.
  1247.       // Note: Some applications require that arguments with spaces be quoted,
  1248.       // while other applications choked when quotes we part of the filename.
  1249.       // In the end, it seems safer to leave the quotes off.
  1250.       sei.lpFile = szApp;
  1251.       sei.lpParameters = szPath;
  1252.    } else {
  1253.       sei.lpFile = szPath;
  1254.    }
  1255.  
  1256. #else
  1257.  
  1258.    // On NT, ShellExecuteEx() will prompt user for association if needed.
  1259.    sei.lpFile = szPath;
  1260.  
  1261. #endif
  1262.  
  1263.    // Launch the file.  All errors will be displayed by ShellExecuteEx().
  1264.    ShellExecuteEx(&sei);
  1265. }
  1266.  
  1267. //******************************************************************************
  1268. void OnActionSelectAll() {
  1269.    for (int i = ListView_GetItemCount(g_hWndList) - 1; i >= 0; i--) {
  1270.       ListView_SetItemState(g_hWndList, i, LVIS_SELECTED, LVIS_SELECTED);
  1271.    }
  1272. }
  1273.  
  1274. //******************************************************************************
  1275. void OnViewExpandedView() {
  1276.  
  1277.    // Toggle our expanded view option.
  1278.    g_fExpandedView = !g_fExpandedView;
  1279.  
  1280.    // Show or remove menu check and toolbar button press.
  1281.    CheckAllMenuItems(IDM_VIEW_EXPANDED_VIEW, g_fExpandedView);
  1282.  
  1283.    // Display the new columns.
  1284.    AddDeleteColumns();
  1285.  
  1286.    // Re-sort if we just did away with out sort column.
  1287.    if (!g_fExpandedView && (g_sortColumn > 3)) {
  1288.       Sort(0, TRUE);
  1289.    }
  1290.  
  1291.    // Write our expanded view option to the registry.
  1292.    WriteOptionInt(TEXT("ExpandedView"), g_fExpandedView);
  1293. }
  1294.  
  1295. //******************************************************************************
  1296. void OnHelp() {
  1297.  
  1298.    // Prepare to launch the help file.
  1299.    SHELLEXECUTEINFO sei;
  1300.    ZeroMemory(&sei, sizeof(sei));
  1301.    sei.cbSize      = sizeof(sei);
  1302.    sei.hwnd        = g_hWndMain;
  1303.    sei.lpFile      = g_szHelpFile;
  1304.  
  1305.    // Launch the file.
  1306.    ShellExecuteEx(&sei);
  1307. }
  1308.  
  1309.  
  1310. //******************************************************************************
  1311. //***** Event Handlers for our List View
  1312. //******************************************************************************
  1313.  
  1314. void OnGetDispInfo(LV_DISPINFO *plvdi) {
  1315.  
  1316.    // Make sure we have the minimum amount of data to process this event.
  1317.    if ((plvdi->item.iItem < 0) || !plvdi->item.lParam || !plvdi->item.pszText) {
  1318.       return;
  1319.    }
  1320.  
  1321.    // Get a pointer to the file node for this item.
  1322.    FILE_NODE *pFile = (FILE_NODE*)plvdi->item.lParam;
  1323.  
  1324.    CHAR szBuffer[_MAX_PATH * 2];
  1325.  
  1326.    switch (plvdi->item.iSubItem) {
  1327.  
  1328.       case 0: // Name
  1329.  
  1330.          // Copy the string to a temporary buffer.
  1331.          strcpy(szBuffer, pFile->szPathAndMethod);
  1332.  
  1333.          // Change all forward slashes to back slashes in the buffer
  1334.          ForwardSlashesToBackSlashesA(szBuffer);
  1335.  
  1336.          // Convert the string to UNICODE and store it in our list control.
  1337.          MBSTOTSTR(plvdi->item.pszText, szBuffer, plvdi->item.cchTextMax);
  1338.  
  1339.          return;
  1340.  
  1341.       case 1: // Size
  1342.          FormatValue(plvdi->item.pszText, pFile->uzSize);
  1343.          return;
  1344.  
  1345.       case 2: // Type
  1346.          MBSTOTSTR(plvdi->item.pszText, BuildTypeString(pFile, szBuffer),
  1347.                   plvdi->item.cchTextMax);
  1348.          return;
  1349.  
  1350.       case 3: // Modified
  1351.          int hour; hour = (pFile->dwModified >> 6) & 0x001F;
  1352.          _stprintf(plvdi->item.pszText, TEXT("%u/%u/%u %u:%02u %cM"),
  1353.                    (pFile->dwModified  >> 16) & 0x000F,
  1354.                    (pFile->dwModified  >> 11) & 0x001F,
  1355.                    ((pFile->dwModified >> 20) & 0x0FFF) % 100,
  1356.                    (hour % 12) ? (hour % 12) : 12,
  1357.                    pFile->dwModified & 0x003F,
  1358.                    hour >= 12 ? 'P' : 'A');
  1359.          return;
  1360.  
  1361.       case 4: // Attributes
  1362.          BuildAttributesString(plvdi->item.pszText, pFile->dwAttributes);
  1363.          return;
  1364.  
  1365.       case 5: // Compressed
  1366.          FormatValue(plvdi->item.pszText, pFile->uzCompressedSize);
  1367.          return;
  1368.  
  1369.       case 6: // Ratio
  1370.          int factor; factor = ratio(pFile->uzSize, pFile->uzCompressedSize);
  1371.          _stprintf(plvdi->item.pszText, TEXT("%d.%d%%"), factor / 10,
  1372.                    ((factor < 0) ? -factor : factor) % 10);
  1373.          return;
  1374.  
  1375.       case 7: // Method
  1376.          MBSTOTSTR(plvdi->item.pszText, pFile->szPathAndMethod + strlen(pFile->szPathAndMethod) + 1,
  1377.                   plvdi->item.cchTextMax);
  1378.          return;
  1379.  
  1380.       case 8: // CRC
  1381.          _stprintf(plvdi->item.pszText, TEXT("%08X"), pFile->dwCRC);
  1382.          return;
  1383.  
  1384.       case 9: // Comment
  1385.          MBSTOTSTR(plvdi->item.pszText, pFile->szComment ? pFile->szComment : "",
  1386.                    plvdi->item.cchTextMax);
  1387.          return;
  1388.    }
  1389. }
  1390.  
  1391. //******************************************************************************
  1392. void OnDeleteItem(NM_LISTVIEW *pnmlv) {
  1393.    if (pnmlv->lParam) {
  1394.  
  1395.       // Free any comment string associated with this item.
  1396.       if (((FILE_NODE*)pnmlv->lParam)->szComment) {
  1397.          delete[] (CHAR*)((FILE_NODE*)pnmlv->lParam)->szComment;
  1398.       }
  1399.  
  1400.       // Free the item itself.
  1401.       delete[] (LPBYTE)pnmlv->lParam;
  1402.    }
  1403. }
  1404.  
  1405. //******************************************************************************
  1406. void OnItemChanged(NM_LISTVIEW *pnmlv) {
  1407.    int count = ListView_GetSelectedCount(pnmlv->hdr.hwndFrom);
  1408.    EnableAllMenuItems(IDM_FILE_PROPERTIES, count > 0);
  1409.    EnableAllMenuItems(IDM_ACTION_EXTRACT,  count > 0);
  1410.    EnableAllMenuItems(IDM_ACTION_TEST,     count > 0);
  1411.    EnableAllMenuItems(IDM_ACTION_VIEW,     count == 1);
  1412. }
  1413.  
  1414. //******************************************************************************
  1415. //***** List View Sort Functions
  1416. //******************************************************************************
  1417.  
  1418. void Sort(int sortColumn, BOOL fForce) {
  1419.  
  1420.    // Do not change the column header text if it is already correct.
  1421.    if (sortColumn != g_sortColumn) {
  1422.  
  1423.       TCHAR szColumn[32];
  1424.       LV_COLUMN lvc;
  1425.       lvc.mask = LVCF_TEXT;
  1426.       lvc.pszText = szColumn;
  1427.  
  1428.       // Remove the '^' from the current sort column.
  1429.       if (g_sortColumn != -1) {
  1430.          _stprintf(szColumn, (g_columns[g_sortColumn].format == LVCFMT_LEFT) ?
  1431.                    TEXT("%s   ") : TEXT("   %s"), g_columns[g_sortColumn].szName);
  1432.          ListView_SetColumn(g_hWndList, g_sortColumn, &lvc);
  1433.       }
  1434.  
  1435.       // Set the new sort column.
  1436.       g_sortColumn = sortColumn;
  1437.  
  1438.       // Add the '^' to the new sort column.
  1439.       _stprintf(szColumn, (g_columns[g_sortColumn].format == LVCFMT_LEFT) ?
  1440.                 TEXT("%s ^") : TEXT("^ %s"), g_columns[g_sortColumn].szName);
  1441.       ListView_SetColumn(g_hWndList, g_sortColumn, &lvc);
  1442.  
  1443.       // Sort the list by the new column.
  1444.       ListView_SortItems(g_hWndList, CompareFunc, g_sortColumn);
  1445.  
  1446.    } else if (fForce) {
  1447.       // Force the list to sort by the same column.
  1448.       ListView_SortItems(g_hWndList, CompareFunc, g_sortColumn);
  1449.    }
  1450. }
  1451.  
  1452. //******************************************************************************
  1453. int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM sortColumn) {
  1454.    FILE_NODE *pFile1 = (FILE_NODE*)lParam1, *pFile2 = (FILE_NODE*)lParam2;
  1455.    TCHAR szBuffer1[8], szBuffer2[8];
  1456.  
  1457.    // Return Negative value if the first item should precede the second.
  1458.    // Return Positive value if the first item should follow the second.
  1459.    // Return Zero if the two items are equivalent.
  1460.  
  1461.    int result = 0;
  1462.  
  1463.    // Compute the relationship based on the current sort column
  1464.    switch (sortColumn) {
  1465.  
  1466.       case 1: // Size - Smallest to Largest
  1467.          if (pFile1->uzSize != pFile2->uzSize) {
  1468.             result = ((pFile1->uzSize < pFile2->uzSize) ? -1 : 1);
  1469.          }
  1470.          break;
  1471.  
  1472.       case 2: { // Type - Volume Label's first, then directories, then files
  1473.          int f1 = (pFile1->dwAttributes & ZFILE_ATTRIBUTE_VOLUME)   ? 1 :
  1474.                   (pFile1->dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 2 : 3;
  1475.          int f2 = (pFile2->dwAttributes & ZFILE_ATTRIBUTE_VOLUME)   ? 1 :
  1476.                   (pFile2->dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 2 : 3;
  1477.          if ((f1 == 3) && (f2 == 3)) {
  1478.             CHAR szType1[128];
  1479.             CHAR szType2[128];
  1480.             result = _stricmp(BuildTypeString(pFile1, szType1),
  1481.                               BuildTypeString(pFile2, szType2));
  1482.          } else {
  1483.             result = f1 - f2;
  1484.          }
  1485.          break;
  1486.       }
  1487.  
  1488.       case 3: // Modified - Newest to Oldest
  1489.          if (pFile1->dwModified != pFile2->dwModified) {
  1490.             result = ((pFile1->dwModified > pFile2->dwModified) ? -1 : 1);
  1491.          }
  1492.          break;
  1493.  
  1494.       case 4: // Attributes - String Sort
  1495.          result = _tcscmp(BuildAttributesString(szBuffer1, pFile1->dwAttributes),
  1496.                           BuildAttributesString(szBuffer2, pFile2->dwAttributes));
  1497.          break;
  1498.  
  1499.       case 5: // Compressed Size - Smallest to Largest
  1500.          if (pFile1->uzCompressedSize != pFile2->uzCompressedSize) {
  1501.             result = ((pFile1->uzCompressedSize < pFile2->uzCompressedSize) ? -1 : 1);
  1502.          }
  1503.          break;
  1504.  
  1505.       case 6: // Ratio - Smallest to Largest
  1506.          int factor1, factor2;
  1507.          factor1 = ratio(pFile1->uzSize, pFile1->uzCompressedSize);
  1508.          factor2 = ratio(pFile2->uzSize, pFile2->uzCompressedSize);
  1509.          result = factor1 - factor2;
  1510.          break;
  1511.  
  1512.       case 7: // Method - String Sort
  1513.          result = _stricmp(pFile1->szPathAndMethod + strlen(pFile1->szPathAndMethod) + 1,
  1514.                            pFile2->szPathAndMethod + strlen(pFile2->szPathAndMethod) + 1);
  1515.          break;
  1516.  
  1517.       case 8: // CRC - Smallest to Largest
  1518.          if (pFile1->dwCRC != pFile2->dwCRC) {
  1519.             result = ((pFile1->dwCRC < pFile2->dwCRC) ? -1 : 1);
  1520.          }
  1521.          break;
  1522.  
  1523.       case 9: // Comment - String Sort
  1524.          result = _stricmp(pFile1->szComment ? pFile1->szComment : "",
  1525.                            pFile2->szComment ? pFile2->szComment : "");
  1526.          break;
  1527.    }
  1528.  
  1529.    // If the sort resulted in a tie, we use the name to break the tie.
  1530.    if (result == 0) {
  1531.       result = _stricmp(pFile1->szPathAndMethod, pFile2->szPathAndMethod);
  1532.    }
  1533.  
  1534.    return result;
  1535. }
  1536.  
  1537.  
  1538. //******************************************************************************
  1539. //***** Helper/Utility Functions
  1540. //******************************************************************************
  1541.  
  1542. void SetCaptionText(LPCTSTR szPrefix) {
  1543.    TCHAR szCaption[_MAX_PATH + 32];
  1544.    if (szPrefix) {
  1545.       _stprintf(szCaption, TEXT("%s - "), szPrefix);
  1546.    } else {
  1547.       *szCaption = 0;
  1548.    }
  1549.    if (*g_szZipFile) {
  1550.       size_t lenPrefix = _tcslen(szCaption);
  1551.       MBSTOTSTR(szCaption + lenPrefix, GetFileFromPath(g_szZipFile),
  1552.                 countof(szCaption) - lenPrefix);
  1553.    } else {
  1554.       _tcscat(szCaption, TEXT("Pocket UnZip"));
  1555.    }
  1556.    SetWindowText(g_hWndMain, szCaption);
  1557. }
  1558.  
  1559. //******************************************************************************
  1560. void DrawBanner(HDC hdc) {
  1561.  
  1562.    // If we were not passed in a DC, then get one now.
  1563.    BOOL fReleaseDC = FALSE;
  1564.    if (!hdc) {
  1565.       hdc = GetDC(g_hWndMain);
  1566.       fReleaseDC = TRUE;
  1567.    }
  1568.  
  1569.    // Compute the banner rectangle.
  1570.    RECT rc;
  1571.    GetClientRect(g_hWndMain, &rc);
  1572.    rc.top += g_cyCmdBar;
  1573.    rc.bottom = rc.top + 22;
  1574.  
  1575.    // Fill in the background with a light grey brush.
  1576.    FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
  1577.  
  1578.    // Draw a highlight line across the top of our banner.
  1579.    POINT pt[2] = { { rc.left, rc.top + 1 }, { rc.right, rc.top + 1 } };
  1580.  
  1581.    SelectObject(hdc, GetStockObject(WHITE_PEN));
  1582.    Polyline(hdc, pt, 2);
  1583.  
  1584.    // Get the ZIP file image.  We do this only once and cache the result.
  1585.    // Note that you do not need to free icons as they are a resource.
  1586.    static HICON hIcon = NULL;
  1587.    if (!hIcon) {
  1588.       hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_ZIPFILE),
  1589.                                IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
  1590.    }
  1591.  
  1592.    // Draw the ZIP file image.
  1593.    DrawIconEx(hdc, rc.left + 6, rc.top + 3, hIcon, 16, 16, 0, NULL, DI_NORMAL);
  1594.  
  1595.    // Set our font and colors.
  1596.    HFONT hFontStock = (HFONT)SelectObject(hdc, g_hFontBanner);
  1597.    SetTextColor(hdc, RGB(0, 0, 0));
  1598.    SetBkMode(hdc, TRANSPARENT);
  1599.  
  1600.    rc.left   += 26;
  1601.    rc.right  -= 48;
  1602.    rc.bottom -=  2;
  1603.  
  1604.    // Decide what text to display.
  1605.    TCHAR szPath[_MAX_PATH + 16];
  1606.    if (g_hWndWaitFor) {
  1607.       _tcscpy(szPath, TEXT("Initializing..."));
  1608.    } else if (*g_szZipFile) {
  1609.       if (g_fLoading) {
  1610. #ifdef UNICODE
  1611.          _stprintf(szPath, TEXT("Loading %S"), g_szZipFile);
  1612. #else
  1613.          _stprintf(szPath, TEXT("Loading %s"), g_szZipFile);
  1614. #endif
  1615.       } else {
  1616.          MBSTOTSTR(szPath, g_szZipFile, countof(szPath));
  1617.       }
  1618.    } else {
  1619.       _tcscpy(szPath, TEXT("No File Loaded"));
  1620.    }
  1621.  
  1622.    // Draw the banner text.
  1623.    DrawText(hdc, szPath, _tcslen(szPath), &rc,
  1624.             DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER);
  1625.  
  1626.    // Remove all non stock objects from the DC
  1627.    SelectObject(hdc, hFontStock);
  1628.  
  1629.    // Free our DC if we created it.
  1630.    if (fReleaseDC) {
  1631.       ReleaseDC(g_hWndMain, hdc);
  1632.    }
  1633. }
  1634.  
  1635. //******************************************************************************
  1636. void AddDeleteColumns() {
  1637.  
  1638.    static int curColumns = 0;
  1639.    int column, newColumns = (g_fExpandedView ? countof(g_columns) : 4);
  1640.  
  1641.    // Are we adding columns?
  1642.    if (newColumns > curColumns) {
  1643.  
  1644.       // Set up column structure.
  1645.       TCHAR szColumn[32];
  1646.       LV_COLUMN lvc;
  1647.       lvc.mask = LVCF_TEXT | LVCF_FMT;
  1648.       lvc.pszText = szColumn;
  1649.  
  1650.       // Loop through each column we need to add.
  1651.       for (column = curColumns; column < newColumns; column++) {
  1652.  
  1653.          // Build the real column string.
  1654.          _stprintf(szColumn, (g_columns[column].format == LVCFMT_LEFT) ?
  1655.                    TEXT("%s   ") : TEXT("   %s"), g_columns[column].szName);
  1656.  
  1657.          // Insert the column with the correct format.
  1658.          lvc.fmt = g_columns[column].format;
  1659.          ListView_InsertColumn(g_hWndList, column, &lvc);
  1660.       }
  1661.  
  1662.    // Otherwise, we are removing columns.
  1663.    } else {
  1664.  
  1665.       // Loop through each column we need to delete and delete them.
  1666.       for (column = curColumns - 1; column >= newColumns; column--) {
  1667.          ListView_DeleteColumn(g_hWndList, column);
  1668.       }
  1669.    }
  1670.  
  1671.    // Store our new column count statically to help us with the next call to
  1672.    // AddDeleteColumns().
  1673.    curColumns = newColumns;
  1674.  
  1675.    // Re-calcualte our column widths.
  1676.    ResizeColumns();
  1677. }
  1678.  
  1679. //******************************************************************************
  1680. void ResizeColumns() {
  1681.  
  1682.    // Hide the window since we are going to be doing some column shifting.
  1683.    ShowWindow(g_hWndList, SW_HIDE);
  1684.  
  1685.    // Resize all the columns to best fit both the column data and the header.
  1686.    for (int column = 0; column < countof(g_columns); column++) {
  1687.       ListView_SetColumnWidth(g_hWndList, column, LVSCW_AUTOSIZE_USEHEADER);
  1688.    }
  1689.  
  1690.    // Show the window again.
  1691.    ShowWindow(g_hWndList, SW_SHOW);
  1692. }
  1693.  
  1694. //******************************************************************************
  1695. LPCTSTR GetZipErrorString(int error) {
  1696.  
  1697.    switch (error) {
  1698.  
  1699.       case PK_OK: // no error
  1700.          return TEXT("Operation completed successfully.");
  1701.  
  1702.       case PK_WARN: // warning error
  1703.          return TEXT("There were warnings during the operation.");
  1704.  
  1705.       case PK_ERR:    // error in zipfile
  1706.       case PK_BADERR: // severe error in zipfile
  1707.          return TEXT("The operation could not be successfully completed.  ")
  1708.                 TEXT("Possible causes are that the ZIP file contains errors, ")
  1709.                 TEXT("or that an error occurred while trying to create a ")
  1710.                 TEXT("directory or file.");
  1711.  
  1712.       case PK_MEM:  // insufficient memory
  1713.       case PK_MEM2: // insufficient memory
  1714.       case PK_MEM3: // insufficient memory
  1715.       case PK_MEM4: // insufficient memory
  1716.       case PK_MEM5: // insufficient memory
  1717.          return TEXT("There is not enough memory to perform the operation.  ")
  1718.                 TEXT("Try closing other running applications or adjust your ")
  1719.                 TEXT("memory configuration.");
  1720.  
  1721.       case PK_NOZIP: // zipfile not found or corrupt.
  1722.          return TEXT("The ZIP file either contains errors or could not be found.");
  1723.  
  1724.       case PK_PARAM: // bad or illegal parameters specified
  1725.          break; // Not used in the Windows CE port.
  1726.  
  1727.       case PK_FIND: // no files found in ZIP file
  1728.          return TEXT("The ZIP file contains errors that prevented the ")
  1729.                 TEXT("operation from completing successfully.  A possible ")
  1730.                 TEXT("cause is that one or more of the files listed as being ")
  1731.                 TEXT("in the ZIP file could not actually be found within the ")
  1732.                 TEXT("ZIP file itself.");
  1733.  
  1734.       case PK_DISK: // disk full or file locked
  1735.          return TEXT("An error occurred while attempting to save a file.  ")
  1736.                 TEXT("Possible causes are that your file storage is full or ")
  1737.                 TEXT("read only, or that a file with the same name already ")
  1738.                 TEXT("exists and is locked by another application.");
  1739.  
  1740.       case PK_EOF: // unexpected end of file
  1741.          return TEXT("The ZIP file contains errors that prevented the ")
  1742.                 TEXT("operation from completing successfully.  A possible ")
  1743.                 TEXT("cause is that your ZIP file is incomplete and might be ")
  1744.                 TEXT("truncated.");
  1745.  
  1746.       case IZ_UNSUP:  // no files found: all unsup. compr/encrypt.
  1747.          return TEXT("None of the files could be processed because they were ")
  1748.                 TEXT("all compressed using an unsupported compression or ")
  1749.                 TEXT("encryption algorithm.");
  1750.  
  1751.       case IZ_BADPWD: // no files found: all had bad password.
  1752.          return TEXT("None of the files could be processed because all the ")
  1753.                 TEXT("password(s) specified were incorrect.");
  1754.  
  1755.       case PK_EXCEPTION: // exception occurred
  1756.          return TEXT("An internal error occurred.  Possible causes are that ")
  1757.                 TEXT("you are out of memory, you are out of file storage ")
  1758.                 TEXT("space, the ZIP file contains unexpected errors, or there ")
  1759.                 TEXT("is a bug in our program (that's why it's free).");
  1760.  
  1761.       case IZ_CTRLC:  // canceled by user's interaction
  1762.       case PK_ABORTED: // user aborted
  1763.          return TEXT("The operation was aborted.");
  1764.    }
  1765.  
  1766.    return TEXT("An unknown error occurred while processing the ZIP file.");
  1767. }
  1768.  
  1769. //******************************************************************************
  1770. void AddFileToListView(FILE_NODE *pFile) {
  1771.  
  1772.    // Set up our List View Item structure.
  1773.    LV_ITEM lvi;
  1774.    ZeroMemory(&lvi, sizeof(lvi));
  1775.    lvi.mask    = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  1776.    lvi.pszText = LPSTR_TEXTCALLBACK;
  1777.    lvi.lParam  = (LPARAM)pFile;
  1778.    lvi.iImage  = IMAGE_GENERIC;
  1779.  
  1780.    // Special case Volume Labels.
  1781.    if (pFile->dwAttributes & ZFILE_ATTRIBUTE_VOLUME) {
  1782.       pFile->szType = "Volume Label";
  1783.       lvi.iImage = IMAGE_VOLUME;
  1784.  
  1785.    // Special case folders.
  1786.    } else if (pFile->dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1787.       pFile->szType = "Folder";
  1788.       lvi.iImage = IMAGE_FOLDER;
  1789.  
  1790.    // Do a lookup on the file extension.
  1791.    } else {
  1792.  
  1793.       // Locate the file portion of our path.
  1794.       LPCSTR pszFile = GetFileFromPath(pFile->szPathAndMethod);
  1795.  
  1796.       // Find the extension portion of our file.
  1797.       LPCSTR pszExt = MBSRCHR(pszFile, '.');
  1798.  
  1799.       // Search our known extension list for this extension.
  1800.       if (pszExt && *(pszExt + 1)) {
  1801.  
  1802.          // Loop through our linked list
  1803.          for (FILE_TYPE_NODE *pft = g_pftHead; pft; pft = pft->pNext) {
  1804.  
  1805.             // Check for a match.
  1806.             if (!_stricmp(pszExt + 1, pft->szExtAndDesc)) {
  1807.  
  1808.                // We found a match, store the image and type string and exit loop.
  1809.                lvi.iImage = pft->image;
  1810.                pFile->szType = pft->szExtAndDesc + strlen(pft->szExtAndDesc) + 1;
  1811.                if (!*pFile->szType) {
  1812.                   pFile->szType = NULL;
  1813.                }
  1814.                break;
  1815.             }
  1816.          }
  1817.       }
  1818.    }
  1819.  
  1820.    // Add the item to our list.
  1821.    ListView_InsertItem(g_hWndList, &lvi);
  1822. }
  1823.  
  1824. //******************************************************************************
  1825. void EnableAllMenuItems(UINT uMenuItem, BOOL fEnabled) {
  1826. #ifdef _WIN32_WCE
  1827.    HMENU hMenu = CommandBar_GetMenu(g_hWndCmdBar, 0);
  1828. #else
  1829.    HMENU hMenu = GetMenu(g_hWndMain);
  1830. #endif
  1831.    EnableMenuItem(hMenu, uMenuItem, fEnabled ? MF_ENABLED : MF_GRAYED);
  1832.    SendMessage(g_hWndCmdBar, TB_ENABLEBUTTON, uMenuItem, MAKELONG(fEnabled, 0));
  1833. }
  1834.  
  1835. //******************************************************************************
  1836. void CheckAllMenuItems(UINT uMenuItem, BOOL fChecked) {
  1837. #ifdef _WIN32_WCE
  1838.    HMENU hMenu = CommandBar_GetMenu(g_hWndCmdBar, 0);
  1839. #else
  1840.    HMENU hMenu = GetMenu(g_hWndMain);
  1841. #endif
  1842.    CheckMenuItem(hMenu, uMenuItem, fChecked ? MF_CHECKED : MF_UNCHECKED);
  1843.    SendMessage(g_hWndCmdBar, TB_PRESSBUTTON, uMenuItem, MAKELONG(fChecked, 0));
  1844. }
  1845.  
  1846. //******************************************************************************
  1847. void CenterWindow(HWND hWnd) {
  1848.  
  1849.    RECT rc, rcParent;
  1850.  
  1851.    // Get our window rectangle.
  1852.    GetWindowRect(hWnd, &rc);
  1853.  
  1854.    // Get our parent's window rectangle.
  1855.    GetWindowRect(GetParent(hWnd), &rcParent);
  1856.  
  1857.    // Center our window over our parent's window.
  1858.    SetWindowPos(hWnd, NULL,
  1859.       rcParent.left + ((rcParent.right  - rcParent.left) - (rc.right  - rc.left)) / 2,
  1860.       rcParent.top  + ((rcParent.bottom - rcParent.top ) - (rc.bottom - rc.top )) / 2,
  1861.       0, 0, SWP_NOZORDER | SWP_NOSIZE);
  1862. }
  1863.  
  1864. //******************************************************************************
  1865. void AddTextToEdit(LPCSTR szText) {
  1866.  
  1867.    if (!g_hWndEdit) {
  1868.       return;
  1869.    }
  1870.  
  1871.    // Add the characters one by one to our edit box while performing the
  1872.    // the following newline conversions:
  1873.    //    Single CR -> CR/LF
  1874.    //    Single LF -> CR/LF
  1875.    //    CR and LF -> CR/LF
  1876.    //    LF and CR -> CR/LF
  1877.    //    0 - 31    -> ^char
  1878.  
  1879.    TCHAR szOut[256], *pszOut = szOut;
  1880.    CHAR *pszIn = (LPSTR)szText, cPrev = '\0';
  1881.  
  1882.    while (*pszIn) {
  1883.  
  1884.       if (*pszIn == '\n') {
  1885.          if (cPrev == '\r') {
  1886.             cPrev = '\0';
  1887.          } else {
  1888.             *(pszOut++) = TEXT('\r');
  1889.             *(pszOut++) = TEXT('\n');
  1890.             cPrev = '\n';
  1891.          }
  1892.  
  1893.       } else if (*pszIn == '\r') {
  1894.          if (cPrev == '\n') {
  1895.             cPrev = '\0';
  1896.          } else {
  1897.             *(pszOut++) = TEXT('\r');
  1898.             *(pszOut++) = TEXT('\n');
  1899.             cPrev = '\r';
  1900.          }
  1901.  
  1902.       } else if ((*pszIn < 32) && (*pszIn != '\t')) {
  1903.          *(pszOut++) = (TCHAR)'^';
  1904.          *(pszOut++) = (TCHAR)(64 + *pszIn);
  1905.          cPrev = *pszIn;
  1906.  
  1907.       } else {
  1908.          *(pszOut++) = (TCHAR)*pszIn;
  1909.          cPrev = *pszIn;
  1910.       }
  1911.       pszIn++;
  1912.  
  1913.       // If our out buffer is full, then dump it to the edit box.
  1914.       if ((pszOut - szOut) > 253) {
  1915.          *pszOut = TEXT('\0');
  1916.          SendMessage(g_hWndEdit, EM_SETSEL, -1, -1);
  1917.          SendMessage(g_hWndEdit, EM_REPLACESEL, FALSE, (LPARAM)szOut);
  1918.          pszOut = szOut;
  1919.       }
  1920.    }
  1921.  
  1922.    // One final flush of any partially full out buffer.
  1923.    if (pszOut > szOut) {
  1924.       *pszOut = TEXT('\0');
  1925.       SendMessage(g_hWndEdit, EM_SETSEL, -1, -1);
  1926.       SendMessage(g_hWndEdit, EM_REPLACESEL, FALSE, (LPARAM)szOut);
  1927.    }
  1928. }
  1929.  
  1930. //******************************************************************************
  1931. LPTSTR FormatValue(LPTSTR szValue, zusz_t uzValue) {
  1932. #ifdef ZIP64_SUPPORT
  1933.     DWORD dw = 0, dwGroup[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  1934. #else
  1935.     DWORD dw = 0, dwGroup[4] = { 0, 0, 0, 0 };
  1936. #endif
  1937.    while (uzValue) {
  1938.       dwGroup[dw++] = (DWORD)(uzValue % 1000);
  1939.       uzValue /= 1000;
  1940.    }
  1941.    switch (dw) {
  1942.       case 2:  _stprintf(szValue, TEXT("%u,%03u"), dwGroup[1], dwGroup[0]); break;
  1943.       case 3:  _stprintf(szValue, TEXT("%u,%03u,%03u"), dwGroup[2], dwGroup[1], dwGroup[0]); break;
  1944.       case 4:  _stprintf(szValue, TEXT("%u,%03u,%03u,%03u"), dwGroup[3], dwGroup[2], dwGroup[1], dwGroup[0]); break;
  1945. #ifdef ZIP64_SUPPORT
  1946.       case 5:
  1947.           _stprintf(szValue, TEXT("%u,%03u,%03u,%03u,%03u"),
  1948.                     dwGroup[4], dwGroup[3], dwGroup[2], dwGroup[1], dwGroup[0]);
  1949.           break;
  1950.       case 6:
  1951.           _stprintf(szValue, TEXT("%u,%03u,%03u,%03u,%03u,%03u"), dwGroup[5],
  1952.                     dwGroup[4], dwGroup[3], dwGroup[2], dwGroup[1], dwGroup[0]);
  1953.           break;
  1954.       case 7:
  1955.           _stprintf(szValue, TEXT("%u,%03u,%03u,%03u,%03u,%03u,%03u"), dwGroup[6], dwGroup[5],
  1956.                     dwGroup[4], dwGroup[3], dwGroup[2], dwGroup[1], dwGroup[0]);
  1957.           break;
  1958.       case 8:
  1959.           _stprintf(szValue, TEXT("%u,%03u,%03u,%03u,%03u,%03u,%03u,%03u"), dwGroup[7], dwGroup[6], dwGroup[5],
  1960.                     dwGroup[4], dwGroup[3], dwGroup[2], dwGroup[1], dwGroup[0]);
  1961. #endif
  1962.       default: _stprintf(szValue, TEXT("%u"), dwGroup[0]);
  1963.    }
  1964.    return szValue;
  1965. }
  1966.  
  1967. //******************************************************************************
  1968. LPTSTR BuildAttributesString(LPTSTR szBuffer, DWORD dwAttributes) {
  1969.    // Build the attribute string according to the flags specified for this file.
  1970.    _stprintf(szBuffer, TEXT("%s%s%s%s%s%s%s%s"),
  1971.              (dwAttributes & ZFILE_ATTRIBUTE_VOLUME)    ? TEXT("V") : TEXT(""),
  1972.              (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)  ? TEXT("D") : TEXT(""),
  1973.              (dwAttributes & FILE_ATTRIBUTE_READONLY)   ? TEXT("R") : TEXT(""),
  1974.              (dwAttributes & FILE_ATTRIBUTE_ARCHIVE)    ? TEXT("A") : TEXT(""),
  1975.              (dwAttributes & FILE_ATTRIBUTE_HIDDEN)     ? TEXT("H") : TEXT(""),
  1976.              (dwAttributes & FILE_ATTRIBUTE_SYSTEM)     ? TEXT("S") : TEXT(""),
  1977.              (dwAttributes & ZFILE_ATTRIBUTE_ENCRYPTED) ? TEXT("E") : TEXT(""),
  1978.              (dwAttributes & ZFILE_ATTRIBUTE_COMMENT)   ? TEXT("C") : TEXT(""));
  1979.    return szBuffer;
  1980. }
  1981.  
  1982. //******************************************************************************
  1983. LPCSTR BuildTypeString(FILE_NODE *pFile, LPSTR szType) {
  1984.  
  1985.    // First check to see if we have a known description.
  1986.    if (pFile->szType) {
  1987.       return pFile->szType;
  1988.    }
  1989.  
  1990.    // Locate the file portion of our path.
  1991.    LPCSTR pszFile = GetFileFromPath(pFile->szPathAndMethod);
  1992.  
  1993.    // Get the extension portion of the file.
  1994.    LPCSTR pszExt = MBSRCHR(pszFile, '.');
  1995.  
  1996.    // If we have an extension create a type name for this file.
  1997.    if (pszExt && *(pszExt + 1)) {
  1998.       strcpy(szType, pszExt + 1);
  1999.       _strupr(szType);
  2000.       strcat(szType, " File");
  2001.       return szType;
  2002.    }
  2003.  
  2004.    // If no extension, then use the default "File".
  2005.    return "File";
  2006. }
  2007.  
  2008. //******************************************************************************
  2009. LPCSTR GetFileFromPath(LPCSTR szPath) {
  2010.    LPCSTR p1 = MBSRCHR(szPath, '/'), p2 = MBSRCHR(szPath, '\\');
  2011.    if (p1 && (p1 > p2)) {
  2012.       return p1 + 1;
  2013.    } else if (p2) {
  2014.       return p2 + 1;
  2015.    }
  2016.    return szPath;
  2017. }
  2018.  
  2019. //******************************************************************************
  2020. void ForwardSlashesToBackSlashesA(LPSTR szBuffer) {
  2021.    while (*szBuffer) {
  2022.       if (*szBuffer == '/') {
  2023.          *szBuffer = '\\';
  2024.       }
  2025.       INCSTR(szBuffer);
  2026.    }
  2027. }
  2028.  
  2029. //******************************************************************************
  2030. void ForwardSlashesToBackSlashesW(LPWSTR szBuffer) {
  2031.    while (*szBuffer) {
  2032.       if (*szBuffer == L'/') {
  2033.          *szBuffer = L'\\';
  2034.       }
  2035.       szBuffer++;
  2036.    }
  2037. }
  2038.  
  2039. //******************************************************************************
  2040. void DeleteDirectory(LPTSTR szPath) {
  2041.  
  2042.    // Make note to where the end of our path is.
  2043.    LPTSTR szEnd = szPath + _tcslen(szPath);
  2044.  
  2045.    // Add our search spec to the path.
  2046.    _tcscpy(szEnd, TEXT("\\*.*"));
  2047.  
  2048.    // Start a directory search.
  2049.    WIN32_FIND_DATA w32fd;
  2050.    HANDLE hFind = FindFirstFile(szPath, &w32fd);
  2051.  
  2052.    // Loop through all entries in this directory.
  2053.    if (hFind != INVALID_HANDLE_VALUE) {
  2054.  
  2055.       do {
  2056.          // Append the file/directory name to the path.
  2057.          _tcscpy(szEnd + 1, w32fd.cFileName);
  2058.  
  2059.          // Check to see if this entry is a subdirectory.
  2060.          if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2061.  
  2062.             // Ignore current directory (.) and previous directory (..)
  2063.             if (_tcscmp(w32fd.cFileName, TEXT("."))   &&
  2064.                 _tcscmp(w32fd.cFileName, TEXT("..")))
  2065.             {
  2066.                // Recurse into DeleteDirectory() to delete subdirectory.
  2067.                DeleteDirectory(szPath);
  2068.             }
  2069.  
  2070.          // Otherwise, it must be a file.
  2071.          } else {
  2072.  
  2073.             // If the file is marked as read-only, then change to read/write.
  2074.             if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  2075.                SetFileAttributes(szPath, FILE_ATTRIBUTE_NORMAL);
  2076.             }
  2077.  
  2078.             // Attempt to delete the file.  If we fail and the file used to be
  2079.             // read-only, then set the read-only bit back on it.
  2080.             if (!DeleteFile(szPath) &&
  2081.                 (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
  2082.             {
  2083.                SetFileAttributes(szPath, FILE_ATTRIBUTE_READONLY);
  2084.             }
  2085.          }
  2086.  
  2087.       // Get the next directory entry.
  2088.       } while (FindNextFile(hFind, &w32fd));
  2089.  
  2090.       // Close the directory search.
  2091.       FindClose(hFind);
  2092.    }
  2093.  
  2094.    // Remove the directory.
  2095.    *szEnd = TEXT('\0');
  2096.    RemoveDirectory(szPath);
  2097. }
  2098.  
  2099.  
  2100. //******************************************************************************
  2101. //***** Registry Functions
  2102. //******************************************************************************
  2103.  
  2104. void RegWriteKey(HKEY hKeyRoot, LPCTSTR szSubKey, LPCTSTR szValue) {
  2105.    HKEY  hKey = NULL;
  2106.    DWORD dwDisposition;
  2107.  
  2108.    if (RegCreateKeyEx(hKeyRoot, szSubKey, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) {
  2109.       if (szValue) {
  2110.          RegSetValueEx(hKey, NULL, 0, REG_SZ, (LPBYTE)szValue,
  2111.                        sizeof(TCHAR) * (_tcslen(szValue) + 1));
  2112.       }
  2113.       RegCloseKey(hKey);
  2114.    }
  2115. }
  2116.  
  2117. //******************************************************************************
  2118. BOOL RegReadKey(HKEY hKeyRoot, LPCTSTR szSubKey, LPTSTR szValue, DWORD cBytes) {
  2119.    *szValue = TEXT('\0');
  2120.    HKEY hKey = NULL;
  2121.    LRESULT lResult = -1;
  2122.  
  2123.    if (RegOpenKeyEx(hKeyRoot, szSubKey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
  2124.       lResult = RegQueryValueEx(hKey, NULL, NULL, NULL, (LPBYTE)szValue, &cBytes);
  2125.       RegCloseKey(hKey);
  2126.    }
  2127.    return ((lResult == ERROR_SUCCESS) && *szValue);
  2128. }
  2129.  
  2130. //******************************************************************************
  2131. void WriteOptionString(LPCTSTR szOption, LPCTSTR szValue) {
  2132.    HKEY hKey = NULL;
  2133.  
  2134.    if (RegOpenKeyEx(HKEY_CURRENT_USER, g_szRegKey, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {
  2135.       RegSetValueEx(hKey, szOption, 0, REG_SZ, (LPBYTE)szValue,
  2136.                     sizeof(TCHAR) * (_tcslen(szValue) + 1));
  2137.       RegCloseKey(hKey);
  2138.    }
  2139. }
  2140.  
  2141. //******************************************************************************
  2142. void WriteOptionInt(LPCTSTR szOption, DWORD dwValue) {
  2143.    HKEY hKey = NULL;
  2144.  
  2145.    if (RegOpenKeyEx(HKEY_CURRENT_USER, g_szRegKey, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {
  2146.       RegSetValueEx(hKey, szOption, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));
  2147.       RegCloseKey(hKey);
  2148.    }
  2149. }
  2150.  
  2151. //******************************************************************************
  2152. LPTSTR GetOptionString(LPCTSTR szOption, LPCTSTR szDefault, LPTSTR szValue, DWORD nSize) {
  2153.    HKEY hKey = NULL;
  2154.    LONG lResult = -1;
  2155.  
  2156.    if (RegOpenKeyEx(HKEY_CURRENT_USER, g_szRegKey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
  2157.       lResult = RegQueryValueEx(hKey, szOption, NULL, NULL, (LPBYTE)szValue, &nSize);
  2158.       RegCloseKey(hKey);
  2159.    }
  2160.    if (lResult != ERROR_SUCCESS) {
  2161.       _tcscpy(szValue, szDefault);
  2162.    }
  2163.    return szValue;
  2164. }
  2165.  
  2166. //******************************************************************************
  2167. DWORD GetOptionInt(LPCTSTR szOption, DWORD dwDefault) {
  2168.    HKEY  hKey = NULL;
  2169.    LONG  lResult = -1;
  2170.    DWORD dwValue;
  2171.    DWORD nSize = sizeof(dwValue);
  2172.  
  2173.    if (RegOpenKeyEx(HKEY_CURRENT_USER, g_szRegKey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
  2174.       lResult = RegQueryValueEx(hKey, szOption, NULL, NULL, (LPBYTE)&dwValue, &nSize);
  2175.       RegCloseKey(hKey);
  2176.    }
  2177.    return (lResult == ERROR_SUCCESS) ? dwValue : dwDefault;
  2178. }
  2179.  
  2180. //******************************************************************************
  2181. //***** EDIT Control Subclass Functions
  2182. //******************************************************************************
  2183.  
  2184. void DisableEditing(HWND hWndEdit) {
  2185.  
  2186.    // Make sure the control does not have ES_READONLY or ES_WANTRETURN styles.
  2187.    DWORD dwStyle = (DWORD)GetWindowLong(hWndEdit, GWL_STYLE);
  2188.    if (dwStyle & (ES_READONLY | ES_WANTRETURN)) {
  2189.       SetWindowLong(hWndEdit, GWL_STYLE, dwStyle & ~(ES_READONLY | ES_WANTRETURN));
  2190.    }
  2191.  
  2192.    // Subclass the control so we can intercept certain keys.
  2193.    g_wpEdit = (WNDPROC)GetWindowLong(hWndEdit, GWL_WNDPROC);
  2194.    SetWindowLong(hWndEdit, GWL_WNDPROC, (LONG)EditSubclassProc);
  2195. }
  2196.  
  2197. //******************************************************************************
  2198. LRESULT CALLBACK EditSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  2199.  
  2200.    BOOL fCtrl, fShift;
  2201.  
  2202.    switch (uMsg) {
  2203.       // For cut, paste, delete, and undo, the control post itself a message.
  2204.       // we throw away that message.  This works as a fail-safe in case we miss
  2205.       // some keystroke that causes one of these operations.  This also disables
  2206.       // the context menu on NT from causing one of these actions to occur.
  2207.       case WM_CUT:
  2208.       case WM_PASTE:
  2209.       case WM_CLEAR:
  2210.       case WM_UNDO:
  2211.          MessageBeep(0);
  2212.          return 0;
  2213.  
  2214.       // WM_CHAR is used for normal characters. A-Z, numbers, symbols, enter,
  2215.       // backspace, esc, and tab. In does not include del or movement keys.
  2216.       case WM_CHAR:
  2217.          fCtrl  = (GetKeyState(VK_CONTROL) & 0x8000) ? TRUE : FALSE;
  2218.  
  2219.          // We only allow CTRL-C (copy), plain ESC, plain TAB, plain ENTER.
  2220.          if (( fCtrl && (wParam == 3))         ||
  2221.              (!fCtrl && (wParam == VK_ESCAPE)) ||
  2222.              (!fCtrl && (wParam == VK_RETURN)) ||
  2223.              (!fCtrl && (wParam == VK_TAB)))
  2224.          {
  2225.             break;
  2226.          }
  2227.          MessageBeep(0);
  2228.          return 0;
  2229.  
  2230.       // WM_KEYDOWN handles del, insert, arrows, pg up/down, home/end.
  2231.       case WM_KEYDOWN:
  2232.          fCtrl  = (GetKeyState(VK_CONTROL) & 0x8000) ? TRUE : FALSE;
  2233.          fShift = (GetKeyState(VK_SHIFT)   & 0x8000) ? TRUE : FALSE;
  2234.  
  2235.          // Skip all forms of DELETE, SHIFT-INSERT (paste),
  2236.          // CTRL-RETURN (hard-return), and CTRL-TAB (hard-tab).
  2237.          if ((          (wParam == VK_DELETE)) ||
  2238.              (fShift && (wParam == VK_INSERT)) ||
  2239.              (fCtrl  && (wParam == VK_RETURN)) ||
  2240.              (fCtrl  && (wParam == VK_TAB)))
  2241.          {
  2242.             MessageBeep(0);
  2243.             return 0;
  2244.          }
  2245.          break;
  2246.    }
  2247.    return CallWindowProc(g_wpEdit, hWnd, uMsg, wParam, lParam);
  2248. }
  2249.  
  2250.  
  2251. //******************************************************************************
  2252. //***** MRU Functions
  2253. //******************************************************************************
  2254.  
  2255. #ifdef _WIN32_WCE
  2256. int GetMenuString(HMENU hMenu, UINT uIDItem, LPTSTR lpString, int nMaxCount,
  2257.                   UINT uFlag) {
  2258.    MENUITEMINFO mii;
  2259.    ZeroMemory(&mii, sizeof(mii));
  2260.    mii.cbSize = sizeof(mii);
  2261.    mii.fMask = MIIM_TYPE;
  2262.    mii.dwTypeData = lpString;
  2263.    mii.cch = nMaxCount;
  2264.    return (GetMenuItemInfo(hMenu, uIDItem, uFlag == MF_BYPOSITION, &mii) ?
  2265.            mii.cch : 0);
  2266. }
  2267. #endif
  2268.  
  2269. //******************************************************************************
  2270. void InitializeMRU() {
  2271.  
  2272.    TCHAR szMRU[MRU_MAX_FILE][_MAX_PATH + 4], szOption[8];
  2273.    int   i, j;
  2274.  
  2275.    // Get our menu handle.
  2276. #ifdef _WIN32_WCE
  2277.    HMENU hMenu = GetSubMenu(CommandBar_GetMenu(g_hWndCmdBar, 0), 0);
  2278. #else
  2279.    HMENU hMenu = GetSubMenu(GetMenu(g_hWndMain), 0);
  2280. #endif
  2281.  
  2282.    // Read all our current MRUs from the registry.
  2283.    for (i = 0, j = 0; i < MRU_MAX_FILE; i++) {
  2284.  
  2285.       // Build option name for current MRU and read from registry.
  2286.       _stprintf(szOption, TEXT("MRU%d"), i+1);
  2287.       GetOptionString(szOption, TEXT(""), &szMRU[i][3], sizeof(TCHAR) * _MAX_PATH);
  2288.  
  2289.       // If this MRU exists, then add it.
  2290.       if (szMRU[i][3]) {
  2291.  
  2292.          // Build the accelerator prefix for this menu item.
  2293.          szMRU[i][0] = TEXT('&');
  2294.          szMRU[i][1] = TEXT('1') + j;
  2295.          szMRU[i][2] = TEXT(' ');
  2296.  
  2297.          // Add the item to our menu.
  2298.          InsertMenu(hMenu, 4 + j, MF_BYPOSITION | MF_STRING, MRU_START_ID + j,
  2299.                     szMRU[i]);
  2300.  
  2301.          // Increment our actual MRU count.
  2302.          j++;
  2303.       }
  2304.    }
  2305. }
  2306.  
  2307. //******************************************************************************
  2308. void AddFileToMRU(LPCSTR szFile) {
  2309.  
  2310.    TCHAR szMRU[MRU_MAX_FILE + 1][_MAX_PATH + 4], szOption[8];
  2311.    int   i, j;
  2312.  
  2313.    // Store the new file in our first MRU index.
  2314.    MBSTOTSTR(&szMRU[0][3], szFile, _MAX_PATH);
  2315.  
  2316.    //---------------------------------------------------------------------------
  2317.    // We first read the current MRU list from the registry, merge in our new
  2318.    // file at the top, and then write back to the registry.  The registry merge
  2319.    // is done to allow multiple instances of Pocket UnZip to maintain a global
  2320.    // MRU list independent to this current instance's MRU list.
  2321.    //---------------------------------------------------------------------------
  2322.  
  2323.    // Read all our current MRUs from the registry.
  2324.    for (i = 1; i <= MRU_MAX_FILE; i++) {
  2325.  
  2326.       // Build option name for current MRU and read from registry.
  2327.       _stprintf(szOption, TEXT("MRU%d"), i);
  2328.       GetOptionString(szOption, TEXT(""), &szMRU[i][3], sizeof(TCHAR) * _MAX_PATH);
  2329.    }
  2330.  
  2331.    // Write our new merged MRU list back to the registry.
  2332.    for (i = 0, j = 0; (i <= MRU_MAX_FILE) && (j < MRU_MAX_FILE); i++) {
  2333.  
  2334.       // If this MRU exists and is different then our new file, then add it.
  2335.       if ((i == 0) || (szMRU[i][3] && _tcsicmp(&szMRU[0][3], &szMRU[i][3]))) {
  2336.  
  2337.          // Build option name for current MRU and write to registry.
  2338.          _stprintf(szOption, TEXT("MRU%d"), ++j);
  2339.          WriteOptionString(szOption, &szMRU[i][3]);
  2340.       }
  2341.    }
  2342.  
  2343.    //---------------------------------------------------------------------------
  2344.    // The next thing we need to do is read our local MRU from our File menu,
  2345.    // merge in our new file, and store the new list back to our File menu.
  2346.    //---------------------------------------------------------------------------
  2347.  
  2348.    // Get our menu handle.
  2349. #ifdef _WIN32_WCE
  2350.    HMENU hMenu = GetSubMenu(CommandBar_GetMenu(g_hWndCmdBar, 0), 0);
  2351. #else
  2352.    HMENU hMenu = GetSubMenu(GetMenu(g_hWndMain), 0);
  2353. #endif
  2354.  
  2355.    // Read all our current MRUs from our File Menu.
  2356.    for (i = 1; i <= MRU_MAX_FILE; i++) {
  2357.  
  2358.       // Query our file Menu for a MRU file.
  2359.       if (GetMenuString(hMenu, MRU_START_ID + i - 1, szMRU[i],
  2360.                         countof(szMRU[0]), MF_BYCOMMAND))
  2361.       {
  2362.          // Delete this item from the menu for now.
  2363.          DeleteMenu(hMenu, MRU_START_ID + i - 1, MF_BYCOMMAND);
  2364.       } else {
  2365.          szMRU[i][3] = TEXT('\0');
  2366.       }
  2367.    }
  2368.  
  2369.    // Write our new merged MRU list back to the File menu.
  2370.    for (i = 0, j = 0; (i <= MRU_MAX_FILE) && (j < MRU_MAX_FILE); i++) {
  2371.  
  2372.       // If this MRU exists and is different then our new file, then add it.
  2373.       if ((i == 0) || (szMRU[i][3] && _tcsicmp(&szMRU[0][3], &szMRU[i][3]))) {
  2374.  
  2375.          // Build the accelerator prefix for this menu item.
  2376.          szMRU[i][0] = TEXT('&');
  2377.          szMRU[i][1] = TEXT('1') + j;
  2378.          szMRU[i][2] = TEXT(' ');
  2379.  
  2380.          // Add the item to our menu.
  2381.          InsertMenu(hMenu, 4 + j, MF_BYPOSITION | MF_STRING, MRU_START_ID + j,
  2382.                     szMRU[i]);
  2383.  
  2384.          // Increment our actual MRU count.
  2385.          j++;
  2386.       }
  2387.    }
  2388. }
  2389.  
  2390. //******************************************************************************
  2391. void RemoveFileFromMRU(LPCTSTR szFile) {
  2392.  
  2393.    TCHAR szMRU[MRU_MAX_FILE][_MAX_PATH + 4], szOption[8];
  2394.    int   i, j;
  2395.    BOOL  fFound;
  2396.  
  2397.    //---------------------------------------------------------------------------
  2398.    // We first look for this file in our global MRU stored in the registry.  We
  2399.    // read the current MRU list from the registry, and then write it back while
  2400.    // removing all occurrances of the file specified.
  2401.    //---------------------------------------------------------------------------
  2402.  
  2403.    // Read all our current MRUs from the registry.
  2404.    for (i = 0, fFound = FALSE; i < MRU_MAX_FILE; i++) {
  2405.  
  2406.       // Build option name for current MRU and read from registry.
  2407.       _stprintf(szOption, TEXT("MRU%d"), i+1);
  2408.       GetOptionString(szOption, TEXT(""), &szMRU[i][3], sizeof(TCHAR) * _MAX_PATH);
  2409.  
  2410.       // Check for a match.
  2411.       if (!_tcsicmp(szFile, &szMRU[i][3])) {
  2412.          szMRU[i][3] = TEXT('\0');
  2413.          fFound = TRUE;
  2414.       }
  2415.    }
  2416.  
  2417.    // Only write the MRU back to the registry if we found a file to remove.
  2418.    if (fFound) {
  2419.  
  2420.       // Write the updated MRU list back to the registry.
  2421.       for (i = 0, j = 0; i < MRU_MAX_FILE; i++) {
  2422.  
  2423.          // If this MRU still exists, then add it.
  2424.          if (szMRU[i][3]) {
  2425.  
  2426.             // Build option name for current MRU and write to registry.
  2427.             _stprintf(szOption, TEXT("MRU%d"), ++j);
  2428.             WriteOptionString(szOption, &szMRU[i][3]);
  2429.          }
  2430.       }
  2431.  
  2432.       // If our list got smaller, clear the unused items in the registry.
  2433.       while (j++ < MRU_MAX_FILE) {
  2434.          _stprintf(szOption, TEXT("MRU%d"), j);
  2435.          WriteOptionString(szOption, TEXT(""));
  2436.       }
  2437.    }
  2438.  
  2439.    //---------------------------------------------------------------------------
  2440.    // We next thing we do is look for this file in our local MRU stored in our
  2441.    // File menu.  We read the current MRU list from the menu, and then write it
  2442.    // back while removing all occurrances of the file specified.
  2443.    //---------------------------------------------------------------------------
  2444.  
  2445.    // Get our menu handle.
  2446. #ifdef _WIN32_WCE
  2447.    HMENU hMenu = GetSubMenu(CommandBar_GetMenu(g_hWndCmdBar, 0), 0);
  2448. #else
  2449.    HMENU hMenu = GetSubMenu(GetMenu(g_hWndMain), 0);
  2450. #endif
  2451.  
  2452.    // Read all our current MRUs from our File Menu.
  2453.    for (i = 0, fFound = FALSE; i < MRU_MAX_FILE; i++) {
  2454.  
  2455.       // Query our file Menu for a MRU file.
  2456.       if (!GetMenuString(hMenu, MRU_START_ID + i, szMRU[i], countof(szMRU[0]),
  2457.           MF_BYCOMMAND))
  2458.       {
  2459.          szMRU[i][3] = TEXT('\0');
  2460.       }
  2461.  
  2462.       // Check for a match.
  2463.       if (!_tcsicmp(szFile, &szMRU[i][3])) {
  2464.          szMRU[i][3] = TEXT('\0');
  2465.          fFound = TRUE;
  2466.       }
  2467.    }
  2468.  
  2469.    // Only update menu if we found a file to remove.
  2470.    if (fFound) {
  2471.  
  2472.       // Clear out our menu's MRU list.
  2473.       for (i = MRU_START_ID; i < (MRU_START_ID + MRU_MAX_FILE); i++) {
  2474.          DeleteMenu(hMenu, i, MF_BYCOMMAND);
  2475.       }
  2476.  
  2477.       // Write the rest of our MRU list back to the menu.
  2478.       for (i = 0, j = 0; i < MRU_MAX_FILE; i++) {
  2479.  
  2480.          // If this MRU still exists, then add it.
  2481.          if (szMRU[i][3]) {
  2482.  
  2483.             // Build the accelerator prefix for this menu item.
  2484.             szMRU[i][0] = TEXT('&');
  2485.             szMRU[i][1] = TEXT('1') + j;
  2486.             szMRU[i][2] = TEXT(' ');
  2487.  
  2488.             // Add the item to our menu.
  2489.             InsertMenu(hMenu, 4 + j, MF_BYPOSITION | MF_STRING, MRU_START_ID + j,
  2490.                        szMRU[i]);
  2491.  
  2492.             // Increment our actual MRU count.
  2493.             j++;
  2494.          }
  2495.       }
  2496.    }
  2497. }
  2498.  
  2499. //******************************************************************************
  2500. void ActivateMRU(UINT uIDItem) {
  2501.    TCHAR szFile[_MAX_PATH + 4];
  2502.  
  2503.    // Get our menu handle.
  2504. #ifdef _WIN32_WCE
  2505.    HMENU hMenu = GetSubMenu(CommandBar_GetMenu(g_hWndCmdBar, 0), 0);
  2506. #else
  2507.    HMENU hMenu = GetSubMenu(GetMenu(g_hWndMain), 0);
  2508. #endif
  2509.  
  2510.    // Query our menu for the selected MRU.
  2511.    if (GetMenuString(hMenu, uIDItem, szFile, countof(szFile), MF_BYCOMMAND)) {
  2512.  
  2513.       // Move past 3 character accelerator prefix and open the file.
  2514.       ReadZipFileList(&szFile[3]);
  2515.    }
  2516. }
  2517.  
  2518.  
  2519. //******************************************************************************
  2520. //***** Open Zip File Functions
  2521. //******************************************************************************
  2522.  
  2523. void ReadZipFileList(LPCTSTR wszPath) {
  2524.  
  2525.    // Show wait cursor.
  2526.    HCURSOR hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2527.  
  2528.    TSTRTOMBS(g_szZipFile, wszPath, countof(g_szZipFile));
  2529.  
  2530.    // Update our banner to show that we are loading.
  2531.    g_fLoading = TRUE;
  2532.    DrawBanner(NULL);
  2533.  
  2534.    // Update our caption to show that we are loading.
  2535.    SetCaptionText(TEXT("Loading"));
  2536.  
  2537.    // Clear our list view.
  2538.    ListView_DeleteAllItems(g_hWndList);
  2539.  
  2540.    // Ghost all our Unzip related menu items.
  2541.    EnableAllMenuItems(IDM_FILE_PROPERTIES,    FALSE);
  2542.    EnableAllMenuItems(IDM_ACTION_EXTRACT,     FALSE);
  2543.    EnableAllMenuItems(IDM_ACTION_EXTRACT_ALL, FALSE);
  2544.    EnableAllMenuItems(IDM_ACTION_TEST,        FALSE);
  2545.    EnableAllMenuItems(IDM_ACTION_TEST_ALL,    FALSE);
  2546.    EnableAllMenuItems(IDM_ACTION_VIEW,        FALSE);
  2547.    EnableAllMenuItems(IDM_ACTION_SELECT_ALL,  FALSE);
  2548.    EnableAllMenuItems(IDM_VIEW_COMMENT,       FALSE);
  2549.  
  2550.    // Let Info-ZIP and our callbacks do the work.
  2551.    SendMessage(g_hWndList, WM_SETREDRAW, FALSE, 0);
  2552.    int result = DoListFiles(g_szZipFile);
  2553.    SendMessage(g_hWndList, WM_SETREDRAW, TRUE, 0);
  2554.  
  2555.    // Restore/remove cursor.
  2556.    SetCursor(hCur);
  2557.  
  2558.    // Update our column widths
  2559.    ResizeColumns();
  2560.  
  2561.    if ((result == PK_OK) || (result == PK_WARN)) {
  2562.  
  2563.       // Sort the items by name.
  2564.       Sort(0, TRUE);
  2565.  
  2566.       // Update this file to our MRU list and menu.
  2567.       AddFileToMRU(g_szZipFile);
  2568.  
  2569.       // Enabled the comment button if the zip file has a comment.
  2570.       if (lpUserFunctions->cchComment) {
  2571.          EnableAllMenuItems(IDM_VIEW_COMMENT, TRUE);
  2572.       }
  2573.  
  2574.       // Update other items that are related to having a Zip file loaded.
  2575.       EnableAllMenuItems(IDM_ACTION_EXTRACT_ALL, TRUE);
  2576.       EnableAllMenuItems(IDM_ACTION_TEST_ALL,    TRUE);
  2577.       EnableAllMenuItems(IDM_ACTION_SELECT_ALL,  TRUE);
  2578.  
  2579.    } else {
  2580.  
  2581.       // Make sure we didn't partially load and added a few files.
  2582.       ListView_DeleteAllItems(g_hWndList);
  2583.  
  2584.       // If the file itself is bad or missing, then remove it from our MRU.
  2585.       if ((result == PK_ERR) || (result == PK_BADERR) || (result == PK_NOZIP) ||
  2586.           (result == PK_FIND) || (result == PK_EOF))
  2587.       {
  2588.          RemoveFileFromMRU(wszPath);
  2589.       }
  2590.  
  2591.       // Display an error.
  2592.       TCHAR szError[_MAX_PATH + 128];
  2593.       _stprintf(szError, TEXT("Failure loading \"%s\".\n\n"), wszPath);
  2594.       _tcscat(szError, GetZipErrorString(result));
  2595.       MessageBox(g_hWndMain, szError, g_szAppName, MB_OK | MB_ICONERROR);
  2596.  
  2597.       // Clear our file status.
  2598.       *g_szZipFile = '\0';
  2599.    }
  2600.  
  2601.    // Update our caption to show that we are done loading.
  2602.    SetCaptionText(NULL);
  2603.  
  2604.    // Update our banner to show that we are done loading.
  2605.    g_fLoading = FALSE;
  2606.    DrawBanner(NULL);
  2607. }
  2608.  
  2609.  
  2610. //******************************************************************************
  2611. //***** Zip File Properties Dialog Functions
  2612. //******************************************************************************
  2613.  
  2614. BOOL CALLBACK DlgProcProperties(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  2615.  
  2616.    switch (uMsg) {
  2617.  
  2618.       case WM_INITDIALOG: {
  2619.  
  2620.          // Add "General" and "Comments" tabs to tab control.  We are using a
  2621.          // poor man's version of a property sheet.  We display our 2 pages
  2622.          // by showing and hiding controls as necessary.  For our purposes,
  2623.          // this is much easier than dealing with separate property pages.
  2624.  
  2625.          TC_ITEM tci;
  2626.          tci.mask = TCIF_TEXT;
  2627.          tci.pszText = TEXT("General");
  2628.          TabCtrl_InsertItem(GetDlgItem(hDlg, IDC_TAB), 0, &tci);
  2629.          tci.pszText = TEXT("Comment");
  2630.          TabCtrl_InsertItem(GetDlgItem(hDlg, IDC_TAB), 1, &tci);
  2631.  
  2632. #ifdef _WIN32_WCE
  2633.          // Add "Ok" button to caption bar.
  2634.          SetWindowLong(hDlg, GWL_EXSTYLE, WS_EX_CAPTIONOKBTN |
  2635.                        GetWindowLong(hDlg, GWL_EXSTYLE));
  2636. #endif
  2637.          // Center us over our parent.
  2638.          CenterWindow(hDlg);
  2639.  
  2640.          int    directory = -1, readOnly = -1, archive = -1, hidden = -1;
  2641.          int    system = -1, encrypted = -1;
  2642.          int    year = -1, month = -1, day = -1, hour = -1, minute = -1, pm = -1;
  2643.          zusz_t uzSize = 0, uzCompressedSize = 0;
  2644.          LPCSTR szPath = NULL, szMethod = NULL, szComment = NULL;
  2645.          DWORD  dwCRC = 0, dwCount = 0, dwCommentCount = 0;
  2646.          TCHAR  szBuffer[MAX_PATH];
  2647.  
  2648.          // Loop through all selected items.
  2649.          LV_ITEM lvi;
  2650.          ZeroMemory(&lvi, sizeof(lvi));
  2651.          lvi.mask = LVIF_PARAM;
  2652.          lvi.iItem = -1;
  2653.          while ((lvi.iItem = ListView_GetNextItem(g_hWndList, lvi.iItem, LVNI_SELECTED)) != -1) {
  2654.  
  2655.             // Get the FILE_NODE for the selected item.
  2656.             ListView_GetItem(g_hWndList, &lvi);
  2657.             FILE_NODE *pFile = (FILE_NODE*)lvi.lParam;
  2658.  
  2659.             // Merge this file's attributes into our accumulative attributes.
  2660.             MergeValues(&directory, (pFile->dwAttributes & FILE_ATTRIBUTE_DIRECTORY)  != 0);
  2661.             MergeValues(&readOnly,  (pFile->dwAttributes & FILE_ATTRIBUTE_READONLY)   != 0);
  2662.             MergeValues(&archive,   (pFile->dwAttributes & FILE_ATTRIBUTE_ARCHIVE)    != 0);
  2663.             MergeValues(&hidden,    (pFile->dwAttributes & FILE_ATTRIBUTE_HIDDEN)     != 0);
  2664.             MergeValues(&system,    (pFile->dwAttributes & FILE_ATTRIBUTE_SYSTEM)     != 0);
  2665.             MergeValues(&encrypted, (pFile->dwAttributes & ZFILE_ATTRIBUTE_ENCRYPTED) != 0);
  2666.  
  2667.             // Merge this file's date/time into our accumulative date/time.
  2668.             int curHour = (pFile->dwModified >> 6) & 0x001F;
  2669.             MergeValues(&year,   (pFile->dwModified >> 20) & 0x0FFF);
  2670.             MergeValues(&month,  (pFile->dwModified >> 16) & 0x000F);
  2671.             MergeValues(&day,    (pFile->dwModified >> 11) & 0x001F);
  2672.             MergeValues(&hour,   (curHour % 12) ? (curHour % 12) : 12);
  2673.             MergeValues(&minute, pFile->dwModified & 0x003F);
  2674.             MergeValues(&pm,     curHour >= 12);
  2675.  
  2676.             // Store this file's name.
  2677.             szPath = pFile->szPathAndMethod;
  2678.  
  2679.             // Store this file's CRC.
  2680.             dwCRC = pFile->dwCRC;
  2681.  
  2682.             // Add the size and compressed size to our accumulative sizes.
  2683.             uzSize += pFile->uzSize;
  2684.             uzCompressedSize += pFile->uzCompressedSize;
  2685.  
  2686.             // Merge in our compression method.
  2687.             LPCSTR szCurMethod = pFile->szPathAndMethod + strlen(pFile->szPathAndMethod) + 1;
  2688.             if ((szMethod == NULL) || !strcmp(szMethod, szCurMethod)) {
  2689.                szMethod = szCurMethod;
  2690.             } else {
  2691.                szMethod = "Multiple Methods";
  2692.             }
  2693.  
  2694.             // Increment our file count.
  2695.             dwCount++;
  2696.  
  2697.             // Increment our comment count if this file has a comment.
  2698.             if (pFile->szComment) {
  2699.                szComment = pFile->szComment;
  2700.                dwCommentCount++;
  2701.             }
  2702.          };
  2703.  
  2704.          if (dwCount > 1) {
  2705.  
  2706.             // If multiple items selected, then display a selected count string
  2707.             // in place of the file name.
  2708.             _stprintf(szBuffer, TEXT("%u items selected."), dwCount);
  2709.             SetDlgItemText(hDlg, IDC_FILE, szBuffer);
  2710.  
  2711.             // Display "Multiple" for CRC if multiple items selected.
  2712.             SetDlgItemText(hDlg, IDC_CRC, TEXT("Multiple CRCs"));
  2713.  
  2714.          } else {
  2715.  
  2716.             // Set the file name text for the single item selected.
  2717.             MBSTOTSTR(szBuffer, szPath, countof(szBuffer));
  2718.             ForwardSlashesToBackSlashes(szBuffer);
  2719.             SetDlgItemText(hDlg, IDC_FILE, szBuffer);
  2720.  
  2721.             // Set the CRC text for the single item selected.
  2722.             _stprintf(szBuffer, TEXT("0x%08X"), dwCRC);
  2723.             SetDlgItemText(hDlg, IDC_CRC, szBuffer);
  2724.          }
  2725.  
  2726.          // Set the Size tally text.
  2727.          FormatValue(szBuffer, uzSize);
  2728.          _tcscat(szBuffer, (dwCount > 1) ? TEXT(" bytes total") : TEXT(" bytes"));
  2729.          SetDlgItemText(hDlg, IDC_FILE_SIZE, szBuffer);
  2730.  
  2731.          // Set the Compressed Size tally text.
  2732.          FormatValue(szBuffer, uzCompressedSize);
  2733.          _tcscat(szBuffer, (dwCount > 1) ? TEXT(" bytes total") : TEXT(" bytes"));
  2734.          SetDlgItemText(hDlg, IDC_COMPRESSED_SIZE, szBuffer);
  2735.  
  2736.          // Set the Compression Factor text.
  2737.          int factor = ratio(uzSize, uzCompressedSize);
  2738.          _stprintf(szBuffer, TEXT("%d.%d%%"), factor / 10,
  2739.                    ((factor < 0) ? -factor : factor) % 10);
  2740.          SetDlgItemText(hDlg, IDC_COMPRESSON_FACTOR, szBuffer);
  2741.  
  2742.          // Set the Compression Method text.
  2743.          MBSTOTSTR(szBuffer, szMethod, countof(szBuffer));
  2744.          SetDlgItemText(hDlg, IDC_COMPRESSION_METHOD, szBuffer);
  2745.  
  2746.          // Set the Attribute check boxes.
  2747.          CheckThreeStateBox(hDlg, IDC_DIRECTORY, directory);
  2748.          CheckThreeStateBox(hDlg, IDC_READONLY,  readOnly);
  2749.          CheckThreeStateBox(hDlg, IDC_ARCHIVE,   archive);
  2750.          CheckThreeStateBox(hDlg, IDC_HIDDEN,    hidden);
  2751.          CheckThreeStateBox(hDlg, IDC_SYSTEM,    system);
  2752.          CheckThreeStateBox(hDlg, IDC_ENCRYPTED, encrypted);
  2753.  
  2754.          // Build and set the Modified Date text.  The MS compiler does not
  2755.          // consider "??/" to be a valid string.  "??/" is a trigraph that is
  2756.          // turned into "\" by the preprocessor and causes grief for the compiler.
  2757.          LPTSTR psz = szBuffer;
  2758.          psz += ((month  < 0) ? _stprintf(psz, TEXT("?\?/")) :
  2759.                                 _stprintf(psz, TEXT("%u/"), month));
  2760.          psz += ((day    < 0) ? _stprintf(psz, TEXT("?\?/")) :
  2761.                                 _stprintf(psz, TEXT("%u/"), day));
  2762.          psz += ((year   < 0) ? _stprintf(psz, TEXT("?\? ")) :
  2763.                                 _stprintf(psz, TEXT("%u "), year % 100));
  2764.          psz += ((hour   < 0) ? _stprintf(psz, TEXT("?\?:")) :
  2765.                                 _stprintf(psz, TEXT("%u:"), hour));
  2766.          psz += ((minute < 0) ? _stprintf(psz, TEXT("?\? ")) :
  2767.                                 _stprintf(psz, TEXT("%02u "), minute));
  2768.          psz += ((pm     < 0) ? _stprintf(psz, TEXT("?M")) :
  2769.                                 _stprintf(psz, TEXT("%cM"), pm ? TEXT('P') : TEXT('A')));
  2770.          SetDlgItemText(hDlg, IDC_MODIFIED, szBuffer);
  2771.  
  2772.          // Store a global handle to our edit control.
  2773.          g_hWndEdit = GetDlgItem(hDlg, IDC_COMMENT);
  2774.  
  2775.          // Disable our edit box from being edited.
  2776.          DisableEditing(g_hWndEdit);
  2777.  
  2778.          // Stuff the appropriate message into the Comment edit control.
  2779.          if (dwCommentCount == 0) {
  2780.             if (dwCount == 1) {
  2781.                AddTextToEdit("This file does not have a comment.");
  2782.             } else {
  2783.                AddTextToEdit("None of the selected files have a comment.");
  2784.             }
  2785.          } else if (dwCount == 1) {
  2786.             AddTextToEdit(szComment);
  2787.          } else {
  2788.             CHAR szTemp[64];
  2789.             _stprintf(szBuffer, TEXT("%u of the selected files %s a comment."),
  2790.                       dwCommentCount, (dwCommentCount == 1)? TEXT("has") : TEXT("have"));
  2791.             TSTRTOMBS(szTemp, szBuffer, countof(szTemp));
  2792.             AddTextToEdit(szTemp);
  2793.          }
  2794.          g_hWndEdit = NULL;
  2795.  
  2796.  
  2797.          // Whooh, done with WM_INITDIALOG
  2798.          return TRUE;
  2799.       }
  2800.  
  2801.       case WM_NOTIFY:
  2802.          // Check to see if tab control was changed to new tab.
  2803.          if (((NMHDR*)lParam)->code == TCN_SELCHANGE) {
  2804.             HWND hWndTab     = ((NMHDR*)lParam)->hwndFrom;
  2805.             HWND hWndComment = GetDlgItem(hDlg, IDC_COMMENT);
  2806.             HWND hWnd        = GetWindow(hDlg, GW_CHILD);
  2807.  
  2808.             // If General tab selected, hide comment edit box and show all other controls.
  2809.             if (TabCtrl_GetCurSel(hWndTab) == 0) {
  2810.                while (hWnd) {
  2811.                   ShowWindow(hWnd, ((hWnd == hWndTab) || (hWnd != hWndComment)) ?
  2812.                              SW_SHOW : SW_HIDE);
  2813.                   hWnd = GetWindow(hWnd, GW_HWNDNEXT);
  2814.                }
  2815.  
  2816.             // If Comment tab selected, hide all controls except comment edit box.
  2817.             } else {
  2818.                while (hWnd) {
  2819.                   ShowWindow(hWnd, ((hWnd == hWndTab) || (hWnd == hWndComment)) ?
  2820.                              SW_SHOW : SW_HIDE);
  2821.                   hWnd = GetWindow(hWnd, GW_HWNDNEXT);
  2822.                }
  2823.             }
  2824.          }
  2825.          return FALSE;
  2826.  
  2827.       case WM_COMMAND:
  2828.          // Exit the dialog on OK (Enter) or CANCEL (Esc).
  2829.          if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) {
  2830.             EndDialog(hDlg, LOWORD(wParam));
  2831.          }
  2832.          return FALSE;
  2833.    }
  2834.    return FALSE;
  2835. }
  2836.  
  2837. //******************************************************************************
  2838. void MergeValues(int *p1, int p2) {
  2839.    if ((*p1 == -1) || (*p1 == p2)) {
  2840.       *p1 = p2;
  2841.    } else {
  2842.       *p1 = -2;
  2843.    }
  2844. }
  2845.  
  2846. //******************************************************************************
  2847. void CheckThreeStateBox(HWND hDlg, int nIDButton, int state) {
  2848.    CheckDlgButton(hDlg, nIDButton, (state == 0) ? BST_UNCHECKED :
  2849.                                    (state == 1) ? BST_CHECKED :
  2850.                                                   BST_INDETERMINATE);
  2851. }
  2852.  
  2853.  
  2854. //******************************************************************************
  2855. //***** Extract/Test Dialog Functions
  2856. //******************************************************************************
  2857.  
  2858. void ExtractOrTestFiles(BOOL fExtract) {
  2859.  
  2860.    EXTRACT_INFO ei;
  2861.    ZeroMemory(&ei, sizeof(ei));
  2862.  
  2863.    // Set our Extract or Test flag.
  2864.    ei.fExtract = fExtract;
  2865.  
  2866.    // Get the number of selected items and make sure we have at least one item.
  2867.    if ((ei.dwFileCount = ListView_GetSelectedCount(g_hWndList)) <= 0) {
  2868.       return;
  2869.    }
  2870.  
  2871.    // If we are not extracting/testing all, then create and buffer large enough to
  2872.    // hold the file list for all the selected files.
  2873.    if ((int)ei.dwFileCount != ListView_GetItemCount(g_hWndList)) {
  2874.       ei.szFileList = new LPSTR[ei.dwFileCount + 1];
  2875.       if (!ei.szFileList) {
  2876.          MessageBox(g_hWndMain, GetZipErrorString(PK_MEM), g_szAppName,
  2877.                     MB_ICONERROR | MB_OK);
  2878.          return;
  2879.       }
  2880.    }
  2881.  
  2882.    ei.dwFileCount = 0;
  2883.    ei.uzByteCount = 0;
  2884.  
  2885.    LV_ITEM lvi;
  2886.    ZeroMemory(&lvi, sizeof(lvi));
  2887.    lvi.mask = LVIF_PARAM;
  2888.    lvi.iItem = -1;
  2889.  
  2890.    // Walk through all the selected files to build our counts and set our file
  2891.    // list pointers into our FILE_NODE paths for each selected item.
  2892.    while ((lvi.iItem = ListView_GetNextItem(g_hWndList, lvi.iItem, LVNI_SELECTED)) >= 0) {
  2893.       ListView_GetItem(g_hWndList, &lvi);
  2894.       if (ei.szFileList) {
  2895.          ei.szFileList[ei.dwFileCount] = ((FILE_NODE*)lvi.lParam)->szPathAndMethod;
  2896.       }
  2897.       ei.dwFileCount++;
  2898.       ei.uzByteCount += ((FILE_NODE*)lvi.lParam)->uzSize;
  2899.    }
  2900.    if (ei.szFileList) {
  2901.       ei.szFileList[ei.dwFileCount] = NULL;
  2902.    }
  2903.  
  2904.    // If we are extracting, display the extract dialog to query for parameters.
  2905.    if (!fExtract || (DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_EXTRACT), g_hWndMain,
  2906.                                     (DLGPROC)DlgProcExtractOrTest, (LPARAM)&ei) == IDOK))
  2907.    {
  2908.       // Display our progress dialog and do the extraction/test.
  2909.       DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_EXTRACT_PROGRESS), g_hWndMain,
  2910.                      (DLGPROC)DlgProcExtractProgress, (LPARAM)&ei);
  2911.    }
  2912.  
  2913.    // Free our file list buffer if we created one.
  2914.    if (ei.szFileList) {
  2915.       delete[] ei.szFileList;
  2916.    }
  2917. }
  2918.  
  2919. //******************************************************************************
  2920. BOOL CALLBACK DlgProcExtractOrTest(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  2921.  
  2922.    static EXTRACT_INFO *pei;
  2923.    TCHAR  szPath[_MAX_PATH];
  2924.  
  2925.    switch (uMsg) {
  2926.  
  2927.       case WM_INITDIALOG:
  2928.  
  2929.          // Store our extract information structure.
  2930.          pei = (EXTRACT_INFO*)lParam;
  2931.  
  2932.          // Load our settings.
  2933.          pei->fRestorePaths = GetOptionInt(TEXT("RestorePaths"), TRUE);
  2934.          pei->overwriteMode = (OVERWRITE_MODE)GetOptionInt(TEXT("OverwriteMode"), OM_PROMPT);
  2935.  
  2936.          // Load and set our path string.
  2937.          GetOptionString(TEXT("ExtractToDirectory"), TEXT("\\"), szPath, sizeof(szPath));
  2938.          SetDlgItemText(hDlg, IDC_EXTRACT_TO, szPath);
  2939.  
  2940.          // Set the state of all the controls.
  2941.          SetDlgItemText(hDlg, IDC_FILE_COUNT, FormatValue(szPath, pei->dwFileCount));
  2942.          SetDlgItemText(hDlg, IDC_BYTE_COUNT, FormatValue(szPath, pei->uzByteCount));
  2943.          CheckDlgButton(hDlg, IDC_RESTORE_PATHS, pei->fRestorePaths);
  2944.          CheckDlgButton(hDlg, IDC_OVERWRITE_PROMPT, pei->overwriteMode == OM_PROMPT);
  2945.          CheckDlgButton(hDlg, IDC_OVERWRITE_NEWER,  pei->overwriteMode == OM_NEWER);
  2946.          CheckDlgButton(hDlg, IDC_OVERWRITE_ALWAYS, pei->overwriteMode == OM_ALWAYS);
  2947.          CheckDlgButton(hDlg, IDC_OVERWRITE_NEVER,  pei->overwriteMode == OM_NEVER);
  2948.  
  2949.          // Limit our edit control to max path.
  2950.          SendDlgItemMessage(hDlg, IDC_EXTRACT_TO, EM_LIMITTEXT, sizeof(szPath) - 1, 0);
  2951.  
  2952.          // Center our dialog.
  2953.          CenterWindow(hDlg);
  2954.          return TRUE;
  2955.  
  2956.       case WM_COMMAND:
  2957.          switch (LOWORD(wParam)) {
  2958.  
  2959.             case IDOK:
  2960.  
  2961.                // Force us to read and validate the extract to directory.
  2962.                SendMessage(hDlg, WM_COMMAND, MAKELONG(IDC_EXTRACT_TO, EN_KILLFOCUS), 0);
  2963.  
  2964.                // Get our current path string.
  2965.                GetDlgItemText(hDlg, IDC_EXTRACT_TO, szPath, countof(szPath));
  2966.  
  2967.                // Verify our "extract to" path is valid.
  2968.                if (!SetExtractToDirectory(szPath)) {
  2969.                   MessageBox(hDlg, TEXT("The directory you entered is invalid or does not exist."),
  2970.                              g_szAppName, MB_ICONERROR | MB_OK);
  2971.                   SetFocus(GetDlgItem(hDlg, IDC_EXTRACT_TO));
  2972.                   return FALSE;
  2973.                }
  2974.  
  2975.                // Query other control values.
  2976.                pei->fRestorePaths = IsDlgButtonChecked(hDlg, IDC_RESTORE_PATHS);
  2977.                pei->overwriteMode =
  2978.                   IsDlgButtonChecked(hDlg, IDC_OVERWRITE_NEWER)  ? OM_NEWER  :
  2979.                   IsDlgButtonChecked(hDlg, IDC_OVERWRITE_ALWAYS) ? OM_ALWAYS :
  2980.                   IsDlgButtonChecked(hDlg, IDC_OVERWRITE_NEVER)  ? OM_NEVER  : OM_PROMPT;
  2981.  
  2982.                // Write our settings.
  2983.                WriteOptionInt(TEXT("RestorePaths"), pei->fRestorePaths);
  2984.                WriteOptionInt(TEXT("OverwriteMode"), pei->overwriteMode);
  2985.                WriteOptionString(TEXT("ExtractToDirectory"), szPath);
  2986.  
  2987.                // Fall through to IDCANCEL
  2988.  
  2989.             case IDCANCEL:
  2990.                EndDialog(hDlg, LOWORD(wParam));
  2991.                return FALSE;
  2992.  
  2993.             case IDC_EXTRACT_TO:
  2994.  
  2995.                // Make sure the path ends in a wack (\).
  2996.                if (HIWORD(wParam) == EN_KILLFOCUS) {
  2997.                   GetDlgItemText(hDlg, IDC_EXTRACT_TO, szPath, countof(szPath));
  2998.                   size_t length = _tcslen(szPath);
  2999.                   if ((length == 0) || szPath[length - 1] != TEXT('\\')) {
  3000.                      szPath[length    ] = TEXT('\\');
  3001.                      szPath[length + 1] = TEXT('\0');
  3002.                      SetDlgItemText(hDlg, IDC_EXTRACT_TO, szPath);
  3003.                   }
  3004.                }
  3005.                return FALSE;
  3006.  
  3007.             case IDC_BROWSE:
  3008.                GetDlgItemText(hDlg, IDC_EXTRACT_TO, szPath, countof(szPath));
  3009.                if (FolderBrowser(szPath, countof(szPath))) {
  3010.                   SetDlgItemText(hDlg, IDC_EXTRACT_TO, szPath);
  3011.                }
  3012.                return FALSE;
  3013.          }
  3014.          return FALSE;
  3015.    }
  3016.    return FALSE;
  3017. }
  3018.  
  3019.  
  3020. //******************************************************************************
  3021. //***** Folder Browsing Dialog Functions
  3022. //******************************************************************************
  3023.  
  3024. BOOL FolderBrowser(LPTSTR szPath, DWORD dwLength) {
  3025.  
  3026. #ifdef _WIN32_WCE
  3027.  
  3028.    // On Windows CE, we use a common save-as dialog to query the diretory.  We
  3029.    // display the dialog in this function, and then we sublass it.  Our subclass
  3030.    // functions tweaks the dialog a bit and and returns the path.
  3031.  
  3032.    ForwardSlashesToBackSlashes(szPath);
  3033.  
  3034.    TCHAR szInitialDir[_MAX_PATH];
  3035.    _tcscpy(szInitialDir, szPath);
  3036.  
  3037.    // Remove trailing wacks from path - The common dialog doesn't like them.
  3038.    size_t length = _tcslen(szInitialDir);
  3039.    while ((length > 0) && (szInitialDir[length - 1] == TEXT('\\'))) {
  3040.       szInitialDir[--length] = TEXT('\0');
  3041.    }
  3042.  
  3043.    // Set up the parameters for our save-as dialog.
  3044.    OPENFILENAME ofn;
  3045.    ZeroMemory(&ofn, sizeof(ofn));
  3046.    ofn.lStructSize     = sizeof(ofn);
  3047.    ofn.hwndOwner       = g_hWndMain;
  3048.    ofn.hInstance       = g_hInst;
  3049.    ofn.lpstrFilter     = TEXT(" \0!\0");
  3050.    ofn.nFilterIndex    = 1;
  3051.    ofn.lpstrFile       = szPath;
  3052.    ofn.nMaxFile        = dwLength;
  3053.    ofn.lpstrInitialDir = *szInitialDir ? szInitialDir : NULL;
  3054.    ofn.lpstrTitle      = TEXT("Extract To");
  3055.    ofn.Flags           = OFN_HIDEREADONLY | OFN_NOVALIDATE | OFN_NOTESTFILECREATE;
  3056.  
  3057.    // Post a message to our main window telling it that we are about to create
  3058.    // a save as dialog.  Our main window will receive this message after the
  3059.    // save as dialog is created.  This gives us a change to subclass the save as
  3060.    // dialog.
  3061.    PostMessage(g_hWndMain, WM_PRIVATE, MSG_SUBCLASS_DIALOG, 0);
  3062.  
  3063.    // Create and display the common save-as dialog.
  3064.    if (GetSaveFileName(&ofn)) {
  3065.  
  3066.       // If success, then remove are special "!" filename from the end.
  3067.       szPath[_tcslen(szPath) - 1] = TEXT('\0');
  3068.       return TRUE;
  3069.    }
  3070.    return FALSE;
  3071.  
  3072. #else // !_WIN32_WCE
  3073.  
  3074.    // On Windows NT, the shell provides us with a nice folder browser dialog.
  3075.    // We don't need to jump through any hoops to make it work like on Windows CE.
  3076.    // The only problem is that on VC 4.0, the libraries don't export the UNICODE
  3077.    // shell APIs because only Win95 had a shell library at the time.  The
  3078.    // following code requires headers and libs from VC 4.2 or later.
  3079.  
  3080.    // Set up our BROWSEINFO structure.
  3081.    BROWSEINFO bi;
  3082.    ZeroMemory(&bi, sizeof(bi));
  3083.    bi.hwndOwner = g_hWndMain;
  3084.    bi.pszDisplayName = szPath;
  3085.    bi.lpszTitle = TEXT("Extract To");
  3086.    bi.ulFlags = BIF_RETURNONLYFSDIRS;
  3087.  
  3088.    // Prompt user for path.
  3089.    LPITEMIDLIST piidl = SHBrowseForFolder(&bi);
  3090.    if (!piidl) {
  3091.       return FALSE;
  3092.    }
  3093.  
  3094.    // Build path string.
  3095.    SHGetPathFromIDList(piidl, szPath);
  3096.  
  3097.    // Free the PIDL returned by SHBrowseForFolder.
  3098.    LPMALLOC pMalloc = NULL;
  3099.    SHGetMalloc(&pMalloc);
  3100.    pMalloc->Free(piidl);
  3101.  
  3102.    // Add trailing wack if one is not present.
  3103.    size_t length = _tcslen(szPath);
  3104.    if ((length > 0) && (szPath[length - 1] != TEXT('\\'))) {
  3105.       szPath[length++] = TEXT('\\');
  3106.       szPath[length]   = TEXT('\0');
  3107.    }
  3108.  
  3109.    return TRUE;
  3110.  
  3111. #endif // _WIN32_WCE
  3112. }
  3113.  
  3114. //******************************************************************************
  3115. #ifdef _WIN32_WCE
  3116. BOOL CALLBACK DlgProcBrowser(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3117.  
  3118.    // This is our subclass of Windows CE's common save-as dialog.  We intercept
  3119.    // the messages we care about and forward everything else to the original
  3120.    // window procedure for the dialog.
  3121.  
  3122.    if (uMsg == WM_PRIVATE) { // wParam always equals MSG_INIT_DIALOG
  3123.  
  3124.       RECT rc1, rc2;
  3125.  
  3126.       // Get the window rectangle for the name edit control.
  3127.       HWND hWnd = GetDlgItem(hDlg, IDC_SAVE_NAME_EDIT);
  3128.       GetWindowRect(hWnd, &rc1);
  3129.       POINT pt1 = { rc1.left, rc1.top };
  3130.       ScreenToClient(hDlg, &pt1);
  3131.  
  3132.       // Hide all the windows we don't want.
  3133.       ShowWindow(hWnd, SW_HIDE);
  3134.       ShowWindow(GetDlgItem(hDlg, IDC_SAVE_NAME_PROMPT), SW_HIDE);
  3135.       ShowWindow(GetDlgItem(hDlg, IDC_SAVE_TYPE_PROMPT), SW_HIDE);
  3136.       ShowWindow(GetDlgItem(hDlg, IDC_SAVE_TYPE_LIST), SW_HIDE);
  3137.  
  3138.       // Get the window rectangle for the file list.
  3139.       hWnd = GetDlgItem(hDlg, IDC_SAVE_FILE_LIST);
  3140.       GetWindowRect(hWnd, &rc2);
  3141.       POINT pt2 = { rc2.left, rc2.top };
  3142.       ScreenToClient(hDlg, &pt2);
  3143.  
  3144.       // Resize the file list to fill the dialog.
  3145.       MoveWindow(hWnd, pt2.x, pt2.y, rc2.right - rc2.left, rc1.bottom - rc2.top, TRUE);
  3146.  
  3147.    } else if ((uMsg == WM_COMMAND) && (LOWORD(wParam) == IDOK)) {
  3148.  
  3149.       // Get our file list window.
  3150.       HWND hWnd = GetDlgItem(hDlg, IDC_SAVE_FILE_LIST);
  3151.  
  3152.       // Check to see if a directory is selected.
  3153.       if (ListView_GetNextItem(hWnd, -1, LVNI_SELECTED) >= 0) {
  3154.  
  3155.          // If a directory is highlighted, then we post ourself a "Ok".  The "Ok"
  3156.          // we are processing now will cause us to change into the highlighted
  3157.          // directory, and our posted "Ok" will close the dialog in that directory.
  3158.          PostMessage(hDlg, uMsg, wParam, lParam);
  3159.  
  3160.       } else {
  3161.          // If no directory is selected, then enter the imaginary filename "!"
  3162.          // into the name edit control and let the "Ok" end this dialog. The
  3163.          // result will be the correct path with a "\!" at the end.
  3164.          SetDlgItemText(hDlg, IDC_SAVE_NAME_EDIT, TEXT("!"));
  3165.       }
  3166.    }
  3167.  
  3168.    // Pass all messages to the base control's window proc.
  3169.    return CallWindowProc(g_wpSaveAsDlg, hDlg, uMsg, wParam, lParam);
  3170. }
  3171. #endif // _WIN32_WCE
  3172.  
  3173. //******************************************************************************
  3174. #ifdef _WIN32_WCE
  3175. void SubclassSaveAsDlg() {
  3176.  
  3177.    // Get our current thread ID so we can compare it to other thread IDs.
  3178.    DWORD dwThreadId = GetCurrentThreadId();
  3179.  
  3180.    // Get the the top window in the z-order that is a child of the desktop.
  3181.    // Dialogs are always children of the desktop on CE.  This first window
  3182.    // should be the dialog we are looking for, but we will walk the window list
  3183.    // just in case.
  3184.    HWND hWnd = GetWindow(g_hWndMain, GW_HWNDFIRST);
  3185.  
  3186.    // Walk the window list.
  3187.    while (hWnd) {
  3188.  
  3189.       // Check to see if this window was created by us and has controls from a
  3190.       // common "save as" dialog.
  3191.       if ((GetWindowThreadProcessId(hWnd, NULL) == dwThreadId) &&
  3192.            GetDlgItem(hWnd, IDC_SAVE_FILE_LIST) &&
  3193.            GetDlgItem(hWnd, IDC_SAVE_NAME_EDIT))
  3194.       {
  3195.          // We found our dialog.  Subclass it.
  3196.          g_wpSaveAsDlg = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
  3197.          SetWindowLong(hWnd, GWL_WNDPROC, (LONG)DlgProcBrowser);
  3198.  
  3199.          // Send our new dialog a message so it can do its initialization.
  3200.          SendMessage(hWnd, WM_PRIVATE, MSG_INIT_DIALOG, 0);
  3201.       }
  3202.  
  3203.       // Get the next window in our window list.
  3204.       hWnd = GetWindow(hWnd, GW_HWNDNEXT);
  3205.    }
  3206. }
  3207. #endif // _WIN32_WCE
  3208.  
  3209.  
  3210. //******************************************************************************
  3211. //***** Extraction/Test/View Progress Dialog Functions
  3212. //******************************************************************************
  3213.  
  3214. BOOL CALLBACK DlgProcExtractProgress(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3215.  
  3216.    static EXTRACT_INFO *pei;
  3217.    static BOOL fComplete;
  3218.    static HWND hWndButton;
  3219.    TCHAR szBuffer[32];
  3220.  
  3221.    switch (uMsg) {
  3222.  
  3223.       case WM_INITDIALOG:
  3224.  
  3225.          // Globally store our handle so our worker thread can post to us.
  3226.          g_hDlgProgress = hDlg;
  3227.  
  3228.          // Get a pointer to our extract information structure.
  3229.          pei = (EXTRACT_INFO*)lParam;
  3230.  
  3231.          // Clear our complete flag.  It will be set to TRUE when done.
  3232.          fComplete = FALSE;
  3233.  
  3234.          // Get and store our edit control.
  3235.          g_hWndEdit = GetDlgItem(hDlg, IDC_LOG);
  3236.  
  3237.          // Disable our edit box from being edited.
  3238.          DisableEditing(g_hWndEdit);
  3239.  
  3240.          // Store a static handle for our Abort/Close button.
  3241.          hWndButton = GetDlgItem(hDlg, IDCANCEL);
  3242.  
  3243. #ifdef _WIN32_WCE
  3244.  
  3245.          // Set our No-Drag style
  3246.          SetWindowLong(hDlg, GWL_EXSTYLE, WS_EX_NODRAG |GetWindowLong(hDlg, GWL_EXSTYLE));
  3247.  
  3248.          RECT rc1, rc2, rcEdit;
  3249.  
  3250.          // Get our current client size.
  3251.          GetClientRect(hDlg, &rc1);
  3252.  
  3253.          // Get the window rectangle for the edit control in client coordinates.
  3254.          GetWindowRect(g_hWndEdit, &rcEdit);
  3255.          ScreenToClient(hDlg, ((POINT*)&rcEdit));
  3256.          ScreenToClient(hDlg, ((POINT*)&rcEdit) + 1);
  3257.  
  3258.          // Resize our dialog to be full screen (same size as parent).
  3259.          GetWindowRect(g_hWndMain, &rc2);
  3260.          MoveWindow(hDlg, rc2.left, rc2.top, rc2.right - rc2.left,
  3261.                     rc2.bottom - rc2.top + 1, FALSE);
  3262.  
  3263.          // Get our new client size.
  3264.          GetClientRect(hDlg, &rc2);
  3265.  
  3266.          // Resize our edit box to fill the client.
  3267.          MoveWindow(g_hWndEdit, rcEdit.left, rcEdit.top,
  3268.                     (rcEdit.right  - rcEdit.left) + (rc2.right  - rc1.right),
  3269.                     (rcEdit.bottom - rcEdit.top)  + (rc2.bottom - rc1.bottom),
  3270.                     FALSE);
  3271.  
  3272. #else
  3273.          // On NT, we just center our dialog over our parent.
  3274.          CenterWindow(hDlg);
  3275. #endif
  3276.  
  3277.          // Store some globals until the extract/test finishes.
  3278.          pei->hWndEditFile       = GetDlgItem(hDlg, IDC_FILE);
  3279.          pei->hWndProgFile       = GetDlgItem(hDlg, IDC_FILE_PROGRESS);
  3280.          pei->hWndProgTotal      = GetDlgItem(hDlg, IDC_TOTAL_PROGRESS);
  3281.          pei->hWndPercentage     = GetDlgItem(hDlg, IDC_PERCENTAGE);
  3282.          pei->hWndFilesProcessed = GetDlgItem(hDlg, IDC_FILES_PROCESSED);
  3283.          pei->hWndBytesProcessed = GetDlgItem(hDlg, IDC_BYTES_PROCESSED);
  3284.  
  3285.          if (pei->fExtract) {
  3286.             // Set our main window's caption.
  3287.             SetCaptionText(TEXT("Extracting"));
  3288.  
  3289.          } else {
  3290.             // Set our main window's caption.
  3291.             SetCaptionText(TEXT("Testing"));
  3292.  
  3293.             // Hide the current file progress for test since it never moves.
  3294.             ShowWindow(pei->hWndProgFile, SW_HIDE);
  3295.          }
  3296.  
  3297.          // Set the ranges on our progress bars.
  3298.          SendMessage(pei->hWndProgFile,  PBM_SETRANGE, 0,
  3299.                      MAKELPARAM(0, PROGRESS_MAX));
  3300.          SendMessage(pei->hWndProgTotal, PBM_SETRANGE, 0,
  3301.                      MAKELPARAM(0, PROGRESS_MAX));
  3302.  
  3303.          // Set our file and byte totals.
  3304.          SetDlgItemText(hDlg, IDC_FILES_TOTAL,
  3305.                         FormatValue(szBuffer, pei->dwFileCount));
  3306.          SetDlgItemText(hDlg, IDC_BYTES_TOTAL,
  3307.                         FormatValue(szBuffer, pei->uzByteCount));
  3308.  
  3309.          // Launch our Extract/Test thread and wait for WM_PRIVATE
  3310.          DoExtractOrTestFiles(g_szZipFile, pei);
  3311.  
  3312.          return TRUE;
  3313.  
  3314.  
  3315.       case WM_PRIVATE: // Sent with wParam equal to MSG_OPERATION_COMPLETE when
  3316.                        // test/extract is complete.
  3317.  
  3318.          // Check to see if the operation was a success
  3319.          if ((pei->result == PK_OK) || (pei->result == PK_WARN)) {
  3320.  
  3321.             // Set all our fields to their "100%" settings.
  3322.             SendMessage(pei->hWndProgFile,  PBM_SETPOS, PROGRESS_MAX, 0);
  3323.             SendMessage(pei->hWndProgTotal, PBM_SETPOS, PROGRESS_MAX, 0);
  3324.             SetWindowText(pei->hWndPercentage, TEXT("100%"));
  3325.             SetDlgItemText(hDlg, IDC_FILES_PROCESSED,
  3326.                            FormatValue(szBuffer, pei->dwFileCount));
  3327.             SetDlgItemText(hDlg, IDC_BYTES_PROCESSED,
  3328.                            FormatValue(szBuffer, pei->uzByteCount));
  3329.          }
  3330.  
  3331.          // Update our status text.
  3332.          SetWindowText(pei->hWndEditFile,
  3333.             (pei->result == PK_OK)      ? TEXT("Completed.  There were no warnings or errors.") :
  3334.             (pei->result == PK_WARN)    ? TEXT("Completed.  There was one or more warnings.") :
  3335.             (pei->result == PK_ABORTED) ? TEXT("Aborted.  There may be warnings or errors.") :
  3336.                                           TEXT("Completed.  There was one or more errors."));
  3337.  
  3338.          // Clear our global edit handle.
  3339.          g_hWndEdit = NULL;
  3340.  
  3341.          // Update our caption to show that we are done extracting/testing.
  3342.          SetCaptionText(NULL);
  3343.  
  3344.          // Change our abort button to now read "Close".
  3345.          SetWindowText(hWndButton, TEXT("&Close"));
  3346.          EnableWindow(hWndButton, TRUE);
  3347.  
  3348.          // Display an error dialog if an error occurred.
  3349.          if ((pei->result != PK_OK) && (pei->result != PK_WARN)) {
  3350.             MessageBox(hDlg, GetZipErrorString(pei->result),
  3351.                        g_szAppName, MB_ICONERROR | MB_OK);
  3352.          }
  3353.  
  3354.          // We are done.  Allow the user to close the dialog.
  3355.          fComplete = TRUE;
  3356.          return FALSE;
  3357.  
  3358.       case WM_COMMAND:
  3359.          switch (LOWORD(wParam)) {
  3360.             case IDCANCEL:
  3361.                // If abort is pressed, then set a flag that our worker thread
  3362.                // periodically checks to decide if it needs to bail out.
  3363.                if (!fComplete && !pei->fAbort) {
  3364.                   pei->fAbort = TRUE;
  3365.                   SetWindowText(hWndButton, TEXT("Aborting..."));
  3366.                   EnableWindow(hWndButton, FALSE);
  3367.                   return FALSE;
  3368.                }
  3369.                // fall through to IDOK
  3370.  
  3371.             case IDOK:
  3372.                // Don't allow dialog to close until extract/test is complete.
  3373.                if (fComplete) {
  3374.                   g_hDlgProgress = NULL;
  3375.                   EndDialog(hDlg, LOWORD(wParam));
  3376.                }
  3377.                return FALSE;
  3378.          }
  3379.          return FALSE;
  3380.    }
  3381.    return FALSE;
  3382. }
  3383.  
  3384. //******************************************************************************
  3385. BOOL CALLBACK DlgProcViewProgress(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3386.  
  3387.    static EXTRACT_INFO *pei;
  3388.  
  3389.    switch (uMsg) {
  3390.  
  3391.       case WM_INITDIALOG:
  3392.  
  3393.          // Globally store our handle so our worker thread can post to us.
  3394.          g_hDlgProgress = hDlg;
  3395.  
  3396.          // Get a pointer to our extract information structure.
  3397.          pei = (EXTRACT_INFO*)lParam;
  3398.  
  3399.          // Center our dialog over our parent.
  3400.          CenterWindow(hDlg);
  3401.  
  3402.          // Store some globals until the extract finishes.
  3403.          pei->hWndProgFile = GetDlgItem(hDlg, IDC_FILE_PROGRESS);
  3404.  
  3405.          // Set the ranges on our progress bar.
  3406.          SendDlgItemMessage(hDlg, IDC_FILE_PROGRESS, PBM_SETRANGE, 0,
  3407.                             MAKELPARAM(0, PROGRESS_MAX));
  3408.  
  3409.          // Launch our Extract thread and wait for WM_PRIVATE message.
  3410.          DoExtractOrTestFiles(g_szZipFile, pei);
  3411.  
  3412.          return TRUE;
  3413.  
  3414.       case WM_PRIVATE: // Sent with wParam equal to MSG_OPERATION_COMPLETE when
  3415.                        // test/extract is complete.
  3416.  
  3417.          // We are done.  Close our dialog.  Any errors will be reported by
  3418.          // OnActionView().
  3419.          g_hDlgProgress = NULL;
  3420.          EndDialog(hDlg, LOWORD(wParam));
  3421.          return FALSE;
  3422.  
  3423.       case WM_COMMAND:
  3424.          // If abort is pressed, then set a flag that our worker thread
  3425.          // periodically checks to decide if it needs to bail out.
  3426.          if ((LOWORD(wParam) == IDCANCEL) && !pei->fAbort) {
  3427.             pei->fAbort = TRUE;
  3428.             SetWindowText(GetDlgItem(hDlg, IDCANCEL), TEXT("Aborting..."));
  3429.             EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
  3430.             return FALSE;
  3431.          }
  3432.    }
  3433.  
  3434.    return FALSE;
  3435. }
  3436.  
  3437. //******************************************************************************
  3438. void UpdateProgress(EXTRACT_INFO *pei, BOOL fFull) {
  3439.  
  3440.    DWORD dwFile, dwTotal, dwPercentage;
  3441.    TCHAR szBuffer[_MAX_PATH + 32];
  3442.  
  3443.    // Compute our file progress bar position.
  3444.    if (pei->uzBytesTotalThisFile) {
  3445.       dwFile = (DWORD)(((DWORDLONG)PROGRESS_MAX *
  3446.                         (DWORDLONG)pei->uzBytesWrittenThisFile) /
  3447.                         (DWORDLONG)pei->uzBytesTotalThisFile);
  3448.    } else {
  3449.       dwFile = PROGRESS_MAX;
  3450.    }
  3451.  
  3452.    // Set our file progress indicators.
  3453.    SendMessage(pei->hWndProgFile,  PBM_SETPOS, dwFile,  0);
  3454.  
  3455.    // If we are only updating our View Progress dialog, then we are done.
  3456.    if (!pei->hWndProgTotal) {
  3457.       return;
  3458.    }
  3459.  
  3460.    // Compute our total progress bar position.
  3461.    dwTotal = (DWORD)(((DWORDLONG)PROGRESS_MAX *
  3462.                       (DWORDLONG)(pei->uzBytesWrittenPreviousFiles +
  3463.                                   pei->uzBytesWrittenThisFile +
  3464.                                   pei->dwFile)) /
  3465.                       (DWORDLONG)(pei->uzByteCount +
  3466.                                   pei->dwFileCount));
  3467.    dwPercentage = dwTotal / (PROGRESS_MAX / 100);
  3468.  
  3469.    // Set our total progress indicators.
  3470.    SendMessage(pei->hWndProgTotal, PBM_SETPOS, dwTotal, 0);
  3471.  
  3472.    // Set our total percentage text.
  3473.    _stprintf(szBuffer, TEXT("%u%%"), dwPercentage);
  3474.    SetWindowText(pei->hWndPercentage, szBuffer);
  3475.  
  3476.    // Set our current file and byte process counts.
  3477.    FormatValue(szBuffer, pei->dwFile - 1);
  3478.    SetWindowText(pei->hWndFilesProcessed, szBuffer);
  3479.    FormatValue(szBuffer, pei->uzBytesWrittenPreviousFiles +
  3480.                pei->uzBytesWrittenThisFile);
  3481.    SetWindowText(pei->hWndBytesProcessed, szBuffer);
  3482.  
  3483.  
  3484.    if (fFull) {
  3485.  
  3486.       // Build our message string.
  3487.       _tcscpy(szBuffer, pei->fExtract ? TEXT("Extract") : TEXT("Test"));
  3488.       size_t preflen = _tcslen(szBuffer);
  3489.       MBSTOTSTR(szBuffer+preflen, pei->szFile,countof(szBuffer)-preflen);
  3490.  
  3491.       // Change all forward slashes to back slashes in the buffer.
  3492.       ForwardSlashesToBackSlashes(szBuffer);
  3493.  
  3494.       // Update the file name in our dialog.
  3495.       SetWindowText(pei->hWndEditFile, szBuffer);
  3496.    }
  3497. }
  3498.  
  3499.  
  3500. //******************************************************************************
  3501. //***** Replace File Dialog Functions
  3502. //******************************************************************************
  3503.  
  3504. int PromptToReplace(LPCSTR szPath) {
  3505.  
  3506.    // Check to see if we are extracting for view only.
  3507.    if (g_fViewing) {
  3508.  
  3509.       // Build prompt.
  3510.       TCHAR szMessage[_MAX_PATH + 128];
  3511.       _stprintf(szMessage,
  3512. #ifdef UNICODE
  3513.          TEXT("A file named \"%S\" has already been extracted for viewing.  ")
  3514. #else
  3515.          TEXT("A file named \"%s\" has already been extracted for viewing.  ")
  3516. #endif
  3517.          TEXT("That file might be opened and locked for viewing by another application.\n\n")
  3518.          TEXT("Would you like to attempt to overwrite it with the new file?"),
  3519.          GetFileFromPath(szPath));
  3520.  
  3521.       // Display prompt.
  3522.       if (IDYES == MessageBox(g_hDlgProgress, szMessage, g_szAppName,
  3523.                               MB_ICONWARNING | MB_YESNO))
  3524.       {
  3525.          // Tell Info-ZIP to continue with extraction.
  3526.          return IDM_REPLACE_YES;
  3527.       }
  3528.  
  3529.       // Remember that the file was skipped and tell Info-ZIP to abort extraction.
  3530.       g_fSkipped = TRUE;
  3531.       return IDM_REPLACE_NO;
  3532.    }
  3533.  
  3534.    // Otherwise, do the normal replace prompt dialog.
  3535.    return DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_REPLACE), g_hWndMain,
  3536.                          (DLGPROC)DlgProcReplace, (LPARAM)szPath);
  3537. }
  3538.  
  3539. //******************************************************************************
  3540. BOOL CALLBACK DlgProcReplace(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3541.    TCHAR szMessage[_MAX_PATH + 32];
  3542.  
  3543.    switch (uMsg) {
  3544.  
  3545.       case WM_INITDIALOG:
  3546.  
  3547.          // Play the question tone to alert the user.
  3548.          MessageBeep(MB_ICONQUESTION);
  3549.  
  3550.          // Display a message with the file name.
  3551. #ifdef UNICODE
  3552.          _stprintf(szMessage, TEXT("\"%S\" already exists."), (LPCSTR)lParam);
  3553. #else
  3554.          _stprintf(szMessage, TEXT("\"%s\" already exists."), (LPCSTR)lParam);
  3555. #endif
  3556.  
  3557.          // Change all forward slashes to back slashes in the buffer.
  3558.          ForwardSlashesToBackSlashes(szMessage);
  3559.  
  3560.          // Display the file string.
  3561.          SetDlgItemText(hDlg, IDC_FILE, szMessage);
  3562.  
  3563.          // Center our dialog over our parent.
  3564.          CenterWindow(hDlg);
  3565.          return TRUE;
  3566.  
  3567.       case WM_COMMAND:
  3568.          switch (LOWORD(wParam)) {
  3569.  
  3570.             case IDCANCEL:
  3571.             case IDOK:
  3572.                EndDialog(hDlg, IDM_REPLACE_NO);
  3573.                break;
  3574.  
  3575.             case IDM_REPLACE_ALL:
  3576.             case IDM_REPLACE_NONE:
  3577.             case IDM_REPLACE_YES:
  3578.             case IDM_REPLACE_NO:
  3579.                EndDialog(hDlg, wParam);
  3580.                break;
  3581.          }
  3582.          return FALSE;
  3583.    }
  3584.    return FALSE;
  3585. }
  3586.  
  3587.  
  3588. //******************************************************************************
  3589. //***** Password Dialog Functions
  3590. //******************************************************************************
  3591.  
  3592. #if CRYPT
  3593.  
  3594. BOOL CALLBACK DlgProcPassword(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3595.  
  3596.    // Return Values:
  3597.    //    IZ_PW_ENTERED    got some PWD string, use/try it
  3598.    //    IZ_PW_CANCEL     no password available (for this entry)
  3599.    //    IZ_PW_CANCELALL  no password, skip any further PWD request
  3600.    //    IZ_PW_ERROR      failure (no mem, no tty, ...)
  3601.  
  3602.    static DECRYPT_INFO *pdi;
  3603.    TCHAR szMessage[_MAX_PATH + 32];
  3604.  
  3605.    switch (uMsg) {
  3606.  
  3607.       case WM_INITDIALOG:
  3608.  
  3609.          // Play the question tone to alert the user.
  3610.          MessageBeep(MB_ICONQUESTION);
  3611.  
  3612. #ifdef _WIN32_WCE
  3613.          // Add "Ok" button to caption bar.
  3614.          SetWindowLong(hDlg, GWL_EXSTYLE, WS_EX_CAPTIONOKBTN |
  3615.                        GetWindowLong(hDlg, GWL_EXSTYLE));
  3616. #endif
  3617.  
  3618.          // Store our decrypt information structure.
  3619.          pdi = (DECRYPT_INFO*)lParam;
  3620.  
  3621.          // Display a message with the file name.
  3622. #ifdef UNICODE
  3623.          _stprintf(szMessage, TEXT("\"%S\" is encrypted."), pdi->szFile);
  3624. #else
  3625.          _stprintf(szMessage, TEXT("\"%s\" is encrypted."), pdi->szFile);
  3626. #endif
  3627.  
  3628.          // Change all forward slashes to back slashes in the buffer.
  3629.          ForwardSlashesToBackSlashes(szMessage);
  3630.  
  3631.          // Display the message with the file name.
  3632.          SetDlgItemText(hDlg, IDC_FILE, szMessage);
  3633.  
  3634.          // Display the appropriate prompt.
  3635.          if (pdi->retry) {
  3636.             _stprintf(szMessage, TEXT("Password was incorrect. Please re-enter (%d/%d)."),
  3637.                      MAX_PASSWORD_RETRIES - pdi->retry + 2, MAX_PASSWORD_RETRIES + 1);
  3638.             SetDlgItemText(hDlg, IDC_PROMPT, szMessage);
  3639.          } else {
  3640.             SetDlgItemText(hDlg, IDC_PROMPT, TEXT("Please enter the password."));
  3641.          }
  3642.  
  3643.          // Limit the password to the size of the password buffer we have been given.
  3644.          SendDlgItemMessage(hDlg, IDC_PASSWORD, EM_LIMITTEXT, pdi->nSize - 1, 0);
  3645.  
  3646.          // Center our dialog over our parent.
  3647.          CenterWindow(hDlg);
  3648.          return TRUE;
  3649.  
  3650.       case WM_COMMAND:
  3651.          switch (LOWORD(wParam)) {
  3652.  
  3653.             case IDOK:
  3654.  
  3655.                // Store the password in our return password buffer.
  3656.                GetDlgItemText(hDlg, IDC_PASSWORD, szMessage, countof(szMessage));
  3657.                TSTRTOMBS(pdi->szPassword, szMessage, pdi->nSize);
  3658.                EndDialog(hDlg, IZ_PW_ENTERED);
  3659.                return FALSE;
  3660.  
  3661.             case IDCANCEL:
  3662.                g_fSkipped = TRUE;
  3663.                EndDialog(hDlg, IZ_PW_CANCEL);
  3664.                return FALSE;
  3665.  
  3666.             case IDC_SKIP_ALL:
  3667.                g_fSkipped = TRUE;
  3668.                EndDialog(hDlg, IZ_PW_CANCELALL);
  3669.                return FALSE;
  3670.          }
  3671.          return FALSE;
  3672.    }
  3673.    return FALSE;
  3674. }
  3675.  
  3676. #endif // CRYPT
  3677.  
  3678. //******************************************************************************
  3679. //***** View Association Dialog Functions
  3680. //******************************************************************************
  3681.  
  3682. BOOL CALLBACK DlgProcViewAssociation(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3683.  
  3684.    static LPTSTR szApp;
  3685.  
  3686.    switch (uMsg) {
  3687.  
  3688.       case WM_INITDIALOG:
  3689.          // Store the path buffer for our application.
  3690.          szApp = (LPTSTR)lParam;
  3691.  
  3692.          // Read our default viewer from the registry.
  3693. #ifdef _WIN32_WCE
  3694.          GetOptionString(TEXT("FileViewer"), TEXT("\\Windows\\PWord.exe"),
  3695.                          szApp, sizeof(TCHAR) * _MAX_PATH);
  3696. #else
  3697.          GetOptionString(TEXT("FileViewer"), TEXT("notepad.exe"),
  3698.                          szApp, sizeof(TCHAR) * _MAX_PATH);
  3699. #endif
  3700.  
  3701.          // Limit our edit control to our buffer size.
  3702.          SendDlgItemMessage(hDlg, IDC_PATH, EM_LIMITTEXT, _MAX_PATH - 1, 0);
  3703.  
  3704.          // Set our path string in our dialog.
  3705.          SetDlgItemText(hDlg, IDC_PATH, szApp);
  3706.  
  3707.          // Center our dialog over our parent.
  3708.          CenterWindow(hDlg);
  3709.          return TRUE;
  3710.  
  3711.       case WM_COMMAND:
  3712.          switch (LOWORD(wParam)) {
  3713.  
  3714.             case IDOK:
  3715.                // Get the text currently in the path edit box and store it.
  3716.                GetDlgItemText(hDlg, IDC_PATH, szApp, _MAX_PATH);
  3717.                WriteOptionString(TEXT("FileViewer"), szApp);
  3718.                // Fall through
  3719.  
  3720.             case IDCANCEL:
  3721.                EndDialog(hDlg, LOWORD(wParam));
  3722.                break;
  3723.  
  3724.             case IDC_BROWSE:
  3725.                // Get the text currently in the path edit box.
  3726.                GetDlgItemText(hDlg, IDC_PATH, szApp, _MAX_PATH);
  3727.  
  3728.                // Get the direcory from the path text.
  3729.                ForwardSlashesToBackSlashes(szApp);
  3730.                TCHAR szInitialDir[_MAX_PATH], *szFile;
  3731.                _tcscpy(szInitialDir, szApp);
  3732.                if (szFile = _tcsrchr(szInitialDir, TEXT('\\'))) {
  3733.                   *szFile = TEXT('\0');
  3734.                }
  3735.  
  3736.                // Prepare to display browse dialog.
  3737.                OPENFILENAME ofn;
  3738.                ZeroMemory(&ofn, sizeof(ofn));
  3739.                ofn.lStructSize     = sizeof(ofn);
  3740.                ofn.hwndOwner       = hDlg;
  3741.                ofn.hInstance       = g_hInst;
  3742.                ofn.lpstrFilter     = TEXT("Programs (*.exe)\0*.exe\0All Files (*.*)\0*.*\0");
  3743.                ofn.nFilterIndex    = 1;
  3744.                ofn.lpstrFile       = szApp;
  3745.                ofn.nMaxFile        = _MAX_PATH;
  3746.                ofn.lpstrInitialDir = szInitialDir;
  3747.                ofn.lpstrTitle      = TEXT("Open With...");
  3748.                ofn.Flags           = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
  3749.                ofn.lpstrDefExt     = TEXT("exe");
  3750.  
  3751.                // Display the browse dialog and update our path edit box if neccessary.
  3752.                if (GetOpenFileName(&ofn)) {
  3753.                   SetDlgItemText(hDlg, IDC_PATH, szApp);
  3754.                }
  3755.                break;
  3756.          }
  3757.          return FALSE;
  3758.    }
  3759.    return FALSE;
  3760. }
  3761.  
  3762.  
  3763. //******************************************************************************
  3764. //***** Comment Dialog Functions
  3765. //******************************************************************************
  3766.  
  3767. BOOL CALLBACK DlgProcComment(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3768.    RECT    rc;
  3769.    HCURSOR hCur;
  3770.    int     result;
  3771.  
  3772.    switch (uMsg) {
  3773.  
  3774.       case WM_INITDIALOG:
  3775.          // Get the handle to our edit box and store it globally.
  3776.          g_hWndEdit = GetDlgItem(hDlg, IDC_COMMENT);
  3777.  
  3778.          // Disable our edit box from being edited.
  3779.          DisableEditing(g_hWndEdit);
  3780.  
  3781. #ifdef _WIN32_WCE
  3782.          // Add "Ok" button to caption bar and make window No-Drag.
  3783.          SetWindowLong(hDlg, GWL_EXSTYLE, WS_EX_CAPTIONOKBTN | WS_EX_NODRAG |
  3784.                        GetWindowLong(hDlg, GWL_EXSTYLE));
  3785.  
  3786.          // On CE, we resize our dialog to be full screen (same size as parent).
  3787.          GetWindowRect(g_hWndMain, &rc);
  3788.          MoveWindow(hDlg, rc.left, rc.top, rc.right - rc.left,
  3789.                     rc.bottom - rc.top + 1, FALSE);
  3790. #else
  3791.          // On NT we just center the dialog.
  3792.          CenterWindow(hDlg);
  3793. #endif
  3794.  
  3795.          // Set our edit control to be the full size of our dialog.
  3796.          GetClientRect(hDlg, &rc);
  3797.          MoveWindow(g_hWndEdit, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
  3798.  
  3799.          // Show hour glass cursor while processing comment.
  3800.          hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  3801.  
  3802.          // Let Info-ZIP and our callbacks do the work.
  3803.          result = DoGetComment(g_szZipFile);
  3804.  
  3805.          // Restore/remove our cursor.
  3806.          SetCursor(hCur);
  3807.  
  3808.          // Display an error dialog if an error occurred.
  3809.          if ((result != PK_OK) && (result != PK_WARN)) {
  3810.             MessageBox(g_hWndMain, GetZipErrorString(result), g_szAppName,
  3811.                        MB_ICONERROR | MB_OK);
  3812.          }
  3813.  
  3814.          // Clear our global edit box handle as we are done with it.
  3815.          g_hWndEdit = NULL;
  3816.  
  3817.          // Return FALSE to prevent edit box from gaining focus and showing highlight.
  3818.          return FALSE;
  3819.  
  3820.       case WM_COMMAND:
  3821.          if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) {
  3822.             EndDialog(hDlg, LOWORD(wParam));
  3823.          }
  3824.          return FALSE;
  3825.    }
  3826.    return FALSE;
  3827. }
  3828.  
  3829.  
  3830. //******************************************************************************
  3831. //***** About Dialog Functions
  3832. //******************************************************************************
  3833.  
  3834. BOOL CALLBACK DlgProcAbout(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  3835.  
  3836.    switch (uMsg) {
  3837.  
  3838.       case WM_INITDIALOG:
  3839.  
  3840. #ifdef _WIN32_WCE
  3841.          // Add "Ok" button to caption bar.
  3842.          SetWindowLong(hDlg, GWL_EXSTYLE, WS_EX_CAPTIONOKBTN |
  3843.                        GetWindowLong(hDlg, GWL_EXSTYLE));
  3844. #endif
  3845.  
  3846.          // Fill in a few static members.
  3847.          // (For VER_FULLVERSION_STR and VER_COMMENT_STR, the TEXT() macro is
  3848.          //  not applicable, because they are defined as a set of concatenated
  3849.          //  string constants. These strings need to be converted to UNICODE
  3850.          //  at runtime, sigh.)
  3851.          TCHAR szBuffer[128];
  3852.          SetDlgItemText(hDlg, IDC_PRODUCT, TEXT(VER_PRODUCT_STR));
  3853. #ifdef UNICODE
  3854.          _stprintf(szBuffer, TEXT("Freeware Version %S"), VER_FULLVERSION_STR);
  3855. #else
  3856.          _stprintf(szBuffer, TEXT("Freeware Version %s"), VER_FULLVERSION_STR);
  3857. #endif
  3858.          SetDlgItemText(hDlg, IDC_VERSION, szBuffer);
  3859.          _stprintf(szBuffer, TEXT("Developed by %s"), TEXT(VER_DEVELOPER_STR));
  3860.          SetDlgItemText(hDlg, IDC_DEVELOPER, szBuffer);
  3861.          SetDlgItemText(hDlg, IDC_COPYRIGHT, TEXT(VER_COPYRIGHT_STR));
  3862. #ifdef UNICODE
  3863.          _stprintf(szBuffer, TEXT("%S"), VER_COMMENT_STR);
  3864.          SetDlgItemText(hDlg, IDC_COMMENT, szBuffer);
  3865. #else
  3866.          SetDlgItemText(hDlg, IDC_COMMENT, VER_COMMENT_STR);
  3867. #endif
  3868.  
  3869.          // Center the dialog over our parent.
  3870.          CenterWindow(hDlg);
  3871.          return TRUE;
  3872.  
  3873.       case WM_COMMAND:
  3874.          if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) {
  3875.             EndDialog(hDlg, 0);
  3876.          }
  3877.          return FALSE;
  3878.    }
  3879.    return FALSE;
  3880. }
  3881.