Wrapping Gecko APIs using Microsoft Visual Studio .NET Managed Extensions for C++

**DRAFT**

Intended Audience:

This paper is intended for a C++ programmer who would like to know how to wrap Gecko engine from unmanaged C++ to managed C++ .  Author assumes the reader is familiar with Gecko and MS Visual Studio.NET with Managed Extensions for C++. 

Background:

  1. Why do we go all this trouble? 
  2. When we have Gecko as ActiveX control, then why don't we use the Runtime-Callable Wrapper (RCW) and COM-Callable Wrapper (CCW)? 

How do we do this?

There are few ways to convert the existing unmanaged code to managed code:
  1. You can use the built-in .NET runtime interop facilities (such as PInvoke or COM Interop)
  2. You can wrap the unmanaged code using the managed extensions to C++.
  3. You can rewrite the entire code in a .NET language.

What do we need?

We need
  1. .NET Framework (1.1)
  2. Microsoft Visual Studio .NET (preferably 2003)
  3. mozilla development environment (http://www.mozilla.org/build/win32.html)

Terminology:

Following terms are used throughout out this document and it is important to understand what each term means. 
Assembly is a building block of the .Net Framework.  It is the fundamental unit of deployment, version control, reuse, activation, scoping, and security permissions.  It provides the Common Language Runtime (CLR) with the information it needs to be aware of type implementations.  It is a collection of types and resources that are built to work together and form a logical unit of functionality. 
Global Assembly Cache is a machine wide code cache that is installed whereever the CLR is installed.  In most cases, if you intend to share an assembly with multiple applications, you should deploy it into the global assembly cache.
Manged code requires the execution environment of the CLR.  Compilers emit managed code as MSIL, the intermidate language.  The reason for the name is that code is managed by the CLR and objects are allocated from heaps managed by the CLR.
Unmanaged code does not use nor require the execution environment of the Common Language Runtime (CLR).  Unmanaged code is outside the reach of the CLR's security system, garbage collector and other services.
Boxing is a technique to convert a value type to a __gc object by using the __box
Int32 i = 42;
__box Int32* b = __box(i);
UnBoxing (dereferencing) is a technique to convert a boxed object to value type by casting.
Color red;
Object* obj = Enum::Parse(__typeof(Color), S"red");
red = *static_cast<__box Color*>(obj);

Managed Object is an instance of a class which is created in the heap and managed by the garbage collector by using the __gc modifier. 
__gc class Point
{
public:
int x;
int y;
};
Value Types ar typically small, short lived objects and they are usually created on the stack. In managed C++, the value types are defined by using __value modifier. 
__value class Point
{
public:
int x;
int y;
};

Necessary Steps:


Gecko APIs

We will be exposing list of Gecko APIs

Coding Techniques:

Managed pointers are managed by the garbage collector so that when copies are made, the gc knows that references are created.  When a pointer is passed to native code, the gc cannot track its usage and so cannot determine any change in object reference.   Furthermore, if a garbage collection occures, the object can be moved in memory, so the gc changes all managed pointers so that they point to the new location.  Because the gc doesn't have access to the pointers passed to native code (unmanaged code), potentially a pointer used in native code could suddenly become invalid.  Use a pinned pointer which tells gc not to move the memory.
//Using pinning
#progma unmanaged
void print(int *p)
{
    printf("%ld\n", *p);
}

#progma managed
_gc struct Test {
    int i;
};

void main()
{
    Test * t = new Test;
    int __pin* p = &t->i;
    print(p);
}

//Using GCHandle
#using <mscorlib.dll>

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

#pragma managed
class AppDomainWrapper
{
private:
int m_handle;
public:
AppDomainWrapper() {
AppDomain* d = AppDomain::Current;
m_handle = (GCHandle::op_Explicit(GCHandle::Alloc(d))).ToInt32();
}
~AppDomainWrapper() {
(GCHandle::op_Explicit(m_handle)).Free();
}
// more functions here...
void PrintBaseDir() {
AppDomain* domain = __try_cast<AppDomain*>(
(GCHandle::op_Explicit(m_handle)).Target);
Console::WriteLine ( S"AppDomain Base Directory: {0}",
domain->BaseDirectory );
}
};

#pragma unmanaged
int main() {
AppDomainWrapper w;
w.PrintBaseDir();
return 0;
}
//Using gcroot
#using <mscorlib.dll>
#include <gcroot.h>

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

#pragma managed
class AppDomainWrapper
{
private:
gcroot<AppDomain*> m_domain;
public:
AppDomainWrapper() {
m_domain = AppDomain::CurrentDomain;
}
~AppDomainWrapper() {
}
// more functions here...
void PrintBaseDir() {
Console::WriteLine ( S"AppDomain Base Directory: {0}",
m_domain->BaseDirectory );
}
};

#pragma unmanaged
int main() {
AppDomainWrapper w;
w.PrintBaseDir();
return 0;



}

Useful Tools:


References


History:

Draft 0.1 : April 9 2003  Roy Yokoyama