文章目录
就是用 go 来写 输入输出是 C 语言类型的函数
// func.go
package main
import "C"
import "fmt"
//export Add
func Add(a C.int, b C.int) C.int {
return a + b
}
//export Print
func Print(s *C.char) {
/*
函数参数可以用 string, 但是用*C.char更通用一些。
由于string的数据结构,是可以被其它go程序调用的,
但其它语言(如 python)就不行了
*/
print("Hello ", C.GoString(s)) //这里不能用fmt包,会报错,调了很久...
}
func main() {
}
go build -ldflags "-s -w" -buildmode=c-shared -o func.dll func.go
Go 调用
package main
import (
"fmt"
"syscall"
)
func main() {
dll := syscall.NewLazyDLL("func.dll")
add := dll.NewProc("Add")
prt := dll.NewProc("Print")
r, err, msg := add.Call(32, 44)
fmt.Println(r)
fmt.Println(err)
fmt.Println(msg)
name := C.CString("Andy")
prt.Call(uintptr(unsafe.Pointer(name)))
}
out:
Python 调用
from ctypes import CDLL, c_char_p
dll = CDLL("func.dll")
dll.Add(32, 33)
dll.Print(c_char_p(bytes("Andy", "utf8")))
C++调用
#include <iostream>
#include <windows.h>
using namespace std;
typedef int(*pAdd)(int a, int b);
typedef void(*pPrt)(char* s);
int main(int argc, char *argv[])
{
HMODULE dll= LoadLibraryA("func.dll");
pAdd add = (pAdd)GetProcAddress(dll, "Add");
pPrt prt = (pPrt)GetProcAddress(dll, "Print");
cout << add(321, 33) << endl;
prt("Andy");
FreeLibrary(dll);
return 0;
}
进阶:传递结构体、数组参数
// go 1.14
// 这段程序用来解析一个 xml 文件,编译成 dll 后,其它语言可调用它来读取 xml
// go 来解析 xml, 处理结果是标准 C 语言数据类型
package main
// 定义 C 结构体
/*
typedef struct {
long long ts;
int mtid;
int blocks;
int blockps;
} VphHeader;
typedef struct {
int id;
char name[64];
char typ[5];
int bps;
int spb;
double scale;
char pq[32];
char pqs[2];
char unit[4];
double min;
double avg;
double max;
double rms;
} VphChannel;
*/
import "C"
import (
"encoding/xml"
"io/ioutil"
"os"
"unsafe"
)
// go 结构体,用来装载解析后的 xml 内容
type VphXML struct{
Timestamp int64 `xml:"Measurement>Timestamp"`
MeasuringTaskID int `xml:"Measurement>MeasuringTaskID"`
Blocks int `xml:"Measurement>Blocks"`
BlocksPerSecond int `xml:"Measurement>BlocksPerSecond"`
Channels []VphChannel `xml:"Measurement>Channels>Channel"`
}
type VphChannel struct {
ID int
Name string
Type string
BytesPerSample int
SamplesPerBlock int
Scale float64
PhysicalQuantity string
PhysicalQuantitySymbol string
Unit string
Minimum float64
Average float64
Maximum float64
RMS float64
}
func (vph *VphXML) Load(file string) error {
fp, err := os.Open(file)
if err != nil {
return err
}
defer fp.Close()
data, err := ioutil.ReadAll(fp)
if err != nil {
return err
}
err = xml.Unmarshal(data, &vph)
if err != nil {
return err
}
return nil
}
// 将 go 字符串转成 C.char 数组
func toCharArray(size int, dst* C.char, src string) {
for j := 0; j < size; j++ {
*dst = C.char(src[j])
// 指针移动 1,相当于 dst++
dst = (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(dst)) + unsafe.Sizeof(C.char(0))))
}
}
//export vph_header
func vph_header(vphFile *C.char, h *C.VphHeader) C.int {
var vph VphXML
err := vph.Load(C.GoString(vphFile))
if err != nil {
print(err.Error())
return -1
}
// 将结果写入 C 结构体
h.ts = C.longlong(vph.Timestamp)
h.mtid = C.int(vph.MeasuringTaskID)
h.blocks = C.int(vph.Blocks)
h.blockps = C.int(vph.BlocksPerSecond)
return C.int(len(vph.Channels))
}
//export vph_channels
func vph_channels(vphFile *C.char, size int, chs* C.VphChannel) C.int {
var vph VphXML
err := vph.Load(C.GoString(vphFile))
if err != nil {
print(err.Error())
return -1
}
for i := 0; i < size; i++ {
chs.id = C.int(vph.Channels[i].ID)
toCharArray(len(vph.Channels[i].Name), &(chs.name[0]), vph.Channels[i].Name)
toCharArray(len(vph.Channels[i].Type), &(chs.typ[0]), vph.Channels[i].Type)
chs.bps = C.int(vph.Channels[i].BytesPerSample)
chs.spb = C.int(vph.Channels[i].SamplesPerBlock)
chs.scale = C.double(vph.Channels[i].Scale)
toCharArray(len(vph.Channels[i].PhysicalQuantity), &(chs.pq[0]), vph.Channels[i].PhysicalQuantity)
toCharArray(len(vph.Channels[i].PhysicalQuantitySymbol), &(chs.pqs[0]), vph.Channels[i].PhysicalQuantitySymbol)
toCharArray(len(vph.Channels[i].Unit), &(chs.unit[0]), vph.Channels[i].Unit)
chs.min = C.double(vph.Channels[i].Minimum)
chs.avg = C.double(vph.Channels[i].Average)
chs.max = C.double(vph.Channels[i].Maximum)
chs.rms = C.double(vph.Channels[i].RMS)
// 指针向后移动 1,相当于 chs++
chs = (*C.VphChannel)(unsafe.Pointer(uintptr(unsafe.Pointer(chs)) + unsafe.Sizeof(C.VphChannel{})))
}
return 0
}
func main() {
}
编译完居然有 1.9 M
接下来使用 Python 调用这个 dll
from ctypes import *
class VphHeader(Structure):
_fields_ = [
("ts", c_longlong),
("mtid", c_int),
("blocks", c_int),
("blockps", c_int)
]
class VphChan(Structure):
_fields_ = [
("id", c_int),
("name", (c_char*64)),
("typ", (c_char*5)),
("bps", c_int),
("spb", c_int),
("scale", c_double),
("pq", (c_char*32)),
("pqs", (c_char*2)),
("unit", (c_char*4)),
("min", c_double),
("avg", c_double),
("max", c_double),
("rms", c_double)
]
vph_file = c_char_p(b"./testfile.xml")
dll = CDLL("./xml_parser.dll")
m = VphHeader()
dll.vph_header.restype = c_int
ch_count = dll.vph_header(vph_file, byref(m))
print(m.ts, m.mtid, m.blocks, m.blockps, ch_count)
# 声明 VphChan 结构体数组
chs = (VphChan * ch_count)()
dll.vph_channels.restype = c_int # 设置函数返回类型
dll.vph_channels(vph_file, ch_count, byref(chs))
for i in range(ch_count):
print(chs[i].id, chs[i].name, chs[i].typ, chs[i].bps, chs[i].spb)