git-svn-id: https://mono-soc-2010.googlecode.com/svn/trunk/cppinterop@7 a470b8cb-0e6f-1642-1b45-71e107334c4b
This commit is contained in:
alexander.corrado 2010-05-26 10:34:07 +00:00
Родитель 389b7dc313
Коммит f7683a594f
22 изменённых файлов: 1715 добавлений и 0 удалений

45
CPPInterop.sln Normal file
Просмотреть файл

@ -0,0 +1,45 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.VisualC.Interop", "Mono.VisualC.Interop\Mono.VisualC.Interop.csproj", "{4A864586-93C5-4DC1-8A80-F094A88506D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPPPOC", "CPPPOC\CPPPOC.csproj", "{7F309FEA-3A3F-40B1-BBF9-A09796253202}"
EndProject
Project("{2857B73E-F847-4B02-9238-064979017E93}") = "CPPTest", "CPPTest\CPPTest.cproj", "{B01E6282-144E-481A-8E1F-95F708DFBC2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Linux|Any CPU = Linux|Any CPU
Mac|Any CPU = Mac|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Linux|Any CPU.ActiveCfg = Linux|Any CPU
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Linux|Any CPU.Build.0 = Linux|Any CPU
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Mac|Any CPU.ActiveCfg = Mac|Any CPU
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Mac|Any CPU.Build.0 = Mac|Any CPU
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Linux|Any CPU.ActiveCfg = Linux|Any CPU
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Linux|Any CPU.Build.0 = Linux|Any CPU
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Mac|Any CPU.ActiveCfg = Mac|Any CPU
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Mac|Any CPU.Build.0 = Mac|Any CPU
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Linux|Any CPU.ActiveCfg = Linux|Any CPU
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Linux|Any CPU.Build.0 = Linux|Any CPU
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Mac|Any CPU.ActiveCfg = Mac|Any CPU
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Mac|Any CPU.Build.0 = Mac|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = CPPPOC\CPPPOC.csproj
Policies = $0
$0.TextStylePolicy = $1
$1.TabWidth = 8
$1.NoTabsAfterNonTabs = True
$1.RemoveTrailingWhitespace = True
$1.inheritsSet = VisualStudio
$1.inheritsScope = text/plain
$1.scope = text/x-csharp
$0.CSharpFormattingPolicy = $2
$2.inheritsSet = Mono
$2.inheritsScope = text/x-csharp
$2.scope = text/x-csharp
name = CPPInterop
EndGlobalSection
EndGlobal

29
CPPPOC/AssemblyInfo.cs Normal file
Просмотреть файл

@ -0,0 +1,29 @@
// AssemblyInfo.cs created with MonoDevelop
// User: alex at 17:40 03/14/2009
//
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("CPPPOC")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]

63
CPPPOC/CPPPOC.csproj Normal file
Просмотреть файл

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{7F309FEA-3A3F-40B1-BBF9-A09796253202}</ProjectGuid>
<OutputType>Exe</OutputType>
<AssemblyName>CPPPOC</AssemblyName>
<RootNamespace>CPPPOC</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Linux|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Mac|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Mac</OutputPath>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Mono.VisualC.Interop\Mono.VisualC.Interop.csproj">
<Project>{4A864586-93C5-4DC1-8A80-F094A88506D7}</Project>
<Name>Mono.VisualC.Interop</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AssemblyInfo.cs" />
<Compile Include="CSimpleClass.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<MonoDevelop>
<Properties InternalTargetFrameworkVersion="3.5" />
</MonoDevelop>
</ProjectExtensions>
</Project>

96
CPPPOC/CSimpleClass.cs Normal file
Просмотреть файл

@ -0,0 +1,96 @@
// CSimpleClass.cs created with MonoDevelop
// User: alex at 17:41 03/14/2009
//
using System;
using System.Runtime.InteropServices;
using Mono.VisualC.Interop;
namespace CPPPOC {
public class CSimpleClass : ICppInstance {
private interface __ICSimpleClass : ICppOverridable<CSimpleClass> {
void CSimpleClass(CppInstancePtr ths, int value);
void M0(CppInstancePtr ths);
[Virtual] void V0(CppInstancePtr ths, int x, int y);
void M1(CppInstancePtr ths, int x);
[Virtual] void V1(CppInstancePtr ths, int x);
void M2(CppInstancePtr ths, int x, int y);
[Virtual] void V2(CppInstancePtr ths);
CppField<int> value {get;}
}
private struct __CSimpleClass {
public int value;
}
private static __ICSimpleClass _impl;
public static void Bind(CppLibrary lib) {
_impl = lib.GetClass<__ICSimpleClass,__CSimpleClass,CSimpleClass>("CSimpleClass");
}
private CppInstancePtr _native;
public CSimpleClass(int value) {
_native = _impl.Alloc(this);
_impl.CSimpleClass(_native, value);
}
public CSimpleClass(IntPtr native) {
_native = native;
}
public virtual IntPtr Native {
get {
return (IntPtr)_native;
}
}
public virtual int value {
get {
return _impl.value[_native];
}
set {
_impl.value[_native] = value;
}
}
public void M0() {
_impl.M0(_native);
}
public void M1(int x) {
_impl.M1(_native, x);
}
public void M2(int x, int y) {
_impl.M2(_native, x, y);
}
[OverrideNative]
public virtual void V0(int x, int y) {
Console.WriteLine("Managed V0({0}, {1})", x, y);
_impl.V0(_native, x, y);
}
[OverrideNative]
public virtual void V1(int x) {
Console.WriteLine("Managed V1({0})", x);
_impl.V1(_native, x);
}
[OverrideNative]
public virtual void V2() {
Console.WriteLine("Managed V2()");
_impl.V2(_native);
}
public void Dispose() {
_impl.Destruct(_native);
_native.Dispose();
}
}
}

47
CPPPOC/Main.cs Normal file
Просмотреть файл

