.net - Converting between 2 different libraries using the same COM interface in C# -
.net - Converting between 2 different libraries using the same COM interface in C# -
i have pair of libraries both utilize same com interface. in 1 library have class implements interface. other library requires object implements interface.
however both libraries have own definition of interface. both different same interface.
so seek case between them follows:
library2.interface intf = (library2.interface)impl;
but raises exception. if following:
library1.interface intf = (library1.interface)impl;
then casts without problem no longer able pass class library2.
i naively assumed both interfaces having same guid prevent beingness problem appear wrong on that. have thought how can convert between 2 libraries? perhaps via marshal of sort?
this interesting problem, , think may have interesting solution it. so, although library1.interface
, library2.interface
2 binary compatible comimport
interfaces, they're still 2 different .net interfaces , cannot cast each other.
to create casting possible, need somehow hide identity of managed library1.interface
.net object behind com-callable wrapper (ccw), create sure ccw doesn't marshaled same .net object. .net marshaller create separate rcw proxy cast library2.interface
plain vanilla com object.
besides using separate com apartments library1.interface
, library2.interface
objects, can think of 1 other way of doing this: com aggregation. .net object can aggregated via marshal.createaggregatedobject
inner object. trick build unmanaged iunknown
com identity object serve outer (parent) object aggregation. such outer object given separate rcw proxy when accessed .net.
below take on this:
var server = comwrapper.create<library2.interface>(() => new library1.server()); var client = new library2.client(); client.callmethod(server);
the whole logic console app (certain knowledge of com binary protocols required understand code):
using system; using system.runtime.interopservices; namespace library1 { [comimport, interfacetype(cominterfacetype.interfaceisiunknown)] [guid("4c08a691-5d61-4e9a-b16d-75bad2834bae")] public interface interface { void testmethod(); } [comvisible(true)] public class server : interface { public server() { } public void testmethod() { console.writeline("testmethod called"); } } } namespace library2 { [comimport, interfacetype(cominterfacetype.interfaceisiunknown)] [guid("4c08a691-5d61-4e9a-b16d-75bad2834bae")] public interface interface { void testmethod(); } public class client { public void callmethod(library2.interface server) { server.testmethod(); } } } namespace testapp { class programme { static void main(string[] args) { // convert library1.server library2.interface var server = comwrapper.create<library2.interface>(() => new library1.server()); var client = new library2.client(); client.callmethod(server); marshal.releasecomobject(server); console.readline(); } } /// <summary> /// comwrapper - http://stackoverflow.com/q/26758316/1768303 /// noseratio /// </summary> public class comwrapper { readonly guid iid_iunknown = new guid("00000000-0000-0000-c000-000000000046"); const int s_ok = 0; const int e_fail = unchecked((int)0x80004005); delegate int queryinterfacemethod(intptr punk, ref guid iid, out intptr ppv); delegate int addrefmethod(intptr punk); delegate int releasemethod(intptr punk); [structlayout(layoutkind.sequential)] struct unkobject { public intptr pvtable; } [structlayout(layoutkind.sequential)] struct unkvtable { public intptr pqueryinterface; public intptr paddref; public intptr prelease; } int _refcount = 0; intptr _pvtable; intptr _outerobject; intptr _aggregatedobject; gchandle _gchandle; queryinterfacemethod _queryinterfacemethod; addrefmethod _addrefmethod; releasemethod _releasemethod; private comwrapper() { } ~comwrapper() { console.writeline("~comwrapper"); free(); } private intptr initialize(func<object> createinnerobject) { seek { // implement iunknown methods _queryinterfacemethod = delegate(intptr punk, ref guid iid, out intptr ppv) { lock (this) { // delegate iid_iunknown aggregated object if (iid_iunknown == iid) { ppv = _outerobject; marshal.addref(_outerobject); homecoming s_ok; } homecoming marshal.queryinterface(_aggregatedobject, ref iid, out ppv); } }; _addrefmethod = delegate(intptr punk) { lock (this) { homecoming ++_refcount; } }; _releasemethod = delegate(intptr punk) { lock (this) { if (0 == --_refcount) { free(); } homecoming _refcount; } }; // create iunknown vtable var vtable = new unkvtable(); vtable.pqueryinterface = marshal.getfunctionpointerfordelegate(_queryinterfacemethod); vtable.paddref = marshal.getfunctionpointerfordelegate(_addrefmethod); vtable.prelease = marshal.getfunctionpointerfordelegate(_releasemethod); _pvtable = marshal.alloccotaskmem(marshal.sizeof(vtable)); marshal.structuretoptr(vtable, _pvtable, false); // create iunknown object var unkobject = new unkobject(); unkobject.pvtable = _pvtable; _outerobject = marshal.alloccotaskmem(marshal.sizeof(unkobject)); marshal.structuretoptr(unkobject, _outerobject, false); // pin managed comwrapper instance _gchandle = gchandle.alloc(this, gchandletype.normal); // create , aggregate inner object _aggregatedobject = marshal.createaggregatedobject(_outerobject, createinnerobject()); homecoming _outerobject; } grab { free(); throw; } } private void free() { console.writeline("free"); if (_aggregatedobject != intptr.zero) { marshal.release(_aggregatedobject); _aggregatedobject = intptr.zero; } if (_pvtable != intptr.zero) { marshal.freecotaskmem(_pvtable); _pvtable = intptr.zero; } if (_outerobject != intptr.zero) { marshal.freecotaskmem(_outerobject); _outerobject = intptr.zero; } if (_gchandle.isallocated) { _gchandle.free(); } } public static t create<t>(func<object> createinnerobject) { var wrapper = new comwrapper(); var unk = wrapper.initialize(createinnerobject); marshal.addref(unk); seek { var comobject = marshal.getobjectforiunknown(unk); homecoming (t)comobject; } { marshal.release(unk); } } } }
c# .net casting com com-interop
Comments
Post a Comment