Skip to content

Commit

Permalink
[DotNet] CoreFoundation Pinvokes (#17505)
Browse files Browse the repository at this point in the history
Updates the pinvokes in CoreFoundation to have blittable types.
I intentionally did *not* do `CFReadStream` and `CFWriteStream` as the
changes needed for those are may create a breaking API change, so those
should probably be their own PR for closer scrutiny.

Please look closely at CFProxySupport as that was the least
straightforward of the changes.
  • Loading branch information
stephen-hawley authored Feb 11, 2023
1 parent 6adfba6 commit 309dbe3
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 6 deletions.
67 changes: 67 additions & 0 deletions src/CoreFoundation/CFMessagePort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,30 +55,43 @@ public class CFMessagePort : NativeObject {

// CFMessagePortContext
[StructLayout (LayoutKind.Sequential)]
#if NET
unsafe
#endif
struct ContextProxy {
/* CFIndex */
nint version; // must be 0
public /* void * */ IntPtr info;
#if NET
public delegate* unmanaged<IntPtr, IntPtr> retain;
public delegate* unmanaged<IntPtr, void> release;
public delegate* unmanaged<IntPtr, IntPtr> copyDescription;
#else
public /* CFAllocatorRetainCallBack*/ Func<IntPtr, IntPtr> retain;
public /* CFAllocatorReleaseCallBack*/ Action<IntPtr> release;
public /* CFAllocatorCopyDescriptionCallBack*/ Func<IntPtr, IntPtr> copyDescription;
#endif
}

public delegate NSData CFMessagePortCallBack (int type, NSData data);

#if !NET
delegate /* CFDataRef */ IntPtr CFMessagePortCallBackProxy (/* CFMessagePortRef */ IntPtr messagePort, /* SInt32 */ int type, /* CFDataRef */ IntPtr data, /* void* */ IntPtr info);

delegate void CFMessagePortInvalidationCallBackProxy (/* CFMessagePortRef */ IntPtr messagePort, /* void * */ IntPtr info);
#endif

static Dictionary<IntPtr, CFMessagePortCallBack> outputHandles = new Dictionary<IntPtr, CFMessagePortCallBack> (Runtime.IntPtrEqualityComparer);

static Dictionary<IntPtr, Action?> invalidationHandles = new Dictionary<IntPtr, Action?> (Runtime.IntPtrEqualityComparer);

static Dictionary<IntPtr, CFMessagePortContext?> messagePortContexts = new Dictionary<IntPtr, CFMessagePortContext?> (Runtime.IntPtrEqualityComparer);

#if !NET
static CFMessagePortCallBackProxy messageOutputCallback = new CFMessagePortCallBackProxy (MessagePortCallback);

static CFMessagePortInvalidationCallBackProxy messageInvalidationCallback = new CFMessagePortInvalidationCallBackProxy (MessagePortInvalidationCallback);
#endif

IntPtr contextHandle;

Expand Down Expand Up @@ -139,7 +152,13 @@ public Action? InvalidationCallback {
invalidationHandles.Add (GetCheckedHandle (), value);
}

#if NET
unsafe {
CFMessagePortSetInvalidationCallBack (Handle, &MessagePortInvalidationCallback);
}
#else
CFMessagePortSetInvalidationCallBack (Handle, messageInvalidationCallback);
#endif
}
}

Expand Down Expand Up @@ -172,8 +191,13 @@ protected override void Dispose (bool disposing)
base.Dispose (disposing);
}

#if NET
[DllImport (Constants.CoreFoundationLibrary)]
static unsafe extern /* CFMessagePortRef */ IntPtr CFMessagePortCreateLocal (/* CFAllocatorRef */ IntPtr allocator, /* CFStringRef */ IntPtr name, delegate* unmanaged<IntPtr, int, IntPtr, IntPtr, IntPtr> callout, /* CFMessagePortContext */ ContextProxy* context, [MarshalAs (UnmanagedType.I1)] bool* shouldFreeInfo);
#else
[DllImport (Constants.CoreFoundationLibrary)]
static extern /* CFMessagePortRef */ IntPtr CFMessagePortCreateLocal (/* CFAllocatorRef */ IntPtr allocator, /* CFStringRef */ IntPtr name, CFMessagePortCallBackProxy callout, /* CFMessagePortContext */ ref ContextProxy context, [MarshalAs (UnmanagedType.I1)] ref bool shouldFreeInfo);
#endif

