Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1999-2003, Ghostgum Software Pty Ltd.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
// $Id: dwsetup.cpp,v 1.11 2005/03/04 21:58:55 ghostgum Exp $
18
//
19
//
20
// This is the setup program for Win32 AFPL Ghostscript
21
//
22
// The starting point is a self extracting zip archive
23
// with the following contents:
24
//   setupgs.exe
25
//   uninstgs.exe
26
//   filelist.txt      (contains list of program files)
27
//   fontlist.txt      (contains list of font files)
28
//   gs#.##\*          (files listed in filelist.txt)
29
//   fonts\*           (fonts listed in fontlist.txt)
30
// This is the same as the zip file created by Aladdin Enterprises,
31
// with the addition of setupgs.exe, uninstgs.exe, filelist.txt and 
32
// fontlist.txt.
33
//
34
// The first line of the files filelist.txt and fontlist.txt
35
// contains the uninstall name to be used.  
36
// The second line contains name of the main directory where 
37
// uninstall log files are to be placed.  
38
// Subsequent lines contain files to be copied (but not directories).
39
// For example, filelist.txt might contain:
40
//   AFPL Ghostscript 6.50
41
//   gs6.50
42
//   gs6.50\bin\gsdll32.dll
43
//   gs6.50\lib\gs_init.ps
44
// The file fontlist.txt might contain:
45
//   AFPL Ghostscript Fonts
46
//   fonts
47
//   fonts\n019003l.pfb
48
//   fonts\n019023l.pfb
49
//
50
// The default install directory is c:\gs.
51
// The default Start Menu Folder is Ghostscript.
52
// These are set in the resources.
53
// The setup program will create the following uninstall log files
54
//   c:\gs\gs#.##\uninstal.txt
55
//   c:\gs\fonts\uninstal.txt
56
// The uninstall program (accessed through control panel) will not 
57
// remove directories nor will it remove itself.
58
//
59
// If the install directory is the same as the current file 
60
// location, no files will be copied, but the existence of each file 
61
// will be checked.  This allows the archive to be unzipped, then
62
// configured in its current location.  Running the uninstall will not 
63
// remove uninstgs.exe, setupgs.exe, filelist.txt or fontlist.txt.
64
 
65
 
66
#define STRICT
67
#include <windows.h>
68
#include <shellapi.h>
69
#include <objbase.h>
70
#include <shlobj.h>
71
#include <stdio.h>
72
#include <direct.h>
73
 
74
#ifdef MAX_PATH
75
#define MAXSTR MAX_PATH
76
#else
77
#define MAXSTR 256
78
#endif
79
 
80
#include "dwsetup.h"
81
#include "dwinst.h"
82
 
83
extern "C" {
84
typedef HRESULT (WINAPI *PFN_SHGetFolderPath)(
85
  HWND hwndOwner,
86
  int nFolder,
87
  HANDLE hToken,
88
  DWORD dwFlags,
89
  LPSTR pszPath);
90
 
91
typedef BOOL (WINAPI *PFN_SHGetSpecialFolderPath)(
92
  HWND hwndOwner,
93
  LPTSTR lpszPath,
94
  int nFolder,
95
  BOOL fCreate);
96
}
97
 
98
//#define DEBUG
99
 
100
#define UNINSTALLPROG "uninstgs.exe"
101
 
102
 
103
/////////////////////////////////
104
// Globals
105
 
106
CInstall cinst;
107
 
108
// TRUE = Place Start Menu items in All Users.
109
// FALSE = Current User
110
BOOL g_bUseCommon;
111
 
112
// TRUE = Destination is the same as Source, so don't copy files.
113
BOOL g_bNoCopy;
114
 
115
// Source directory, usually a temporary directory created by
116
// unzip self extractor.
117
CHAR g_szSourceDir[MAXSTR];
118
 
119
// Target directory for program and fonts.
120
// Default loaded from resources
121
CHAR g_szTargetDir[MAXSTR];
122
 
123
// Target Group for shortcut.
124
// Default loaded from resources
125
CHAR g_szTargetGroup[MAXSTR];
126
 
127
// Setup application name, loaded from resources
128
CHAR g_szAppName[MAXSTR];
129
 
130
BOOL g_bInstallFonts = TRUE;
131
BOOL g_bCJKFonts = FALSE;
132
BOOL g_bAllUsers = FALSE;
133
 
134
 
135
HWND g_hMain;		// Main install dialog
136
HWND g_hWndText;	// Install log dialog
137
HINSTANCE g_hInstance;
138
 
139
// If a directory is listed on the command line, g_bBatch will
140
// be TRUE and a silent install will occur.
141
BOOL g_bBatch = FALSE;	
142
 
143
BOOL g_bQuit = FALSE;	// TRUE = Get out of message loop.
144
BOOL g_bError = FALSE;	// TRUE = Install was not successful
145
BOOL is_winnt = FALSE;	// Disable "All Users" if not NT.
146
 
147
#ifdef _WIN64
148
#define DLGRETURN INT_PTR
149
#else
150
#define DLGRETURN BOOL
151
#endif
152
 
153
// Prototypes
154
DLGRETURN CALLBACK MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
155
void gs_addmess_count(const char *str, int count);
156
void gs_addmess(const char *str);
157
void gs_addmess_update(void);
158
BOOL init();
159
BOOL install_all();
160
BOOL install_prog();
161
BOOL install_fonts();
162
BOOL make_filelist(int argc, char *argv[]);
163
int get_font_path(char *path, unsigned int pathlen);
164
BOOL write_cidfmap(const char *gspath, const char *cidpath);
165
BOOL GetProgramFiles(LPTSTR path);
166
 
167
 
168
//////////////////////////////////////////////////////////////////////
169
// Entry point
170
//////////////////////////////////////////////////////////////////////
171
 
172
int APIENTRY WinMain(HINSTANCE hInstance,
173
                     HINSTANCE hPrevInstance,
174
                     LPSTR     lpCmdLine,
175
                     int       nCmdShow)