@ -0,0 +1,47 @@
// Main.cs created with MonoDevelop
// User: alex at 17:40 03/14/2009
//
using System;
using Mono.VisualC.Interop;
using System.Runtime.InteropServices;
namespace CPPPOC
{
class MainClass
{
public static void Main(string[] args)
{
// bind all wrapper classes to their native implementations
CppLibrary cppTest = new CppLibrary("CPPTest", new Mono.VisualC.Interop.ABI.Itanium());
CSimpleClass.Bind(cppTest);
CSimpleClass csc1 = new CSimpleClass(CreateCSimpleSubClass(10));
CSimpleClass csc2 = new CSimpleClass(2);
try {
csc1.M0();
Console.WriteLine("Managed code got value: {0}", csc1.value);
csc2.M0();
Console.WriteLine("Managed code got value: {0}", csc2.value);
csc1.value = 100;
csc1.V2();
csc2.value = 200;
csc2.V2();
} finally {
DestroyCSimpleSubClass(csc1.Native);
csc1.Dispose();
csc2.Dispose();
}
}
[DllImport("CPPTest")]
public static extern IntPtr CreateCSimpleSubClass(int value);
[DllImport("CPPTest")]
public static extern void DestroyCSimpleSubClass(IntPtr obj);
}
}

66
CPPTest/CPPTest.cpp Normal file
Просмотреть файл

@ -0,0 +1,66 @@
/*
* CPPTest.cpp
* CPPTest
*
* Created by Alex Corrado on 3/14/09.
* Copyright 2009 __MyCompanyName__. All rights reserved.
*
*/
#include <stdio.h>
#include "CPPTest.h"
CSimpleClass::CSimpleClass(int value) : value(value) {
printf("CSimpleClass(%d)\n", value);
this->value = value;
}
CSimpleSubClass::CSimpleSubClass(int value) : CSimpleClass(value) {
printf("CSimpleSubClass(%d)\n", value);
}
CSimpleClass::~CSimpleClass() {
printf("~CSimpleClass\n");
}
void CSimpleClass::M0() {
printf("C++/CSimpleClass::M0()\n");
V0(value, value + 1);
V1(value);
V2();
}
void CSimpleClass::V0(int x, int y) {
printf("C++/CSimpleClass::V0(%d, %d)\n", x, y);
}
void CSimpleSubClass::V0(int x, int y) {
printf("C++/CSimpleSubClass::V0(%d, %d)\n", x, y);
}
void CSimpleClass::M1(int x) {
printf("C++/CSimpleClass::M1(%d)\n", x);
V0(x, value);
V1(x);
V2();
}
void CSimpleClass::V1(int x) {
printf("C++/CSimpleClass::V1(%d)\n", x);
}
void CSimpleSubClass::V1(int x) {
printf("C++/CSimpleSubClass::V1(%d)\n", x);
}
void CSimpleClass::M2(int x, int y) {
}
void CSimpleClass::V2() {
printf("C++/CSimpleClass::V2() - value: %d\n", value);
}
void CSimpleSubClass::V2() {
printf("C++/CSimpleSubClass::V2() - value: %d\n", value);
}

63
CPPTest/CPPTest.cproj Normal file
Просмотреть файл

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{B01E6282-144E-481A-8E1F-95F708DFBC2D}</ProjectGuid>
<Compiler>
<Compiler ctype="GccCompiler" />
</Compiler>
<Language>C</Language>
<Target>Bin</Target>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug</OutputPath>
<DefineSymbols>DEBUG MONODEVELOP</DefineSymbols>
<SourceDirectory>.</SourceDirectory>
<OutputName>CPPTest</OutputName>
<CompileTarget>SharedLibrary</CompileTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release</OutputPath>
<DefineSymbols>MONODEVELOP</DefineSymbols>
<SourceDirectory>.</SourceDirectory>
<OptimizationLevel>3</OptimizationLevel>
<OutputName>CPPTest</OutputName>
<CompileTarget>Bin</CompileTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Mac|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>build\Debug</OutputPath>
<ExtraLinkerArguments>-dynamic</ExtraLinkerArguments>
<DefineSymbols>DEBUG MONODEVELOP</DefineSymbols>
<SourceDirectory>.</SourceDirectory>
<CustomCommands>
<CustomCommands>
<Command type="Build" command="xcodebuild -configuration Debug" workingdir="${ProjectDir}" />
<Command type="AfterBuild" command="cp ./build/Debug/libCPPTest.dylib ../CPPPOC/bin/Mac" workingdir="${ProjectDir}" />
</CustomCommands>
</CustomCommands>
<OutputName>libCPPTest.dylib</OutputName>
<CompileTarget>Bin</CompileTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Linux|AnyCPU' ">
<OutputPath>build\Debug</OutputPath>
<DefineSymbols>DEBUG MONODEVELOP</DefineSymbols>
<CompileTarget>SharedLibrary</CompileTarget>
<OutputName>libCPPTest.so</OutputName>
<CustomCommands>
<CustomCommands>
<Command type="AfterBuild" command="cp ./build/Debug/libCPPTest.so ../CPPPOC/bin/Linux" workingdir="${ProjectDir}" />
</CustomCommands>
</CustomCommands>
</PropertyGroup>
<ItemGroup>
<None Include="CPPTest.h" />
</ItemGroup>
<ItemGroup>
<Compile Include="CPPTest.cpp" />
</ItemGroup>
</Project>

47
CPPTest/CPPTest.h Normal file
Просмотреть файл

@ -0,0 +1,47 @@
/*
* CPPTest.h
* CPPTest
*
* Created by Alex Corrado on 3/14/09.
* Copyright 2009 __MyCompanyName__. All rights reserved.
*
*/
#ifndef CPPTest_
#define CPPTest_
/* The classes below are exported */
#pragma GCC visibility push(default)
class CSimpleClass {
public:
int value;
CSimpleClass(int value);
~CSimpleClass();
void M0();
virtual void V0(int x, int y);
void M1(int x);
virtual void V1(int x);
void M2(int x, int y);
virtual void V2();
};
class CSimpleSubClass : CSimpleClass {
public:
CSimpleSubClass(int value);
virtual void V0(int x, int y);
virtual void V1(int x);
virtual void V2();
};
extern "C" {
CSimpleSubClass* CreateCSimpleSubClass(int value) {
return new CSimpleSubClass(value);
}
void DestroyCSimpleSubClass(CSimpleSubClass* obj) {
delete obj;
}
}
#pragma GCC visibility pop
#endif

