Quick.Process.pas 16 KB

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