How to get LINQPad to Dump() System.__ComObject re

2019-02-14 00:39发布

I am playing around with using LINQPad to rapidly develop small ArcObjects (a COM-based library for ESRI's ArcGIS software) applications and have had some success in using it to Dump() the properties of COM objects that I initialize from .NET, but any COM objects that are obtained from an existing COM object are simply dumped as System.__ComObject references, which is not particularly useful:

LINQPad Screenshot

This help topic explains why this is happening, which I think I understand, but would like to know what options there are for working around this behavior, especially in the context of making LINQPad (even) more powerful.

Interestingly, Visual Studio's debugger is able to display the properties of these objects, and even values for value types:

Visual Studio debugging ArcObjects

What mechanism does Visual Studio use to achieve this introspection, and why doesn't LINQPad's Dump method do the same? Edit: See related question about how VS does this: How does Visual Studio's debugger/interactive window dump the properties of COM Objects in .NET?

The ArcObjects .NET SDK includes PIAs with RCWs for each CoClass a COM interface may be implemented by, so I'm thinking it should be possible to wrap these objects programmatically.

As a workaround I have successfully used Marshal.CreateWrapperOfType() within my LINQ queries to coerce LINQPad to dump the properties of the object when I happen to know which CoClass should be used. Of course, this only properly dumps value type properties -- any COM-based reference type properties are still reported as System.__ComObject, so a proper solution would have to work recursively to get those wrapped as well.

In a previous question I learned that the CoClass can be determined at runtime if it implements IPersist, which a good portion of ArcObjects do. Can I somehow use this technique, or another one, to automatically coerce a System.__ComObject to the appropriate RCW from the PIAs? And if so, how can I implement this in LINQPad, e.g. by providing an ICustomMemberProvider implementation? Can this be made to be recursive, so that properties that are also COM objects be wrapped as well?

I am using LINQPad 4.x which targets .NET 4.0, but am also interested in supporting LINQPad 2.x (so solutions that work on both .NET 3.5 and .NET 4.0 would be preferred, but that's not a requirement).

Update: I've figured out the first part of my question which was how to wrap a System.__ComObject in its RCW using the CLSID returned by IPersist.GetClassID. See this related question and this answer for the code I'm using.

I would still like to know how I can work this into LINQPad's Dump method.

1条回答
Fickle 薄情
2楼-- · 2019-02-14 01:39

I've been having some of the same issues (except I'm working with iTunes COM library).

In visual studio you don't realize it but each debug window is asking the COM library to create the type when you open it. This is different than Dump() which is, well, not interactive.

The only solution I've found is if I know what type the list is to do a OfType<>() cast to that type. This will iterate over the list and force COM to create the elements.

So in your example above you would say:

var layers = map.EnumerateLayers("etc")
      .Select(s => s.OfType<Layer>())
      .Dump();

NB - Your millage may vary, it turns out for the OP example this was needed.

var layers = map.EnumerateLayers()
      .OfType<IGeoFeatureLayer>()
      .Dump();

Depending on the COM you might have to take this to the next step and pull out the members from there (with com you have to ask to get the value) something like this:

var layers = map.EnumerateLayers("etc")
      .Select(x => x.OfType<Layer>())
      .Select(x => new { x.Depth, x.Dimention, }) // etc 
      .Dump();

Sure would be nice if there was a "magical" way to make this happen, but I don't believe there is because of the nature of COM.

查看更多
登录 后发表回答