以前做的一个项目有个需求, 需要在Linux系统上的服务后端根据前端配置动态编译出能在Windows平台运行的程序, 并且能支持程序带图标, 虽然使用Go语言能够方便的编译跨平台运行的代码, 但编译带资源图标的Windows可执行程序还未尝试过, 本篇文章对这部分内容做一个实践尝试。
如果要单独完成这项任务, 需要准备以下几样"配菜":
1.rsrc程序, 开源项目, 地址: https://github.com/akavel/rsrc
2.一份模拟在Windows平台运行的源码, 当被自动编译后, 能看到运行效果。
3.一份图标文件, 文件后缀为.ico。
4.一份模拟服务后端功能的程序, 该程序负责编译出最终带图标的Windows程序。
下面将依次介绍这四样"配菜"的调制方法:
rsrc程序是一个开源专门负责将ico资源文件写入Windows PE可执行程序的工具, 该工具使用Go语言开发, 能够支持将rsrc代码编译成支持不同平台的可执行程序, 支持的系统包括: Darwin、Linux(amd64)、Windows(386)、Windows(amd64)。
rsrc程序的参数在实际使用过程中主要有三个:
1.-arch 该参数将指定嵌入资源后的程序运行的CPU架构, 为了向下同时兼容32位和64位系统, 这里一般设置的参数为:"-arch 386"。
2.-ico 该参数执行传入的图标文件保存的全路径。
3.-o 该参数是一个输出参数, 将输出一个.res或者.syso的资源文件, 如果不指定资源文件的名字, rsrc将默认输出一个名为:rsrc_windows_{arch}.syso的文件, 在我们实际应用中, 将输出格式为: .syso的文件。
模拟Windows程序, 这里为了能看到运行的实际效果, 将在Go代码中嵌入Windows的对话框元素, 双击执行后将弹出一个对话框, 表示程序运行成功。以下是模拟Windows程序的Go源码:
package main
import (
"syscall"
"unsafe"
)
var (
user32, _ = syscall.LoadLibrary("user32.dll")
messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW")
)
func IntPtr(n int) uintptr {
return uintptr(n)
}
func StrPtr(s string) uintptr {
return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)))
}
func ShowMessage(title, text string) {
user32 := syscall.NewLazyDLL("user32.dll")
MessageBoxW := user32.NewProc("MessageBoxW")
MessageBoxW.Call(IntPtr(0), StrPtr(text), StrPtr(title), IntPtr(0))
}
func main() {
defer syscall.FreeLibrary(user32)
ShowMessage("成功消息!", "Hello!")
}
这段代码运行成功后, 将弹出一个成功的对话框。
图标文件准备标准的ico格式文件即可, 以下是我的图标文件:
模拟服务后端程序主要负责利用rsrc将资源文件编译成.syso格式,并利用go build完成最终的二进程程序编译, 完整代码如下:`
package main
import (
"fmt"
"os/exec"
)
const (
hIconPath = "/home/gocompile/icon/Folder.ico" // 图标文件路径
ProgramName = "gocompile" // 编译完成最终程序名称
compileOutput = "./output/gocompile.exe" // 编译之后最终的输出目录
)
func CompileProgram() {
cmd := "./rsrc -arch 386 -ico " + hIconPath + " -o " + ProgramName + ".syso" + " && " +
"CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o " + compileOutput
_, err := exec.Command("bash", "-c", cmd).Output()
if err != nil {
fmt.Println(err.Error())
return
}
}
func main() {
CompileProgram()
}
利用以下命令将模拟服务后端程序编译成能在Linux系统上运行的程序:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o gocontrol
将编译完成的gocontrol程序上传到Linux服务器, 准备完成最终的跨平台程序编译。
在Linux服务器上建立工作目录:/home/gocompile, 在目录下新建code和icon两个目录, 将本地的所有图标文件上传到icon文件夹下;将
编译完成的gocontrol、模拟Windows程序源码、rsrc程序上传到code 目录下, 如图:
编辑/etc/profile, 在最后加上路径:export PATH=$PATH:/home/gocompile/code,如图 :
执行: source /etc/profile
进入code目录, 给予gocontrol和rsrc可执行权限, ./gocontrol运行程序, 如果没有错误产生,将在output目录中生成名为gocompile.exe程序,如图:
将output中的gocompile.exe程序拷贝到WIndows系统上,可以看到已经生成了带图标的程序,如图:
双击执行该程序, 可以看到已经执行成功:
这里主要利用了rsrc开源库将图标资源写入可执行程序, 利用Go语言的跨平台特性方便的在Linux系统上生成了在WIndows系统中执行的程序。