Why am I getting an ExecutionEngineException when attempting COM aggregation - c#

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.

Related

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?
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.

Get a COM object to throw an exception on any result except S_OK (0) in C#

I'm currently working on an upgrade to a project that extensively uses COM / MFC / (who knows how many other technologies). As part of the upgrade, we're trying to move as much functionality as we can into managed C# code, but unfortunately some stuff just can't move (for reasons I won't go into). One of those things is a section of code that grossly abuses COM, and returns pointers in the HRESULTs that later get cast to various other COM-interface implementing objects. I've tried the following code to convert an HRESULT into a pointer that I can then get an interface from :
MyComInterfaceInCS myObj = null;
try
{
world.GetTD_MyComInterfaceInCS();
}
catch (COMException comException)
{
int pointerValue = Marshal.GetHRForException(comException);
IntPtr myObjPointer = new IntPtr(pointerValue);
myObj = (MyComInterfaceInCS) Marshal.GetObjectForIUnknown(myObjPointer);
}
.... but, no COM exception gets thrown, and I'm guessing it's because the pointer is not a negative value, and hence isn't technically a COM error. Is there any way to configure COM on the object to throw a COM exception on anything BUT S_OK (0) ?
Two thoughts:
Find and kill (or severely maim)
whoever implemented this library
Implement a wrapper in managed C++
as you will need to get at the
method call's HResult directly and I
cannot think of a way to do that
using interop.
EDIT
Another option is to declare the COM interfaces in C# such that the signature of each method returns an HRESULT and uses [out retval] for return values. This will alow you to get and examine the HRESULT of all method calls, not just ones that throw a COMException.
By default COM interop typeimport "fixes up" the method signatures such that the HRESULT return is removed on the managed side and the interop layer throws exceptions for E_FAIL etc, essentially throwing away S_OK, S_FALSE and the like.
This article has some explanation and the PreserveSig attriubte documentation has some additoinal detail.
This will likely require that you declare all of the COM interfaces manually rather than using tlbimp but you may be able to get tlbimp to preserve the signature of the COM methods.
This would allow you to stay purely in C# but in your shoes I'd go with managed C++ as it is just easier to interact with COM in this non-standard fashion from there.

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++.

COM Object Clean Up

What is the difference between the two lines of code below:
CComPtr< IInterface > m_interface;
IInterface* m_interface;
I know that CComPtr help eliminate memory leaks, but I am getting inconsistent results. When declaring the pointer with CComPtr< IInterface > m_interface;
and using the interface in my C# code there are no errors, however using the Interface in VC++ I get an unhandled exception error, even if I comment out the instance creation of IInterface.
I am pretty sure the problem is in here somewhere:
STDMETHODIMP CSomeClass::get_IClass(IClass** var)
{
return m_class_var->QueryInterface(var);
}
STDMETHODIMP CSomeClass::putref_IClass(IClass* var)
{
m_class_var = var;
return S_OK;
}
When I declare the interface pointer with: IInterface* m_interface;
I get a RPC_E_SERVERFAULT error when testing the Interface in C# and have to explicitly call GC.Collect() to avoid the error being thrown after instantiation of a few objects. When testing the Interface in VC++ the error is consistent however when it occurs is different. If I comment out the instance creation of IInterface the code runs fine, however when I try to create an instance I get same error as before, just a vague unhandled exception error. What am I doing wrong here?
CComPtr is a smart pointer designed to do the 'right' thing when used with COM idioms.
Your code for get_IClass looks good, but putref_IClass needs to call AddRef on the IClass as you're storing it. If you used CComPtr that would happen automatically.
You'll need to add more details about your VC++ unhandled exception.
IInstance* m_instance is a simple pointer to an IInstance object. You have to manage the lifetime of this pointer yourself. You don't new and delete COM objects like you would ordinary objects. Instead, the operating system allocates the object when you call the WINAPI function `CoCreateInstance' :
// instantiate the CoClass which implements IInstance...
IInstance* instance = 0;
HRESULT hr = CoCreateInstance(__uuidof(mylibrary::MyCoClass), 0, CLSCTX_INPROC_SERVER, __uuidof(mylib::IInstance), &instance);
: :
// We're done, so release the object...
instance->Release();
instance = 0;
Each COM object implements reference counting. When the last reference to an object has been Release()ed, the COM object destroys itself.
Using CComPtr<> simplifies how you manage the lifetime of the COM objects. It is a smart pointer similar in nature to std::auto_ptr or Boost's shared_ptr, but it works with COM objects. Typically when using a CComPtr you would call the CreateInstance member function rather than calling the WINAPI function, and you would not explicitly call Release when you are done. Just let the CComPtr go out of scope, and when it's destructor is called it will call Release for you:
void function()
{
// instantiate the CoClass which implements IMyInterface...
CComPtr<IInstance> instance;
instance.CoCreateInstance(__uuidof(mylibrary::MyCoClass));
: :
// We're done, so release the object...
// dont have to do anything, it will be released when function() exits
}
CComPtr< IInterface > m_interface is an object. Whereas IInterface* m_interface is a pointer.
The first will have its destructor called when it goes out of scope and I think (a long time since I used it) it will automatically call m_interface ->Release().
The latter is a pointer to an interface and you have to manage when m_interface->Release() is called.
Can you confirm that the COM object is not being released before access?

