Найти мой собственный идентификатор процесса в VBScript - PullRequest
9 голосов
/ 28 ноября 2011

Я использую следующий фрагмент кода, чтобы определить, под каким идентификатором процесса работает мой 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всегда вызывается с сообщением об ошибке " Out of memory ".Это кажется безумным сообщением об ошибке, просто чтобы узнать свой собственный идентификатор процесса!

Кто-нибудь может порекомендовать лучший способ сделать это?

Ответы [ 9 ]

13 голосов
/ 09 декабря 2011

мшта завершается немедленно.Возможно, уже слишком поздно для достижения идентификатора родительского процесса с помощью службы 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
6 голосов
/ 15 марта 2013

Вот еще лучший фрагмент кода:

      ' ***********************************************************************************************************
      ' 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
4 голосов
/ 16 марта 2013

Мне нравится идея Кул-Тигина (+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 раз быстрее (!), Неплохо:)

1 голос
/ 06 апреля 2016

Чтобы получить собственный идентификатор процесса сценария 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
1 голос
/ 03 ноября 2012

Вот лучше, но в 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);
}
1 голос
/ 01 июня 2012

Я только что нашел эту ветку, которая частично решила мою проблему.Спасибо всем.

"код не может определить, какой идентификатор процесса принадлежит какому скрипту": 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, ​​все должно быть в порядке.

0 голосов
/ 22 октября 2018

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)
0 голосов
/ 12 ноября 2017

Получить текущий идентификатор процесса

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
0 голосов
/ 06 декабря 2011

Это не мой ответ, я нашел это на некоторых форумах групп 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

...