Reflection, InvokeMember, and out parameters

R

Robert May

We have a VB6 COM component that I don't have control over. All of the
values are returned from method calls using byref parameters. They
frequently break binary compatibility, in fact, there are three different
versions of the component, one for dev, one for test, and one for
production, all of which are broken with each re-compile, which means I
can't use early binding and COM interop. Ugh.

Using InvokeMember, how do I get values from an out parameter? I saw the
documentation about ParameterModifier, but I'm afraid that I just don't get
it. :) A code sample would be appreciated.

Here's the code I have so far (the Response.Write value returns an empty
string as the return value, which isn't valid):

Type commonLogonType=
Type.GetTypeFromProgID("ADESecurity.CSecurity");



object commonLogon=
Activator.CreateInstance(commonLogonType);



arrayInput = new Object[] {""};


commonLogonType.InvokeMember("GetUserRightsAsXML",BindingFlags.InvokeMethod,
null,commonLogon,arrayInput,mods,null,null);



Response.Write("Returned XML value=" +
Convert.ToString(arrayInput[arrayInput.GetLowerBound(0)]));

Response.End();

}





Here's the VB method call (the return value is a 0 or non-zero value
indicating success or failure of the call, r_szXMLResult is the actual value
that is returned):



Public Function GetUserRightsAsXML(ByRef r_szXMLResult As Variant, _
Optional ByVal p_szXMLRootName As Variant
= "adesecuritypermissions", _
Optional ByVal p_lEntityID As Variant =
0, _
Optional ByVal p_bDebugDisplay As Boolean
= False _
) As Variant



Any help would be greatly appreciated.



Robert
 
J

Jon Skeet [C# MVP]

Robert May said:
We have a VB6 COM component that I don't have control over. All of the
values are returned from method calls using byref parameters. They
frequently break binary compatibility, in fact, there are three different
versions of the component, one for dev, one for test, and one for
production, all of which are broken with each re-compile, which means I
can't use early binding and COM interop. Ugh.

Using InvokeMember, how do I get values from an out parameter? I saw the
documentation about ParameterModifier, but I'm afraid that I just don't get
it. :) A code sample would be appreciated.

I don't whether COM is screwing you up or not, but normally you just
look at the value of the parameter array afterwards. Here's an example:

using System;
using System.Reflection;

public class Test
{
public static void GiveHello (out string x)
{
x = "hello";
}

static void Main()
{
Type t = typeof(Test);
MethodInfo method = typeof(Test).GetMethod("GiveHello");
object[] p = new object[]{null};
method.Invoke(null, p);
t.InvokeMember("GiveHello",
BindingFlags.InvokeMethod|
BindingFlags.Static|
BindingFlags.Public,
null, null, p);
Console.WriteLine (p[0]);
}
}

I'm not sure why you're using arrayInput.GetLowerBound(0) in your code
rather than just 0, but it certainly looks like it should be working...
 
R

Robert May

It must be something to do with COM. For example, the documentation on
invoke memeber says the following:

ParameterModifier is only used when calling through COM interop, and only
parameters that are passed by reference are handled.

So I need to be able to send in a parameter modifier that tells COM that
this is an out parameter, but I don't know how to use the ParameterModifier.
All it has is an integer that tells how many parameters to modify.

Ah hah! I just figured it out, and it's not terribly intuitive. The help
states in several places that you can set the parameter modifier to pdout,
pdin, pdlcid, etc . . . However, this is in reference to what the binder
does with COM code, not to the parameter modifier itself. It can only be
set to true or false, which I'm guessing simply tells the binder that it
should check to see if a variable is passed by reference. What fun.

As to the GetLowerBounds call, I'm a vb hack, and can never keep my bounds
straight . . . :). It's been changed. Here's the invoke member code that
actually works (starting with the definition of the arguments):

arrayInput = new Object[] {""};

ParameterModifier[] mods=new ParameterModifier[1]{new ParameterModifier(1)};

mods[0][0] = true;

commonLogonType.InvokeMember("GetUserRightsAsXML",BindingFlags.InvokeMethod,
null,commonLogon,arrayInput,mods,null,null);

Thanks for your help.


Robert

Jon Skeet said:
Robert May said:
We have a VB6 COM component that I don't have control over. All of the
values are returned from method calls using byref parameters. They
frequently break binary compatibility, in fact, there are three different
versions of the component, one for dev, one for test, and one for
production, all of which are broken with each re-compile, which means I
can't use early binding and COM interop. Ugh.

Using InvokeMember, how do I get values from an out parameter? I saw the
documentation about ParameterModifier, but I'm afraid that I just don't get
it. :) A code sample would be appreciated.

I don't whether COM is screwing you up or not, but normally you just
look at the value of the parameter array afterwards. Here's an example:

using System;
using System.Reflection;

public class Test
{
public static void GiveHello (out string x)
{
x = "hello";
}

static void Main()
{
Type t = typeof(Test);
MethodInfo method = typeof(Test).GetMethod("GiveHello");
object[] p = new object[]{null};
method.Invoke(null, p);
t.InvokeMember("GiveHello",
BindingFlags.InvokeMethod|
BindingFlags.Static|
BindingFlags.Public,
null, null, p);
Console.WriteLine (p[0]);
}
}

I'm not sure why you're using arrayInput.GetLowerBound(0) in your code
rather than just 0, but it certainly looks like it should be working...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top