C# 编译器如何检测 COM 类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1093536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does the C# compiler detect COM types?
提问by Jon Skeet
EDIT:I've written the results up as a blog post.
编辑:我已将结果写成博客文章。
The C# compiler treats COM types somewhat magically. For instance, this statement looks normal...
C# 编译器有点神奇地处理 COM 类型。例如,这句话看起来很正常......
Word.Application app = new Word.Application();
... until you realise that Application
is an interface. Calling a constructor on an interface? Yoiks! This actually gets translated into a call to Type.GetTypeFromCLSID()
and another to Activator.CreateInstance
.
...直到你意识到这Application
是一个接口。在接口上调用构造函数?哟!这实际上被转换为对 的调用Type.GetTypeFromCLSID()
和对的调用Activator.CreateInstance
。
Additionally, in C# 4, you can use non-ref arguments for ref
parameters, and the compiler just adds a local variable to pass by reference, discarding the results:
此外,在 C# 4 中,您可以使用非 ref 参数作为ref
参数,编译器只需添加一个局部变量以通过引用传递,丢弃结果:
// FileName parameter is *really* a ref parameter
app.ActiveDocument.SaveAs(FileName: "test.doc");
(Yeah, there are a bunch of arguments missing. Aren't optional parameters nice? :)
(是的,缺少一堆参数。可选参数不是很好吗?:)
I'm trying to investigate the compiler behaviour, and I'm failing to fake the first part. I can do the second part with no problem:
我正在尝试调查编译器的行为,但我没有伪造第一部分。我可以毫无问题地完成第二部分:
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);
}
}
I'd like to be able to write:
我希望能够写:
Dummy dummy = new Dummy();
though. Obviously it'll go bang at execution time, but that's okay. I'm just experimenting.
尽管。显然它会在执行时爆炸,但没关系。我只是在试验。
The other attributes added by the compiler for linked COM PIAs (CompilerGenerated
and TypeIdentifier
) don't seem to do the trick... what's the magic sauce?
编译器为链接的 COM PIA (CompilerGenerated
和TypeIdentifier
)添加的其他属性似乎没有用……神奇的酱汁是什么?
采纳答案by Michael Petrotta
By no means am I an expert in this, but I stumbled recently on what I think you want: the CoClassattribute class.
我绝不是这方面的专家,但我最近偶然发现了我认为你想要的东西:CoClass属性类。
[System.Runtime.InteropServices.CoClass(typeof(Test))]
public interface Dummy { }
A coclass supplies concrete implementation(s) of one or more interfaces. In COM, such concrete implementations can be written in any programming language that supports COM component development, e.g. Delphi, C++, Visual Basic, etc.
coclass 提供一个或多个接口的具体实现。在 COM 中,这样的具体实现可以用任何支持 COM 组件开发的编程语言编写,例如 Delphi、C++、Visual Basic 等。
See my answer to a similar question about the Microsoft Speech API, where you're able to "instantiate" the interface SpVoice
(but really, you're instantiating SPVoiceClass
).
请参阅我对有关 Microsoft Speech API 的类似问题的回答,您可以在其中“实例化”接口SpVoice
(但实际上,您正在实例化SPVoiceClass
)。
[CoClass(typeof(SpVoiceClass))]
public interface SpVoice : ISpeechVoice, _ISpeechVoiceEvents_Event { }
回答by Eric Lippert
Between you and Michael you've almost got the pieces put together. I think this is how it works. (I didn't write the code, so I might be slightly mis-stating it, but I'm pretty sure this is how it goes.)
在你和迈克尔之间,你几乎已经把碎片拼凑起来了。我认为这就是它的工作原理。(代码不是我写的,所以我可能有点误解,但我很确定它是这样的。)
If:
如果:
- you are "new"ing an interface type, and
- the interface type has a known coclass, and
- you ARE using the "no pia" feature for this interface
- 您是“新”接口类型,并且
- 接口类型有一个已知的 coclass,并且
- 您正在为此界面使用“no pia”功能
then the code is generated as (IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))
然后代码生成为 (IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))
If:
如果:
- you are "new"ing an interface type, and
- the interface type has a known coclass, and
- you ARE NOT using the "no pia" feature for this interface
- 您是“新”接口类型,并且
- 接口类型有一个已知的 coclass,并且
- 您没有为此界面使用“no pia”功能
then the code is generated as if you'd said "new COCLASSTYPE()".
然后生成代码就像您说“new COCLASSTYPE()”一样。
Jon, feel free to bug me or Sam directly if you have questions about this stuff. FYI, Sam is the expert on this feature.
Jon,如果您对这些内容有任何疑问,请随时直接找我或 Sam。仅供参考,Sam 是此功能的专家。
回答by Jon Skeet
Okay, this is just to put a bit more flesh on Michael's answer (he's welcome to add it in if he wants to, in which case I'll remove this one).
好的,这只是为了让迈克尔的回答更加充实(如果他愿意,欢迎他添加它,在这种情况下,我会删除这个)。
Looking at the original PIA for Word.Application, there are three types involved (ignoring the events):
查看 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
{
}
There are two interfaces for reasons that Eric Lippert talks about in another answer. And there, as you said, is the CoClass
- both in terms of the class itself and the attribute on the Application
interface.
由于 Eric Lippert 在另一个答案中谈到的原因,有两个接口。正如您所说,就CoClass
类本身和Application
接口上的属性而言,这是- 。
Now if we use PIA linking in C# 4, someof this is embedded in the resulting binary... but not all of it. An application which justcreates an instance of Application
ends up with these types:
现在,如果我们在 C# 4 中使用 PIA 链接,其中一些嵌入在生成的二进制文件中......但不是全部。一个只创建 的实例的应用程序Application
以这些类型结束:
[ComImport, TypeIdentifier, Guid("..."), CompilerGenerated]
public interface _Application
[ComImport, Guid("..."), CompilerGenerated, TypeIdentifier]
public interface Application : _Application
No ApplicationClass
- presumably because that will be loaded dynamically from the realCOM type at execution time.
不ApplicationClass
- 大概是因为它将在执行时从真实的COM 类型动态加载。
Another interesting thing is the difference in the code between the linked version and the non-linked version. If you decompile the line
另一个有趣的事情是链接版本和非链接版本之间的代码差异。如果您反编译该行
Word.Application application = new Word.Application();
in the referencedversion it ends up as:
在引用的版本中,它最终为:
Application application = new ApplicationClass();
whereas in the linkedversion it ends up as
而在链接版本中,它最终为
Application application = (Application)
Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("...")));
So it looks like the "real" PIA needs the CoClass
attribute, but the linked version doesn't because there isn'ta CoClass
the compiler can actually reference. It has to do it dynamically.
所以它看起来像“真实” PIA需要的CoClass
属性,但链接的版本,不会因为有没有一个CoClass
编译器实际上可以参考。它必须动态地做到这一点。
I might try to fake up a COM interface using this information and see if I can get the compiler to link it...
我可能会尝试使用这些信息伪造一个 COM 接口,看看我是否可以让编译器链接它......
回答by Rasmus Faber
Just to add a bit of confirmation to Michael's answer:
只是在迈克尔的回答中添加一点确认:
The following code compiles and runs:
以下代码编译并运行:
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();
}
}
You need both the ComImportAttribute
and the GuidAttribute
for it to work.
您需要ComImportAttribute
和GuidAttribute
才能使其工作。
Also note the information when you hover the mouse over the new IFoo()
: Intellisense properly picks up on the information: Nice!
当您将鼠标悬停在new IFoo()
: Intellisense 正确拾取信息时,还要注意信息: 很好!