Skip to content

Commit 9fb50fd

Browse files
committed
Added support for MarshalAsAttribute, OutAttribute and InAttribute
1 parent c4e00c1 commit 9fb50fd

2 files changed

Lines changed: 63 additions & 62 deletions

File tree

src/DllManipulator.cs

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ public class DllManipulator : MonoBehaviour
1616
public const string DLL_PATH_PATTERN_NAME_MACRO = "{name}";
1717
public const string DLL_PATH_PATTERN_ASSETS_MACRO = "{assets}";
1818
public const string DLL_PATH_PATTERN_PROJECT_MACRO = "{proj}";
19-
private static readonly Type[] DELEGATE_CTOR_PARAMETERS = new[] { typeof(object), typeof(IntPtr) };
20-
private static readonly Type[] UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS = new[] { typeof(CallingConvention) };
19+
public static readonly Type[] SUPPORTED_PARAMATER_ATTRIBUTES = { typeof(MarshalAsAttribute), typeof(InAttribute), typeof(OutAttribute) };
20+
private static readonly Type[] DELEGATE_CTOR_PARAMETERS = { typeof(object), typeof(IntPtr) };
21+
private static readonly Type[] UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS = { typeof(CallingConvention) };
22+
private static readonly Type[] MARSHAL_AS_ATTRIBUTE_CTOR_PARAMETERS = { typeof(UnmanagedType) };
2123

2224
public DllManipulatorOptions Options = new DllManipulatorOptions()
2325
{
@@ -262,10 +264,10 @@ private static DynamicMethod CreateNewNativeFunctionMock(MethodInfo nativeMethod
262264
var targetDelegateInvokeMethod = nativeFunction.delegateType.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public);
263265

264266
var mockedDynamicMethod = new DynamicMethod(dllName + ":::" + nativeFunctionSymbol, nativeMethod.ReturnType, parametersTypes, typeof(DllManipulator));
265-
mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, nativeMethod.ReturnParameter.Name);
267+
mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, null);
266268
for (int i = 0; i < parameters.Length; i++)
267269
{
268-
mockedDynamicMethod.DefineParameter(parameters[i].Position, parameters[i].Attributes, parameters[i].Name);
270+
mockedDynamicMethod.DefineParameter(i + 1, parameters[i].Attributes, null);
269271
}
270272

271273
if (_nativeFunctionsField == null)
@@ -333,7 +335,7 @@ private static void AddNativeFunction(NativeFunction nativeFunction)
333335
_nativeFunctions[_nativeFunctionsCount++] = nativeFunction;
334336
}
335337

336-
private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionSignature funcionSignature)
338+
private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionSignature functionSignature)
337339
{
338340
if (_customDelegateTypesModule == null)
339341
{
@@ -347,31 +349,81 @@ private static Type CreateDelegateTypeForNativeFunctionSignature(NativeFunctionS
347349

348350
//ufp = UnmanagedFunctionPointer
349351
var ufpAttrType = typeof(UnmanagedFunctionPointerAttribute);
350-
var ufpAttrCtor = ufpAttrType.GetConstructor(UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS);
351-
var ufpAttrCtorArgValues = new object[] { funcionSignature.callingConvention };
352-
var ufpAttrNamedFields = new [] {
352+
var ufpAttrCtor = ufpAttrType.GetConstructor(UNMANAGED_FUNCTION_POINTER_ATTRIBUTE_CTOR_PARAMETERS);
353+
object[] ufpAttrCtorArgValues = { functionSignature.callingConvention };
354+
FieldInfo[] ufpAttrNamedFields = {
353355
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.BestFitMapping), BindingFlags.Public | BindingFlags.Instance),
354356
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.CharSet), BindingFlags.Public | BindingFlags.Instance),
355357
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.SetLastError), BindingFlags.Public | BindingFlags.Instance),
356358
ufpAttrType.GetField(nameof(UnmanagedFunctionPointerAttribute.ThrowOnUnmappableChar), BindingFlags.Public | BindingFlags.Instance),
357359
};
358-
var ufpAttrFieldValues = new object[] { funcionSignature.bestFitMapping, funcionSignature.charSet, funcionSignature.setLastError, funcionSignature.throwOnUnmappableChar };
360+
object[] ufpAttrFieldValues = { functionSignature.bestFitMapping, functionSignature.charSet, functionSignature.setLastError, functionSignature.throwOnUnmappableChar };
359361
var ufpAttrBuilder = new CustomAttributeBuilder(ufpAttrCtor, ufpAttrCtorArgValues, ufpAttrNamedFields, ufpAttrFieldValues);
360362
delBuilder.SetCustomAttribute(ufpAttrBuilder);
361363

362-
363364
var ctorBuilder = delBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
364365
CallingConventions.Standard, DELEGATE_CTOR_PARAMETERS);
365366
ctorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
366367

367368
var invokeBuilder = delBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot,
368-
CallingConventions.Standard | CallingConventions.HasThis, funcionSignature.returnParameterType, funcionSignature.parameterTypes);
369+
CallingConventions.Standard | CallingConventions.HasThis, functionSignature.returnParameter.type, functionSignature.parameters.Select(p => p.type).ToArray());
369370
invokeBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
371+
var invokeReturnParam = invokeBuilder.DefineParameter(0, functionSignature.returnParameter.parameterAttributes, null);
372+
foreach (var attr in functionSignature.returnParameter.customAttributes)
373+
{
374+
invokeReturnParam.SetCustomAttribute(GetAttributeBuilderFromAttributeInstance(attr));
375+
}
376+
for (int i = 0; i < functionSignature.parameters.Length; i++)
377+
{
378+
var param = functionSignature.parameters[i];
379+
var paramBuilder = invokeBuilder.DefineParameter(i + 1, param.parameterAttributes, null);
380+
foreach(var attr in param.customAttributes)
381+
{
382+
paramBuilder.SetCustomAttribute(GetAttributeBuilderFromAttributeInstance(attr)); //Throws exception. See https://github.com/mono/mono/issues/12747
383+
}
384+
}
370385

