Quick.Process.pas 15 KB

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