Quick.Process.pas 15 KB

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