Golang与DLL交互

29 篇文章 0 订阅

Golang与DLL交互

在系统级编程中,经常需要使用C/C++来编写模块给Go调用,目前官方支持这种调用,但是需要CGO做支撑。
这里研究了Golang与DLL进行数据交换的几种方式,(重点是:指针,结构体,回掉函数)并做以测试。

依赖条件:

  • C接口形式的DLL(这里使用了VS2015编写)
  • CGO依赖的GCC(这里使用Qt5.9自带的Minigw)

1. DLL代码

#include "stdafx.h"
#include <stdio.h>

#ifndef TM_ROBOT_API
#define TM_ROBOT_API extern "C" __declspec(dllexport) 
#else
#define TM_ROBOT_API extern "C" __declspec(dllimport) 
#endif

TM_ROBOT_API void fun1(int* pVal) {
    char buff[128];
    sprintf(buff, "fun1, val: %d -> 999\n", *pVal);
    OutputDebugStringA(buff);
    *pVal = 999;    
}

struct MyStruct
{
    int nVal1;
    float nVal2;
};

typedef void(*CB_MY)(float nVal, float fVal);

TM_ROBOT_API void fun2(MyStruct* pVal) {
    char buff[128];
    sprintf(buff, "fun2, val: 1->%d, 2->%.4f -> 1,2\n", pVal->nVal1, pVal->nVal2);
    OutputDebugStringA(buff);
    pVal->nVal1 = 1;
    pVal->nVal2 = 2.2;
}

TM_ROBOT_API void fun3(CB_MY pFun) {
    char buff[128];
    sprintf(buff, "fun3, val: %08X -> call it 10s\n", pFun);
    OutputDebugStringA(buff);
    for (int i=80;i<100;i++)
    {
        pFun(i*3.3, i*1.1);
        Sleep(10);
    }
}

注意,这里的调用约定为extern "C" __declspec(dllexport)

2. Golang代码

package main

/*
// 依赖GCC
typedef struct _MyStruct
{
    int nVal1;
    float nVal2;
}MyStruct;

*/
import "C"
import (
    "fmt"
    "syscall"
    "unsafe"
)

func cb_my(val1 float32, val2 float32) int32 {
    fmt.Println("cb_my:", val1, val2)
    return 0
}

func main() {
    Handle := syscall.NewLazyDLL("gocTest.dll")
    Fun3 := Handle.NewProc("fun3")
    Fun2 := Handle.NewProc("fun2")
    Fun1 := Handle.NewProc("fun1")
    var aa int32 = 10 // var aa C.int = 10
    Fun1.Call(uintptr(unsafe.Pointer(&aa)))
    fmt.Println("########", aa)
    var arg1 C.MyStruct // 使用Go的Struct会有问题
    arg1.nVal1 = 11
    arg1.nVal2 = 22.22
    Fun2.Call(uintptr(unsafe.Pointer(&arg1)))
    fmt.Println("************", arg1)
    var fn = syscall.NewCallbackCDecl(cb_my) // 注意调用约定
    Fun3.Call(fn)
    fmt.Println("============")
}

3. 总结

  1. fun1做简单的测试就能通过,如果时float等其他类型,尽量使用CGO模块类型。
  2. fun2如果使用Golang的结构体,除了int32之外的参数都有问题,需要CGO模块类型。
  3. fun3特别注意NewCallbackCDecl/NewCallback与回掉函数的约定,否则会出很多异常。

其他高阶混合编程,这里不做涉及。

参考

https://studygolang.com/articles/1424
http://lib.csdn.net/article/go/33766

类型转换对照表:

