+/**
+ * cominterop_get_native_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_native_wrapper_adjusted (MonoMethod *method)
+{
+ MonoMethod *res;
+ MonoMethodBuilder *mb_native;
+ MonoMarshalSpec **mspecs;
+ MonoMethodSignature *sig, *sig_native;
+ MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method;
+ int i;
+
+ sig = mono_method_signature (method);
+
+ // create unmanaged wrapper
+ mb_native = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE);
+ sig_native = signature_cominterop (method->klass->image, sig);
+
+ mspecs = g_new (MonoMarshalSpec*, sig_native->param_count+1);
+ memset (mspecs, 0, sizeof(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 (!MONO_TYPE_IS_VOID (sig->ret))
+ mspecs[sig_native->param_count] = mspecs[0];
+
+ mspecs[0] = NULL;
+
+ mono_marshal_emit_native_wrapper(mb_native, sig_native, piinfo, mspecs, piinfo->addr);
+
+ mono_loader_lock ();
+ mono_marshal_lock ();
+ res = mono_mb_create_method (mb_native, sig_native, sig_native->param_count + 16);
+ mono_marshal_unlock ();
+ mono_loader_unlock ();
+
+ mono_mb_free (mb_native);
+
+ for (i = sig_native->param_count; i >= 0; i--)
+ if (mspecs [i])
+ mono_metadata_free_marshal_spec (mspecs [i]);
+ g_free (mspecs);
+
+ return res;
+}
+
+/**
+ * cominterop_get_native_wrapper:
+ * @method: managed method
+ *
+ * Returns: the generated method to call
+ */
+static MonoMethod *
+cominterop_get_native_wrapper (MonoMethod *method)
+{
+ MonoMethod *res;
+ GHashTable *cache;
+ MonoMethodBuilder *mb;
+ MonoMethodSignature *sig, *csig;
+
+ g_assert (method);
+
+ cache = method->klass->image->cominterop_wrapper_cache;
+ if ((res = mono_marshal_find_in_cache (cache, method)))
+ return res;
+
+ sig = mono_method_signature (method);
+ mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_COMINTEROP);
+
+ /* if method klass is import, that means method
+ * is really a com call. let interop system emit it.
+ */
+ if (MONO_CLASS_IS_IMPORT(method->klass)) {
+ /* FIXME: we have to call actual class .ctor
+ * instead of just __ComObject .ctor.
+ */
+ if (!strcmp(method->name, ".ctor")) {
+ static MonoMethod *ctor = NULL;
+
+ if (!ctor)
+ ctor = mono_class_get_method_from_name (mono_defaults.com_object_class, ".ctor", 0);
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_managed_call (mb, ctor, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+ }
+ else {
+ static MonoMethod * ThrowExceptionForHR = NULL;
+ static MonoMethod * GetInterface = NULL;
+ MonoMethod *adjusted_method;
+ int hr;
+ int retval = 0;
+ int ptr_this;
+ int i;
+ if (!GetInterface)
+ GetInterface = mono_class_get_method_from_name (mono_defaults.com_object_class, "GetInterface", 1);
+
+ // add local variables
+ hr = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
+ ptr_this = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ retval = mono_mb_add_local (mb, sig->ret);
+
+ // get the type for the interface the method is defined on
+ // and then get the underlying COM interface for that type
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ptr (mb, method);
+ mono_mb_emit_icall (mb, cominterop_get_method_interface);
+ mono_mb_emit_managed_call (mb, GetInterface, NULL);
+ mono_mb_emit_stloc (mb, ptr_this);
+
+ // arg 1 is unmanaged this pointer
+ mono_mb_emit_ldloc (mb, ptr_this);
+
+ // load args
+ for (i = 1; i <= sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i);
+
+ // push managed return value as byref last argument
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_ldloc_addr (mb, retval);
+
+ adjusted_method = cominterop_get_native_wrapper_adjusted (method);
+ mono_mb_emit_managed_call (mb, adjusted_method, NULL);
+
+ // store HRESULT to check
+ mono_mb_emit_stloc (mb, hr);
+
+ if (!ThrowExceptionForHR)
+ ThrowExceptionForHR = mono_class_get_method_from_name (mono_defaults.marshal_class, "ThrowExceptionForHR", 1);
+ mono_mb_emit_ldloc (mb, hr);
+ mono_mb_emit_managed_call (mb, ThrowExceptionForHR, NULL);
+
+ // load return value managed is expecting
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_ldloc (mb, retval);
+
+ mono_mb_emit_byte (mb, CEE_RET);
+ }
+
+
+ }
+ /* Does this case ever get hit? */
+ else {
+ char *msg = g_strdup ("non imported interfaces on \
+ imported classes is not yet implemented.");
+ mono_mb_emit_exception (mb, "NotSupportedException", msg);
+ }
+ csig = signature_dup (method->klass->image, sig);
+ csig->pinvoke = 0;
+ res = mono_mb_create_and_cache (cache, method,
+ mb, csig, csig->param_count + 16);
+ mono_mb_free (mb);
+ return res;
+}
+
+/**
+ * cominterop_get_invoke:
+ * @method: managed method
+ *
+ * Returns: the generated method that calls the underlying __ComObject
+ * rather than the proxy object.
+ */
+static MonoMethod *
+cominterop_get_invoke (MonoMethod *method)
+{
+ MonoMethodSignature *sig;
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ int i, temp_obj;
+ GHashTable* cache = method->klass->image->cominterop_invoke_cache;
+
+ g_assert (method);
+
+ if ((res = mono_marshal_find_in_cache (cache, method)))
+ return res;
+
+ sig = signature_no_pinvoke (method);
+
+ /* we cant remote methods without this pointer */
+ if (!sig->hasthis)
+ return method;
+
+ mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_COMINTEROP_INVOKE);
+
+ /* get real proxy object, which is a ComInteropProxy in this case*/
+ temp_obj = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg);
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoTransparentProxy, rp));
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+
+ /* load the RCW from the ComInteropProxy*/
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoComInteropProxy, com_object));
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+
+ /* load args and make the call on the RCW */
+ for (i = 1; i <= sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i);
+
+ if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
+ MonoMethod * native_wrapper = cominterop_get_native_wrapper(method);
+ mono_mb_emit_managed_call (mb, native_wrapper, NULL);
+ }
+ else {
+ if (method->flags & METHOD_ATTRIBUTE_VIRTUAL)
+ mono_mb_emit_op (mb, CEE_CALLVIRT, method);
+ else
+ mono_mb_emit_op (mb, CEE_CALL, method);
+ }
+
+ if (!strcmp(method->name, ".ctor")) {
+ static MonoClass *com_interop_proxy_class = NULL;
+ static MonoMethod *cache_proxy = NULL;
+
+ if (!com_interop_proxy_class)
+ com_interop_proxy_class = mono_class_from_name (mono_defaults.corlib, "Mono.Interop", "ComInteropProxy");
+ if (!cache_proxy)
+ cache_proxy = mono_class_get_method_from_name (com_interop_proxy_class, "CacheProxy", 0);
+
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoTransparentProxy, rp));
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ mono_mb_emit_managed_call (mb, cache_proxy, NULL);
+ }
+
+ emit_thread_interrupt_checkpoint (mb);
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ res = mono_mb_create_and_cache (cache, method, mb, sig, sig->param_count + 16);
+ mono_mb_free (mb);
+
+ return res;
+}
+