Find my own process ID in VBScript
Solution 1
mshta terminates itself immediately. Maybe it's too late to achieve parent process id by using WMI service.
So, I'd use something like this to eliminate concurrent script processes.
- Generate random things.
- Determine an application which could be installed on each system, never terminates by itself (e.g. command prompt with /k parameter).
- Start the application in hidden mode with generated random argument (WshShell.Run).
- Wait a few milliseconds
- Query the running processes by using command line argument value.
- Get the ParentProcessId property.
Function CurrProcessId
Dim oShell, sCmd, oWMI, oChldPrcs, oCols, lOut
lOut = 0
Set oShell = CreateObject("WScript.Shell")
Set oWMI = GetObject(_
"winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
sCmd = "/K " & Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
oShell.Run "%comspec% " & sCmd, 0
WScript.Sleep 100 'For healthier skin, get some sleep
Set oChldPrcs = oWMI.ExecQuery(_
"Select * From Win32_Process Where CommandLine Like '%" & sCmd & "'",,32)
For Each oCols In oChldPrcs
lOut = oCols.ParentProcessId 'get parent
oCols.Terminate 'process terminated
Exit For
Next
CurrProcessId = lOut
End Function
Dim ProcessId
ProcessId = CurrProcessId 'will remain valid indefinitely
WScript.Echo ProcessId
Solution 2
Here's an even better code snippet:
' ***********************************************************************************************************
' lng_MyProcessID finds and returns my own process ID. This is excruciatingly difficult in VBScript. The
' method used here forks "cmd /c pause" with .Exec, and then uses the returned .Exec object's .ProcessID
' attribute to feed into WMI to get that process's Win32_Process descriptor object, and then uses THAT
' WMI Win32_Process descriptor object's .ParentProcessId attribute, which will be OUR Process ID, and finally
' we terminate the waiting cmd process. Execing cmd is what causes the brief cmd window to flash at start up,
' and I can' figure out out how to hide that window.
' returns: My own Process ID as a long int; zero if we can't get it.
' ************************************************************************************************************
Function lng_MyProcessID ()
lng_MyProcessID = 0 ' Initially assume failure
If objWMIService Is Nothing Then Exit Function ' Should only happen if in Guest or other super-limited account
Set objChildProcess = objWshShell.Exec ( """%ComSpec%"" /C pause" ) ' Fork a child process that just waits until its killed
Set colPIDs= objWMIService.ExecQuery ( "Select * From Win32_Process Where ProcessId=" & objChildProcess.ProcessID,, 0 )
For Each objPID In colPIDs ' There's exactly 1 item, but .ItemIndex(0) doesn't work in XP
lng_MyProcessID = objPID.ParentProcessId ' Return child's parent Process ID, which is MY process ID!
Next
Call objChildProcess.Terminate() ' Terminate our temp child
End Function ' lng_MyProcessID
Solution 3
I like Kul-Tigin's idea (+1), and Asok Smith's idea (based on .Exec
) deserve respect (+1), and it w'd been even better if .Exec
run hidden process. So, to feed my curiosity, I also toyed with this and this's what I did.
ts1 = Timer : res1 = CurrProcessId : te1 = Timer - ts1
ts2 = Timer : res2 = ThisProcessId : te2 = Timer - ts2
WScript.Echo "CurrProcessId", res1, FormatNumber(te1, 6), _
vbCrLf & "ThisProcessId", res2, FormatNumber(te2, 6), _
vbCrLf & "CurrProcessId / ThisProcessId = " & te1 / te2
'> CurrProcessId 6946 0,437500
'> ThisProcessId 6946 0,015625
'> CurrProcessId / ThisProcessId = 28
Function ThisProcessId
ThisProcessId = 0
Dim sTFile, oPrc
With CreateObject("Scripting.FileSystemObject")
sTFile = .BuildPath(.GetSpecialFolder(2), "sleep.vbs")
With .OpenTextFile(sTFile, 2, True)
.Write "WScript.Sleep 1000"
End With
End With
With CreateObject("WScript.Shell").Exec("WScript " & sTFile)
For Each oPrc In GetObject("winmgmts:\\.\root\cimv2").ExecQuery(_
"Select * From Win32_Process Where ProcessId=" & .ProcessID)
Exit For : Next
ThisProcessId = oPrc.ParentProcessId
End With
End Function
28 times faster(!), not bad :)
Solution 4
You may use Sleep
from kernel32
instead of mshta
.
MsgBox GetProcId()
Function GetProcId()
With GetObject("winmgmts:\\.\root\CIMV2:Win32_Process.Handle='" & CreateObject("WScript.Shell").Exec("rundll32 kernel32,Sleep").ProcessId & "'")
GetProcId = .ParentProcessId
.Terminate
End With
End Function
Code taken from here.
Also there is parent process name detection based on this approach.
Solution 5
Here is a better one, but in JScript (sorry, you translate it to VB ...)
var WshShell = WScript.CreateObject("WScript.Shell");
var objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2");
var childProcess =
WshShell.Exec
(
'"' + WshShell.Environment('PROCESS')('ComSpec') + '"'
+
" /C Echo \"Text lines\" && Set /p VarName="
);
childProcess.StdOut.ReadLine();
var current_pid =
objWMIService.ExecQuery
(
"Select * From Win32_Process Where ProcessId=" + childProcess.ProcessID
);
current_pid = (new Enumerator(current_pid)).item().ParentProcessId;
if (current_pid)
{
childProcess.StdIn.WriteLine("value"); // child process should now exit
WScript.Echo("Current PID: " + current_pid);
}
else
{
WScript.StdErr.WriteLine("Get current PID from WMI failed.");
WScript.Quit(7);
}
Richard
Updated on October 15, 2020Comments
-
Richard over 3 years
I'm using the following code snippet to determine what process ID my vbscript is running as:
On Error Resume Next Dim iMyPID : iMyPID = GetObject("winmgmts:root\cimv2").Get("Win32_Process.Handle='" & CreateObject("WScript.Shell").Exec("mshta.exe").ProcessID & "'").ParentProcessId If Err.Number <> 0 Then Call Handle_Error(Err.Description) On Error Goto 0
On my Windows 7 (32-bit) machine this works about 90% of the time and
iMyPID
contains the process ID of the currently running script. However 10% of the timeHandle_Error
gets called with the error message "SWbemServicesEX: Not found".Recently someone else running Windows 7 (64-bit) reported that
Handle_Error
always gets called with the error message "Out of memory". This seems an insane error message just to find out your own process ID!Can anyone recommend a better way of doing this?
-
Richard over 12 yearsThanks for this. Unfortunately this code just takes a list of all the running processes and assumes that any process which contains the name of that script is that script. In other words if two scripts are running at the same time, the code is unable to determine which process ID belongs to which script.
-
Panayot Karabakalov about 11 yearsWhy you think this method is better?
Exec
popup visible window. -
Panayot Karabakalov about 11 yearsBecause you have a good idea (+1), I thought that my comments will encourage you to improve your answer, but I lost patience and published it as my own answer.
-
Harry Johnston about 10 yearsNote that this depends on hoping that the random number generated will be unique, which is risky if a number of scripts are being launched simultaneously.
-
Kul-Tigin about 10 years@HarryJohnston You're right! Now it's much more robust with a v4 UUID.
-
iRon almost 9 yearsNote that file names (sCmd) might include characters that have special meaning for the WMI like operator (e.g. %, _, ^, [ and ], see: msdn.microsoft.com/en-us/library/aa392263(v=vs.85).aspx), therefore it is saver to check the commandline within the For Each loop.
-
Kul-Tigin almost 9 years@iRon You're right about WQL's Like operator behaviours but
sCmd
isn't a file name here and it cannot contain that special characters. It's just something like/K {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
so it's safe to use in a WMI Query with Like operator. -
Richard over 6 yearsUnfortunately this code just takes a list of all the running processes and assumes that any process which contains the name of that script is that script. In other words if two scripts are running at the same time, the code is unable to determine which process ID belongs to which script.
-
Richard over 3 yearsI've only been able to briefly test this but it looks to be the shortest and cleanest solution.