Skip to content

Commit

Permalink
Add some registry functionality
Browse files Browse the repository at this point in the history
Adds a Registry class for reading the registry.

Also adds the start of a `SpanReader`.

Change the root namespace of the test assembly.
  • Loading branch information
JeremyKuhne committed Sep 28, 2023
1 parent 1df1f32 commit 30206b9
Show file tree
Hide file tree
Showing 38 changed files with 590 additions and 108 deletions.
11 changes: 11 additions & 0 deletions src/thirtytwo/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ GlobalLock
GlobalSize
GlobalUnlock
HFONT
HKEY_*
HRESULT
IAccessible
IAccessibleEx
Expand Down Expand Up @@ -178,6 +179,8 @@ IsWindowEnabled
IsWindowVisible
IUIAutomationElement
IUnknown
KEY_INFORMATION_CLASS
KEY_NAME_INFORMATION
KF_*
LIBFLAGS
LoadCursor
Expand Down Expand Up @@ -215,6 +218,11 @@ PROPVARIANT
PropVariantClear
RegisterClassEx
RegisterClipboardFormat
RegCloseKey
RegEnumValue
RegOpenKeyEx
RegQueryInfoKey
RegQueryValueEx
ReleaseDC
ROLE_SYSTEM_*
S_FALSE
Expand Down Expand Up @@ -246,7 +254,10 @@ SHParseDisplayName
ShowWindow
STATE_SYSTEM_*
STATIC_STYLES
STATUS_BUFFER_OVERFLOW
STATUS_BUFFER_TOO_SMALL
STATUS_INFO_LENGTH_MISMATCH
STATUS_SUCCESS
SubtractRect
SYS_COLOR_INDEX
SYSTEM_PROCESS_INFORMATION
Expand Down
8 changes: 8 additions & 0 deletions src/thirtytwo/Support/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ public static unsafe class Error
[DoesNotReturn]
public static void Throw(this WIN32_ERROR error, string? path = null) => throw error.GetException(path);

public static void ThrowIfFailed(this WIN32_ERROR error, string? path = null)
{
if (error != WIN32_ERROR.ERROR_SUCCESS)
{
error.Throw(path);
}
}

[DoesNotReturn]
public static void ThrowLastError(string? path = null) => Throw(GetLastError(), path);

Expand Down
20 changes: 20 additions & 0 deletions src/thirtytwo/Support/SpanExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Windows.Support;

namespace System;

public static class SpanExtensions
Expand All @@ -22,4 +24,22 @@ public static Span<char> SliceAtNull(this Span<char> span)
int index = span.IndexOf('\0');
return index == -1 ? span : span[..index];
}

/// <summary>
/// Splits into strings on the given <paramref name="delimiter"/>.
/// </summary>
public static IEnumerable<string> Split(this ReadOnlySpan<char> span, char delimiter, bool includeEmptyStrings = false)
{
List<string> strings = new();
SpanReader<char> reader = new(span);
while (reader.TryReadTo(out var next, delimiter))
{
if (includeEmptyStrings || !next.IsEmpty)
{
strings.Add(next.ToString());
}
}

return strings;
}
}
46 changes: 46 additions & 0 deletions src/thirtytwo/Support/SpanReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Buffers;

namespace Windows.Support;

/// <summary>
/// Simple span reader. Follows <see cref="SequenceReader{T}"/>.
/// </summary>
public ref struct SpanReader<T> where T : unmanaged, IEquatable<T>
{
public ReadOnlySpan<T> Span { get; }
public int Index { get; private set; }

public SpanReader(ReadOnlySpan<T> span) => Span = span;

public readonly ReadOnlySpan<T> Remaining => Span[Index..];

/// <summary>
/// Try to read everything up to the given <paramref name="delimiter"/>.
/// </summary>
/// <param name="span">The read data, if any.</param>
/// <param name="delimiter">The delimiter to look for.</param>
/// <param name="advancePastDelimiter"><see langword="true"/> to move past the <paramref name="delimiter"/> if found.</param>
/// <returns><see langword="true"/> if the <paramref name="delimiter"/> was found.</returns>
public bool TryReadTo(out ReadOnlySpan<T> span, T delimiter, bool advancePastDelimiter = true)
{
bool found = false;
ReadOnlySpan<T> remaining = Remaining;
int index = remaining.IndexOf(delimiter);

if (index != -1)
{
span = index == 0 ? default : remaining[..index];
Index += index + (advancePastDelimiter ? 1 : 0);
found = true;
}
else
{
span = default;
}

return found;
}
}
6 changes: 3 additions & 3 deletions src/thirtytwo/Support/ValueBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public ValueBuffer(int initialCapacity)

