diff --git a/HLab.Sys.Windows.API/WinReg.cs b/HLab.Sys.Windows.API/WinReg.cs
index f1e4570..d269bb7 100644
--- a/HLab.Sys.Windows.API/WinReg.cs
+++ b/HLab.Sys.Windows.API/WinReg.cs
@@ -22,6 +22,7 @@ You should have received a copy of the GNU General Public License
*/
using System;
+using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
@@ -58,24 +59,37 @@ public static RegistryKey RegistryKey(string path, int parent = 0)
public static string GetHKeyName(nint hKey)
{
- var result = string.Empty;
-
var status = Wdm.ZwQueryKey(hKey, Wdm.KeyInformationClass.KeyNameInformation, 0, 0, out var needed);
- if (status != 0xC0000023) return result;
+ if (status != 0xC0000023 /* STATUS_BUFFER_TOO_SMALL */ || needed < sizeof(uint)) return string.Empty;
- var pKni = Marshal.AllocHGlobal(cb: sizeof(uint) + needed + 4 /*paranoia*/);
- status = Wdm.ZwQueryKey(hKey, Wdm.KeyInformationClass.KeyNameInformation, pKni, needed, out needed);
- if (status == 0) // STATUS_SUCCESS
+ var buffer = Marshal.AllocHGlobal(needed);
+ try
+ {
+ var capacity = needed;
+ status = Wdm.ZwQueryKey(hKey, Wdm.KeyInformationClass.KeyNameInformation, buffer, capacity, out var returned);
+ if (status != 0 /* STATUS_SUCCESS */ || returned < sizeof(uint) || returned > capacity)
+ return string.Empty;
+
+ var bytes = new byte[returned];
+ Marshal.Copy(buffer, bytes, 0, returned);
+ return DecodeKeyNameInformation(bytes);
+ }
+ finally
{
- var bytes = new char[2 + needed + 2];
- Marshal.Copy(pKni, bytes, 0, needed);
- // startIndex == 2 skips the NameLength field of the structure (2 chars == 4 bytes)
- // needed/2 reduces value from bytes to chars
- // needed/2 - 2 reduces length to not include the NameLength
- result = new string(bytes, 2, (needed / 2) - 2);
+ Marshal.FreeHGlobal(buffer);
}
- Marshal.FreeHGlobal(pKni);
- return result;
+ }
+
+ /// Decode byte-counted KEY_NAME_INFORMATION without unmanaged over-read.
+ public static string DecodeKeyNameInformation(ReadOnlySpan buffer)
+ {
+ if (buffer.Length < sizeof(uint)) return string.Empty;
+ var nameLength = BitConverter.ToUInt32(buffer[..sizeof(uint)]);
+ if ((nameLength & 1) != 0 || nameLength > buffer.Length - sizeof(uint))
+ throw new InvalidDataException("Invalid KEY_NAME_INFORMATION byte length.");
+
+ return Encoding.Unicode.GetString(
+ buffer.Slice(sizeof(uint), checked((int)nameLength)));
}