How to: Use Native COM Servers with CRCWs

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

Unlike other .NET languages, Visual C++ provides interop features that allow unmanaged APIs, including COM interfaces, to be accessed directly and seamlessly. For COM interop in particular, this provides powerful advantages.

Example

Like the example in How to: Use Native COM Servers with TLBIMP, this example uses the COM interfaces defined in Quartz.dll (found in the C:\window\System32 directory) to play AVI files. In this case, however, C++ interop is used instead of a separate interop assembly generated with Tlbimp.exe. There are several advantages to this technique. In this case the interop code is built into the application, so there is no dependency on a separate assembly. Also, the exposed managed interface is customized to be more .NET-like. For example, the RenderFile method takes a System.String instead of a char*. The managed version of the COM interface is called a Custom Runtime Callable Wrapper (CRCW).

Writing CRCWs does not require an interop assembly, but it does require header files that define the COM interfaces. For COM components that include type libraries, these headers can be generated using the MIDL Compiler.

The first portion of the following code example defines the custom wrapper, which exposes the members that will be exposed to the managed application. The second portion is a console application that uses the custom wrapper to play AVI files.

Execute the resulting .exe file with the name of a valid AVI file and the file is rendered in a window.

// 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();
}

See Also

Reference

Using Native COM Servers from .NET