mirror of https://github.com/mono/CppSharp.git
Browse Source
git-svn-id: https://mono-soc-2010.googlecode.com/svn/trunk/cppinterop@7 a470b8cb-0e6f-1642-1b45-71e107334c4bpull/1/head
22 changed files with 1715 additions and 0 deletions
@ -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 |
||||||
@ -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> |
||||||
@ -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); |
||||||
|
} |
||||||
@ -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> |
||||||
@ -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,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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue