英文:
How to implement DllMain entry point in Go
问题
我正在使用Go构建一个DLL。
我无法实现Windows的DllMain入口点。
我的目标是当应用程序通过LoadLibrary加载dll并调用Test方法时,DllMain也会被调用。然而,目前应用程序陷入了死锁状态,什么都没有发生。
这个应用程序是相当简单的Python代码
# App code
import ctypes
lib = ctypes.CDLL("./mydll.dll")
lib.Test()
注意:
-
我正在使用以下版本进行构建
go version go1.16.4 windows/amd64
并使用以下命令进行构建
go build -o mydll.dll --buildmode=c-shared
-
如果我移除DllMain,一切都正常工作,应用程序可以成功调用Test方法。
-
我知道我可以在DllMain下使用switch case来指定条件,比如进程附加、分离等,我只是想保持尽可能简单。
//Go Code
package main
import "C"
import (
"fmt"
"os"
)
//export Test
func Test() {
os.Create("fooFile")
}
//export DllMain
func DllMain(a uintptr, b uint32, c uintptr) int32 {
return 1
}
func main() {
}
英文:
I'm using Go to build a DLL.
I'm not able to implement windows DllMain entry point.
My goal is when an app loads the dll via LoadLibrary
to call the Test
method, the DllMain
will be called as well. However currently the app is kind of stuck in deadlock and nothing happens.
The app is quite simple python code
# App code
import ctypes
lib = ctypes.CDLL("./mydll.dll")
lib.Test()
Notes:
-
I am building using
go version go1.16.4 windows/amd64
and building using
go build -o mydll.dll --buildmode=c-shared
-
If I remove
DllMain
, everything works fine and app can call theTest
method successfully. -
I'm aware that I can
switch
case underDllMain
to specify conditions such as process attached, detached ...etc, I just wanted to keep it as simple as possible.
//Go Code
package main
import "C"
import (
"fmt"
"os"
)
//export Test
func Test() {
os.Create("fooFile")
}
//export DllMain
func DllMain(a uintptr, b uint32, c uintptr) int32 {
return 1
}
func main() {
}
答案1
得分: 3
DllMain
不能是Go函数,因为第一次调用Go函数会初始化Go运行时,而这不能在DllMain
的范围内完成(在DllMain
的范围内只能完成非常少的事情)。
作为一种解决方法,你可以用C语言编写DllMain
,并在一个单独的线程中调用Go代码,就像这个示例中所示。但是在DllMain
的范围内无法与该线程同步,这样做会导致死锁。
还有一个_cgo_wait_runtime_init_done
函数,但它也是异步的。
因此,如果你需要在DLL加载时同步执行一些Go操作,那就没办法了。最好的方法是在调用任何其他API之前定义一个导出的“Init
”函数并调用它。
当然,在Go中初始化加载的惯用方式是通过init()
函数:
package main
import "C"
import (
"fmt"
)
func init() {
fmt.Println("init()")
}
//export Test
func Test() {
fmt.Println("Test()")
}
func main() {
}
编译并运行:
go build -o mydll.dll --buildmode=c-shared
rundll32 mydll,Test
输出:
init()
Test()
英文:
DllMain
can't be a Go function, since invoking a Go function the first time initializes the Go runtime, which can't be done in scope of DllMain
(there are very few things that can be done in scope of DllMain
).
As a workaround, you can write DllMain
in C and invoke Go code in a separate thread as in this example. But you can't synchronize with that thread in the scope of DllMain
, doing so will again lead to a deadlock.
There's also _cgo_wait_runtime_init_done
, but it also is asynchronous.
So if you need to perform some Go action synchronously on DLL attachment, you're out of luck. It's best to just define an "Init
" exported function and call it before calling any other API.
And of course, the idiomatic way in Go to initialize on load is via an init()
function:
package main
import "C"
import (
"fmt"
)
func init() {
fmt.Println("init()")
}
//export Test
func Test() {
fmt.Println("Test()")
}
func main() {
}
Compile and run:
go build -o mydll.dll --buildmode=c-shared
rundll32 mydll,Test
Output:
<!-- language: lang-none -->
init()
Test()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论