forked from jrsoftware/issrc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRegSvr.pas
223 lines (205 loc) · 8.25 KB
/
RegSvr.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
unit RegSvr;
{
Inno Setup
Copyright (C) 1997-2012 Jordan Russell
Portions by Martijn Laan
For conditions of distribution and use, see LICENSE.TXT.
Registers OLE servers & type libraries after a reboot
}
interface
procedure RunRegSvr;
implementation
uses
Windows, SysUtils, Classes, Forms, PathFunc, CmnFunc2, InstFunc, InstFnc2,
FileClass, CmnFunc, Struct, Main, Msgs, MsgIDs, RegDLL, Helper;
procedure DeleteOldTempFiles(const Path: String);
{ Removes any old isRS-???.tmp files from Path. Not strictly necessary, but
in case a prior multi-install run left behind multiple .tmp files now is a
good time to clean them up. }
var
H: THandle;
FindData: TWin32FindData;
Filename: String;
begin
H := FindFirstFile(PChar(Path + 'isRS-???.tmp'), FindData);
if H <> INVALID_HANDLE_VALUE then begin
try
repeat
{ Yes, this StrLIComp is superfluous. When deleting files from
potentionally the Windows directory I can't help but be *extra*
careful. :) }
if (StrLIComp(FindData.cFileName, 'isRS-', Length('isRS-')) = 0) and
(FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0) then begin
Filename := Path + FindData.cFileName;
{ If the file is read-only, try to strip the attribute }
if FindData.dwFileAttributes and FILE_ATTRIBUTE_READONLY <> 0 then
SetFileAttributes(PChar(Filename), FindData.dwFileAttributes
and not FILE_ATTRIBUTE_READONLY);
DeleteFile(Filename);
end;
until not FindNextFile(H, FindData);
finally
Windows.FindClose(H);
end;
end;
end;
function RenameToNonRandomTempName(const Filename: String): String;
{ Renames Filename to a name in the format: isRS-nnn.tmp. Returns the new
filename if successful, or '' if not.
Note: This is an NT-only function, as it calls MoveFileEx. }
var
Path, NewFilename: String;
Attribs: DWORD;
Attempts, I: Integer;
begin
Result := '';
Path := PathExtractPath(Filename);
Attempts := 0;
for I := 0 to 999 do begin
NewFilename := Path + Format('isRS-%.3u.tmp', [I]);
Attribs := GetFileAttributes(PChar(NewFilename));
if Attribs <> $FFFFFFFF then begin
{ Skip any directories that happen to named NewFilename }
if Attribs and FILE_ATTRIBUTE_DIRECTORY <> 0 then
Continue;
{ If the existing file is read-only, try to strip the attribute }
if Attribs and FILE_ATTRIBUTE_READONLY <> 0 then
SetFileAttributes(PChar(NewFilename), Attribs and not FILE_ATTRIBUTE_READONLY);
end;
if MoveFileEx(PChar(Filename), PChar(NewFilename), MOVEFILE_REPLACE_EXISTING) then begin
Result := NewFilename;
Break;
end;
Inc(Attempts);
{ Limit MoveFileEx calls to 10 since it can be really slow over network
connections when a file is in use }
if Attempts = 10 then
Break;
end;
end;
procedure DeleteSelf;
var
SelfFilename, NewFilename: String;
begin
SelfFilename := NewParamStr(0);
if Win32Platform = VER_PLATFORM_WIN32_NT then begin
{ On NT, RestartReplace will fail if the user doesn't have admin
privileges. We don't want to leak temporary files, so try to rename
ourself to a non-random name. This way, future runs should just keep
overwriting the same temp file. }
DeleteOldTempFiles(PathExtractPath(SelfFilename));
NewFilename := RenameToNonRandomTempName(SelfFilename);
if NewFilename <> '' then
RestartReplace(False, NewFilename, '')
else
RestartReplace(False, SelfFilename, '');
end
else
RestartReplace(False, SelfFilename, '');
end;
procedure RunRegSvr;
var
CreatedAsAdmin, NoErrorMessages: Boolean;
Mutex: THandle;
F: TTextFileReader;
MsgFilename, ListFilename, L, RegFilename: String;
begin
if CompareText(NewParamStr(1), '/REG') = 0 then
CreatedAsAdmin := True
else if CompareText(NewParamStr(1), '/REGU') = 0 then
CreatedAsAdmin := False
else
Exit;
{ Set default title; it's set again below after the messages are read }
Application.Title := 'Setup';
{ This is needed for D3+: Must force the application window visible since
we aren't displaying any forms }
ShowWindow(Application.Handle, SW_SHOW);
InitializeCommonVars;
{ Try to create and acquire a mutex.
In cases where multiple IS installers have each created their own RegSvr
RunOnce entries in HKCU, Windows Explorer will execute them asynchronously.
This could have undesirable ramifications -- what might happen if the same
DLL were registered simultaneously by two RegSvr processes? Could the
registry entries be in an incomplete/inconsistent state? I'm not sure, so
a mutex is used here to ensure registrations are serialized. }
Mutex := Windows.CreateMutex(nil, False, 'Inno-Setup-RegSvr-Mutex');
ShowWindow(Application.Handle, SW_HIDE); { hide taskbar button while waiting }
if Mutex <> 0 then begin
{ Even though we have no visible windows, process messages while waiting
so Windows doesn't think we're hung }
repeat
Application.ProcessMessages;
until MsgWaitForMultipleObjects(1, Mutex, False, INFINITE,
QS_ALLINPUT) <> WAIT_OBJECT_0+1;
end;
ShowWindow(Application.Handle, SW_SHOW);
try
MsgFilename := PathChangeExt(NewParamStr(0), '.msg');
ListFilename := PathChangeExt(NewParamStr(0), '.lst');
{ The .lst file may not exist at this point, if we were already run
previously, but the RunOnce entry could not be removed due to lack of
admin privileges. }
if NewFileExists(ListFilename) then begin
{ Need to load messages in order to display exception messages below.
Note: The .msg file only exists when the .lst file does. }
LoadSetupMessages(MsgFilename, 0, True);
SetMessageBoxRightToLeft(lfRightToLeft in MessagesLangOptions.Flags);
Application.Title := SetupMessages[msgSetupAppTitle];
try
{ Extract the 64-bit helper }
CreateTempInstallDir;
F := TTextFileReader.Create(ListFilename, fdOpenExisting, faRead, fsRead);
try
while not F.Eof do begin
L := F.ReadLine;
if (Length(L) > 4) and (L[1] = '[') and (L[4] = ']') then begin
RegFilename := Copy(L, 5, Maxint);
NoErrorMessages := (L[3] = 'q') or (CreatedAsAdmin and not IsAdmin);
try
case L[2] of
's': RegisterServer(False, False, RegFilename, NoErrorMessages);
'S': RegisterServer(False, True, RegFilename, NoErrorMessages);
't': RegisterTypeLibrary(RegFilename);
'T': HelperRegisterTypeLibrary(False, RegFilename);
end;
except
{ Display the exception message (with a caption of 'Setup' so
people have some clue of what generated it), and keep going.
Exception: Don't display the message if the program was
installed as an admin (causing the RunOnce entry to be created
in HKLM) and the user isn't logged in as an admin now. That's
almost certainly going to result in errors; let's not complain
about it. The RunOnce entry should survive a logoff (since
only admins can write to HKLM's RunOnce); once the user logs
back in as an admin the files will get registered for real,
and we won't suppress error messages then. }
if not NoErrorMessages then
AppMessageBox(PChar(RegFilename + SNewLine2 +
FmtSetupMessage1(msgErrorRegisterServer, GetExceptMessage)),
PChar(SetupMessages[msgSetupAppTitle]), MB_OK or MB_ICONEXCLAMATION);
end;
end;
end;
finally
F.Free;
end;
finally
RemoveTempInstallDir;
end;
end;
DeleteFile(ListFilename);
DeleteFile(MsgFilename);
try
DeleteSelf;
except
{ ignore exceptions }
end;
finally
if Mutex <> 0 then begin
ReleaseMutex(Mutex);
CloseHandle(Mutex);
end;
end;
end;
end.