如何:将本机 COM 服务器与 CRCW 一起使用
更新:2007 年 11 月
与其他 .NET 语言不同,Visual C++ 提供了允许直接并无缝访问非托管 API(包括 COM 接口)的互操作功能。特别是对于 COM Interop 而言,这提供了强大的优势。
示例
与如何:将本机 COM 服务器用于 TLBIMP 中的示例相似,此示例使用 Quartz.dll(在 C:\window\System32 目录中)中定义的 COM 接口来播放 AVI 文件。但是在此情况下,将使用 C++ Interop 而不是由 Tlbimp.exe 生成的单独互操作程序集。这项技术有几个优点。在此情况下,将在应用程序中内置互操作代码,所以不依赖于单独程序集。而且,公开的托管接口被自定义为更像 .NET。例如,RenderFile 方法接受 System.String 而不是 char*。COM 接口的托管版本被称为自定义运行库可调用包装 (CRCW)。
编写 CRCW 不需要互操作程序集,但需要定义 COM 接口的头文件。对于包含类型库的 COM 组件,这些头文件可以使用 MIDL Compiler 来生成。
下面的代码示例的第一部分定义自定义包装,该包装公开将向托管应用程序公开的成员。第二部分是使用自定义包装来播放 AVI 文件的控制台应用程序。
以有效的 AVI 文件的名称来执行生成的 .exe 文件,该文件将在窗口中呈现。
// use_native_COM_servers_with_CRCWs.cpp
// compile with: /clr
// processor: x86
#include <comdef.h>
#import "quartz.tlb" no_namespace
using namespace System;
using namespace System::Runtime::InteropServices;
//_COM_SMARTPTR_TYPEDEF(IMediaControl, IID_IMediaControl);
ref struct Player : public IDisposable {
Player() : fm((new IMediaControlPtr())) {
fm->CreateInstance(__uuidof(FilgraphManager), 0, CLSCTX_INPROC_SERVER);
if ((*fm) == 0)
throw gcnew Exception("Could not create COM object");
}
~Player() {
this->!Player();
}
!Player() {
(*fm).Release();
delete fm;
}
void RenderFile(String^ file) {
IntPtr ip = Marshal::StringToBSTR(file);
BSTR b = static_cast<BSTR>(ip.ToPointer());
(*fm)->RenderFile(b);
Marshal::FreeBSTR(ip);
}
void Run() {
(*fm)->Run();
}
private:
IMediaControlPtr* fm;
};
void DisplayUsage() {
Console::WriteLine("AVIPlayer2: Plays AVI files.");
Console::WriteLine("Usage: AVIPlayer2.EXE <filename>");
}
int main() {
array<String^>^ args = Environment::GetCommandLineArgs();
if (args->Length != 2) {
DisplayUsage();
return 0;
}
String^ filename = args[1];
if (filename->Equals("/?")) {
DisplayUsage();
return 0;
}
Player^ player = gcnew Player;
player->RenderFile(filename);
player->Run();
Console::WriteLine("press any key");
Console::ReadLine();
}