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:
- Why do we go all this trouble?
- 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:
- You can use the built-in .NET runtime interop facilities (such as
PInvoke or COM Interop)
- You can wrap the unmanaged code using the managed extensions to
C++.
- You can rewrite the entire code in a .NET language.
What do we need?
We need
- .NET Framework (1.1)
- Microsoft Visual Studio .NET (preferably 2003)
- 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.
- Managed code vs Unmanaged code
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.
- CLR
- Runtime-Callable Wrapper (RCW)
- COM-Callable Wrapper (CCW)
- Boxing
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
- nsresult NS_InitEmbedding(nsILocalFile *aMozBinDirectory,
nsIDirectoryServiceProvider *aAppFileLocProvider);
- nsresult NS_TermEmbedding();
- ...
Coding Techniques:
-
Using managed object in unmanaged code
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 unmanaged object in managed code
//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:
- Viewing Assembly Contents
- MSIL Disassembler (ildasm.exe)
- TLBIMP.exe
- TLBEXP.exe
- REGASM.exe
- AXIMP.exe
- REGSVCS.exe
References
- Visual Studio .NET
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsintro7/html/vsstartpage.asp)
- Managed Extensions for C++ programming (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmex/html/vcconmcoverview.asp)
- Managed Extensions for C++ specifications
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/html/vcManagedExtensionsSpec_Start.asp)
History:
Draft 0.1 : April 9 2003 Roy Yokoyama