最近学习golang,调用了dll文件,打包后没有dll文件,没能单文件部署就想办法实现单文件部署
微信群里有人建议`go-bindata`实现打包静态资源
1. golang调用dll的实现
基础类`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 <stdio.h>
#include <stdlib.h>
*/
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
}
2. 打包dll到exe的实现
我调用的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文件也变大了,单文件部署就没问题了.