问题
I'm working with a COM-API, Autodesk Inventor.
This test passes:
[Test]
public void CastTest()
{
Inventor.Document document = _application.ActiveDocument;
var assemblyDocument = (Inventor.AssemblyDocument)document;
Assert.NotNull(assemblyDocument);
}
This test fails:
[Test]
public void IsAssignableFromTest()
{
Assert.IsTrue(typeof(Inventor.Document).IsAssignableFrom(typeof(Inventor.AssemblyDocument)));
}
I don't know much about COM at all, is there a way to check if one COM type "inherits" another using reflection or some COM voodoo?
回答1:
The COM type system is not compatible to .NET. Instead you are programming against wrappers (so called RCW's). To test if you can convert one COM object into another one, COM provides the QueryInterface-method as a member of IUnknown
, which every COM object must implement. However .NET hides those details for you so that you can write COM code that does "feel" like .NET code.
If you take a look at the disassembly of Inventors interop-library, you will recognize, that there is no direct relation between Document
and AssemblyDocument
. Both are interfaces that do only implement the default interface of their respective coclasses and are attributed with the CoClassAttribute
. But in their inheritance tree, they are not directly related to each other. They may both implement the same interface (I guess something like IDocument
), but you cannot convert a WinForms button into a picture box either, even though they both implment the Control
-interface.
This is what reflection and IsAssignableFrom
is testing: The metadata that each CLR-type provides. COM works different here. Each COM object can "decide" on it's own if it can be called from another interface. Therefor it implements QueryInterface
. And therefor you have to create an instance of your source type, before you can perform your test (COM does not know static members).
A traditional cast does call QueryInterface
, so your test could simply look like:
[Test]
public void IsAssignableFromTest()
{
Assert.IsNotNull(_application.ActiveDocument as Inventor.AssemblyDocument);
}
Otherwise you could call QueryInterface directly through the Marshal
-class.
However, it is not possible to test type metadata through reflection with COM objects.
回答2:
If you want to use the Inventor API, you could check for document type before casting it to another type.
Ex.: (VBA)
Dim oDoc As Document
Dim oAssyDoc As AssemblyDocument
Set oDoc = ThisApplication.ActiveDocument
If oDoc.DocumentType = DocumentTypeEnum.kAssemblyDocumentObject Then
Set oAssyDoc = oDoc
End If
Best regards,
来源:https://stackoverflow.com/questions/23885002/isassignablefrom-with-com