176
{
177
	MSG msg;
178
	g_hInstance = hInstance;
179
 
180
	if (!init()) {
181
		MessageBox(HWND_DESKTOP, "Initialisation failed", 
182
			g_szAppName, MB_OK);
183
		return 1;
184
	}
185
 
186
	if (!g_bBatch) {
187
		while (GetMessage(&msg, (HWND)NULL, 0, 0)) {
188
			if (!IsDialogMessage(g_hWndText, &msg) && 
189
				!IsDialogMessage(g_hMain, &msg)) {
190
				TranslateMessage(&msg);
191
				DispatchMessage(&msg);
192
			}
193
		}
194
		DestroyWindow(g_hMain);
195
	}
196
 
197
	return (g_bError ? 1 : 0);
198
}
199
 
200
 
201
 
202
 
203
//////////////////////////////////////////////////////////////////////
204
// Text log window
205
//////////////////////////////////////////////////////////////////////
206
 
207
 
208
#define TWLENGTH 32768
209
#define TWSCROLL 1024
210
char twbuf[TWLENGTH];
211
int twend;
212
 
213
// Modeless Dialog Box
214
DLGRETURN CALLBACK 
215
TextWinDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
216
{
217
    switch(message) {
218
	case WM_INITDIALOG:
219
		EnableWindow(g_hMain, FALSE);
220
		return TRUE;
221
	case WM_COMMAND:
222
		switch(LOWORD(wParam)) {
223
		case IDC_TEXTWIN_COPY:
224
			{HGLOBAL hglobal;
225
			LPSTR p;
226
			DWORD result;
227
			int start, end;
228
			result = SendDlgItemMessage(hwnd, IDC_TEXTWIN_MLE, EM_GETSEL, (WPARAM)0, (LPARAM)0);
229
			start = LOWORD(result);
230
			end   = HIWORD(result);
231
			if (start == end) {
232
				start = 0;
233
				end = twend;
234
			}
235
			hglobal = GlobalAlloc(GHND | GMEM_SHARE, end-start+1);
236
			if (hglobal == (HGLOBAL)NULL) {
237
				MessageBeep(-1);
238
				return(FALSE);
239
			}
240
			p = (char *)GlobalLock(hglobal);
241
			if (p == (LPSTR)NULL) {
242
				MessageBeep(-1);
243
				return(FALSE);
244
			}
245
			lstrcpyn(p, twbuf+start, end-start);
246
			GlobalUnlock(hglobal);
247
			OpenClipboard(hwnd);
248
			EmptyClipboard();
249
			SetClipboardData(CF_TEXT, hglobal);
250
			CloseClipboard();
251
			}
252
			break;
253
		case IDCANCEL:
254
			g_bQuit = TRUE;
255
			DestroyWindow(hwnd);
256
			return TRUE;
257
		}
258
		break;
259
	case WM_CLOSE:
260
			DestroyWindow(hwnd);
261
			return TRUE;
262
	case WM_DESTROY:
263
			g_bQuit = TRUE;
264
			g_hWndText = (HWND)NULL;
265
			EnableWindow(g_hMain, TRUE);
266
			PostQuitMessage(0);
267
			break;
268
	}
269
	return FALSE;
270
}
271
 
272
 
273
 
274
// Add string to log window
275
void
276
gs_addmess_count(const char *str, int count)
277
{
278
	const char *s;
279
	char *p;
280
	int i, lfcount;
281
	MSG msg;
282
 
283
	// we need to add \r after each \n, so count the \n's
284
	lfcount = 0;
285
	s = str;
286
	for (i=0; i<count; i++) {
287
		if (*s == '\n')
288
			lfcount++;
289
		s++;
290
	}
291
 
292
	if (count + lfcount >= TWSCROLL)
293
		return;		// too large
294
	if (count + lfcount + twend >= TWLENGTH-1) {
295
		// scroll buffer
296
		twend -= TWSCROLL;
297
		memmove(twbuf, twbuf+TWSCROLL, twend);
298
	}
299
	p = twbuf+twend;
300
	for (i=0; i<count; i++) {
301
		if (*str == '\n') {
302
			*p++ = '\r';
303
		}
304
		*p++ = *str++;
305
	}
306
	twend += (count + lfcount);
307
	*(twbuf+twend) = '\0';
308
 
309
 
310
	// Update the dialog box
311
	if (g_bBatch)
312
		return;
313
 
314
	gs_addmess_update();
315
	while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) {
316
		if (!IsDialogMessage(g_hWndText, &msg) && 
317
			!IsDialogMessage(g_hMain, &msg)) {
318
			TranslateMessage(&msg);
319
			DispatchMessage(&msg);
320
		}
321
	}
322
}
323
 
324
void
325
gs_addmess(const char *str)
326
{
327
	gs_addmess_count(str, lstrlen(str));
328
 
329
}
330
 
331
 
