环境:
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: Size{600, 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{
- TextEdit{AssignTo: &outTE, ReadOnly: true, VScroll: true},
- Composite{
- Layout: HBox{},
- Children: []Widget{
- Composite{
- Layout: VBox{},
- MaxSize: Size{300, 60},
- MinSize: Size{100, 60},
- Children: []Widget{
- Composite{
- MaxSize: Size{50, 60},
- Layout: VBox{
- Margins: Margins{10, 5, 10, 10},
- },
- Children: []Widget{
- Composite{
- MaxSize: Size{50, 60},
- Layout: HBox{
- Margins: Margins{0, 0, 0, 5},
- },
- Children: []Widget{
- Label{
- Text: "服务端口:",
- },
- LineEdit{
- AssignTo: &outTEport,
- MaxLength: 5,
- Row: 1,
- MinSize: Size{60, 15},
- OnTextChanged: portChanged,
- Text: defaultPort,
- },
- },
- },
- Composite{
- MaxSize: Size{50, 60},
- Layout: HBox{
- Margins: Margins{0, 0, 0, 5},
- },
- Children: []Widget{
- Label{
- Text: "启动参数:",
- },
- LineEdit{
- AssignTo: &outTEext,
- Row: 1,
- MinSize: Size{160, 15},
- OnTextChanged: extChanged,
- },
- },
- },
- },
- },
- },
- },
- PushButton{
- Text: "启动服务",
- MinSize: Size{80, 60},
- MaxSize: Size{80, 60},
- AssignTo: &startBtn,
- OnClicked: func() {
- go startVees()
- },
- },
- PushButton{
- Text: "关闭服务",
- MinSize: Size{80, 60},
- MaxSize: Size{80, 60},
- AssignTo: &closeBtn,
- OnClicked: func() {
- stopVees()
- },
- },
- },
- },
- },
- }.Create()
- View Code
效果如下:
结语: 刚开始接触 Golang, 该篇只是学习 Golang 的简单笔记, 需要详细代码请发邮箱, 以上代码及实现方式并没有仔细去考究, 欢迎读者指正和提问.
来源: http://www.bubuko.com/infodetail-3213711.html