Properly accessing a SafeArray of VT_UNKNOWN with SafeArrayGetElement

We have a COM component who’s implementation and interface definition exist in managed code but is driven by a native component. The managed component is returning a SafeArray back to the native code via the following method declaration.
interface IExample {
<return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
object[] DoSomeOperation()
}
The generated native signature properly passes this back as a SafeArray.
During a code review though we came up with some questions about calls to the resulting array with SafeArrayGetElement. The issue is whether or not SafeArrayGetElement returns a IUnknown instance which is AddRef'd or not. Essentially it boils down to which of the following is correct
Example 1:
CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));
Example 2:
IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));
The documentation is very thin on this subject. It only includes the following line.
If the data element is a string, object, or variant, the function copies the element in the correct way.
The definition of correct is a bit ambiguous.
The first method should be correct, and would be in line with handling of objects throughout COM, presumably the definition you found makes the assumption that the consumer knows the correct way.
The other items mentioned require it. Copying a VARIANT or a SAFEARRAY carries an implicit AddRef() when they contain objects. VARIANT doesn't require it when VT_BYREF is present, though.
VariantCopy # MSDN
SafeArrayCopy # MSDN
This behavior isn't intrinsic to SAFEARRAYs or VARIANTs as it is part of the rules of handling parameters in COM. There isn't anything stopping someone from trying to circumvent the rules, though.
For input parameters, it isn't the responsibility of the callee to AddRef() unless they intend to keep the interface pointer for further use. However, other cases of parameter use require it.
For example, interfaces placed in VARIANTs or other containers should have at least one AddRef() call applied, otherwise this would create issues when using VARIANTs as output parameters from COM methods, as transfer of data/references is one-way. The original object could expire by the time the call arrives at its destination. Similarly, marshaling an interface into a Stream requires an AddRef() as well.
Similarly, calling by reference requires at least one AddRef call as well. If this were not the case, then any suitable long-running call (say, through DCOM) may not arrive at its destination with the guarantee that the referenced object is still alive. Extra AddRef()/Release() calls are frequently skipped here, though, as the object should already be at 1+ due to creation in or before the calling scope.
If it is possible to modify the component, and your calls are in-process, then it may be desirable to use the GIT instead. This allows you to pass a token instead, and it will be easier to marshal the interface across COM apartments. The lifetime of the objects involved then becomes the responsibility of the caller through the duration of the call, and you'll be able to trap cases where an object could not be marshaled.
Creating the Global Interface Table # MSDN
Also interesting is the footnote for BSTRs.
If the implementation of a function that takes a BSTR reference parameter assigns a new BSTR to the parameter, it must free the previously referenced BSTR.
String Manipulation Functions (COM)
It should be AddRef:ed, but I don't have first-hand info that that's the case (e.g. I haven't read the source).
I think the documentation is pretty clear, though -- copying an interface pointer 'correctly' is AddRef:ing it.
If you want to be really sure, build a super-simple ATL COM object which implements IUnknown, stuff a number of them into a SAFEARRAY and put a breakpoint in CComObjectBase<>::InternalAddRef (if my memory serves). Then debug a call to SafeArrayGetElement and see if your breakpoint is hit.

Categories

Resources