2017-06-21 14 views
0

Mac OSで.Net Coreを実行しているときにPInvokeを使用してAppKitメソッドを呼び出すことはできますか?具体的には、NSWorkspace.Recycleメソッドを使用します。.Net CoreアプリケーションからAppKitメソッドを呼び出す方法は?

documentationによると、PInvokeはMacOSでサポートされていますが、これを使ってAppKitなどのフレームワークとのやりとり方法を明確にしていません。

答えて

3

PInvokeはCメソッドのみですが、Objective CランタイムはCメソッドに基づいているため、これらを使用することができます。 Xamarinは必要なセットアップと便利な方法をすべて提供しますが、これは可能です。この例では、安全でないコードを使用し、従って、csprojファイルに次のプロパティセットを必要とすることに注意:

<AllowUnsafeBlocks>true</AllowUnsafeBlocks> 

以下は完全なコードであり、注意して正しいシグネチャを持つインポート複数の時間にobjc_msgSendニーズ。 Cでは、正しいシグネチャを持つ異なる関数ポインタにキャストすることによってこれが行われます。 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 
     } 
    } 
} 
関連する問題