Reflection on COM Interop objects - c#

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?

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.

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

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.

Related

How does Reflection retrieve data assigned to variables?

My understanding so far is that Reflection is used to retrieve the names of Types and their members from an assembly via its metadata. I've come across a few examples of Reflection in action identical to the following example.
class Person
{
public string Name {get;set;}
}
static void Main(string[] args)
{
Person person = new Person();
person.Name = "John";
Console.WriteLine(person.GetType().GetProperty("Name").GetValue(person)); //John
person.Name = "Mary";
Console.WriteLine(person.GetType().GetProperty("Name").GetValue(person)); //Mary
}
I understand how Reflection can get names of Types, members, etc. as this is what's stored in an assembly's metadata, but how does Reflection retrieve the value associated with it? This is dynamic data that changes during a program's execution (as shown in the example), which an assembly's metadata doesn't contain (right?).
Would be grateful for clarification on how Reflection retrieves actual values and whether this is actually the case. Please correct anything I've said that's not correct!
The short answer is that reflection is a feature of the runtime, so it has access to runtime information. Were it a separate library with no runtime "hooks", you're right, it wouldn't be able to get the values of properties at runtime, or make calls, or anything else that wouldn't be essentially observable from the assembly file on disk.
Long answer where I prove this to myself:
Microsoft makes available a reference version of the C# source code used to write the base class libraries for .NET Framework. If we look at the PropertyInfo.GetValue(object) method you use in your example, it's defined here. Following the trail of calls we eventually get to an abstract method of the same name but different parameters. Further down in the source file is the implementing class, RuntimePropertyInfo, and its override of GetValue we see that it is implemented by calling the property's get accessor (as, under the hood, properties are just collections of methods with certain signature conventions - GetGetMethod is a funny name meaning "get me the method defined as get for the current property"):
MethodInfo m = GetGetMethod(true);
if (m == null)
throw new ArgumentException(System.Environment.GetResourceString("Arg_GetMethNotFnd"));
return m.Invoke(obj, invokeAttr, binder, index, null);
If we do a similar spelunking journey on MethodInfo.Invoke, we eventually reach RuntimeMethodHandle.InvokeMethod, which is declared:
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static object InvokeMethod(object target, object[] arguments, Signature sig, bool constructor);
The extern keyword on a class means "I don't have the body for this method in C#, look elsewhere for it". For most users of C# this means they're using DllImport to reference native code, but this method has a different attribute: MethodImpl, with the MethodImplOptions.InternalCall enum value. This is the C# way of saying to the compiler, "I don't have the body, but the actual runtime itself does".
So at the end of our journey we reach the point where the Reflection API relies on the runtime itself. Of course, the runtime and the base class libraries have to be developed in tandem to ensure these sync-up points exist.
Interestingly, the actual standard for .NET - ECMA-335 - makes the Reflection API optional, when the implementation (meaning the runtime + base class libraries) adheres to the bare minimum "Kernel Profile" (cite: 6th Ed., §IV.4.1.3). So actually there are implementations of .NET where you're not explicitly allowed to inspect the runtime like this, but given the reliance some kinds of applications have on reflection, all big implementations (original .NET Framework, .NET Core / the new .NET, and Mono) provide it.
The TLDR is the CLR (Common Language Runtime) keeps track of all your objects in memory and when you call GetValue it retrieves the value for you.
Note this post is a little old, but here is a rough idea of what an object looks like in memory:
Please refer to the part that says Object Instance. An object laid out in memory, contains the following 4 items:
Syncblk
TypeHandle
Instance Fields
String Literals
When you call GetValue, using an OBJECTREF to the object instance, it finds the starting location where your instance fields are stored. From there it can figure out which instance to retrieve based on the type.
I believe the actual call is FieldDesc::GetInstanceField.

Why am I getting an ExecutionEngineException when attempting COM aggregation

