为 Linux 中的 Mono/Gtk# 代码实现来自 GStreamer 的回调
Implementing callbacks from GStreamer for Mono/Gtk# code in Linux
我正在 Debian 下使用 Mono 并实现一个简单的媒体播放器(基于 Gstreamer SDK tutorial #5)。
与该教程不同,我正在使用 c#/Gtk# 并尝试进行我自己的 Interop 绑定以使用 GStreamer 库。
现在,我可以调用 GStreamer 和其他库中的简单函数,使用如下:
[DllImport("libc.so")]
public static extern int getpid ();
[DllImport("libgstreamer-1.0.so")]
public static extern bool gst_uri_is_valid (String uri);
这一切都很棒。但是,我不确定如何处理回调函数,需要将信号连接到我自己的代码。具体来说,g_signal_connect_data()
函数需要几个回调地址来向我的代码传递信息。
如果代码是 C 而不是 C#,那没有问题,我会简单地使用 &function
作为回调地址传递。但是,鉴于代码是在 C# 中,允许回调的最佳进程(或任何进程,真的)是什么?
事实证明,C# 互操作性实际上为此目的提供了委托,您可以获得这些委托的函数指针来处理非托管代码。
举个例子,让我们考虑一个人为的案例,您有一些 C 代码用于回调 C# 以便将两个数字相加。
我们将在 C 中提供一个函数,它将接收回调地址和两个数字:
typedef int (callbackFn)(int, int);
int addViaCallBack (callbackFn fn, int a, int b) {
return fn (a, b);
}
您可以使用以下命令从该文件创建一个库:
gcc -fpic -c -o addViaCallback.o addViaCallback.c
gcc -shared -o addViaCallback.so addViaCallback.o
然后您将有一些可以从 C# 调用的东西,它将回调 到 C# 来完成它的工作。在 C# 方面,它基本上包括:
- 正在创建
DllImport
绑定以便您可以调用 C 函数。
- 为该 C 函数创建要回调的 C# 回调函数。
- 创建委托,然后为该回调创建函数指针。
- 使用函数指针和数字调用 C 函数。
以下 C# 代码显示了如何执行此操作:
using System;
using System.Runtime.InteropServices;
namespace callbacktest {
class MainClass {
[DllImport("addViaCallback.so",
CallingConvention = CallingConvention.Cdecl)]
public static extern int addViaCallback (IntPtr cb, int n1, int n2);
public static int CallbackFn (int a, int b) {
return a + b;
}
public delegate int AddViaCallbackDelegate (int a, int b);
public static void Main (string[] args) {
IntPtr addViaCallbackFp = Marshal.GetFunctionPointerForDelegate(
new AddViaCallbackDelegate (MainClass.CallbackFn));
for (int a = 0; a < 5; a++) {
for (int b = 0; b < 5; b++) {
int c = addViaCallback (addViaCallbackFp, a, b);
Console.WriteLine (a + " + " + b + " = " + c);
}
}
}
}
}
C# 代码调用 C 函数,传递 C# 函数回调和要相加的两个数字。然后,该 C 函数使用这两个数字调用 C# 回调函数。回调函数将数字和 returns 结果添加到 C 函数,后者又将 returns 添加到 C#(1).
C# C
|------------------| |--------------------|
| Calls C function | | |
| with (callback, | | |
| n1, n2). |___ | |
| | \ | |
| | \___\| |
| | /| Receives from C# |
| | | callback, n1, n2) |
| | | then calls back to |
| | ___| C# with (n1, n2). |
| | / | |
| |/___/ | |
| Receives (n1, |\ | |
| n2), adds them, | | |
| and returns sum. |___ | |
| | \ | |
| | \___\| |
| | /| Receives sum, |
| | ___| passes it back. |
| | / | |
| |/___/ | |
| Receives sum. |\ | |
|------------------| |--------------------|
(1) 没关系,如果一开始只是在 C# 中添加数字,那将是 无限 .这只是为了提供一个简单的示例 - GStreamer far 比将两个数字相加要复杂得多。
我正在 Debian 下使用 Mono 并实现一个简单的媒体播放器(基于 Gstreamer SDK tutorial #5)。
与该教程不同,我正在使用 c#/Gtk# 并尝试进行我自己的 Interop 绑定以使用 GStreamer 库。
现在,我可以调用 GStreamer 和其他库中的简单函数,使用如下:
[DllImport("libc.so")]
public static extern int getpid ();
[DllImport("libgstreamer-1.0.so")]
public static extern bool gst_uri_is_valid (String uri);
这一切都很棒。但是,我不确定如何处理回调函数,需要将信号连接到我自己的代码。具体来说,g_signal_connect_data()
函数需要几个回调地址来向我的代码传递信息。
如果代码是 C 而不是 C#,那没有问题,我会简单地使用 &function
作为回调地址传递。但是,鉴于代码是在 C# 中,允许回调的最佳进程(或任何进程,真的)是什么?
事实证明,C# 互操作性实际上为此目的提供了委托,您可以获得这些委托的函数指针来处理非托管代码。
举个例子,让我们考虑一个人为的案例,您有一些 C 代码用于回调 C# 以便将两个数字相加。
我们将在 C 中提供一个函数,它将接收回调地址和两个数字:
typedef int (callbackFn)(int, int);
int addViaCallBack (callbackFn fn, int a, int b) {
return fn (a, b);
}
您可以使用以下命令从该文件创建一个库:
gcc -fpic -c -o addViaCallback.o addViaCallback.c
gcc -shared -o addViaCallback.so addViaCallback.o
然后您将有一些可以从 C# 调用的东西,它将回调 到 C# 来完成它的工作。在 C# 方面,它基本上包括:
- 正在创建
DllImport
绑定以便您可以调用 C 函数。 - 为该 C 函数创建要回调的 C# 回调函数。
- 创建委托,然后为该回调创建函数指针。
- 使用函数指针和数字调用 C 函数。
以下 C# 代码显示了如何执行此操作:
using System;
using System.Runtime.InteropServices;
namespace callbacktest {
class MainClass {
[DllImport("addViaCallback.so",
CallingConvention = CallingConvention.Cdecl)]
public static extern int addViaCallback (IntPtr cb, int n1, int n2);
public static int CallbackFn (int a, int b) {
return a + b;
}
public delegate int AddViaCallbackDelegate (int a, int b);
public static void Main (string[] args) {
IntPtr addViaCallbackFp = Marshal.GetFunctionPointerForDelegate(
new AddViaCallbackDelegate (MainClass.CallbackFn));
for (int a = 0; a < 5; a++) {
for (int b = 0; b < 5; b++) {
int c = addViaCallback (addViaCallbackFp, a, b);
Console.WriteLine (a + " + " + b + " = " + c);
}
}
}
}
}
C# 代码调用 C 函数,传递 C# 函数回调和要相加的两个数字。然后,该 C 函数使用这两个数字调用 C# 回调函数。回调函数将数字和 returns 结果添加到 C 函数,后者又将 returns 添加到 C#(1).
C# C
|------------------| |--------------------|
| Calls C function | | |
| with (callback, | | |
| n1, n2). |___ | |
| | \ | |
| | \___\| |
| | /| Receives from C# |
| | | callback, n1, n2) |
| | | then calls back to |
| | ___| C# with (n1, n2). |
| | / | |
| |/___/ | |
| Receives (n1, |\ | |
| n2), adds them, | | |
| and returns sum. |___ | |
| | \ | |
| | \___\| |
| | /| Receives sum, |
| | ___| passes it back. |
| | / | |
| |/___/ | |
| Receives sum. |\ | |
|------------------| |--------------------|
(1) 没关系,如果一开始只是在 C# 中添加数字,那将是 无限 .这只是为了提供一个简单的示例 - GStreamer far 比将两个数字相加要复杂得多。