Найти мой собственный идентификатор процесса в 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.
Итак, я бы использовал что-то вроде этого, чтобы исключить параллельные процессы сценария.

  1. Генерация случайных вещей.
  2. Определите приложение, которое может быть установлено в каждой системе, никогда не завершается само по себе (например, командная строка с параметром /k).
  3. Запустите приложение в скрытом режиме с сгенерированным случайным аргументом ( WshShell.Run).
  4. Подождите несколько миллисекунд
  5. Запросите запущенные процессы, используя значение аргумента командной строки.
  6. Получите свойство 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

Оригинальная тема обсуждения на форуме групп Google

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()
Другие вопросы по тегам