简体   繁体   English

如何从.Net Core应用程序PInvoke AppKit方法?

[英]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.

相关问题 无法从 Linux 上的 .NET 核心调用 libc function - Can't pinvoke libc function from .NET core on Linux .NET:如何PInvoke UpdateProcThreadAttribute - .NET : How to PInvoke UpdateProcThreadAttribute ASP.Net Core Web API应用程序:如何从我所有的操作方法中返回成功并且不以某种模式运行代码? - ASP.Net Core Web API application : How to return success from all my action methods and not run the code in a certain mode? 如何在 .NET Core 中正确处理异步方法 - How to handle async methods properly in .NET Core 通过PInvoke从Silverlight 5调用C#dll库方法 - Call C# dll library methods from Silverlight 5 via PInvoke 如何从 .net 核心应用程序向服务总线主题发送消息 - How to send a message to service bus topic from .net core application 如何将 .NET 核心应用程序的回调传递给 C DLL - How to pass a callback from .NET Core application to a C DLL 如何从 a.Net 核心应用程序中的 SQL 语句中获取标量值? - How to get scalar value from a SQL statement in a .Net core application? 如何从运行在 Linux 上的 .NET 核心应用程序关闭计算机 - How to shutdown computer from a .NET Core application running on Linux 如何使用 wix 中的 .NET Core Runtime 先决条件安装网络核心应用程序 - How to install a net core application with .NET Core Runtime prerequisite from wix
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM