Golang与C#混合编程的实践探讨

Golang与C#混合编程的实践探讨 我正在开发一个项目,其中有一个设备可以通过供应商提供的C# DLL进行控制。我希望使用Golang处理网络通信,用于与网络中其他PC建立连接。问题是我们无法直接在Golang中使用C# DLL。那么,有哪些可能的方法可以将C#和Golang结合使用?

4 回复

如果你想从DLL中调用某些内容,也可以参考这个资源。

更多关于Golang与C#混合编程的实践探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


https://github.com/matiasinsaurralde/go-dotnet 声称可以实现这一功能。不过我个人没有使用过这个包的经验。

谢谢 😄。我正在寻找逻辑上实现的方法。我也遇到了相同的git仓库,但使用别人的API没什么意义。(会在学习过程中造成瓶颈 😒)

我认为,一种方法可能是使用套接字连接两个程序,即Golang和C#,但我不确定其效率和安全性。

// 代码示例保留原样

在Golang与C#混合编程的场景中,有几种可行的方法可以集成C# DLL与Go代码。以下是具体实现方案:

1. 使用CGO通过C语言桥接

将C# DLL封装为C语言兼容的接口,然后在Go中通过CGO调用。这种方法需要创建一个C# COM组件或使用P/Invoke。

C#端示例(编译为COM组件):

using System;
using System.Runtime.InteropServices;

[ComVisible(true)]
[Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
public interface IDeviceControl
{
    int ConnectDevice(string config);
    void DisconnectDevice();
}

[ComVisible(true)]
[Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]
public class DeviceController : IDeviceControl
{
    public int ConnectDevice(string config)
    {
        // 设备连接逻辑
        return 1;
    }
    
    public void DisconnectDevice()
    {
        // 设备断开逻辑
    }
}

Go端调用示例:

package main

/*
#cgo windows LDFLAGS: -lole32 -loleaut32
#include <windows.h>
#include <oleauto.h>

// C声明COM接口方法
int ConnectDevice(void* ptr, char* config);
void DisconnectDevice(void* ptr);
*/
import "C"
import (
    "unsafe"
)

func main() {
    // 初始化COM
    C.CoInitialize(nil)
    defer C.CoUninitialize()

    // 创建COM实例(需提前注册C# DLL)
    var deviceControl *C.IUnknown
    hr := C.CoCreateInstance(
        &C.CLSID_DeviceController, // 注册的CLSID
        nil,
        C.CLSCTX_ALL,
        &C.IID_IDeviceControl,     // 接口IID
        (*unsafe.Pointer)(unsafe.Pointer(&deviceControl)))
    
    if hr != 0 {
        panic("COM实例创建失败")
    }
    defer deviceControl.Release()

    // 调用设备连接
    config := C.CString("device_config")
    defer C.free(unsafe.Pointer(config))
    result := C.ConnectDevice(unsafe.Pointer(deviceControl), config)
    println("连接结果:", result)
}

2. 通过进程间通信(IPC)

将C#代码编译为独立可执行文件,通过标准输入输出或命名管道与Go进程通信。

C#控制台程序示例:

using System;

class Program
{
    static void Main()
    {
        while (true)
        {
            string command = Console.ReadLine();
            if (command == "connect")
            {
                // 执行设备连接
                Console.WriteLine("connected");
            }
        }
    }
}

Go端IPC调用示例:

package main

import (
    "bufio"
    "os/exec"
)

func main() {
    cmd := exec.Command("DeviceController.exe")
    stdin, _ := cmd.StdinPipe()
    stdout, _ := cmd.StdoutPipe()
    
    cmd.Start()
    
    // 发送命令
    stdin.Write([]byte("connect\n"))
    
    // 读取响应
    reader := bufio.NewReader(stdout)
    response, _ := reader.ReadString('\n')
    println("响应:", response)
}

3. 使用gRPC跨语言通信

将C#功能封装为gRPC服务,Go客户端通过gRPC协议调用。

C# gRPC服务端示例:

using Grpc.Core;
using System.Threading.Tasks;

public class DeviceService : DeviceControl.DeviceControlBase
{
    public override Task<ConnectResponse> Connect(ConnectRequest request, ServerCallContext context)
    {
        // 调用C# DLL设备控制
        return Task.FromResult(new ConnectResponse { Success = true });
    }
}

Go gRPC客户端示例:

package main

import (
    "context"
    "log"
    
    "google.golang.org/grpc"
    pb "path/to/grpc/proto"
)

func main() {
    conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
    client := pb.NewDeviceControlClient(conn)
    
    response, _ := client.Connect(context.Background(), &pb.ConnectRequest{Config: "device_config"})
    println("连接状态:", response.Success)
}

4. 使用Windows API直接加载DLL

通过syscall包直接调用系统API加载DLL(仅适用于简单函数)。

package main

import (
    "syscall"
    "unsafe"
)

func main() {
    dll := syscall.NewLazyDLL("DeviceControl.dll")
    connectProc := dll.NewProc("ConnectDevice")
    
    config := "device_config"
    ret, _, _ := connectProc.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(config))))
    println("返回值:", ret)
}

选择方案时需考虑:

  • CGO方案适合性能要求高的场景,但需要处理COM注册
  • IPC方案实现简单,但通信开销较大
  • gRPC方案适合分布式系统,需要额外定义proto文件
  • 直接DLL加载仅适用于导出函数简单的场景

根据具体设备控制接口的复杂度和性能要求选择最适合的方案。

回到顶部