-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: syscall: allow to pass STARTUPINFOEX to StartProcess Windows API #44005
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
Comments
I think you forgot to mention the key part: adding a new None of the existing ones are in SCREAMING_CASE, though: https://golang.org/pkg/syscall/?GOOS=windows#SysProcAttr Nor are any other types in So how about defined it like: type ProcThreadAttrList struct { _ [1]byte }
func (*ProcThreadAttrList) Release()
func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttrList, error)
etc |
I actually think this is the wrong way to go about this. Instead of exposing that crazy structure, we should add fields to SysProcAttrs for the ones we want to support, such as PROC_THREAD_ATTRIBUTE_HANDLE_LIST and PROC_THREAD_ATTRIBUTE_PARENT_PROCESS. Actually, I think we should take that a step further and always use PROC_THREAD_ATTRIBUTE_HANDLE_LIST when launching processes, in order to prevent handle leaks and prevent races with functions like ShellExecute. And we might even be able to remove that global lock we're using now, which would be great. To that end, I propose:
I think this would cover your use cases well and also make things more robust for current users. |
Indeed. I just updated my proposal. PTAL.
PROC_THREAD_ATTRIBUTE_LIST is how it is spelled by Microsoft. It is impossible to google otherwise. But I am not married to this idea. I am fine to change my proposal. Alex |
I disagree. I propose to add single new field to SysProcAttrs. And that should cover all scenarios in the future. Once implemented, any user can implement any of the types supported by UpdateProcThreadAttribute Windows API without the need to hack standard library.
This proposal is strictly about adding ability to pass STARTUPINFOEX to StartProcess. Alex |
It's fine to mention the Microsoft spelling in godoc. Google will handle the rest. |
Names are important. And PROC_THREAD_ATTRIBUTE_LIST is clear without looking at the go doc. This name provides immediate meaning once I see it. Just like I see meaning of exported variable when its name starts with capital letter. I, while I agree, it does not look pretty, that is what this thing is called. I would like to hear others opinions before changing our mind. Alex |
What's the point of introducing a non-Go-like API to users when we already have a very nice abstraction over this? There's nothing special about these new attributes, except they're passed to CreateProcess yet-another-way because Microsoft has different generations of programmers who like to add new ways of doing things. In Go, though, nobody benefits from that. Plus, I see little benefit in supporting all of those flags. Some have weird gotchas. The two I mentioned are a reasonable start.
In that case, I think this proposal is misguided and should be rejected. I'll open a new GitHub issue. |
This is syscall package. Syscall package provides access to OS. It is fine to use abstractions in our code. But I chose not to use abstractions in my proposal. Abstractions needs to be understood by users and documented. My proposal tried to stay as close to Windows API as possible instead. Alex |
That struct happens to be in syscall because that's where platform-specific structs go. But what we're actually talking about something that intimately interacts with and affects os.StartProcess. And for there we already have a very well-defined, easy-to-use, and most importantly stable way of passing additional Windows process attributes in, via SysProcAttr. Exposing some behemoth Microsoft structure without considering the interactions with the rest of os.StartProcess is a recipe for disaster and a huge burden on users. This proposal is very, very unwise. |
Another issue with this is purely technical: inherited fds need need to be relative to the new parent, not to the original parent. There's no way Go can set up a proper process context given the API you propose. My proposal in #44011 deals with this very well. |
I spent another several hours evaluating at this, writing new code to test different schemes, and seeing what looked most coherent. I could not figure out a way to expose And semantically, allowing the user to pass But I get the desire to be able to play with low level structs for users who want these in So here is what I propose:
Point (1) will help address your objective in a clean way. And point (2) will help give "extra power" to people doing crazy stuff, and it will add it in the place meant for this type of stuff -- It's worth noting that (2) has never been safe to use in the past, because of issues with leaking inheritable handles. However, the series I posted to #44011 fixes this issue definitively, which means we can safely have this type of "raw" functionality in |
Change https://golang.org/cl/288412 mentions this issue: |
I've just submitted a CL that implements part (2) of my previous comment. |
@alexbrainman -- Because I have some hint of what your objective here is, I wrote some example code to show how to launch an unelevated process from an elevated one using the new definitions I just added to x/sys/windows. It works very well: package main
import (
"log"
"path/filepath"
"unsafe"
"golang.org/x/sys/windows"
)
func main() {
windowsDirectory, err := windows.GetWindowsDirectory()
if err != nil {
log.Fatalf("Could not find Windows directory: %v", err)
}
notepad := filepath.Join(windowsDirectory, "notepad.exe")
notepad16, err := windows.UTF16PtrFromString(notepad)
if err != nil {
log.Fatalf("Could not perform string conversion %v", err)
}
shellWindow := windows.GetShellWindow()
if shellWindow == 0 {
log.Fatalf("Could not find shell window")
}
var shellPid uint32
_, err = windows.GetWindowThreadProcessId(shellWindow, &shellPid)
if err != nil {
log.Fatalf("Could not get shell pid: %v", err)
}
shellProcess, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS, false, shellPid)
if err != nil {
log.Fatalf("Could not open shell process: %v", err)
}
defer windows.CloseHandle(shellProcess)
startupInfo := new(windows.StartupInfoEx)
startupInfo.Cb = uint32(unsafe.Sizeof(*startupInfo))
startupInfo.ProcThreadAttributeList, err = windows.NewProcThreadAttributeList(1)
if err != nil {
log.Fatalf("Could not create proc thread attribute list: %v", err)
}
err = startupInfo.ProcThreadAttributeList.Add(windows.PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, unsafe.Pointer(&shellProcess), unsafe.Sizeof(shellProcess))
if err != nil {
log.Fatalf("Could not set parent process handle: %v", err)
}
processInfo := new(windows.ProcessInformation)
err = windows.CreateProcess(notepad16, notepad16, nil, nil, false, windows.CREATE_NEW_CONSOLE|windows.EXTENDED_STARTUPINFO_PRESENT, nil, nil, &startupInfo.StartupInfo, processInfo)
if err != nil {
log.Fatalf("Could not create process: %v", err)
}
windows.CloseHandle(processInfo.Process)
windows.CloseHandle(processInfo.Thread)
} |
Commented over on #44011 (comment):
|
This proposal has been added to the active column of the proposals project |
@gopherbot add OS-Windows |
Yes. Your code looks similar to mine. I only also need to pass command line parameters to the new process. And I need to adjust new process environment. Alex |
@alexbrainman Could you review the CL for this, then, so that you can use that code in x/sys? https://golang.org/cl/288412 is a simple x/sys/windows CL. |
Based on the discussion above (because letting the user pass a STARTUPINFOEX precludes the Go runtime from ever being able to control any of those fields itself), this seems like a likely decline. |
Based on the discussion above, this proposal seems like a likely decline. |
No change in consensus, so declined. |
The bulk of the documentation for these is in: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute This allows creating processes from scratch that use advanced options, such as changing the process parent. Fixes golang/go#44005. Change-Id: Id5cc5400541e57710b9e888cd37ef4f925d510fe Reviewed-on: https://go-review.googlesource.com/c/sys/+/288412 Trust: Jason A. Donenfeld <[email protected]> Trust: Alex Brainman <[email protected]> Run-TryBot: Jason A. Donenfeld <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Alex Brainman <[email protected]>
The changes to add PROC_THREAD_ATTRIBUTE_LIST seemed to have broken the (os.exec) Cmd.Start() functionality on Windows. |
I needed to pass STARTUPINFOEX structure to Windows StartProcess API. It is impossible to do without copying quite a bit of Go main lib code.
I propose we add new PROC_THREAD_ATTRIBUTE_LIST type to syscall package with this public API:
then we can pass new PROC_THREAD_ATTRIBUTE_LIST variable via new syscall.SysProcAttr.ProcThreadAttributeList field
This will allow me to build my program by using standard os/exec package.
I expect this new addition might be useful for other projects (for example #21085 and probably many others). But I don't intend add more code except this proposal.
You can see my proposed change at
https://go-review.googlesource.com/c/go/+/288272
Thank you for considering.
/cc @zx2c4 @bradfitz @mattn
Alex
The text was updated successfully, but these errors were encountered: