Setup.RegSvr.pas 7.7 KB

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