HOWTO: Basic Native/Managed Code Interop

IIS7 core extensibility model supports both native and managed code as first-class citizens. So, I feel it is time for a little refresher on managed/native code interop... starting with the more popular route of how to wrap native code API for use within managed code. I am using the newer syntax introduced with .Net Framework 2.0 instead of the older, kludgy syntax.

Now, I am going to ignore the reciprocal route of calling managed code from native code for a couple of reasons:

  • It is just boiler plate COM Interop within native code after generating and registering the CCW (COM Callable Wrapper) of the managed class.
  • Why don't you just write a managed code module/handler in IIS7 to directly use that managed API?

The example illustrates how to use Managed Code to:

  • Pass a .NET String into Managed C++
  • Manipulate a .NET String in Managed C++
  • Return a .NET String from Managed C++
  • Pass in arbitrary number of args into Managed C++

Remember to use a Class Library Project for Sample.h and Sample.cpp to create a Managed C++ Wrapper around native code API, and you can use the resulting Managed Assembly from Sample.cs managed code.

Enjoy,

//David

Sample.h

 #pragma once
#include <windows.h>
#include "SomeNativeAPI.h"

using namespace System;
using namespace System::Runtime::InteropServices;

namespace Sample
{
    public ref class ManagedClass
    {
    public:
        ManagedClass( String^ name );
        ~ManagedClass();
        !ManagedClass();
        String^ DebugPrint( String^ format, ...array<String^>^ args );
    private:
        SomeNativeType* m_pType;
    };
}

Sample.cpp

 #include "Sample.h"

Sample::ManagedClass::ManagedClass( String^ name )
{
    //
    // Convert .NET String into LPSTR for 
    // Native code API to use in constructor
    //
    IntPtr szName;
    szName = Marshal::StringToHGlobalAnsi( name );
    m_pType = new SomeNativeType( szName );
    Marshal::FreeHGlobal( szName );
}
Sample::ManagedClass::~ManagedClass()
{
    this->!ManagedClass();
}
Sample::ManagedClass::!ManagedClass()
{
    delete m_pType;
}
String^ Sample::ManagedClass::DebugPrint( String^ format, ...array<String^>^ args )
{
    //
    // Use Managed Code to format variable arguments as .NET String,
    // convert the .NET String into Unicode String, and pass
    // it to Native API
    //
    String^ formattedString = System::String::Format( format, args );
    IntPtr  wszFormattedString;

    wszFormattedString = Marshal::StringToHGlobalUni( formattedString );
    m_pType->SomeFunctionUnicode( wszFormattedString );
    Marshal::FreeHGlobal( wszFormattedString );

    return formattedString;
}

Sample.cs

 namespace Sample
{
    class Program
    {
        static void Main( string[] args )
        {
            Sample.ManagedClass cls = new Sample.ManagedClass( "Name?" );
            System.Console.WriteLine( cls.DebugPrint( "0:{0},1:{1}", "N", "V" ) );
        }
    }
}