Вызов функций Go из C#
Введение
Недавно предо мной встала задача вызова функций Go из C#. В этом посте я решил поделиться простыми примерами кода, которые возможно вам пригодятся.
Для этого я сгенерировал файл Go dll и вызывал его из C#.
https://t.me/addlist/MUtJEeJSxeY2YTFi – полезная папка для Golang разработчиков.
Вызов со значениями int
Сначала я попробую отправить значение int в качестве аргумента функции Go и получить от нее значение int.
[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);
}
}
[Go] main.go
package main
import "C"
func main() { }
// publish functions by "//export ~"
//export CallInt
func CallInt(num int) int {
return num + 3
}
Создайте файл dll
go build -buildmode=c-shared -o dllsample.dll .
Вызов со строковыми значениями
Поскольку строковый тип C# и строковый тип Go несовместимы, эти скрипты вызовут исключение.
[C#] CallSample.cs
...
[DllImport("dllsample")]
private static extern string CallString(string text);
...
public string CallGoString(string text)
{
return CallString(text);
}
}
[Go] main.go
...
// DON'T DO THIS
//export CallString
func CallString(text string) string {
return fmt.Sprintf("%s World!", text)
}
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[])
Чтобы решить эту проблему, я должен использовать “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) ?? "";
}
}
[Go] main.go
...
//export CallString
func CallString(text *C.char) *C.char {
gs := C.GoString(text)
return C.CString(fmt.Sprintf("%s World!", gs))
}
- cgo common – cmd/cgo – Go Packages
- How to call go from c with string (char *) as the parameter without making a copy – GitHub
- GoからCのライブラリを呼ぶ – Qiita
Вызов с помощью массивов
Чтобы отправить массив int в функцию Go, я должен преобразовать его в IntPtr.
А чтобы получить массив int из функции Go, я должен преобразовать его из 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]}");
}
}
...
[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)
}
@csharp_ci – C# телеграм канал
+1
+1
1
+1
+1
+1