web-dev-qa-db-ja.com

VBScriptで自分のプロセスIDを探す

次のコードスニペットを使用して、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

私のWindows7(32ビット)マシンでは、これは約90%の時間で機能し、iMyPIDには現在実行中のスクリプトのプロセスIDが含まれています。ただし、10%の確率でHandle_Errorはエラーメッセージ「SWbemServicesEX:Not found "」で呼び出されます。

最近、Windows 7(64ビット)を実行している他の誰かがHandle_Errorは常にエラーメッセージ「メモリ不足 "」で呼び出されます。これは、自分のプロセスIDを見つけるためだけの非常識なエラーメッセージのようです。

誰かがこれを行うためのより良い方法をお勧めできますか?

9
Richard

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
13
Kul-Tigin

さらに優れたコードスニペットは次のとおりです。

      ' ***********************************************************************************************************
      ' 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
6
Asok Smith

私は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

私はちょうど私の問題を部分的に解決したこのスレッドを見つけました。皆さん、ありがとうございました。

「コードは、どのプロセス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を取得できるよりも速くスクリプトを起動した場合を除いて、すべてが正常である必要があります。

1
nobur

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
1
user6163900

これはより良いものですが、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
Toughy

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)
0
Chris D

現在のプロセス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
0
Shubham Verma

これは私の答えではありません、私はいくつかのグーグルグループディスカッションフォーラムでこれを見つけました...それがあなたを助けるかどうか見てください。

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グループフォーラムの元のディスカッションスレッド

0
Buddha