Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
snail007 committed Dec 27, 2017
1 parent 686f230 commit e3b25a5
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
dist
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,23 @@
# go-wingui
本项目是golang在windows上用户图形界面gui解决方案,本项目可以不用对你的go写的网站程序做任何修改,就可以实现本地化,使用go+html+css+js写windows本地应用
# 使用说明
1.环境要求是windows,理论上xp以上32位,64位都支持。
2.下载程序[go-wingui](https://github.com/snail007/go-wingui/releases/tag/1.0)
3.解压到比如D盘,D:\dist
4.把你的go网站程序(假设名字是app.exe)放入D:\dist\app文件夹
5、假设你的app.exe启动之后,访问链接是http://127.0.0.1:9999/index
编辑D:\dist\launcher.ini,做如下部分修改:
start_exec="app\app.exe"
start_url="http://127.0.0.1:9999/index"
6.配置完毕,双击D:\dist\launcher.exe,即可看到效果了。

**提示:**
如果是go程序,为了避免出现程序启动出现命令行黑框,编译go程序的时候加上参数:go build -ldflags="-H=windowsgui"

# 原理介绍
本项目原理是写了一个本地应用,使用autoit3脚本技术把自带的谷歌浏览器嵌入到应用里面实现了一个“浏览器”,然后“浏览器”里面打开go网站,这样就实现了一个不依赖系统浏览器的独立gui本地应用程序。我们可以使用方便强大的html+css+js完成优美的应用界面,功能可以通过RPC,ajax调用后端go Web服务实现。

# 源码使用
1.克隆源码
git clone https://github.com/snail007/go-wingui.git ./
2.下载绿色版chrome浏览器,解压后把文件夹chrome,放在位置:go-wingui/cef/chrome,chrome.exe的路径应该是:go-wingui/cef/chrome/chrome.exe
3.开发环境完成

2 changes: 2 additions & 0 deletions cef/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.exe
chrome
62 changes: 62 additions & 0 deletions cef/cef.au3
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#RequireAdmin
#NoTrayIcon
#Region
#AutoIt3Wrapper_Icon=favicon.ico
#AutoIt3Wrapper_Res_Fileversion=1.0.0.11
#AutoIt3Wrapper_Res_Fileversion_AutoIncrement=y
#EndRegion
#include <WinApi.au3>
#include <GUIConstantsEx.au3>
#Include <GuiToolBar.au3>
#include <WindowsConstants.au3>
#include <Constants.au3>
#include <AutoItConstants.au3>
#include <TrayConstants.au3>

If UBound($CmdLine) <> 5 Then
Exit(0)
EndIf
Local $url=$CmdLine[1]
Local $title=$CmdLine[2]
Local $width=$CmdLine[3]
Local $height=$CmdLine[4]

Local $flag="[CLASS:Chrome_WidgetWin_1]"
Run("chrome/chrome.exe --chrome-frame --kiosk "&$url&"&w="&$width&"&h="&$height)
WinWait($flag, "")
Opt('wintitlematchmode', 4)
Local $hWnd = WinGetHandle($flag)
Local $pWnd = GUICreate($title,$width,$height)
_WinAPI_SetParent($hWnd,$pWnd)
GUISetState(@SW_SHOW,$pWnd)
Opt("TrayMenuMode",11)
Local $showHide = TrayCreateItem("显示/隐藏")
Local $idExit = TrayCreateItem("退出")
TraySetState($TRAY_ICONSTATE_SHOW) ;
TraySetToolTip($title)
Local Const $sCLSID_TaskbarList = "{56FDF344-FD6D-11D0-958A-006097C9A090}"
Local Const $sIID_ITaskbarList = "{56FDF342-FD6D-11D0-958A-006097C9A090}"
Local Const $sTagITaskbarList = "HrInit hresult(); AddTab hresult(hwnd); DeleteTab hresult(hwnd); ActivateTab hresult(hwnd); SetActiveAlt hresult(hwnd);"
Local $oTaskbarList = ObjCreateInterface($sCLSID_TaskbarList, $sIID_ITaskbarList, $sTagITaskbarList)
$oTaskbarList.HrInit()
$oTaskbarList.DeleteTab($hWnd)
While 1
$msg = GUIGetMsg()
$imsg = TrayGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
GUISetState(@SW_HIDE,$pWnd)
Case $imsg = 0
ContinueLoop
Case $imsg = $idExit
Exit(0)
Case $imsg = $showHide
Local $iState = WinGetState($hWnd)
If BitAND($iState,2) Then
GUISetState(@SW_HIDE,$pWnd)
Else
GUISetState(@SW_SHOW,$pWnd)
WinActivate($pWnd)
EndIf
EndSelect
WEnd
Binary file added cef/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions launcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.exe
Binary file added launcher/cef2go.syso
Binary file not shown.
154 changes: 154 additions & 0 deletions launcher/launcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package main

import (
"fmt"
"net/url"
"os"
"os/exec"
"os/signal"

"path/filepath"
"strings"
"syscall"

ini "github.com/go-ini/ini"
)

type Settings struct {
SrvURL string
Width int32
Height int32
Title string
URL string
AppPid int
LauncherPid int
}

var (
srvPath = ""
cefPath = ""
cfg, err = ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true}, "launcher.ini")
cmdSrv *exec.Cmd
cmdApp *exec.Cmd
cmdCEF *exec.Cmd
wd, _ = os.Getwd()
)