I am attempting to aggregate a .NET object within a COM object (the outer unknown) using the following code:
Object obj = ... // some ComVisible .NET object
var comObj = (IMyComInterface)Activator.CreateInstance(Type.GetTypeFromProgID("some.progid"));
var unknown = Marshal.GetIUnknownForObject(comObj);
var innerUnknown = Marshal.CreateAggregatedObject(unknown, obj);
// This is where the ExecutionEngineException is thrown.
var aggObj = Marshal.GetObjectForIUnknown(innerUnknown);
// Make outer object aware of our dotnet object, say.
comObj.DotNetObj = aggObj;
Marshal.Release(unknown);
...
Is there anything "obvious" wrong with this code? This exception is leaving me with very little to go on. Also annoying is that the exception does not always happen, although it does more often than not.
Is there another way to achieve this? (ie marshal the innerUnknown IntPtr to my COM object)
NOTE: I have to target version 2 of the framework and have not tried version 4, and am, as far as I know, fully up to date.
To answer the question the reason it is failing is that the innerUnknown is the wrong unknown. The method is intended to get the active RCW for a COM IUnknown, but this IUnknown is a Com-callable wrapper for a Managed object.
In any case, the outer object needs the innerUnknown pointer. If you gave it the one you are trying to you would get a stack overflow in QueryInterface.
In your example above, the .Net object thinks it has been aggregated by the COM object, but the COM object isn't aware. Assuming it is your COM object, you need to give it innerUnknown, and then implement a delegating IUnknown (if you haven't already). You don't need to call GetObjectForIUnknown.
You are kind of doing it backwards however. The usual way is for the COM object to call CoCreateInstance passing itself as the outer unknown. The runtime then calls CreateAggregatedObject for you.
If you want the .Net object to aggregate the COM object, you should inherit from the RCW. This will create the COM object as an aggregated object passing your .Net inner unknown to CoCreateInstance. But it has to be an object which supports aggregation.
If you want the COM object to aggregate the .Net object, you have to use a COM object which does that. Typical examples are ADO aggregating OLEDB providers, ADSI aggregating ADSI extensions, WMI (I think). But it has to be supported by the object, you can't tell any old object to aggregate you. Usually it occurs by the other object calling CoCreateInstance, which is handled by the .Net runtime, which will call CreateAggregatedObject for you, and pass the innerUnknown back to the caller.

Finding Out what Interfaces are Queryable for a COM Object?

