DEV Community

Masui Masanori
Masui Masanori

Posted on • Updated on

Calling Go functions from C#

Intro

This time, I will call some Go functions from C#.
To do this, I should generate a Go dll file and call it from C#.

Calling with int values

First, I will try sending an int value as an argument of a Go funtion and receiving int value from it.

[C#] CallSample.cs

using System.Runtime.InteropServices;

namespace CallDllSample;

public class CallSample
{
    [DllImport("dllsample")]
    private static extern int CallInt(int num);

    public int CallGoInt(int num)
    {
        return CallInt(num);
    }
}
Enter fullscreen mode Exit fullscreen mode

[Go] main.go

package main

import "C"

func main() { }

// publish functions by "//export ~"
//export CallInt
func CallInt(num int) int {
    return num + 3
}
Enter fullscreen mode Exit fullscreen mode

Build a dll file

go build -buildmode=c-shared -o dllsample.dll .
Enter fullscreen mode Exit fullscreen mode

Calling with string values

Because C#'s string type and Go's string type are not compatible, these codes cause an exception.

[C#] CallSample.cs

...
    [DllImport("dllsample")]
    private static extern string CallString(string text);
...
    public string CallGoString(string text)
    {
        return CallString(text);
    }
}
Enter fullscreen mode Exit fullscreen mode

[Go] main.go

...
// DON'T DO THIS
//export CallString
func CallString(text string) string {
    return fmt.Sprintf("%s World!", text)
}
Enter fullscreen mode Exit fullscreen mode

Result

Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
   at CallDllSample.CallSample.CallString(System.String)
--------------------------------
   at CallDllSample.CallSample.CallGoString(System.String)
   at Program.<Main>$(System.String[])
Enter fullscreen mode Exit fullscreen mode

To resolve that, I have to use "C.char".

[C#] CallSample.cs

...
    [DllImport("dllsample")]
    private static extern IntPtr CallString(string text);
...
    public string CallGoString(string text)
    {
        var result = CallString(text);
        Console.WriteLine(result);
        return Marshal.PtrToStringAnsi(result) ?? "";
    }
}
Enter fullscreen mode Exit fullscreen mode

[Go] main.go

...
//export CallString
func CallString(text *C.char) *C.char {
    gs := C.GoString(text)
    return C.CString(fmt.Sprintf("%s World!", gs))
}
Enter fullscreen mode Exit fullscreen mode

Calling with arrays

To send int array to the Go function, I should convert it to IntPtr.
And to receive int array from the Go function, I should convert from IntPtr.

[C#] CallSample.cs

...
    public void CallGoArray()
    {
        // Convert from C# int array to IntPtr
        var nums = new int[]{ 4, 2, 5, 8 };
        IntPtr intPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * nums.Length);
        Marshal.Copy(nums, 0, intPtr, nums.Length);
        var pointerResult = CallArray(intPtr, nums.Length);

        // Convert from IntPtr to C# int array
        var results = new int[nums.Length];
        Marshal.Copy(pointerResult, results, 0, results.Length);

        for(var i = 0; i < results.Length; i++)
        {
            Console.WriteLine($"From Go Index: {i} Value: {results[i]}");
        }
    }
...
Enter fullscreen mode Exit fullscreen mode

[Go] main.go

...
//export CallArray
func CallArray(values *C.int, length C.int) *C.int {
    // Convert from C int array to Go int array
    cInts := (*[1 << 30]C.int)(unsafe.Pointer(values))[:length:length]

    goResults := make([]int, int(length))
    for i, v := range cInts {
        goResults[i] = int(v)
        log.Printf("From C# Index: %d Value: %d", int(i), int(v))
    }
    // Convert from Go int array to C int array
    results := C.malloc(C.size_t(length) * C.size_t(unsafe.Sizeof(uintptr(0))))
    pointerResult := (*[1 << 30]C.int)(results)
    for i := 0; i < int(length); i++ {
        pointerResult[i] = C.int(goResults[i] + 2)  
    }
    return (*C.int)(results)
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)