/// <summary>Create a factory which only supports objects implemented in .NET</summary> static Func <object, bool, IntPtr> createOneWayToNativeFactory <I>(Guid iid) where I : class { // The builder gets captured by the lambda. // This is what we want, the constructor takes noticeable time, the code outside the lambda runs once per interface type, the code inside lambda runs once per object instance. InterfaceBuilder builder = new InterfaceBuilder(typeof(I)); return((object obj, bool addRef) => { if (null == obj) { // Marshalling null return IntPtr.Zero; } Debug.Assert(obj is I); // It could be the same managed object is reused across native calls. If that's the case, the cache already contains the native pointer. I managed = (I)obj; IntPtr?wrapped = WrappersCache <I> .lookup(managed); if (wrapped.HasValue) { return wrapped.Value; } Delegate[] delegates = builder.compile(managed); ManagedObject wrapper = new ManagedObject(managed, iid, delegates); WrappersCache <I> .add(managed, wrapper); if (addRef) { wrapper.callAddRef(); } return wrapper.address; }); }
/// <summary>Create a factory which supports two-way marshaling.</summary> static Func <object, bool, IntPtr> createTwoWayFactory <I>(Guid iid) where I : class { // The builder gets captured by the lambda. // This is what we want, the constructor takes noticeable time, the code outside the lambda runs once per interface type, the code inside lambda runs once per object instance. InterfaceBuilder builder = new InterfaceBuilder(typeof(I)); return((object obj, bool addRef) => { if (null == obj) { // Marshalling null return IntPtr.Zero; } Debug.Assert(obj is I); if (obj is RuntimeClass rc) { // That .NET object is not actually managed, it's a wrapper around C++ implemented COM interface. if (rc.iid == iid) { // It wraps around the same interface if (addRef) { rc.addRef(); } return rc.nativePointer; } // It wraps around different interface. Call QueryInterface on the native object. return rc.queryInterface(iid, addRef); } // It could be the same managed object is reused across native calls. If that's the case, the cache already contains the native pointer. I managed = (I)obj; IntPtr?wrapped = WrappersCache <I> .lookup(managed); if (wrapped.HasValue) { return wrapped.Value; } Delegate[] delegates = builder.compile(managed); ManagedObject wrapper = new ManagedObject(managed, iid, delegates); WrappersCache <I> .add(managed, wrapper); if (addRef) { wrapper.callAddRef(); } return wrapper.address; }); }
static Func <object, bool, IntPtr> getFactory <I>() where I : class { Type tInterface = typeof(I); Func <object, bool, IntPtr> result; lock ( syncRoot ) { if (cache.TryGetValue(tInterface, out result)) { return(result); } Guid iid = ReflectionUtils.checkInterface(tInterface); // The builder gets captured by the lambda. // This is what we want, the constructor takes noticeable time, the code outside lambda runs once per interface type, the code inside lambda runs once per object instance. InterfaceBuilder builder = new InterfaceBuilder(tInterface); result = (object obj, bool addRef) => { if (null == obj) { // Marshalling null return(IntPtr.Zero); } Debug.Assert(obj is I); RuntimeClass rc = obj as RuntimeClass; if (null != rc) { // That .NET object is not actually managed, it's a wrapper around C++ implemented COM interface. if (rc.iid == iid) { // It wraps around the same interface if (addRef) { rc.addRef(); } return(rc.nativePointer); } // It wraps around different interface. Call QueryInterface on the native object. return(rc.queryInterface(iid, addRef)); } // It could be the same managed object is reused across native calls. If that's the case, the cache already contains the native pointer. I managed = (I)obj; IntPtr?wrapped = WrappersCache <I> .lookup(managed); if (wrapped.HasValue) { return(wrapped.Value); } Delegate[] delegates = builder.compile(managed); ManagedObject wrapper = new ManagedObject(managed, iid, delegates); WrappersCache <I> .add(managed, wrapper); if (addRef) { wrapper.callAddRef(); } return(wrapper.address); }; cache.Add(tInterface, result); return(result); } }