[Mono-dev] COM Interop in Mono

Kornél Pál kornelpal at hotmail.com
Thu Nov 17 12:06:49 EST 2005


Hi,

Unless you want to use it on Linux immediately (and of course if it can be
used on Linux), this should be Windows only currently. As such it can be
simply #ifdef'd as windows-only.

Porting COM interop to Linux will need some support functions exported by
Mono to support everiyhing (not all of the listed functions are necessary):

http://msdn.microsoft.com/library/en-us/com/html/36563ef5-1523-4b77-bca9-472c39e04785.asp
http://msdn.microsoft.com/library/en-us/automat/html/4ecb0c1f-4e4d-4e8b-bd55-9ac6568d027b.asp

Kornél

----- Original Message -----
From: "Jonathan S. Chambers" <Jonathan.Chambers at ansys.com>
To: "Paolo Molaro" <lupus at ximian.com>; <mono-devel-list at lists.ximian.com>
Sent: Thursday, November 17, 2005 5:52 PM
Subject: RE: [Mono-dev] COM Interop in Mono


Well,
Some things are windows only (BSTR marshalling comes to mind),
at least for now. Should that not be included at this point, or should
it be put inside of an #ifdef? On #mono there was some concern about
different levels of support across platforms.
Also, if I leave this inside of mono for now, any advice on
that? Should I stick all my routines at the bottom of the file for
example, or tag them all with a comment, 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 Paolo
Molaro
Sent: Thursday, November 17, 2005 11:49 AM
To: mono-devel-list at lists.ximian.com
Subject: Re: [Mono-dev] COM Interop in Mono

On 11/16/05 Jonathan S. Chambers wrote:
> 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.

Thanks for the patch.
I don't think this stuff should be moved to a library, at least not
until it's windows-only. Moving to a module may be needed later if we
support other COM-like systems using the same interface, but that is
likely way off.


> Index: metadata/class.c
> ===================================================================
> --- metadata/class.c (revision 52794)
> +++ metadata/class.c (working copy)
> @@ -2682,6 +2682,12 @@
>  g_assert (class->field.count == 0);
>  }
>
> + /* reserve space to store COM object pointer in RCW */
> + if (class->flags & TYPE_ATTRIBUTE_IMPORT &&
!MONO_CLASS_IS_INTERFACE(class)) {
> + class->instance_size += 2 * sizeof (gpointer);

Reserve room just for one pointer.

> Index: metadata/object-internals.h
> ===================================================================
> --- metadata/object-internals.h (revision 52794)
> +++ metadata/object-internals.h (working copy)
> @@ -993,6 +993,17 @@
>  guint32 location;
>  } MonoManifestResourceInfo;
>
> +
> +typedef struct {
> + MonoObject object;
> + MonoString *guid;
> +} MonoReflectionGuidAttribute;
> +
> +typedef struct {
> + MonoObject object;
> + guint16 intType;
> +} MonoInterfaceTypeAttribute;

Add also a:

typedef struct {
MonoObject object;
gpointer comptr;
} MonoCOMWrapper;

and always use it instead of doing pointer arithmetric etc.

> Index: metadata/marshal.c
> ===================================================================
> --- metadata/marshal.c (revision 52794)
> +++ metadata/marshal.c (working copy)
> @@ -6309,6 +6309,279 @@
>  mono_mb_emit_byte (mb, CEE_RET);
>  }
>
> +void
> +component_get_object_and_fnc_ptr(MonoObject *this, MonoMethod*
method, gpointer* pObj, gpointer* pFunc)

Most of these functions should be static. If they need to be exported,
they should be in an header file and have the usual mono_ prefix.

