为 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 比将两个数字相加要复杂得多。