public Span<T> Span { get; private set; }

public int Length => Span.Length;
public readonly int Length => Span.Length;

/// <summary>
/// Ensure that the buffer has enough space for <paramref name="capacity"/> number of elements.
Expand Down Expand Up @@ -101,9 +101,9 @@ public unsafe void EnsureCapacity(int capacity, bool copy = false)
}
}

public ref T this[int index] => ref Span[index];
public readonly ref T this[int index] => ref Span[index];

public ref T GetPinnableReference() => ref MemoryMarshal.GetReference(Span);
public readonly ref T GetPinnableReference() => ref MemoryMarshal.GetReference(Span);

public string ToStringAndDispose(int length)
{
Expand Down
39 changes: 39 additions & 0 deletions src/thirtytwo/Wdk/Interop.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.InteropServices;
using Windows.Wdk.System.SystemServices;
using Windows.Win32.System.Registry;

namespace Windows.Wdk;

#pragma warning disable SA1313 // Parameter names should begin with lower-case letter

public static partial class Interop
{
/// <summary>
/// The NtQueryKey routine provides information about the class of a registry key, and the number and sizes of its subkeys.
/// </summary>
/// <param name="Length">Specifies the size, in bytes, of the <paramref name="KeyInformation"/> buffer.</param>
/// <param name="ResultLength">
/// Pointer to a variable that receives the size, in bytes, of the requested key information. If NtQueryKey returns
/// <see cref="NTSTATUS.STATUS_SUCCESS"/>, the variable contains the amount of data returned. If NwQueryKey returns
/// <see cref="NTSTATUS.STATUS_BUFFER_OVERFLOW"/> or <see cref="NTSTATUS.STATUS_BUFFER_TOO_SMALL"/>, you can use the
/// value of the variable to determine the required buffer size.
/// </param>
/// <remarks>
/// <para>
/// <see href="https://learn.microsoft.com/windows-hardware/drivers/ddi/wdm/nf-wdm-zwquerykey">Read more on learn.microsoft.com</see>.
/// </para>
/// </remarks>
[DllImport("ntdll.dll", ExactSpelling = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
public static extern unsafe NTSTATUS NtQueryKey(
HKEY KeyHandle,
KEY_INFORMATION_CLASS KeyInformationClass,
void* KeyInformation,
uint Length,
uint* ResultLength);
}

#pragma warning restore SA1313 // Parameter names should begin with lower-case letter
35 changes: 35 additions & 0 deletions src/thirtytwo/Win32/System/Registry/HKEY.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Windows.Support;

namespace Windows.Win32.System.Registry;

public partial struct HKEY : IDisposable
{
private const uint REMOTE_HANDLE_TAG = 0x00000001;
private const uint REG_CLASSES_SPECIAL_TAG = 0x00000002;

public bool IsPerfKey()
=> this == HKEY_PERFORMANCE_DATA || this == HKEY_PERFORMANCE_NLSTEXT || this == HKEY_PERFORMANCE_TEXT;

/// <summary>
/// Returns true if the key is from the local machine.
/// </summary>
public bool IsLocalKey => (Value & REMOTE_HANDLE_TAG) == 0;

/// <summary>
/// Returns true if the key is special (notably in <see cref="HKEY.HKEY_CLASSES_ROOT"/>, where
/// it might be redirected to per user settings).
/// </summary>
public bool IsSpecialKey => (Value & REG_CLASSES_SPECIAL_TAG) != 0;

public void Dispose()
{
WIN32_ERROR error = Interop.RegCloseKey(this);
if (error != WIN32_ERROR.ERROR_SUCCESS)
{
error.Throw();
}
}
}
Loading

0 comments on commit 30206b9

Please sign in to comment.