func init() {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
_srv, _ := cfg.Section("").GetKey("srv")
_cef, _ := cfg.Section("").GetKey("cef")
srvPath = _srv.String()
cefPath = _cef.String()
srvPath = filepath.Join(wd, filepath.Join(srvPath))
cefPath = filepath.Join(wd, filepath.Join(cefPath))
}
func main() {
_title, _ := cfg.Section("").GetKey("app_title")
_url, _ := cfg.Section("").GetKey("start_url")
_width, _ := cfg.Section("").GetKey("window_width")
_height, _ := cfg.Section("").GetKey("window_height")

srvURL := startSrv()
//fmt.Print(srvURL)
startApp()
w, _ := _width.Int()
h, _ := _height.Int()
startCEF(srvURL, _title.String(), _url.String(), w, h)
startTray()
Clean()
}
func startTray() {

}
func startCEF(srvURL, title, url0 string, width, height int) {
args := []string{
srvURL + "/load?url=" + url.QueryEscape(url0),
title,
fmt.Sprintf("%d", width),
fmt.Sprintf("%d", height),
}
cmdCEF = exec.Command(cefPath, args...)
cmdCEF.Dir = filepath.Dir(cefPath)
err = cmdCEF.Start()
if err != nil {
fmt.Printf("ERR:%s", err)
killALl()
os.Exit(1)
}
go func() {
cmdCEF.Wait()
killALl()
os.Exit(0)
}()
}
func startSrv() string {
buf := make([]byte, 100)
cmdSrv = exec.Command(srvPath)
cmdSrv.Dir = filepath.Dir(srvPath)
outp, _ := cmdSrv.StdoutPipe()
err := cmdSrv.Start()
if err != nil {
fmt.Printf("ERR:%s", err)
killALl()
os.Exit(1)
}
n, _ := outp.Read(buf)
return fmt.Sprintf("%s", string(buf[:n]))
}

func startApp() {
argsString, _ := cfg.Section("").GetKey("start_exec")
if argsString.String() == "" {
return
}
args := strings.Split(argsString.String(), " ")
app, _ := filepath.Abs(args[0])
if len(args) > 1 {
cmdApp = exec.Command(app, args[1:]...)
} else {
cmdApp = exec.Command(app)
}
cmdApp.Dir = filepath.Dir(app)
err := cmdApp.Start()
if err != nil {
fmt.Printf("ERR:%s", err)
killALl()
os.Exit(1)
}
}
func Clean() {
signalChan := make(chan os.Signal, 1)
cleanupDone := make(chan bool)
signal.Notify(signalChan,
os.Interrupt,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
for _ = range signalChan {
fmt.Println("\nReceived an interrupt, stopping services...")
killALl()
cleanupDone <- true
}
}()
<-cleanupDone
}
func killALl() {
if cmdApp != nil && cmdApp.Process != nil {
fmt.Printf("\nclean app process %d", cmdApp.Process.Pid)
cmdApp.Process.Kill()
}
if cmdSrv != nil && cmdSrv.Process != nil {
fmt.Printf("\nclean srv process %d", cmdSrv.Process.Pid)
cmdSrv.Process.Kill()
}
if cmdCEF != nil && cmdCEF.Process != nil {
fmt.Printf("\nclean cef process %d", cmdCEF.Process.Pid)
cmdCEF.Process.Kill()
}
}
15 changes: 15 additions & 0 deletions launcher/launcher.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#程序标题
app_title="GO-GUI"
#程序初始化的时候,需要调用的程序(可以放在app目录下面),一般是本地网站程序,没有留空
#start_exec="app/myapp.exe"
start_exec=""
#程序初始化的时候,打开的首页
start_url="http://www.baidu.com"
#窗口宽度,单位像素
window_width=800
#窗口高度,单位像素
window_height=500
#内部服务,不要修改
srv="../srv/srv.exe"
#内部服务,不要修改
cef="../cef/cef.exe"
1 change: 1 addition & 0 deletions srv/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.exe
Binary file added srv/cef2go.syso
Binary file not shown.
65 changes: 65 additions & 0 deletions srv/srv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"flag"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"strings"
"time"
)

func main() {
listen := flag.String("b", "127.0.0.1", "binding ip")
flag.Parse()
fmt.Fprintf(os.Stdout, "http://%s:%s", *listen, run_http_server(*listen))
select {}
}

func handler(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if strings.HasPrefix(req.URL.Path, "/load") {
f, _ := os.OpenFile("start.html", os.O_RDONLY, 0666)
c, _ := ioutil.ReadAll(f)
fmt.Fprintf(w, string(c))
} else if strings.HasPrefix(req.URL.Path, "/ping") {
_url := req.URL.Query().Get("url")
err := HTTPGet(_url, 5000)
if err == nil {
fmt.Fprintf(w, "1")
}
fmt.Println(err)
}
}

func run_http_server(ip string) (port string) {
http.HandleFunc("/", handler)
listener, err := net.Listen("tcp", ip+":0")
if err != nil {
panic(err)
}
go func() {
panic(http.Serve(listener, nil))
}()
return fmt.Sprintf("%d", listener.Addr().(*net.TCPAddr).Port)
}

func HTTPGet(URL string, timeout int) (err error) {
tr := &http.Transport{}
var resp *http.Response
var client *http.Client
defer func() {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
tr.CloseIdleConnections()
}()
client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr}
resp, err = client.Get(URL)
if err != nil {
return
}
return
}
89 changes: 89 additions & 0 deletions srv/start.html

Large diffs are not rendered by default.

0 comments on commit e3b25a5

Please sign in to comment.