Windows kernel32 SetConsoleCtrlHandler

Full disclosure, I do not actually know Go, the language, other than what I’ve absorbed over the last few days of research. I’m just trying to figure out a fix for an issue with https://github.com/mroote/factorio-server-manager/releases, so I can submit a PR.

I think I have a workaround, that sounds like it SHOULD work, and that Stack Overflow seems to confirm should work in other environments. To implement it, I need to make a call to https://msdn.microsoft.com/en-us/library/windows/desktop/ms686016(v=vs.85).aspx Can anyone show me the proper way to import and call this method?

The real underlying issue is related to simulating an os.Interrupt signal (I.E. CTRL_C_EVENT in Windows) for a process.
If anyone wants to take a stab at that, I can elaborate further.

I have no concrete example at hand, but the articles cgo and Calling a Windows DLL from the Go wiki might be good starting points.

I actually managed to get this much on my own, but it wasn’t working for the SetConsoleCtrlHandler call, mainly I think because it didn’t like me trying to pass nil for the first parameter.

func sendCtrlEvent(pid int, eventId uint) {
	d, e := syscall.LoadDLL("kernel32.dll")
	if e != nil {
		log.Fatalf("LoadDLL: %v\n", e)
	}
	p, e := d.FindProc("GenerateConsoleCtrlEvent")
	if e != nil {
		log.Fatalf("FindProc: %v\n", e)
	}
    r, _, e := p.Call(uintptr(eventId), uintptr(pid))
    if r == 0 {
    	log.Fatalf("GenerateConsoleCtrlEvent: %v\n", e)
    }
}

That looks like it should work to me. You noticed the docs that say you can’t send this to another process group, so the only valid parameters to that call would be zero (for ctrl-c) and zero (for current process group), right?

What I currently have working is…

GenerateConsoleCtrlEvent(0, syscall.CTRL_C_EVENT);

This correctly sends CTRL+C to the target process, causing it to initiate a file save. What it also does is transmit CTRL+C to the current app itself, since it owns the process I’m trying to close. The suggested solution I found on Stack Overflow is to use SetConsoleCtrlHandler to temporarily disable CTRL+C within this app, allowing me to close the target process, then re-enable CTRL+C.

func disableCtrlHandler() {
	d, e := syscall.LoadDLL("kernel32.dll")
	if e != nil {
		log.Fatalf("LoadDLL: %v\n", e)
	}
	p, e := d.FindProc("SetConsoleCtrlHandler")
	if e != nil {
		log.Fatalf("FindProc: %v\n", e)
	}
    r, _, e := p.Call(nil, uintptr(1))
    if r == 0 {
    	log.Fatalf("SetConsoleCtrlHandler: %v\n", e)
    }
}

The specific error here is

cannot use nil as type uintptr in argument to p.Call

I’m just not sure what to make of a pointer that can’t be set to null. I also wasn’t sure if there was a better way to pass “true” and “false” to the call, other than “uintptr(1)” and “uintptr(0)”

Did you try uintptr(0) instead of nil?

Yeah, that worked. I did try that once before, but for some other reason, it looked to me like it wasn’t working, so I wrote it off.

Thanks for the input, everyone!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.