1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
7
This software is distributed under license and may not be copied, modified
8
or distributed except as expressly authorized under the terms of that
9
license. Refer to licensing information at http://www.artifex.com/
10
or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11
San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
14
// $Id: dwinst.cpp 8022 2007-06-05 22:23:38Z giles $
26
#define UNINSTALLKEY TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
27
#define UNINSTALLSTRINGKEY TEXT("UninstallString")
28
#define DISPLAYNAMEKEY TEXT("DisplayName")
29
#define UNINSTALL_FILE "uninstal.txt"
30
char szSection[] = "////////////////////////////////\n";
33
#define mktemp(x) _mktemp(x)
34
#define chdir(x) _chdir(x)
35
#define mkdir(x) _mkdir(x)
40
//////////////////////////////////////////////////////////////////////
41
// Construction/Destruction
42
//////////////////////////////////////////////////////////////////////
48
m_szTargetDir[0] = '\0';
49
m_szTargetGroup[0] = '\0';
50
m_szPrograms[0] = '\0';
51
m_szMainDir[0] = '\0';
61
void CInstall::CleanUp(void)
63
// delete all temporary files
71
if (strlen(m_szRegistryNew))
72
DeleteFile(m_szRegistryNew);
73
m_szRegistryNew[0] = '\0';
75
if (strlen(m_szRegistryOld))
76
DeleteFile(m_szRegistryOld);
77
m_szRegistryOld[0] = '\0';
79
if (strlen(m_szShellNew))
80
DeleteFile(m_szShellNew);
81
m_szShellNew[0] = '\0';
83
if (strlen(m_szShellOld))
84
DeleteFile(m_szShellOld);
85
m_szShellOld[0] = '\0';
87
if (strlen(m_szFileNew))
88
DeleteFile(m_szFileNew);
89
m_szFileNew[0] = '\0';
93
void CInstall::SetMessageFunction(void(*fn)(const char *))
98
void CInstall::AddMessage(const char *message)
101
(*AddMessageFn)(message);
104
void CInstall::SetTargetDir(const char *szTargetDir)
106
strcpy(m_szTargetDir, szTargetDir);
107
// remove trailing backslash
109
p = m_szTargetDir + strlen(m_szTargetDir) - 1;
114
void CInstall::SetTargetGroup(const char *szTargetGroup)
116
strcpy(m_szTargetGroup, szTargetGroup);
117
// remove trailing backslash
119
p = m_szTargetGroup + strlen(m_szTargetGroup) - 1;
124
const char *CInstall::GetMainDir()
129
const char *CInstall::GetUninstallName()
131
return m_szUninstallName;
134
BOOL CInstall::Init(const char *szSourceDir, const char *szFileList)
138
strcpy(m_szSourceDir, szSourceDir);
139
// remove trailing backslash
141
p = m_szSourceDir + strlen(m_szSourceDir) - 1;
144
strcpy(m_szFileList, szFileList);
146
m_szRegistryNew[0] = m_szRegistryOld[0] =
147
m_szShellNew[0] = m_szShellOld[0] =
148
m_szFileNew[0] = '\0';
150
// Open list of files
151
SetCurrentDirectory(m_szSourceDir);
152
f = fopen(m_szFileList, "r");
153
if (f == (FILE *)NULL) {
155
wsprintf(buf, "Failed to open \042%s\042\n", m_szFileList);
160
// get application and directory name
161
m_szUninstallName[0] = '\0';
162
if (!fgets(m_szUninstallName, sizeof(m_szUninstallName), f)) {
163
AddMessage("Invalid file list\n");
167
if (*m_szUninstallName )
168
m_szUninstallName[strlen(m_szUninstallName)-1] = '\0';
170
m_szMainDir[0] = '\0';
171
if (!fgets(m_szMainDir, sizeof(m_szMainDir), f)) {
172
AddMessage("Invalid file list\n");
177
m_szMainDir[strlen(m_szMainDir)-1] = '\0';
180
// Create log directory
181
strcpy(m_szLogDir, m_szTargetDir);
182
strcat(m_szLogDir, "\\");
183
strcat(m_szLogDir, m_szMainDir);
190
//////////////////////////////////////////
191
// File installation methods
193
BOOL CInstall::InstallFiles(BOOL bNoCopy, BOOL *pbQuit)
195
char szLogNew[MAXSTR];
197
AddMessage(bNoCopy ? "Checking" : "Copying");
198
AddMessage(" files listed in ");
199
AddMessage(m_szFileList);
202
// Open list of files
203
SetCurrentDirectory(m_szSourceDir);
204
FILE *f = fopen(m_szFileList, "r");
205
if (f == (FILE *)NULL) {
206
AddMessage("Failed to open \042");
207
AddMessage(m_szFileList);
208
AddMessage("\042\n");
212
// skip application and directory name
213
fgets(szLogNew, sizeof(szLogNew), f);
214
fgets(szLogNew, sizeof(szLogNew), f);
218
m_fLogNew = MakeTemp(m_szFileNew);
220
AddMessage("Failed to create FileNew temporary file\n");
226
while (fgets(line, sizeof(line), f) != (char *)NULL) {
230
line[strlen(line)-1] = '\0';
231
if (!InstallFile(line, bNoCopy)) {
244
void CInstall::AppendFileNew(const char *filename)
247
/* mark backup file for uninstall */
248
if ((f = fopen(m_szFileNew, "a")) != (FILE *)NULL) {
256
// requires a full path to be specified, so ignores root \
257
// apart from root \, must not contain trailing \
259
// c:\ (OK, but useless)
261
// c:\gstools\ (incorrect)
262
// c:gstools (incorrect)
263
// gstools (incorrect)
264
// The following UNC names should work,
265
// but didn't under Win3.1 because gs_chdir wouldn't accept UNC names
266
// Needs to be tested under Windows 95.
267
// \\server\sharename\gstools (OK)
268
// \\server\sharename\ (OK, but useless)
271
BOOL CInstall::MakeDir(const char *dirname)
275
if (strlen(dirname) < 3)
278
if (isalpha(dirname[0]) && dirname[1]==':' && dirname[2]=='\\') {
282
else if (dirname[1]=='\\' && dirname[1]=='\\') {
284
p = strchr(dirname+2, '\\'); // skip servername
288
p = strchr(p, '\\'); // skip sharename
293
// not full path so error
298
strncpy(newdir, dirname, (int)(p-dirname));
299
newdir[(int)(p-dirname)] = '\0';
305
if (p >= dirname + strlen(dirname))
309
p = dirname + strlen(dirname);
312
return SetCurrentDirectory(dirname);
315
void CInstall::ResetReadonly(const char *filename)
317
DWORD dwAttr = GetFileAttributes(filename);
318
if (dwAttr & FILE_ATTRIBUTE_READONLY)
319
SetFileAttributes(filename, dwAttr & (~FILE_ATTRIBUTE_READONLY));
322
BOOL CInstall::InstallFile(char *filename, BOOL bNoCopy)
324
char existing_name[MAXSTR];
325
char new_name[MAXSTR];
326
char dir_name[MAXSTR];
328
strcpy(existing_name, m_szSourceDir);
329
strcat(existing_name, "\\");
330
strcat(existing_name, filename);
331
strcpy(new_name, m_szTargetDir);
332
strcat(new_name, "\\");
333
strcat(new_name, filename);
334
strcpy(dir_name, new_name);
335
char *p = strrchr(dir_name, '\\');
338
if (!MakeDir(dir_name)) {
339
AddMessage("Failed to make directory ");
340
AddMessage(dir_name);
346
AddMessage(new_name);
350
// Don't copy files. Leave them where they are.
351
// Check that all files exist
353
if ((f = fopen(existing_name, "r")) == (FILE *)NULL) {
354
AddMessage("Missing file ");
355
AddMessage(existing_name);
362
if (!CopyFile(existing_name, new_name, FALSE)) {
363
char message[MAXSTR+MAXSTR+100];
364
wsprintf(message, "Failed to copy file %s to %s\n",
365
existing_name, new_name);
369
ResetReadonly(new_name);
370
fputs(new_name, m_fLogNew);
371
fputs("\n", m_fLogNew);
378
//////////////////////////////////////////
381
BOOL CInstall::StartMenuBegin()
383
m_fLogNew = MakeTemp(m_szShellNew);
385
AddMessage("Failed to create ShellNew temporary file\n");
389
m_fLogOld = MakeTemp(m_szShellOld);
391
AddMessage("Failed to create ShellNew temporary file\n");
395
// make folder if needed
397
strcpy(szLink, m_szPrograms);
398
strcat(szLink, "\\");
399
strcat(szLink, m_szTargetGroup);
400
if (chdir(szLink) != 0) {
401
if (mkdir(szLink) != 0) {
403
wsprintf(buf, "Couldn't make Programs folder \042%s'042", szLink);
410
fprintf(m_fLogOld, "Group=%s\n\n", szLink);
412
fprintf(m_fLogNew, "Group=%s\n\n", szLink);
417
BOOL CInstall::StartMenuEnd()
428
BOOL CInstall::StartMenuAdd(const char *szDescription,
429
const char *szProgram, const char *szArguments)
431
if (!CreateShellLink(szDescription, szProgram, szArguments)) {
432
AddMessage("Couldn't make shell link for ");
433
AddMessage(szDescription);
443
BOOL CInstall::CreateShellLink(LPCSTR description, LPCSTR program,
444
LPCSTR arguments, LPCSTR icon, int nIconIndex)
449
strcpy(szLink, m_szPrograms);
450
strcat(szLink, "\\");
451
strcat(szLink, m_szTargetGroup);
452
strcat(szLink, "\\");
453
strcat(szLink, description);
454
strcat(szLink, ".LNK");
455
AddMessage("Adding shell link\n ");
459
// Ensure string is UNICODE.
461
MultiByteToWideChar(CP_ACP, 0, szLink, -1, wsz, MAX_PATH);
463
// Save old shell link
465
// Get a pointer to the IShellLink interface.
466
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
467
IID_IShellLink, (void **)&psl);
468
if (SUCCEEDED(hres)) {
470
// Query IShellLink for the IPersistFile interface.
471
hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
472
if (SUCCEEDED(hres)) {
473
// Load the shell link.
474
hres = ppf->Load(wsz, STGM_READ);
475
if (SUCCEEDED(hres)) {
477
hres = psl->Resolve(HWND_DESKTOP, SLR_ANY_MATCH);
478
if (SUCCEEDED(hres)) {
479
// found it, so save details
485
fprintf(m_fLogOld, "Name=%s\n", szLink);
486
hres = psl->GetPath(szTemp, MAXSTR, (WIN32_FIND_DATA *)&wfd,
489
fprintf(m_fLogOld, "Path=%s\n", szTemp);
490
hres = psl->GetDescription(szTemp, MAXSTR);
492
fprintf(m_fLogOld, "Description=%s\n", szTemp);
493
hres = psl->GetArguments(szTemp, MAXSTR);
494
if (SUCCEEDED(hres) && (szTemp[0] != '\0'))
495
fprintf(m_fLogOld, "Arguments=%s\n", szTemp);
496
hres = psl->GetWorkingDirectory(szTemp, MAXSTR);
497
if (SUCCEEDED(hres) && (szTemp[0] != '\0'))
498
fprintf(m_fLogOld, "Directory=%s\n", szTemp);
499
hres = psl->GetIconLocation(szTemp, MAXSTR, &i);
500
if (SUCCEEDED(hres) && (szTemp[0] != '\0')) {
501
fprintf(m_fLogOld, "IconLocation=%s\n", szTemp);
502
fprintf(m_fLogOld, "IconIndex=%d\n", i);
504
fprintf(m_fLogOld, "\n");
507
// Release pointer to IPersistFile.
510
// Release pointer to IShellLink.
515
// Save new shell link
517
// Get a pointer to the IShellLink interface.
518
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
519
IID_IShellLink, (void **)&psl);
520
if (SUCCEEDED(hres)) {
522
// Query IShellLink for the IPersistFile interface for
523
// saving the shell link in persistent storage.
524
hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
525
if (SUCCEEDED(hres)) {
526
fprintf(m_fLogNew, "Name=%s\n", szLink);
528
// Set the path to the shell link target.
529
hres = psl->SetPath(program);
530
if (!SUCCEEDED(hres))
531
AddMessage("SetPath failed!");
532
fprintf(m_fLogNew, "Path=%s\n", program);
533
// Set the description of the shell link.
534
hres = psl->SetDescription(description);
535
if (!SUCCEEDED(hres))
536
AddMessage("SetDescription failed!");
537
fprintf(m_fLogNew, "Description=%s\n", description);
538
if (arguments != (LPCSTR)NULL) {
539
// Set the arguments of the shell link target.
540
hres = psl->SetArguments(arguments);
541
if (!SUCCEEDED(hres))
542
AddMessage("SetArguments failed!");
543
fprintf(m_fLogNew, "Arguments=%s\n", arguments);
545
if (icon != (LPCSTR)NULL) {
546
// Set the arguments of the shell link target.
547
hres = psl->SetIconLocation(icon, nIconIndex);
548
if (!SUCCEEDED(hres))
549
AddMessage("SetIconLocation failed!");
550
fprintf(m_fLogNew, "IconLocation=%s\n", icon);
551
fprintf(m_fLogNew, "IconIndex=%d\n", nIconIndex);
554
// Save the link via the IPersistFile::Save method.
555
hres = ppf->Save(wsz, TRUE);
556
// Release pointer to IPersistFile.
559
// Release pointer to IShellLink.
561
fprintf(m_fLogNew, "\n");
568
//////////////////////////////////////////
572
reg_quote(char *d, const char *s)
582
BOOL CInstall::UpdateRegistryBegin()
584
const char regheader[]="REGEDIT4\n";
585
m_fLogNew = MakeTemp(m_szRegistryNew);
587
AddMessage("Failed to create RegistryNew temporary file\n");
590
fputs(regheader, m_fLogNew);
592
m_fLogOld = MakeTemp(m_szRegistryOld);
594
AddMessage("Failed to create RegistryOld temporary file\n");
598
fputs(regheader, m_fLogOld);
603
BOOL CInstall::UpdateRegistryEnd()
614
BOOL CInstall::UpdateRegistryKey(const char *product, const char *version)
616
const char hkey_name[] = "HKEY_LOCAL_MACHINE";
617
const HKEY hkey_key = HKEY_LOCAL_MACHINE;
618
const char key_format[] = "\n[%s\\%s]\n";
620
/* Create default registry entries */
625
// Create/Open application key
626
sprintf(name, "SOFTWARE\\%s", product);
627
lrc = RegOpenKey(hkey_key, name, &hkey);
628
if (lrc == ERROR_SUCCESS) {
629
fprintf(m_fLogOld, key_format, hkey_name, name);
632
lrc = RegCreateKey(hkey_key, name, &hkey);
633
if (lrc == ERROR_SUCCESS)
634
fprintf(m_fLogNew, key_format, hkey_name, name);
636
if (lrc == ERROR_SUCCESS)
639
// Create/Open application version key
640
sprintf(name, "SOFTWARE\\%s\\%s", product, version);
643
AddMessage(hkey_name);
647
lrc = RegOpenKey(hkey_key, name, &hkey);
648
if (lrc == ERROR_SUCCESS)
649
fprintf(m_fLogOld, key_format, hkey_name, name);
651
lrc = RegCreateKey(hkey_key, name, &hkey);
652
if (lrc == ERROR_SUCCESS) {
653
fprintf(m_fLogNew, key_format, hkey_name, name);
661
BOOL CInstall::UpdateRegistryValue(const char *product, const char *version,
662
const char *name, const char *value)
667
// Open application/version key
668
sprintf(appver, "SOFTWARE\\%s\\%s", product, version);
670
if (RegOpenKey(HKEY_LOCAL_MACHINE, appver, &hkey)
672
flag = SetRegistryValue(hkey, name, value);
679
BOOL CInstall::SetRegistryValue(HKEY hkey, const char *value_name, const char *value)
686
cbData = sizeof(buf);
688
if (RegQueryValueEx(hkey, value_name, 0, &keytype,
689
(LPBYTE)buf, &cbData) == ERROR_SUCCESS) {
690
reg_quote(qbuf, buf);
691
fprintf(m_fLogOld, "\042%s\042=\042%s\042\n", value_name, qbuf);
693
reg_quote(qbuf, value);
694
fprintf(m_fLogNew, "\042%s\042=\042%s\042\n", value_name, qbuf);
696
AddMessage(value_name);
700
if (RegSetValueEx(hkey, value_name, 0, REG_SZ,
701
(CONST BYTE *)value, strlen(value)+1) != ERROR_SUCCESS)
706
////////////////////////////////////
710
BOOL CInstall::WriteUninstall(const char *szProg, BOOL bNoCopy)
716
char ungsprog[MAXSTR];
718
lstrcpy(ungsprog, m_szTargetDir);
719
lstrcat(ungsprog, "\\");
720
lstrcat(ungsprog, szProg);
722
lstrcpy(buffer, m_szSourceDir);
723
lstrcat(buffer, "\\");
724
lstrcat(buffer, szProg);
727
// Don't copy files. Leave them where they are.
728
// Check that all files exist
730
if ((f = fopen(buffer, "r")) == (FILE *)NULL) {
731
AddMessage("Missing file ");
738
else if (!CopyFile(buffer, ungsprog, FALSE)) {
739
char message[MAXSTR+MAXSTR+100];
740
wsprintf(message, "Failed to copy file %s to %s", buffer, ungsprog);
744
ResetReadonly(ungsprog);
746
/* write registry entries for uninstall */
747
if ((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINSTALLKEY, 0,
748
KEY_ALL_ACCESS, &hkey)) != ERROR_SUCCESS) {
749
/* failed to open key, so try to create it */
750
rc = RegCreateKey(HKEY_LOCAL_MACHINE, UNINSTALLKEY, &hkey);
752
if (rc == ERROR_SUCCESS) {
753
// Uninstall key for program
754
if (RegCreateKey(hkey, m_szUninstallName, &hsubkey) == ERROR_SUCCESS) {
755
RegSetValueEx(hsubkey, DISPLAYNAMEKEY, 0, REG_SZ,
756
(CONST BYTE *)m_szUninstallName, lstrlen(m_szUninstallName)+1);
757
lstrcpy(buffer, ungsprog);
758
lstrcat(buffer, " \042");
759
lstrcat(buffer, m_szTargetDir);
760
lstrcat(buffer, "\\");
761
lstrcat(buffer, m_szMainDir);
762
lstrcat(buffer, "\\");
763
lstrcat(buffer, UNINSTALL_FILE);
764
lstrcat(buffer, "\042");
766
AddMessage(m_szUninstallName);
770
RegSetValueEx(hsubkey, UNINSTALLSTRINGKEY, 0, REG_SZ,
771
(CONST BYTE *)buffer, lstrlen(buffer)+1);
772
RegCloseKey(hsubkey);
782
CInstall::CopyFileContents(FILE *df, FILE *sf)
786
while ((count = fread(buf, 1, sizeof(buf), sf)) != 0)
787
fwrite(buf, 1, count, df);
790
FILE *CInstall::MakeTemp(char *fname)
793
if ( (temp = getenv("TEMP")) == NULL )
794
strcpy(fname, m_szTargetDir);
798
/* Prevent X's in path from being converted by mktemp. */
799
for ( temp = fname; *temp; temp++ ) {
800
*temp = (char)tolower(*temp);
804
if ( strlen(fname) && (fname[strlen(fname)-1] != '\\') )
807
strcat(fname, "gsXXXXXX");
809
AddMessage("Creating temporary file ");
812
return fopen(fname, "w");
815
BOOL CInstall::MakeLog()
818
char szFileName[MAXSTR];
819
char szLogDir[MAXSTR];
820
strcpy(szLogDir, m_szTargetDir);
821
strcat(szLogDir, "\\");
822
strcat(szLogDir, m_szMainDir);
823
strcat(szLogDir, "\\");
825
strcpy(szFileName, szLogDir);
826
strcat(szFileName, UNINSTALL_FILE);
827
lf = fopen(szFileName, "w");
828
if (lf == (FILE *)NULL) {
829
AddMessage("Can't create uninstall log");
833
fputs(szSection, lf);
834
fputs("UninstallName\n", lf);
835
fputs(m_szUninstallName, lf);
838
if (strlen(m_szRegistryNew) &&
839
(f = fopen(m_szRegistryNew, "r")) != (FILE *)NULL) {
840
fputs(szSection, lf);
841
fputs("RegistryNew\n", lf);
842
CopyFileContents(lf, f);
845
DeleteFile(m_szRegistryNew);
846
m_szRegistryNew[0] = '\0';
849
if (strlen(m_szRegistryOld) &&
850
(f = fopen(m_szRegistryOld, "r")) != (FILE *)NULL) {
851
fputs(szSection, lf);
852
fputs("RegistryOld\n", lf);
853
CopyFileContents(lf, f);
856
DeleteFile(m_szRegistryOld);
857
m_szRegistryOld[0] = '\0';
860
if (strlen(m_szShellNew) &&
861
(f = fopen(m_szShellNew, "r")) != (FILE *)NULL) {
862
fputs(szSection, lf);
863
fputs("ShellNew\n", lf);
864
CopyFileContents(lf, f);
867
DeleteFile(m_szShellNew);
868
m_szShellNew[0] = '\0';
871
if (strlen(m_szShellOld) &&
872
(f = fopen(m_szShellOld, "r")) != (FILE *)NULL) {
873
fputs(szSection, lf);
874
fputs("ShellOld\n", lf);
875
CopyFileContents(lf, f);
878
DeleteFile(m_szShellOld);
879
m_szShellOld[0] = '\0';
882
if (strlen(m_szFileNew) &&
883
(f = fopen(m_szFileNew, "r")) != (FILE *)NULL) {
884
fputs(szSection, lf);
885
fputs("FileNew\n", lf);
886
CopyFileContents(lf, f);
889
DeleteFile(m_szFileNew);
890
m_szFileNew[0] = '\0';
893
fputs(szSection, lf);
899
BOOL CInstall::GetPrograms(BOOL bUseCommon, char *buf, int buflen)
901
// Get the directory for the Program menu. This is
902
// stored in the Registry under HKEY_CURRENT_USER\Software\
903
// Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Programs.
907
ULONG ulSize = buflen;
908
HKEY hrkey = HKEY_CURRENT_USER;
910
hrkey = HKEY_LOCAL_MACHINE;
911
if (RegOpenKeyEx(hrkey,
912
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
914
&hCU) == ERROR_SUCCESS) {
915
rc = RegQueryValueEx( hCU,
916
bUseCommon ? "Common Programs" : "Programs",
919
(unsigned char *)buf,
927
// This is an alternate version, but it needs
928
// Internet Explorer 4.0 with Web Integrated Desktop.
929
// It does not work with the standard
930
// Windows 95, Windows NT 4.0, Internet Explorer 3.0,
931
// and Internet Explorer 4.0 without Web Integrated Desktop.
934
m_szPrograms[0] = '\0';
935
int nFolder = CSIDL_PROGRAMS;
937
nFolder = CSIDL_COMMON_PROGRAMS;
939
rc = SHGetSpecialFolderPath(HWND_DESKTOP, m_szPrograms,
941
return (rc == NOERROR);
946
BOOL CInstall::SetAllUsers(BOOL bUseCommon)
948
m_bUseCommon = bUseCommon;
949
return GetPrograms(bUseCommon, m_szPrograms, sizeof(m_szPrograms));
953
//////////////////////////////////////////////////////////////////////