Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I retrieve the value for parameters of type out or ref using Type.InvokeMember?

A long title, but I wanted it to be specific. The title is really the question. Even though the method that InvokeMember is calling has an out parameter and is assigning a value to to that parameter I can't grab that value. Here is the code I was initially using:

string parameter = "";
int result = Convert.ToInt32(typeof(Ability).InvokeMember(selectedMove, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { parameter }));

I changed it this, which now makes it work as intended but I don't know why:

object[] args = new object[1];      //necessary to retrieve ref/out parameter
int result = Convert.ToInt32(typeof(Ability).InvokeMember(selectedMove, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, args));
like image 545
Greener Avatar asked Mar 26 '12 22:03

Greener


3 Answers

I just wanted to help someone who is struggling(I did) with unmanaged(COM) and getting ref-parameter back. So, when using InvokeMember against COM-method, you have to tell which arguments are ref-type. This is achieved by using ParameterModifier-class, For example:

object[] args = new object[3] { param1, param2, errorStr };
ParameterModifier pMod = new ParameterModifier(3);
pMod[2] = true;            
ParameterModifier[] mods = { pMod };

object tempObj = myCOMObject.GetType().InvokeMember("MyCOMMethod", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public, null, myCOMObject, args, mods, null, null);

In the code above, the 3rd argument is set to be a reference (pMod[2] = true;)

like image 132
RonVibbentrop Avatar answered Oct 23 '22 03:10

RonVibbentrop


Your second snippet is missing a rather essential line of code. It should look like this, assuming the out argument is of type string:

object[] args = new object[1];      //necessary to retrieve ref/out parameter
int result = Convert.ToInt32(typeof(Ability).InvokeMember(selectedMove, 
    BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, 
    null, null, args));
string outValue = (string)args[0];  // <===  here!

It should now also be obvious why your 1st snippet cannot work, you don't have a reference to the object[] array that you pass so you can never retrieve the modified argument.

like image 42
Hans Passant Avatar answered Oct 23 '22 02:10

Hans Passant


In your first code example, the call to InvokeMember doesn't modify the value of the parameter variable, it just replaces the first item in the parameter array (which now points to a different string instance). Since you didn't keep a reference to this array, you can't retrieve the value of the output parameter.

In other words: the array initially contains a copy of the parameter variable (i.e. a copy of the reference to an empty string). After the call, parameter and the value in the array refer to 2 different string instances.

like image 29
Thomas Levesque Avatar answered Oct 23 '22 04:10

Thomas Levesque