123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- { ***************************************************************************
- Copyright (c) 2016-2021 Kike Pérez
- Unit : Quick.Process
- Description : Process functions
- Author : Kike Pérez
- Version : 1.5
- Created : 14/07/2017
- Modified : 08/07/2021
- This file is part of QuickLib: https://github.com/exilon/QuickLib
- ***************************************************************************
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- *************************************************************************** }
- unit Quick.Process;
- {$i QuickLib.inc}
- interface
- uses
- {$IFDEF MSWINDOWS}
- Windows,
- ShellAPI,
- Quick.Console,
- {$IFNDEF CONSOLE}
- Controls,
- {$IFNDEF FPC}
- Vcl.Forms,
- Winapi.Messages,
- {$ENDIF}
- {$ENDIF}
- {$IFNDEF FPC}
- TlHelp32,
- psapi,
- {$ELSE}
- JwaTlHelp32,
- Process,
- {$ENDIF}
- {$ELSE}
- Posix.Base,
- Posix.Fcntl,
- {$ENDIF}
- Classes,
- DateUtils,
- SysUtils,
- Quick.Commons;
- {$IFDEF DELPHILINUX}
- type
- TStreamHandle = pointer;
- function popen(const command: PAnsiChar; const _type: PAnsiChar): TStreamHandle; cdecl; external libc name _PU + 'popen';
- function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose';
- function fgets(buffer: pointer; size: int32; Stream: TStreamHAndle): pointer; cdecl; external libc name _PU + 'fgets';
- {$ENDIF}
- //stop a running process
- {$IFDEF MSWINDOWS}
- function KillProcess(const aFileName : string) : Integer; overload;
- {$ELSE}
- function KillProcess(const aProcessName : string) : Integer; overload;
- {$ENDIF}
- function KillProcess(aProcessId : Cardinal) : Boolean; overload;
- //run process as Admin privilegies
- {$IFDEF MSWINDOWS}
- function RunAsAdmin(hWnd: HWND; const aFilename, aParameters: string): Boolean;
- //impersonate logon
- function Impersonate(const aDomain, aUser, aPassword : string): Boolean;
- //revert logon to real logged user
- procedure RevertToSelf;
- //remove dead icons from taskbar tray
- procedure RemoveDeadIcons;
- //get a process of running processes
- function GetProcessList : TstringList;
- //determine if a process is running
- function IsProcessRunnig(const aFileName: string; aFullPath: Boolean): Boolean;
- //get id running process
- function GetProcessId(const aFilename : string; out vProcessId : Integer) : Boolean; overload;
- //get user name is running a process
- function GetProcessUser(aProcessId : DWORD) : string; overload;
- function GetProcessUser(const aFileName : string) : string; overload;
- //executes an aplication and wait for terminate
- function ExecuteAndWait(const aFilename, aCommandLine: string): Boolean;
- function ShellExecuteAndWait(const aOperation, aFileName, aParameter, aDirectory : string; aShowMode : Word; aWaitForTerminate: Boolean) : LongInt;
- {$ENDIF}
- //runs a command and gets console output
- function RunCommand(const aFilename, aParameters : string) : TStringList;
- {$IFDEF MSWINDOWS}
- {$IFNDEF FPC}
- //execute an application and return handle
- function ShellExecuteReturnHandle(const aOperation, aFileName, aParameters, aWorkingDir : string; aShowMode: Integer) : THandle;
- {$ENDIF}
- //find an open main window handle
- function FindMainWindow(PID: DWord): DWord;
- //wait for a time period to find an opened main window handle
- function FindMainWindowTimeout(ProcHND : THandle; TimeoutSecs : Integer = 20) : THandle; overload;
- //wait for a time period to find an opened window handle
- function FindWindowTimeout(const aWindowsName : string; TimeoutMSecs : Integer = 1000) : THandle;
- {$IFNDEF CONSOLE}
- //capture a window handle and show it into a wincontrol
- procedure CaptureWindowIntoControl(aWindowHandle: THandle; aContainer: TWinControl);
- {$ENDIF}
- {$ENDIF}
- implementation
- {$IFDEF MSWINDOWS}
- const
- DNLEN = 15;
- UNLEN = 256;
- type
- PEnumInfo = ^TEnumInfo;
- TEnumInfo = record
- ProcessID: DWORD;
- HWND: THandle;
- end;
- PTOKEN_USER = ^TOKEN_USER;
- TOKEN_USER = record
- User: TSidAndAttributes;
- end;
- function EnumWindowsProc(hwnd : DWord; var einfo: TEnumInfo) : BOOL; stdcall;
- var
- PID: DWord;
- begin
- GetWindowThreadProcessId(hwnd, @PID);
- Result := (PID <> einfo.ProcessID) or (not IsWindowVisible(hwnd)) or (not IsWindowEnabled(hwnd));
- if not Result then einfo.HWND := hwnd;
- end;
- {$IFNDEF FPC}
- function CreateWin9xProcessList : TStringList;
- var
- hSnapShot: THandle;
- ProcInfo: TProcessEntry32;
- begin
- Result := TStringList.Create;
- hSnapShot := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (hSnapShot <> THandle(-1)) then
- begin
- ProcInfo.dwSize := SizeOf(ProcInfo);
- if (Process32First(hSnapshot, ProcInfo)) then
- begin
- Result.Add(ProcInfo.szExeFile);
- while (Process32Next(hSnapShot, ProcInfo)) do Result.Add(ProcInfo.szExeFile);
- end;
- CloseHandle(hSnapShot);
- end;
- end;
- function CreateWinNTProcessList : TstringList;
- var
- PIDArray: array [0..1023] of DWORD;
- cb: DWORD;
- I: Integer;
- ProcCount: Integer;
- hMod: HMODULE;
- hProcess: THandle;
- ModuleName: array [0..300] of Char;
- begin
- Result := TStringList.Create;
- EnumProcesses(@PIDArray, SizeOf(PIDArray), cb);
- ProcCount := cb div SizeOf(DWORD);
- for I := 0 to ProcCount - 1 do
- begin
- hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or
- PROCESS_VM_READ,
- False,
- PIDArray[I]);
- if (hProcess <> 0) then
- begin
- EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cb);
- GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName));
- Result.Add(ModuleName);
- CloseHandle(hProcess);
- end;
- end;
- end;
- function GetProcessList : TStringList;
- var
- ovinfo: TOSVersionInfo;
- begin
- Result := nil;
- ovinfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
- GetVersionEx(ovinfo);
- case ovinfo.dwPlatformId of
- VER_PLATFORM_WIN32_WINDOWS : Result := CreateWin9xProcessList;
- VER_PLATFORM_WIN32_NT : Result := CreateWinNTProcessList;
- end
- end;
- {$ELSE}
- function GetProcessList : TStringList;
- var
- pr : THandle;
- pe: TProcessEntry32;
- begin
- Result := TStringList.Create;
- pr := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
- try
- pe.dwSize := SizeOf(pe);
- if Process32First(pr,pe) then
- begin
- while Process32Next(pr,pe) do Result.Add(pe.szExeFile);
- end;
- finally
- CloseHandle(pr);
- end;
- end;
- {$ENDIF}
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- function KillProcess(const aFileName: string): Integer;
- const
- PROCESS_TERMINATE = $0001;
- var
- ContinueLoop: BOOL;
- FSnapshotHandle: THandle;
- FProcessEntry32: TProcessEntry32;
- begin
- Result := 0;
- FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
- ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
- while Integer(ContinueLoop) <> 0 do
- begin
- if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
- UpperCase(aFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
- UpperCase(aFileName))) then
- Result := Integer(TerminateProcess(
- OpenProcess(PROCESS_TERMINATE,
- BOOL(0),
- FProcessEntry32.th32ProcessID),
- 0));
- ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
- end;
- CloseHandle(FSnapshotHandle);
- end;
- {$ELSE}
- function KillProcess(const aProcessName: string): Integer;
- var
- sl : TStringList;
- begin
- sl := RunCommand('pkill',aProcessName);
- try
- Result := 1;
- finally
- sl.Free;
- end;
- end;
- {$ENDIF}
- function KillProcess(aProcessId : Cardinal) : Boolean;
- {$IFDEF MSWINDOWS}
- var
- hProcess : THandle;
- begin
- Result := False;
- hProcess := OpenProcess(PROCESS_TERMINATE,False,aProcessId);
- if hProcess > 0 then
- try
- Result := Win32Check(Windows.TerminateProcess(hProcess,0));
- finally
- CloseHandle(hProcess);
- end;
- end;
- {$ELSE}
- var
- sl : TStringList;
- begin
- sl := RunCommand('kill',aProcessId.ToString);
- try
- Result := True;
- finally
- sl.Free;
- end;
- end;
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- function RunAsAdmin(hWnd: HWND; const aFilename, aParameters: string): Boolean;
- var
- shinfo: TShellExecuteInfo;
- begin
- ZeroMemory(@shinfo, SizeOf(shinfo));
- shinfo.cbSize := SizeOf(TShellExecuteInfo);
- shinfo.Wnd := hwnd;
- shinfo.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
- shinfo.lpVerb := PChar('runas');
- shinfo.lpFile := PChar(aFilename);
- if aParameters <> '' then shinfo.lpParameters := PChar(aParameters);
- shinfo.nShow := SW_SHOWNORMAL;
- {$IFDEF FPC}
- Result := ShellExecuteExW(@shinfo);
- {$ELSE}
- Result := ShellExecuteEx(@shinfo);
- {$ENDIF}
- end;
- function Impersonate(const aDomain, aUser, aPassword : string): Boolean;
- var
- htoken : THandle;
- begin
- Result := False;
- if LogonUser(PChar(aUser),PChar(aDomain),PChar(aPassword),LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,htoken) then
- begin
- Result := ImpersonateLoggedOnUser(htoken);
- end;
- end;
- procedure RevertToSelf;
- begin
- Windows.RevertToSelf;
- end;
- procedure RemoveDeadIcons;
- var
- TrayWindow : HWnd;
- WindowRect : TRect;
- SmallIconWidth : Integer;
- SmallIconHeight : Integer;
- CursorPos : TPoint;
- Row : Integer;
- Col : Integer;
- begin
- TrayWindow := FindWindowEx(FindWindow('Shell_TrayWnd',NIL),0,'TrayNotifyWnd',NIL);
- if not GetWindowRect(TrayWindow,WindowRect) then Exit;
- SmallIconWidth := GetSystemMetrics(SM_CXSMICON);
- SmallIconHeight := GetSystemMetrics(SM_CYSMICON);
- GetCursorPos(CursorPos);
- with WindowRect do
- begin
- for Row := 0 to (Bottom - Top) DIV SmallIconHeight do
- begin
- for Col := 0 to (Right - Left) DIV SmallIconWidth do
- begin
- SetCursorPos(Left + Col * SmallIconWidth, Top + Row * SmallIconHeight);
- Sleep(0);
- end;
- end;
- end;
- SetCursorPos(CursorPos.X,CursorPos.Y);
- RedrawWindow(TrayWindow,NIL,0,RDW_INVALIDATE OR RDW_ERASE OR RDW_UPDATENOW);
- end;
- function IsProcessRunnig(const aFileName: string; aFullPath: Boolean): Boolean;
- var
- i: Integer;
- proclist: TstringList;
- begin
- try
- proclist := GetProcessList;
- Result := False;
- if proclist = nil then Exit;
- for i := 0 to proclist.Count - 1 do
- begin
- if not aFullPath then
- begin
- if CompareText(ExtractFileName(proclist.Strings[i]), aFileName) = 0 then Result := True
- end
- else if CompareText(proclist.strings[i], aFileName) = 0 then Result := True;
- if Result then Break;
- end;
- finally
- proclist.Free;
- end;
- end;
- function GetProcessId(const aFilename : string; out vProcessId : Integer) : Boolean;
- var
- nproc: BOOL;
- snapHnd : THandle;
- procEntry: TProcessEntry32;
- begin
- result := false;
- snapHnd := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- try
- procEntry.dwSize := Sizeof(procEntry);
- nproc := Process32First(snapHnd, procEntry);
- while Integer(nproc) <> 0 do
- begin
- if (StrIComp(PChar(ExtractFileName(procEntry.szExeFile)), PChar(aFilename)) = 0)
- or (StrIComp(procEntry.szExeFile, PChar(aFilename)) = 0) then
- begin
- vProcessId := procEntry.th32ProcessID;
- Result := true;
- Break;
- end;
- nproc := Process32Next(snapHnd, procEntry);
- end;
- finally
- CloseHandle(snapHnd);
- end;
- end;
- function GetProcessUser(aProcessId : DWORD): string;
- var
- buffer, domain, user: DWORD;
- procHnd, tokenHnd: THandle;
- lpUser: PTOKEN_USER;
- snu: SID_NAME_USE;
- szDomain: array [0..DNLEN] of Char;
- szUser: array [0..UNLEN] of Char;
- begin
- Result := '';
- procHnd := OpenProcess(PROCESS_QUERY_INFORMATION, False, aProcessId);
- if procHnd = 0 then Exit;
- try
- if not OpenProcessToken(procHnd, TOKEN_QUERY, tokenHnd) then Exit;
- try
- if not GetTokenInformation(tokenHnd, TokenUser, nil, 0, buffer) then
- begin
- if GetLastError <> ERROR_INSUFFICIENT_BUFFER then Exit;
- end;
- if buffer = 0 then Exit;
- GetMem(lpUser, buffer);
- if not Assigned(lpUser) then Exit;
- try
- if not GetTokenInformation(tokenHnd, TokenUser, lpUser, buffer, buffer) then Exit;
- domain := DNLEN + 1;
- user := UNLEN + 1;
- if LookupAccountSid(nil, lpUser.User.Sid, szUser, user, szDomain,
- domain, snu) then Result := szUser;
- finally
- FreeMem(lpUser);
- end;
- finally
- CloseHandle(tokenHnd);
- end;
- finally
- CloseHandle(procHnd);
- end;
- end;
- function GetProcessUser(const aFilename : string) : string;
- var
- procId : Integer;
- begin
- if not GetProcessId(aFilename,procId) then raise Exception.Create('Process not found!')
- else Result := GetProcessUser(procId);
- end;
- function ExecuteAndWait(const aFilename, aCommandLine: string): Boolean;
- var
- dwExitCode: DWORD;
- tpiProcess: TProcessInformation;
- tsiStartup: TStartupInfo;
- begin
- Result := False;
- FillChar(tsiStartup, SizeOf(TStartupInfo), 0);
- tsiStartup.cb := SizeOf(TStartupInfo);
- if CreateProcess(PChar(aFilename), PChar(aCommandLine), nil, nil, False, 0,
- nil, nil, tsiStartup, tpiProcess) then
- begin
- if WAIT_OBJECT_0 = WaitForSingleObject(tpiProcess.hProcess, INFINITE) then
- begin
- if GetExitCodeProcess(tpiProcess.hProcess, dwExitCode) then
- begin
- if dwExitCode = 0 then
- Result := True
- else
- SetLastError(dwExitCode + $2000);
- end;
- end;
- dwExitCode := GetLastError;
- CloseHandle(tpiProcess.hProcess);
- CloseHandle(tpiProcess.hThread);
- SetLastError(dwExitCode);
- end;
- end;
- {$ENDIF}
- function RunCommand(const aFilename, aParameters : string) : TStringList;
- {$IFDEF MSWINDOWS}
- begin
- Result := TStringList.Create;
- RunConsoleCommand(aFilename,aParameters,nil,Result);
- end;
- {$ELSE}
- var
- Handle: TStreamHandle;
- Data: array[0..511] of uint8;
- command : PAnsiChar;
- begin
- Result := TStringList.Create;
- try
- if aParameters.IsEmpty then command := PAnsiChar(AnsiString(aFilename))
- else command := PAnsiChar(AnsiString(aFilename + ' ' + aParameters));
- Handle := popen(command, 'r');
- try
- while fgets(@Data[0], Sizeof(Data), Handle) <> nil do Result.Add(Utf8ToString(@Data[0]));
- finally
- pclose(Handle);
- end;
- except
- on E: Exception do
- begin
- Result.Free;
- Exception.CreateFmt('RunCommand: %s',[e.Message]);
- end;
- end;
- end;
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- function ShellExecuteAndWait(const aOperation, aFileName, aParameter, aDirectory: string; aShowMode : Word; aWaitForTerminate: Boolean) : LongInt;
- var
- done: Boolean;
- shinfo: TShellExecuteInfo;
- begin
- FillChar(shinfo, SizeOf(shinfo), Chr(0));
- shinfo.cbSize := SizeOf(shinfo);
- shinfo.fMask := SEE_MASK_NOCLOSEPROCESS;
- shinfo.lpVerb := PChar(aOperation);
- shinfo.lpFile := PChar(aFileName);
- shinfo.lpParameters := PChar(aParameter);
- shinfo.lpDirectory := PChar(aDirectory);
- shinfo.nShow := aShowMode;
- {$IFDEF FPC}
- done := Boolean(ShellExecuteExW(@shinfo));
- {$ELSE}
- done := Boolean(ShellExecuteEx(@shinfo));
- {$ENDIF}
- if done then
- begin
- if aWaitForTerminate then
- begin
- while WaitForSingleObject(shinfo.hProcess, 100) = WAIT_TIMEOUT do
- begin
- {$IFDEF CONSOLE}
- ProcessMessages;
- {$ELSE}
- Application.ProcessMessages;
- {$ENDIF}
- end;
- done := GetExitCodeProcess(shinfo.hProcess, DWORD(Result));
- end
- else Result := 0;
- end;
- if not done then Result := -1;
- end;
- {$IFNDEF FPC}
- function ShellExecuteReturnHandle(const aOperation, aFileName, aParameters, aWorkingDir : string; aShowMode: Integer) : THandle;
- var
- exInfo: TShellExecuteInfo;
- Ph: THandle;
- begin
- Result := 0;
- FillChar(exInfo, SizeOf(exInfo), 0);
- with exInfo do
- begin
- cbSize := SizeOf(exInfo);
- fMask := SEE_MASK_NOCLOSEPROCESS;
- Wnd := GetActiveWindow();
- ExInfo.lpVerb := PChar(aOperation);
- ExInfo.lpParameters := PChar(aParameters);
- exInfo.lpDirectory := PChar(aWorkingDir);
- lpFile := PChar(aFileName);
- nShow := aShowMode;
- end;
- if ShellExecuteEx(@exInfo) then Ph := exInfo.hProcess;
- Result := Windows.GetProcessId(exInfo.hProcess);
- End;
- {$ENDIF}
- function FindMainWindow(PID : DWord): DWORD;
- var
- eInfo: TEnumInfo;
- begin
- eInfo.ProcessID := PID;
- eInfo.HWND := 0;
- EnumWindows(@EnumWindowsProc, Integer(@eInfo));
- Result := eInfo.HWND;
- end;
- function FindMainWindowTimeout(ProcHND : THandle; TimeoutSecs : Integer = 20) : THandle;
- var
- startime : TDateTime;
- begin
- if ProcHND = 0 then Exit;
- startime := Now();
- Result := 0;
- repeat
- Result := FindMainWindow(ProcHND);
- {$IFDEF CONSOLE}
- ProcessMessages;
- {$ELSE}
- Application.ProcessMessages;
- {$ENDIF}
- until (Result <> 0) or (SecondsBetween(Now(),startime) > TimeoutSecs);
- end;
- function FindWindowTimeout(const aWindowsName : string; TimeoutMSecs : Integer = 1000) : THandle;
- var
- startime : TDateTime;
- begin
- startime := Now();
- repeat
- Result := FindWindow(0,{$IFDEF FPC}PChar{$ELSE}PWideChar{$ENDIF}(aWindowsName));
- {$IFDEF CONSOLE}
- ProcessMessages;
- {$ELSE}
- Application.ProcessMessages;
- {$ENDIF}
- until (Result <> 0) or (MilliSecondsBetween(Now(),startime) > TimeoutMSecs);
- end;
- {$IFNDEF CONSOLE}
- procedure CaptureWindowIntoControl(aWindowHandle: THandle; aContainer: TWinControl);
- var
- WindowStyle : Integer;
- appthreadId: Cardinal;
- begin
- WindowStyle := GetWindowLong(aWindowHandle, GWL_STYLE);
- WindowStyle := WindowStyle
- // - WS_CAPTION
- - WS_BORDER
- // - WS_OVERLAPPED
- - WS_THICKFRAME;
- SetWindowLong(aWindowHandle,GWL_STYLE,WindowStyle);
- appthreadId := GetWindowThreadProcessId(aWindowHandle, nil);
- AttachThreadInput(GetCurrentThreadId, appthreadId, True);
- SetParent(aWindowHandle,aContainer.Handle);
- SendMessage(aContainer.Handle, WM_UPDATEUISTATE, UIS_INITIALIZE, 0);
- UpdateWindow(aWindowHandle);
- SetWindowLong(aContainer.Handle, GWL_STYLE, GetWindowLong(aContainer.Handle,GWL_STYLE) or WS_CLIPCHILDREN);
- SetWindowPos(aWindowHandle,0,0,0,aContainer.ClientWidth,aContainer.ClientHeight,SWP_NOOWNERZORDER);
- SetForegroundWindow(aWindowHandle);
- end;
- {$ENDIF}
- {$ENDIF}
- end.
|