332
void
333
gs_addmess_update(void)
334
{
335
	HWND hwndmess = g_hWndText;
336
 
337
	if (g_bBatch)
338
		return;
339
 
340
	if (IsWindow(hwndmess)) {
341
		HWND hwndtext = GetDlgItem(hwndmess, IDC_TEXTWIN_MLE);
342
		DWORD linecount;
343
		SendMessage(hwndtext, WM_SETREDRAW, FALSE, 0);
344
		SetDlgItemText(hwndmess, IDC_TEXTWIN_MLE, twbuf);
345
		linecount = SendDlgItemMessage(hwndmess, IDC_TEXTWIN_MLE, EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
346
		SendDlgItemMessage(hwndmess, IDC_TEXTWIN_MLE, EM_LINESCROLL, (WPARAM)0, (LPARAM)linecount-14);
347
		SendMessage(hwndtext, WM_SETREDRAW, TRUE, 0);
348
		InvalidateRect(hwndtext, (LPRECT)NULL, TRUE);
349
		UpdateWindow(hwndtext);
350
	}
351
}
352
 
353
 
354
//////////////////////////////////////////////////////////////////////
355
// Browse dialog box 
356
//////////////////////////////////////////////////////////////////////
357
 
358
// nasty GLOBALS
359
char szFolderName[MAXSTR];
360
char szDirName[MAXSTR];
361
 
362
DLGRETURN CALLBACK 
363
DirDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
364
{
365
	WORD notify_message;
366
 
367
	switch(message) {
368
	case WM_INITDIALOG:
369
		DlgDirList(hwnd, szDirName, IDC_FILES, IDC_FOLDER, 
370
			DDL_DRIVES | DDL_DIRECTORY);
371
		SetDlgItemText(hwnd, IDC_TARGET, szFolderName);
372
		return FALSE;
373
    case WM_COMMAND:
374
		notify_message = HIWORD(wParam);
375
		switch (LOWORD(wParam)) {
376
		case IDC_FILES:
377
			if (notify_message == LBN_DBLCLK) {
378
				CHAR szPath[MAXSTR];
379
				DlgDirSelectEx(hwnd, szPath, sizeof(szPath), IDC_FILES);
380
				DlgDirList(hwnd, szPath, IDC_FILES, IDC_FOLDER, 
381
					DDL_DRIVES | DDL_DIRECTORY);
382
			}
383
			return FALSE;
384
		case IDOK:
385
			GetDlgItemText(hwnd, IDC_FOLDER, szDirName, sizeof(szDirName));
386
			GetDlgItemText(hwnd, IDC_TARGET, szFolderName, sizeof(szFolderName));
387
			EndDialog(hwnd, TRUE);
388
			return TRUE;
389
		case IDCANCEL:
390
			EndDialog(hwnd, FALSE);
391
			return TRUE;
392
		}
393
		return FALSE;
394
	}
395
	return FALSE;
396
}
397
 
398
 
399
//////////////////////////////////////////////////////////////////////
400
// Initialisation and Main dialog box
401
//////////////////////////////////////////////////////////////////////
402
 
403
void
404
message_box(const char *str)
405
{
406
	MessageBox(HWND_DESKTOP, str, g_szAppName, MB_OK);
407
}
408
 
409
 
410
BOOL
411
init()
412
{
413
	DWORD dwVersion = GetVersion();
414
	// get source directory
415
	GetCurrentDirectory(sizeof(g_szSourceDir), g_szSourceDir);
416
 
417
	// load strings
418
	LoadString(g_hInstance, IDS_APPNAME, g_szAppName, sizeof(g_szAppName));
419
	LoadString(g_hInstance, IDS_TARGET_GROUP, 
420
		g_szTargetGroup, sizeof(g_szTargetGroup));
421
 
422
	if (LOBYTE(LOWORD(dwVersion)) < 4) {
423
        MessageBox(HWND_DESKTOP, 
424
			"This install program needs Windows 4.0 or later",
425
			g_szAppName, MB_OK);
426
		return FALSE;
427
	}
428
	if ( (HIWORD(dwVersion) & 0x8000) == 0)
429
		is_winnt = TRUE;
430
 
431
 
432
	cinst.SetMessageFunction(message_box);
433
 
434
#define MAXCMDTOKENS 128
435
 
436
	int argc;
437
	LPSTR argv[MAXCMDTOKENS];
438
	LPSTR p;
439
	char command[256];
440
	char *args;
441
	char *d, *e;
442
 
443
	p = GetCommandLine();
444
 
445
	argc = 0;
446
	args = (char *)malloc(lstrlen(p)+1);
447
	if (args == (char *)NULL)
448
		return 1;
449
 
450
	// Parse command line handling quotes.
451
	d = args;
452
	while (*p) {
453
		// for each argument
454
 
455
		if (argc >= MAXCMDTOKENS - 1)
456
			break;
457
 
458
		e = d;
459
		while ((*p) && (*p != ' ')) {
460
			if (*p == '\042') {
461
				// Remove quotes, skipping over embedded spaces.
462
				// Doesn't handle embedded quotes.
463
				p++;
464
				while ((*p) && (*p != '\042'))
465
					*d++ =*p++;
466
			}
467
			else 
468
				*d++ = *p;
469
			if (*p)
470
				p++;
471
		}
472
		*d++ = '\0';
473
		argv[argc++] = e;
474
 
475
		while ((*p) && (*p == ' '))
476
			p++;	// Skip over trailing spaces
477
	}
478
	argv[argc] = NULL;
479
 
480
	if (strlen(argv[0]) == 0) {
481
		GetModuleFileName(g_hInstance, command, sizeof(command)-1);
482
		argv[0] = command;
483
	}
484
 
485
	if (argc > 2) {
486
		// Probably creating filelist.txt
487
		return make_filelist(argc, argv);
488
	}
489
 
490
 
491
	// check if batch mode requested
492
	// get location of target directory from command line as argv[1]
493
	if (argc == 2) {
494
		strncpy(g_szTargetDir, argv[1], sizeof(g_szTargetDir));
495
		g_bBatch = TRUE;
496
		if (is_winnt)
497
			g_bAllUsers = TRUE;
498
	}
499
	if (g_bBatch) {
500
		if (!install_all()) {
501
			// display log showing error
502
			g_bBatch = FALSE;
503
			g_hWndText = CreateDialogParam(g_hInstance, 
504
				MAKEINTRESOURCE(IDD_TEXTWIN), 
505
				(HWND)HWND_DESKTOP, TextWinDlgProc, 
506
				(LPARAM)NULL);
507
			gs_addmess_update();
508
		}
509
		return TRUE;
510
	}
511
 
512
	// Interactive setup
513
	if (!GetProgramFiles(g_szTargetDir))
514
	    strcpy(g_szTargetDir, "C:\\Program Files");
515
	strcat(g_szTargetDir, "\\");
516
	LoadString(g_hInstance, IDS_TARGET_DIR, 
517
	    g_szTargetDir+strlen(g_szTargetDir), 
518
	    sizeof(g_szTargetDir)-strlen(g_szTargetDir));
519
 
520
	// main dialog box
521
	g_hMain = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_MAIN), (HWND)NULL, MainDlgProc, (LPARAM)NULL);
522
	// centre dialog on screen
523
	int width = GetSystemMetrics(SM_CXFULLSCREEN);
524
	int height = GetSystemMetrics(SM_CYFULLSCREEN);
525
	RECT rect;
526
	GetWindowRect(g_hMain, &rect);
527
	MoveWindow(g_hMain, (width - (rect.right - rect.left))/2,
528
		(height - (rect.bottom - rect.top))/2,
529
		(rect.right - rect.left),
530
		(rect.bottom - rect.top), FALSE);
531
 
532
	// initialize targets
533
	cinst.SetMessageFunction(message_box);
534
	if (!cinst.Init(g_szSourceDir, "filelist.txt"))
535
		return FALSE;
536
 
537
	SetDlgItemText(g_hMain, IDC_TARGET_DIR, g_szTargetDir);
538
	SetDlgItemText(g_hMain, IDC_TARGET_GROUP, g_szTargetGroup);
