c3c7b48fa9
This patch includes all marshalling and proxy handling code on the .NET side as well as the native side needed for a fully functional UNO bridge. It also includes some changes and corrections to net_basetypes and netmaker needed for the bridge to work properly. It also includes the FirstUnoContact example in C# as demonstration. Change-Id: I406932938a4415d24408fb41ddfa7d8eeb5d1f94 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170916 Tested-by: Jenkins Reviewed-by: Hossein <hossein@libreoffice.org>
227 lines
9 KiB
C#
227 lines
9 KiB
C#
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
|
|
using com.sun.star.uno.helper;
|
|
|
|
namespace com.sun.star.uno.native
|
|
{
|
|
internal class NetEnvironment
|
|
{
|
|
public InteropContext Context { get; }
|
|
|
|
private readonly Marshaller _marshaller;
|
|
private readonly WeakIndexTable _indices;
|
|
private readonly WeakOidTypeTable _locals;
|
|
private readonly WeakOidTypeTable _proxies;
|
|
private readonly string _oidSuffix;
|
|
private readonly Dictionary<int, string> _oids;
|
|
|
|
public NetEnvironment()
|
|
{
|
|
Context = new InteropContext()
|
|
{
|
|
createProxy = CreateProxy,
|
|
lookupObjectId = LookupOid,
|
|
registerInterface = RegisterInterface,
|
|
lookupInterface = LookupInterface,
|
|
revokeInterface = RevokeInterface,
|
|
dispatchCall = DispatchCall,
|
|
throwError = ThrowError,
|
|
};
|
|
|
|
_marshaller = new Marshaller(this);
|
|
_indices = new WeakIndexTable();
|
|
_locals = new WeakOidTypeTable();
|
|
_proxies = new WeakOidTypeTable();
|
|
_oidSuffix = $";net[0];{Guid.NewGuid()}";
|
|
_oids = new Dictionary<int, string>();
|
|
}
|
|
|
|
public IntPtr CreateProxy(string oid, string interfaceName,
|
|
IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc)
|
|
{
|
|
Type interfaceType = TypeHelper.ParseType(interfaceName);
|
|
object proxy = NativeUnoProxy.CreateProxy(oid, interfaceType,
|
|
pBridge, pUnoInterface, pTypeDesc, this);
|
|
int index = _indices.Register(proxy);
|
|
_oids.Add(index, oid);
|
|
_proxies.RegisterInterface(proxy, oid, interfaceType);
|
|
return (IntPtr)index;
|
|
}
|
|
|
|
public string LookupOid(IntPtr pObject) => _oids[(int)pObject];
|
|
|
|
public IntPtr RegisterInterface(IntPtr pObject, string pOid, string pInterfaceName)
|
|
{
|
|
Type interfaceType = TypeHelper.ParseType(pInterfaceName);
|
|
object obj = _indices.Lookup((int)pObject);
|
|
obj = (obj is NativeUnoProxy ? _proxies : _locals)
|
|
.RegisterInterface(obj, pOid, interfaceType);
|
|
return (IntPtr)_indices.Register(obj);
|
|
}
|
|
|
|
public IntPtr RegisterLocal(object obj, Type interfaceType)
|
|
{
|
|
int index = _indices.Register(obj);
|
|
if (!_oids.ContainsKey(index))
|
|
{
|
|
string oid = $"{index}{_oidSuffix}";
|
|
_oids.Add(index, oid);
|
|
}
|
|
_locals.RegisterInterface(obj, _oids[index], interfaceType);
|
|
return (IntPtr)index;
|
|
}
|
|
|
|
public IntPtr LookupInterface(string pOid, string pInterfaceName)
|
|
{
|
|
Type interfaceType = TypeHelper.ParseType(pInterfaceName);
|
|
return LookupInterface(pOid, interfaceType);
|
|
}
|
|
public IntPtr LookupInterface(string pOid, Type type)
|
|
{
|
|
object obj = _proxies.GetInterface(pOid, type)
|
|
?? _locals.GetInterface(pOid, type);
|
|
return (IntPtr)_indices.Register(obj);
|
|
}
|
|
|
|
public void RevokeInterface(string pOid, string pInterfaceName)
|
|
{
|
|
Type interfaceType = TypeHelper.ParseType(pInterfaceName);
|
|
if (!_proxies.RevokeInterface(pOid, interfaceType))
|
|
_locals.RevokeInterface(pOid, interfaceType);
|
|
}
|
|
|
|
public void ThrowError(string pWhere, string pMessage)
|
|
{
|
|
throw new RuntimeException($"{pMessage} at {pWhere}", null);
|
|
}
|
|
|
|
public object GetRegisteredObject(IntPtr pIndex)
|
|
{
|
|
return _indices.Lookup((int)pIndex);
|
|
}
|
|
|
|
public unsafe void ReleaseProxy(NativeUnoProxy nup,
|
|
IntPtr pBridge, IntPtr pUnoInterface, IntPtr pTypeDesc)
|
|
{
|
|
_oids.Remove(_indices.Register(nup));
|
|
_proxies.RevokeInterface(nup.Oid, nup.Type);
|
|
InteropMethods.releaseProxy(pBridge, pUnoInterface, pTypeDesc);
|
|
}
|
|
|
|
public unsafe object InvokeMethod(MethodInfo targetMethod, object[] args,
|
|
IntPtr bridgeHandle, IntPtr interfaceHandle, IntPtr typeHandle)
|
|
{
|
|
ParameterInfo[] parameters = targetMethod.GetParameters();
|
|
|
|
InteropValue* values = stackalloc InteropValue[parameters.Length + 2];
|
|
InteropValue* retVal = &values[parameters.Length];
|
|
InteropValue* excVal = &values[parameters.Length + 1];
|
|
|
|
for (int i = 0; i < parameters.Length; i++)
|
|
{
|
|
ParameterInfo param = parameters[i];
|
|
Type paramType = TypeHelper.RemoveReference(param.ParameterType);
|
|
bool isInOrInOut = !(param.ParameterType.IsByRef && param.IsOut);
|
|
if (isInOrInOut)
|
|
_marshaller.MarshalObject(paramType, args[i], &values[i], false);
|
|
}
|
|
|
|
bool error = InteropMethods.dispatchCall(
|
|
bridgeHandle, interfaceHandle, typeHandle,
|
|
targetMethod.Name, values, retVal, excVal) == 0;
|
|
|
|
for (int i = 0; i < parameters.Length; i++)
|
|
{
|
|
ParameterInfo param = parameters[i];
|
|
Type paramType = TypeHelper.RemoveReference(param.ParameterType);
|
|
bool isInOutOrOut = param.ParameterType.IsByRef;
|
|
if (isInOutOrOut)
|
|
_marshaller.UnmarshalObject(paramType, ref args[i], &values[i], true);
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
object exception = null;
|
|
_marshaller.UnmarshalObject(typeof(Any), ref exception, excVal, true);
|
|
throw (Exception)((Any)exception).Value;
|
|
}
|
|
|
|
if (targetMethod.ReturnType != typeof(void))
|
|
{
|
|
object result = null;
|
|
_marshaller.UnmarshalObject(targetMethod.ReturnType, ref result, retVal, true);
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public unsafe byte DispatchCall(IntPtr pNetInterface, string pMethodName,
|
|
IntPtr pArgs, IntPtr pRet, IntPtr pExc)
|
|
{
|
|
try
|
|
{
|
|
int methodNameStart = pMethodName.IndexOf(':') + 2;
|
|
int methodNameEnd = pMethodName.IndexOf(':', methodNameStart);
|
|
if (methodNameEnd > methodNameStart)
|
|
pMethodName = pMethodName.Substring(methodNameStart, methodNameEnd - methodNameStart);
|
|
else
|
|
pMethodName = pMethodName.Substring(methodNameStart);
|
|
|
|
object target = _indices.Lookup((int)pNetInterface);
|
|
if (target == null)
|
|
throw new RuntimeException($"{pMethodName} was called on a null or unregistered interface", null);
|
|
|
|
Type targetType = target.GetType();
|
|
MethodInfo targetMethod = targetType.GetMethod(pMethodName,
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
|
|
if (targetMethod == null)
|
|
throw new RuntimeException($"could not find method {pMethodName} on interface {target.GetType()}", null);
|
|
|
|
ParameterInfo[] parameters = targetMethod.GetParameters();
|
|
object[] args = new object[parameters.Length];
|
|
|
|
for (int i = 0; i < parameters.Length; i++)
|
|
{
|
|
ParameterInfo param = parameters[i];
|
|
Type paramType = TypeHelper.RemoveReference(param.ParameterType);
|
|
bool isInOrInOut = !(param.ParameterType.IsByRef && param.IsOut);
|
|
if (isInOrInOut)
|
|
_marshaller.UnmarshalObject(paramType, ref args[i], &((InteropValue*)(void*)pArgs)[i], true);
|
|
}
|
|
|
|
object ret = targetMethod.Invoke(target, args);
|
|
|
|
for (int i = 0; i < parameters.Length; i++)
|
|
{
|
|
ParameterInfo param = parameters[i];
|
|
Type paramType = TypeHelper.RemoveReference(param.ParameterType);
|
|
bool isInOutOrOut = param.ParameterType.IsByRef;
|
|
if (isInOutOrOut)
|
|
_marshaller.MarshalObject(paramType, args[i], &((InteropValue*)(void*)pArgs)[i], false);
|
|
}
|
|
|
|
if (targetMethod.ReturnType != typeof(void))
|
|
_marshaller.MarshalObject(targetMethod.ReturnType, ret, (InteropValue*)(void*)pRet, false);
|
|
return 1;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_marshaller.MarshalObject(typeof(Any), new Any(exception.InnerException),
|
|
(InteropValue*)(void*)pExc, false);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|