go语言自带的jsonrpc 不支持http/websocket,而是使用tcp协议,
aardio中增加了一个库 wsock.tcp.jsonClient 支持与go语言进行jsonrpc调用。

先使用go语言编写一个exe文件( 当然你可以把后缀名改为 dll,下面的代码一样可以运行 )
go语言代码如下,注意 go里面{换行写是语法错误 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import (
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)

type Args struct {
X, Y int
}

var listener net.Listener

type Calculator struct{}

func (t *Calculator) Add(args *Args, reply *int) error {
*reply = args.X + args.Y
return nil
}

func (t *Calculator) Exit(args *int, reply *int) error {
listener.Close()
return nil
}

func main() {
cal := new(Calculator)
server := rpc.NewServer()
server.Register(cal)

listener, e := net.Listen("tcp", "localhost:0")
if e != nil {
fmt.Printf("error:%s\n", e)
return
} else {
fmt.Printf("%s\n", listener.Addr().String())
}

for {
if conn, err := listener.Accept(); err != nil {
log.Fatal("error: " + err.Error())
} else {
go server.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
}

假设上面用go语言生成的exe文件名为gotest.exe,并且是放在当前工程目录下,然后我们用下面的 aardio 代码调用这个 gotest.exe 里的go函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio调用go语言演示";right=759;bottom=469)
winform.add(
button={cls="button";text="调用Go语言的Calculator.Add函数";left=382;top=389;right=678;bottom=427;z=5};
edit={cls="edit";left=19;top=12;right=732;bottom=352;edge=1;hscroll=1;multiline=1;vscroll=1;z=1};
editX={cls="edit";text="2";left=109;top=392;right=185;bottom=424;edge=1;z=2};
editY={cls="edit";text="3";left=238;top=392;right=320;bottom=420;edge=1;z=3};
static={cls="static";text="+";left=198;top=395;right=230;bottom=420;align="center";transparent=1;z=4}
)
/*}}*/

import wsock.tcp.jsonClient;
import process.popen;

//gotest.exe是使用go语言编写的exe文件。
var gofile = process.popen("/gotest.exe");
if(!gofile) return winform.msgbox("启动go程序失败");

//读取服务端地址(使用自动分配的空闲端口避免冲突)
var url = gofile.read();
if( !url || string.startWith(url,"error:") ) return winform.msgbox("启动go程序失败");

//连接go语言服务端
var go = wsock.tcp.jsonClient(url)
winform.edit.print("已连接到go程序",url);

winform.button.oncommand = function(id,event){

//调用go语言提供的函数
var rep = go.Calculator.Add({
X = tonumber(winform.editX.text);
Y = tonumber(winform.editY.text);
} )

if( rep[["result"]] ){
winform.edit.print( "调用成功", rep.result )
}
else {
winform.edit.print( rep[["error"]] )
}
}

winform.show()
win.loopMessage();

//通知go程序退出
go.Calculator.Exit(0);

附:调用Go语言编译器例子

1
2
3
4
5
6
7
8
9
10
11
12
13
import console;
import golang;

var go = golang();

go.runCode("/hello.go",`
package main
import "fmt"
func main() {
fmt.Println("hello world")
}`)

console.pause();

最新版golang扩展库已支持自动下载配置Go编译器。
Go最新版已经支持调用生成DLL文件(需要调用gcc),在aardio中可以直接调用Go生成的DLL文件(使用cdecl调用约定),下面是调用Go编译器生成DLL的演示。

下面看演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import console;
import golang;

//创建Go编译器
var go = golang();

//Go源码与字符串都是UTF-8编码,跟aardio一样很方便
string.save("/hello.go",`
package main

import "C"
import "fmt"

//export SayHello
func SayHello(name string) {
fmt.Printf("Go says: %s!\n", name)
}

func main() {
//DLL入口函数,没有也要写个空的
} `)

//编译Go源码生成DLL文件
go.buildShared("/hello.go","/hello.dll");

//加载Go编译的DLL,注意要指定cdecl调用约定
var dll = raw.loadDll("/hello.dll",,"cdecl");

//直接就可以调用DLL里的函数,不用声明
console.open();
var str = "必须在Go函数名前面加上 export 函数名的注释才能导出函数";
dll.SayHello(str,#str);

console.log( n )
console.pause();

Go写DLL要注意一个特别的地方,Go导出函数前必须写一行注释声明导出函数,例如上面的 //export SayHello

Go语言里的字符串GoString是一个结构体,用aardio来表示是这样的:

1
2
3
4
5
6
7
8
class goString{
ctor( str ){
this.s = str;
this.n = #str;
};
string s;
addr n;
}

如果是在API函数里传值,一个GoString展开为2个API参数来表示就可以了(一个字符串,后面跟一个字符串长度)

因为aardio传结构体都是传指针,如果用结构体,在Go里面要声明为指针,示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import console;
import golang;

//创建Go编译器
var go = golang();

//Go源码与字符串都是UTF-8编码,跟aardio一样很方便
string.save("/hello.go",`
package main

import "C"
import "fmt"

//export SayHello
func SayHello(name *string) {
fmt.Printf("Go says: %s!\n", *name)
}

func main() {
//DLL入口函数,没有也要写个空的
} `)

//编译Go源码生成DLL文件
go.buildShared("/hello.go","/hello.dll");

//加载Go编译的DLL,注意要指定cdecl调用约定
var dll = raw.loadDll("/hello.dll",,"cdecl");

class goString{
ctor( str ){
this.s = str;
this.n = #str;
};
string s;
addr n;
}

//直接就可以调用DLL里的函数,不用声明
console.open();
dll.SayHello( goString( "必须在Go函数名前面加上 export 函数名的注释才能导出函数") );

console.log( n )
console.pause();

需要先安装MinGW( GCC )

可以下载安装 MinGW-W64: https://sourceforge.net/projects/mingw-w64 这个只能安装在64位系统。
也可以下载安装 TDM-GCC: http://tdm-gcc.tdragon.net/download 这个提供支持32位、64位安装包。

golang扩展库会自动搜索MinGW,MinGW-W64,TDM-GCC的安装位置,不需要手动配置。
当然也可以调用golang扩展库提供的addPath函数自己添加gcc.exe所在的目录。

Go生成的文件很大,加上-ldflags "-s -w"参数会小一些,go.buildShared() 已经自动加上这些参数。
编译上面的代码生成的DLL只有1MB多一点,而且可以支持WinXP,不需要依赖外部运行库,还是非常不错的。
而且测试了一下,编译的DLL还能内存加载。