539
	SetDlgItemText(g_hMain, IDC_PRODUCT_NAME, cinst.GetUninstallName());
540
	SendDlgItemMessage(g_hMain, IDC_INSTALL_FONTS, BM_SETCHECK, BST_CHECKED, 0);
541
	ShowWindow(g_hMain, SW_SHOWNORMAL);
542
 
543
	return (g_hMain != (HWND)NULL); /* success */
544
}
545
 
546
 
547
// Main Modeless Dialog Box
548
DLGRETURN CALLBACK 
549
MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
550
{
551
	switch(message) {
552
	case WM_INITDIALOG:
553
		EnableWindow(GetDlgItem(hwnd, IDC_ALLUSERS), is_winnt);
554
		return TRUE;
555
	case WM_COMMAND:
556
		switch(LOWORD(wParam)) {
557
		case IDC_README:
558
			{
559
			char buf[MAXSTR];
560
			sprintf(buf, "%s\\%s\\doc\\Readme.htm", g_szSourceDir,
561
				cinst.GetMainDir());
562
			ShellExecute(hwnd, NULL, buf, NULL, g_szSourceDir, 
563
				SW_SHOWNORMAL);
564
			}
565
			return TRUE;
566
		case IDC_BROWSE_DIR:
567
			{ char dir[MAXSTR];
568
			char *p;
569
			GetDlgItemText(hwnd, IDC_TARGET_DIR, dir, sizeof(dir));
570
			strcpy(szDirName, dir);
571
			if ( (p = strrchr(szDirName, '\\')) != (char *)NULL ) {
572
				strcpy(szFolderName, p+1);
573
				if (p == szDirName+2)
574
					p++;	// step over c:\   //
575
				*p = '\0';
576
			}
577
			else {
578
				strcpy(szDirName, "c:\\");
579
				strcpy(szFolderName, dir);
580
			}
581
			if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIRDLG), 
582
				hwnd, DirDlgProc)) {
583
				strcpy(dir, szDirName);
584
				if (strlen(dir) && (dir[strlen(dir)-1] != '\\'))
585
					strcat(dir, "\\");
586
				strcat(dir, szFolderName);
587
				SetDlgItemText(hwnd, IDC_TARGET_DIR, dir);
588
			}
589
			}
590
			return TRUE;
591
		case IDC_BROWSE_GROUP:
592
			{ char dir[MAXSTR];
593
			char programs[MAXSTR];
594
			char *p;
595
			GetDlgItemText(hwnd, IDC_TARGET_GROUP, dir, sizeof(dir));
596
			cinst.GetPrograms(
597
				SendDlgItemMessage(hwnd, IDC_ALLUSERS,
598
				BM_GETCHECK, 0, 0) == BST_CHECKED,
599
				programs, sizeof(programs));
600
			strcpy(szDirName, programs);
601
			strcpy(szFolderName, dir);
602
			if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIRDLG), 
603
				hwnd, DirDlgProc)) {
604
				strcpy(dir, szFolderName);
605
				p = szDirName;
606
				if (strnicmp(szDirName, programs, 
607
					strlen(programs)) == 0) {
608
					p += strlen(programs);
609
					if (*p == '\\')
610
						p++;
611
					strcpy(dir, p);
612
					if (strlen(dir) && 
613
						(dir[strlen(dir)-1] != '\\'))
614
						strcat(dir, "\\");
615
					strcat(dir, szFolderName);
616
				}
617
				SetDlgItemText(hwnd, IDC_TARGET_GROUP, dir);
618
			}
619
			}
620
			return TRUE;
621
		case IDCANCEL:
622
			PostQuitMessage(0);
623
			return TRUE;
624
		case IDC_INSTALL:
625
			GetDlgItemText(hwnd, IDC_TARGET_DIR, 
626
				g_szTargetDir, sizeof(g_szTargetDir));
627
			GetDlgItemText(hwnd, IDC_TARGET_GROUP, 
628
				g_szTargetGroup, sizeof(g_szTargetGroup));
629
			g_bInstallFonts = (SendDlgItemMessage(g_hMain, 
630
				IDC_INSTALL_FONTS, BM_GETCHECK, 0, 0) 
631
				== BST_CHECKED);
632
			g_bCJKFonts = (SendDlgItemMessage(g_hMain, 
633
				IDC_CJK_FONTS, BM_GETCHECK, 0, 0) 
634
				== BST_CHECKED);
635
			g_bAllUsers = (SendDlgItemMessage(hwnd, 
636
				IDC_ALLUSERS, BM_GETCHECK, 0, 0
637
				) == BST_CHECKED);
638
 
639
			// install log dialog box
640
			g_hWndText = CreateDialogParam(g_hInstance, 
641
				MAKEINTRESOURCE(IDD_TEXTWIN), 
642
				(HWND)hwnd, TextWinDlgProc, (LPARAM)NULL);
643
			EnableWindow(GetDlgItem(hwnd, IDC_INSTALL), FALSE);
644
			if (install_all())
645
				PostQuitMessage(0);
646
			return TRUE;
647
		default:
648
			return(FALSE);
649
		}
650
		case WM_CLOSE:
651
			PostQuitMessage(0);
652
			return TRUE;
653
    }
654
    return FALSE;
655
}
656
 
