在使用Go语言开发应用程序时,有个非常方便的地方就是编译得到的可执行文件可以不依赖任何动态链接库、并且不需要任何运行环境即可运行,这一点Java就没那么方便了。

不过在Windows上编译得到的exe文件,默认是不带图标和任何属性信息的,那么怎么才能让我们编译得到的可执行文件带上图标和属性信息呢?
事实上,借助go-winres这款命令行工具即可很方便地实现。

1,安装go-winres命令

执行下列命令即可安装go-winres命令到电脑中:

go install github.com/tc-hib/go-winres@latest

2,创建配置模板

我们首先需要一个配置文件来指定我们的图标文件和属性信息,然后将它们编译得到资源文件,最后将资源文件和Go源代码一起编译即可得到带有图标的可执行文件了!

我们可以先生成一个配置模板,然后进行修改即可,执行下列命令:

go-winres init

然后在当前目录下,你会发现多了个winres目录,其中winres.json就是配置文件,而两个png文件则是它自带的图标文件:

3,修改配置

使用文本编辑器打开winres.json文件,其中有一些初始内容:

其中的大致结构我们不需要修改,基本上就是修改指定其中部分字段值即可。

下面,我们来单独看一下每个部分。

(1) 图标指定

我们来看看图标部分,声明在json文件中的RT_GROUP_ICON属性中:

"RT_GROUP_ICON": {
    "APP": {
        "0000": [
            "icon.png",
            "icon16.png"
        ]
    }
}

上述APP表示一组图标,其中还有一个0000,表示语言,不过这两个属性名称都不需要怎么动,重点是看0000这个属性,它是一个数组,而很显然数组中记录着一组图标,在其中可以指定不同分辨率的图标。在这里指定的图标就会被应用到最终编译的可执行文件上去。

图标文件可以就是png文件,将它和winres.json放在同一目录下,然后在这个0000数组中指定图标文件名即可。

假设我现在需要自定义图标,并且只需要一个文件,那么我可以先删除里面自带的两个png文件,然后把我的图标放在和winres.json同级目录下:

然后修改图标部分配置如下:

"RT_GROUP_ICON": {
    "APP": {
        "0000": [
            "my-icon.png"
        ]
    }
}

这样,图标就指定完成了!

需要注意的是,图片文件尺寸不能大于256 x 256!

(2) 软件清单

软件清单部分声明在RT_MANIFEST属性中:

"RT_MANIFEST": {
    "#1": {
        "0409": {
            "identity": {
                "name": "",
                "version": ""
            },
            "description": "",
            "minimum-os": "win7",
            "execution-level": "as invoker",
            "ui-access": false,
            "auto-elevate": false,
            "dpi-awareness": "system",
            "disable-theming": false,
            "disable-window-filtering": false,
            "high-resolution-scrolling-aware": false,
            "ultra-high-resolution-scrolling-aware": false,
            "long-path-aware": false,
            "printer-driver-isolation": false,
            "gdi-scaling": false,
            "segment-heap": false,
            "use-common-controls-v6": false
        }
    }
}

上述并非所有属性都需要填写和修改,这里将重要的部分讲解一下:

  • description 文件的描述信息

  • minimum-os 最低要求操作系统,可以是以下值:

    • "vista"
    • "win7"
    • "win8"
    • "win8.1"
    • "win10"
  • execution-level 应用程序所需的权限,可以是:

    • "as invoker" 不需要任何权限
    • "highest" 使用当前用户的可用最高权限
    • "administrator" 强制要求管理员权限才能运行

对于identify属性,普通应用程序建议留空即可,也可以将其删除。

(3) 元数据信息

这部分定义在RT_VERSION属性中:

"RT_VERSION": {
    "#1": {
        "0000": {
            "fixed": {
                "file_version": "0.0.0.0",
                "product_version": "0.0.0.0"
            },
            "info": {
                "0409": {
                    "Comments": "",
                    "CompanyName": "",
                    "FileDescription": "",
                    "FileVersion": "",
                    "InternalName": "",
                    "LegalCopyright": "",
                    "LegalTrademarks": "",
                    "OriginalFilename": "",
                    "PrivateBuild": "",
                    "ProductName": "",
                    "ProductVersion": "",
                    "SpecialBuild": ""
                }
            }
        }
    }
}

