从C#调用Golang函数的实现方法

从C#调用Golang函数的实现方法 我正在尝试从C#调用一个Go语言编写的DLL,并将其结果和性能与从C#调用C语言编写的DLL进行比较,因此我进行了以下操作:

我首先从构建C语言的DLL并调用它开始。 第一步:编写C代码

// cmdll.c
// 编译选项: -LD
int __declspec(dllexport) SampleMethod(int i)
{
  return i*10;
}

第二步:编译C代码:

  • 打开 Visual Studio x64 Native Tools Command Prompt
  • 运行命令:cl -LD cmdll.c

第三步:编写C#代码

// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
    [DllImport("Cmdll.dll")]
    public static extern int SampleMethod(int x); // 函数签名,必须有返回类型

    static void Main()
    {
        Console.WriteLine("SampleMethod() returns {0}.", SamplMethod(5));
    }
}

第四步:编译C#文件并构建可执行文件:

  • 打开 Visual Studio x64 Native Tools Command Prompt
  • 运行命令:csc -platform:x64 cm.cs

以上步骤运行顺利。

我想用Go语言实现同样的功能,并遵循了以下步骤:

第一步:编写Go代码:

//lib.go
package main

import "C"

//export SamplMethod
func SamplMethod(i int) int {
	return i * 10
}

func main() {
	// 需要一个main函数来让CGO将包编译为C共享库
}

第二步:通过编译上述代码来构建DLL文件:

go build -ldflags="-s -w" -o lib.dll -buildmode=c-shared lib.go

我使用了 -ldflags="-s -w" 来减小生成的文件大小,并且不确定应该使用哪个 -buildmode,所以随机选择了 c-shared 而不是 c-archive。 更新:我也尝试了 go build -ldflags="-s -w" -o lib.dll -buildmode=c-archive lib.go,得到了相同的结果。

第三步:编写一个C代码,将 go 生成的 .dll.h 文件结合起来,生成一个等效的 c dll

//goDll.c
#include <stdio.h>
#include "lib.h"
// 强制gcc链接Go运行时(可能有比这更好的解决方案)
GoInt SamplMethod(GoInt i);

void main() {
}

第四步:编译goDll.c文件:

gcc -shared -pthread -o goDll.dll goDll.c lib.dll -lWinMM -lntdll -lWS2_32

第五步:构建C#代码来调用生成的DLL,代码与上面相同,但更改了DLL文件名:

// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
    [DllImport("goDll.dll")]
    public static extern int SampleMethod(int x); // 函数签名,必须有返回类型

    static void Main()
    {
        Console.WriteLine("SampleMethod() returns {0}.", SamplMethod(5));
    }
}

第四步:编译C#文件并构建可执行文件:

  • 打开 Visual Studio x64 Native Tools Command Prompt
  • 运行命令:csc -platform:x64 cm.cs

然后尝试运行生成的 ./cm.exe 文件,但遇到了以下错误:

Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'SampleMethod' in DLL 'goDll.dll'.
   at MainClass.SampleMethod(Int32 i)
   at MainClass.Main()

更多关于从C#调用Golang函数的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

我发现自己在过程中多做了一个不必要的步骤,以下是我顺利运行的方法:

步骤1:编写Go代码:

// main.go
package main

import "C"
import "fmt"

//export HelloWorld
func HelloWorld() {
	fmt.Printf("hello world from GO\n")
}

func main() {}

// 编译命令:
// go build -ldflags="-s -w" -buildmode=c-shared -o libgo.dll main.go

步骤2:编写C#代码:

// main.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
    [DllImport("libgo.dll")]
    public static extern void HelloWorld(); // 函数签名,必须有返回类型

    static void Main()
    {
        HelloWorld();
    }
}

// 编译命令:
// 打开:
// Visual Studio x64 Native Tools Command Prompt
// csc -platform:x64 cm.cs

步骤3:编译两个文件,首先编译go文件

步骤4:运行可执行文件:

image

更多关于从C#调用Golang函数的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


从C#调用Go函数需要直接使用Go生成的DLL,而不是通过C包装层。以下是正确的实现方法:

1. 编写Go代码

// lib.go
package main

import "C"

//export SampleMethod
func SampleMethod(i C.int) C.int {
    return i * 10
}

func main() {
    // 空main函数,仅用于编译
}

关键点

  • 使用C.int类型而不是Go的int类型
  • 导出函数名必须与C#中调用的名称完全一致

2. 编译Go DLL

go build -o goLib.dll -buildmode=c-shared lib.go

这会生成两个文件:goLib.dllgoLib.h

3. 直接C#调用Go DLL

// Program.cs
using System;
using System.Runtime.InteropServices;

public class MainClass
{
    // 直接调用Go生成的DLL
    [DllImport("goLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int SampleMethod(int x);

    static void Main()
    {
        try
        {
            int result = SampleMethod(5);
            Console.WriteLine($"SampleMethod() returns {result}.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

4. 编译和运行

# 编译C#
csc -platform:x64 Program.cs

# 运行(确保goLib.dll在同一目录)
./Program.exe

性能对比示例

如果你需要对比C和Go的性能,可以这样实现:

// benchmark.go
package main

import "C"

//export Fibonacci
func Fibonacci(n C.int) C.int {
    if n <= 1 {
        return n
    }
    return Fibonacci(n-1) + Fibonacci(n-2)
}

//export ProcessArray
func ProcessArray(arrPtr *C.int, length C.int) {
    // 处理数组的逻辑
    arr := (*[1 << 30]C.int)(unsafe.Pointer(arrPtr))[:length:length]
    for i := C.int(0); i < length; i++ {
        arr[i] = arr[i] * 2
    }
}

func main() {}

对应的C#调用:

[DllImport("goLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Fibonacci(int n);

[DllImport("goLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void ProcessArray(IntPtr array, int length);

关键注意事项

  1. 调用约定:必须使用CallingConvention.Cdecl
  2. 类型匹配:Go中的C.int对应C#的int
  3. 内存管理:复杂类型需要手动管理内存
  4. 线程安全:Go的runtime需要初始化,确保单线程初始化调用

你的问题在于通过C语言进行了不必要的包装层。Go可以直接生成C兼容的DLL,C#可以直接调用。移除中间C包装步骤,直接使用Go生成的DLL即可解决EntryPointNotFoundException错误。

回到顶部