657
// install program and files
658
BOOL
659
install_all()
660
{
661
	gs_addmess("Source Directory=");
662
	gs_addmess(g_szSourceDir);
663
	gs_addmess("\n");
664
	gs_addmess("Target Directory=");
665
	gs_addmess(g_szTargetDir);
666
	gs_addmess("\n");
667
	gs_addmess("Target Shell Folder=");
668
	gs_addmess(g_szTargetGroup);
669
	gs_addmess("\n");
670
	gs_addmess(g_bAllUsers ? "  All users\n" : "  Current user\n");
671
 
672
	if (stricmp(g_szSourceDir, g_szTargetDir) == 0) {
673
		// Don't copy files
674
		if (!g_bBatch)
675
			if (::MessageBox(g_hWndText, "Install location is the same as the current file location.  No files will be copied.", g_szAppName, MB_OKCANCEL) 
676
				!= IDOK) {
677
				return FALSE;
678
			}
679
		g_bNoCopy = TRUE;
680
	}
681
 
682
 
683
	if (g_bQuit)
684
		return FALSE;
685
 
686
	if (!install_prog()) {
687
		cinst.CleanUp();
688
		g_bError = TRUE;
689
		return FALSE;
690
	}
691
	if (g_bInstallFonts && !install_fonts()) {
692
		cinst.CleanUp();
693
		g_bError = TRUE;
694
		return FALSE;
695
	}
696
 
697
	gs_addmess("Install successful\n");
698
 
699
	// show start menu folder
700
	if (!g_bBatch) {
701
		char szFolder[MAXSTR];
702
		szFolder[0] = '\0';
703
		cinst.GetPrograms(g_bAllUsers, szFolder, sizeof(szFolder));
704
		strcat(szFolder, "\\");
705
		strcat(szFolder, g_szTargetGroup);
706
		ShellExecute(HWND_DESKTOP, "open", szFolder, 
707
			NULL, NULL, SW_SHOWNORMAL);
708
	}
709
 
710
#ifdef DEBUG
711
	return FALSE;
712
#endif
713
 
714
	return TRUE;
715
}
716
 
717
BOOL
718
install_prog()
719
{
720
	char *regkey1 = "AFPL Ghostscript";
721
	char regkey2[16];
722
	char szDLL[MAXSTR];
723
	char szLIB[MAXSTR+MAXSTR];
724
	char szProgram[MAXSTR];
725
	char szArguments[MAXSTR];
726
	char szDescription[MAXSTR];
727
	char szDotVersion[MAXSTR];
728
	char szPlatformSuffix[MAXSTR];
729
	const char *pSuffix = "";
730
 
731
	if (g_bQuit)
732
		return FALSE;
733
 
734
	cinst.SetMessageFunction(gs_addmess);
735
	cinst.SetTargetDir(g_szTargetDir);
736
	cinst.SetTargetGroup(g_szTargetGroup);
737
	cinst.SetAllUsers(g_bAllUsers);
738
	if (!cinst.Init(g_szSourceDir, "filelist.txt"))
739
		return FALSE;
740
 
741
	// Get GS version number
742
	gs_addmess("Installing Program...\n");
743
	int nGSversion = 0;
744
	const char *p = cinst.GetMainDir();
745
	while (*p && !isdigit(*p))	// skip over "gs" prefix
746
		p++;
747
	if (strlen(p) == 4)
748
		nGSversion = (p[0]-'0')*100 + (p[2]-'0')*10 + (p[3]-'0');
749
	else if (strlen(p) == 3)
750
		nGSversion = (p[0]-'0')*100 + (p[2]-'0')*10;
751
        strncpy(szDotVersion, p, sizeof(szDotVersion));
752
	strncpy(regkey2, szDotVersion, sizeof(regkey2));
753
 
754
	// copy files
755
	if (!cinst.InstallFiles(g_bNoCopy, &g_bQuit)) {
756
		gs_addmess("Program install failed\n");
757
		return FALSE;
758
	}
759
 
760
	if (g_bQuit)
761
		return FALSE;
762
 
763
	// write registry entries
764
	gs_addmess("Updating Registry\n");
765
	if (!cinst.UpdateRegistryBegin()) {
766
		gs_addmess("Failed to begin registry update\n");
767
		return FALSE;
768
	}
769
	if (!cinst.UpdateRegistryKey(regkey1, regkey2)) {
770
		gs_addmess("Failed to open/create registry application key\n");
771
		return FALSE;
772
	}
773
	strcpy(szDLL, g_szTargetDir);
774
	strcat(szDLL, "\\");
775
	strcat(szDLL, cinst.GetMainDir());
776
	strcat(szDLL, "\\bin\\gsdll32.dll");
777
	if (!cinst.UpdateRegistryValue(regkey1, regkey2, "GS_DLL", szDLL)) {
778
		gs_addmess("Failed to add registry value\n");
779
		return FALSE;
780
	}
781
	strcpy(szLIB, g_szTargetDir);
782
	strcat(szLIB, "\\");
783
	strcat(szLIB, cinst.GetMainDir());
784
	strcat(szLIB, "\\lib;");
785
	strcat(szLIB, g_szTargetDir);
786
	strcat(szLIB, "\\fonts;");
787
	strcat(szLIB, g_szTargetDir);
788
	strcat(szLIB, "\\");
789
	strcat(szLIB, cinst.GetMainDir());
790
	strcat(szLIB, "\\Resource");
791
	if (g_bCJKFonts) {
792
	    strcat(szLIB, ";");
793
	    get_font_path(szLIB+strlen(szLIB), sizeof(szLIB)-strlen(szLIB)-1);
794
	}
795
	if (!cinst.UpdateRegistryValue(regkey1, regkey2, "GS_LIB", szLIB)) {
796
		gs_addmess("Failed to add registry value\n");
797
		return FALSE;
798
	}
799
	if (!cinst.UpdateRegistryEnd()) {
800
		gs_addmess("Failed to end registry update\n");
801
		return FALSE;
802
	}
803
	if (g_bQuit)
804
		return FALSE;
805
 
806
	// Add Start Menu items
807
	gs_addmess("Adding Start Menu items\n");
808
 
809
        memset(szPlatformSuffix, 0, sizeof(szPlatformSuffix));
810
	if (GetProgramFiles(szPlatformSuffix)) {
811
	    /* If ProgramFiles has a suffix like " (x86)" then use
812
	     * it for Start menu entries to distinguish between
813
	     * 32-bit and 64-bit programs.
814
	     */
815
	    for (pSuffix = szPlatformSuffix; *pSuffix; pSuffix++)
816
		if ((pSuffix[0] == ' ') && (pSuffix[1] == '('))
817
		    break;
818
	}
819
	else {
820
	    pSuffix = "";
821
	}
822
 
823
 
824
	if (!cinst.StartMenuBegin()) {
825
		gs_addmess("Failed to begin Start Menu update\n");
826
		return FALSE;
827
	}
828
	strcpy(szProgram, g_szTargetDir);
829
	strcat(szProgram, "\\");
830
	strcat(szProgram, cinst.GetMainDir());
831
	strcat(szProgram, "\\bin\\gswin32.exe");
832
	strcpy(szArguments, "\042-I");
833
	strcat(szArguments, szLIB);
834
	strcat(szArguments, "\042");
835
	sprintf(szDescription, "Ghostscript %s%s", szDotVersion, pSuffix);
836
	if (!cinst.StartMenuAdd(szDescription, szProgram, szArguments)) {
837
		gs_addmess("Failed to add Start Menu item\n");
838
		return FALSE;
839
	}
840
	strcpy(szProgram, g_szTargetDir);
841
	strcat(szProgram, "\\");
842
	strcat(szProgram, cinst.GetMainDir());
843
	strcat(szProgram, "\\doc\\Readme.htm");
844
	sprintf(szDescription, "Ghostscript Readme %s%s", 
845
		szDotVersion, pSuffix);
846
	if (!cinst.StartMenuAdd(szDescription, szProgram, NULL)) {
847
		gs_addmess("Failed to add Start Menu item\n");
848
		return FALSE;
849
	}
850
	if (!cinst.StartMenuEnd()) {
851
		gs_addmess("Failed to end Start Menu update\n");
852
		return FALSE;
853
	}
854
 
855
        /* Create lib/cidfmap */
856
	if (g_bCJKFonts) {
857
		FILE *f;
858
		char szCIDFmap[MAXSTR];
859
		char szCIDFmap_bak[MAXSTR];
860
		char szGSPATH[MAXSTR];
861
 
862
		/* backup old cidfmap */
863
		strcpy(szCIDFmap, g_szTargetDir);
864
		strcat(szCIDFmap, "\\");
865
		strcat(szCIDFmap, cinst.GetMainDir());
866
		strcat(szCIDFmap, "\\lib\\cidfmap");
867
		strcpy(szCIDFmap_bak, szCIDFmap);
868
		strcat(szCIDFmap_bak, ".bak");
869
		gs_addmess("Backing up\n  ");
870
		gs_addmess(szCIDFmap);
871
		gs_addmess("\nto\n  ");
872
		gs_addmess(szCIDFmap_bak);
873
		gs_addmess("\n");
874
		rename(szCIDFmap, szCIDFmap_bak);
875
 
876
		/* mark backup for uninstall */
877
		cinst.AppendFileNew(szCIDFmap_bak);
878
 
879
		/* write new cidfmap */
880
		gs_addmess("Writing cidfmap\n   ");
881
		gs_addmess(szCIDFmap);
882
		gs_addmess("\n");
883
		strcpy(szGSPATH, g_szTargetDir);
884
		strcat(szGSPATH, "\\");
885
		strcat(szGSPATH, cinst.GetMainDir());
886
		if (!write_cidfmap(szGSPATH, szCIDFmap)) {
887
			gs_addmess("Failed to write cidfmap\n");
888
			return FALSE;
889
		}
890
	}
891
 
892
	// consolidate logs into one uninstall file
893
	if (cinst.MakeLog()) {
894
		// add uninstall entry for "Add/Remove Programs"
895
		gs_addmess("Adding uninstall program\n");
896
		if (!cinst.WriteUninstall(UNINSTALLPROG, g_bNoCopy)) {
897
			gs_addmess("Failed to write uninstall entry\n");
898
			return FALSE;
899
		}
900
	}
901
	else {
902
		gs_addmess("Failed to write uninstall log\n");
903
		// If batch install, files might be on a server
904
		// in a write protected directory.
905
		// Don't return an error for batch install.
906
		if (g_bBatch)
907
			return TRUE;
908
		return FALSE;
909
	}
910
 
911
	gs_addmess("Program install successful\n");
912
	return TRUE;
913
}
914
 