[DllImport (Constants.CoreFoundationLibrary)]
static extern /* CFMessagePortRef */ IntPtr CFMessagePortCreateRemote (/* CFAllocatorRef */ IntPtr allocator, /* CFStringRef */ IntPtr name);
Expand Down Expand Up @@ -208,8 +232,13 @@ protected override void Dispose (bool disposing)
[DllImport (Constants.CoreFoundationLibrary)]
static extern void CFMessagePortSetDispatchQueue (/* CFMessagePortRef */ IntPtr ms, dispatch_queue_t queue);

#if NET
[DllImport (Constants.CoreFoundationLibrary)]
static unsafe extern void CFMessagePortSetInvalidationCallBack (/* CFMessagePortRef */ IntPtr ms, delegate* unmanaged<IntPtr, IntPtr, void> callout);
#else
[DllImport (Constants.CoreFoundationLibrary)]
static extern void CFMessagePortSetInvalidationCallBack (/* CFMessagePortRef */ IntPtr ms, CFMessagePortInvalidationCallBackProxy callout);
#endif

[DllImport (Constants.CoreFoundationLibrary)]
static extern IntPtr CFMessagePortGetInvalidationCallBack (/* CFMessagePortRef */ IntPtr ms);
Expand All @@ -234,19 +263,37 @@ protected override void Dispose (bool disposing)
var shortHandle = GCHandle.Alloc (contextProxy);

if (context is not null) {
#if NET
unsafe {
if (context.Retain is not null)
contextProxy.retain = &RetainProxy;
if (context.Release is not null)
contextProxy.release = &ReleaseProxy;
if (context.CopyDescription is not null)
contextProxy.copyDescription = &CopyDescriptionProxy;
}
#else
if (context.Retain is not null)
contextProxy.retain = RetainProxy;
if (context.Release is not null)
contextProxy.release = ReleaseProxy;
if (context.CopyDescription is not null)
contextProxy.copyDescription = CopyDescriptionProxy;
#endif
contextProxy.info = (IntPtr) shortHandle;
lock (messagePortContexts)
messagePortContexts.Add (contextProxy.info, context);
}