char -->  C.char -->  byte
signed char -->  C.schar -->  int8
unsigned char -->  C.uchar -->  uint8
short int -->  C.short -->  int16
short unsigned int -->  C.ushort -->  uint16
int -->  C.int -->  int
unsigned int -->  C.uint -->  uint32
long int -->  C.long -->  int32 or int64
long unsigned int -->  C.ulong -->  uint32 or uint64
long long int -->  C.longlong -->  int64
long long unsigned int -->  C.ulonglong -->  uint64
float -->  C.float -->  float32
double -->  C.double -->  float64
wchar_t -->  C.wchar_t  -->  
void * -> unsafe.Pointer
### 回答1: 你可以使用 `os/exec` 包来在 Go 中实现与 Shell 的交互。下面是一个简单的示例代码: ```go package main import ( "bufio" "fmt" "os/exec" ) func main() { cmd := exec.Command("sh") stdin, err := cmd.StdinPipe() if err != nil { panic(err) } stdout, err := cmd.StdoutPipe() if err != nil { panic(err) } scanner := bufio.NewScanner(stdout) if err := cmd.Start(); err != nil { panic(err) } go func() { defer stdin.Close() stdin.Write([]byte("echo hello world\n")) }() for scanner.Scan() { fmt.Println(scanner.Text()) } if err := cmd.Wait(); err != nil { panic(err) } } ``` 这个例子创建了一个 `sh` 进程,并通过管道向其输入了一个命令 `echo hello world`,然后读取了输出并打印到控制台。你可以根据自己的需求修改这个例子。 ### 回答2: golang的Shell交互是指在Golang程序中执行Shell命令并与其进行交互的功能。 在Golang中实现Shell交互可以使用os/exec包。该包提供了执行Shell命令的函数和方法,可以通过它们来执行Shell命令并获取命令的标准输出、标准错误和执行结果。 首先,我们需要引入os/exec包。然后,使用exec.Command函数创建一个Cmd类型的对象,该对象表示要执行的Shell命令。可以通过Cmd对象的方法设置命令的参数、工作目录等。 接下来,使用Cmd对象的CombinedOutput方法来执行Shell命令,并获取命令的输出结果。CombinedOutput方法会返回一个字节数组,其中存储了命令的标准输出和标准错误。 最后,我们可以将字节数组转换为字符串,并进行相应的处理,如打印输出或进一步解析。 以下是一个简单的示例代码,演示了如何在Golang中实现Shell交互的功能: ``` package main import ( "fmt" "log" "os/exec" ) func main() { cmd := exec.Command("ls", "-l") // 创建Cmd对象,执行ls -l命令 output, err := cmd.CombinedOutput() // 执行命令并获取输出结果 if err != nil { log.Fatal(err) // 如果执行命令出错,打印错误信息并退出 } fmt.Println(string(output)) // 将字节数组转换为字符串并打印输出 } ``` 以上代码会执行`ls -l`命令,并将结果打印到控制台。 需要注意的是,在使用Golang执行Shell命令时,需要保证可执行的命令位于环境变量`$PATH`所指定的路径中,否则需要指定完整的命令路径。示例中的`ls`命令是一个常见的Shell命令,因此不需要指定路径。 总结来说,通过使用os/exec包,我们可以在Golang程序中实现Shell交互的功能,从而执行Shell命令,并获取其输出结果。 ### 回答3: Go语言本身并不提供直接的Shell交互功能,但我们可以使用一些第三方库来实现类似的功能。 一个常用的库是“os/exec”,它允许我们执行外部 Shell 命令并与其进行交互。下面是一个简单的示例代码: ```go package main import ( "bufio" "fmt" "os" "os/exec" ) func main() { // 创建一个命令对象 cmd := exec.Command("/bin/sh") // 获取命令的标准输入和输出管道 stdin, _ := cmd.StdinPipe() stdout, _ := cmd.StdoutPipe() // 启动命令 cmd.Start() // 准备一个输入读取器,用于读取用户输入 reader := bufio.NewReader(os.Stdin) for { // 读取用户输入 input, _ := reader.ReadString('\n') // 将用户输入写入命令的标准输入管道 stdin.Write([]byte(input)) // 读取命令的标准输出 output := make([]byte, 1024) stdout.Read(output) // 打印输出结果 fmt.Println(string(output)) } } ``` 这段代码会启动一个新的Shell,并不断读取用户的输入并将其发送给Shell的标准输入,然后读取Shell的标准输出并打印出来。 请注意,这里的示例代码没有处理错误情况,为了简单起见,省略了错误处理部分。在实际应用中,我们应该对错误进行适当处理。 希望这个示例能够帮助你实现Go语言下的Shell交互功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值