diff --git a/Source/OpenTK.Rewrite/Program.cs b/Source/OpenTK.Rewrite/Program.cs index bae00913..c5e9e06a 100644 --- a/Source/OpenTK.Rewrite/Program.cs +++ b/Source/OpenTK.Rewrite/Program.cs @@ -47,6 +47,7 @@ namespace OpenTK.Rewrite // mscorlib types static AssemblyDefinition mscorlib; static TypeDefinition TypeMarshal; + static TypeDefinition TypeStringArray; static TypeDefinition TypeStringBuilder; static TypeDefinition TypeVoid; static TypeDefinition TypeIntPtr; @@ -111,6 +112,7 @@ namespace OpenTK.Rewrite return; } TypeMarshal = mscorlib.MainModule.GetType("System.Runtime.InteropServices.Marshal"); + TypeStringArray = mscorlib.MainModule.GetType("System.String").MakeArrayType().Resolve(); TypeStringBuilder = mscorlib.MainModule.GetType("System.Text.StringBuilder"); TypeVoid = mscorlib.MainModule.GetType("System.Void"); TypeIntPtr = mscorlib.MainModule.GetType("System.IntPtr"); @@ -328,7 +330,7 @@ namespace OpenTK.Rewrite // try { // foo_sb_ptr = Marshal.AllocHGlobal(sb.Capacity + 1); -- already emitted // glGetShaderInfoLog(..., foo_sb_ptr); -- already emitted - // MarshalStringBuilder(foo_sb_ptr, foo); + // MarshalPtrToStringBuilder(foo_sb_ptr, foo); // } // finally { // Marshal.FreeHGlobal(foo_sb_ptr); @@ -358,6 +360,52 @@ namespace OpenTK.Rewrite } } + static void EmitStringArrayParameter(MethodDefinition wrapper, TypeReference p, MethodBody body, ILProcessor il) + { + // string[] masrhaling: + // IntPtr ptr = MarshalStringArrayToPtr(strings); + // try { + // calli + // } + // finally { + // UnmarshalStringArray(ptr); + // } + var marshal_str_array_to_ptr = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalStringArrayToPtr")); + + // IntPtr ptr; + var variable_name = p.Name + " _string_array_ptr"; + body.Variables.Add(new VariableDefinition(variable_name, TypeIntPtr)); + int index = body.Variables.Count - 1; + + // ptr = MarshalStringArrayToPtr(strings); + il.Emit(OpCodes.Call, marshal_str_array_to_ptr); + il.Emit(OpCodes.Stloc, index); + il.Emit(OpCodes.Ldloc, index); + + // The finally block will be emitted in the function epilogue + } + + static void EmitStringArrayEpilogue(MethodDefinition wrapper, MethodDefinition native, MethodBody body, ILProcessor il) + { + for (int i = 0; i < wrapper.Parameters.Count; i++) + { + var p = wrapper.Parameters[i].ParameterType; + if (p.Name == "String" && p.IsArray) + { + var free = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "FreeStringArrayPtr")); + var get_length = wrapper.Module.Import(TypeStringArray.Methods.First(m => m.Name == "get_Length")); + + // FreeStringArrayPtr(string_array_ptr, string_array.Length) + var variable_name = p.Name + "_string_array_ptr"; + var v = body.Variables.First(m => m.Name == variable_name); + il.Emit(OpCodes.Ldloc, v.Index); + il.Emit(OpCodes.Ldarg, i); + il.Emit(OpCodes.Callvirt, get_length); + il.Emit(OpCodes.Call, free); + } + } + } + private static void EmitConvenienceWrapper(MethodDefinition wrapper, MethodDefinition native, int difference, MethodBody body, ILProcessor il) { @@ -491,8 +539,7 @@ namespace OpenTK.Rewrite } else { - // String[] requires special marshalling. - // Let the runtime handle this for now. + EmitStringArrayParameter(method, p, body, il); } } } diff --git a/Source/OpenTK/BindingsBase.cs b/Source/OpenTK/BindingsBase.cs index 1f2372e1..36d421fe 100644 --- a/Source/OpenTK/BindingsBase.cs +++ b/Source/OpenTK/BindingsBase.cs @@ -119,13 +119,13 @@ namespace OpenTK /// unique objects, but all instances of ES10.GL should return the same object. protected abstract object SyncRoot { get; } - /// + /// /// Marshals a pointer to a null-terminated byte array to the specified StringBuilder. /// This method supports OpenTK and is not intended to be called by user code. - /// + /// /// A pointer to a null-terminated byte array. /// The StringBuilder to receive the contents of the pointer. - protected static void MarshalPtrToStringBuilder(IntPtr ptr, StringBuilder sb) + protected static void MarshalPtrToStringBuilder(IntPtr ptr, StringBuilder sb) { if (ptr == IntPtr.Zero) throw new ArgumentException("ptr"); @@ -144,6 +144,52 @@ namespace OpenTK } } + /// + /// Marshals a string array to unmanaged memory by calling + /// Marshal.AllocHGlobal for each element. + /// + /// An unmanaged pointer to an array of null-terminated strings + /// The string array to marshal. + protected static IntPtr MarshalStringArrayToPtr(string[] str_array) + { + IntPtr ptr = IntPtr.Zero; + if (str_array != null && str_array.Length != 0) + { + ptr = Marshal.AllocHGlobal(str_array.Length * IntPtr.Size); + if (ptr == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + + for (int i = 0; i < str_array.Length; i++) + { + IntPtr str = Marshal.StringToHGlobalAnsi(str_array[i]); + if (str == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + + Marshal.WriteIntPtr(ptr, i * IntPtr.Size, str); + } + } + return ptr; + } + + /// + /// Frees a string array that has previously been + /// marshalled by MarshalStringArrayToPtr. + /// + /// An unmanaged pointer allocated by MarshalStringArrayToPtr + /// The length of the string array. + protected static void FreeStringArrayPtr(IntPtr ptr, int length) + { + for (int i = 0; i < length; i++) + { + Marshal.FreeHGlobal(Marshal.ReadIntPtr(ptr, length * IntPtr.Size)); + } + Marshal.FreeHGlobal(ptr); + } + #endregion #region Internal Members