371386
_createdDelegateTypes++;
372387
return delBuilder.CreateType();
373388
}
374389

390+
private static CustomAttributeBuilder GetAttributeBuilderFromAttributeInstance(Attribute attribute)
391+
{
392+
var attrType = attribute.GetType();
393+
switch (attribute)
394+
{
395+
case MarshalAsAttribute marshalAsAttribute:
396+
{
397+
var ctor = attrType.GetConstructor(MARSHAL_AS_ATTRIBUTE_CTOR_PARAMETERS);
398+
object[] ctorArgs = { marshalAsAttribute.Value };
399+
var fieldArguments = new List<FieldInfo>();
400+
var fieldArgumentValues = new List<object>();
401+
foreach (var field in attrType.GetFields(BindingFlags.Public | BindingFlags.Instance))
402+
{
403+
fieldArguments.Add(field);
404+
fieldArgumentValues.Add(field.GetValue(attribute));
405+
}
406+
407+
//MarshalAsAttribute has no properties other than Value, which is passed in constructor
408+
return new CustomAttributeBuilder(ctor, ctorArgs, Array.Empty<PropertyInfo>(), Array.Empty<object>(),
409+
fieldArguments.ToArray(), fieldArgumentValues.ToArray());
410+
}
411+
case InAttribute _:
412+
{
413+
var ctor = attrType.GetConstructor(Type.EmptyTypes);
414+
return new CustomAttributeBuilder(ctor, Array.Empty<object>(), Array.Empty<PropertyInfo>(), Array.Empty<object>(),
415+
Array.Empty<FieldInfo>(), Array.Empty<object>());
416+
}
417+
case OutAttribute _:
418+
{
419+
var ctor = attrType.GetConstructor(Type.EmptyTypes);
420+
return new CustomAttributeBuilder(ctor, Array.Empty<object>(), Array.Empty<PropertyInfo>(), Array.Empty<object>(),
421+
Array.Empty<FieldInfo>(), Array.Empty<object>());
422+
}
423+
default:
424+
throw new NotImplementedException($"Attribute {attrType} is not supported");
425+
}
426+
}
375427

376428
private static string GetDllPath(string dllName)
377429
{

src/NativeFunction.cs

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Reflection;
5-
using System.Runtime.InteropServices;
62

73
namespace DllManipulator.Internal
84
{
@@ -50,51 +46,4 @@ public NativeFunction(NativeFunctionIdentity identity, NativeDll containingDll)
5046
this.containingDll = containingDll;
5147
}
5248
}
53-
54-
internal class NativeFunctionSignature
55-
{
56-
public readonly Type returnParameterType;
57-
public readonly Type[] parameterTypes;
58-
public readonly CallingConvention callingConvention;
59-
public readonly bool bestFitMapping;
60-
public readonly CharSet charSet;
61-
public readonly bool setLastError;
62-
public readonly bool throwOnUnmappableChar;
63-
64-
public NativeFunctionSignature(MethodInfo methodInfo, CallingConvention callingConvention, bool bestFitMapping, CharSet charSet, bool setLastError, bool throwOnUnmappableChar)
65-
{
66-
this.returnParameterType = methodInfo.ReturnType;
67-
this.parameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
68-
this.callingConvention = callingConvention;
69-
this.bestFitMapping = bestFitMapping;
70-
this.charSet = charSet;
71-
this.setLastError = setLastError;
72-
this.throwOnUnmappableChar = throwOnUnmappableChar;
73-
}
74-
75-
public override bool Equals(object obj)
76-
{
77-
return obj is NativeFunctionSignature other &&
78-
EqualityComparer<Type>.Default.Equals(returnParameterType, other.returnParameterType) &&
79-
EqualityComparer<Type[]>.Default.Equals(parameterTypes, other.parameterTypes) &&
80-
callingConvention == other.callingConvention &&
81-
bestFitMapping == other.bestFitMapping &&
82-
charSet == other.charSet &&
83-
setLastError == other.setLastError &&
84-
throwOnUnmappableChar == other.throwOnUnmappableChar;
85-
}
86-
87-
public override int GetHashCode()
88-
{
89-
var hashCode = 763644728;
90-
hashCode = hashCode * -1521134295 + EqualityComparer<Type>.Default.GetHashCode(returnParameterType);
91-
hashCode = hashCode * -1521134295 + EqualityComparer<Type[]>.Default.GetHashCode(parameterTypes);
92-
hashCode = hashCode * -1521134295 + callingConvention.GetHashCode();
93-
hashCode = hashCode * -1521134295 + bestFitMapping.GetHashCode();
94-
hashCode = hashCode * -1521134295 + charSet.GetHashCode();
95-
hashCode = hashCode * -1521134295 + setLastError.GetHashCode();
96-
hashCode = hashCode * -1521134295 + throwOnUnmappableChar.GetHashCode();
97-
return hashCode;
98-
}
99-
}
10049
}

0 commit comments

Comments
 (0)