From f41e1fd2a997cb8cb703b26e51f8be3df8d27369 Mon Sep 17 00:00:00 2001 From: rdipardo <59004801+rdipardo@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:02:45 -0500 Subject: [PATCH] Make `ClikeStringArray` memory-safe for wide strings Cf. https://github.com/notepad-plus-plus/notepad-plus-plus/issues/15997#issuecomment-2566862521 --- .../Kbg.NppPluginNET/ClikeStringArray.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/Npp.DotNet.Plugin/Kbg.NppPluginNET/ClikeStringArray.cs b/lib/Npp.DotNet.Plugin/Kbg.NppPluginNET/ClikeStringArray.cs index b6d8a8e..863f406 100644 --- a/lib/Npp.DotNet.Plugin/Kbg.NppPluginNET/ClikeStringArray.cs +++ b/lib/Npp.DotNet.Plugin/Kbg.NppPluginNET/ClikeStringArray.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Text; namespace Npp.DotNet.Plugin { @@ -16,29 +17,47 @@ public class ClikeStringArray : IDisposable internal List _nativeItems; bool _disposed = false; - public ClikeStringArray(int num, int stringCapacity) + /// + /// Creates a new with capacity for strings. + /// + /// The number of strings in this . + /// + /// The number of wide characters in each string, not counting the NULL terminator. + /// The memory size of each string in bytes will be equal to stringLength x sizeof(wchar_t), + /// or simply stringLength x 2 on Unicode-aware versions of Windows. + /// + /// + /// should be no greater than , less 1 for the NULL terminator. + /// The constructor will truncate any larger values. + /// + public ClikeStringArray(int num, int stringLength) { _nativeArray = Marshal.AllocHGlobal((num + 1) * IntPtr.Size); _nativeItems = new List(); for (int i = 0; i < num; i++) { - IntPtr item = Marshal.AllocHGlobal(stringCapacity); + int cbSize = Math.Min(stringLength + 1, Win32.MAX_PATH - 1) * Marshal.SystemDefaultCharSize; + IntPtr item = Marshal.AllocHGlobal(cbSize); Marshal.WriteIntPtr(_nativeArray + (i * IntPtr.Size), item); _nativeItems.Add(item); } Marshal.WriteIntPtr(_nativeArray + (num * IntPtr.Size), IntPtr.Zero); } - public ClikeStringArray(List lstStrings) + + /// + /// Creates a new from the given . + /// + /// A of managed .NET strings. + /// + /// Each string in should be no greater than , less 1 for the NULL terminator. + /// + public ClikeStringArray(List stringList) : this(stringList.Count, Win32.MAX_PATH - 1) { - _nativeArray = Marshal.AllocHGlobal((lstStrings.Count + 1) * IntPtr.Size); - _nativeItems = new List(); - for (int i = 0; i < lstStrings.Count; i++) + for (int i = 0; i < stringList.Count; i++) { - IntPtr item = Marshal.StringToHGlobalUni(lstStrings[i]); - Marshal.WriteIntPtr(_nativeArray + (i * IntPtr.Size), item); - _nativeItems.Add(item); + byte[] bytes = Encoding.Unicode.GetBytes(stringList[i]); + Marshal.Copy(bytes, 0, _nativeItems[i], bytes.Length); } - Marshal.WriteIntPtr(_nativeArray + (lstStrings.Count * IntPtr.Size), IntPtr.Zero); } public IntPtr NativePointer { get { return _nativeArray; } }