C#编译器如何检测COMtypes?

编辑:我已经写了结果作为博客文章 。


C#编译器有点神奇地对待COMtypes。 例如,这个声明看起来很正常

Word.Application app = new Word.Application(); 

…直到你意识到Application是一个接口。 调用接口上的构造函数? Yoiks! 这实际上被转换成对Type.GetTypeFromCLSID()的调用,而另一个转换为Activator.CreateInstance

此外,在C#4中,您可以使用ref参数的非参数参数,编译器只是添加一个局部variables来引用传递,放弃结果:

 // FileName parameter is *really* a ref parameter app.ActiveDocument.SaveAs(FileName: "test.doc"); 

(是的,有一大堆参数丢失。是不是可选参数不错?:)

我试图调查编译器的行为,我没有假冒第一部分。 我可以做没有问题的第二部分:

 using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; [ComImport, GuidAttribute("00012345-0000-0000-0000-000000000011")] public interface Dummy { void Foo(ref int x); } class Test { static void Main() { Dummy dummy = null; dummy.Foo(10); } } 

我想能够写:

 Dummy dummy = new Dummy(); 

虽然。 在执行时显然会爆炸,但没关系。 我只是在试验。

编译器为链接的COM TypeIdentifierCompilerGeneratedTypeIdentifier )添加的其他属性似乎并没有成为诀窍…什么是魔法酱?

绝不是我在这方面的专家,但最近我偶然发现了我想要的东西: CoClass属性类。

 [System.Runtime.InteropServices.CoClass(typeof(Test))] public interface Dummy { } 

一个coclass提供一个或多个接口的具体实现。 在COM中,这样的具体实现可以用任何支持COM组件开发的编程语言编写,例如Delphi,C ++,Visual Basic等。

看到我对类似的Microsoft Speech API问题的回答 ,在那里你可以“实例化”接口SpVoice (但实际上,你正在实例化SPVoiceClass )。

 [CoClass(typeof(SpVoiceClass))] public interface SpVoice : ISpeechVoice, _ISpeechVoiceEvents_Event { } 

在你和迈克尔之间你几乎把这些东西放在一起。 我认为这是如何工作的。 (我没有写代码,所以我可能会略微错误地说明,但我确定这是事实。)

如果:

  • 你是一个新的接口types,并且
  • 接口types有一个已知的coclass,和
  • 你正在使用这个界面的“无pia”function

那么代码生成为(IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))

如果:

  • 你是一个新的接口types,并且
  • 接口types有一个已知的coclass,和
  • 你没有使用这个接口的“无pia”function

那么代码生成就像你说的“新的COCLASSTYPE()”一样。

乔恩,如果你对这个问题有任何疑问,请随时纠正我或萨姆。 供参考,山姆是这个function的专家。

好吧,这只是为了让迈克尔的回答更有意思(如果他愿意的话,欢迎join),在这种情况下,我会删除这个。

查看Word.Application的原始PIA,涉及三种types(忽略事件):

 [ComImport, TypeLibType(...), Guid("..."), DefaultMember("Name")] public interface _Application { ... } [ComImport, Guid("..."), CoClass(typeof(ApplicationClass))] public interface Application : _Application { } [ComImport, ClassInterface(...), ComSourceInterfaces("..."), Guid("..."), TypeLibType((short) 2), DefaultMember("Name")] public class ApplicationClass : _Application, Application { } 

Eric Lippert在另一个答案中提到了两个接口。 在那里,正如你所说的,是CoClass无论是在类本身还是在Application接口上的属性。

现在,如果我们在C#4中使用PIA链接,则其中的一些embedded到所产生的二进制文件中,但不是全部。 刚刚创build应用程序实例的Application以这些types结束:

 [ComImport, TypeIdentifier, Guid("..."), CompilerGenerated] public interface _Application [ComImport, Guid("..."), CompilerGenerated, TypeIdentifier] public interface Application : _Application 

没有ApplicationClass – 大概是因为它会在执行时从实际的 COMtypesdynamic加载。

另一个有趣的事情是链接版本和非链接版本之间的代码差异。 如果你反编译这行

 Word.Application application = new Word.Application(); 

引用的版本中,结果如下:

 Application application = new ApplicationClass(); 

而在链接的版本,它结束了

 Application application = (Application) Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("..."))); 

所以它看起来像“真正的”PIA需要CoClass属性,但链接版本并不是因为没有编译器实际可以引用的CoClass 。 它必须dynamic地执行。

我可能试图用这个信息来伪造一个COM接口,看看我能否让编译器链接它…

只是为了给迈克尔的答案增加一些确认:

以下代码编译并运行:

 public class Program { public class Foo : IFoo { } [Guid("00000000-0000-0000-0000-000000000000")] [CoClass(typeof(Foo))] [ComImport] public interface IFoo { } static void Main(string[] args) { IFoo foo = new IFoo(); } } 

您需要ComImportAttributeGuidAttribute才能正常工作。

当鼠标hover在new IFoo()时,请注意以下信息:智能感知适当地提取信息:很好!