Quick.Process.pas 15 KB

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