[英]How to PInvoke AppKit methods from .Net Core application?
Is it possible to use PInvoke to call into AppKit methods when running on .Net Core on Mac OS? 在Mac OS的.Net Core上运行时,是否可以使用PInvoke调用AppKit方法? Specifically, I would like to use the
NSWorkspace.Recycle
method. 具体来说,我想使用
NSWorkspace.Recycle
方法。
According to the documentation PInvoke is supported on MacOS, it is just not clear how one would use this to interact with frameworks such as AppKit. 根据MacOS支持PInvoke的文档 ,尚不清楚人们将如何使用它与AppKit等框架进行交互。
PInvoke is for C methods only but since the Objective C runtime is based on C methods, we can use them. PInvoke仅适用于C方法,但是由于Objective C运行时基于C方法,因此我们可以使用它们。 Xamarin would provide all the necessary setup and convenience method but this is possible.
Xamarin将提供所有必要的设置和便捷方法,但这是可能的。 Note that this example uses unsafe code and thus needs the following property set in the csproj file:
请注意,此示例使用了不安全的代码,因此需要在csproj文件中设置以下属性:
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Below is the full code, note that objc_msgSend
needs to imported multiple time with the correct signature. 下面是完整的代码,请注意
objc_msgSend
需要使用正确的签名多次导入。 In C this would be done by casting it to different function pointers with the correct signature. 在C语言中,可以通过将其强制转换为具有正确签名的不同函数指针来完成。 The frameworks also need to be imported by via a full path since the CoreCLR has no probing logic for frameworks, it would search for a
lib{sth}.dylib
instead. 由于CoreCLR没有针对框架的探测逻辑,因此还需要通过完整路径导入框架,它会搜索
lib{sth}.dylib
。
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace NSWorkspaceExample
{
public class Program
{
static void Main(string[] args)
{
var testFile = Path.Combine(Directory.GetCurrentDirectory(), "test.txt");
File.WriteAllText(testFile, "example file content");
var cfstrTestFile = CreateCFString(testFile);
var nsURL = objc_getClass("NSURL");
var fileUrl = objc_msgSend_retIntPtr_IntPtr(nsURL, GetSelector("fileURLWithPath:"), cfstrTestFile);
CFRelease(cfstrTestFile);
var urlArray = CreateCFArray(new IntPtr[] {fileUrl});
var nsWorkspace = objc_getClass("NSWorkspace");
var sharedWorkspace = objc_msgSend_retIntPtr(nsWorkspace, GetSelector("sharedWorkspace"));
objc_msgSend_retVoid_IntPtr_IntPtr(sharedWorkspace, GetSelector("recycleURLs:completionHandler:"), urlArray, IntPtr.Zero);
CFRelease(urlArray);
CFRelease(fileUrl);
CFRelease(sharedWorkspace);
// sleep since we didn't go through the troubles of creating a block object as a callback
Thread.Sleep(1000);
}
public static IntPtr GetSelector(string name)
{
IntPtr cfstrSelector = CreateCFString(name);
IntPtr selector = NSSelectorFromString(cfstrSelector);
CFRelease(cfstrSelector);
return selector;
}
private const string FoundationFramework = "/System/Library/Frameworks/Foundation.framework/Foundation";
private const string AppKitFramework = "/System/Library/Frameworks/AppKit.framework/AppKit";
public unsafe static IntPtr CreateCFString(string aString)
{
var bytes = Encoding.Unicode.GetBytes(aString);
fixed (byte* b = bytes) {
var cfStr = CFStringCreateWithBytes(IntPtr.Zero, (IntPtr)b, bytes.Length, CFStringEncoding.UTF16, false);
return cfStr;
}
}
// warning: this doesn't call retain/release on the elements in the array
public unsafe static IntPtr CreateCFArray(IntPtr[] objectes)
{
fixed(IntPtr* vals = objectes) {
return CFArrayCreate(IntPtr.Zero, (IntPtr)vals, objectes.Length, IntPtr.Zero);
}
}
[DllImport(FoundationFramework)]
public static extern IntPtr CFStringCreateWithBytes(IntPtr allocator, IntPtr buffer, long bufferLength, CFStringEncoding encoding, bool isExternalRepresentation);
[DllImport(FoundationFramework)]
public static extern IntPtr CFArrayCreate(IntPtr allocator, IntPtr values, long numValues, IntPtr callbackStruct);
[DllImport(FoundationFramework)]
public static extern void CFRetain(IntPtr handle);
[DllImport(FoundationFramework)]
public static extern void CFRelease(IntPtr handle);
[DllImport(AppKitFramework, CharSet = CharSet.Ansi)]
public static extern IntPtr objc_getClass(string name);
[DllImport(AppKitFramework)]
public static extern IntPtr NSSelectorFromString(IntPtr cfstr);
[DllImport(FoundationFramework, EntryPoint="objc_msgSend")]
public static extern IntPtr objc_msgSend_retIntPtr(IntPtr target, IntPtr selector);
[DllImport(FoundationFramework, EntryPoint="objc_msgSend")]
public static extern void objc_msgSend_retVoid_IntPtr_IntPtr(IntPtr target, IntPtr selector, IntPtr param1, IntPtr param2);
[DllImport(FoundationFramework, EntryPoint="objc_msgSend")]
public static extern IntPtr objc_msgSend_retIntPtr_IntPtr(IntPtr target, IntPtr selector, IntPtr param);
[DllImport(FoundationFramework, EntryPoint="objc_msgSend")]
public static extern void objc_msgSend_retVoid(IntPtr target, IntPtr selector);
public enum CFStringEncoding : uint
{
UTF16 = 0x0100,
UTF16BE = 0x10000100,
UTF16LE = 0x14000100,
ASCII = 0x0600
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.