915
 
916
BOOL
917
install_fonts()
918
{
919
	cinst.SetMessageFunction(gs_addmess);
920
	cinst.SetTargetDir(g_szTargetDir);
921
	cinst.SetTargetGroup(g_szTargetGroup);
922
	cinst.SetAllUsers(g_bAllUsers);
923
	if (!cinst.Init(g_szSourceDir, "fontlist.txt"))
924
		return FALSE;
925
 
926
	// copy files
927
	if (!cinst.InstallFiles(g_bNoCopy, &g_bQuit)) {
928
		gs_addmess("Font install failed\n");
929
		return FALSE;
930
	}
931
 
932
	if (g_bQuit)
933
		return FALSE;
934
 
935
	if (g_bNoCopy) {
936
		// Don't write uninstall log or entry
937
		// since we didn't copy any files.
938
		cinst.CleanUp();
939
	}
940
	else {
941
		// consolidate logs into one uninstall file
942
		if (cinst.MakeLog()) {
943
			// add uninstall entry for "Add/Remove Programs"
944
			gs_addmess("Adding uninstall program\n");
945
			if (!cinst.WriteUninstall(UNINSTALLPROG, g_bNoCopy)) {
946
				gs_addmess("Failed to write uninstall entry\n");
947
				return FALSE;
948
			}
949
		}
950
		else {
951
			gs_addmess("Failed to write uninstall log\n");
952
			// If batch install, files might be on a server
953
			// in a write protected directory.
954
			// Don't return an error for batch install.
955
			if (g_bBatch)
956
				return TRUE;
957
			return FALSE;
958
		}
959
	}
960
 
961
	gs_addmess("Font install successful\n");
962
	return TRUE;
963
}
964
 
965
//////////////////////////////////////////////////////////////////////
966
// Create lib/cidfmap based on installed fonts
967
//////////////////////////////////////////////////////////////////////
968
 
969
/* Get the path to enumerate for fonts */
970
int
971
get_font_path(char *path, unsigned int pathlen)
972
{
973
    int i;
974
    int len = GetWindowsDirectory(path, pathlen);
975
    if (len == 0)
976
       return -1;
977
    if (pathlen - strlen(path) < 8)
978
       return -1;
979
    strncat(path, "/fonts", pathlen - strlen(path) - 7);
980
    for (i = strlen(path)-1; i >= 0; i--)
981
       if (path[i] == '\\')
982
           path[i] = '/';
983
    return len;
984
}
985
 
