Setup.SpawnClient.pas 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. unit Setup.SpawnClient;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2024 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Spawn client
  8. NOTE: These functions are NOT thread-safe. Do not call them from multiple
  9. threads simultaneously.
  10. }
  11. interface
  12. uses
  13. Windows, SysUtils, Messages, Setup.InstFunc, Shared.CommonFunc;
  14. procedure InitializeSpawnClient(const AServerWnd: HWND);
  15. function InstExecEx(const RunAsOriginalUser: Boolean;
  16. const DisableFsRedir: Boolean; const Filename, Params, WorkingDir: String;
  17. const Wait: TExecWait; const ShowCmd: Integer;
  18. const ProcessMessagesProc: TProcedure; const OutputReader: TCreateProcessOutputReader;
  19. var ResultCode: Integer): Boolean;
  20. function InstShellExecEx(const RunAsOriginalUser: Boolean;
  21. const Verb, Filename, Params, WorkingDir: String;
  22. const Wait: TExecWait; const ShowCmd: Integer;
  23. const ProcessMessagesProc: TProcedure; var ResultCode: Integer): Boolean;
  24. implementation
  25. uses
  26. Classes, Setup.SpawnCommon;
  27. var
  28. SpawnServerPresent: Boolean;
  29. SpawnServerWnd: HWND;
  30. procedure WriteLongintToStream(const M: TMemoryStream; const Value: Longint);
  31. begin
  32. M.WriteBuffer(Value, SizeOf(Value));
  33. end;
  34. procedure WriteStringToStream(const M: TMemoryStream; const Value: String);
  35. var
  36. Len: Integer;
  37. begin
  38. Len := Length(Value);
  39. if Len > $FFFF then
  40. InternalError('WriteStringToStream: Length limit exceeded');
  41. WriteLongintToStream(M, Len);
  42. M.WriteBuffer(Value[1], Len * SizeOf(Value[1]));
  43. end;
  44. procedure AllowSpawnServerToSetForegroundWindow;
  45. { This is called to allow processes started by the spawn server process to
  46. come to the foreground, above the current process's windows. The effect
  47. normally lasts until new input is generated (a keystroke or click, not
  48. simply mouse movement).
  49. Note: If the spawn server process has no visible windows, it seems this
  50. isn't needed; the process can set the foreground window as it pleases.
  51. If it does have a visible window, though, it definitely is needed (e.g. in
  52. the /DebugSpawnServer case). Let's not rely on any undocumented behavior and
  53. call AllowSetForegroundWindow unconditionally. }
  54. var
  55. PID: DWORD;
  56. AllowSetForegroundWindowFunc: function(dwProcessId: DWORD): BOOL; stdcall;
  57. begin
  58. if GetWindowThreadProcessId(SpawnServerWnd, @PID) <> 0 then begin
  59. AllowSetForegroundWindowFunc := GetProcAddress(GetModuleHandle(user32),
  60. 'AllowSetForegroundWindow');
  61. if Assigned(AllowSetForegroundWindowFunc) then
  62. AllowSetForegroundWindowFunc(PID);
  63. end;
  64. end;
  65. function QuerySpawnServer(const SequenceNumber: Word;
  66. const Operation: Integer): Word;
  67. var
  68. MsgResult: LRESULT;
  69. begin
  70. MsgResult := SendMessage(SpawnServerWnd, WM_SpawnServer_Query, Operation,
  71. SequenceNumber);
  72. if MsgResult and not $FFFF <> SPAWN_MSGRESULT_SUCCESS_BITS then
  73. InternalErrorFmt('QuerySpawnServer: Unexpected response: $%x', [MsgResult]);
  74. Result := Word(MsgResult);
  75. end;
  76. function CallSpawnServer(const CopyDataMsg: DWORD; var M: TMemoryStream;
  77. const ProcessMessagesProc: TProcedure; var ResultCode: Integer): Boolean;
  78. var
  79. CopyDataStruct: TCopyDataStruct;
  80. MsgResult: LRESULT;
  81. SequenceNumber: Word;
  82. Status: Word;
  83. LastQueryTime, NowTime: DWORD;
  84. begin
  85. CopyDataStruct.dwData := CopyDataMsg;
  86. CopyDataStruct.cbData := M.Size;
  87. CopyDataStruct.lpData := M.Memory;
  88. AllowSpawnServerToSetForegroundWindow;
  89. MsgResult := SendMessage(SpawnServerWnd, WM_COPYDATA, 0, LPARAM(@CopyDataStruct));
  90. FreeAndNil(M); { it isn't needed anymore, might as well free now }
  91. if MsgResult = SPAWN_MSGRESULT_OUT_OF_MEMORY then
  92. OutOfMemoryError;
  93. if MsgResult and not $FFFF <> SPAWN_MSGRESULT_SUCCESS_BITS then
  94. InternalErrorFmt('CallSpawnServer: Unexpected response: $%x', [MsgResult]);
  95. SequenceNumber := Word(MsgResult);
  96. LastQueryTime := GetTickCount;
  97. repeat
  98. ProcessMessagesProc;
  99. { Now that the queue is empty (we mustn't break without first processing
  100. messages found by a previous MsgWaitForMultipleObjects call), see if
  101. the status changed, but only if at least 10 ms has elapsed since the
  102. last query }
  103. NowTime := GetTickCount;
  104. if Cardinal(NowTime - LastQueryTime) >= Cardinal(10) then begin
  105. LastQueryTime := NowTime;
  106. Status := QuerySpawnServer(SequenceNumber, SPAWN_QUERY_STATUS);
  107. case Status of
  108. SPAWN_STATUS_RUNNING: ;
  109. SPAWN_STATUS_RETURNED_TRUE, SPAWN_STATUS_RETURNED_FALSE: Break;
  110. else
  111. InternalErrorFmt('CallSpawnServer: Unexpected status: %d', [Status]);
  112. end;
  113. end;
  114. { Delay for 10 ms, or until a message arrives }
  115. MsgWaitForMultipleObjects(0, THandle(nil^), False, 10, QS_ALLINPUT);
  116. until False;
  117. ResultCode := QuerySpawnServer(SequenceNumber, SPAWN_QUERY_RESULTCODE_LO) or
  118. (QuerySpawnServer(SequenceNumber, SPAWN_QUERY_RESULTCODE_HI) shl 16);
  119. Result := (Status = SPAWN_STATUS_RETURNED_TRUE);
  120. end;
  121. function InstExecEx(const RunAsOriginalUser: Boolean;
  122. const DisableFsRedir: Boolean; const Filename, Params, WorkingDir: String;
  123. const Wait: TExecWait; const ShowCmd: Integer;
  124. const ProcessMessagesProc: TProcedure; const OutputReader: TCreateProcessOutputReader;
  125. var ResultCode: Integer): Boolean;
  126. var
  127. M: TMemoryStream;
  128. begin
  129. if not RunAsOriginalUser or not SpawnServerPresent then begin
  130. Result := InstExec(DisableFsRedir, Filename, Params, WorkingDir,
  131. Wait, ShowCmd, ProcessMessagesProc, OutputReader, ResultCode);
  132. Exit;
  133. end;
  134. M := TMemoryStream.Create;
  135. try
  136. WriteLongintToStream(M, Ord(DisableFsRedir));
  137. WriteStringToStream(M, Filename);
  138. WriteStringToStream(M, Params);
  139. WriteStringToStream(M, WorkingDir);
  140. WriteLongintToStream(M, Ord(Wait));
  141. WriteLongintToStream(M, ShowCmd);
  142. WriteStringToStream(M, GetCurrentDir);
  143. Result := CallSpawnServer(CD_SpawnServer_Exec, M, ProcessMessagesProc,
  144. ResultCode);
  145. finally
  146. M.Free;
  147. end;
  148. end;
  149. function InstShellExecEx(const RunAsOriginalUser: Boolean;
  150. const Verb, Filename, Params, WorkingDir: String;
  151. const Wait: TExecWait; const ShowCmd: Integer;
  152. const ProcessMessagesProc: TProcedure; var ResultCode: Integer): Boolean;
  153. var
  154. M: TMemoryStream;
  155. begin
  156. if not RunAsOriginalUser or not SpawnServerPresent then begin
  157. Result := InstShellExec(Verb, Filename, Params, WorkingDir,
  158. Wait, ShowCmd, ProcessMessagesProc, ResultCode);
  159. Exit;
  160. end;
  161. M := TMemoryStream.Create;
  162. try
  163. WriteStringToStream(M, Verb);
  164. WriteStringToStream(M, Filename);
  165. WriteStringToStream(M, Params);
  166. WriteStringToStream(M, WorkingDir);
  167. WriteLongintToStream(M, Ord(Wait));
  168. WriteLongintToStream(M, ShowCmd);
  169. WriteStringToStream(M, GetCurrentDir);
  170. Result := CallSpawnServer(CD_SpawnServer_ShellExec, M, ProcessMessagesProc,
  171. ResultCode);
  172. finally
  173. M.Free;
  174. end;
  175. end;
  176. procedure InitializeSpawnClient(const AServerWnd: HWND);
  177. begin
  178. SpawnServerWnd := AServerWnd;
  179. SpawnServerPresent := True;
  180. end;
  181. end.