home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / unzip532.zip / wince / winmain.cpp < prev    next >
C/C++ Source or Header  |  1997-04-27  |  134KB  |  3,780 lines

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