986
BOOL write_cidfmap(const char *gspath, const char *cidpath)
987
{
988
    char fontpath[MAXSTR];
989
    char buf[4*MAXSTR];
990
    STARTUPINFO siStartInfo;
991
    PROCESS_INFORMATION piProcInfo;
992
 
993
    get_font_path(fontpath, sizeof(fontpath)-1);
994
 
995
    strcpy(buf, "\042");
996
    strcat(buf, gspath);
997
    strcat(buf, "\\bin\\gswin32c.exe\042 -q -dBATCH \042-sFONTDIR=");
998
    strcat(buf, fontpath);
999
    strcat(buf, "\042 \042");
1000
    strcat(buf, "-sCIDFMAP=");
1001
    strcat(buf, cidpath);
1002
    strcat(buf, "\042 \042");
1003
    strcat(buf, gspath);
1004
    strcat(buf, "\\lib\\mkcidfm.ps\042");
1005
 
1006
    siStartInfo.cb = sizeof(STARTUPINFO);
1007
    siStartInfo.lpReserved = NULL;
1008
    siStartInfo.lpDesktop = NULL;
1009
    siStartInfo.lpTitle = NULL;  /* use executable name as title */
1010
    siStartInfo.dwX = siStartInfo.dwY = CW_USEDEFAULT;		/* ignored */
1011
    siStartInfo.dwXSize = siStartInfo.dwYSize = CW_USEDEFAULT;	/* ignored */
1012
    siStartInfo.dwXCountChars = 80;
1013
    siStartInfo.dwYCountChars = 25;
1014
    siStartInfo.dwFillAttribute = 0;			/* ignored */
1015
    siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
1016
    siStartInfo.wShowWindow = SW_HIDE;
1017
    siStartInfo.cbReserved2 = 0;
1018
    siStartInfo.lpReserved2 = NULL;
1019
    siStartInfo.hStdInput = NULL;
1020
    siStartInfo.hStdOutput = NULL;
1021
    siStartInfo.hStdError = NULL;
1022
 
1023
    /* Create the child process. */
1024
    if (!CreateProcess(NULL,
1025
        (char *)buf,  /* command line                       */
1026
        NULL,          /* process security attributes        */
1027
        NULL,          /* primary thread security attributes */
1028
        FALSE,         /* handles are not inherited          */
1029
        0,             /* creation flags                     */
1030
        NULL,          /* environment                        */
1031
        NULL,          /* use parent's current directory     */
1032
        &siStartInfo,  /* STARTUPINFO pointer                */
1033
        &piProcInfo))  /* receives PROCESS_INFORMATION  */
1034
	    return FALSE;
1035
 
1036
    /* We don't care if ghostscript fails, so just return */
1037
 
1038
    CloseHandle(piProcInfo.hProcess);
1039
    CloseHandle(piProcInfo.hThread);
1040
 
1041
    return TRUE;
1042
}
1043
 
1044
 
1045
//////////////////////////////////////////////////////////////////////
1046
// Create file list
1047
//////////////////////////////////////////////////////////////////////
1048
 
1049
FILE *fList;
1050
 
1051
typedef int (*PFN_dodir)(const char *name);
1052
 
1053
/* Called once for each directory */
1054
int
1055
dodir(const char *filename)
1056
{
1057
    return 0;
1058
}
1059
 
1060
/* Called once for each file */
1061
int
1062
dofile(const char *filename)
1063
{
1064
    if (fList != (FILE *)NULL) {
1065
		fputs(filename, fList);
1066
		fputs("\n", fList);
1067
    }
1068
 
1069
    return 0;
1070
}
1071
 
1072
 
1073
/* Walk through directory 'path', calling dodir() for given directory
1074
 * and dofile() for each file.
1075
 * If recurse=1, recurse into subdirectories, calling dodir() for
1076
 * each directory.
1077
 */
1078
int 
1079
dirwalk(char *path, int recurse, PFN_dodir dodir, PFN_dodir dofile)
1080
{    
1081
	WIN32_FIND_DATA find_data;
1082
	HANDLE find_handle;
1083
	char pattern[MAXSTR];	/* orig pattern + modified pattern */
1084
	char base[MAXSTR];
1085
	char name[MAXSTR];
1086
	BOOL bMore = TRUE;
1087
	char *p;
1088
 
1089
 
1090
	if (path) {
1091
		strcpy(pattern, path);
1092
		if (strlen(pattern) != 0)  {
1093
			p = pattern + strlen(pattern) -1;
1094
			if (*p == '\\')
1095
				*p = '\0';		// truncate trailing backslash
1096
		}
1097
 
1098
		strcpy(base, pattern);
1099
		if (strchr(base, '*') != NULL) {
1100
			// wildcard already included
1101
			// truncate it from the base path
1102
			if ( (p = strrchr(base, '\\')) != NULL )
1103
				*(++p) = '\0';
1104
		}
1105
		else if (isalpha(pattern[0]) && 
1106
			pattern[1]==':' && pattern[2]=='\0')  {
1107
			strcat(pattern, "\\*");		// search entire disk
1108
			strcat(base, "\\");
1109
		}
1110
		else {
1111
			// wildcard NOT included
1112
			// check to see if path is a directory
1113
			find_handle = FindFirstFile(pattern, &find_data);
1114
			if (find_handle != INVALID_HANDLE_VALUE) {
1115
				FindClose(find_handle);
1116
				if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1117
					strcat(pattern, "\\*");		// yes, search files 
1118
					strcat(base, "\\");
1119
				}
1120
				else {
1121
					dofile(path);				// no, return just this file
1122
					return 0;
1123
				}
1124
			}
1125
			else
1126
				return 1;	// path invalid
1127
		}
1128
	}
1129
	else {
1130
		base[0] = '\0';
1131
		strcpy(pattern, "*");
1132
	}
1133
 
1134
	find_handle = FindFirstFile(pattern,  &find_data);
1135
	if (find_handle == INVALID_HANDLE_VALUE)
1136
		return 1;
1137
 
1138
	while (bMore) {
1139
		strcpy(name, base);
1140
		strcat(name, find_data.cFileName);
1141
		if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1142
			if ( strcmp(find_data.cFileName, ".") && 
1143
				strcmp(find_data.cFileName, "..") ) {
1144
				dodir(name);
1145
				if (recurse)
1146
					dirwalk(name, recurse, dodir, dofile);
1147
			}
1148
		}
1149
		else {
1150
			dofile(name);
1151
		}
1152
		bMore = FindNextFile(find_handle, &find_data);
1153
	}
1154
	FindClose(find_handle);
1155
 
1156
	return 0;
1157
}
1158
 
1159
 
1160
 
1161
// This is used when creating a file list.
1162
 
