【问题标题】:Find my own process ID in VBScript在 VBScript 中查找我自己的进程 ID
【发布时间】:2011-11-28 12:49:09
【问题描述】:

我正在使用以下代码 sn-p 来确定我的 vbscript 运行的进程 ID:

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 包含当前正在运行的脚本的进程 ID。但是,有 10% 的时间会调用 Handle_Error 并显示错误消息“SWbemServicesEX: Not found”。

最近运行 Windows 7(64 位)的其他人报告说,Handle_Error 总是被调用并显示错误消息“内存不足”。这似乎是一个疯狂的错误消息,只是为了找出您自己的进程 ID!

谁能推荐一个更好的方法?

【问题讨论】:

    标签: vbscript pid


    【解决方案1】:

    mshta 会立即自行终止。使用 WMI 服务获取父进程 id 可能为时已晚。
    所以,我会使用这样的东西来消除并发脚本进程。

    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
    

    【讨论】:

    • 请注意,这取决于希望生成的随机数是唯一的,如果同时启动多个脚本,这是有风险的。
    • @HarryJohnston 你是对的!现在它使用 v4 UUID 更加强大。
    • 请注意,文件名 (sCmd) 可能包含对 WMI 类似运算符具有特殊含义的字符(例如 %、_、^、[ 和 ],请参阅:msdn.microsoft.com/en-us/library/aa392263(v=vs.85).aspx),因此它更节省检查 For Each 循环中的命令行。
    • @iRon 您对 WQL 的 Like 运算符行为是正确的,但 sCmd 不是这里的文件名,它不能包含特殊字符。它就像/K {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}这样的东西,所以在WMI查询中使用Like操作符是安全的。
    【解决方案2】:

    这里有一个更好的代码 sn-p:

          ' ***********************************************************************************************************
          ' 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
    

    【讨论】:

    • 为什么你认为这种方法更好? Exec 弹出可见窗口。
    • 因为你有一个好主意(+1),我以为我的cmets会鼓励你改进你的答案,但我失去了耐心,把它作为我自己的答案发布了。
    【解决方案3】:

    我喜欢 Kul-Tigin 的想法 (+1),Asok Smith 的想法(基于 .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 倍(!),还不错 :)

    【讨论】:

      【解决方案4】:

      您可以使用kernel32 中的Sleep 而不是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
      

      代码取自here

      还有there is父进程名检测基于此方法。

      【讨论】:

      • 我只能简单地测试一下,但它看起来是最短和最干净的解决方案。
      【解决方案5】:

      这里有一个更好的,但是在 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);
      }
      

      【讨论】:

        【解决方案6】:

        我刚刚发现这个帖子部分解决了我的问题。 谢谢大家。

        “代码无法确定哪个进程ID属于哪个脚本”:是的,但由于这是您的脚本必须完成的第一个任务,您可以保留生命周期最短的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 的速度,否则一切都必须正常。

        【讨论】:

          【解决方案7】:

          要检索 VB 脚本自己的进程 ID,您可以依赖 Process 对象的属性 CreationDate。

          在启动 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
          

          【讨论】:

            【解决方案8】:

            Powershell 可用于检索调用的 VBScript 进程 ID。这种方法利用了exit 命令的可选参数,它指定了程序的退出代码。并且,如果WShell.Run方法的第三个可选参数设置为True,那么它会在powershell关闭后返回退出码(即VBScript进程ID)。

            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)
            

            【讨论】:

              【解决方案9】:

              这不是我的答案,我在一些谷歌群组讨论论坛中找到了这个...看看它是否对你有帮助。

              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
              

              Original Discussion Thread in Google Groups forum

              【讨论】:

              • 谢谢。不幸的是,这段代码只列出了所有正在运行的进程,并假定包含该脚本名称的任何进程都是该脚本。也就是说如果两个脚本同时运行,代码无法判断哪个进程ID属于哪个脚本。
              【解决方案10】:

              获取当前进程ID

              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
              

              【讨论】:

              • 不幸的是,这段代码只列出了所有正在运行的进程,并假定包含该脚本名称的任何进程都是该脚本。也就是说如果两个脚本同时运行,代码无法判断哪个进程ID属于哪个脚本。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-09-07
              • 1970-01-01
              • 2012-08-05
              • 1970-01-01
              相关资源
              最近更新 更多