Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does dynamic method invoke fail when reflection still works?

Why can't a dynamic object invoke these methods on the NameTranslate COM object when reflection can?

Failing example using dynamic:

Type ntt = Type.GetTypeFromProgID("NameTranslate");
dynamic nto = Activator.CreateInstance(ntt);
nto.Init(3,null)

The third line fails with a NotImplementedException and the message The method or operation is not implemented.

A similar attempt that works using a different COM object (WScript.Shell and SendKeys):

Type shellType = Type.GetTypeFromProgID("WScript.Shell");
dynamic shell = Activator.CreateInstance(shellType);
shell.SendKeys("abc");

Getting back to the first sample. If I use reflection and invoke the methods using the InvokeMethod method things work fine.

Working example using reflection:

Type ntt = Type.GetTypeFromProgID("NameTranslate");
object nto = Activator.CreateInstance(ntt);
object[] initParams = new object[]{3,null};
ntt.InvokeMember("Init", BindingFlags.InvokeMethod, null, nto, initParams);

I believe this must have something to do with how the COM object is created or marked - but for the life of me I can't see anything in the docs, object browser or registry that indicates these COM objects and their subs/functions are marked private or something else that would normally throw off the dynamic keyword.

NameTranslate documentation on MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/aa706046.aspx

like image 453
Goyuix Avatar asked Jan 07 '14 00:01

Goyuix


1 Answers

Interestingly, none of NameTranslate methods are callable via dynamic. I have only a theoretical explanation of this, below.

AFAIK, when .NET DLR deals with COM objects for dynamic invocation, it tries to make use of a COM type library, if available, and then resorts to IDispatch. That's how it is different from Reflection, which calls IDispatch right away when used with COM objects.

The ActiveDS type library (C:\Windows\System32\activeds.tlb), as viewed with OleView, appears to be somewhat ill-formed. It includes a lot of non-automation compatible declarations, including interfaces:

interface IPrivateDispatch;
interface ITypeInfo;
interface ITypeComp;
interface ITypeLib;
interface IPrivateUnknown;

The class definition for NameTranslate itself looks like this:

[
  uuid(274FAE1F-3626-11D1-A3A4-00C04FB950DC)
]
coclass NameTranslate {
    [default] interface IADsNameTranslate;
    interface IDispatch;
};

It's unusual (although not prohibited) to declare IDispatch inside coclass.

So, I'd assume such type library and/or coclass definition might be confusing the DLR in this case.

As a workaround, you can import it with TlbImp.exe activeds.tlb (that would produce a bunch of warnings), add the output interop assembly to your project and call the API directly. This works:

Type ntt = Type.GetTypeFromProgID("NameTranslate");
var nto = Activator.CreateInstance(ntt) as ActiveDs.IADsNameTranslate;
nto.Init(3, null);
like image 187
noseratio Avatar answered Oct 05 '22 22:10

noseratio