Find PID From Process Started In Shell.

I need to find the PID of a program that I am starting via the shell. The Shell is mode 1 and I am using a thread to control sending the commands and receiving the data back from the shell. The program I need to close is plink. The problem is I already have one instance of plink running that I need to keep open so I can’t just do this: Shell.Execute("taskkill /F /IM plink*") otherwise it would close both instances.

I found that I could rename the plink file itself to something else and close that instead and it works but I really don’t want to have to include two versions of the same file with my program. Or rename the file that’s probably some kind of no no licensing issue anyway.

This is what I would like to do (this is within a thread):

  
  dim xShell As New Shell
  
  xShell.Mode = 1
  
  xShell.Execute(strConnect) //Starts the file instance that I need to close later
  
  me.Sleep(10000) //Gives time to connect

  xShell.Write(strCommand) //Executes a command I need
  
  me.Sleep(5000)  //Give time for result back
  
  strResult = xShell.Result

  //FIND THE PID of the process started with xShell.Execute(strConnect)  

  xShell.Execute("taskkill /F /PID <pid>") //Close the process
  
  xShell.Close

If there isn’t a way to do this is there a way to assign a different name to the process when you open it? Thanks for any help.

One way that was thrown about in the past was to grab a process list immediately before starting the shell and then again immediately after and comparing them. The new plink process is the one you want to kill later.

@Tim Hare That’s a really good idea. Thank you. :slight_smile:

Try “WMIC process call create” which you can get the PID back from

I dont find WMIC well documented

https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx

Thank you both! Looks like I have some research and testing to do! Much appreciated! :slight_smile:

The issue with “read the process list, start your shell, read it again” is there is the very real possibility you end up with a race condition where several start “all at once” and you get several pids for “the one you just started”

wmic should be able to avoid this and get JUST the pid for the process started

its just not really clear to use :slight_smile:
Lots of boards have questions about using it to start a process, set the working directory when you do this etc

Yeah that occurred to me as well. Thanks I’m sure using wmic won’t be easy but at least I know what I’m looking for now.

I think I’m missing something.
Doesn’t xShell.PID return the PID?
(and about the sleeps, why not use the DataAvailable event and follow up there?)

[quote=272558:@Marco Hof]I think I’m missing something.
Doesn’t xShell.PID return the PID?
(and about the sleeps, why not use the DataAvailable event and follow up there?)[/quote]
I’m not looking for the PID of the shell I’m looking for the PID of a program started in the shell.
I probably will use the DataAvailable event in the final code this is just code I wrote for testing. :wink:

Well, not sure if it helps but (at least in OSX and Linux) if you kill a parent, the childs also go away.
But I understand now. I didn’t see at first that your shell is starting other shells. :slight_smile:

A couple of methods I’ve used (but you still have to parse the stuff). Both have filtering options you can use. I think there was a WFS method or Win32 declare that gets the processID of the launched process, but can’t seem to find it. There are a couple of Win32 gurus around so I hope you get more answers.

wmic process where name='plink.exe' get processid
tasklist /fi "imagename eq plink.exe"

This one opens PLINK and returns the processID. But you still have to parse for it.

wmic process call create "plink.exe"

:EDIT:
getting closer:

wmic process call create "plink.exe" | find "ProcessId"

@Norman Palardy
I finally worked out how to get wmic to work with the plink params but now I have a new problem.

[code]dim strConnect As String = "WMIC PROCESS CALL CREATE " + chr(34) + “C:\aaa\plink\plink.exe -ssh -pw myPass myUser@myDomain.com -P 22” + chr(34)

dim Shell As New xShell

Shell.Mode = 2

Shell.Execute(strConnect)[/code]

This works, my app receives the PID from the shell BUT it launches a second command window outside of my app with the plink session so I can’t send any commands via plink. Any ideas on how to get around this? Thanks!

So I finally had to give up on WMIC PROCESS CALL CREATE because even though it works to provide me the PID of the plink instance as it’s opened it opens plink in a new command window outside of my application when the WMIC command is used from a shell in my application. I’m sure there is probably some clever way using a good amount of VB code to use the PID of the shell and send commands to it outside of the application but using the plink session outside of the shell in my application is just not a solution for me. What a shame.

I ended up using a different WMIC command and writing a parsing method that gives me all of the PIDs of every plink instance. However the last PID is always the last one that was opened so if I call this method right after I create a new plink instance I will always have the PID of the plink instance I need.

So @Tim Hare I more or less used your solution. Just one without a need to compare lists.

Here’s my method, I’m sure there is a better way to parse this out, but it’s what I came up with. Feel free to improve on it if you like, I’m always open to improvements.

Method getPID returns the last PID of the last plink instance opened.

  dim s as string
  dim a as string
  dim r as string
  
  dim arr() as string
  dim arr2() as string
  
  dim i as integer
  dim x as integer
  dim z as integer
  
  dim strConnect As String = "WMIC PROCESS WHERE " + chr(34) + "NAME like '%%plink.exe%%'" + chr(34) + " GET PROCESSID"
  
  dim pShell As New Shell
  pShell.Mode = 0
  pShell.Execute(strConnect)
  s = pShell.Result
  
  s = s.Replace("ProcessId"," ") 
  
  for i = 1 to s.len
    a = Mid(s, i, 1) 
    if a = " " then
      a = "_"
    end
    r = r + a
  next i
  
  arr = Split(r, "_")
  
  for i = 0 to arr.ubound
    x = cDbl(arr(i))
    if x <> 0 then
      arr2.Append(arr(i))
    end
  next
  
  ' Get the most recent plink PID
  z = arr2.Ubound
  
  Return arr2(z)

I also found another unusual idea I thought I would share. If you are using the same process multiple times like this and need to close a specific one if the process takes parameters and they are different for each instance you could also use this command to close a specific process. Too broad for me but someone else might find it useful.

wmic Path win32_process Where "CommandLine Like '%%-ssh -pw%%'" Call Terminate

Hello, a little late but you can use also tasklist to do that, the result is easier to parse and the command is most commun :

[code] dim s as string

dim arr() as string
dim arr2() as string
dim arr3() as String

dim i as integer
dim z as integer

dim strConnect As String = "tasklist /fo csv | findstr /i " + chr(34) + "plink" + chr(34)

dim pShell As New Shell
pShell.Mode = 0
pShell.Execute(strConnect)
s = pShell.Result

s = s.Replaceall(chr(34),"")
arr = Split(s,EndOfLine)

for i = 0 to arr.ubound
  arr2 = Split(arr(i),",")
  arr3.Append arr2(1)
next
z = arr3.ubound

' Get the most recent plink PID
return arr3(z)

[/code]

That is what I do for myself.

Thanks for good info

Added a small check in case tasklist returns an empty line.

[code] dim s as string

dim arr() as string
dim arr2() as string
dim arr3() as String

dim i as integer
dim z as integer

dim strConnect As String = "tasklist /fo csv | findstr /i " + chr(34) + "plink" + chr(34)

dim pShell As New Shell
pShell.Mode = 0
pShell.Execute(strConnect)
s = pShell.Result

s = s.Replaceall(chr(34),"")
arr = Split(s,EndOfLine)

for i = 0 to arr.ubound
  arr2 = Split(arr(i),",")

if arr2.ubound<1 then continue //Added

  arr3.Append arr2(1)
next
z = arr3.ubound

' Get the most recent plink PID
return arr3(z)[/code]