Find PID From Process Started In Shell.

  1. 3 years ago

    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.

    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 :)
    Lots of boards have questions about using it to start a process, set the working directory when you do this etc

  2. Tim H

    16 Jun 2016 Pre-Release Testers Portland, OR USA

    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.

  3. @Tim H That's a really good idea. Thank you. :)

  4. Norman P

    16 Jun 2016 Xojo Inc, Pre-Release Testers, Xojo Pro Seeking work. npalardy@great-w...

    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

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

  6. Norman P

    16 Jun 2016 Xojo Inc, Pre-Release Testers, Xojo Pro Answer Seeking work. npalardy@great-w...

    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 :)
    Lots of boards have questions about using it to start a process, set the working directory when you do this etc

  7. 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.

  8. Marco H

    16 Jun 2016 Pre-Release Testers Cali, Colombia

    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?)

  9. @Marco H 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?)

    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. ;)

  10. Marco H

    16 Jun 2016 Pre-Release Testers Cali, Colombia

    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. :)

  11. 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"
  12. Edited 3 years ago

    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"
  13. Edited 3 years ago

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

    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)

    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!

  14. Edited 3 years ago

    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 H 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
  15. michel b

    17 Sep 2016 Pre-Release Testers France
    Edited 3 years ago

    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 :

        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)
  16. Michel B

    17 Sep 2016 Pre-Release Testers, Xojo Pro RubberViews.com

    That is what I do for myself.

  17. 3 months ago

    Dan B

    Jul 10 Pre-Release Testers, Xojo Pro

    Thanks for good info

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

     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)

or Sign Up to reply!