这里也并非所有属性都需要填写和修改,这里将重要的部分讲解一下:

  • fixed 属性中的两个属性,主要是声明文件版本和程序版本,按照x.y.z.w的格式自己填写即可

  • info 主要是属性信息,其中0409是英语的语言代码,表示在英文环境下显示其中的属性,在info中可以定义多个语言环境下的属性信息,具体大家可以查看官方项目示例,在其中:

    • CompanyName公司名称
    • FileDescription文件描述
    • FileVersion 文件版本
    • ProductVersion 程序版本
    • LegalCopyright 版权信息
    • OriginalFilename 原始文件名
    • ProductName 程序名称

(4) 简易winres.json模板

可见上述有许多属性是我们大多数时候不需要进行设定的,可以删除,因此这里我提供一个模板,大家可以复制作为自己的winres.json文件使用:

{
    "RT_GROUP_ICON": {
        "APP": {
            "0000": [
                "my-icon.png"
            ]
        }
    },
    "RT_MANIFEST": {
        "#1": {
            "2052": {
                "description": "我的Go示例程序",
                "minimum-os": "win7",
                "execution-level": "highest",
                "ui-access": false,
                "auto-elevate": false,
                "dpi-awareness": "system",
                "disable-theming": false,
                "disable-window-filtering": false,
                "high-resolution-scrolling-aware": false,
                "ultra-high-resolution-scrolling-aware": false,
                "long-path-aware": false,
                "printer-driver-isolation": false,
                "gdi-scaling": false,
                "segment-heap": false,
                "use-common-controls-v6": false
            }
        }
    },
    "RT_VERSION": {
        "#1": {
            "0000": {
                "fixed": {
                    "file_version": "1.0.0.0"
                },
                "info": {
                    "2052": {
                        "CompanyName": "公司名称",
                        "FileDescription": "我的Golang示例程序",
                        "LegalCopyright": "© 版权",
                        "OriginalFilename": "demo.exe",
                        "ProductName": "Go示例",
                        "ProductVersion": "1.0.0.0"
                    }
                }
            }
        }
    }
}

大家修改其中信息为自己的即可。

4,编译资源

在winres文件夹所在目录下执行命令:

go-winres make

此时你会发现多了这几个syso文件,这就是编译得到的资源:

需要注意的是,上述命令的运行路径是在winres文件夹所在目录下,而不是winres文件夹里面。

当然,你也可以使用–in参数指定json文件位置,如果你的运行路径不在winres文件夹的所在目录下的话:

# 指定json配置文件在上一级目录的res目录中
go-winres make --in "../res/winres.json"

还可以使用–out参数指定输出的资源文件位置,具体大家可以通过命令go-winres make –help查看帮助。

5,编译Go程序

首先确保上述编译得到的两个syso资源文件和Go语言模块配置go.mod在同级目录下:

然后像平时一样,在go.mod所在目录下执行构建命令即可:

go build -ldflags "-w -s"

这样,我们得到的可执行文件就有图标和属性信息了!

上述命令中,ldflags参数指定的是去除调试和符号信息以减小构建产物文件大小,这个参数可以省略。

可见我们编译得到的资源文件都有着类似windowsamd64这样的后缀,这个后缀是用于go build命令链接资源时,根据架构判断链接哪个资源用的,例如当我们编译64位程序时,go build就会自动寻找名为xxx_windows_amd64.syso的资源文件进行链接,同样地如果是编译为32位程序,则是寻找名为xxx_windows_386.syso的资源文件,因此syso资源文件的文件名必须是xxx_windows_amd64.syso这样的形式,不可以修改其后缀。

与此同时,在进行go build时,go.mod文件必须要和syso文件在同一目录下,如果没有go.mod文件,那主程序文件(被编译的)就需要和syso文件在同一目录下