- if (verify) {
- ccw = g_hash_table_lookup (ccw_interface_hash, ccw_entry);
- }
- else {
- ccw = ccw_entry->ccw;
- g_assert (ccw);
- }
- if (ccw)
- return mono_gchandle_get_target (ccw->gc_handle);
- else
- return NULL;
-}
-
-static void
-cominterop_setup_marshal_context (EmitMarshalContext *m, MonoMethod *method)
-{
- MonoMethodSignature *sig, *csig;
- sig = mono_method_signature (method);
- /* we copy the signature, so that we can modify it */
- /* FIXME: which to use? */
- csig = signature_dup (method->klass->image, sig);
- /* csig = mono_metadata_signature_dup (sig); */
-
- /* STDCALL on windows, CDECL everywhere else to work with XPCOM and MainWin COM */
-#ifdef PLATFORM_WIN32
- csig->call_convention = MONO_CALL_STDCALL;
-#else
- csig->call_convention = MONO_CALL_C;
-#endif
- csig->hasthis = 0;
- csig->pinvoke = 1;
-
- m->image = method->klass->image;
- m->piinfo = NULL;
- m->retobj_var = 0;
- m->sig = sig;
- m->csig = csig;
-}
-
-/**
- * cominterop_get_ccw:
- * @object: a pointer to the object
- * @itf: interface type needed
- *
- * Returns: a value indicating if the object is a
- * Runtime Callable Wrapper (RCW) for a COM object
- */
-static gpointer
-cominterop_get_ccw (MonoObject* object, MonoClass* itf)
-{
- int i;
- MonoCCW *ccw = NULL;
- MonoCCWInterface* ccw_entry = NULL;
- gpointer *vtable = NULL;
- static gpointer iunknown[3] = {NULL, NULL, NULL};
- static gpointer idispatch[4] = {NULL, NULL, NULL, NULL};
- MonoClass* iface = NULL;
- MonoClass* klass = NULL;
- EmitMarshalContext m;
- int start_slot = 3;
- int method_count = 0;
- GList *ccw_list, *ccw_list_item;
- MonoCustomAttrInfo *cinfo = NULL;
-
- if (!object)
- return NULL;
-
- klass = mono_object_get_class (object);
-
- if (!ccw_hash)
- ccw_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
- if (!ccw_interface_hash)
- ccw_interface_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
-
- ccw_list = g_hash_table_lookup (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)));
-
- ccw_list_item = ccw_list;
- while (ccw_list_item) {
- MonoCCW* ccw_iter = ccw_list_item->data;
- if (mono_gchandle_get_target (ccw_iter->gc_handle) == object) {
- ccw = ccw_iter;
- break;
- }
- ccw_list_item = g_list_next(ccw_list_item);
- }
-
- if (!iunknown [0]) {
- iunknown [0] = cominterop_ccw_queryinterface;
- iunknown [1] = cominterop_ccw_addref;
- iunknown [2] = cominterop_ccw_release;
- }
-
- if (!idispatch [0]) {
- idispatch [0] = cominterop_ccw_get_type_info_count;
- idispatch [1] = cominterop_ccw_get_type_info;
- idispatch [2] = cominterop_ccw_get_ids_of_names;
- idispatch [3] = cominterop_ccw_invoke;
- }
-
- if (!ccw) {
- ccw = g_new0 (MonoCCW, 1);
-#ifdef PLATFORM_WIN32
- ccw->free_marshaler = 0;
-#endif
- ccw->vtable_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
- ccw->ref_count = 0;
- /* just alloc a weak handle until we are addref'd*/
- ccw->gc_handle = mono_gchandle_new_weakref (object, FALSE);
-
- if (!ccw_list) {
- ccw_list = g_list_alloc ();
- ccw_list->data = ccw;
- }
- else
- ccw_list = g_list_append (ccw_list, ccw);
- g_hash_table_insert (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)), ccw_list);
- /* register for finalization to clean up ccw */
- mono_object_register_finalizer (object);
- }
-
- cinfo = mono_custom_attrs_from_class (itf);
- if (cinfo) {
- static MonoClass* coclass_attribute = NULL;
- if (!coclass_attribute)
- coclass_attribute = mono_class_from_name (mono_defaults.corlib, "System.Runtime.InteropServices", "CoClassAttribute");
- if (mono_custom_attrs_has_attr (cinfo, coclass_attribute)) {
- g_assert(itf->interface_count && itf->interfaces[0]);
- itf = itf->interfaces[0];
- }
- if (!cinfo->cached)
- mono_custom_attrs_free (cinfo);
- }
-
- iface = itf;
- if (iface == mono_defaults.iunknown_class) {
- start_slot = 3;
- }
- else if (iface == mono_defaults.idispatch_class) {
- start_slot = 7;
- }
- else {
- method_count += iface->method.count;
- start_slot = cominterop_get_com_slot_begin (iface);
- iface = NULL;
- }
-
- ccw_entry = g_hash_table_lookup (ccw->vtable_hash, itf);
-
- if (!ccw_entry) {
- int vtable_index = method_count-1+start_slot;
- mono_loader_lock ();
- vtable = mono_image_alloc0 (klass->image, sizeof (gpointer)*(method_count+start_slot));
- mono_loader_unlock ();
- memcpy (vtable, iunknown, sizeof (iunknown));
- if (start_slot == 7)
- memcpy (vtable+3, idispatch, sizeof (idispatch));
-
- iface = itf;
- for (i = iface->method.count-1; i >= 0;i--) {
- int param_index = 0;
- MonoMethodBuilder *mb;
- MonoMarshalSpec ** mspecs;
- MonoMethod *wrapper_method, *adjust_method;
- MonoMethod *method = iface->methods [i];
- MonoMethodSignature* sig_adjusted;
- MonoMethodSignature* sig = mono_method_signature (method);
- gboolean preserve_sig = method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG;
-
-
- mb = mono_mb_new (iface, method->name, MONO_WRAPPER_NATIVE_TO_MANAGED);
- adjust_method = cominterop_get_managed_wrapper_adjusted (method);
- sig_adjusted = mono_method_signature (adjust_method);
-
- mspecs = g_new (MonoMarshalSpec*, sig_adjusted->param_count + 1);
- mono_method_get_marshal_info (method, mspecs);
-
-
- /* move managed args up one */
- for (param_index = sig->param_count; param_index >= 1; param_index--) {
- int mspec_index = param_index+1;
- mspecs [mspec_index] = mspecs [param_index];
-
- if (mspecs[mspec_index] == NULL) {
- if (sig_adjusted->params[param_index]->type == MONO_TYPE_OBJECT) {
- mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1);
- mspecs[mspec_index]->native = MONO_NATIVE_STRUCT;
- }
- else if (sig_adjusted->params[param_index]->type == MONO_TYPE_STRING) {
- mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1);
- mspecs[mspec_index]->native = MONO_NATIVE_BSTR;
- }
- else if (sig_adjusted->params[param_index]->type == MONO_TYPE_CLASS) {
- mspecs[mspec_index] = g_new0 (MonoMarshalSpec, 1);
- mspecs[mspec_index]->native = MONO_NATIVE_INTERFACE;
- }
- }
- }
-
- /* first arg is IntPtr for interface */
- mspecs [1] = NULL;
-
- /* move return spec to last param */
- if (!preserve_sig && !MONO_TYPE_IS_VOID (sig->ret)) {
- if (mspecs [0] == NULL) {
- if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_OBJECT) {
- mspecs[0] = g_new0 (MonoMarshalSpec, 1);
- mspecs[0]->native = MONO_NATIVE_STRUCT;
- }
- else if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_STRING) {
- mspecs[0] = g_new0 (MonoMarshalSpec, 1);
- mspecs[0]->native = MONO_NATIVE_BSTR;
- }
- else if (sig_adjusted->params[sig_adjusted->param_count-1]->type == MONO_TYPE_CLASS) {
- mspecs[0] = g_new0 (MonoMarshalSpec, 1);
- mspecs[0]->native = MONO_NATIVE_INTERFACE;
- }
- }
-
- mspecs [sig_adjusted->param_count] = mspecs [0];
- mspecs [0] = NULL;
- }
-
- cominterop_setup_marshal_context (&m, adjust_method);
- m.mb = mb;
- mono_marshal_emit_managed_wrapper (mb, sig_adjusted, mspecs, &m, adjust_method, NULL);
- mono_loader_lock ();
- mono_marshal_lock ();
- wrapper_method = mono_mb_create_method (mb, m.csig, m.csig->param_count + 16);
- mono_marshal_unlock ();
- mono_loader_unlock ();
-
- /* skip visiblity since we call internal methods */
- wrapper_method->skip_visibility = TRUE;
-
- vtable [vtable_index--] = mono_compile_method (wrapper_method);
-
-
- for (param_index = sig_adjusted->param_count; param_index >= 0; param_index--)
- if (mspecs [param_index])
- mono_metadata_free_marshal_spec (mspecs [param_index]);
- g_free (mspecs);
- }
-
- ccw_entry = g_new0 (MonoCCWInterface, 1);
- ccw_entry->ccw = ccw;
- ccw_entry->vtable = vtable;
- g_hash_table_insert (ccw->vtable_hash, itf, ccw_entry);
- g_hash_table_insert (ccw_interface_hash, ccw_entry, ccw);
- }
-
- return ccw_entry;
-}
-
-static gboolean
-mono_marshal_free_ccw_entry (gpointer key, gpointer value, gpointer user_data)
-{
- g_assert (value);
- g_free (value);
- return TRUE;
-}
-
-/**
- * mono_marshal_free_ccw:
- * @object: the mono object
- *
- * Returns: whether the object had a CCW
- */
-gboolean
-mono_marshal_free_ccw (MonoObject* object)
-{
- GList *ccw_list, *ccw_list_orig, *ccw_list_item;
- /* no ccw's were created */
- if (!ccw_hash || g_hash_table_size (ccw_hash) == 0)
- return FALSE;
-
- /* need to cache orig list address to remove from hash_table if empty */
- mono_cominterop_lock ();
- ccw_list = ccw_list_orig = g_hash_table_lookup (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)));
- mono_cominterop_unlock ();
-
- if (!ccw_list)
- return FALSE;
-
- ccw_list_item = ccw_list;
- while (ccw_list_item) {
- MonoCCW* ccw_iter = ccw_list_item->data;
- MonoObject* handle_target = mono_gchandle_get_target (ccw_iter->gc_handle);
-
- /* Looks like the GC NULLs the weakref handle target before running the
- * finalizer. So if we get a NULL target, destroy the CCW as well. */
- if (!handle_target || handle_target == object) {
- /* remove all interfaces */
- g_hash_table_foreach_remove (ccw_iter->vtable_hash, mono_marshal_free_ccw_entry, NULL);
- g_hash_table_destroy (ccw_iter->vtable_hash);
-
- /* get next before we delete */
- ccw_list_item = g_list_next(ccw_list_item);
-
- /* remove ccw from list */
- ccw_list = g_list_remove (ccw_list, ccw_iter);
- g_free (ccw_iter);
- }
- else
- ccw_list_item = g_list_next(ccw_list_item);
- }
-
- /* if list is empty remove original address from hash */
- if (g_list_length (ccw_list) == 0)
- g_hash_table_remove (ccw_hash, GINT_TO_POINTER (mono_object_hash (object)));
-
-
- return TRUE;
-}
-
-/**
- * cominterop_get_managed_wrapper_adjusted:
- * @method: managed COM Interop method
- *
- * Returns: the generated method to call with signature matching
- * the unmanaged COM Method signature
- */
-static MonoMethod *
-cominterop_get_managed_wrapper_adjusted (MonoMethod *method)
-{
- static MonoMethod *get_hr_for_exception = NULL;
- MonoMethod *res = NULL;
- MonoMethodBuilder *mb;
- MonoMarshalSpec **mspecs;
- MonoMethodSignature *sig, *sig_native;
- MonoExceptionClause *main_clause = NULL;
- MonoMethodHeader *header;
- int pos_leave;
- int hr = 0;
- int i;
- gboolean preserve_sig = method->iflags & METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG;
-
- if (!get_hr_for_exception)
- get_hr_for_exception = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetHRForException", -1);
-
- sig = mono_method_signature (method);
-
- /* create unmanaged wrapper */
- mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_COMINTEROP);
-
- sig_native = cominterop_method_signature (method);
-
- mspecs = g_new0 (MonoMarshalSpec*, sig_native->param_count+1);
-
- mono_method_get_marshal_info (method, mspecs);
-
- /* move managed args up one */
- for (i = sig->param_count; i >= 1; i--)
- mspecs [i+1] = mspecs [i];
-
- /* first arg is IntPtr for interface */
- mspecs [1] = NULL;
-
- /* move return spec to last param */
- if (!preserve_sig && !MONO_TYPE_IS_VOID (sig->ret))
- mspecs [sig_native->param_count] = mspecs [0];
-
- mspecs [0] = NULL;
-
- if (!preserve_sig) {
- hr = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
-
- /* try */
- main_clause = g_new0 (MonoExceptionClause, 1);
- main_clause->try_offset = mono_mb_get_label (mb);
- }
-
- /* load last param to store result if not preserve_sig and not void */
- if (!preserve_sig && !MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_ldarg (mb, sig_native->param_count-1);
-
- /* the CCW -> object conversion */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_icon (mb, FALSE);
- mono_mb_emit_icall (mb, cominterop_get_ccw_object);
-
- for (i = 0; i < sig->param_count; i++)
- mono_mb_emit_ldarg (mb, i+1);
-
- mono_mb_emit_managed_call (mb, method, NULL);
-
- if (!preserve_sig) {
- /* store result if not preserve_sig and we have one */
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_byte (mb, mono_type_to_stind (sig->ret));
-
- pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
-
- /* Main exception catch */
- main_clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
- main_clause->try_len = mono_mb_get_pos (mb) - main_clause->try_offset;
- main_clause->data.catch_class = mono_defaults.object_class;
-
- /* handler code */
- main_clause->handler_offset = mono_mb_get_label (mb);
- mono_mb_emit_managed_call (mb, get_hr_for_exception, NULL);
- mono_mb_emit_stloc (mb, hr);
- mono_mb_emit_branch (mb, CEE_LEAVE);
- main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset;
- /* end catch */
-
- mono_mb_patch_branch (mb, pos_leave);
-
- mono_mb_emit_ldloc (mb, hr);
- }
-
- mono_mb_emit_byte (mb, CEE_RET);
-
- mono_loader_lock ();
- mono_marshal_lock ();
- res = mono_mb_create_method (mb, sig_native, sig_native->param_count + 16);
- mono_marshal_unlock ();
- mono_loader_unlock ();
-
- mono_mb_free (mb);
-
- for (i = sig_native->param_count; i >= 0; i--)
- if (mspecs [i])
- mono_metadata_free_marshal_spec (mspecs [i]);
- g_free (mspecs);
-
- if (!preserve_sig) {
- header = ((MonoMethodNormal *)res)->header;
- header->num_clauses = 1;
- header->clauses = main_clause;
- }
-
- return res;
-}
-
-/**
- * cominterop_mono_string_to_guid:
- *
- * Converts the standard string representation of a GUID
- * to a 16 byte Microsoft GUID.
- */
-static void
-cominterop_mono_string_to_guid (const MonoString* string, guint8 *guid) {
- gunichar2 * chars = mono_string_chars (string);
- int i = 0;
- static guint8 indexes[16] = {7, 5, 3, 1, 12, 10, 17, 15, 20, 22, 25, 27, 29, 31, 33, 35};
-
- for (i = 0; i < sizeof(indexes); i++)
- guid [i] = g_unichar_xdigit_value (chars [indexes [i]]) + (g_unichar_xdigit_value (chars [indexes [i] - 1]) << 4);
-}
-
-static gboolean
-cominterop_class_guid_equal (guint8* guid, MonoClass* klass)
-{
- guint8 klass_guid [16];
- if (cominterop_class_guid (klass, klass_guid))
- return !memcmp (guid, klass_guid, sizeof (klass_guid));
- return FALSE;
-}
-
-static int STDCALL
-cominterop_ccw_addref (MonoCCWInterface* ccwe)
-{
- gint32 ref_count = 0;
- MonoCCW* ccw = ccwe->ccw;
- g_assert (ccw);
- g_assert (ccw->gc_handle);
- g_assert (ccw->ref_count >= 0);
- ref_count = InterlockedIncrement ((gint32*)&ccw->ref_count);
- if (ref_count == 1) {
- guint32 oldhandle = ccw->gc_handle;
- g_assert (oldhandle);
- /* since we now have a ref count, alloc a strong handle*/
- ccw->gc_handle = mono_gchandle_new (mono_gchandle_get_target (oldhandle), FALSE);
- mono_gchandle_free (oldhandle);
- }
- return ref_count;
-}
-
-static int STDCALL
-cominterop_ccw_release (MonoCCWInterface* ccwe)
-{
- gint32 ref_count = 0;
- MonoCCW* ccw = ccwe->ccw;
- g_assert (ccw);
- g_assert (ccw->ref_count > 0);
- ref_count = InterlockedDecrement ((gint32*)&ccw->ref_count);
- if (ref_count == 0) {
- /* allow gc of object */
- guint32 oldhandle = ccw->gc_handle;
- g_assert (oldhandle);
-#ifdef PLATFORM_WIN32
- if (ccw->free_marshaler)
- ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal (ccw->free_marshaler);
-#endif
- ccw->gc_handle = mono_gchandle_new_weakref (mono_gchandle_get_target (oldhandle), FALSE);
- mono_gchandle_free (oldhandle);
- }
- return ref_count;
-}
-
-#define MONO_S_OK 0x00000000L
-#define MONO_E_NOINTERFACE 0x80004002L
-#define MONO_E_NOTIMPL 0x80004001L
-
-#ifdef PLATFORM_WIN32
-static const IID MONO_IID_IMarshal = {0x3, 0x0, 0x0, {0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}};
-#endif
-
-#ifdef PLATFORM_WIN32
-/* All ccw objects are free threaded */
-static int
-cominterop_ccw_getfreethreadedmarshaler (MonoCCW* ccw, MonoObject* object, gpointer* ppv)
-{
-#ifdef PLATFORM_WIN32
- if (!ccw->free_marshaler) {
- int ret = 0;
- gpointer tunk;
- tunk = cominterop_get_ccw (object, mono_defaults.iunknown_class);
- /* remember to addref on QI */
- cominterop_ccw_addref (tunk);
- ret = CoCreateFreeThreadedMarshaler (tunk, (LPUNKNOWN*)&ccw->free_marshaler);
- cominterop_ccw_release(tunk);
- }
-
- if (!ccw->free_marshaler)
- return MONO_E_NOINTERFACE;
-
- return ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (ccw->free_marshaler, (IID*)&MONO_IID_IMarshal, ppv);
-#else
- return MONO_E_NOINTERFACE;
-#endif
-}
-#endif
-
-static int STDCALL
-cominterop_ccw_queryinterface (MonoCCWInterface* ccwe, guint8* riid, gpointer* ppv)
-{
- GPtrArray *ifaces;
- MonoClass *itf = NULL;
- int i;
- MonoCCW* ccw = ccwe->ccw;
- MonoClass* klass = NULL;
- MonoObject* object = mono_gchandle_get_target (ccw->gc_handle);
-
- g_assert (object);
- klass = mono_object_class (object);
-
- if (ppv)
- *ppv = NULL;
-
- if (!mono_domain_get ())
- mono_thread_attach (mono_get_root_domain ());
-
- /* handle IUnknown special */
- if (cominterop_class_guid_equal (riid, mono_defaults.iunknown_class)) {
- *ppv = cominterop_get_ccw (object, mono_defaults.iunknown_class);
- /* remember to addref on QI */
- cominterop_ccw_addref (*ppv);
- return MONO_S_OK;
- }
-
- /* handle IDispatch special */
- if (cominterop_class_guid_equal (riid, mono_defaults.idispatch_class)) {
- *ppv = cominterop_get_ccw (object, mono_defaults.idispatch_class);
- /* remember to addref on QI */
- cominterop_ccw_addref (*ppv);
- return MONO_S_OK;
- }
-
-#ifdef PLATFORM_WIN32
- /* handle IMarshal special */
- if (0 == memcmp (riid, &MONO_IID_IMarshal, sizeof (IID))) {
- return cominterop_ccw_getfreethreadedmarshaler (ccw, object, ppv);
- }
-#endif
-
- ifaces = mono_class_get_implemented_interfaces (klass);
- if (ifaces) {
- for (i = 0; i < ifaces->len; ++i) {
- MonoClass *ic = NULL;
- ic = g_ptr_array_index (ifaces, i);
- if (cominterop_class_guid_equal (riid, ic)) {
- itf = ic;
- break;
- }
- }
- g_ptr_array_free (ifaces, TRUE);
- }
- if (itf) {
- *ppv = cominterop_get_ccw (object, itf);
- /* remember to addref on QI */
- cominterop_ccw_addref (*ppv);
- return MONO_S_OK;
- }
-
- return MONO_E_NOINTERFACE;
-}
-
-static int STDCALL
-cominterop_ccw_get_type_info_count (MonoCCWInterface* ccwe, guint32 *pctinfo)
-{
- return MONO_E_NOTIMPL;
-}
-
-static int STDCALL
-cominterop_ccw_get_type_info (MonoCCWInterface* ccwe, guint32 iTInfo, guint32 lcid, gpointer *ppTInfo)
-{
- return MONO_E_NOTIMPL;
-}
-
-static int STDCALL
-cominterop_ccw_get_ids_of_names (MonoCCWInterface* ccwe, gpointer riid,
- gunichar2** rgszNames, guint32 cNames,
- guint32 lcid, gint32 *rgDispId)
-{
- return MONO_E_NOTIMPL;
-}
-
-static int STDCALL
-cominterop_ccw_invoke (MonoCCWInterface* ccwe, guint32 dispIdMember,
- gpointer riid, guint32 lcid,
- guint16 wFlags, gpointer pDispParams,
- gpointer pVarResult, gpointer pExcepInfo,
- guint32 *puArgErr)
-{
- return MONO_E_NOTIMPL;
-}
-
-#else /* DISABLE_COM */
-
-gboolean
-mono_marshal_free_ccw (MonoObject* object)
-{
- return FALSE;
-}
-
-#endif /* DISABLE_COM */
-
-void
-mono_marshal_find_nonzero_bit_offset (guint8 *buf, int len, int *byte_offset, guint8 *bitmask)
-{
- int i;
- guint8 byte;
-
- for (i = 0; i < len; ++i)
- if (buf [i])
- break;
-
- g_assert (i < len);