Golang模块与资源相对路径的使用指南
Golang模块与资源相对路径的使用指南 你好 🙂
我的一个模块遇到了问题。基本上,它找不到我包含在模块中的 *.dll 文件。问题是,如果我在我的另一个模块中尝试使用这个模块,就会收到错误。
我的文件结构如下:
- yjuqsoft
-- lua53
--- go.mod
--- lua.go
--- lua53.dll
-- test
--- go.mod
--- main.go
以下是文件内容:
yjuqsoft/lua53/go.mod
module yjuqsoft/lua53
go 1.14
require golang.org/x/sys v0.0.0-20200331124033-c3d80250170d
yjuqsoft/lua53/lua.go
package lua53
import (
"fmt"
"golang.org/x/sys/windows"
)
var dll *windows.DLL
func init() {
tmp, err := windows.LoadDLL("lua53.dll") // 有问题的行
dll = tmp
fmt.Println(err)
}
// DllRelease unloads DLL from memory
func DllRelease() {
dll.Release()
}
yjuqsoft/test/go.mod
module yjuqsoft/test
go 1.14
require yjuqsoft/lua53 v0.0.0
replace yjuqsoft/lua53 => ../lua53
yjuqsoft/test/main.go
package main
import _ "yjuqsoft/lua53"
func main() {
// lua53.DllRelease()
}
如果我运行这个,会得到以下输出:Failed to load lua53.dll: The specified module could not be found.
好吧,…… 找不到模块。如果我使用绝对路径,一切正常。但我不想这样做,因为我的模块 yjuqsoft/lua53 应该是独立的。那么,我该如何解决这个问题呢?
顺便说一下,所有文件都存储在 U 盘上。GOPATH 没有指向这个 U 盘。如果这有什么影响的话。
更多关于Golang模块与资源相对路径的使用指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
@Yjuq 你是怎么运行这个的?我看到 DLL 文件是和你的 Go 源代码放在一起的,但如果你运行的是类似 go install 这样的命令,二进制文件很可能被放到了其他地方。安装时你可能需要手动移动/复制那个文件。
// 代码示例:这里可以放置相关的Go代码
更多关于Golang模块与资源相对路径的使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我只是进入测试目录并使用 go build 并运行它。我认为问题在于工作目录根本就是错误的。我刚刚找到了一个对我有效的解决方案。基本上就是摆脱相对路径,并根据 lua.go 文件的存储位置生成一个绝对路径。
package lua53
import (
"golang.org/x/sys/windows"
"path/filepath"
"runtime"
)
var dll *windows.DLL
func init() {
_, path, _, _ := runtime.Caller(0)
path = filepath.Join(filepath.Dir(path), "lua53.dll")
dll = windows.MustLoadDLL(path)
}
// DllRelease unloads DLL from memory
func DllRelease() {
dll.Release()
}
在Go模块中处理DLL等资源文件时,需要明确资源文件的嵌入和加载方式。windows.LoadDLL()默认在系统路径和当前工作目录中查找DLL,但在模块依赖场景下,这通常不是资源文件的实际位置。
以下是几种解决方案:
方案1:使用//go:embed嵌入资源(Go 1.16+)
// lua.go
package lua53
import (
_ "embed"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"golang.org/x/sys/windows"
)
//go:embed lua53.dll
var dllBytes []byte
var dll *windows.DLL
func init() {
// 创建临时文件
tmpFile, err := ioutil.TempFile("", "lua53_*.dll")
if err != nil {
panic(err)
}
defer tmpFile.Close()
// 写入DLL数据
if _, err := tmpFile.Write(dllBytes); err != nil {
panic(err)
}
tmpFile.Close()
// 从临时文件加载DLL
dll, err = windows.LoadDLL(tmpFile.Name())
if err != nil {
panic(err)
}
// 清理临时文件(可选,DLL加载后可以删除)
os.Remove(tmpFile.Name())
fmt.Println("DLL loaded successfully")
}
方案2:使用embed.FS(Go 1.16+)
// lua.go
package lua53
import (
"embed"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"golang.org/x/sys/windows"
)
//go:embed lua53.dll
var dllFS embed.FS
var dll *windows.DLL
func init() {
// 从嵌入的文件系统读取
dllData, err := dllFS.ReadFile("lua53.dll")
if err != nil {
panic(err)
}
// 创建临时文件
tmpFile, err := ioutil.TempFile("", "lua53_*.dll")
if err != nil {
panic(err)
}
// 写入并加载
if _, err := tmpFile.Write(dllData); err != nil {
tmpFile.Close()
panic(err)
}
tmpFile.Close()
dll, err = windows.LoadDLL(tmpFile.Name())
if err != nil {
panic(err)
}
// 可选:删除临时文件
os.Remove(tmpFile.Name())
}
方案3:使用相对路径查找(适用于旧版本Go)
// lua.go
package lua53
import (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"golang.org/x/sys/windows"
)
var dll *windows.DLL
func init() {
// 获取当前源文件所在目录
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
// 构建DLL完整路径
dllPath := filepath.Join(dir, "lua53.dll")
// 检查文件是否存在
if _, err := os.Stat(dllPath); os.IsNotExist(err) {
// 尝试从模块根目录查找
dir = findModuleRoot(dir)
dllPath = filepath.Join(dir, "lua53.dll")
}
dll, err = windows.LoadDLL(dllPath)
fmt.Println("Loading DLL from:", dllPath)
fmt.Println(err)
}
func findModuleRoot(startDir string) string {
dir := startDir
for {
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
return startDir
}
方案4:使用资源文件系统包
// 使用第三方包如 github.com/mjibson/esc 或 packr
// 首先安装:go get github.com/mjibson/esc
//go:generate esc -o lua53_resources.go -pkg lua53 -private lua53.dll
// lua.go
package lua53
import (
"fmt"
"io/ioutil"
"os"
"golang.org/x/sys/windows"
)
var dll *windows.DLL
func init() {
// 从生成的文件系统读取
dllData := _escFSMustByte(false, "/lua53.dll")
tmpFile, err := ioutil.TempFile("", "lua53_*.dll")
if err != nil {
panic(err)
}
if _, err := tmpFile.Write(dllData); err != nil {
tmpFile.Close()
panic(err)
}
tmpFile.Close()
dll, err = windows.LoadDLL(tmpFile.Name())
if err != nil {
panic(err)
}
os.Remove(tmpFile.Name())
}
推荐方案
对于新项目,建议使用方案1或方案2的//go:embed特性,这是Go 1.16+的标准做法。如果必须支持旧版本Go,方案3提供了基于相对路径的查找方法。
关键点是:资源文件必须被正确嵌入到模块中,并在运行时提取到可访问的位置(如临时文件)进行加载。直接使用windows.LoadDLL("lua53.dll")无法找到模块内的资源文件,因为加载路径取决于调用模块的工作目录。

