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 TypeIdentifier
( CompilerGenerated
和TypeIdentifier
)添加的其他属性似乎并没有成为诀窍…什么是魔法酱?
绝不是我在这方面的专家,但最近我偶然发现了我想要的东西: 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(); } }
您需要ComImportAttribute
和GuidAttribute
才能正常工作。
当鼠标hover在new IFoo()
时,请注意以下信息:智能感知适当地提取信息:很好!