[Mono-dev] COM Interop in Mono
Paolo Molaro
lupus at ximian.com
Thu Nov 17 11:48:33 EST 2005
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
More information about the Mono-devel-list
mailing list