在VBScript中查找我自己的进程ID

时间:2011-11-28 12:49:10

标签: vbscript pid

我正在使用以下代码段来确定我的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:未找到”。

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

有人可以推荐一种更好的方法吗?

10 个答案:

答案 0 :(得分:13)

mshta 会立即终止。也许通过使用WMI服务来实现父进程id为时已晚 所以,我会使用类似的东西来消除并发脚本进程。

  1. 生成随机内容。
  2. 确定可以安装在每个系统上的应用程序,永远不会自行终止(例如带/ k参数的命令提示符)。
  3. 使用生成的随机参数(WshShell.Run)以隐藏模式启动应用程序。
  4. 等几毫秒
  5. 使用命令行参数值查询正在运行的进程。
  6. 获取ParentProcessId属性。
  7. 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
    

答案 1 :(得分:6)

这是一个更好的代码片段:

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

答案 2 :(得分:4)

我喜欢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倍(!),还不错:)。

答案 3 :(得分:1)

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

“代码无法确定哪个进程ID属于哪个脚本”: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,但一切都必须正常。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

要检索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

答案 6 :(得分:1)

您可以使用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

代码取自here

也基于这种方法there is父进程名称检测。

答案 7 :(得分:0)

这不是我的答案,我在一些谷歌小组讨论论坛中发现了这一点......看看它是否对你有帮助。

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

答案 8 :(得分:0)

获取当前的processID

In [31]: alphabets # List of alphabets in the string
Out[31]: ['A', 'G', 'A']

In [32]: digits  # List of numbers in the string ( Including numbers more than one digit)
Out[32]: ['3', '3']

In [33]: list_of_strings = [alphabets[i]*int(digits[i]) for i in range(0, len(alphabets)-1)]  # List of strings after repetition

In [34]: list_of_strings
Out[34]: ['AAA', 'GGG']

In [35]: joined_string = "".join(list_of_strings) # Joined list of strings

In [36]: joined_string
Out[36]: 'AAAGGG'

In [38]: final_output = joined_string + input_string[-1] # Append last character of the string

In [39]: final_output
Out[39]: 'AAAGGGA'

答案 9 :(得分:0)

Powershell可用于检索调用的VBScript进程ID。这种方法利用了exit命令的可选参数,该参数指定程序的退出代码。而且,如果将WShell.Run方法的可选第3个参数设置为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)