Quick.Process.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. { ***************************************************************************
  2. Copyright (c) 2016-2018 Kike Pérez
  3. Unit : Quick.Process
  4. Description : Process functions
  5. Author : Kike Pérez
  6. Version : 1.5
  7. Created : 14/07/2017
  8. Modified : 15/12/2020
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Process;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Windows,
  26. Classes,
  27. {$IFNDEF CONSOLE}
  28. Controls,
  29. {$IFNDEF FPC}
  30. Vcl.Forms,
  31. Winapi.Messages,
  32. {$ENDIF}
  33. {$ENDIF}
  34. DateUtils,
  35. {$IFNDEF FPC}
  36. TlHelp32,
  37. psapi,
  38. {$ELSE}
  39. JwaTlHelp32,
  40. Process,
  41. {$ENDIF}
  42. SysUtils,
  43. ShellAPI,
  44. Quick.Commons;
  45. //stop a running process
  46. function KillProcess(const aFileName : string) : Integer; overload;
  47. function KillProcess(aProcessId : Cardinal) : Boolean; overload;
  48. //run process as Admin privilegies
  49. function RunAsAdmin(hWnd: HWND; const aFilename, aParameters: string): Boolean;
  50. //impersonate logon
  51. function Impersonate(const aDomain, aUser, aPassword : string): Boolean;
  52. //revert logon to real logged user
  53. procedure RevertToSelf;
  54. //remove dead icons from taskbar tray
  55. procedure RemoveDeadIcons;
  56. //get a process of running processes
  57. function GetProcessList : TstringList;
  58. //determine if a process is running
  59. function IsProcessRunnig(const aFileName: string; aFullPath: Boolean): Boolean;
  60. //get id running process
  61. function GetProcessId(const aFilename : string; out vProcessId : Integer) : Boolean; overload;
  62. //get user name is running a process
  63. function GetProcessUser(aProcessId : DWORD) : string; overload;
  64. function GetProcessUser(const aFileName : string) : string; overload;
  65. //executes an aplication and wait for terminate
  66. function ExecuteAndWait(const aFilename, aCommandLine: string): Boolean;
  67. function ShellExecuteAndWait(const aOperation, aFileName, aParameter, aDirectory : string; aShowMode : Word; aWaitForTerminate: Boolean) : LongInt;
  68. {$IFNDEF FPC}
  69. //execute an application and return handle
  70. function ShellExecuteReturnHandle(const aOperation, aFileName, aParameters, aWorkingDir : string; aShowMode: Integer) : THandle;
  71. {$ENDIF}
  72. //find an open main window handle
  73. function FindMainWindow(PID: DWord): DWord;
  74. //wait for a time period to find an opened main window handle
  75. function FindMainWindowTimeout(ProcHND : THandle; TimeoutSecs : Integer = 20) : THandle; overload;
  76. //wait for a time period to find an opened window handle
  77. function FindWindowTimeout(const aWindowsName : string; TimeoutMSecs : Integer = 1000) : THandle;
  78. {$IFNDEF CONSOLE}
  79. //capture a window handle and show it into a wincontrol
  80. procedure CaptureWindowIntoControl(aWindowHandle: THandle; aContainer: TWinControl);
  81. {$ENDIF}
  82. implementation
  83. const
  84. DNLEN = 15;
  85. UNLEN = 256;
  86. type
  87. PEnumInfo = ^TEnumInfo;
  88. TEnumInfo = record
  89. ProcessID: DWORD;
  90. HWND: THandle;
  91. end;
  92. PTOKEN_USER = ^TOKEN_USER;
  93. TOKEN_USER = record
  94. User: TSidAndAttributes;
  95. end;
  96. function EnumWindowsProc(hwnd : DWord; var einfo: TEnumInfo) : BOOL; stdcall;
  97. var
  98. PID: DWord;
  99. begin
  100. GetWindowThreadProcessId(hwnd, @PID);
  101. Result := (PID <> einfo.ProcessID) or (not IsWindowVisible(hwnd)) or (not IsWindowEnabled(hwnd));
  102. if not Result then einfo.HWND := hwnd;
  103. end;
  104. {$IFNDEF FPC}
  105. function CreateWin9xProcessList : TStringList;
  106. var
  107. hSnapShot: THandle;
  108. ProcInfo: TProcessEntry32;
  109. begin
  110. Result := TStringList.Create;
  111. hSnapShot := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  112. if (hSnapShot <> THandle(-1)) then
  113. begin
  114. ProcInfo.dwSize := SizeOf(ProcInfo);
  115. if (Process32First(hSnapshot, ProcInfo)) then
  116. begin
  117. Result.Add(ProcInfo.szExeFile);
  118. while (Process32Next(hSnapShot, ProcInfo)) do Result.Add(ProcInfo.szExeFile);
  119. end;
  120. CloseHandle(hSnapShot);
  121. end;
  122. end;
  123. function CreateWinNTProcessList : TstringList;
  124. var
  125. PIDArray: array [0..1023] of DWORD;
  126. cb: DWORD;
  127. I: Integer;
  128. ProcCount: Integer;
  129. hMod: HMODULE;
  130. hProcess: THandle;
  131. ModuleName: array [0..300] of Char;
  132. begin
  133. Result := TStringList.Create;
  134. EnumProcesses(@PIDArray, SizeOf(PIDArray), cb);
  135. ProcCount := cb div SizeOf(DWORD);
  136. for I := 0 to ProcCount - 1 do
  137. begin
  138. hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or
  139. PROCESS_VM_READ,
  140. False,
  141. PIDArray[I]);
  142. if (hProcess <> 0) then
  143. begin
  144. EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cb);
  145. GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName));
  146. Result.Add(ModuleName);
  147. CloseHandle(hProcess);
  148. end;
  149. end;
  150. end;
  151. function GetProcessList : TStringList;
  152. var
  153. ovinfo: TOSVersionInfo;
  154. begin
  155. Result := nil;
  156. ovinfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  157. GetVersionEx(ovinfo);
  158. case ovinfo.dwPlatformId of
  159. VER_PLATFORM_WIN32_WINDOWS : Result := CreateWin9xProcessList;
  160. VER_PLATFORM_WIN32_NT : Result := CreateWinNTProcessList;
  161. end
  162. end;
  163. {$ELSE}
  164. function GetProcessList : TStringList;
  165. var
  166. pr : THandle;
  167. pe: TProcessEntry32;
  168. begin
  169. Result := TStringList.Create;
  170. pr := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
  171. try
  172. pe.dwSize := SizeOf(pe);
  173. if Process32First(pr,pe) then
  174. begin
  175. while Process32Next(pr,pe) do Result.Add(pe.szExeFile);
  176. end;
  177. finally
  178. CloseHandle(pr);
  179. end;
  180. end;
  181. {$ENDIF}
  182. function KillProcess(const aFileName: string): Integer;
  183. const
  184. PROCESS_TERMINATE = $0001;
  185. var
  186. ContinueLoop: BOOL;
  187. FSnapshotHandle: THandle;
  188. FProcessEntry32: TProcessEntry32;
  189. begin
  190. Result := 0;
  191. FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  192. FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  193. ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  194. while Integer(ContinueLoop) <> 0 do
  195. begin
  196. if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
  197. UpperCase(aFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
  198. UpperCase(aFileName))) then
  199. Result := Integer(TerminateProcess(
  200. OpenProcess(PROCESS_TERMINATE,
  201. BOOL(0),
  202. FProcessEntry32.th32ProcessID),
  203. 0));
  204. ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  205. end;
  206. CloseHandle(FSnapshotHandle);
  207. end;
  208. function KillProcess(aProcessId : Cardinal) : Boolean;
  209. var
  210. hProcess : THandle;
  211. begin
  212. Result := False;
  213. hProcess := OpenProcess(PROCESS_TERMINATE,False,aProcessId);
  214. if hProcess > 0 then
  215. try
  216. Result := Win32Check(Windows.TerminateProcess(hProcess,0));
  217. finally
  218. CloseHandle(hProcess);
  219. end;
  220. end;
  221. function RunAsAdmin(hWnd: HWND; const aFilename, aParameters: string): Boolean;
  222. var
  223. shinfo: TShellExecuteInfo;
  224. begin
  225. ZeroMemory(@shinfo, SizeOf(shinfo));
  226. shinfo.cbSize := SizeOf(TShellExecuteInfo);
  227. shinfo.Wnd := hwnd;
  228. shinfo.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
  229. shinfo.lpVerb := PChar('runas');
  230. shinfo.lpFile := PChar(aFilename);
  231. if aParameters <> '' then shinfo.lpParameters := PChar(aParameters);
  232. shinfo.nShow := SW_SHOWNORMAL;
  233. {$IFDEF FPC}
  234. Result := ShellExecuteExW(@shinfo);
  235. {$ELSE}
  236. Result := ShellExecuteEx(@shinfo);
  237. {$ENDIF}
  238. end;
  239. function Impersonate(const aDomain, aUser, aPassword : string): Boolean;
  240. var
  241. htoken : THandle;
  242. begin
  243. Result := False;
  244. if LogonUser(PChar(aUser),PChar(aDomain),PChar(aPassword),LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,htoken) then
  245. begin
  246. Result := ImpersonateLoggedOnUser(htoken);
  247. end;
  248. end;
  249. procedure RevertToSelf;
  250. begin
  251. Windows.RevertToSelf;
  252. end;
  253. procedure RemoveDeadIcons;
  254. var
  255. TrayWindow : HWnd;
  256. WindowRect : TRect;
  257. SmallIconWidth : Integer;
  258. SmallIconHeight : Integer;
  259. CursorPos : TPoint;
  260. Row : Integer;
  261. Col : Integer;
  262. begin
  263. TrayWindow := FindWindowEx(FindWindow('Shell_TrayWnd',NIL),0,'TrayNotifyWnd',NIL);
  264. if not GetWindowRect(TrayWindow,WindowRect) then Exit;
  265. SmallIconWidth := GetSystemMetrics(SM_CXSMICON);
  266. SmallIconHeight := GetSystemMetrics(SM_CYSMICON);
  267. GetCursorPos(CursorPos);
  268. with WindowRect do
  269. begin
  270. for Row := 0 to (Bottom - Top) DIV SmallIconHeight do
  271. begin
  272. for Col := 0 to (Right - Left) DIV SmallIconWidth do
  273. begin
  274. SetCursorPos(Left + Col * SmallIconWidth, Top + Row * SmallIconHeight);
  275. Sleep(0);
  276. end;
  277. end;
  278. end;
  279. SetCursorPos(CursorPos.X,CursorPos.Y);
  280. RedrawWindow(TrayWindow,NIL,0,RDW_INVALIDATE OR RDW_ERASE OR RDW_UPDATENOW);
  281. end;
  282. function IsProcessRunnig(const aFileName: string; aFullPath: Boolean): Boolean;
  283. var
  284. i: Integer;
  285. proclist: TstringList;
  286. begin
  287. try
  288. proclist := GetProcessList;
  289. Result := False;
  290. if proclist = nil then Exit;
  291. for i := 0 to proclist.Count - 1 do
  292. begin
  293. if not aFullPath then
  294. begin
  295. if CompareText(ExtractFileName(proclist.Strings[i]), aFileName) = 0 then Result := True
  296. end
  297. else if CompareText(proclist.strings[i], aFileName) = 0 then Result := True;
  298. if Result then Break;
  299. end;
  300. finally
  301. proclist.Free;
  302. end;
  303. end;
  304. function GetProcessId(const aFilename : string; out vProcessId : Integer) : Boolean;
  305. var
  306. nproc: BOOL;
  307. snapHnd : THandle;
  308. procEntry: TProcessEntry32;
  309. begin
  310. result := false;
  311. snapHnd := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  312. try
  313. procEntry.dwSize := Sizeof(procEntry);
  314. nproc := Process32First(snapHnd, procEntry);
  315. while Integer(nproc) <> 0 do
  316. begin
  317. if (StrIComp(PChar(ExtractFileName(procEntry.szExeFile)), PChar(aFilename)) = 0)
  318. or (StrIComp(procEntry.szExeFile, PChar(aFilename)) = 0) then
  319. begin
  320. vProcessId := procEntry.th32ProcessID;
  321. Result := true;
  322. Break;
  323. end;
  324. nproc := Process32Next(snapHnd, procEntry);
  325. end;
  326. finally
  327. CloseHandle(snapHnd);
  328. end;
  329. end;
  330. function GetProcessUser(aProcessId : DWORD): string;
  331. var
  332. buffer, domain, user: DWORD;
  333. procHnd, tokenHnd: THandle;
  334. lpUser: PTOKEN_USER;
  335. snu: SID_NAME_USE;
  336. szDomain: array [0..DNLEN] of Char;
  337. szUser: array [0..UNLEN] of Char;
  338. begin
  339. Result := '';
  340. procHnd := OpenProcess(PROCESS_QUERY_INFORMATION, False, aProcessId);
  341. if procHnd = 0 then Exit;
  342. try
  343. if not OpenProcessToken(procHnd, TOKEN_QUERY, tokenHnd) then Exit;
  344. try
  345. if not GetTokenInformation(tokenHnd, TokenUser, nil, 0, buffer) then
  346. begin
  347. if GetLastError <> ERROR_INSUFFICIENT_BUFFER then Exit;
  348. end;
  349. if buffer = 0 then Exit;
  350. GetMem(lpUser, buffer);
  351. if not Assigned(lpUser) then Exit;
  352. try
  353. if not GetTokenInformation(tokenHnd, TokenUser, lpUser, buffer, buffer) then Exit;
  354. domain := DNLEN + 1;
  355. user := UNLEN + 1;
  356. if LookupAccountSid(nil, lpUser.User.Sid, szUser, user, szDomain,
  357. domain, snu) then Result := szUser;
  358. finally
  359. FreeMem(lpUser);
  360. end;
  361. finally
  362. CloseHandle(tokenHnd);
  363. end;
  364. finally
  365. CloseHandle(procHnd);
  366. end;
  367. end;
  368. function GetProcessUser(const aFilename : string) : string;
  369. var
  370. procId : Integer;
  371. begin
  372. if not GetProcessId(aFilename,procId) then raise Exception.Create('Process not found!')
  373. else Result := GetProcessUser(procId);
  374. end;
  375. function ExecuteAndWait(const aFilename, aCommandLine: string): Boolean;
  376. var
  377. dwExitCode: DWORD;
  378. tpiProcess: TProcessInformation;
  379. tsiStartup: TStartupInfo;
  380. begin
  381. Result := False;
  382. FillChar(tsiStartup, SizeOf(TStartupInfo), 0);
  383. tsiStartup.cb := SizeOf(TStartupInfo);
  384. if CreateProcess(PChar(aFilename), PChar(aCommandLine), nil, nil, False, 0,
  385. nil, nil, tsiStartup, tpiProcess) then
  386. begin
  387. if WAIT_OBJECT_0 = WaitForSingleObject(tpiProcess.hProcess, INFINITE) then
  388. begin
  389. if GetExitCodeProcess(tpiProcess.hProcess, dwExitCode) then
  390. begin
  391. if dwExitCode = 0 then
  392. Result := True
  393. else
  394. SetLastError(dwExitCode + $2000);
  395. end;
  396. end;
  397. dwExitCode := GetLastError;
  398. CloseHandle(tpiProcess.hProcess);
  399. CloseHandle(tpiProcess.hThread);
  400. SetLastError(dwExitCode);
  401. end;
  402. end;
  403. function ShellExecuteAndWait(const aOperation, aFileName, aParameter, aDirectory: string; aShowMode : Word; aWaitForTerminate: Boolean) : LongInt;
  404. var
  405. done: Boolean;
  406. shinfo: TShellExecuteInfo;
  407. begin
  408. FillChar(shinfo, SizeOf(shinfo), Chr(0));
  409. shinfo.cbSize := SizeOf(shinfo);
  410. shinfo.fMask := SEE_MASK_NOCLOSEPROCESS;
  411. shinfo.lpVerb := PChar(aOperation);
  412. shinfo.lpFile := PChar(aFileName);
  413. shinfo.lpParameters := PChar(aParameter);
  414. shinfo.lpDirectory := PChar(aDirectory);
  415. shinfo.nShow := aShowMode;
  416. {$IFDEF FPC}
  417. done := Boolean(ShellExecuteExW(@shinfo));
  418. {$ELSE}
  419. done := Boolean(ShellExecuteEx(@shinfo));
  420. {$ENDIF}
  421. if done then
  422. begin
  423. if aWaitForTerminate then
  424. begin
  425. while WaitForSingleObject(shinfo.hProcess, 100) = WAIT_TIMEOUT do
  426. begin
  427. {$IFDEF CONSOLE}
  428. ProcessMessages;
  429. {$ELSE}
  430. Application.ProcessMessages;
  431. {$ENDIF}
  432. end;
  433. done := GetExitCodeProcess(shinfo.hProcess, DWORD(Result));
  434. end
  435. else Result := 0;
  436. end;
  437. if not done then Result := -1;
  438. end;
  439. {$IFNDEF FPC}
  440. function ShellExecuteReturnHandle(const aOperation, aFileName, aParameters, aWorkingDir : string; aShowMode: Integer) : THandle;
  441. var
  442. exInfo: TShellExecuteInfo;
  443. Ph: THandle;
  444. begin
  445. Result := 0;
  446. FillChar(exInfo, SizeOf(exInfo), 0);
  447. with exInfo do
  448. begin
  449. cbSize := SizeOf(exInfo);
  450. fMask := SEE_MASK_NOCLOSEPROCESS;
  451. Wnd := GetActiveWindow();
  452. ExInfo.lpVerb := PChar(aOperation);
  453. ExInfo.lpParameters := PChar(aParameters);
  454. exInfo.lpDirectory := PChar(aWorkingDir);
  455. lpFile := PChar(aFileName);
  456. nShow := aShowMode;
  457. end;
  458. if ShellExecuteEx(@exInfo) then Ph := exInfo.hProcess;
  459. Result := Windows.GetProcessId(exInfo.hProcess);
  460. End;
  461. {$ENDIF}
  462. function FindMainWindow(PID : DWord): DWORD;
  463. var
  464. eInfo: TEnumInfo;
  465. begin
  466. eInfo.ProcessID := PID;
  467. eInfo.HWND := 0;
  468. EnumWindows(@EnumWindowsProc, Integer(@eInfo));
  469. Result := eInfo.HWND;
  470. end;
  471. function FindMainWindowTimeout(ProcHND : THandle; TimeoutSecs : Integer = 20) : THandle;
  472. var
  473. startime : TDateTime;
  474. begin
  475. if ProcHND = 0 then Exit;
  476. startime := Now();
  477. Result := 0;
  478. repeat
  479. Result := FindMainWindow(ProcHND);
  480. {$IFDEF CONSOLE}
  481. ProcessMessages;
  482. {$ELSE}
  483. Application.ProcessMessages;
  484. {$ENDIF}
  485. until (Result <> 0) or (SecondsBetween(Now(),startime) > TimeoutSecs);
  486. end;
  487. function FindWindowTimeout(const aWindowsName : string; TimeoutMSecs : Integer = 1000) : THandle;
  488. var
  489. startime : TDateTime;
  490. begin
  491. startime := Now();
  492. repeat
  493. Result := FindWindow(0,{$IFDEF FPC}PChar{$ELSE}PWideChar{$ENDIF}(aWindowsName));
  494. {$IFDEF CONSOLE}
  495. ProcessMessages;
  496. {$ELSE}
  497. Application.ProcessMessages;
  498. {$ENDIF}
  499. until (Result <> 0) or (MilliSecondsBetween(Now(),startime) > TimeoutMSecs);
  500. end;
  501. {$IFNDEF CONSOLE}
  502. procedure CaptureWindowIntoControl(aWindowHandle: THandle; aContainer: TWinControl);
  503. var
  504. WindowStyle : Integer;
  505. appthreadId: Cardinal;
  506. begin
  507. WindowStyle := GetWindowLong(aWindowHandle, GWL_STYLE);
  508. WindowStyle := WindowStyle
  509. // - WS_CAPTION
  510. - WS_BORDER
  511. // - WS_OVERLAPPED
  512. - WS_THICKFRAME;
  513. SetWindowLong(aWindowHandle,GWL_STYLE,WindowStyle);
  514. appthreadId := GetWindowThreadProcessId(aWindowHandle, nil);
  515. AttachThreadInput(GetCurrentThreadId, appthreadId, True);
  516. SetParent(aWindowHandle,aContainer.Handle);
  517. SendMessage(aContainer.Handle, WM_UPDATEUISTATE, UIS_INITIALIZE, 0);
  518. UpdateWindow(aWindowHandle);
  519. SetWindowLong(aContainer.Handle, GWL_STYLE, GetWindowLong(aContainer.Handle,GWL_STYLE) or WS_CLIPCHILDREN);
  520. SetWindowPos(aWindowHandle,0,0,0,aContainer.ClientWidth,aContainer.ClientHeight,SWP_NOOWNERZORDER);
  521. SetForegroundWindow(aWindowHandle);
  522. end;
  523. {$ENDIF}
  524. end.