Найти мой собственный идентификатор процесса в VBScript
Я использую следующий фрагмент кода, чтобы определить, под каким идентификатором процесса работает мой vbscript:
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
На моей Windows 7 (32-битной) машине это работает около 90% времени и iMyPID
содержит идентификатор процесса текущего запущенного скрипта. Однако 10% времени Handle_Error
вызывается с сообщением об ошибке " SWbemServicesEX: не найден ".
Недавно кто-то еще под управлением Windows 7 (64-разрядная версия) сообщил, что Handle_Error
всегда вызывается с сообщением об ошибке " Недостаточно памяти ". Это кажется безумным сообщением об ошибке просто, чтобы узнать свой собственный идентификатор процесса!
Кто-нибудь может порекомендовать лучший способ сделать это?
11 ответов
МШТА прекращает себя немедленно. Возможно, уже слишком поздно достигать идентификатора родительского процесса с помощью сервиса WMI.
Итак, я бы использовал что-то вроде этого, чтобы исключить параллельные процессы сценария.
- Генерация случайных вещей.
- Определите приложение, которое может быть установлено в каждой системе, никогда не завершается само по себе (например, командная строка с параметром /k).
- Запустите приложение в скрытом режиме с сгенерированным случайным аргументом ( WshShell.Run).
- Подождите несколько миллисекунд
- Запросите запущенные процессы, используя значение аргумента командной строки.
- Получите свойство ParentProcessId.
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
Вот еще лучший фрагмент кода:
' ***********************************************************************************************************
' 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
Мне нравится идея Кул-Тигина (+1) и идея Асока Смита (основанная на .Exec
) заслуживают уважения (+1), и было бы еще лучше, если бы .Exec
запустить скрытый процесс. Итак, чтобы прокормить свое любопытство, я тоже поиграл с этим, и это то, что я сделал.
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 раз быстрее (!), Неплохо:)
Вы можете использовать
Sleep
из
kernel32
вместо
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
Код взят отсюда.
Также на основе этого подхода есть определение имени родительского процесса.
Вот лучше, но в JScript (извините, вы переводите его на 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);
}
Это не мой ответ, я нашел это на некоторых форумах групп Google... Посмотрите, поможет ли это вам.
Set objSWbemServices = GetObject ("WinMgmts:Root\Cimv2")
Set colProcess = objSWbemServices.ExecQuery ("Select * From Win32_Process")
For Each objProcess In colProcess
If InStr (objProcess.CommandLine, WScript.ScriptName) <> 0 Then
WScript.Echo objProcess.Name, objProcess.ProcessId, objProcess.CommandLine
End If
Next
Powershell можно использовать для получения идентификатора вызывающего процесса VBScript. Этот подход использует необязательный аргумент exit
команда, которая указывает код завершения программы. И, если необязательный третий аргумент WShell.Run
метод установлен в True
затем он вернет код выхода (который является идентификатором процесса VBScript) после закрытия powershell.
Dim sCmd
Dim WShell
sCmd = _
"powershell -command exit " & _
"(gwmi Win32_Process -Filter " & _
"\""processid='$PID'\"").parentprocessid"
Set WShell = CreateObject("WScript.Shell")
MsgBox WShell.Run(sCmd, 0, True)
Я только нашел эту ветку, которая частично решила мою проблему. Спасибо вам всем.
"код не может определить, какой идентификатор процесса принадлежит какому сценарию": true, но поскольку это первая задача, которую должен выполнить ваш сценарий, вы можете сохранить Pid с самым коротким временем жизни.
Set com = CreateObject("Wscript.Shell")
Set objSWbemServices = GetObject ("WinMgmts:Root\Cimv2")
Set colProcess = objSWbemServices.ExecQuery ("Select * From Win32_Process")
dim toto, thisPid
thisPid=""
toto=200 ' just a high value like 200sec
For Each objProcess In colProcess
If InStr (objProcess.CommandLine, WScript.ScriptName) <> 0 Then
Ptime=((Cdbl(objProcess.UserModeTime)+Cdbl(objProcess.KernelModeTime))/10000000)
if toto > Ptime then
toto = Ptime
thisPid = objProcess.ProcessId
End If
End If
Next
If thisPid="" then
WScript.Echo "unable to get the PID"
Else
WScript.Echo "PID of this script : "&thisPid
End If
За исключением случаев, когда вы запускаете сценарии быстрее, чем каждый из них может получить свой Pid, все должно быть в порядке.
Чтобы получить собственный идентификатор процесса сценария VB, вы можете использовать свойство CreationDate объекта Process.
В момент запуска сценария VB процесс, выполняющий сценарий, будет иметь самую последнюю CreationDate из всех процессов, выполняющих один и тот же сценарий.
Фактически, он будет иметь самую высокую CreationDate из всех запущенных процессов.
Итак, чтобы получить PID, первое, что нужно сделать, - это поиск процесса с самой высокой CreationDate.
'Searching for processes
Dim strScriptName
Dim WMI, wql
Dim objProcess
'
'My process
Dim datHighest
Dim lngMyProcessId
'Which script to look for ?
strScriptName = "WScript.exe"
'strScriptName = "Notepad.exe"
'Iniitialise
datHighest = Cdbl(0)
Set WMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
wql = "SELECT * FROM Win32_Process WHERE Name = '" & strScriptName & "'"
'
For Each objProcess In WMI.ExecQuery(wql)
'The next If is not necessary, it only restricts the search to all processes on the current VB Script
'If Instr(objProcess.CommandLine, WScript.ScriptName) <> 0 Then
If objProcess.CreationDate > datHighest Then
'Take the process with the highest CreationDate so far
' e.g. 20160406121130.510941+120 i.e. 2016-04-06 12h11m:30s and fraction
datHighest = objProcess.CreationDate
lngMyProcessId = objProcess.ProcessId
End If
'End If
Next
'Show The result
WScript.Echo "My process Id = " & lngMyProcessId
Получить текущий идентификатор процесса
Set WshShell = CreateObject("WScript.Shell")
currentProgram=wscript.ScriptName
Const strComputer = "."
Dim objWMIService, colProcessList
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
query="SELECT * FROM Win32_Process WHERE Name = 'wscript.exe' "
Set colProcessList = objWMIService.ExecQuery(query)
For Each objProcess in colProcessList
If (InStr (objProcess.commandLine,wscript.ScriptName) <> 0 )Then
processDetails="Current ProcessId : "& objProcess.ProcessId & " \n, And Process Name:" & objProcess.name &"\n CommandLine is :"& objProcess.CommandLine
message = msgbox(processDetails,16,"Details")
End If
Я использовал это, чтобы получить собственный идентификатор процесса сценариев.
Function GetPid()
GetPid=GetObject("winmgmts:\\.\root\CIMV2").ExecQuery("Select * From Win32_Process Where CommandLine Like '%" &Wscript.ScriptName& "%'").ItemIndex(0).ProcessId
End Function
Wscript.Echo GetPid()