Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Reflection, GetMember() and COM Objects?


:P
On this page:

I'm working on an old app that interfaces with a legacy COM object. In reviewing some of my wwDataBinder code I noticed that it didn't work against COM objects for databinding. With a few minor changes I've been able to make the binding code work by using the the higher level Type.InvokeMember method which works with COM objects for getting and setting properties and calling COM objects (although that method is quite a bit slower).

However, for unbinding I need to figure out first what the type is for the actual value I'm binding back to. So I'm using code like this to retrieve the type info for a binding source property:

// *** It's an object property or field - get it
MemberInfo[] MInfo = BindingSourceObject.GetType().GetMember(BindingSourceMember, 
   BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
BindingFlags.Instance | BindingFlags.IgnoreCase);

if
(MInfo[0].MemberType == MemberTypes.Field)
{
   FieldInfo Field = (FieldInfo)MInfo[0];               
   typBindingSource = Field.FieldType;
}
else
{
   PropertyInfo loField = (PropertyInfo)MInfo[0];               
   typBindingSource = loField.PropertyType;
}

This code works fine with plain .NET objects but it fails against COM objects.

In fact I can't figure out how to do any sort of reflection against the COM object, short of using Type().InvokeMember. Any attempt to move over the type results in nothing being returned at all.

Basically I have an instance of a COM object and I need to get type information on one of the properties of that object.

Anybody know of a workaround for this with COM?

I've run into this situation before with some of my generic Reflection routines that I use to Get/Set properties/fields and call methods. In the end the easiest solution was just to create a seperate set of methods that work against COM objects. However in this case unfortunately I need to get at the lower level features.

Updated: 9/17/2007

I found a somewhat unsatisfying workaround that appears to work for my scenarios. Basically I figured I can just call InvokeMember() on the actual property and retrieve it and then  retrieve the type from the returned value. Since we are dealing with COM here the value will be a variant and it should AFAIK reflect the proper .NET type even if the value is NULL (for a string).

The updated code looks like this:

    Type t = BindingSourceObject.GetType();
 
    if (t.IsCOMObject)
    {
        object val = t.InvokeMember(BindingSourceMember, wwUtils.MemberAccess |  BindingFlags.GetProperty, null, BindingSourceObject, null);
        if (val == null)
            typBindingSource = typeof(string);
        else
        typBindingSource = val.GetType();
    }
    else
    {
        PropertyInfo[] pi = BindingSourceObject.GetType().GetProperties();
 
        // *** It's an object property or field - get it
        MemberInfo[] MInfo = t.GetMember(BindingSourceMember,
                                         wwUtils.MemberAccess);
        if (MInfo[0].MemberType == MemberTypes.Field)
        {
            FieldInfo Field = (FieldInfo)MInfo[0];
            typBindingSource = Field.FieldType;
        }
        else
        {
            PropertyInfo loField = (PropertyInfo)MInfo[0];
            typBindingSource = loField.PropertyType;
        }
    }

Since databinding properties invariably are value types except for string, this approach should work fairly reliably and it does in the examples I tried. I'm doing this with Visual FoxPro as the COM client and it works with all the Fox types anyway. The only non-value type that you can bind would be string and it can potentially be null. So if the value is in fact null I assume that the type is a string. If it's not the code will fail, but I can't think of another situation where the value would be null (errors will throw and get captured as a binding error).

This solves my immediate problem of binding to the COM object and it doesn't really interfere with the existing code since it's a separate code path, so there's no performance penalty for non-COM objects.

This is a hack to say the least, but if somebody sees a better way to enumerate type information for COM objects (short of using the COM type APIs <s>)  I would love to know. I suspect it can't be done - following the code in InvokeMember in the Rotor source seems to lead quickly down into unmanaged code if the type is a COM object as somebody mentioned in a previous post.

Posted in .NET  CSharp  COM  


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024