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