Tools and libraries to glue C/C++ APIs to high-level languages
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

170 lines
5.3 KiB

//
// Mono.VisualC.Interop.CppInstancePtr.cs: Represents a pointer to a native C++ instance
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
// Andreia Gaita (shana@spoiledcat.net)
//
// Copyright (C) 2010 Alexander Corrado
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Mono.VisualC.Interop.ABI;
namespace Mono.VisualC.Interop {
public struct CppInstancePtr : ICppObject {
private IntPtr ptr;
internal IntPtr native_vtptr;
private bool manage_memory;
private static Dictionary<Type,object> implCache = null;
// TODO: the managed instance argument may only be NULL if all methods in TWrapper
// that correspond to the virtual methods in Iface are static.
public static CppInstancePtr ForManagedObject<Iface,TWrapper> (TWrapper managed)
where Iface : ICppClassOverridable<TWrapper>
{
object cachedImpl;
Iface impl;
if (implCache == null)
implCache = new Dictionary<Type,object> ();
if (!implCache.TryGetValue (typeof (Iface), out cachedImpl))
{
VirtualOnlyAbi virtualABI = new VirtualOnlyAbi (VTable.BindToSignature);
impl = virtualABI.ImplementClass<Iface> (typeof (TWrapper), new CppLibrary (string.Empty), string.Empty);
implCache.Add (typeof (Iface), impl);
}
else
impl = (Iface)cachedImpl;
CppInstancePtr instance = impl.Alloc (managed);
impl.TypeInfo.VTable.InitInstance (ref instance);
return instance;
}
// Alloc a new C++ instance
// NOTE: native_vtptr will be set later after native ctor is called
internal CppInstancePtr (int nativeSize, object managedWrapper)
{
// Under the hood, we're secretly subclassing this C++ class to store a
// handle to the managed wrapper.
int allocSize = nativeSize + Marshal.SizeOf (typeof (IntPtr));
ptr = Marshal.AllocHGlobal (allocSize);
// zero memory for sanity
// FIXME: This should be an initblk
byte[] zeroArray = new byte [allocSize];
Marshal.Copy (zeroArray, 0, ptr, allocSize);
IntPtr handlePtr = MakeGCHandle (managedWrapper);
Marshal.WriteIntPtr (ptr, nativeSize, handlePtr);
manage_memory = true;
}
// Alloc a new C++ instance when there is no managed wrapper.
internal CppInstancePtr (int nativeSize)
{
ptr = Marshal.AllocHGlobal (nativeSize);
manage_memory = 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");
// Kludge! CppInstancePtr doesn't know whether this class is virtual or not, but we'll just assume that either
// way it's at least sizeof(void*) and read what would be the vtptr anyway. Supposedly, if it's not virtual,
// the wrappers won't use this field anyway...
native_vtptr = Marshal.ReadIntPtr (native);
ptr = native;
manage_memory = 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;
}
}
CppInstancePtr ICppObject.Native {
get { return this; }
}
public bool IsManagedAlloc {
get { return manage_memory; }
}
internal static IntPtr MakeGCHandle (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 (manage_memory && ptr != IntPtr.Zero)
Marshal.FreeHGlobal (ptr);
ptr = IntPtr.Zero;
manage_memory = false;
}
}
}