I am working with ESRI's ArcObjects COM Library, i am trying really hard to figure out what type "selected" should be:
IMxDocument doc = m_application.Document as IMxDocument;
object selected = doc.SelectedItem;
SelectedItem returns a comobject (Not Null), generally representing the data type that is currently selected. However i do not have the faintest idea what type i am supposed to cast it to. When i debug it, i don't really see anything useful:
http://imgur.com/Yfo6G
(watch debug after the value is set)
ESRI's ArcObjects library is huge, and is pretty poorly documented, i simply cannot figure it out. I even went so far as to manually check about 50 or so interfaces that i thought it should be.
Does anyone have any ideas how i can figure this out?
EDIT To clarify their documentation is absolutely no help, neither is their forums.
After reading your question, the answers, and the comments, you may have to write a utility to find the answer by brute force.
Use reflection to scrape a list of interfaces out of your interop assembly, then simply loop over this list and see if your object supports each interface in turn.
Update
Some sample code:
object unknown = //your com object...
Type someComObjectType = typeof(ExampleTypeInInteropAssembly);
Assembly interopAssembly = someComObjectType.Assembly;
Func<Type, bool> implementsInterface = iface =>
{
try
{
Marshal.GetComInterfaceForObject(unknown, iface);
return true;
}
catch (InvalidCastException)
{
return false;
}
};
List<Type> supportedInterfaces = interopAssembly.
GetTypes().
Where(t => t.IsInterface).
Where(implementsInterface).
ToList();
if (supportedInterfaces.Count > 0)
{
supportedInterfaces.ForEach(Console.WriteLine);
}
else
{
Console.WriteLine("No supported interfaces found :(");
}
I'm not familiar with that library, but I can give some suggestions. Once you look at the problem from the point of view of COM you'll see that there is no simple answer.
(Do keep in mind that that in COM all objects are just objects, and that the only requirement is that it must support IUNKNOWN (and possibly other interfaces). So the answer to the question "what type of object it is" can often have more than one answer.)
The important thing to remember is that in COM the list of interfaces for an object is not defined in any sort of metadata like it is in .NET (except that a library usually provides an optional type library as a form of documentation for development tools -- more on that in a minute).
The list of interfaces is officially defined only by the results of calling IUNKNOWN's QueryInterface() method -- that is, it's defined entirely by the result of executing code.
Some times the list might be hard-coded. Often, the list might not be known until runtime, and it might not even be known until somebody asks. The only rule is that the list of interfaces needs to be stable and what I call sensible: the list cannot change over time for a given object instance; it must support IUNKNOWN, which sometimes people forget; if it supports a derived interface, it must support its base; and a couple of other I'm sure I'm forgetting.
That last point is crucial to your problem: COM doesn't know a priori what interfaces are supported by any object. The .NET runtime doesn't know either -- not from COM anyway. The only way for .NET to know would be if the Type Library for the object says that the object returned is of a specific interface. Lacking that, all you have is an IUNKNOWN pointer and you have to ask for specific interfaces via code and see if you get an answer other than NULL.
Since the type of the SelectedItem propery is object, it means that the type library simply says "the return type is an interface pointer of type IUNKNOWN" (it might be IDISPATCH, but the principle stands). The exact type obviously depends on runtime circumstances -- "what happens to be selected right now".
(In .NET, the return type is actually System.__ComObject because you don't get a naked interface pointer but a COM callable wrapper which a .NET based proxy to the object)
You are at the mercy of the (poor?) documentation of the library to get a clue on what kinds of interfaces the returned object might support. Lacking that, code like Chibacity's might get you a partial list as well (I have not reviewed that code). Ultimately, you probably want to use that code to get a list of candidate interfaces during debugging.
Once you know a few possibilities that interest you, you can save yourself some typing trouble by just using the C# as operator (which causes the COM callable wrapper to issue the corresponding COM spells against the native object).
I aggree that the documentation is lacking at certain places but the help is pretty specific in your case:
Remarks
This property returns a reference to the currently selected item in
the table of contents. The return is an IUnknown because there
are several possbile objects the selected item can be.
When working in the Display tab, the reference could be to a Map
object if you have a data frame selected, one of the Layer objects
(FeatureLayer, FDOGraphicsLayer, etc) if you have a layer selected, or
a LegendGroup if you have a unique value or heading selected.
In the Source tab, the reference can be to any of the above objects
plus a Table, FeatureDataset, or Workspace.
In the case where more than one item is selected, the reference is to
a Set object.
http://help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/index.html#/SelectedItem_Property/000v00000124000000/
I'am affraid there is no way to list the interfaces implemented by a com object. However, you still can brute force it by querying it against a list of interface you are interested in.
Edit:
Some code that may help:
foreach(Type comInterfacType in comInterfaceTypesIAmInterestedIn) {
IntPtr comInterface = Marshal.GetComInterfaceForObject(o, comInterfaceType);
if(comInterface != IntPtr.Zero) {
Console.WriteLine("o implements " + comInterfaceType);
Marshal.ReleaseComObject(o);
}
}
(would have added this as a comment, but I'm a noob and my rep is insufficient)
It's been awhile since I've worked with ArcObjects, but I do remember that the object model was ridiculously large and poorly documented. That said, doesn't IMxDocument.SelectedItem refer to the item that is selected in the TOC/layers control? If so, wouldn't it return an instance of IMap or ILayer?
try selected.GetType().ToString();
It should give the the type of object that it is.
Try read the docs. If the SDK does not help, try read the type library in the OLEView utility that is shipped with Windows Resource Kit and Visual C++.

What is the difference between Wrapper and Reflection

I was very confused about the reflection and wrapper, I know that reflection can reflect the object into another object type, wrapper can convert the primitive type into object. Is this correct?
A wrapper will wrap around another object which may hide some of the complexities of using the original object / provide diffrent naming conventions etc.
forgive the C# syntax and the fairly contrived example
class SimplePerson{
ComplexPerson _person;
public void WalkForward (int steps){
for (int i = 0; i < steps; i ++){
_person.LeftFoot ();
_person.MoveFoot ();
_person.PlaceFoot ();
}
}
// More methods
}
Reflection on the other hand can be used to retrieve methods / fields / properties and metadata in general from an object.
Again forgive the C#
SimplePerson _person;
Console.WriteLine ("Class {0} has the following methods:", _person.GetType().Name);
foreach (var method in _person.GetType().GetMethods()){
Console.WriteLine ("\t {0}", method.Name);
}
which should give output something like (depending on the class obviously)
Class SimplePerson has the following methods:
Eat
WalkForward
RunForward
Your concept of reflection is wrong. Reflection lets a program investigate its own classes at runtime. For example, you get an obect of an (at compile time) unknown class and find out which fields and methods it has.
A wrapper is simply a class that takes an object and wraps it, i.e. it adds (almost) no new functionality, but exposes a different interface than the original class. A special case are the wrappers for primitive types; since primitive types are not objects in some languages, e.g. Java, wrapper classes for those primitive classes allow for treating those primitve types like objects.
wrapper can convert the primitive type into object
Ehm, you seem to be confused. A "wrapper" is usually some code that hides an API ("wraps" it), to simplify calls etc. . You are probably thinking about autoboxing, which allows you to use primitive types like their corresponding objects and vice versa.
reflection can reflect the object into another object type
No, reflection allows you to retrieve information about available classes and their members at runtime, and to invoke their functionality, even if they're not available at compile time.
See http://java.sun.com/docs/books/tutorial/reflect/ for details. Please read this, then come back if you still are confused, and ask a new question.
I know that reflection can reflect the object into another object type
Not really. Reflection is used to dynamically access/modify objects at runtime.
Suppose you have the class Foo you can create an instance programatically with:
Object o = new Foo();
But you can also create an instance at runtime only with the name:
public Object createInstanceOf( String className )
throws InstantiationException,
IllegalAccessException {
return Class.forName( className ).newInstance();
}
Object o = createInstanceOf( "Foo" ) ;
That's basically what reflection is all about.
wrapper can convert the primitive type into object
In general terms a wrappers simply ... well wraps another object. In particular for Java primitives you're right, Java wrappers do wrap the primitives so they can be used as any other regular object, for instance to pass an int to an object that receive an object you'll have to use a wrapper:
public void somethingWith( Object o ) {}
....
int i = 1234;
somethingWith( new Integer( i ) );
Prior to java 1.5 you could not invoke the method somethingWith with out having to explicitly create the wrapper instance.
Today with autoboxing the compiler does that for you and you can invoke directly:
somethingWith( 1234 ) ;
I hope this helps and doesn't confuse you more.

Using COM object from C++ that in C#.NET returns object []

I have a COM object that I'm trying to use from C++ (not .NET), and all of the example programs and manual are written assuming the use of C#.NET or VB.NET. COM is new to me so I'm a bit overwhelmed. I'm using #import on the TLB but am struggling to deal with the variants that are used as parameters. I have one particular method, that according to the docs and the example programs in C#.NET, is supposed to return an object[]. Then I'm supposed to cast the first entry in this array to a ControlEvent which then tells me what to do with the rest of the objects in the array. The C#.NET example looks like:
object [] objEvent = (object []) Ctl.GetEvent();
ControlEvent ev = (ControlEvent) objEvent[0];
In my case, GetEvent is returning me a _variant_t and I need to know how to convert this to an object[] so that I can further process. Its not clear to me even how I express 'object' in C++. I see _variant_t documentation showing me a million things I can convert the variant to, but none of them seem to be converting to anything I can use. I'm hoping for some assistance converting the above C#.NET code to Visual C++
Thanks.
Typically, you look at the vt member of the variant to see what type of thing it actually is. In this case I would expect it to be an array, so you would expect that the vartype would be some variation on VT_ARRAY (usually it is bitwise OR'ed with the type of the members). Then, you get the parray member which contains the SAFEARRAY instance that actually holds the array, and use the normal safe array functions to get the data out of the array.
I haven't done this, but from reading the documentation for the _variant_t class (and the comments below which corrected my original post), I think you should read the vt field of the _variant_t instance (actually the VARTYPE vt field of the VARIANT instance: the _variant_t instance directly derives from VARIANT) to see what type of thing it contains, as described in the reference documentation for the VARIANT struct. One you know what type of thing is contained in the variant, use the corresponding type-specific operator to read it.
You'll be in for some hurt if you try to use COM without understanding it (and you may want a book which describes that); you may well need to know about the IUnknown interface and the AddRef method, for example.

Categories

Resources