开发者

Finding Out what Interfaces are Queryable for a COM Object?

I am work开发者_开发技巧ing 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++.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