最近学习golang,调用了dll文件,打包后没有dll文件,没能单文件部署就想办法实现单文件部署
微信群里有人建议`go-bindata`实现打包静态资源
基础类`Dll`实现dll文件的加载及方法通用的调用
- package dll
-
- import (
- "errors"
- "fmt"
- "syscall"
- "unsafe"
-
- "github.com/hbh112233abc/seal/internal/pkg"
- )
-
- type Dll struct {
- DllPath string
- dll *syscall.DLL
- }
-
- func NewDLL(dllPath string) (*Dll, error) {
- if !pkg.IsFile(dllPath) {
- panic(fmt.Errorf("dllPath not found: %v", dllPath))
- }
-
- d := &Dll{
- DllPath: dllPath,
- }
- d.init()
- return d, nil
- }
-
- func (d *Dll) init() {
- handle, err := syscall.LoadDLL(d.DllPath)
- if err != nil {
- panic(fmt.Errorf("dll load error: %v", err))
- }
- d.dll = handle
- }
-
- func (d *Dll) Call(procName string, params ...unsafe.Pointer) error {
- proc, err := d.dll.FindProc(procName)
- if err != nil {
- return err
- }
- data := make([]uintptr, len(params))
- for _, p := range params {
- data = append(data, uintptr(p))
- }
- ret, _, err := proc.Call(data...)
- if int(ret) != 0 {
- return errors.New(DllCallErr.Error(int(ret)))
- }
- return nil
- }
`edccom.go`实现具体的dll调用方法封装
- package dll
-
- /*
- #cgo windows CFLAGS: -DX86=1
- #cgo !windows LDFLAGS: -lm
- #include
- #include
- */
- import "C"
-
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "unsafe"
-
- "github.com/hbh112233abc/seal/internal/pkg"
- )
-
- type SealParser struct {
- PageCount int
- SealInfo []SealInfo
- }
-
- type SealInfo struct {
- HandlerName string
- IdCard string
- SealCount int8
- SealName string
- SealTime string
- Type string
- NPage int8 `json:"nPage"`
- PosLeft float32 `json:"posLeft"`
- PosTop float32 `json:"posTop"`
- }
-
- type EDCCom struct {
- *Dll
- }
-
- func NewEDCCom(dllPath string) (*EDCCom, error) {
- if !strings.HasSuffix(dllPath, "EDCCom.dll") {
- panic(fmt.Errorf("dllPath must `EDCCom.dll`: %v", dllPath))
- }
- d, err := NewDLL(dllPath)
- if err != nil {
- return nil, fmt.Errorf("DLL load failed: %v", err)
- }
- return &EDCCom{d}, nil
- }
-
- func (ec *EDCCom) GetSealInfo(file string) (SealParser, error) {
- exist := pkg.IsFile(file)
- if !exist {
- return SealParser{}, fmt.Errorf("file not found: %s", file)
- }
- ext := filepath.Ext(file)
- ext = strings.ToLower(ext)
-
- var procName string
- switch ext {
- case ".edc":
- procName = "Com_GetEdcSealInfo"
- case ".pdf":
- procName = "Com_GetPdfSealInfo"
- default:
- return SealParser{}, fmt.Errorf("unsupported ext: %s", ext)
- }
-
- return ec.sealInfo(procName, file)
- }
-
- //获取印章信息
- func (ec *EDCCom) sealInfo(procName string, file string) (SealParser, error) {
- proc, err := ec.dll.FindProc(procName)
- if err != nil {
- fmt.Printf("proc %s not found,err:%v \n", procName, err)
- return SealParser{}, err
- }
-
- var sealParser SealParser
- //转换成GBK编码
- gbkDecode, _ := pkg.Utf8ToGbk([]byte(file))
- file = string(gbkDecode)
-
- input := C.CString(file)
- defer C.free(unsafe.Pointer(input))
-
- var result C.char
- var size int
- ret, _, err := proc.Call(
- uintptr(unsafe.Pointer(input)),
- uintptr(unsafe.Pointer(&result)),
- uintptr(unsafe.Pointer(&size)),
- )
- if ret != 0 {
- fmt.Println("Com_GetEdcSealInfo failed: ", DllCallErr.Error(int(ret)), err)
- return sealParser, err
- }
-
- resultString := C.GoStringN(&result, C.int(4096))
- // fmt.Println(resultString)
- resultDecode, err := pkg.GbkToUtf8([]byte(resultString))
- if err != nil {
- fmt.Println("GBK to utf8 failed: ", err)
- return sealParser, err
- }
- // fmt.Println(string(resultDecode))
-
- index := bytes.IndexByte(resultDecode, 0)
- resultDecodeTrim := resultDecode[:index]
- err = json.Unmarshal(resultDecodeTrim, &sealParser)
- if err != nil {
- fmt.Println("json.Unmarshal failed: ", err)
- return sealParser, err
- }
-
- return sealParser, nil
- }
最终调用方法
- func GetSealInfo() error {
- pwd := pkg.CurrentAbPath()
- dllPath := filepath.Join(pwd, "EDCCom.dll")
- fmt.Println("dll:", dllPath)
- dll, err := dll.NewEDCCom(dllPath)
- if err != nil {
- return err
- }
-
- edcFile := filepath.Join(pwd, "tests", "files", "signed.edc")
- res, err := dll.GetSealInfo(edcFile)
- if err != nil {
- return err
- }
- fmt.Printf("%s seal info: \n %#v \n", edcFile, res)
-
- return nil
- }
有关dll调用可以参考: Go调用dll及大部分问题的解决方案,附源码 - 灯火消逝的码头 - 博客园
我调用的dll如下:
安装`go-bindata`
go install -a -v github.com/go-bindata/go-bindata/...@latest
在`main.go`添加
//go:generate go-bindata -fs -o=EDCCom.go -prefix=./ -nocompress -nomemcopy ./docs/dll
命令行执行
go generate
此时main.go目录下将生成`EDCCom.go`
main.go中增加`init`方法,释放dll文件
- func init() {
- pwd := pkg.CurrentAbPath()
- dllFiles, err := AssetDir("docs/dll")
- if err != nil {
- log.Fatal(err)
- return
- }
- for _, dllFile := range dllFiles {
- // fmt.Println(dllFile)
- localDll := filepath.Join(pwd, dllFile)
- if !pkg.IsFile(localDll) {
- bytes, err := Asset(fmt.Sprintf("docs/dll/%s", dllFile))
- if err != nil {
- log.Fatal(err)
- return
- }
- ioutil.WriteFile(localDll, bytes, 0644)
- }
- }
- }
此时运行`go run .`就会先检查目录下有没有dll文件,如果没有则先生成dll文件
打包后的exe文件也变大了,单文件部署就没问题了.