如何将 C# 中的方法指针传递给 C 库?
How to pass a method pointer in C# to C library?
有谁知道如何将 C# 中的方法强制转换为 void* 成员?
在下面的示例中,sigaction.sa_handler 是一个指定回调函数的 void* 成员。
环境:
- VisualStudio 专业版 2019 16.9.4
- dotnet 核心 SDK 3.1
- Ubuntu 在 WSL
- Tmds.Linux 0.5.0
sigaction sigact = new sigaction();
//Error CS0428 Cannot convert method group 'catcher' to non-delegate type 'void*'.Did you intend to invoke the method?
sigact.sa_handler = catcher;
//Error CS0030 Cannot convert type 'method' to 'void*'
sigact.sa_handler = (void *)catcher;
//Error CS0029 Cannot implicitly convert type 'SignalCallback' to 'void*'
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = callback;
//Error CS0030 Cannot convert type 'SignalCallback' to 'void*'
sigact.sa_handler = (void*)callback;
sigaction 结构(来自Tmds.Linux)
public struct sigaction
{
public sigset_t sa_mask;
public void* sa_handler { get; set; }
public void* sa_sigaction { get; set; }
public int sa_flags { readonly get; set; }
public void* sa_restorer { readonly get; set; }
}
完整源代码:
using System;
using static Tmds.Linux.LibC;
using Tmds.Linux;
using System.Runtime.InteropServices;
delegate void SignalCallback(int sig);
namespace example
{
class Program
{
static void catcher(int sig)
{
Console.Write($"Signal catcher called for signal {sig}");
}
static void timestamp(string str)
{
Console.Write($"The time {str} is {DateTime.Now}");
}
static unsafe int Main(string[] args)
{
int result = 0;
sigaction sigact = new sigaction();
sigset_t waitset = new sigset_t();
siginfo_t info = new siginfo_t();
sigset_t* ptr = &sigact.sa_mask;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
//Error CS0428 Cannot convert method group 'catcher' to non-delegate type 'void*'.Did you intend to invoke the method?
sigact.sa_handler = catcher;
//Error CS0029 Cannot implicitly convert type 'SignalCallback' to 'void*'
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = callback;
sigaction(SIGALRM, &sigact, null);
sigemptyset(&waitset);
sigaddset(&waitset, SIGALRM);
sigprocmask(SIG_BLOCK, &waitset, null);
alarm(10);
timestamp("before sigwaitinfo()");
result = sigwaitinfo(&waitset, &info);
if (result == SIGALRM)
Console.WriteLine($"sigwaitinfo() returned for signal {info.si_signo}");
else
{
Console.WriteLine($"sigwait() returned code {result}");
Console.WriteLine($"sigwait() returned error number {errno}");
Console.WriteLine("sigwait() function failed");
}
timestamp("after sigwaitinfo()");
return result;
}
}
}
要从委托中获取 void*
指针,您需要 Marshal.GetFunctionPointerForDelegate
。您必须 确保委托在最终使用前未被垃圾回收。 您可以使用 GC.KeepAlive
来实现。
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = Marshal.GetFunctionPointerForDelegate(callback);
// do the rest of your stuff
// after it's last usage by unmanaged code
GC.KeepAlive(callback);
然而:
您真的应该更多地依赖 P/Invoke 来进行任何转换。它在大多数情况下应该能够整理出 void*
转换,您只需要 GC.KeepAlive
.
您当前的代码 非常 有问题,因为您将托管指针全部投射到 void*
。
例如,不是这个调用,它正在执行从 &
托管指针到 void*
的转换
sigemptyset(&waitset);
相反,你会使用
sigemptyset(ref waitset);
理想情况下,您的结构不应使用属性,因为这会使在字段上定义属性变得更加困难。我注意到 sigaction
的顺序似乎不正确,从我快速 Google.
中可以看出
你应该这样定义sigaction
public struct sigaction
{
public SignalCallback sa_handler;
public SignalAction sa_sigaction;
public sigset_t sa_mask;
public int sa_flags;
public SignalRestorer sa_restorer;
}
有谁知道如何将 C# 中的方法强制转换为 void* 成员?
在下面的示例中,sigaction.sa_handler 是一个指定回调函数的 void* 成员。
环境:
- VisualStudio 专业版 2019 16.9.4
- dotnet 核心 SDK 3.1
- Ubuntu 在 WSL
- Tmds.Linux 0.5.0
sigaction sigact = new sigaction();
//Error CS0428 Cannot convert method group 'catcher' to non-delegate type 'void*'.Did you intend to invoke the method?
sigact.sa_handler = catcher;
//Error CS0030 Cannot convert type 'method' to 'void*'
sigact.sa_handler = (void *)catcher;
//Error CS0029 Cannot implicitly convert type 'SignalCallback' to 'void*'
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = callback;
//Error CS0030 Cannot convert type 'SignalCallback' to 'void*'
sigact.sa_handler = (void*)callback;
sigaction 结构(来自Tmds.Linux)
public struct sigaction
{
public sigset_t sa_mask;
public void* sa_handler { get; set; }
public void* sa_sigaction { get; set; }
public int sa_flags { readonly get; set; }
public void* sa_restorer { readonly get; set; }
}
完整源代码:
using System;
using static Tmds.Linux.LibC;
using Tmds.Linux;
using System.Runtime.InteropServices;
delegate void SignalCallback(int sig);
namespace example
{
class Program
{
static void catcher(int sig)
{
Console.Write($"Signal catcher called for signal {sig}");
}
static void timestamp(string str)
{
Console.Write($"The time {str} is {DateTime.Now}");
}
static unsafe int Main(string[] args)
{
int result = 0;
sigaction sigact = new sigaction();
sigset_t waitset = new sigset_t();
siginfo_t info = new siginfo_t();
sigset_t* ptr = &sigact.sa_mask;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
//Error CS0428 Cannot convert method group 'catcher' to non-delegate type 'void*'.Did you intend to invoke the method?
sigact.sa_handler = catcher;
//Error CS0029 Cannot implicitly convert type 'SignalCallback' to 'void*'
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = callback;
sigaction(SIGALRM, &sigact, null);
sigemptyset(&waitset);
sigaddset(&waitset, SIGALRM);
sigprocmask(SIG_BLOCK, &waitset, null);
alarm(10);
timestamp("before sigwaitinfo()");
result = sigwaitinfo(&waitset, &info);
if (result == SIGALRM)
Console.WriteLine($"sigwaitinfo() returned for signal {info.si_signo}");
else
{
Console.WriteLine($"sigwait() returned code {result}");
Console.WriteLine($"sigwait() returned error number {errno}");
Console.WriteLine("sigwait() function failed");
}
timestamp("after sigwaitinfo()");
return result;
}
}
}
要从委托中获取 void*
指针,您需要 Marshal.GetFunctionPointerForDelegate
。您必须 确保委托在最终使用前未被垃圾回收。 您可以使用 GC.KeepAlive
来实现。
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = Marshal.GetFunctionPointerForDelegate(callback);
// do the rest of your stuff
// after it's last usage by unmanaged code
GC.KeepAlive(callback);
然而:
您真的应该更多地依赖 P/Invoke 来进行任何转换。它在大多数情况下应该能够整理出 void*
转换,您只需要 GC.KeepAlive
.
您当前的代码 非常 有问题,因为您将托管指针全部投射到 void*
。
例如,不是这个调用,它正在执行从 &
托管指针到 void*
sigemptyset(&waitset);
相反,你会使用
sigemptyset(ref waitset);
理想情况下,您的结构不应使用属性,因为这会使在字段上定义属性变得更加困难。我注意到 sigaction
的顺序似乎不正确,从我快速 Google.
你应该这样定义sigaction
public struct sigaction
{
public SignalCallback sa_handler;
public SignalAction sa_sigaction;
public sigset_t sa_mask;
public int sa_flags;
public SignalRestorer sa_restorer;
}