Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection on COM Interop objects

Trying to create a mapper for an Microsoft Office object to POCO's and found this

// doesn't work
// returns an empty array where o is a RCW on an office object
foreach(var pi in  o.GetType().GetProperties() ) 
    tgt.SetValue(rc, pi.GetValue(o, null));

so have to resort to this

foreach(var field in tgt.GetFields() ){
    var pv = o.InvokeMember(field.Name, System.Reflection.BindingFlags.GetProperty, null, o, null);
    i.SetValue(rc, pv);
}

which works for now but wondering why the RCW.GetProperties() doesn't work here?

like image 578
Kumar Avatar asked Mar 16 '12 10:03

Kumar


3 Answers

The other two answers as of this writing are correct, but they miss an important opportunity to explain how the late binding of a COM object looks in terms of the .NET type system. When you call GetType on the COM object, the return value is the __ComObject internal type, not the COM interface type that you normally work with when writing interop code. You can see this in the debugger, or with some code like Console.WriteLine(o.GetType().Name);.

The __ComObject type has no properties; that's why you get an empty array when you call o.GetType().GetProperties(). (At least some things in life make sense!)

If you decompile the InvokeMember method, you'll find that it has special handling for COM objects, delegating the call to an internal native method. For "regular" .NET objects, the method uses "regular" .NET reflection, retrieving the appropriate MemberInfo for the requested member, and invoking it.

You can use .NET reflection on the interface type. For example, if you know that the object is an Excel Worksheet, you can use typeof(Worksheet).GetProperties(), and use the resulting PropertyInfo instances with your object. If you don't know the type of the object at compile time, however, you need to call GetType(), as in your example code. In that case, you're stuck with using InvokeMember.

like image 182
phoog Avatar answered Nov 17 '22 18:11

phoog


It is because the COM object is lately bound. The run time does not know what methods/properties will be available on a COM object until they are accessed/invoked.

Here are some good articles on the subject:

http://support.microsoft.com/default.aspx?scid=kb;en-us;Q302902

http://www.codeproject.com/Articles/10838/How-To-Get-Properties-and-Methods-in-Late-Binding

like image 27
Jamil Geor Avatar answered Nov 17 '22 20:11

Jamil Geor


You need to specify them by name using Type.InvokeMember(propertyName, BindingFlags.GetProperty, binder, target, args) because there's no way of knowing what properties a lately-bound object will have at compile-time. Instead, you need to perform that lookup at runtime, usually via string comparison.

RCW.GetProperties() would only work if you could determine the properties and their locations at compile-time.

like image 22
Showtime Avatar answered Nov 17 '22 18:11

Showtime