try {
#if NET
IntPtr portHandle;
unsafe {
portHandle = CFMessagePortCreateLocal (allocator.GetHandle (), n, &MessagePortCallback, &contextProxy, &shouldFreeInfo);
}
#else
var portHandle = CFMessagePortCreateLocal (allocator.GetHandle (), n, messageOutputCallback, ref contextProxy, ref shouldFreeInfo);
#endif

// TODO handle should free info
if (portHandle == IntPtr.Zero)
Expand Down Expand Up @@ -279,7 +326,11 @@ protected override void Dispose (bool disposing)
//
// Proxy callbacks
//
#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (Func<IntPtr, IntPtr>))]
#endif
static IntPtr RetainProxy (IntPtr info)
{
INativeObject? result = null;
Expand All @@ -295,7 +346,11 @@ static IntPtr RetainProxy (IntPtr info)
return result.GetHandle ();
}

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (Action<IntPtr>))]
#endif
static void ReleaseProxy (IntPtr info)
{
CFMessagePortContext? context;
Expand All @@ -307,7 +362,11 @@ static void ReleaseProxy (IntPtr info)
context.Release ();
}

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (Func<IntPtr, IntPtr>))]
#endif
static IntPtr CopyDescriptionProxy (IntPtr info)
{
NSString? result = null;
Expand All @@ -322,7 +381,11 @@ static IntPtr CopyDescriptionProxy (IntPtr info)
return result.GetHandle ();
}

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (CFMessagePortCallBackProxy))]
#endif
static IntPtr MessagePortCallback (IntPtr local, int msgid, IntPtr data, IntPtr info)
{
CFMessagePortCallBack callback;
Expand All @@ -341,7 +404,11 @@ static IntPtr MessagePortCallback (IntPtr local, int msgid, IntPtr data, IntPtr
}
}

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (CFMessagePortInvalidationCallBackProxy))]
#endif
static void MessagePortInvalidationCallback (IntPtr messagePort, IntPtr info)
{
Action? callback;
Expand Down
24 changes: 24 additions & 0 deletions src/CoreFoundation/CFNotificationCenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,23 @@ public CFNotificationObserverToken AddObserver (string name, INativeObject objec
lock (listeners) {
if (!listeners.TryGetValue (name, out listenersForName)) {
listenersForName = new List<CFNotificationObserverToken> (1);
#if NET
unsafe {
CFNotificationCenterAddObserver (center: Handle,
observer: Handle,
callback: &NotificationCallback,
name: strHandle,
obj: token.observedObject,
suspensionBehavior: (IntPtr) suspensionBehavior);
}
#else
CFNotificationCenterAddObserver (center: Handle,
observer: Handle,
callback: NotificationCallback,
name: strHandle,
obj: token.observedObject,
suspensionBehavior: (IntPtr) suspensionBehavior);
#endif
} else
listenersForName = new List<CFNotificationObserverToken> (listenersForName);
listenersForName.Add (token);
Expand Down Expand Up @@ -163,9 +174,15 @@ void notification (string? name, NSDictionary? userInfo)
}
}

#if !NET
delegate void CFNotificationCallback (CFNotificationCenterRef center, IntPtr observer, IntPtr name, IntPtr obj, IntPtr userInfo);
#endif

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (CFNotificationCallback))]
#endif
static void NotificationCallback (CFNotificationCenterRef centerPtr, IntPtr observer, IntPtr name, IntPtr obj, IntPtr userInfo)
{
CFNotificationCenter center;
Expand Down Expand Up @@ -245,10 +262,17 @@ public void RemoveEveryObserver ()
}


#if NET
[DllImport (Constants.CoreFoundationLibrary)]
static extern unsafe void CFNotificationCenterAddObserver (CFNotificationCenterRef center, IntPtr observer,
delegate* unmanaged<CFNotificationCenterRef, IntPtr, IntPtr, IntPtr, IntPtr, void> callback, IntPtr name, IntPtr obj,
/* CFNotificationSuspensionBehavior */ IntPtr suspensionBehavior);
#else
[DllImport (Constants.CoreFoundationLibrary)]
static extern unsafe void CFNotificationCenterAddObserver (CFNotificationCenterRef center, IntPtr observer,
CFNotificationCallback callback, IntPtr name, IntPtr obj,
/* CFNotificationSuspensionBehavior */ IntPtr suspensionBehavior);
#endif

[DllImport (Constants.CoreFoundationLibrary)]
static extern unsafe void CFNotificationCenterPostNotificationWithOptions (CFNotificationCenterRef center, IntPtr name, IntPtr obj, IntPtr userInfo, int options);
Expand Down
Loading

5 comments on commit 309dbe3

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Status for 'xamarin-macios - sample testing (build)': failure.

  • ❌ Debug_iPhone_AF: Failed
  • ❌ Debug_iPhone_GR: Failed
  • ❌ Debug_iPhone_SZ: Failed
  • ❌ Debug_iPhoneSimulator: Failed
  • ❌ Release_iPhone_AF: Failed
  • ❌ Release_iPhone_GR: Failed
  • ❌ Release_iPhone_SZ: Failed
  • ❌ Release_iPhoneSimulator: Failed
  • ❌ Debug_Mac: Failed
  • ❌ Release_Mac: Failed
  • ❌ PublishPerformanceData: Failed

Please sign in to comment.