1163
BOOL make_filelist(int argc, char *argv[])
1164
{
1165
    char *title = NULL;
1166
    char *dir = NULL;
1167
    char *list = NULL;
1168
    int i;
1169
    g_bBatch = TRUE;	// Don't run message loop
1170
 
1171
    for (i=1; i<argc; i++) {
1172
		if (strcmp(argv[i], "-title") == 0) {
1173
			i++;
1174
			title = argv[i];
1175
		}
1176
		else if (strcmp(argv[i], "-dir") == 0) {
1177
			i++;
1178
			dir = argv[i];
1179
		}
1180
		else if (strcmp(argv[i], "-list") == 0) {
1181
			i++;
1182
			list = argv[i];
1183
		}
1184
		else {
1185
		    if ((title == NULL) || (strlen(title) == 0) ||
1186
			(dir == NULL) || (strlen(dir) == 0) ||
1187
			(list == NULL) || (strlen(list) == 0)) {
1188
			message_box("Usage: setupgs -title \042AFPL Ghostscript #.##\042 -dir \042gs#.##\042 -list \042filelist.txt\042 spec1 spec2 specn\n");
1189
			return FALSE;
1190
		    }
1191
		    if (fList == (FILE *)NULL) {
1192
			    if ( (fList = fopen(list, "w")) == (FILE *)NULL ) {
1193
					message_box("Can't write list file\n");
1194
					return FALSE;
1195
			    }
1196
			    fputs(title, fList);
1197
			    fputs("\n", fList);
1198
			    fputs(dir, fList);
1199
			    fputs("\n", fList);
1200
		    }
1201
		    if (argv[i][0] == '@') {
1202
			// Use @filename with list of files/directories
1203
			// to avoid DOS command line limit
1204
			FILE *f;
1205
			char buf[MAXSTR];
1206
			int j;
1207
			if ( (f = fopen(&(argv[i][1]), "r")) != (FILE *)NULL) {
1208
			    while (fgets(buf, sizeof(buf), f)) {
1209
				// remove trailing newline and spaces
1210
				while ( ((j = strlen(buf)-1) >= 0) &&
1211
				    ((buf[j] == '\n') || (buf[j] == ' ')) )
1212
				    buf[j] = '\0';
1213
			        dirwalk(buf, TRUE, &dodir, &dofile);
1214
			    }
1215
			    fclose(f);
1216
			}
1217
			else {
1218
				wsprintf(buf, "Can't open @ file \042%s\042",
1219
				    &argv[i][1]);
1220
				message_box(buf);
1221
			}
1222
		    }
1223
		    else
1224
		        dirwalk(argv[i], TRUE, &dodir, &dofile);
1225
		}
1226
    }
1227
 
1228
    if (fList != (FILE *)NULL) {
1229
        fclose(fList);
1230
	fList = NULL;
1231
    }
1232
    return TRUE;
1233
}
1234
 
1235
//////////////////////////////////////////////////////////////////////
1236
 
1237
#ifndef CSIDL_PROGRAM_FILES
1238
#define CSIDL_PROGRAM_FILES 0x0026
1239
#endif
1240
#ifndef CSIDL_FLAG_CREATE
1241
#define CSIDL_FLAG_CREATE 0x8000
1242
#endif
1243
#ifndef SHGFP_TYPE_CURRENT
1244
#define SHGFP_TYPE_CURRENT 0
1245
#endif
1246
 
1247
BOOL 
1248
GetProgramFiles(LPTSTR path) 
1249
{
1250
    PFN_SHGetSpecialFolderPath PSHGetSpecialFolderPath = NULL;
1251
    PFN_SHGetFolderPath PSHGetFolderPath = NULL;
1252
    HMODULE hModuleShell32 = NULL;
1253
    HMODULE hModuleShfolder = NULL;
1254
    BOOL fOk = FALSE;
1255
    hModuleShfolder = LoadLibrary("shfolder.dll");
1256
    hModuleShell32 = LoadLibrary("shell32.dll");
1257
 
1258
    if (hModuleShfolder) {
1259
	PSHGetFolderPath = (PFN_SHGetFolderPath)
1260
	    GetProcAddress(hModuleShfolder, "SHGetFolderPathA");
1261
	if (PSHGetFolderPath) {
1262
	    fOk = (PSHGetFolderPath(HWND_DESKTOP, 
1263
		CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, 
1264
		NULL, SHGFP_TYPE_CURRENT, path) == S_OK);
1265
	}
1266
    }
1267
 
1268
    if (!fOk && hModuleShell32) {
1269
	PSHGetFolderPath = (PFN_SHGetFolderPath)
1270
	    GetProcAddress(hModuleShell32, "SHGetFolderPathA");
1271
	if (PSHGetFolderPath) {
1272
	    fOk = (PSHGetFolderPath(HWND_DESKTOP, 
1273
		CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, 
1274
		NULL, SHGFP_TYPE_CURRENT, path) == S_OK);
1275
	}
1276
    }
1277
 
1278
    if (!fOk && hModuleShell32) {
1279
	PSHGetSpecialFolderPath = (PFN_SHGetSpecialFolderPath)
1280
	    GetProcAddress(hModuleShell32, "SHGetSpecialFolderPathA");
1281
	if (PSHGetSpecialFolderPath) {
1282
	    fOk = PSHGetSpecialFolderPath(HWND_DESKTOP, path,
1283
		CSIDL_PROGRAM_FILES, TRUE);
1284
	}
1285
    }
1286
 
1287
    if (!fOk) {
1288
	/* If all else fails (probably Win95), try the registry */
1289
	LONG rc;
1290
	HKEY hkey;
1291
	DWORD cbData;
1292
	DWORD keytype;
1293
	rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
1294
	    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hkey);
1295
	if (rc == ERROR_SUCCESS) {
1296
	    cbData = MAX_PATH;
1297
	    keytype =  REG_SZ;
1298
	    if (rc == ERROR_SUCCESS)
1299
		rc = RegQueryValueEx(hkey, "ProgramFilesDir", 0, &keytype, 
1300
		    (LPBYTE)path, &cbData);
1301
	    RegCloseKey(hkey);
1302
	}
1303
	fOk = (rc == ERROR_SUCCESS);
1304
    }
1305
    return fOk;
1306
}
1307
 
1308
 
1309
//////////////////////////////////////////////////////////////////////