Skip to content

proposal: x/sys/windows/svc: Call AllocConsole as part of Run #72884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
mediocregopher opened this issue Mar 15, 2025 · 7 comments
Open

proposal: x/sys/windows/svc: Call AllocConsole as part of Run #72884

mediocregopher opened this issue Mar 15, 2025 · 7 comments
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool OS-Windows Proposal
Milestone

Comments

@mediocregopher
Copy link

mediocregopher commented Mar 15, 2025

Proposal Details

Background

I am implementing a windows service which act as a kind of process manager; based on user input during its lifetime the service process will spawn its own child processes (with the CREATE_NEW_PROCESS_GROUP flag set), and based on other user input the service may decide to shut down those child processes using windows.GenerateConsoleCtrlEvent(syscall.CTRL_BREAK_EVENT, pid). This control event acts more or less like a SIGINT in windows world.

When running the process as an actual service, via the Windows Service Manager, I use svc.Run to make all the necessary system calls and process control messages.

When running the service from console the GenerateConsoleCtrlEvent works fine. However, when being run as a service, the GenerateConsolCtrlEvent returns the error The handle is invalid.. This is apparently because when running a process as a service windows does not allocate a console for the process.

Adding the following into my Handler's Execute method fixes the problem:

if r, _, err := windows.NewLazySystemDLL("kernel32.dll").NewProc("AllocConsole").Call(); r == 0 {
	panic(err)
}

Loading the DLL manually is necessary because AllocConsole is not exposed via x/sys/windows.

I discovered that AllocConsole would solve this issue based on this 20 year old thread from CodeGuru.

Proposal

I propose that svc.Run should call AllocConsole itself, perhaps just after the call to RegisterServiceCtrlHandlerEx. I am not a professional windows developer so I could well be mistaken, but I don't believe there's any reason to not allocate a console, and doing so would save other users from having to dig up this odd requirement in the future.

If there is a reason why someone would not want their service to have a console attached then perhaps a new function, RunOpt, could be introduced to the package. Something like:

type RunOpts struct {
    AllocConsole bool
}

func RunOpt(name string, handler Handler, opts RunOpts) error

The AllocConsole field could specifically document why a console allocation would be desired, which would expose this possible edge-case to the user.

Secondary Proposal: Expose AllocConsole in x/sys/windows

I don't know what the process is for adding new syscalls to x/sys/windows, but having AllocConsole exposed there would be convenient.

@gopherbot gopherbot added this to the Proposal milestone Mar 15, 2025
@gabyhelp gabyhelp added the LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool label Mar 15, 2025
@ianlancetaylor
Copy link
Member

CC @golang/windows

@alexbrainman
Copy link
Member

Why you cannot run AllocConsole API before you start your service?

I propose that svc.Run should call AllocConsole itself, perhaps just after the call to RegisterServiceCtrlHandlerEx. I am not a professional windows developer so I could well be mistaken, but I don't believe there's any reason to not allocate a console, and doing so would save other users from having to dig up this odd requirement in the future.

I think what you are doing is pretty unusual, because average windows service does not interact with logged in user. Whole point of the service is to run without user logged in.

But I am not a Windows expert enough to decide if adding AllocConsole to the begging of this code is a problem or not. Perhaps other users will help.

If there is a reason why someone would not want their service to have a console attached then perhaps a new function, RunOpt, could be introduced to the package.

I don't think this feature is important enough to warrant new option that every user need to understand. But others might disagree.

I don't know what the process is for adding new syscalls to x/sys/windows, but having AllocConsole exposed there would be convenient.

You don't need a proposal. If you want to add AllocConsole Windows API to the x/sys/windows package, just send a change.

Alex

@mediocregopher
Copy link
Author

Why you cannot run AllocConsole API before you start your service?

I can do so, and that is my solution at the moment, but it's not an obvious thing to have to do, and I thought if there was no downside to the svc package doing it automatically it would save folks some headache in the future.

I think what you are doing is pretty unusual, because average windows service does not interact with logged in user. Whole point of the service is to run without user logged in.

Users programs interact with the service via an RPC interface. This is fairly standard, based on both MSDN docs and questions people ask online 0 1 2 3. The unusual part is that the service is spawning and managing its own child processes, which is something I've found very little precedent for, except for that one thread I linked in the OP.

I don't think this feature is important enough to warrant new option that every user need to understand. But others might disagree.

I also don't know if this warrants an API change, which is why I listed it as a secondary option. IMO the package calling AllocConsole automatically would be much more preferable, if there's no downside to doing so.

You don't need a proposal. If you want to add AllocConsole Windows API to the x/sys/windows package, just send a change.

Sounds good, will do so :)

@alexbrainman
Copy link
Member

I can do so, and that is my solution at the moment, ...

Good.

but it's not an obvious thing to have to do, and I thought if there was no downside to the svc package doing it automatically it would save folks some headache in the future.

You are the first who complained about this problem. So your situation is not very common. I don't think that adding more code will "improve" the situation - new code needs to be maintained by the Go team.

Alex

@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Mar 19, 2025
@ssexton71
Copy link

As a longtime Windows dev who has written several services, I will tell you this: the usual way to control services like you are trying to do is not to emulate SIGINT but to use a Win32 Event (CreateEvent/SetEvent) both when running as a service AND when running as a console. C style events (SIGINT, etc) are second class citizens in Windows. You will be much happier with the Win32 IPC primitives: Events, Mutexes, etc.

@mediocregopher
Copy link
Author

@ssexton71 unfortunately the child services I'm working with are not themselves written for windows, but for unix (but with windows support). So they don't set up windows event handlers like you describe, but do respond to the C-style events, so that's the avenue I'm taking to send them a simple shutdown message.

@ssexton71
Copy link

That is your choice ofc. I was just trying to add some color since there seemed to be some q as to whether your approach based on Unix signal handlers is typical of a Windows service. It isn't. CreateEvent/SetEvent/WaitForMultipleObjects is. Idk if WFMO plays nice with goroutines though, since it blocks the thread. Note this isnt the same as a WIndows GUI event loop. It is more like a semaphore with a max value of 1. So not a lot to set up but it will tie up a thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool OS-Windows Proposal
Projects
Status: Incoming
Development

No branches or pull requests

6 participants