你正在使用哪个版本的Go(go version
)?
$ go version
go version go1.14.7 windows/amd64
这个问题在最新版本中是否会重现?
没有尝试过,但我非常确定它会发生。
你正在使用什么操作系统和处理器架构(go env
)?
go env
输出
$ go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Bupjae\AppData\Local\go-build
set GOENV=C:\Users\Bupjae\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\Bupjae\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=c:\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=c:\go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\Bupjae\AppData\Local\Temp\go-build843644218=/tmp/go-build -gno-record-gcc-switches
你做了什么?
package main
import (
"fmt"
"golang.org/x/sys/windows"
)
func main() {
var handle windows.Handle
wrongName := windows.StringToUTF16Ptr("ADFADFASDFSDAFASFD")
err := windows.GetModuleHandleEx(0, wrongName, &handle)
if handle == 0 {
fmt.Println(err)
fmt.Println(windows.GetLastError())
}
}
你期望看到什么?
打印出描述“模块未找到”两次的错误信息。
你看到了什么?
根据 https://golang.org/src/runtime/sys_windows_amd64.s ,在调用实际的DLL函数(第58行)之前,最后的错误信息被擦除(第22-23行)。这使得syscall.GetLastError(和windows.GetLastError)完全失去了意义。
7条答案
按热度按时间4c8rllxm1#
这个想法是错误将通过调用返回,所以你永远不应该自己调用
GetLastError
。你的代码显示:err
的值是你想要的错误。因此,如果这里有什么要做的,简单地记录一下没有必要调用
GetLastError
。我不知道为什么我们甚至定义了它。ct3nt3jp2#
CC @alexbrainman
am46iovg3#
Ian说:不要直接从你的代码中调用GetLastError。Go运行时会在后台为你调用GetLastError。
在Go代码中调用GetLastError是没有意义的,因为每次调用新的Windows API时,GetLastError都会发生变化。但Go运行时始终调用不同的Windows API(例如获取内存或创建线程)。你代码中看不到任何Windows API调用并不意味着你的程序没有调用Windows API。
Go运行时还始终在改变线程。GetLastError是线程特定的。因此,如果你从代码中调用GetLastError,你可能会从另一个无关的线程获取GetLastError值。
根据https://golang.org/src/runtime/sys_windows_amd64.s的说法,在调用实际的DLL函数(第58行)之前,最后一个错误信息会被擦除(第22-23行)。
在这个函数的第70-73行,它会在Windows API完成后立即收集GetLastError值。只需使用这个值即可。
Alex
1aaf6o9v4#
抱歉,我无意中提升了一个超过一年的帖子。
我知道调用GetLastError并不是获取错误的正确方法。
然后,我认为它应该得到适当的文档说明,例如“GetLastError将始终返回nil。请使用每个API调用的返回值”,正如之前的评论所建议的那样。也许将GetLastError标记为已弃用也是可能的。
uqjltbpv5#
作为替代方案,我想知道我们是否可以更改Go运行时以提供一个更强的不变式,使
GetLastError
实际上起作用。例如,这个不变式可能是可行的:“如果调用的goroutine被锁定到一个操作系统线程上,那么在系统调用之后(中间没有其他函数调用或变量声明)立即调用GetLastError
将返回该系统调用设置的错误。”也许
GetLastError
应该在调用的goroutine不被锁定到操作系统线程时发生恐慌(或无条件地返回错误)?这个不变式可能需要在保留和恢复运行时的信号处理器中的错误值方面更加小心,尽管如果需要为这个不变式更改信号处理器,它们可能也同样需要,以避免在使用cgo的程序中破坏
GetLastError
的返回值。(CC @golang/windows)
dffbzjpn6#
作为替代方案,我想知道我们是否可以更改Go运行时以提供一个更强的不变式,使
GetLastError
实际上起作用。例如,这个不变式可能是可行的:“如果调用的goroutine被锁定到一个操作系统线程上,那么在系统调用之后立即调用GetLastError
(中间没有其他函数调用或变量声明)将返回该系统调用设置的错误。”与仅使用原始API调用返回的GetLastError值相比,使运行时代码和文档变得更加复杂有什么好处?参见
在第70-73行,此函数在Windows API完成后收集GetLastError值。直接使用该值即可。
来自#41220(评论)。
也许
GetLastError
应该恐慌(或无条件地返回错误)如果我对此表示同意,那也没关系。但我认为很少有人会调用GetLastError。
我们也可以只是不使用它并解释原因。
Alex
dgiusagp7#
将运行时代码和文档变得更复杂,与仅使用原始API调用返回的GetLastError值相比,有什么好处?
好处是调用者可以使用
GetLastError
来实现Microsoft文档中描述的目的(例如在#64170中的案例),并获得该系统调用的文档化行为,就像他们可以为syscall
和x/sys/windows
包提供的其他大多数系统调用函数一样。