Просмотреть файл

@ -0,0 +1,487 @@
//
// Mono.VisualC.Interop.ABI.CppAbi.cs: Represents an abstract C++ ABI
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
//TODO: Exception handling, operator overloading etc.
//TODO: Allow interface to override default calling convention
public abstract class CppAbi {
protected ModuleBuilder implModule;
protected TypeBuilder implType;
protected Type interfaceType, layoutType, wrapperType;
protected string library, className;
protected FieldBuilder vtableField;
protected ILGenerator ctorIL;
public virtual Iface ImplementClass<Iface, NLayout> (ModuleBuilder implModule, Type wrapperType, string lib, string className)
where NLayout : struct
//where Iface : ICppNativeInterface or ICppNativeInterfaceManaged
{
this.implModule = implModule;
this.library = lib;
this.className = className;
this.interfaceType = typeof (Iface);
this.layoutType = typeof (NLayout);
this.wrapperType = wrapperType;
MethodInfo[] methods = interfaceType.GetMethods ();
// sort methods into declaration order
// TODO: This is kinda kludgy isn't it?
Array.Sort (methods, (x, y) => x.MetadataToken - y.MetadataToken);
int vtableIndex = 0;
List<Delegate> vtableDelegates = new List<Delegate>();
// Implement all methods
for (int i = 0; i < methods.Length; i++) {
// Skip over special methods like property accessors -- properties will be handled later
if (methods [i].IsSpecialName)
continue;
DefineMethod (methods [i], vtableIndex);
if (!Modifiers.IsVirtual (methods [i]))
continue;
MethodInfo overrideTarget = FindManagedOverrideTarget (methods [i], VTable.BindOverridesOnly);
if (overrideTarget != null)
vtableDelegates.Insert(vtableIndex, GetManagedOverrideTrampoline (methods [i], overrideTarget));
vtableIndex++;
}
DefineImplType ();
// Implement all properties
foreach (var property in interfaceType.GetProperties ())
DefineFieldProperty (property);
ctorIL.Emit (OpCodes.Ret);
return (Iface)Activator.CreateInstance (implType.CreateType (), vtable);
}
// These methods might be more commonly overridden for a given C++ ABI:
public virtual MethodType GetMethodType (MethodInfo imethod)
{
if (imethod.Name.Equals (className))
return MethodType.NativeCtor;
else if (imethod.Name.Equals ("Alloc"))
return MethodType.ManagedAlloc;
else if (imethod.Name.Equals ("Destruct"))
return MethodType.NativeDtor;
return MethodType.Native;
}
public virtual int FieldOffsetPadding {
get { return Marshal.SizeOf (typeof (IntPtr)); }
}
protected virtual int NativeSize {
get {
// By default: native size = C++ class size + field offset padding (usually just vtable pointer)
// TODO: Only include vtable ptr if there are virtual functions? Here I guess it doesn't really matter,
// we're just allocing extra memory.
return Marshal.SizeOf (layoutType) + FieldOffsetPadding;
}
}
protected virtual VTable MakeVTable (Delegate[] overrides)
{
return new VTableManaged (overrides);
}
// The members below must be implemented for a given C++ ABI:
public abstract string GetMangledMethodName (MethodInfo methodInfo);
public abstract CallingConvention DefaultCallingConvention { get; }
protected virtual void DefineImplType ()
{
implType = implModule.DefineType (implModule.Name + "_" + interfaceType.Name + "_Impl",
TypeAttributes.Class | TypeAttributes.Sealed);
implType.AddInterfaceImplementation (interfaceType);
vtableField = implType.DefineField ("_vtable", typeof (VTable), FieldAttributes.InitOnly | FieldAttributes.Private);
ConstructorBuilder ctor = implType.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard,
new Type[] { typeof (VTable) });
ctorIL = ctor.GetILGenerator ();
// this._vtable = (VTable passed to constructor)
ctorIL.Emit (OpCodes.Ldarg_0);
ctorIL.Emit (OpCodes.Ldarg_1);
ctorIL.Emit (OpCodes.Stfld, vtableField);
}
protected virtual MethodBuilder DefineMethod (MethodInfo interfaceMethod, int index)
{
// 0. Introspect method
MethodType methodType = GetMethodType (interfaceMethod);
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, false);
// 1. Generate managed trampoline to call native method
MethodBuilder trampoline = GetMethodBuilder (interfaceMethod);
ILGenerator il = trampoline.GetILGenerator ();
if (methodType == MethodType.ManagedAlloc) {
EmitManagedAlloc (il);
il.Emit (OpCodes.Ret);
return trampoline;
}
// 2. Load the native C++ instance pointer
LocalBuilder cppInstancePtr, nativePtr;
EmitLoadInstancePtr (il, parameterTypes[0], out cppInstancePtr, out nativePtr);
// 3. Make sure our native pointer is a valid reference. If not, throw ObjectDisposedException
EmitCheckDisposed (il, nativePtr, methodType);
MethodInfo nativeMethod;
if (!Modifiers.IsVirtual (interfaceMethod)) {
nativeMethod = null;
else
nativeMethod = GetPInvokeForMethod (interfaceMethod);
switch (methodType) {
case MethodType.NativeCtor:
EmitConstruct (il, nativeMethod, parameterTypes.Length, nativePtr);
break;
case MethodType.NativeDtor:
EmitDestruct (il, nativeMethod, parameterTypes.Length, nativePtr);
break;
default: // regular native method
EmitCallNative (il, nativeMethod, parameterTypes.Length, nativePtr);
break;
}
} else
il.Emit (OpCodes.Ret);
return trampoline;
}
protected virtual PropertyBuilder DefineFieldProperty (PropertyInfo property)
{
if (property.CanWrite)
throw new InvalidProgramException ("Properties in C++ interface must be read-only.");
MethodInfo imethod = property.GetGetMethod ();
string methodName = imethod.Name;
string propName = property.Name;
Type retType = imethod.ReturnType;
if ((!retType.IsGenericType) || (!retType.GetGenericTypeDefinition ().Equals (typeof (CppField<>))))
throw new InvalidProgramException ("Properties in C++ interface can only be of type CppField.");
PropertyBuilder fieldProp = implType.DefineProperty (propName, PropertyAttributes.None, retType, Type.EmptyTypes);
FieldBuilder fieldData = implType.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private);
// init our field data with a new instance of CppField
// first, get field offset
ctorIL.Emit (OpCodes.Ldarg_0);
/* TODO: Code prolly should not emit hardcoded offsets n such, in case we end up saving these assemblies in the future.
* Something more like this perhaps? (need to figure out how to get field offset padding into this)
* ctorIL.Emit(OpCodes.Ldtoken, nativeLayout);
* ctorIL.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
* ctorIL.Emit(OpCodes.Ldstr, propName);
* ctorIL.Emit(OpCodes.Call, typeof(Marshal).GetMethod("OffsetOf"));
*/
int fieldOffset = ((int)Marshal.OffsetOf (layoutType, propName)) + FieldOffsetPadding;
ctorIL.Emit (OpCodes.Ldc_I4, fieldOffset);
ctorIL.Emit (OpCodes.Newobj, retType.GetConstructor (new Type[] { typeof(int) }));
ctorIL.Emit (OpCodes.Stfld, fieldData);
MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder fieldGetter = implType.DefineMethod (methodName, methodAttr, retType, Type.EmptyTypes);
ILGenerator il = fieldGetter.GetILGenerator ();
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, fieldData);
il.Emit (OpCodes.Ret);
fieldProp.SetGetMethod (fieldGetter);
return fieldProp;
}
/**
* Implements the managed trampoline that will be invoked from the vtable by native C++ code when overriding
* the specified C++ virtual method with the specified managed one.
*/
protected virtual Delegate GetManagedOverrideTrampoline (MethodInfo interfaceMethod, MethodInfo targetMethod)
{
Type delegateType = Util.GetDelegateTypeForMethodInfo (implModule, interfaceMethod);
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, true);
// TODO: According to http://msdn.microsoft.com/en-us/library/w16z8yc4.aspx
// The dynamic method created with this constructor has access to public and internal members of all the types contained in module m.
// This does not appear to hold true, so we also disable JIT visibility checks.
DynamicMethod trampolineIn = new DynamicMethod (wrapperType.Name + "_" + interfaceMethod.Name + "_FromNative", interfaceMethod.ReturnType,
parameterTypes, typeof (CppInstancePtr).Module, true);
ILGenerator il = trampolineIn.GetILGenerator ();
// for static methods:
OpCode callInstruction = OpCodes.Call;
int argLoadStart = 0;
// for instance methods, we need an instance to call them on!
if (!targetMethod.IsStatic) {
callInstruction = OpCodes.Callvirt;
argLoadStart = 1;
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldc_I4, NativeSize);
MethodInfo getManagedObj = typeof (CppInstancePtr).GetMethod ("GetManaged", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod (wrapperType);
il.Emit (OpCodes.Call, getManagedObj);
}
for (int i = argLoadStart; i < parameterTypes.Length; i++) {
il.Emit (OpCodes.Ldarg, i);
}
il.Emit (OpCodes.Tailcall);
il.Emit (callInstruction, targetMethod);
il.Emit (OpCodes.Ret);
return trampolineIn.CreateDelegate (delegateType);
}
protected virtual MethodInfo FindManagedOverrideTarget (MethodInfo interfaceMethod, MemberFilter filter)
{
MemberInfo[] possibleMembers = wrapperType.FindMembers (MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static, filter, interfaceMethod);
if (possibleMembers.Length > 1)
throw new InvalidProgramException ("More than one possible override found when binding virtual method: " + interfaceMethod.Name);
else if (possibleMembers.Length == 0)
return null;
return (MethodInfo)possibleMembers [0];
}
/**
* Defines a new MethodBuilder with the same signature as the passed MethodInfo
*/
protected virtual MethodBuilder GetMethodBuilder (MethodInfo interfaceMethod)
{
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, false);
MethodBuilder methodBuilder = implType.DefineMethod (interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual,
interfaceMethod.ReturnType, parameterTypes);
return methodBuilder;
}
/**
* Defines a new MethodBuilder that calls the specified C++ (non-virtual) method using its mangled name
*/
protected virtual MethodBuilder GetPInvokeForMethod (MethodInfo signature)
{
string entryPoint = GetMangledMethodName (signature);
if (entryPoint == null)
throw new NotSupportedException ("Could not mangle method name.");
Type[] parameterTypes = Util.GetMethodParameterTypes (signature, true);
MethodBuilder builder = implType.DefinePInvokeMethod ("__$" + signature.Name + "_Impl", library, entryPoint,
MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.PinvokeImpl,
CallingConventions.Any, signature.ReturnType, parameterTypes,
DefaultCallingConvention, CharSet.Ansi);
builder.SetImplementationFlags (builder.GetMethodImplementationFlags () | MethodImplAttributes.PreserveSig);
return builder;
}
/**
* Emits IL to allocate the memory for a new instance of the C++ class.
* To complete method, emit OpCodes.Ret.
*/
protected virtual void EmitManagedAlloc (ILGenerator il)
{
//TODO: Do not hard-emit native size in case assembly is saved?
il.Emit (OpCodes.Ldc_I4, NativeSize);
if (wrapperType != null) {
// load managed object
il.Emit (OpCodes.Ldarg_1);
//new CppInstancePtr (Abi.GetNativeSize (typeof (NativeLayout)), managedWrapper);
il.Emit (OpCodes.Newobj, typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null,
new Type[] { typeof (int), typeof (object) }, null));
} else
il.Emit (OpCodes.Newobj, typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null,
new Type[] { typeof (int) }, null));
}
protected virtual void EmitConstruct (ILGenerator il, MethodInfo nativeMethod, int parameterCount, LocalBuilder nativePtr)
{
EmitCallNative (il, nativeMethod, parameterCount, nativePtr);
EmitInitVTable (il, nativePtr);
}
protected virtual void EmitDestruct (ILGenerator il, MethodInfo nativeMethod, int parameterCount, LocalBuilder nativePtr)
{
EmitResetVTable (il, nativePtr);
EmitCallNative (il, nativeMethod, parameterCount, nativePtr);
}
/**
* Emits IL to call the native method. nativeMethod should be either a method obtained by
* GetPInvokeForMethod or the MethodInfo of a vtable method.
* To complete method, emit OpCodes.Ret.
*/
protected virtual void EmitCallNative (ILGenerator il, MethodInfo nativeMethod, int parameterCount, LocalBuilder nativePtr)
{
il.Emit (OpCodes.Ldloc_S, nativePtr);
for (int i = 2; i <= parameterCount; i++)
il.Emit (OpCodes.Ldarg, i);
il.Emit (OpCodes.Call, nativeMethod);
}
/**
* Emits IL to set the vtable pointer of the instance (if class has a vtable).
* This should usually happen in the managed wrapper of the C++ instance constructor.
*/
protected virtual void EmitInitVTable (ILGenerator il, LocalBuilder nativePtr)
{
// this._vtable.InitInstance (nativePtr);
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, vtableField);
il.Emit (OpCodes.Ldloc_S, nativePtr);
EmitVTableOp (il, typeof (VTable).GetMethod ("InitInstance"), 2, false);
}
protected virtual void EmitResetVTable (ILGenerator il, LocalBuilder nativePtr)
{
// this._vtable.ResetInstance (nativePtr);
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, vtableField);
il.Emit (OpCodes.Ldloc_S, nativePtr);
EmitVTableOp (il, typeof(VTable).GetMethod ("ResetInstance"), 2, false);
}
/**
* A utility function to emit the IL for a vtable-dependant operation.
* In other words, classes with no virtual methods will not have vtables,
* so this method emits code to check for that and either throw an exception
* or do nothing if no vtable exists. To use, push the arguments to the method you
* want to call and pass the stackHeight for the call. If no vtable exists, this method
* will emit code to pop the arguments off the stack.
*/
protected virtual void EmitVTableOp(ILGenerator il, MethodInfo method, int stackHeight, bool throwOnNoVTable)
{
// prepare a jump; do not call vtable method if no vtable
Label noVirt = il.DefineLabel ();
Label dontPushOrThrow = il.DefineLabel ();
il.Emit (OpCodes.Ldarg_0); // load this
il.Emit (OpCodes.Ldfld, vtableField); // load this._vtable
il.Emit (OpCodes.Brfalse_S, noVirt); // if (vtableInfo == null) goto noVirt
il.Emit (OpCodes.Callvirt, method); // call method
il.Emit (OpCodes.Br_S, dontPushOrThrow); // goto dontPushOrThrow
il.MarkLabel (noVirt);
// noVirt:
// since there is no vtable, we did not make the method call.
// pop arguments
for (int i = 0; i < stackHeight; i++)
il.Emit (OpCodes.Pop);
// if the method was supposed to return a value, we must
// still push something onto the stack
// TODO: This is a kludge. What about value types?
if (!method.ReturnType.Equals (typeof (void)))
il.Emit (OpCodes.Ldnull);
if (throwOnNoVTable) {
il.Emit (OpCodes.Ldstr, "Native class has no VTable.");
il.Emit (OpCodes.Newobj, typeof (InvalidOperationException).GetConstructor(new Type[] {typeof (string)}));
il.Emit (OpCodes.Throw);
}
il.MarkLabel (dontPushOrThrow);
}
protected virtual void EmitLoadInstancePtr (ILGenerator il, Type firstParamType, out LocalBuilder cppip, out LocalBuilder native)
{
cppip = null;
native = il.DeclareLocal (typeof (IntPtr));
il.Emit (OpCodes.Ldarg_1);
if (firstParamType.Equals (typeof (CppInstancePtr))) {
cppip = il.DeclareLocal (typeof (CppInstancePtr));
il.Emit (OpCodes.Stloc_S, cppip);
il.Emit (OpCodes.Ldloca_S, cppip);
il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("Native").GetGetMethod ());
il.Emit (OpCodes.Stloc_S, native);
} else if (firstParamType.Equals (typeof (IntPtr)))
il.Emit (OpCodes.Stloc_S, native);
else
throw new ArgumentException ("First argument to C++ method must be IntPtr or CppInstancePtr.");
}
protected virtual void EmitCheckManagedAlloc (ILGenerator il, LocalBuilder cppip)
{
// make sure we were allocated by managed code
// if not, return
Label managedAlloc = il.DefineLabel ();
il.Emit (OpCodes.Ldloca_S, cppip);
il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("IsManagedAlloc").GetGetMethod ());
il.Emit (OpCodes.Brtrue_S, managedAlloc);
il.Emit (OpCodes.Ret);
il.MarkLabel (managedAlloc);
}
/**
* throw ObjectDisposedException if we have a null pointer for native
* however, allow destructor to be called even if we're disposed (just return immediately)
*/
protected virtual void EmitCheckDisposed (ILGenerator il, LocalBuilder native, MethodType methodType)
{
Label validRef = il.DefineLabel ();
il.Emit (OpCodes.Ldloc_S, native);
il.Emit (OpCodes.Brtrue_S, validRef);
if (methodType == MethodType.NativeDtor) {
il.Emit (OpCodes.Ret);
il.MarkLabel (validRef);
} else {
il.Emit (OpCodes.Ldstr, String.Empty);
il.Emit (OpCodes.Newobj, typeof (ObjectDisposedException).GetConstructor (new Type[] { typeof(string) }));
il.Emit (OpCodes.Throw);
il.MarkLabel (validRef);
}
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
using System;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public class Itanium : CppAbi {
public Itanium() {}
public override CallingConvention DefaultCallingConvention {
get {
return CallingConvention.Cdecl;
}
}
public override string GetMangledMethodName(MethodInfo methodInfo) {
string methodName = methodInfo.Name;
MethodType methodType = GetMethodType(methodInfo);
ParameterInfo[] parameters = methodInfo.GetParameters();
StringBuilder nm = new StringBuilder("_ZN", 30);
nm.Append(className.Length).Append(className);
switch (methodType) {
case MethodType.NativeCtor:
nm.Append("C1");
break;
case MethodType.NativeDtor:
nm.Append("D1");
break;
default:
nm.Append(methodName.Length).Append(methodName);
break;
}
nm.Append("E");
if (parameters.Length == 1) { //only the C++ "this" object
nm.Append("v");
} else for (int i = 1; i < parameters.Length; i++) {
nm.Append(GetTypeCode(parameters[i].ParameterType));
}
return nm.ToString();
}
protected virtual string GetTypeCode(Type t) {
if (t.Equals(typeof(int))) return "i";
throw new NotSupportedException("Unsupported parameter type: " + t.ToString());
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using System;
namespace Mono.VisualC.Interop {
public enum MethodType {
Native,
NativeCtor,
NativeDtor,
ManagedAlloc
}
}

Просмотреть файл

@ -0,0 +1,141 @@
//
// Mono.VisualC.Interop.ABI.VTable.cs: abstract vtable
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public abstract class VTable : IDisposable {
protected IntPtr basePtr, vtPtr;
public virtual int EntryCount { get; protected set; }
public abstract int EntrySize { get; }
public abstract void EmitVirtualCall (ILGenerator il, IntPtr native, int index);
// Creates a new VTable
public VTable (Delegate[] overrides)
{
EntryCount = overrides.Length;
int vtableSize = EntryCount * EntrySize;
IntPtr vtEntryPtr;
basePtr = IntPtr.Zero;
vtPtr = Marshal.AllocHGlobal (vtableSize);
try {
int offset = 0;
for (int i = 0; i < EntryCount; i++) {
if (overrides [i] != null) // managed override
vtEntryPtr = Marshal.GetFunctionPointerForDelegate (overrides [i]);
else
vtEntryPtr = IntPtr.Zero;
Marshal.WriteIntPtr (vtPtr, offset, vtEntryPtr);
offset += EntrySize;
}
} catch {
Marshal.FreeHGlobal (vtPtr);
throw;
}
}
public virtual void InitInstance (IntPtr instance)
{
if (basePtr == IntPtr.Zero) {
basePtr = Marshal.ReadIntPtr (instance);
int offset = 0;
for (int i = 0; i < EntryCount; i++) {
IntPtr vtEntryPtr = Marshal.ReadIntPtr (vtPtr, offset);
if (vtEntryPtr == IntPtr.Zero)
Marshal.WriteIntPtr (vtPtr, offset, Marshal.ReadIntPtr (basePtr, offset));
offset += EntrySize;
}
}
Marshal.WriteIntPtr (instance, vtPtr);
}
public virtual void ResetInstance (IntPtr instance)
{
Marshal.WriteIntPtr (instance, basePtr);
}
public IntPtr Pointer {
get { return vtPtr; }
}
protected virtual void Dispose (bool disposing)
{
if (vtPtr != IntPtr.Zero) {
Marshal.FreeHGlobal (vtPtr);
vtPtr = IntPtr.Zero;
}
}
// TODO: This WON'T usually be called because VTables are associated with classes
// and managed C++ class wrappers are staticly held?
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
~VTable ()
{
Dispose (false);
}
public static bool BindOverridesOnly (MemberInfo member, object obj)
{
bool result = BindAny (member, obj);
if (member.GetCustomAttributes (typeof (OverrideNativeAttribute), true).Length != 1)
return false;
return result;
}
public static bool BindAny (MemberInfo member, object obj)
{
MethodInfo imethod = (MethodInfo) obj;
MethodInfo candidate = (MethodInfo) member;
if (!candidate.Name.Equals (imethod.Name))
return false;
ParameterInfo[] invokeParams = imethod.GetParameters ();
ParameterInfo[] methodParams = candidate.GetParameters ();
if (invokeParams.Length == methodParams.Length) {
for (int i = 0; i < invokeParams.Length; i++) {
if (!invokeParams [i].ParameterType.IsAssignableFrom (methodParams [i].ParameterType))
return false;
}
} else if (invokeParams.Length == methodParams.Length + 1) {
for (int i = 1; i < invokeParams.Length; i++) {
if (!invokeParams [i].ParameterType.IsAssignableFrom (methodParams [i - 1].ParameterType))
return false;
}
} else
return false;
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
//
// Mono.VisualC.Interop.ABI.VTableCOM.cs: vtable implementation based on COM interop (reqiures mono patch)
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public class VTableCOM : VTable {
public VTableCOM (Delegate[] entries) : base(entries)
{
}
public override int EntrySize {
get { return Marshal.SizeOf (typeof (IntPtr)); }
}
public override T GetDelegateForNative<T> (IntPtr native, int index)
{
return default(T);
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
//
// Mono.VisualC.Interop.ABI.VTableManaged.cs: Managed vtable implementation
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public class VTableManaged : VTable {
public VTableManaged (Delegate[] entries) : base(entries)
{
}
public override int EntrySize {
get { return Marshal.SizeOf (typeof (IntPtr)); }
}
public override T GetDelegateForNative<T> (IntPtr native, int index)
{
IntPtr vtable = Marshal.ReadIntPtr (native);
if (vtable == vtPtr) // do not return managed overrides
vtable = basePtr;
IntPtr ftnptr = Marshal.ReadIntPtr (vtable, index * EntrySize);
if (ftnptr == IntPtr.Zero)
return null;
Delegate del = Marshal.GetDelegateForFunctionPointer (ftnptr, typeof (T));
return del as T;
}
}
/*
protected static Type GetNativeLayoutType(MethodInfo thisMethod) {
ParameterInfo[] parameters = thisMethod.GetParameters();
if (parameters.Length < 1) return null;
Type nativeLayoutType = parameters[0].ParameterType.GetElementType();
return nativeLayoutType;
}
*/
}

Просмотреть файл

@ -0,0 +1,34 @@
// AssemblyInfo.cs created with MonoDevelop
// User: alex at 21:36 03/13/2009
//
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("Mono.VisualC.Interop")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]
[assembly: CLSCompliant(true)]
// TODO: This will not work if we ever support saving these assemblies
[assembly: InternalsVisibleTo("__CPPLibraryImplAssembly")]

Просмотреть файл

@ -0,0 +1,27 @@
//
// Mono.VisualC.Interop.Attributes.cs
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Reflection;
namespace Mono.VisualC.Interop {
[AttributeUsage (AttributeTargets.Method)]
public class VirtualAttribute : Attribute {}
[AttributeUsage (AttributeTargets.Method)]
public class OverrideNativeAttribute : Attribute {}
public static class Modifiers {
public static bool IsVirtual (MethodInfo method)
{
return method.IsDefined (typeof (VirtualAttribute), false);
}
}
}

Просмотреть файл

@ -0,0 +1,51 @@
//
// Mono.VisualC.Interop.CppField.cs: Represents a field in a native C++ object
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop
{
public class CppField<T>
{
private int fieldOffset;
public CppField (int fieldOffset)
{
this.fieldOffset = fieldOffset;
}
public T this [CppInstancePtr ip] {
get {
Type retType = typeof(T);
object retVal;
if (retType.Equals(typeof(Byte)))
retVal = Marshal.ReadByte(ip.Native, fieldOffset);
else if (retType.Equals(typeof(Int16)))
retVal = Marshal.ReadInt16(ip.Native, fieldOffset);
else if (retType.Equals(typeof(Int32)))
retVal = Marshal.ReadInt32(ip.Native, fieldOffset);
else throw new NotImplementedException("Cannot read C++ fields of type " + retType.Name);
return (T)retVal;
}
set {
Type setType = typeof(T);
object setVal = value;
if (setType.Equals(typeof(Byte)))
Marshal.WriteByte(ip.Native, fieldOffset, (byte)setVal);
else if (setType.Equals(typeof(Int16)))
Marshal.WriteInt16(ip.Native, fieldOffset, (Int16)setVal);
else if (setType.Equals(typeof(Int32)))
Marshal.WriteInt32(ip.Native, fieldOffset, (Int32)setVal);
else throw new NotImplementedException("Cannot write C++ fields of type " + setType.Name);
}
}
}
}

Просмотреть файл

@ -0,0 +1,101 @@
//
// Mono.VisualC.Interop.CppInstancePtr.cs: Represents a pointer to a native C++ instance
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop {
public struct CppInstancePtr : ICppInstance {
private IntPtr ptr;
private bool manageMemory;
// Alloc a new C++ instance
internal CppInstancePtr (int nativeSize, object managedWrapper)
{
// Under the hood, we're secretly subclassing this C++ class to store a
// handle to the managed wrapper.
ptr = Marshal.AllocHGlobal (nativeSize + Marshal.SizeOf (typeof (IntPtr)));
IntPtr handlePtr = GetGCHandle (managedWrapper);
Marshal.WriteIntPtr (ptr, nativeSize, handlePtr);
manageMemory = true;
}
// Alloc a new C++ instance when there is no managed wrapper.
internal CppInstancePtr (int nativeSize)
{
ptr = Marshal.AllocHGlobal (nativeSize);
manageMemory = true;
}
// Get a CppInstancePtr for an existing C++ instance from an IntPtr
public CppInstancePtr (IntPtr native)
{
if (native == IntPtr.Zero)
throw new ArgumentOutOfRangeException ("native cannot be null pointer");
ptr = native;
manageMemory = false;
}
// Provide casts to/from IntPtr:
public static implicit operator CppInstancePtr (IntPtr native)
{
return new CppInstancePtr (native);
}
// cast from CppInstancePtr -> IntPtr is explicit because we lose information
public static explicit operator IntPtr (CppInstancePtr ip)
{
return ip.Native;
}
public IntPtr Native {
get {
if (ptr == IntPtr.Zero)
throw new ObjectDisposedException ("CppInstancePtr");
return ptr;
}
}
public bool IsManagedAlloc {
get { return manageMemory; }
}
internal static IntPtr GetGCHandle (object managedWrapper)
{
// TODO: Dispose() should probably be called at some point on this GCHandle.
GCHandle handle = GCHandle.Alloc (managedWrapper, GCHandleType.Normal);
return GCHandle.ToIntPtr (handle);
}
// WARNING! This method is not safe. DO NOT call
// if we do not KNOW that this instance is managed.
internal static T GetManaged<T> (IntPtr native, int nativeSize) where T : class
{
IntPtr handlePtr = Marshal.ReadIntPtr (native, nativeSize);
GCHandle handle = GCHandle.FromIntPtr (handlePtr);
return handle.Target as T;
}
// TODO: Free GCHandle?
public void Dispose ()
{
if (manageMemory && ptr != IntPtr.Zero)
Marshal.FreeHGlobal (ptr);
ptr = IntPtr.Zero;
manageMemory = false;
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
//
// Mono.VisualC.Interop.CppLibrary.cs: Represents a native C++ library for interop
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Reflection.Emit;
using Mono.VisualC.Interop.ABI;
namespace Mono.VisualC.Interop
{
public sealed class CppLibrary
{
private static AssemblyBuilder interopAssembly;
private static ModuleBuilder interopModule;
private CppAbi abi;
private string name;
static CppLibrary ()
{
AssemblyName assemblyName = new AssemblyName ("__CPPLibraryImplAssembly");
string moduleName = "__CPPLibraryImplModule";
interopAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly (assemblyName, AssemblyBuilderAccess.Run);
interopModule = interopAssembly.DefineDynamicModule (moduleName);
}
public CppLibrary (string name, CppAbi abi)
{
if (name == null)
throw new ArgumentNullException ("Name cannot be NULL.");
if (abi == null)
throw new ArgumentNullException ("Abi cannot be NULL.");
this.name = name;
this.abi = abi;
}
public string Name {
get { return name; }
}
public CppAbi Abi {
get { return abi; }
}
// For a class that may have fields with no virtual methods to be overridden
public Iface GetClass<Iface,NativeLayout> (string className)
where Iface : ICppInstantiatable
where NativeLayout : struct
{
return Abi.ImplementClass<Iface, NativeLayout> (interopModule, null, Name, className);
}
// For a class that may have fields and virtual methods to be overridden
public Iface GetClass<Iface,NativeLayout,Managed> (string className)
where Iface : ICppOverridable<Managed>
where NativeLayout : struct
where Managed : ICppInstance
{
return Abi.ImplementClass<Iface, NativeLayout> (interopModule, typeof (Managed), Name, className);
}
// TODO: Define a method for pure virtual classes (no NativeLayout)?
}
}

Просмотреть файл

@ -0,0 +1,31 @@
//
// Mono.VisualC.Interop.Interfaces.cs
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright (C) 2010 Alexander Corrado
//
using System;
namespace Mono.VisualC.Interop
{
public interface ICppInstance : IDisposable
{
IntPtr Native { get; }
}
public interface ICppInstantiatable
{
CppInstancePtr Alloc();
void Destruct(CppInstancePtr instance);
}
public interface ICppOverridable<T> where T : ICppInstance
{
CppInstancePtr Alloc(T managed);
void Destruct(CppInstancePtr instance);
}
}

Просмотреть файл

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<ProjectGuid>{4A864586-93C5-4DC1-8A80-F094A88506D7}</ProjectGuid>
<OutputType>Library</OutputType>
<SchemaVersion>2.0</SchemaVersion>
<RootNamespace>Mono.VisualC.Interop</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>Mono.VisualC.Interop</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>CPPInterop</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Linux|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ConsolePause>false</ConsolePause>
<AssemblyName>CPPInterop</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Mac|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Mac</OutputPath>
<WarningLevel>4</WarningLevel>
<AssemblyName>Mono.VisualC.Interop</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="CppLibrary.cs" />
<Compile Include="AssemblyInfo.cs" />
<Compile Include="ABI\CppAbi.cs" />
<Compile Include="ABI\Impl\Itanium.cs" />
<Compile Include="Interfaces.cs" />
<Compile Include="ABI\VTableManaged.cs" />
<Compile Include="Attributes.cs" />
<Compile Include="CppInstancePtr.cs" />
<Compile Include="CppField.cs" />
<Compile Include="ABI\VTableCOM.cs" />
<Compile Include="ABI\VTable.cs" />
<Compile Include="Util.cs" />
<Compile Include="ABI\MethodType.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Folder Include="ABI\" />
<Folder Include="ABI\Impl\" />
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
<Properties InternalTargetFrameworkVersion="3.5" />
</MonoDevelop>
</ProjectExtensions>
</Project>

Просмотреть файл

@ -0,0 +1,68 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace Mono.VisualC.Interop {
public static class Util {
public static MethodInfo GetMethodInfoForDelegate (Type delType)
{
return delType.GetMethod ("Invoke");
}
public static Type GetDelegateTypeForMethodInfo (ModuleBuilder mod, MethodInfo targetMethod)
{
TypeAttributes typeAttr = TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass;
TypeBuilder del = mod.DefineType (mod.Name + "_" + targetMethod.Name + "_VTdel", typeAttr, typeof(MulticastDelegate));
MethodAttributes ctorAttr = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public;
ConstructorBuilder ctor = del.DefineConstructor (ctorAttr, CallingConventions.Standard, new Type[] { typeof(object), typeof(System.IntPtr) });
ctor.SetImplementationFlags (MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
Type[] parameterTypes = GetMethodParameterTypes (targetMethod, true);
MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual;
MethodBuilder invokeMethod = del.DefineMethod ("Invoke", methodAttr, targetMethod.ReturnType, parameterTypes);
invokeMethod.SetImplementationFlags (MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
return del.CreateType ();
}
public static Delegate GetDelegateForMethodInfo (ModuleBuilder mod, MethodInfo targetMethod)
{
Type delType = GetDelegateTypeForMethodInfo (mod, targetMethod);
return Delegate.CreateDelegate (delType, targetMethod);
}
public static Type[] GetDelegateParameterTypes (Type delType)
{
MethodInfo invoke = GetMethodInfoForDelegate (delType);
if (invoke == null)
return null;
return GetMethodParameterTypes (invoke, false);
}
public static Type[] GetMethodParameterTypes (MethodInfo method, bool forPInvoke)
{
ParameterInfo[] parameters = method.GetParameters ();
Type[] parameterTypes = new Type [parameters.Length];
for (int i = 0; i < parameters.Length; i++) {
if (forPInvoke) {
if (parameters [i].ParameterType.Equals (typeof (CppInstancePtr)))
parameterTypes [i] = typeof (IntPtr);
else if (parameters [i].ParameterType.IsPointer)
parameterTypes [i] = parameters [i].ParameterType.GetElementType ().MakeByRefType ();
else
parameterTypes [i] = parameters [i].ParameterType;
} else
parameterTypes [i] = parameters [i].ParameterType;
}
return parameterTypes;
}
}
}