Tecnologer
Tecnologer

Reputation: 498

How can I get handle of Windows process in Go?

I'm trying to call function RegisterDeviceNotificationW of user32.dll. But the first param of the function is the "handle to the window or service that will receive device events" (this I getted from Microsoft).

Based on Cloud Printer Connector I tried to get the handler with svc.StatusHandler(), but does not work for me, every time I run I get the follow error:

The handle is invalid.

Well, using the same code of the examples of sys I created my own "service", replacing the beep function by RegisterDeviceNotification (the same code of google) and sent the name of my service and the value returned from svc.StatusHandler(), but I received again the same message: "The handle is invalid."

Function RegisterDeviceNotification

var (
    u32                            = syscall.MustLoadDLL("user32.dll")
    registerDeviceNotificationProc = u32.MustFindProc("RegisterDeviceNotificationW")
)   

Here is where I need sent a valid Handler

func RegisterDeviceNotification(handle windows.Handle) error {

    var notificationFilter DevBroadcastDevinterface
    notificationFilter.dwSize = uint32(unsafe.Sizeof(notificationFilter))
    notificationFilter.dwDeviceType = DBT_DEVTYP_DEVICEINTERFACE
    notificationFilter.dwReserved = 0
    // BUG(pastarmovj): This class is ignored for now. Figure out what the right GUID is.
    notificationFilter.classGuid = PRINTERS_DEVICE_CLASS
    notificationFilter.szName = 0

    r1, _, err := registerDeviceNotificationProc.Call(uintptr(handle), uintptr(unsafe.Pointer(&notificationFilter)), DEVICE_NOTIFY_SERVICE_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES)
    if r1 == 0 {
        return err
    }
    return nil
}

Call the function

func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
    h := windows.Handle(uintptr(unsafe.Pointer(m)))
    err := RegisterDeviceNotification(h)

    fmt.Println(err)
}

NOTE: I tried too with the pointer of "myService":

h := windows.Handle(uintptr(unsafe.Pointer(m)))
err := RegisterDeviceNotification(h)

EDIT: I tried with GetCurrentProcess, but does not work:

Retrieves a pseudo handle for the current process.

h, err := syscall.GetCurrentProcess()
err = RegisterDeviceNotification(windows.Handle(h))

The question: How can I get a valid handle of the process in Go?

PS: If exists any problem with my question, please, let me know to improve.

Upvotes: 14

Views: 5763

Answers (4)

Gillespie
Gillespie

Reputation: 6561

Here's how you can get the handle of a process given the PID:

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

// https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
const PROCESS_ALL_ACCESS = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xffff

func GetWindowsHandle(pid int) windows.Handle {
    handle, err := windows.OpenProcess(PROCESS_ALL_ACCESS, false, uint32(pid))
    if err != nil {
        return err
    }

    return handle
}

I believe you also need to make sure to call windows.CloseHandle(handle) when you are done with it.

Full example:

package main

import (
    "log"
    "os/exec"

    "golang.org/x/sys/windows"
)

// https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
const PROCESS_ALL_ACCESS = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xffff

func SetPriorityWindows(pid int, priority uint32) error {
    handle, err := windows.OpenProcess(PROCESS_ALL_ACCESS, false, uint32(pid))
    if err != nil {
        return err
    }
    defer windows.CloseHandle(handle) // Technically this can fail, but we ignore it

    err = windows.SetPriorityClass(handle, priority)
    if err != nil {
        return err
    }

    return nil
}

func main() {
    cmd := exec.Command("python", "hello.py")
    err := cmd.Start()
    if err != nil {
        log.Fatal(err)
    }

    // Set priority to above normal
    err = SetPriorityWindows(cmd.Process.Pid, windows.ABOVE_NORMAL_PRIORITY_CLASS)
    if err != nil {
        log.Fatal(err)
    }

    err = cmd.Wait()
}

Upvotes: 1

TimeLoad
TimeLoad

