golang 编写 dll 给别的语言调用


就是用 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
    还是有点大的,880KB,纯C 编译的只有48KB,应该是没有包含全部的依赖吧,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:
76
0
The operation completed successfully.
Hello Andy

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)
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值