robertjohns 0 Posted July 5, 2023 Is there any possibility to check if the exe is executed by clicking on its own icon or by external exe Suppose Exe-1 and Exe-2 Need to check Exe-1 is whether executed by clicking on its own icon or executed by external Exe-2 Share this post Link to post
Der schöne Günther 312 Posted July 5, 2023 Do you have control over Exe-2 or is it by somebody else? If it is, can you be sure that the behaviour of Exe-2 might be changing in the future? Share this post Link to post
timfrost 75 Posted July 5, 2023 With the Windows API you can find information about your own process from its process ID, and in this "process information" is the process ID of the parent process that started your process. From this you can find whether the name of that process is Explorer or EXE1 (or something else). If you are not familiar with all this the API calls can appear quite complicated - look for examples in Delphi of using the Windows ToolHelp API, which includes the functions you need, and for which Delphi provides an interface in Winapi.TlHelp32. There are plenty of code examples around which you can copy but someone else may be able to give you a link to code that does exactly what you need. Share this post Link to post
robertjohns 0 Posted July 5, 2023 (edited) 53 minutes ago, Der schöne Günther said: Do you have control over Exe-2 or is it by somebody else? If it is, can you be sure that the behaviour of Exe-2 might be changing in the future? No I don't have control over Exe-2, Exe-1 must not run if it is not executed by its own Edited July 5, 2023 by robertjohns Share this post Link to post
Remy Lebeau 1345 Posted July 5, 2023 37 minutes ago, timfrost said: With the Windows API you can find information about your own process from its process ID, and in this "process information" is the process ID of the parent process that started your process. From this you can find whether the name of that process is Explorer or EXE1 (or something else). It is also possible to check if your process is being executed by a .lnk shortcut, by using the GetStartupInfo() API and checking the STARTUPINFO.dwFlags field for the STARTF_TITLEISLINKNAME flag. Share this post Link to post
robertjohns 0 Posted July 6, 2023 (edited) How to implement these methods Edited July 7, 2023 by robertjohns Share this post Link to post
programmerdelphi2k 237 Posted July 6, 2023 (edited) @robertjohns you would can some like this... You could check the "processID" of the "parent" that ran the current process. Normally, when running an application, "MSExplorer" is the "parent" (but not necessarily). So, if Exe2 executed Exe1, "Exe2" would have to be the "parent" of "Exe1", naturally, this might not be true at all, as there are ways to bypass this task. here is a very easy code to find on the internet, and that I just did some checks to try to find the answer im PROJECT1.exe ... see the "2nd" Project1.exe on left Memo and the right Memo = ProcessID from 1º and ParentID from 2º we'll have 2 instance of "Project1.exe" = 1 with BDS.exe and 1 with "Project1.exe" (from "run me" button) now, just do the changes and clean the code not necessary... the code is not mine! ok implementation {$R *.dfm} uses Winapi.ShellAPI, Winapi.PsAPI, Winapi.TlHelp32; function SearchParentProcessFrom(const PID: cardinal): string; var hProcess: THandle; path : array [0 .. MAX_PATH - 1] of char; begin result := '... Parent empty ...'; hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID); // if hProcess <> 0 then try if (GetModuleFileNameEx(hProcess, 0, path, MAX_PATH) <> 0) then // RaiseLastOSError; result := PID.ToString + '=' + ExtractFileName(path); // get Exename/Module finally CloseHandle(hProcess) end else result := '.... ERROR hProcess = 0 = No-Opended file = no problem at all'; end; function CheckProcessOnMemory: TArray<string>; const PROCESS_TERMINATE = $0001; var ContinueLoop : BOOL; FSnapshotHandle : THandle; FProcessEntry32 : TProcessEntry32; ListOfProcessOnMemory: TStringList; LArrIDandName : TArray<string>; LProcessID : cardinal; LProcessParentID : cardinal; i, LCounter : integer; LText : string; begin result := []; // ListOfProcessOnMemory := TStringList.Create; try FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); try FProcessEntry32.dwSize := SizeOf(FProcessEntry32); Process32First(FSnapshotHandle, FProcessEntry32); // while Process32Next(FSnapshotHandle, FProcessEntry32) do ListOfProcessOnMemory.AddPair( { a=b=c } { } FProcessEntry32.th32ProcessID.ToString + '=' + FProcessEntry32.th32ParentProcessID.ToString, { } FProcessEntry32.szExeFile); // Form1.Memo1.Lines.AddStrings(ListOfProcessOnMemory.ToStringArray); finally CloseHandle(FSnapshotHandle); end; // i := ListOfProcessOnMemory.Count; LCounter := 0; // while (LCounter < i) do begin LArrIDandName := ListOfProcessOnMemory[LCounter].Split(['=']); // 3 items (a=b=c) above... // if (length(LArrIDandName) = 3) then begin LProcessID := StrToIntDef(LArrIDandName[0], 0); // processID LProcessParentID := StrToIntDef(LArrIDandName[1], 0); // parentID // // if (LProcessID <> 0) then // "0" = "System" begin LText := SearchParentProcessFrom(LProcessParentID); // find parentID name // // parentID + processName if not(LText.IsEmpty) and LText.Contains(LArrIDandName[1]) and (LText.Contains(LArrIDandName[2])) then LText := LText + ' = load by itself'; // Form1.Memo2.Lines.Add(LArrIDandName[0] + '=' + LArrIDandName[2] + ' Parent: ' + LArrIDandName[1] + ' -> Search: ' + LText); end; end; // LCounter := LCounter + 1; end; finally ListOfProcessOnMemory.Free; end; end; procedure TForm1.Btn_All_Process_On_MemoryClick(Sender: TObject); begin CheckProcessOnMemory; end; procedure TForm1.Btn_Run_ME_againClick(Sender: TObject); begin // run me again... by myself! ShellExecute(0, PWideChar(''), PWideChar('project1.exe'), PWideChar(''), PWideChar(''), sw_normal); end; Edited July 6, 2023 by programmerdelphi2k Share this post Link to post
KodeZwerg 54 Posted July 6, 2023 (edited) Why not simple use a mutex and check at startup for its presence? No need to do all that what @programmerdelphi2k wrote at all. @programmerdelphi2k I suggest to switch to EnumProcesses(), it is way faster compared to CreateToolhelp32Snapshot(). Here's a small snippet example that you could run at startup function AppStartedByItself: Boolean; var dummy: THandle; FSA: SECURITY_ATTRIBUTES; FSD: SECURITY_DESCRIPTOR; begin InitializeSecurityDescriptor(@FSD, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(@FSD, True, nil, False); FSA.lpSecurityDescriptor := @FSD; FSA.nLength := SizeOf(SECURITY_ATTRIBUTES); FSA.bInheritHandle := True; dummy := CreateMutexW(@FSA, True, PWideChar('Global\' + 'SomethingUnique')); Result := (GetLastError = ERROR_ALREADY_EXISTS); end; call this method and react on True (app was started by itself) or False (app was started not by itself) //updated the result setting Edited July 6, 2023 by KodeZwerg Share this post Link to post
programmerdelphi2k 237 Posted July 6, 2023 (edited) 27 minutes ago, KodeZwerg said: No need to do all that what @programmerdelphi2k wrote at all. as I said: the code is from internet... I just checked the ParentID! of course, that exists API functions for better checking! now test your code for: as I did in my code above... same step-by-step Run the app as usual now, re-run this same app using procedure TForm1.Btn_Run_ME_againClick(Sender: TObject); begin // run me again... by myself! ShellExecute(0, PWideChar(''), PWideChar('project1.exe'), PWideChar(''), PWideChar(''), sw_normal); end; now, on first app run your code again summary: the 2 session app will be showing that it was load by itself!!! --> but the first app (session) was called by BDS.exe or Explorer, and 2nd app was called by 1st App then, your code is fail too! only the 2nd session was loaded by itself ... the 1st session was loaded by BDS.exe!!! each session has a ID distinct, because it is process distincts Edited July 6, 2023 by programmerdelphi2k Share this post Link to post
KodeZwerg 54 Posted July 6, 2023 This method is not made to be run more than once. Thats why I wrote, at startup to choose what to do after check. Share this post Link to post
KodeZwerg 54 Posted July 6, 2023 function GetParentProcessID(const AProcessID: DWORD): DWORD; var aProcesses: array[0..1023] of DWORD; cbNeeded, cProcesses, i: DWORD; hProcess: THandle; parentProcessID: DWORD; begin Result := 0; // Get the list of process identifiers if not EnumProcesses(@aProcesses[0], SizeOf(aProcesses), cbNeeded) then Exit; // Calculate how many process identifiers were returned cProcesses := cbNeeded div SizeOf(DWORD); // Find the process with the given process ID and get its parent process ID for i := 0 to cProcesses - 1 do begin if aProcesses[i] = AProcessID then begin hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, AProcessID); if hProcess <> 0 then begin try if GetProcessId(hProcess) <> 0 then parentProcessID := GetProcessId(hProcess); finally CloseHandle(hProcess); end; end; Break; end; end; Result := parentProcessID; end; Heres a faster way to get Parent ProcessID. 1 Share this post Link to post
programmerdelphi2k 237 Posted July 6, 2023 @KodeZwerg your codes dont answer the OP... unfortunatelly I dont have time to time-competition, for now! perhaps another day Share this post Link to post
Remy Lebeau 1345 Posted July 8, 2023 (edited) On 7/6/2023 at 1:08 PM, KodeZwerg said: Heres a faster way to get Parent ProcessID. That code can't possibly work as you propose. For one thing, you can't determine parent/child relationships using EnumProcesses(). It only gives you a flat array of all the running process IDs at that moment, there is no guarantee as to their order in the array. Second, you are simply looking for the caller's passed-in PID in the array, and when found then you are opening a HANDLE to that process and querying its PID - the same PID that you already have! GetProcessId() does not give you a parent PID. The only APIs I am aware of that can provide you with a parent PID are: CreateToolhelp32Snapshot() + Process32(First|Next)() NtQueryInformationProcess() So, for example, using Toolhelp32: function GetParentProcessID(const AProcessID: DWORD): DWORD; var hSnapshot: THandle; pe: PROCESSENTRY32; begin Result := 0; hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if hSnapshot <> INVALID_HANDLE_VALUE then try pe.dwSize := SizeOf(dwSize); if Process32First(hSnapshot, pe) then repeat if pe.th32ProcessID = AProcessID then begin Result := pe.th32ParentProcessID; Exit; end; until not Process32Next(hSnapshot, pe); finally CloseHandle(hSnapshot); end; end; Or, using NTDLL: function GetParentProcessID(const AProcessID: DWORD): DWORD; var hProcess: THandle; pbi: PROCESS_BASIC_INFORMATION; begin Result := 0; hProcess := OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, AProcessID); if hProcess <> 0 then try if NtQueryInformationProcess(hProcess, ProcessBasicInformation, @pbi, SizeOf(pbi), nil) = STATUS_SUCCESS then Result := DWORD(pbi.InheritedFromUniqueProcessId); finally CloseHandle(hProcess); end; end; Edited July 8, 2023 by Remy Lebeau 1 Share this post Link to post
KodeZwerg 54 Posted July 9, 2023 Hey @Remy Lebeau, thank you so much for clarification, very appreciated the second code! Share this post Link to post
Fr0sT.Brutal 899 Posted July 10, 2023 (edited) Just remember that process IDs are not monotonic and could be reused if a process closes and another one starts. @robertjohns are you trying to protect from launch under a debugger? Edited July 10, 2023 by Fr0sT.Brutal Share this post Link to post
KodeZwerg 54 Posted July 12, 2023 On 7/8/2023 at 3:29 AM, Remy Lebeau said: So, for example, using Toolhelp32: function GetParentProcessID(const AProcessID: DWORD): DWORD; var hSnapshot: THandle; pe: PROCESSENTRY32; begin Result := 0; hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if hSnapshot <> INVALID_HANDLE_VALUE then try pe.dwSize := SizeOf(dwSize); if Process32First(hSnapshot, pe) then repeat if pe.th32ProcessID = AProcessID then begin Result := pe.th32ParentProcessID; Exit; end; until not Process32Next(hSnapshot, pe); finally CloseHandle(hSnapshot); end; end; A mini small correction; pe.dwSize := SizeOf(pe); 1 Share this post Link to post