C# 编译器如何检测 COM 类型?
Posted
技术标签:
【中文标题】C# 编译器如何检测 COM 类型?【英文标题】:How does the C# compiler detect COM types? 【发布时间】:2010-11-08 18:37:21 【问题描述】:编辑:我已将结果写成blog post。
C# 编译器对 COM 类型的处理有点神奇。比如这个语句看起来很正常……
Word.Application app = new Word.Application();
...直到您意识到Application
是一个接口。在接口上调用构造函数?呸!这实际上被转化为对Type.GetTypeFromCLSID()
的调用和对Activator.CreateInstance
的调用。
另外,在 C# 4 中,您可以对ref
参数使用非引用参数,编译器只需添加一个局部变量以通过引用传递,丢弃结果:
// 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 PIA(CompilerGenerated
和 TypeIdentifier
)添加的其他属性似乎不起作用...什么是神奇的酱汁?
【问题讨论】:
可选参数不是很好吗? IMO,不,他们不好。微软正试图通过向 C# 添加膨胀来修复 Office COM 接口中的缺陷。 @Mehrdad:当然,可选参数在 COM 之外很有用。您需要注意默认值,但在它们和命名参数之间,构建可用的不可变类型要容易得多。 是的。具体来说,命名参数可能是实际上需要与某些动态环境进行互操作的。当然,毫无疑问,这是一个有用的功能,但这并不意味着它是免费的。它以简单为代价(明确规定的设计目标)。就个人而言,我认为 C# 对于团队遗漏的功能来说是惊人的(否则,它可能是 C++ 的克隆)。 C# 团队很棒,但企业环境很难没有政治。我猜安德斯本人对此并不十分高兴,正如他在 PDC'08 演讲中所说:“我们花了十年时间才回到原来的位置。” 我同意团队需要密切关注复杂性。动态的东西增加了很多复杂性,对于大多数开发者来说价值不大,但对于一些开发者来说价值很高。 我已经看到框架开发人员开始在很多地方讨论它的用途。 IMO 是时候找到dynamic
的好用处了……我们太习惯于静态/强类型,不知道为什么它在 COM 之外很重要。
【参考方案1】:
我绝不是这方面的专家,但我最近偶然发现了我认为你想要的东西:CoClass 属性类。
[System.Runtime.InteropServices.CoClass(typeof(Test))]
public interface Dummy
一个 coclass 供应混凝土 一个或多个的实现 接口。在 COM 中,这样的具体 实现可以写成任何 支持 COM 的编程语言 组件开发,例如德尔福, C++、Visual Basic 等。
参见my answer to a similar question about the Microsoft Speech API,您可以在其中“实例化”接口SpVoice
(但实际上,您正在实例化SPVoiceClass
)。
[CoClass(typeof(SpVoiceClass))]
public interface SpVoice : ISpeechVoice, _ISpeechVoiceEvents_Event
【讨论】:
非常有趣 - 稍后会尝试。链接的 PIA 类型虽然没有 CoClass。也许这与链接过程有关 - 我将查看原始 PIA... +1,因为在 Eric Lippert 和 Jon Skeet 也回答时写出了公认的答案;)不,真的,+1 提到 CoClass。【参考方案2】:只是为迈克尔的回答添加一点确认:
以下代码编译运行:
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
才能工作。
当您将鼠标悬停在 new IFoo()
上时,还要注意该信息:Intellisense 正确地拾取信息:很好!
【讨论】:
谢谢,我正在尝试,但我缺少 ComImport 属性,但是当我转到源代码时,我正在使用 F12 只显示 CoClass还有 Guid,这是为什么呢?【参考方案3】:好的,这只是为了让迈克尔的回答更加充实(如果他愿意,欢迎他添加它,在这种情况下我会删除它)。
查看 Word.Application 的原始 PIA,涉及三种类型(忽略事件):
[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 在another answer 中谈到的原因,有两个接口。正如您所说,那里是CoClass
- 就类本身和Application
接口上的属性而言。
现在,如果我们在 C# 4 中使用 PIA 链接,一些会嵌入到生成的二进制文件中......但不是全部。一个只是创建Application
实例的应用程序最终会得到这些类型:
[ComImport, TypeIdentifier, Guid("..."), CompilerGenerated]
public interface _Application
[ComImport, Guid("..."), CompilerGenerated, TypeIdentifier]
public interface Application : _Application
没有ApplicationClass
- 大概是因为它会在执行时从real COM 类型动态加载。
另一个有趣的事情是链接版本和非链接版本之间的代码差异。如果你反编译该行
Word.Application application = new Word.Application();
在 引用 版本中,它最终为:
Application application = new ApplicationClass();
而在 linked 版本中,它最终是
Application application = (Application)
Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("...")));
所以看起来“真正的”PIA 需要CoClass
属性,但链接版本不需要,因为编译器实际上可以引用CoClass
。没有。它必须动态执行。
我可能会尝试使用这些信息伪造一个 COM 接口,看看我是否可以让编译器链接它...
【讨论】:
【参考方案4】:在你和迈克尔之间,你几乎已经把所有的碎片拼凑在一起了。我认为这就是它的工作原理。 (我没有编写代码,所以我可能会稍微错误地陈述它,但我很确定它是这样的。)
如果:
您正在“新建”一个接口类型,并且 接口类型有一个已知的 coclass,并且 您正在为此界面使用“no pia”功能然后代码生成为 (IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))
如果:
您正在“新建”一个接口类型,并且 接口类型有一个已知的 coclass,并且 您没有为此界面使用“no pia”功能然后生成代码,就像您说“new COCLASSTYPE()”一样。
Jon,如果您对这些内容有任何疑问,请随时直接向我或 Sam 提出问题。仅供参考,Sam 是此功能的专家。
【讨论】:
以上是关于C# 编译器如何检测 COM 类型?的主要内容,如果未能解决你的问题,请参考以下文章