Reputation: 371

Here's some code I wrote a while ago to handle this, it was working last time I checked. I've created wrapper functions for a bunch of winapi calls.

package main

import "syscall"

// import the required winapi DLLs and functions
var (
    dllKernel32 := syscall.NewLazyDLL("kernel32.dll")
    procOpenProcess = dllKernel32.NewProc("OpenProcess")
    procCloseHandle = dllKernel32.NewProc("CloseHandle")
)

// when passing this to the OpenProcess function it will get
// a handle with the PROCESS_ALL_ACCESS permissions
const PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | uintptr(0xFFFF)

func main() {
    pid := uint32(123456)
    inheritHandle := uint32(1)

    handle, err := OpenProcess(PROCESS_ALL_ACCESS, inheritHandle, pid)
   
    if err != nil {
        panic(err)
    }
    
    ... // whatever your code wants to do

    err = CloseHandle(handle)

    if err != nil {
        panic(err)
    }

}

// Wraps the winapi OpenProcess function
func OpenProcess(desiredAccess uintptr, inheritHandle uint32, pid uint32) (uintptr, error) {
    // call the winapi function to get a handle
    handle, _, err := procOpenProcess.Call(desiredAccess, uintptr(inheritHandle), uintptr(pid))

    // check for errors
    if handle == 0 || err != nil {
        return r1, err
    }

    // return the handle
    return handle, nil
}

// Wraps the winapi CloseHandle function
func CloseHandle(handle uintptr) error {
    // call the winapi function to close the handle
    r, _, err := procCloseHandle.Call(handle)

    // check for errors
    if r == 0 || err != nil {
        return r1, err
    }

    return nil
}

Make sure to close the handle when you're done using it, don't want to keep random handles open indefinitely.

Here's some resources to help you with what's going on here:

https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess

https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle

https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights

Upvotes: 1

MonsterRob
MonsterRob

Reputation: 139


import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "os/exec"
    "strings"
    "syscall"
    "time"
    "unsafe"
    "golang.org/x/sys/windows"
)

func main() {
    ProcessUtils{}.SetWinNice(13716, windows.HIGH_PRIORITY_CLASS)
}

var (
    kernel32        = syscall.MustLoadDLL("kernel32.dll")
    procOpenProcess = kernel32.MustFindProc("OpenProcess")
)

const PROCESS_ALL_ACCESS = 0x1F0FFF

type ProcessUtils struct {
}


func (ProcessUtils) GetProcessHandle(pid int) windows.Handle {
    handle, _, _ := procOpenProcess.Call(ToUintptr(PROCESS_ALL_ACCESS), ToUintptr(true), ToUintptr(pid))
    return windows.Handle(handle)
}

func (u ProcessUtils) SetWinNice(pid int, priority uint32) error {
    handle := u.GetProcessHandle(pid)
    return windows.SetPriorityClass(handle, priority)
}
func ToUintptr(val interface{}) uintptr {
    switch t := val.(type) {
    case string:
        p, _ := syscall.UTF16PtrFromString(val.(string))
        return uintptr(unsafe.Pointer(p))
    case int:
        return uintptr(val.(int))
    default:
        _ = t
        return uintptr(0)
    }
}

Upvotes: 1

sh.seo
sh.seo

Reputation: 1610

syscall.GetCurrentProcess () can be used like this.

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    kernel32, err := syscall.LoadDLL("kernel32.dll")
    if err != nil {
        fmt.Println(err)
    }
    defer kernel32.Release()

    proc, err := kernel32.FindProc("IsWow64Process")
    if err != nil {
        fmt.Println(err)
    }

    handle, err := syscall.GetCurrentProcess()
    if err != nil {
        fmt.Println(err)
    }

    var result bool

    _, _, msg := proc.Call(uintptr(handle), uintptr(unsafe.Pointer(&result)))

    fmt.Printf("%v\n", msg)
}

Upvotes: 1

Related Questions