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