[Mono-dev] COM Interop in Mono
Jonathan S. Chambers
Jonathan.Chambers at ansys.com
Wed Nov 16 16:02:55 EST 2005
Anyone have any advice about beginning to make this all an external library accessible to mono? Where does it go? How is mono aware of it/find it? How does mono load it/call methods, etc.
Thanks,
Jonathan
-----Original Message-----
From: mono-devel-list-bounces at lists.ximian.com [mailto:mono-devel-list-bounces at lists.ximian.com] On Behalf Of Jonathan S. Chambers
Sent: Wednesday, November 16, 2005 3:08 PM
To: Kornél Pál
Cc: mono-devel-list at lists.ximian.com
Subject: RE: [Mono-dev] COM Interop in Mono
Well, I am by no means an expert on ABIs of different compilers. But, I have built a small COM style object on linux using gcc, and have been able to use it from managed code with my own flavor of COM interop. Is that pure luck, I don't know? ;-)
I understand that much of the infrastructure of COM interop is exposed via the Marshal class. However, none of that functionality is currently implemented in mono. That is what I am trying to do.
COM methods don't have to return HRESULTS, but all that I am currently testing with does. I'll fix that assumption later.
As for the cross app domain marshalling and whatever wonderful features exist, I am not worried about that right now. If I can use a COM object in C# within a single threaded/app domain environment, I will be happy.
I have looked at your code in ComIStreamMarshaler (and Peter's in Win32Dnd). That was the source for what I did in previous attempts at COM Interop (see original email).
The code is ugly right now, but I would appreciate any help/advice as it moves forward.
Thanks,
Jonathan
-----Original Message-----
From: Kornél Pál [mailto:kornelpal at hotmail.com]
Sent: Wednesday, November 16, 2005 2:31 PM
To: Jonathan S. Chambers
Cc: mono-devel-list at lists.ximian.com
Subject: Re: [Mono-dev] COM Interop in Mono
Hi,
The attached code looks promising.
I absolutely agree with you that any kind of ABI (including COM ABI) can be
implemented. The problem that I tried to point out is however that
implementing COM interop in Mono using an ABI that cannot be accessed by
compilers is useless. On Windows you can simply use COM ABI as a C++ object
because the compiler has support for that ABI. If you use the same ABI on
Linux for example without having a C++ compiler that supports the same ABI
you will not be able to use COM interop on that platform (unless you use
assembly code for example to produce the same calls).
So it's OK to implement COM ABI in Mono but do you have a compiler on the
other side of interop with support for the same ABI?
If you want to undersand MS.NET COM interop correctly you should look at
Mashal class. Methods and documentations as well. As I see all the
functionality suported by COM interop layer is available through Marshal
class as well that helps to understand it's infrastructure.
You mentioned in one of your previous messages that COM methods have HRESULT
return value. This is only optional. They can have any return type. When
using PreserveSigAttribute the original signature is used instead of using
HRESULT as return value and transofrming return value to parameter. This
means that there is no HRESULT <-> exception mapping done by the runtime.
You should have a look at
mcs/class/System.Drawing/System.Drawing/ComIStreamMarshaler.cs as well. This
is implemented in pure C# so Peter's work is somehow nearer to runtime level
but my work is more similar to MS.NET COM interop in it's behavior. There
are some differences like MS.NET uses the same QueryInterface, AddRef and
Release implementations and don't have to set out parameter when error is
returned in HRESULT but is very similar.
Also note that MS.NET COM interop has cross-AppDomain marshaling support
unlike delegate marshaling.
If you need help in undersanding COM interop infrastrucutre or a specific
functionality feel free to ask me.
Kornél
----- Original Message -----
From: "Jonathan S. Chambers" <Jonathan.Chambers at ansys.com>
To: "Kornél Pál" <kornelpal at hotmail.com>
Cc: <mono-devel-list at lists.ximian.com>
Sent: Wednesday, November 16, 2005 7:49 PM
Subject: RE: [Mono-dev] COM Interop in Mono
Attached is a diff of some current progress. These changes are all in place
right now, they would of course need moved to an external library.
Kornel, to answer your questions:
This is intended to be the beginning of a framework to leverage multiple
component technologies. COM Interop is well defined so I'm starting there.
COM is not necessarily Windows specific. It can be ported via tools (such as
those provided by Mainsoft) or on one's own if you follow the COM ABI
standard.
As for XPCOM, I know little about it. I've looked into it a little, and
while it may not have as well defined an ABI, there may still be hope for
it.
- Jonathan
-----Original Message-----
From: Kornél Pál [mailto:kornelpal at hotmail.com]
Sent: Wednesday, November 16, 2005 1:07 PM
To: Jonathan S. Chambers
Cc: mono-devel-list at lists.ximian.com
Subject: Re: [Mono-dev] COM Interop in Mono
Hi,
You are referencing something that you want to upload to SVN but did not
show it yet. What is it exactly?
Note that there were discussions about COM interop previously you should
look at them as well.
Some important things:
COM is Windows specific. If you want to use it you can use MS.NET instead of
Mono as relaying on COM interop is platform dependent. So there is no use to
implement COM interop only for use with Windows.
On the other hand COM interop could be implemented as a platform independent
object oriented P/Invoke-like layer that could be cool. But the problem is
that on Windows COM specifies a standard ABI (Application Binary Interface)
for representing COM objects that can be accessed by code compiled using
different compilers.
On other platforms (Linux for example) there is no standard ABI for objects,
only for classic C functions. This makes the things difficult. In addition
things like XPCOM assume that you use the same compiler on both sides of
interop so has no specific ABI, it depends on the C++ compiler you use.
Some people would like to implement COM interop as Bonobo interop in Mono
but these two layers are so much different that it cannot be done. Bonobo
interop has to be implemented separatedly as it requires different interop
attributes for example.
Kornél
----- Original Message -----
From: "Jonathan S. Chambers" <Jonathan.Chambers at ansys.com>
To: "Jonathan S. Chambers" <Jonathan.Chambers at ansys.com>
Cc: <mono-devel-list at lists.ximian.com>
Sent: Wednesday, November 16, 2005 5:50 PM
Subject: RE: [Mono-dev] COM Interop in Mono
Well, no one has responded yet, so here comes the next email ;-)
This work is coming along, so I'd like to get it in svn soon.
Would this go under the trunk, as in /trunk/cominterop or something like
that? If anyone has a better name, please suggest. Also, I think this
work will also be similar to work needed for XPCOM. Should both be in
the same lib? Or where can I put shared functionality?
Ok, more details. All calls to the RCW's are finding their way to the
correct methods on the COM objects. Currently, I find the interface that
the method belongs to. I then subtract interface->method.start from the
method->slot. This gives me the offset of the method in the interface. I
then compensate for whether the interface is IUnknown based or IDispatch
based (3 or 7 methods at beginning of vtable). I then call that offset
on the correct vtable.
Now, more about integrating into mono. To correctly support COM interop
in mono, the following functionality is needed. I'm trying to begin to
define what the API needs to support.
1. Object Creation
2. Method Calls
3. Object Destruction (will all component wrappers need a finalizer?)
4. Casting (more difficult for me, can be supported later as this
dynamically adds interfaces to an object)
5. Interface/COM object marshalling
6. BSTR marshalling (I think this should be in this library, so mono has
no dependencies on the Sys*String routines for BSTRS)
Some humble beginning like:
/* creates unmanaged object */
gpointer
component_create(MonoObject* this);
/* destroys unmanaged object */
void
component_destroy(MonoObject * this);
/* gets managed->unmanaged wrapper for method calls, called by
mono_marshal_get_native_wrapper? */
MonoMethod *
component_get_method (MonoMethod *method)
Not sure about the marshalling yet...
Thanks,
Jonathan
-----Original Message-----
From: mono-devel-list-bounces at lists.ximian.com
[mailto:mono-devel-list-bounces at lists.ximian.com] On Behalf Of Jonathan
S. Chambers
Sent: Monday, November 14, 2005 10:06 PM
Cc: mono-devel-list at lists.ximian.com
Subject: [Mono-dev] COM Interop in Mono
Hello all,
Here is a brief overview of some work involving COM Interop support
in mono. I'm sure I'm wrong on multiple things below, so feel free to
suggest/correct.
COM Standard
Don't feel like saying alot. Lots of docs available.
MS COM Interop
Again, well documented. Especially via '.NET and COM: The Complete
Interoperability Guide' by Adam Nathan. COM interop is twofold.
1. Managed Client, COM Server - This allows for unmanaged com components
to be used in the managed runtime. This occurs via runtime callable
wrappers (RCW). The RCW is a managed wrapper that manages a single
unmanaged com object. The lifetime of the COM object is managed via the
RCW; the RCW's lifetime is managed like any other managed object. Also,
the interop can occur via early binding (using the Interop assembly and
vtables) or late binding via reflection.
2. COM Client, Managed Server - In this case, an managed object is
exposed to COM via a COM callable wrapper (CCW). The CCW behaves like a
normal COM object to the client. The CCW exposes interfaces
corresponding to the managed object's methods/properties, as well as
IUnknown, IDispatch, and other interfaces.
Another thing to note about COM interop is the runtime support for
casting. Normal managed objects fail upon a cast when an interface is
not implemented by that object. A RCW will try to cast the normal way,
via the metadata provided about the class. If the cast fails, the
runtime will then call QueryInterface on the underlying COM object for
the interface in question. If the QueryInterface succeeds, then the
runtime allows the cast to occur.
I'm ignoring other semi-important (depending on your situation) aspects
of COM interop such as COM threading, connection points, etc.
Old Approach (all in C#, runtime agnostic; i.e. worked on MS. Net and
Mono)
I'm not going to spend much time on this. This work initially grew out
of looking at some code by Peter Bartok in Win32Dnd.cs in MWF. A COM
interface (really just a vtable) is essentially a structure of function
pointers. Function pointers can be marshalled as delegates (in 2.0) and
vice versa. So, for each COM interface a corresponding struct of
delegates was defined; except, the all the method signatures were
modified to take an IntPtr as the first argument, the 'this; pointer.
Once the COM object was created, it was QI'd for that interface. Then,
the returned pointer (which points to the vtable) was read via
Marshal.ReadIntPtr. This value is then marshalled as the struct
previously defined. The delegates can now be invoked.
In addition, custom marshallers were written for the marshalling of
interfaces and BSTRs. Tricks were played with pinning, GCHandles, etc
for lifetime management. Also, to get around the fact that there was no
runtime support for 'late' casting, Reflection.Rmit was used to build
dynamic wrappers.
This method proved useful, but ugly and inefficient to say the least.
Current Approach
This method involves the mono runtime directly. Currently, I'm focusing
on RCW, i.e. using COM components in managed code. I'm also using the
Interop assemblies generated via MS's tlbimp.exe (in the future, someone
could easily write a tool to generate compatible assemblies via an XML
definition or whatever instead of from a typelib).
When a RCW class is loaded by the runtime, extra space is allocated in
the class instance to contain the unmanaged object pointer. Upon
creation of a RCW, the underlying COM object is created via
CoCreateInstance and it's pointer is stored in the RCW.
All methods on a RCW are marked internal call. Thus, for each method a
managed->unmanaged wrapper is generated. Currently, here is the process
for that.
1. The pointer to the COM Object is obtained from the RCW.
2. The function pointer to the method obtained. The method layout in the
interop assembly is the same order as that of the vtable of the COM
object. Thus, the offset of the method on the RCW is used to offset into
the vtable of the COM object, and get the correct function pointer.
3. The interface pointer and method arguments are pushed on the stack.
If the managed method has a return value, a reference to a local
variable of that type is pushed on the stack as the last arg to the COM
method. The function pointer is called, expecting a int32 as a return
value (an HRESULT that will be translated to an exception in the case of
a failure). If there is a return value, this return value is pushed onto
the stack and the method returns.
Currently, this mechanism allows for the use of a COM collection of
doubles in managed code.
TODO (lots, I'll stick to near term)
1. Get lifetime management of COM object working correctly. Perhaps RCW
will need finalizer that calls Release on COM object?
2. RCW has one pointer to COM object, but COM object has multiple
vtables. Need correct vtable for current interface that is being called.
3. BSTR marshalling
4. Interface marshalling
5. Use COM marshalling rules instead of PInvoke rules
6. 'late' casting
7. Wrap this into all into a separate library, and communicate with
runtime via API so no component technology specific code is in mono.
...
58. CCW
59. connection points
etc.
I know very little about XPCOM, but I understand that it is quite
similar. This should be able to be extended to that quite easily I
believe.
Thanks,
Jonathan
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list at lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list at lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list at lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list
More information about the Mono-devel-list
mailing list