I need to call a static void method with an out parameter in another assembly using the ILGenerator. using the code below, I am able to call the method, and it runs correctly, but the out parameter is not returned, even though the local variable gets set. I'm not sure where to go from here to actually get the out parameter value back.
Here is the code i'm using:
public delegate void DynamicMethodDelegate(object _target, params object[] _params);
public class DynamicDelegateStaticFactory
{
public static DynamicMethodDelegate CreateMethodCaller(MethodInfo method)
{
ParameterInfo[] parameters = method.GetParameters();
Type[] args = { typeof(object), typeof(object[]) };
DynamicMethod dynam =
new DynamicMethod
(
method.Name
, method.ReturnType
, args
, typeof(DynamicDelegateStaticFactory)
, true
);
//Add parmeter attributes to the new method from the existing method
for (int i = 0; i < parameters.Length; i++)
{
dynam.DefineParameter
(
i,
parameters[i].Attributes,
parameters[i].Name
);
}
ILGenerator il = dynam.GetILGenerator();
// If method isn't static push target instance on top of stack.
if (!method.IsStatic)
{
// Argument 0 of dynamic method is target instance.
il.Emit(OpCodes.Ldarg_0);
}
// Lay out args array onto stack.
LocalBuilder[] locals = new LocalBuilder[parameters.Length];
for (int i = 0; i < parameters.Length; i++ )
{
//Push args array reference onto the stack, followed
//by the current argument index (i). The Ldelem_Ref opcode
//will resolve them to args[i].
if (parameters[i].IsOut)
{
locals[i] = il.DeclareLocal(parameters[i].ParameterType.GetElementType());
il.Emit(OpCodes.Ldloca, locals[locals.Length - 1].LocalIndex);
}
else
{
// Argument 1 of dynamic method is argument array.
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
}
// If parameter [i] is a value type perform an unboxing.
Type parameterType = parameters[i].ParameterType;
if (parameterType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, parameterType);
}
}
if (method.IsFinal || !method.IsVirtual)
{
il.Emit(OpCodes.Call, method);
}
else
{
il.Emit(OpCodes.Callvirt, method);
}
if (method.ReturnType != typeof(void))
{
// If result is of value type it needs to be boxed
if (method.ReturnType.IsValueType)
{
il.Emit(OpCodes.Box, method.ReturnType);
}
}
else
{
il.Emit(OpCodes.Ldnull);
}
il.Emit(OpCodes.Ret);
return (DynamicMethodDelegate)dynam.CreateDelegate(typeof(DynamicMethodDelegate));
}
And here is how I call it:
//Create the method
MethodInfo methodInfo = LoadXmlMethodInfo;
IlHelper.MethodHelper.DynamicMethodDelegate delegate1 = MethodHelper.DynamicDelegateStaticFactory.CreateMethodCaller(methodInfo);
//Call the method
xmlReaderWarnings = null;
object[] args = new object[] { NewTaxReturn, xml, xmlReaderWarnings };
delegate1(null, args);
//Extract the out parameter (xmlReaderWarnings)
xmlReaderWarnings = (IList)args[2]; //args[2] is reader warning out parameter.
How can I set up the out parameter correctly to retrieve the out value?
Got it! Had to re-assign the local variable to the out variable in my argument array. Final code below
public delegate void DynamicMethodDelegate(object _target, params object[] _params);
public class DynamicDelegateStaticFactory
{
public static DynamicMethodDelegate CreateMethodCaller(MethodInfo method)
{
ParameterInfo[] parameters = method.GetParameters();
Type[] args = { typeof(object), typeof(object[]) };
DynamicMethod dynam =
new DynamicMethod
(
method.Name
, method.ReturnType
, args
, typeof(DynamicDelegateStaticFactory)
, true
);
//Add parmeter attributes to the new method from the existing method
for (int i = 0; i < parameters.Length; i++)
{
dynam.DefineParameter
(
i,
parameters[i].Attributes,
parameters[i].Name
);
}
ILGenerator il = dynam.GetILGenerator();
// If method isn't static push target instance on top of stack.
if (!method.IsStatic)
{
// Argument 0 of dynamic method is target instance.
il.Emit(OpCodes.Ldarg_0);
}
// Lay out args array onto stack.
LocalBuilder[] locals = new LocalBuilder[parameters.Length];
List<LocalBuilder> outOrRefLocals = new List<LocalBuilder>();
for (int i = 0; i < parameters.Length; i++ )
{
//Push args array reference onto the stack, followed
//by the current argument index (i). The Ldelem_Ref opcode
//will resolve them to args[i].
if (!parameters[i].IsOut)
{
// Argument 1 of dynamic method is argument array.
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
}
// If parameter [i] is a value type perform an unboxing.
Type parameterType = parameters[i].ParameterType;
if (parameterType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, parameterType);
}
}
//Create locals for out parameters
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].IsOut)
{
locals[i] = il.DeclareLocal(parameters[i].ParameterType.GetElementType());
il.Emit(OpCodes.Ldloca, locals[locals.Length - 1]);
}
}
if (method.IsFinal || !method.IsVirtual)
{
il.Emit(OpCodes.Call, method);
}
else
{
il.Emit(OpCodes.Callvirt, method);
}
for (int idx = 0; idx < parameters.Length; ++idx)
{
if (parameters[idx].IsOut || parameters[idx].ParameterType.IsByRef)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, idx);
il.Emit(OpCodes.Ldloc, locals[idx].LocalIndex);
if (parameters[idx].ParameterType.GetElementType().IsValueType)
il.Emit(OpCodes.Box, parameters[idx].ParameterType.GetElementType());
il.Emit(OpCodes.Stelem_Ref);
}
}
if (method.ReturnType != typeof(void))
{
// If result is of value type it needs to be boxed
if (method.ReturnType.IsValueType)
{
il.Emit(OpCodes.Box, method.ReturnType);
}
}
else
{
il.Emit(OpCodes.Ldnull);
}
il.Emit(OpCodes.Ret);
return (DynamicMethodDelegate)dynam.CreateDelegate(typeof(DynamicMethodDelegate));
}
private static void EmitUnboxOrCast(ILGenerator il, Type typeOnStack)
{
if (typeOnStack.IsValueType || typeOnStack.IsGenericParameter)
{
il.Emit(OpCodes.Unbox_Any, typeOnStack);
}
else
{
il.Emit(OpCodes.Castclass, typeOnStack);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With