EDIT: 我把结果写成blog post .
C#编译器有点神奇地处理COM类型 . 例如,这个陈述看起来很正常......
Word.Application app = new Word.Application();
......直到你意识到 Application
是一个界面 . 在接口上调用构造函数? Yoiks!这实际上被转换为对Type.GetTypeFromCLSID()的调用以及对Activator.CreateInstance的另一次调用 .
此外,在C#4中,您可以为 ref
参数使用非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
)添加的其他属性是什么?
4 回答
在你和迈克尔之间,你几乎把这些东西放在一起 . 我认为这是它的工作原理 . (我没有编写代码,所以我可能会稍微误解它,但我很确定这是怎么回事 . )
如果:
你是一个接口类型_315174
接口类型有一个已知的coclass,和
您正在使用此界面的"no pia"功能
然后代码生成为(IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))
如果:
你是_315176的接口类型,和
接口类型有一个已知的coclass,和
您没有在此界面中使用"no pia"功能
然后生成代码就像你说“new COCLASSTYPE()”一样 .
乔恩,如果你对这些东西有疑问,请随时给我或Sam直接打扰 . 仅供参考,Sam是此功能的专家 .
只是为迈克尔的回答添加一点确认:
以下代码编译并运行:
你需要
ComImportAttribute
和GuidAttribute
来使它工作 .当你将鼠标悬停在
new IFoo()
上时,请注意信息:Intellisense正确地获取信息:很好!我绝不是这方面的专家,但最近我偶然发现了我想要的东西:CoClass属性类 .
请参阅my answer to a similar question about the Microsoft Speech API,您可以在"instantiate"接口
SpVoice
(但实际上,您正在实例化SPVoiceClass
) .好吧,这只是为迈克尔的回答增加了一点点(如果他愿意的话,欢迎加入它,在这种情况下我会删除它) .
查看Word.Application的原始PIA,涉及三种类型(忽略事件):
由于Eric Lippert在another answer中谈到的原因,有两个接口 . 正如你所说,那里是
CoClass
- 无论是类本身还是Application
界面上的属性 .现在,如果我们在C#4中使用PIA链接,其中一些嵌入在生成的二进制文件中......但不是全部 . 只创建
Application
实例的应用程序最终会使用以下类型:否
ApplicationClass
- 可能是因为它将在执行时从真实的COM类型动态加载 .另一个有趣的事情是链接版本和非链接版本之间的代码差异 . 如果您反编译该行
在引用的版本中,它最终为:
而在链接版本中它最终为
所以看起来"real" PIA需要
CoClass
属性,但链接版本不需要,因为编译器实际上没有引用CoClass
. 它必须动态地完成它 .我可能会尝试使用此信息伪造一个COM接口,看看我是否可以让编译器链接它...