C # 编译器如何检测 COM 类型?

编辑: 我把结果写成了 博客文章


C # 编译器在某种程度上奇迹般地处理 COM 类型。

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

直到你意识到 Application是一个接口。在接口上调用构造函数?哎呀!这实际上被转换成对 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 (CompilerGeneratedTypeIdentifier)添加的其他属性似乎没有起到作用... ... 有什么妙招?

16266 次浏览

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

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

一辆高级货车提供混凝土 实施一个或多个 在 COM 中,这种具体的 实现可以在任何 支持 COM 的编程语言 组件开发,例如 Delphi, C + + ,VisualBasic 等等。

参见 我回答了一个类似的问题,关于微软语音 API,在这里您可以“实例化”接口 SpVoice(但实际上,您实例化的是 SPVoiceClass)。

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

在你和迈克尔之间你已经差不多把线索拼凑起来了。我觉得就是这么回事。(我没有编写代码,所以我可能有点说错了,但是我很确定事情就是这样发展的。)

如果:

  • 您正在“新”一个接口类型,并且
  • 接口类型有一个已知的 coclass,并且
  • 你正在为这个界面使用“ no pia”特性

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

如果:

  • 您正在“新”一个接口类型,并且
  • 接口类型有一个已知的 coclass,并且
  • 你没有在这个界面上使用“ no pia”功能

然后生成代码,就好像您说的是“ new COCLASSTYPE ()”一样。

乔恩,如果你对这些东西有什么问题可以直接来找我或者山姆。顺便说一句,山姆是这方面的专家。

好的,这只是为了给迈克尔的回答添加更多的内容(如果他愿意,欢迎他加进来,这种情况下我会删除这个)。

看看 Word 的原始 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
{
}

由于 EricLippert 在 另一个答案中谈到的原因,有两个接口。正如您所说的,这就是 CoClass-无论是从类本身还是 Application接口上的属性来说。

现在,如果我们在 C # 4中使用 PIA 链接,这个 一些会嵌入到最终的二进制文件中... ... 但不是全部。只是创建 Application实例的应用程序最终具有以下类型:

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


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

没有 ApplicationClass-可能是因为它将在执行时从 真的 COM 类型动态加载。

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

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

引用版本中,它的结局是:

Application application = new ApplicationClass();

而在 联系在一起版本中,它的结果是

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

所以看起来“真正的”PIA 需要 CoClass属性,但链接版本不需要,因为编译器实际上可以引用 不是 a CoClass。必须是动态的。

我可能会用这些信息伪造一个 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来工作。

当鼠标悬停在 new IFoo()上时,还要注意信息: Intellisense 正确地捕捉到信息: 很好!