Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get process id by process name in windows environment?

Tags:

windows

go

I want to get the process id by the process name in windows environment?

I find golang only has the api os.FindProcess(id),but no by name.

like image 240
Dev Zhou Avatar asked Mar 31 '16 12:03

Dev Zhou


4 Answers

I had to struggle with this too, and found the way to the solution not very straightforward, because… WinApi :)

In the end you have to create a snapshot of the current windows process list using CreateToolhelp32Snapshot. Then you get the first process in the snapshot with Process32First. After that keep iterating over the list with Process32Next, until you get the ERROR_NO_MORE_FILES error. Only then you have the whole process list.

See how2readwindowsprocesses for a working example.

Here is the gist:

const TH32CS_SNAPPROCESS = 0x00000002

type WindowsProcess struct {
    ProcessID       int
    ParentProcessID int
    Exe             string
}

func processes() ([]WindowsProcess, error) {
    handle, err := windows.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
    if err != nil {
        return nil, err
    }
    defer windows.CloseHandle(handle)

    var entry windows.ProcessEntry32
    entry.Size = uint32(unsafe.Sizeof(entry))
    // get the first process
    err = windows.Process32First(handle, &entry)
    if err != nil {
        return nil, err
    }

    results := make([]WindowsProcess, 0, 50)
    for {
        results = append(results, newWindowsProcess(&entry))

        err = windows.Process32Next(handle, &entry)
        if err != nil {
            // windows sends ERROR_NO_MORE_FILES on last process
            if err == syscall.ERROR_NO_MORE_FILES {
                return results, nil
            }
            return nil, err
        }
    }
}

func findProcessByName(processes []WindowsProcess, name string) *WindowsProcess {
    for _, p := range processes {
        if strings.ToLower(p.Exe) == strings.ToLower(name) {
            return &p
        }
    }
    return nil
}

func newWindowsProcess(e *windows.ProcessEntry32) WindowsProcess {
    // Find when the string ends for decoding
    end := 0
    for {
        if e.ExeFile[end] == 0 {
            break
        }
        end++
    }

    return WindowsProcess{
        ProcessID:       int(e.ProcessID),
        ParentProcessID: int(e.ParentProcessID),
        Exe:             syscall.UTF16ToString(e.ExeFile[:end]),
    }
}
like image 98
Denis Brodbeck Avatar answered Sep 22 '22 08:09

Denis Brodbeck


You can list all the processes and match them with the name you want to find, by using the updated sys call package, https://godoc.org/golang.org/x/sys, it has most of the windows api.

func Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error)
func Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error)

also see the msdn docs: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684834(v=vs.85).aspx

like image 25
fluter Avatar answered Sep 19 '22 08:09

fluter


const TH32CS_SNAPPROCESS = 0x00000002

type WindowsProcess struct {
    ProcessID       int
    ParentProcessID int
    Exe             string
}

func newWindowsProcess(e *syscall.ProcessEntry32) WindowsProcess {
    // Find when the string ends for decoding
    end := 0
    for {
        if e.ExeFile[end] == 0 {
            break
        }
        end++
    }

    return WindowsProcess{
        ProcessID:       int(e.ProcessID),
        ParentProcessID: int(e.ParentProcessID),
        Exe:             syscall.UTF16ToString(e.ExeFile[:end]),
    }
}

func processes() ([]WindowsProcess, error) {
    handle, err := syscall.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
    if err != nil {
        return nil, err
    }
    defer syscall.CloseHandle(handle)

    var entry syscall.ProcessEntry32
    entry.Size = uint32(unsafe.Sizeof(entry))
    // get the first process
    err = syscall.Process32First(handle, &entry)
    if err != nil {
        return nil, err
    }

    results := make([]WindowsProcess, 0, 50)
    for {
        results = append(results, newWindowsProcess(&entry))

        err = syscall.Process32Next(handle, &entry)
        if err != nil {
            // windows sends ERROR_NO_MORE_FILES on last process
            if err == syscall.ERROR_NO_MORE_FILES {
                return results, nil
            }
            return nil, err
        }
    }
}

func findProcessByName(processes []WindowsProcess, name string) *WindowsProcess {
    for _, p := range processes {
        if bytes.Contains([]byte(strings.ToUpper(p.Exe)),     []byte(strings.ToUpper(name))) {
            return &p
        }
    }
    return nil
}
like image 21
Jin William Avatar answered Sep 21 '22 08:09

Jin William


This seems to do it:

package main

import (
   "fmt"
   "golang.org/x/sys/windows"
)

// unsafe.Sizeof(windows.ProcessEntry32{})
const processEntrySize = 568

func processID(name string) (uint32, error) {
   h, e := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
   if e != nil { return 0, e }
   p := windows.ProcessEntry32{Size: processEntrySize}
   for {
      e := windows.Process32Next(h, &p)
      if e != nil { return 0, e }
      if windows.UTF16ToString(p.ExeFile[:]) == name {
         return p.ProcessID, nil
      }
   }
   return 0, fmt.Errorf("%q not found", name)
}

func main() {
   n, e := processID("WindowsTerminal.exe")
   if e != nil {
      panic(e)
   }
   println(n)
}

https://pkg.go.dev/golang.org/x/sys/windows#CreateToolhelp32Snapshot

like image 40
Zombo Avatar answered Sep 23 '22 08:09

Zombo