From 7afe48c9798aa3e5eb5124ba0a978fdd81985d79 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 12 Jan 2014 22:05:15 +0100 Subject: [PATCH] [Audio] Don't crash when Alc.GetString() returns null MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Alc.GetString() could crash if the unmanaged code returned null due to any kind of failure. This is now fixed and better documented. Additionally, the array overload for Alc.GetString() will now correctly forward the ‘device’ parameter to unmanaged code. --- Source/OpenTK/Audio/OpenAL/Alc/Alc.cs | 67 ++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs b/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs index 38767a1..371bd81 100644 --- a/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs +++ b/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Security; @@ -262,7 +263,13 @@ namespace OpenTK.Audio.OpenAL /// A string containing the name of the Device. public static string GetString(IntPtr device, AlcGetString param) { - return Marshal.PtrToStringAnsi(GetStringPrivate(device, param)); + IntPtr pstr = GetStringPrivate(device, param); + string str = String.Empty; + if (pstr != IntPtr.Zero) + { + str = Marshal.PtrToStringAnsi(pstr); + } + return str; } /// This function returns a List of strings related to the context. @@ -277,26 +284,54 @@ namespace OpenTK.Audio.OpenAL public static IList GetString(IntPtr device, AlcGetStringList param) { List result = new List(); - IntPtr t = GetStringPrivate(IntPtr.Zero, (AlcGetString)param); - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - byte b; - int offset = 0; - do + + // We cannot use Marshal.PtrToStringAnsi(), + // because alcGetString is defined to return either a nul-terminated string, + // or an array of nul-terminated strings terminated by an extra nul. + // Marshal.PtrToStringAnsi() will fail in the latter case (it will only + // return the very first string in the array.) + // We'll have to marshal this ourselves. + IntPtr t = GetStringPrivate(device, (AlcGetString)param); + if (t != IntPtr.Zero) { - b = Marshal.ReadByte(t, offset++); - if (b != 0) - sb.Append((char)b); - if (b == 0) + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + byte b; + int offset = 0; + do { - result.Add(sb.ToString()); - if (Marshal.ReadByte(t, offset) == 0) // offset already properly increased through ++ - break; // 2x null + b = Marshal.ReadByte(t, offset++); + if (b != 0) + { + sb.Append((char)b); + } else - sb.Remove(0, sb.Length); // 1x null + { + // One string from the array is complete + result.Add(sb.ToString()); + + // Check whether the array has finished + // Note: offset already been increased through offset++ above + if (Marshal.ReadByte(t, offset) == 0) + { + // 2x consecutive nuls, we've read the whole array + break; + } + else + { + // Another string is starting, clear the StringBuilder + sb.Remove(0, sb.Length); + } + } } - } while (true); + while (true); + } + else + { + Debug.Print("[Audio] Alc.GetString({0}, {1}) returned null.", + device, param); + } - return (IList)result; + return result; } [DllImport(Alc.Lib, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = Alc.Style, CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity()] -- 2.7.4