使用Golang制作一个简单的启动Jar包的桌面程序

Posted zhijian233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Golang制作一个简单的启动Jar包的桌面程序相关的知识,希望对你有一定的参考价值。

环境:

1、WindowsXP(不支持xp)以上

2、Golang1.11.X及以上

3、JDK or JRE

 

第三方库:

1、Windows桌面应用库:WALK (https://github.com/lxn/walk)

2、编码转换库:mahonia(https://github.com/axgle/mahonia)

 

需要实现的功能

1.点击启动按钮运行Jar包并且将控制台输出到桌面程序的文本框中

2.重复启动应该被禁止

3.点击关闭按钮关闭对应当前启动的Java程序

 

实现细节(对应上面的功能):

1.开个新线程去执行CMD命令,通过javaw.exe运行Jar包:

 

// 执行cmd命令
func execCommand(commandName string, params []string) bool 
    cmd := exec.Command(commandName, params...)

    stdout, err := cmd.StdoutPipe()
    if err != nil 
        return false
    

    cmd.Start()

    reader := bufio.NewReader(stdout)

    for 
        out, err2 := reader.ReadBytes(\\n)
        if err2 != nil || io.EOF == err2 
            break
        
        // 转换到gbk编码
        srcCoder := mahonia.NewDecoder("gbk")
        resultstr := bytes2str(out)
        result := srcCoder.ConvertString(resultstr)

        if pId == 0 
            // 从输出数据中获取进程id,关闭的时候用的到
            pId, _ = strconv.Atoi(GetVeesPid(result, `PID `, " "))
        

        // 输出到文本框
        outTE.SetText(outTE.Text() + result)
    

    cmd.Wait()
    return true

启动Jar包:

command := "javaw"
params := []string"-Dfile-encoding=UTF-8", "-jar", "vees2.jar", "--server.port=7072"
execCommand(command, params)

 

2.开个线程,定时检测当前启动的Java程序是否仍在运行,是则禁止继续启动

 

3.关闭程序和开启一样,都调用cmd命令

command := "taskkill"
params := []string"/f", "/pid", strconv.Itoa(pId)
execCommand(command, params)

 

这里有一些关于进程的函数:

技术图片
var (
    modKernel32                  = syscall.NewLazyDLL("kernel32.dll")
    procCloseHandle              = modKernel32.NewProc("CloseHandle")
    procCreateToolhelp32Snapshot = modKernel32.NewProc("CreateToolhelp32Snapshot")
    procProcess32First           = modKernel32.NewProc("Process32FirstW")
    procProcess32Next            = modKernel32.NewProc("Process32NextW")
    procGetCurrentProcessId      = modKernel32.NewProc("GetCurrentProcessId")
)

// Some constants from the Windows API
const (
    ERROR_NO_MORE_FILES = 0x12
    MAX_PATH            = 260
)

// PROCESSENTRY32 is the Windows API structure that contains a process‘s
// information.
type PROCESSENTRY32 struct 
    Size              uint32
    CntUsage          uint32
    ProcessID         uint32
    DefaultHeapID     uintptr
    ModuleID          uint32
    CntThreads        uint32
    ParentProcessID   uint32
    PriorityClassBase int32
    Flags             uint32
    ExeFile           [MAX_PATH]uint16


// Process is an implementation of Process for Windows.
type Process struct 
    pid  int
    ppid int
    exe  string


// NewWindowsProcess convert external to internal process data structure
func NewWindowsProcess(e *PROCESSENTRY32) Process 
    // Find when the string ends for decoding
    end := 0
    for 
        if e.ExeFile[end] == 0 
            break
        
        end++
    
    return Process
        pid:  int(e.ProcessID),
        ppid: int(e.ParentProcessID),
        exe:  syscall.UTF16ToString(e.ExeFile[:end]),
    


// FindProcess will find process by its ID
func FindProcess(pid int) (Process, bool) 
    processes, err := ListProcess()
    if err == nil 
        for _, process := range processes 
            if process.pid == pid 
                return process, true
            
        
    
    return Process, false


// ListProcess returns list of all active system processes
func ListProcess() ([]Process, error) 
    handle, _, _ := procCreateToolhelp32Snapshot.Call(0x00000002, 0)
    if handle < 0 
        return nil, syscall.GetLastError()
    
    defer procCloseHandle.Call(handle)

    entry := PROCESSENTRY32
    entry.Size = uint32(unsafe.Sizeof(entry))
    ret, _, _ := procProcess32First.Call(handle, uintptr(unsafe.Pointer(&entry)))
    if ret == 0 
        return nil, fmt.Errorf("Error retrieving process info.")
    

    results := make([]Process, 0, 50)
    for 
        results = append(results, NewWindowsProcess(&entry))

        ret, _, _ := procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry)))
        if ret == 0 
            break
        
    

    return results, nil