> +{
> + IUnknown * pUnk = NULL;
> + int ** vtable;
> + int i = 0;
> + int offset = 0;
> + GUID clsid;
> +
> + for (i = 0; i < method->klass->interface_count; i++)
> + {

The brace needs to go in the previous line, several of these in the
patch.

> + int first;
> + MonoClass* itf = *(method->klass->interfaces+i);

Change to method->klass->interfaces [i];

> +
> + first = itf->method.first;
> + if (first <= method->slot && first+itf->method.count >
method->slot)

Needs spaces around operators like +.

> + {
> + static MonoClass *GuidAttribute;
> + static MonoClass *InterfaceTypeAttribute;
> + MonoCustomAttrInfo *cinfo;
> + MonoReflectionGuidAttribute *attr;
> + MonoInterfaceTypeAttribute* itf_attr;
> +
> + offset = method->slot - first;
> +
> + if (!GuidAttribute)
> + GuidAttribute = mono_class_from_name
(mono_defaults.corlib, "System.Runtime.InteropServices",
"GuidAttribute");

You need GuidAttribute already in two places: please add it to the
mono_defaults struct. Aso, please use lower case variable names.

> + if (!InterfaceTypeAttribute)
> + InterfaceTypeAttribute =
mono_class_from_name (mono_defaults.corlib,
"System.Runtime.InteropServices", "InterfaceTypeAttribute");
> +
> + if (GuidAttribute) {

No need for this check here: if the attribute is not in corlib there are
bigger issues and we'd have the proper code to check it somewhere _once_
instead of at each loop iteration.

> + cinfo = mono_custom_attrs_from_class
(itf);
> + if (cinfo) {
> + attr =
(MonoReflectionGuidAttribute*)mono_custom_attrs_get_attr (cinfo,
GuidAttribute);
> + itf_attr =
(MonoInterfaceTypeAttribute*)mono_custom_attrs_get_attr (cinfo,
InterfaceTypeAttribute);
> + if (attr) {
> + LPOLESTR temp;
> + wchar_t buf[50];
> +
wsprintf(buf,L"{%s}",attr->guid->chars);

You're not allowed to access MonoString->chars directly. Use
mono_string_chars(). This code though, should avoid creating the object
and just access the guid data inside the proper cinfo->data.
Of course this code is invariant in the loop, so it should be moved out
of it.

> + CLSIDFromString(buf,
&clsid);
> +
((IUnknown*)*((int*)this+sizeof(MonoObject)))->lpVtbl->QueryInterface(((
IUnknown*)*((int*)this+sizeof(MonoObject))), &clsid, &pUnk);

this should be a MonoCOMWrapper, so you can simplify here.

> +
> + if (pUnk)
> + {
> + *pObj = pUnk;
> + vtable =
pUnk->lpVtbl;
> +
> + if (itf_attr &&
itf_attr->intType == 1)
> + offset
+= 3;
> + else
> + offset
+= 7;

Please assign a meaningful name to these magic numbers.

> +void
> +component_create (MonoObject * this)
> +{
> + void * pUnk;
> + int i = 0;
> + GUID clsid;
> +
> +
> + static MonoClass *GuidAttribute;
> + MonoCustomAttrInfo *cinfo;
> + MonoReflectionGuidAttribute *attr;
> + static int coinit = 0;
> +
> + if (!coinit)
> + {
> + CoInitialize(NULL);

How expensive is this call? Maybe it can be called unconditionally on
windows in the mono_runtime_init() function.

> + coinit = 1;
> + }
> +
> + if (!GuidAttribute)
> + GuidAttribute = mono_class_from_name
(mono_defaults.corlib, "System.Runtime.InteropServices",
"GuidAttribute");
> +
> + if (GuidAttribute) {

Same comments as above, move the lookup and check somewhere else.

> +MonoMethod *
> +component_get_native_wrapper (MonoMethod *method)
> +{
> + MonoMethodSignature *sig, *csig;
> + MonoMethodBuilder *mb;
> + MonoMethod *res;
> + GHashTable *cache;
> +
> + g_assert (method != NULL);
> + g_assert (method->klass->flags & TYPE_ATTRIBUTE_IMPORT);
> + g_assert (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
| METHOD_IMPL_ATTRIBUTE_RUNTIME));
> +
> + cache = method->klass->image->native_wrapper_cache;
> + sig = mono_method_signature (method);
> +
> + mb = mono_mb_new (method->klass, method->name,
MONO_WRAPPER_MANAGED_TO_NATIVE);
> + mb->method->save_lmf = 1;
> +
> + g_print ("Finding internal method for COM object
'%s.%s::%s'.\n",method->klass->name_space,
method->klass->name,method->name);
> +
> + if (!strcmp(method->name,".ctor"))
> + {
> + csig = sig;
> +
> + if (sig->hasthis)
> + mono_mb_emit_byte (mb, CEE_LDARG_0);
> +
> + /*for (i = 0; i < sig->param_count; i++)
> + mono_mb_emit_ldarg (mb, i + sig->hasthis);
> + */
> +
> + mono_mb_emit_native_call (mb, csig, component_create);
> + emit_thread_interrupt_checkpoint (mb);
> + mono_mb_emit_byte (mb, CEE_RET);
> +
> + csig = mono_metadata_signature_dup (csig);
> + csig->pinvoke = 0;
> + res = mono_mb_create_and_cache (cache, method,
> +
mb, csig, csig->param_count + 16);
> + mono_mb_free (mb);
> + return res;
> + }
> + else
> + {
> + /* get the function pointer from the object/vtable */
> + static MonoMethodSignature *comsig = NULL;
> + MonoMethodSignature *callsig = NULL;
> + int i = 0;
> +
> + csig = sig;
> +
> + if (!comsig) {
> + comsig = mono_metadata_signature_alloc
(method->klass->image, 3);
> + comsig->hasthis = 1;
> + comsig->params [0] =
&mono_defaults.int_class->byval_arg;
> + comsig->params [1] =
&mono_defaults.int_class->this_arg;
> + comsig->params [2] =
&mono_defaults.int_class->this_arg;
> + comsig->ret =
&mono_defaults.void_class->byval_arg;
> + comsig->pinvoke = 0;
> + }
> +
> + callsig = mono_metadata_signature_alloc
(method->klass->image, csig->param_count + (MONO_TYPE_IS_VOID(csig->ret)
? 0: 1));
> + callsig->hasthis = 1;
> + callsig->ret = &mono_defaults.int_class->byval_arg;
> +
> + /* regular params */
> + for (i = 0; i < csig->param_count; i++)
> + callsig->params[i] = csig->params[i];
> +
> + /* return val as last param by ref*/
> + if (!MONO_TYPE_IS_VOID(csig->ret))
> + callsig->params[csig->param_count] =
&mono_class_from_mono_type(csig->ret)->this_arg;
> +
> + /* not sure about this */
> + callsig->pinvoke = 1;
> +
> + /* COM is stdcall */
> + callsig->call_convention = MONO_CALL_STDCALL;
> +
> + /* for func ptr */
> + mono_mb_add_local (mb,
&mono_defaults.int_class->byval_arg);
> + mono_mb_add_local (mb,
&mono_defaults.int_class->byval_arg);

Always store the return value from mono_mb_add_local() in an
appropriately named local var and always use that, instead of magic
numbers when you need to emit ldloc etc.

Thanks for working on this!

lupus

--
-----------------------------------------------------------------
lupus at debian.org                                     debian/rules
lupus at ximian.com                             Monkeys do it better
_______________________________________________
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