// MapProcess same as ListProcess but returned as map for lookup by processID
func MapProcess() map[int]Process 
    procs, _ := ListProcess()
    list := make(map[int]Process, len(procs))
    for _, proc := range procs 
        list[proc.pid] = proc
    
    return list


// CurrentProcessID return processID of the process that calls this function
func CurrentProcessID() int 
    id, _, _ := procGetCurrentProcessId.Call()
    return int(id)


// IsProcessActive returns true if one of the running processes uses the given executable
func IsProcessActive(exe string) bool 
    processes, err := ListProcess()
    if err == nil 
        for _, process := range processes 
            if process.exe == exe 
                return true
            
        
    
    return false
View Code

 

窗体的布局:

技术图片
    err := MainWindow
        Title:    "xxx后台服务",
        AssignTo: &mw,
        MinSize:  Size600, 400,
        Layout:   VBox,
        MenuItems: []MenuItem
            Menu
                Text: "编辑",
                Items: []MenuItem
                    Separator,
                    Action
                        Text:        "复制控制台",
                        OnTriggered: func()  
                            if err := walk.Clipboard().SetText(outTE.Text()); err == nil 
                                walk.MsgBox(mw, "复制成功", "已复制控制台日志到粘贴板", walk.MsgBoxOK)
                            
                        ,
                    ,
                    Separator,
                    Action
                        Text:        "退出",
                        OnTriggered: func()  mw.Close() ,
                    ,
                ,
            ,
            Menu
                Text: "帮助",
                Items: []MenuItem
                    Separator,
                    Action
                        Text:        "如何使用?",
                        OnTriggered: func() 
                            walk.MsgBox(mw, "使用", "直接点击‘启动’服务即可,服务端口为访问端口,启动参数可不填", walk.MsgBoxIconQuestion)
                        ,
                    ,
                    Action
                        Text:        "无法启动?",
                        OnTriggered: func() 
                            walk.MsgBox(mw, "帮助", "x", walk.MsgBoxIconQuestion)
                        ,
                    ,
                    Action
                        Text:        "版本",
                        OnTriggered: func() 
                            walk.MsgBox(mw, "版本", "xxxV1.0\\nby zhiJiaN 2019-09-25", walk.MsgBoxIconQuestion)
                        ,
                    ,
                ,
            ,
        ,
        Children: []Widget

            TextEditAssignTo: &outTE, ReadOnly: true, VScroll: true,

            Composite
                Layout: HBox,
                Children: []Widget

                    Composite
                        Layout:  VBox,
                        MaxSize: Size300, 60,
                        MinSize: Size100, 60,
                        Children: []Widget
                            Composite
                                MaxSize: Size50, 60,

                                Layout: VBox
                                    Margins: Margins10, 5, 10, 10,
                                ,

                                Children: []Widget

                                    Composite
                                        MaxSize: Size50, 60,

                                        Layout: HBox
                                            Margins: Margins0, 0, 0, 5,
                                        ,
                                        Children: []Widget
                                            Label
                                                Text: "服务端口:",
                                            ,
                                            LineEdit
                                                AssignTo:      &outTEport,
                                                MaxLength:     5,
                                                Row:           1,
                                                MinSize:       Size60, 15,
                                                OnTextChanged: portChanged,
                                                Text:          defaultPort,
                                            ,
                                        ,
                                    ,

                                    Composite
                                        MaxSize: Size50, 60,

                                        Layout: HBox
                                            Margins: Margins0, 0, 0, 5,
                                        ,
                                        Children: []Widget
                                            Label
                                                Text: "启动参数:",
                                            ,
                                            LineEdit
                                                AssignTo:      &outTEext,
                                                Row:           1,
                                                MinSize:       Size160, 15,
                                                OnTextChanged: extChanged,
                                            ,
                                        ,
                                    ,
                                ,
                            ,
                        ,
                    ,
                    PushButton
                        Text:     "启动服务",
                        MinSize:  Size80, 60,
                        MaxSize:  Size80, 60,
                        AssignTo: &startBtn,

                        OnClicked: func() 
                            go startVees()
                        ,
                    ,
                    PushButton
                        Text:     "关闭服务",
                        MinSize:  Size80, 60,
                        MaxSize:  Size80, 60,
                        AssignTo: &closeBtn,
                        OnClicked: func() 
                            stopVees()
                        ,
                    ,
                ,
            ,
        ,
    .Create()
View Code

 

效果如下:

技术图片

 

 

结语:刚开始接触Golang,该篇只是学习Golang的简单笔记,需要详细代码请发邮箱,以上代码及实现方式并没有仔细去考究,欢迎读者指正和提问。

 

以上是关于使用Golang制作一个简单的启动Jar包的桌面程序的主要内容,如果未能解决你的问题,请参考以下文章

jar包的一生

java打jar包的几种方式详解

Github工具分享之快速电脑桌面启动器Wox

linux下jar包的生存与开机自动启动

jar包的各种启动方式总结

JarToexe&安装包的制作