Generator: Add Enum and Struct

Win32: IUnknown and DXGI generation.
This commit is contained in:
Amer Koleci
2022-09-01 13:57:00 +02:00
parent 18e10653c0
commit 30bd7e9336
16 changed files with 2901 additions and 18 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "src/Generator/win32json"]
path = src/Generator/win32json
url = https://github.com/marlersoft/win32json.git

64
src/Generator/ApiData.cs Normal file
View File

@@ -0,0 +1,64 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
#nullable disable
namespace Generator;
public class ApiDataArrayShape
{
public int Size { get; set; }
}
public class ApiDataType
{
public string Kind { get; set; }
public string Name { get; set; }
// Kind == Array
public ApiDataArrayShape Shape { get; set; }
public ApiDataType Child { get; set; }
}
public class ApiDataConstant
{
public string Name { get; set; }
public ApiDataType Type { get; set; }
public string ValueType { get; set; }
public object Value { get; set; }
}
public class ApiEnumValue
{
public string Name { get; set; }
public object Value { get; set; }
}
public class ApiStructField
{
public string Name { get; set; }
public ApiDataType Type { get; set; }
}
public class ApiType
{
public string Name { get; set; }
public string Kind { get; set; }
// Enum
public bool Flags { get; set; }
public bool Scoped { get; set; }
public string IntegerBase { get; set; }
public ApiEnumValue[] Values { get; set; }
// Struct
public int Size { get; set; }
public int PackingSize { get; set; }
public ApiStructField[] Fields { get; set; }
}
public sealed class ApiData
{
public ApiDataConstant[] Constants { get; set; }
public ApiType[] Types { get; set; }
}

View File

@@ -12,7 +12,7 @@ public sealed class CodeWriter : IDisposable
public int IndentLevel { get; private set; } public int IndentLevel { get; private set; }
public CodeWriter(string fileName, bool enableNullable, params string[] namespaces) public CodeWriter(string fileName, string ns, params string[] usingNamespaces)
{ {
_indentStrings = new string[10]; _indentStrings = new string[10];
for (int i = 0; i < _indentStrings.Length; i++) for (int i = 0; i < _indentStrings.Length; i++)
@@ -31,23 +31,18 @@ public sealed class CodeWriter : IDisposable
_writer.WriteLine("// ------------------------------------------------------------------------------"); _writer.WriteLine("// ------------------------------------------------------------------------------");
_writer.WriteLine(); _writer.WriteLine();
if (enableNullable) _writer.WriteLine($"using System;");
_writer.WriteLine($"using System.Runtime.CompilerServices;");
_writer.WriteLine($"using System.Diagnostics.CodeAnalysis;");
foreach (string usingNamespace in usingNamespaces)
{ {
_writer.WriteLine($"#nullable enable"); _writer.WriteLine($"using {usingNamespace};");
}
_writer.WriteLine(); _writer.WriteLine();
}
foreach (string ns in namespaces) _writer.WriteLine($"namespace {ns};");
{
_writer.WriteLine($"using {ns};");
}
if (namespaces.Length > 0)
{
_writer.WriteLine();
}
_writer.WriteLine("namespace Vortice.Vulkan;");
_writer.WriteLine(); _writer.WriteLine();
} }
@@ -79,6 +74,12 @@ public sealed class CodeWriter : IDisposable
_shouldIndent = true; _shouldIndent = true;
} }
public void WriteUndindented(string @string)
{
_writer.WriteLine(@string);
_shouldIndent = true;
}
public void BeginBlock(string content) public void BeginBlock(string content)
{ {
WriteLine(content); WriteLine(content);

View File

@@ -8,10 +8,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="vulkan/*.*"> <PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </ItemGroup>
</Content>
<Content Include="vk_video/*.*"> <ItemGroup>
<Content Include="win32json/api/*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>

View File

@@ -1,10 +1,59 @@
// Copyright © Amer Koleci and Contributors. // Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information. // Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.Xml.Linq;
using Microsoft.VisualBasic.FileIO;
using Newtonsoft.Json;
namespace Generator; namespace Generator;
public static class Program public static class Program
{ {
private static readonly string[] jsons = new[]
{
"Graphics.Dxgi.Common.json"
};
private static readonly Dictionary<string, string> s_csNameMappings = new()
{
{"Void", "void" },
{"Byte", "byte" },
{"SByte", "sbyte" },
{"Int8", "sbyte" },
{"Int16", "short" },
{"Int32", "int" },
{"Int64", "long" },
{"UInt8", "byte" },
{"UInt16", "ushort" },
{"UInt32", "uint" },
{"UInt64", "ulong" },
{"Single", "float" },
{"Double", "double" },
{ "BOOL", "Bool32" },
};
private static readonly Dictionary<string, string> s_knownTypesPrefixes = new()
{
{ "DXGI_COLOR_SPACE_TYPE", "DXGI_COLOR_SPACE" },
};
private static readonly Dictionary<string, string> s_knownEnumValueNames = new()
{
{ "DXGI_FORMAT_420_OPAQUE", "Opaque420" }
};
private static readonly HashSet<string> s_ignoredParts = new(StringComparer.OrdinalIgnoreCase)
{
"DXGI"
};
private static readonly HashSet<string> s_preserveCaps = new(StringComparer.OrdinalIgnoreCase)
{
};
public static int Main(string[] args) public static int Main(string[] args)
{ {
string outputPath = AppContext.BaseDirectory; string outputPath = AppContext.BaseDirectory;
@@ -23,6 +72,374 @@ public static class Program
Directory.CreateDirectory(outputPath); Directory.CreateDirectory(outputPath);
} }
foreach (string jsonFile in jsons)
{
string outputFolder = Path.Combine(outputPath, "Graphics");
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
string finalPath = Path.Combine(AppContext.BaseDirectory, "win32json", "api", jsonFile);
string jsonData = File.ReadAllText(finalPath);
ApiData? api = JsonConvert.DeserializeObject<ApiData>(jsonData);
Generate(api!, outputFolder);
}
return 0; return 0;
} }
private static void Generate(ApiData api, string outputPath)
{
using var writer = new CodeWriter(
Path.Combine(outputPath, "Dxgi.Common.cs"),
"Win32.Graphics.Dxgi");
GenerateConstants(writer, api);
GenerateTypes(writer, api);
}
private static void GenerateConstants(CodeWriter writer, ApiData api)
{
using (writer.PushBlock($"public static partial class Apis"))
{
foreach (var constant in api.Constants)
{
if (ShouldSkipConstant(constant))
continue;
string typeName = GetTypeName(constant.ValueType);
writer.WriteLine($"public const {typeName} {constant.Name} = {constant.Value};");
}
}
writer.WriteLine();
}
private static void GenerateTypes(CodeWriter writer, ApiData api)
{
writer.WriteLine($"#region Enums");
foreach (ApiType enumType in api.Types.Where(item => item.Kind.ToLowerInvariant() == "enum"))
{
GenerateEnum(writer, enumType);
}
writer.WriteLine($"#endregion Enums");
writer.WriteLine();
writer.WriteLine($"#region Structs");
foreach (ApiType structType in api.Types.Where(item => item.Kind.ToLowerInvariant() == "struct"))
{
GenerateStruct(writer, structType);
}
writer.WriteLine($"#endregion Structs");
writer.WriteLine();
}
private static void GenerateEnum(CodeWriter writer, ApiType enumType)
{
if (enumType.Flags)
{
writer.WriteLine("[Flags]");
}
string csTypeName = GetDataTypeName(enumType.Name, out string enumPrefix);
string baseTypeName = GetTypeName(enumType.IntegerBase);
AddCsMapping(enumType.Name, csTypeName);
writer.WriteLine($"/// <unmanaged>{enumType.Name}</unmanaged>");
using (writer.PushBlock($"public enum {csTypeName} : {baseTypeName}"))
{
foreach (ApiEnumValue value in enumType.Values)
{
if (value.Name.EndsWith("_FORCE_DWORD") ||
value.Name.EndsWith("_FORCE_UINT"))
continue;
string enumValueName = GetPrettyFieldName(value.Name, enumPrefix);
writer.WriteLine($"/// <unmanaged>{value.Name}</unmanaged>");
writer.WriteLine($"{enumValueName} = {value.Value},");
}
}
writer.WriteLine();
}
private static void GenerateStruct(CodeWriter writer, ApiType structType)
{
string csTypeName = GetDataTypeName(structType.Name, out string structPrefix);
AddCsMapping(structType.Name, csTypeName);
writer.WriteLine($"/// <unmanaged>{structType.Name}</unmanaged>");
using (writer.PushBlock($"public partial struct {csTypeName}"))
{
foreach (ApiStructField field in structType.Fields)
{
if (field.Name.EndsWith("_FORCE_DWORD"))
continue;
string fieldValueName = GetPrettyFieldName(field.Name, structPrefix);
string fieldTypeName = GetTypeName(field.Type);
//writer.WriteLine($"/// <unmanaged>{field.Name}</unmanaged>");
if (fieldTypeName == "Array")
{
bool canUseFixed = false;
if (field.Type.Child.Kind == "Native")
{
canUseFixed = true;
}
fieldTypeName = GetTypeName(field.Type.Child);
if (canUseFixed)
{
writer.WriteLine($"public unsafe fixed {fieldTypeName} {fieldValueName}[{field.Type.Shape.Size}];");
}
else
{
writer.WriteLine($"public {fieldValueName}__FixedBuffer {fieldValueName};");
writer.WriteLine();
using (writer.PushBlock($"public unsafe struct {fieldValueName}__FixedBuffer"))
{
for (int i = 0; i < field.Type.Shape.Size; i++)
{
writer.WriteLine($"public {fieldTypeName} e{i};");
}
writer.WriteLine();
writer.WriteLine("[UnscopedRef]");
using (writer.PushBlock($"public ref {fieldTypeName} this[int index]"))
{
writer.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
using (writer.PushBlock("get"))
{
writer.WriteLine($"return ref AsSpan()[index];");
}
}
writer.WriteLine();
writer.WriteLine("[UnscopedRef]");
writer.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
using (writer.PushBlock($"public Span<{fieldTypeName}> AsSpan()"))
{
writer.WriteUndindented("#if NET6_0_OR_GREATER");
writer.WriteLine($"return MemoryMarshal.CreateSpan(ref e0, {field.Type.Shape.Size});");
writer.WriteUndindented("#else");
writer.WriteLine($"return new(Unsafe.AsPointer(ref e0), {field.Type.Shape.Size});");
writer.WriteUndindented("#endif");
}
}
}
}
else
{
writer.WriteLine($"public {fieldTypeName} {fieldValueName};");
}
}
}
writer.WriteLine();
}
private static bool ShouldSkipConstant(ApiDataConstant constant)
{
if (constant.Name == "_FACDXGI")
{
return true;
}
return false;
}
private static string GetDataTypeName(string typeName, out string prefix)
{
prefix = typeName;
if (s_knownTypesPrefixes.TryGetValue(typeName, out string? knowPrefix))
{
prefix = knowPrefix!;
}
string[] parts = typeName.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
var sb = new StringBuilder();
foreach (string part in parts)
{
if (s_ignoredParts.Contains(part))
{
continue;
}
if (s_preserveCaps.Contains(part))
{
sb.Append(part);
}
else
{
if (part.Equals("DESC", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Description");
}
else
{
sb.Append(char.ToUpper(part[0]));
for (int i = 1; i < part.Length; i++)
{
sb.Append(char.ToLower(part[i]));
}
}
}
}
string prettyName = sb.ToString();
return (char.IsNumber(prettyName[0])) ? "_" + prettyName : prettyName;
}
private static string GetPrettyFieldName(string value, string enumPrefix)
{
if (s_knownEnumValueNames.TryGetValue(value, out string? knownName))
{
return knownName;
}
if (value.IndexOf(enumPrefix) != 0)
{
return value;
}
bool isDXGIFormat = enumPrefix == "DXGI_FORMAT";
string[] parts = value[enumPrefix.Length..].Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
var sb = new StringBuilder();
foreach (string part in parts)
{
if (s_ignoredParts.Contains(part))
{
continue;
}
if (s_preserveCaps.Contains(part))
{
sb.Append(part);
}
else
{
if (isDXGIFormat)
{
if (part.Equals("UNKNOWN", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Unknown");
}
else if (part.Equals("TYPELESS", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Typeless");
}
else if (part.Equals("UNORM", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Unorm");
}
else if (part.Equals("SNORM", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Snorm");
}
else if (part.Equals("UINT", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Uint");
}
else if (part.Equals("SINT", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Sint");
}
else if (part.Equals("FLOAT", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Float");
}
else if (part.Equals("SRGB", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Srgb");
}
else if (part.Equals("SHAREDEXP", StringComparison.OrdinalIgnoreCase))
{
sb.Append("SharedExp");
}
else if (part.Equals("SAMPLER", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Sampler");
}
else if (part.Equals("FEEDBACK", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Feedback");
}
else if (part.Equals("MIN", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Min");
}
else if (part.Equals("MIP", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Mip");
}
else if (part.Equals("OPAQUE", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Opaque");
}
else if (part.Equals("REGION", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Region");
}
else if (part.Equals("USED", StringComparison.OrdinalIgnoreCase))
{
sb.Append("Used");
}
else
{
sb.Append(part);
}
}
else
{
sb.Append(char.ToUpper(part[0]));
for (int i = 1; i < part.Length; i++)
{
sb.Append(char.ToLower(part[i]));
}
}
}
}
string prettyName = sb.ToString();
return (char.IsNumber(prettyName[0])) ? "_" + prettyName : prettyName;
}
private static string GetTypeName(ApiDataType dataType)
{
if (dataType.Kind == "ApiRef")
{
return GetTypeName(dataType.Name);
}
else if (dataType.Kind == "Array")
{
return "Array";
}
else if (dataType.Kind == "PointerTo")
{
throw new NotImplementedException();
}
return GetTypeName(dataType.Name);
}
private static void AddCsMapping(string typeName, string csTypeName)
{
s_csNameMappings[typeName] = csTypeName;
}
private static string GetTypeName(string name)
{
if (s_csNameMappings.TryGetValue(name, out string? mappedName))
{
return mappedName;
}
return name;
}
} }

View File

@@ -0,0 +1,39 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
// Copyright © Tanner Gooding and Contributors. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
using System.Diagnostics;
namespace Win32;
/// <summary>Defines the type of a member as it was used in the native signature.</summary>
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false, Inherited = true)]
[Conditional("DEBUG")]
internal sealed partial class NativeTypeNameAttribute : Attribute
{
/// <summary>Initializes a new instance of the <see cref="NativeTypeNameAttribute" /> class.</summary>
/// <param name="name">The name of the type that was used in the native signature.</param>
public NativeTypeNameAttribute(string name)
{
Name = name;
}
/// <summary>Gets the name of the type that was used in the native signature.</summary>
public string Name { get; }
}
/// <summary>Defines the vtbl index of a method as it was in the native signature.</summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
[Conditional("DEBUG")]
internal sealed partial class VtblIndexAttribute : Attribute
{
/// <summary>Initializes a new instance of the <see cref="VtblIndexAttribute" /> class.</summary>
/// <param name="index">The vtbl index of a method as it was in the native signature.</param>
public VtblIndexAttribute(uint index)
{
Index = index;
}
/// <summary>Gets the vtbl index of a method as it was in the native signature.</summary>
public uint Index { get; }
}

View File

@@ -0,0 +1,99 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
namespace Win32;
public readonly partial struct Bool32 : IComparable, IComparable<Bool32>, IEquatable<Bool32>, IFormattable
{
public readonly int Value;
public Bool32(int value)
{
Value = value;
}
public static Bool32 True => new Bool32(1);
public static Bool32 False => new Bool32(0);
public static bool operator ==(Bool32 left, Bool32 right) => left.Value == right.Value;
public static bool operator !=(Bool32 left, Bool32 right) => left.Value != right.Value;
public static bool operator <(Bool32 left, Bool32 right) => left.Value < right.Value;
public static bool operator <=(Bool32 left, Bool32 right) => left.Value <= right.Value;
public static bool operator >(Bool32 left, Bool32 right) => left.Value > right.Value;
public static bool operator >=(Bool32 left, Bool32 right) => left.Value >= right.Value;
public static implicit operator bool(Bool32 value) => value.Value != 0;
public static implicit operator Bool32(bool value) => new Bool32(value ? 1 : 0);
public static bool operator false(Bool32 value) => value.Value == 0;
public static bool operator true(Bool32 value) => value.Value != 0;
public static implicit operator Bool32(byte value) => new Bool32(value);
public static explicit operator byte(Bool32 value) => (byte)(value.Value);
public static implicit operator Bool32(short value) => new Bool32(value);
public static explicit operator short(Bool32 value) => (short)(value.Value);
public static implicit operator Bool32(int value) => new Bool32(value);
public static implicit operator int(Bool32 value) => value.Value;
public static explicit operator Bool32(long value) => new Bool32(unchecked((int)(value)));
public static implicit operator long(Bool32 value) => value.Value;
public static explicit operator Bool32(nint value) => new Bool32(unchecked((int)(value)));
public static implicit operator nint(Bool32 value) => value.Value;
public static implicit operator Bool32(sbyte value) => new Bool32(value);
public static explicit operator sbyte(Bool32 value) => (sbyte)(value.Value);
public static implicit operator Bool32(ushort value) => new Bool32(value);
public static explicit operator ushort(Bool32 value) => (ushort)(value.Value);
public static explicit operator Bool32(uint value) => new Bool32(unchecked((int)(value)));
public static explicit operator uint(Bool32 value) => (uint)(value.Value);
public static explicit operator Bool32(ulong value) => new Bool32(unchecked((int)(value)));
public static explicit operator ulong(Bool32 value) => (ulong)(value.Value);
public static explicit operator Bool32(nuint value) => new Bool32(unchecked((int)(value)));
public static explicit operator nuint(Bool32 value) => (nuint)(value.Value);
public int CompareTo(object? obj)
{
if (obj is Bool32 other)
{
return CompareTo(other);
}
return (obj is null) ? 1 : throw new ArgumentException("obj is not an instance of Bool32.");
}
public int CompareTo(Bool32 other) => Value.CompareTo(other.Value);
public override bool Equals(object? obj) => (obj is Bool32 other) && Equals(other);
public bool Equals(Bool32 other) => Value.Equals(other.Value);
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value.ToString();
public string ToString(string? format, IFormatProvider? formatProvider) => Value.ToString(format, formatProvider);
}

318
src/Vortice.Win32/ComPtr.cs Normal file
View File

@@ -0,0 +1,318 @@
// Copyright © Tanner Gooding and Contributors. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
// Ported from winrt/wrl/client.h in the Windows SDK for Windows 10.0.22621.0
// Original source is Copyright © Microsoft. All rights reserved.
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using static Win32.Apis;
namespace Win32;
/// <summary>A type that allows working with pointers to COM objects more securely.</summary>
/// <typeparam name="T">The type to wrap in the current <see cref="ComPtr{T}"/> instance.</typeparam>
/// <remarks>While this type is not marked as <see langword="ref"/> so that it can also be used in fields, make sure to keep the reference counts properly tracked if you do store <see cref="ComPtr{T}"/> instances on the heap.</remarks>
public unsafe struct ComPtr<T> : IDisposable
where T : unmanaged, IUnknown.Interface
{
/// <summary>The raw pointer to a COM object, if existing.</summary>
private T* ptr_;
/// <summary>Creates a new <see cref="ComPtr{T}"/> instance from a raw pointer and increments the ref count.</summary>
/// <param name="other">The raw pointer to wrap.</param>
public ComPtr(T* other)
{
ptr_ = other;
InternalAddRef();
}
/// <summary>Creates a new <see cref="ComPtr{T}"/> instance from a second one and increments the ref count.</summary>
/// <param name="other">The other <see cref="ComPtr{T}"/> instance to copy.</param>
public ComPtr(ComPtr<T> other)
{
ptr_ = other.ptr_;
InternalAddRef();
}
/// <summary>Converts a raw pointer to a new <see cref="ComPtr{T}"/> instance and increments the ref count.</summary>
/// <param name="other">The raw pointer to wrap.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ComPtr<T>(T* other)
=> new ComPtr<T>(other);
/// <summary>Unwraps a <see cref="ComPtr{T}"/> instance and returns the internal raw pointer.</summary>
/// <param name="other">The <see cref="ComPtr{T}"/> instance to unwrap.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T*(ComPtr<T> other)
=> other.Get();
/// <summary>Converts the current object reference to type <typeparamref name="U"/> and assigns that to a target <see cref="ComPtr{T}"/> value.</summary>
/// <typeparam name="U">The interface type to use to try casting the current COM object.</typeparam>
/// <param name="p">A raw pointer to the target <see cref="ComPtr{T}"/> value to write to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target type <typeparamref name="U"/>.</returns>
/// <remarks>This method will automatically release the target COM object pointed to by <paramref name="p"/>, if any.</remarks>
public readonly HResult As<U>(ComPtr<U>* p)
where U : unmanaged, IUnknown.Interface
{
return ptr_->QueryInterface(__uuidof<U>(), (void**)p->ReleaseAndGetAddressOf());
}
/// <summary>Converts the current object reference to type <typeparamref name="U"/> and assigns that to a target <see cref="ComPtr{T}"/> value.</summary>
/// <typeparam name="U">The interface type to use to try casting the current COM object.</typeparam>
/// <param name="other">A reference to the target <see cref="ComPtr{T}"/> value to write to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target type <typeparamref name="U"/>.</returns>
/// <remarks>This method will automatically release the target COM object pointed to by <paramref name="other"/>, if any.</remarks>
public readonly HResult As<U>(ref ComPtr<U> other)
where U : unmanaged, IUnknown.Interface
{
U* ptr;
HResult result = ptr_->QueryInterface(__uuidof<U>(), (void**)&ptr);
other.Attach(ptr);
return result;
}
/// <summary>Converts the current object reference to a type indicated by the given IID and assigns that to a target <see cref="ComPtr{T}"/> value.</summary>
/// <param name="riid">The IID indicating the interface type to convert the COM object reference to.</param>
/// <param name="other">A raw pointer to the target <see cref="ComPtr{T}"/> value to write to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target IID.</returns>
/// <remarks>This method will automatically release the target COM object pointed to by <paramref name="other"/>, if any.</remarks>
public readonly HResult AsIID(Guid* riid, ComPtr<IUnknown>* other)
{
return ptr_->QueryInterface(riid, (void**)other->ReleaseAndGetAddressOf());
}
/// <summary>Converts the current object reference to a type indicated by the given IID and assigns that to a target <see cref="ComPtr{T}"/> value.</summary>
/// <param name="riid">The IID indicating the interface type to convert the COM object reference to.</param>
/// <param name="other">A reference to the target <see cref="ComPtr{T}"/> value to write to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target IID.</returns>
/// <remarks>This method will automatically release the target COM object pointed to by <paramref name="other"/>, if any.</remarks>
public readonly HResult AsIID(Guid* riid, ref ComPtr<IUnknown> other)
{
IUnknown* ptr;
HResult result = ptr_->QueryInterface(riid, (void**)&ptr);
other.Attach(ptr);
return result;
}
/// <summary>Releases the current COM object, if any, and replaces the internal pointer with an input raw pointer.</summary>
/// <param name="other">The input raw pointer to wrap.</param>
/// <remarks>This method will release the current raw pointer, if any, but it will not increment the references for <paramref name="other"/>.</remarks>
public void Attach(T* other)
{
if (ptr_ != null)
{
var @ref = ptr_->Release();
Debug.Assert((@ref != 0) || (ptr_ != other));
}
ptr_ = other;
}
/// <summary>Returns the raw pointer wrapped by the current instance, and resets the current <see cref="ComPtr{T}"/> value.</summary>
/// <returns>The raw pointer wrapped by the current <see cref="ComPtr{T}"/> value.</returns>
/// <remarks>This method will not change the reference count for the COM object in use.</remarks>
public T* Detach()
{
T* ptr = ptr_;
ptr_ = null;
return ptr;
}
/// <summary>Increments the reference count for the current COM object, if any, and copies its address to a target raw pointer.</summary>
/// <param name="ptr">The target raw pointer to copy the address of the current COM object to.</param>
/// <returns>This method always returns <see cref="HResult.Ok"/>.</returns>
public readonly HResult CopyTo(T** ptr)
{
InternalAddRef();
*ptr = ptr_;
return HResult.Ok;
}
/// <summary>Increments the reference count for the current COM object, if any, and copies its address to a target <see cref="ComPtr{T}"/>.</summary>
/// <param name="p">The target raw pointer to copy the address of the current COM object to.</param>
/// <returns>This method always returns <see cref="HResult.Ok"/>.</returns>
public readonly HResult CopyTo(ComPtr<T>* p)
{
InternalAddRef();
*p->ReleaseAndGetAddressOf() = ptr_;
return HResult.Ok;
}
/// <summary>Increments the reference count for the current COM object, if any, and copies its address to a target <see cref="ComPtr{T}"/>.</summary>
/// <param name="other">The target reference to copy the address of the current COM object to.</param>
/// <returns>This method always returns <see cref="HResult.Ok"/>.</returns>
public readonly HResult CopyTo(ref ComPtr<T> other)
{
InternalAddRef();
other.Attach(ptr_);
return HResult.Ok;
}
/// <summary>Converts the current COM object reference to a given interface type and assigns that to a target raw pointer.</summary>
/// <param name="ptr">The target raw pointer to copy the address of the current COM object to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target type <typeparamref name="U"/>.</returns>
public readonly HResult CopyTo<U>(U** ptr)
where U : unmanaged, IUnknown.Interface
{
return ptr_->QueryInterface(__uuidof<U>(), (void**)ptr);
}
/// <summary>Converts the current COM object reference to a given interface type and assigns that to a target <see cref="ComPtr{T}"/>.</summary>
/// <param name="p">The target raw pointer to copy the address of the current COM object to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target type <typeparamref name="U"/>.</returns>
public readonly HResult CopyTo<U>(ComPtr<U>* p)
where U : unmanaged, IUnknown.Interface
{
return ptr_->QueryInterface(__uuidof<U>(), (void**)p->ReleaseAndGetAddressOf());
}
/// <summary>Converts the current COM object reference to a given interface type and assigns that to a target <see cref="ComPtr{T}"/>.</summary>
/// <param name="other">The target reference to copy the address of the current COM object to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target type <typeparamref name="U"/>.</returns>
public readonly HResult CopyTo<U>(ref ComPtr<U> other)
where U : unmanaged, IUnknown.Interface
{
U* ptr;
HResult result = ptr_->QueryInterface(__uuidof<U>(), (void**)&ptr);
other.Attach(ptr);
return result;
}
/// <summary>Converts the current object reference to a type indicated by the given IID and assigns that to a target address.</summary>
/// <param name="riid">The IID indicating the interface type to convert the COM object reference to.</param>
/// <param name="ptr">The target raw pointer to copy the address of the current COM object to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target IID.</returns>
public readonly HResult CopyTo(Guid* riid, void** ptr)
{
return ptr_->QueryInterface(riid, ptr);
}
/// <summary>Converts the current object reference to a type indicated by the given IID and assigns that to a target <see cref="ComPtr{T}"/> value.</summary>
/// <param name="riid">The IID indicating the interface type to convert the COM object reference to.</param>
/// <param name="p">The target raw pointer to copy the address of the current COM object to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target IID.</returns>
public readonly HResult CopyTo(Guid* riid, ComPtr<IUnknown>* p)
{
return ptr_->QueryInterface(riid, (void**)p->ReleaseAndGetAddressOf());
}
/// <summary>Converts the current object reference to a type indicated by the given IID and assigns that to a target <see cref="ComPtr{T}"/> value.</summary>
/// <param name="riid">The IID indicating the interface type to convert the COM object reference to.</param>
/// <param name="other">The target reference to copy the address of the current COM object to.</param>
/// <returns>The result of <see cref="IUnknown.QueryInterface"/> for the target IID.</returns>
public readonly HResult CopyTo(Guid* riid, ref ComPtr<IUnknown> other)
{
IUnknown* ptr;
HResult result = ptr_->QueryInterface(riid, (void**)&ptr);
other.Attach(ptr);
return result;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
T* pointer = ptr_;
if (pointer != null)
{
ptr_ = null;
_ = pointer->Release();
}
}
/// <summary>Gets the currently wrapped raw pointer to a COM object.</summary>
/// <returns>The raw pointer wrapped by the current <see cref="ComPtr{T}"/> instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly T* Get()
{
return ptr_;
}
/// <summary>Gets the address of the current <see cref="ComPtr{T}"/> instance as a raw <typeparamref name="T"/> double pointer. This method is only valid when the current <see cref="ComPtr{T}"/> instance is on the stack or pinned.
/// </summary>
/// <returns>The raw pointer to the current <see cref="ComPtr{T}"/> instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly T** GetAddressOf()
{
return (T**)Unsafe.AsPointer(ref Unsafe.AsRef(in this));
}
/// <summary>Gets the address of the current <see cref="ComPtr{T}"/> instance as a raw <typeparamref name="T"/> double pointer.</summary>
/// <returns>The raw pointer to the current <see cref="ComPtr{T}"/> instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref T* GetPinnableReference()
{
fixed (T** ptr = &ptr_)
{
return ref *ptr;
}
}
/// <summary>Releases the current COM object in use and gets the address of the <see cref="ComPtr{T}"/> instance as a raw <typeparamref name="T"/> double pointer. This method is only valid when the current <see cref="ComPtr{T}"/> instance is on the stack or pinned.</summary>
/// <returns>The raw pointer to the current <see cref="ComPtr{T}"/> instance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T** ReleaseAndGetAddressOf()
{
_ = InternalRelease();
return GetAddressOf();
}
/// <summary>Resets the current instance by decrementing the reference count for the target COM object and setting the internal raw pointer to <see langword="null"/>.</summary>
/// <returns>The updated reference count for the COM object that was in use, if any.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Reset()
{
return InternalRelease();
}
/// <summary>Swaps the current COM object reference with that of a given <see cref="ComPtr{T}"/> instance.</summary>
/// <param name="r">The target <see cref="ComPtr{T}"/> instance to swap with the current one.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Swap(ComPtr<T>* r)
{
T* tmp = ptr_;
ptr_ = r->ptr_;
r->ptr_ = tmp;
}
/// <summary>Swaps the current COM object reference with that of a given <see cref="ComPtr{T}"/> instance.</summary>
/// <param name="other">The target <see cref="ComPtr{T}"/> instance to swap with the current one.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Swap(ref ComPtr<T> other)
{
T* tmp = ptr_;
ptr_ = other.ptr_;
other.ptr_ = tmp;
}
// Increments the reference count for the current COM object, if any
private readonly void InternalAddRef()
{
T* temp = ptr_;
if (temp != null)
{
_ = temp->AddRef();
}
}
// Decrements the reference count for the current COM object, if any
private uint InternalRelease()
{
uint @ref = 0;
T* temp = ptr_;
if (temp != null)
{
ptr_ = null;
@ref = temp->Release();
}
return @ref;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
namespace Win32.Graphics.Dxgi;
public partial struct Rgb
{
/// <summary>
/// Initialize instance of <see cref="Rgb"/> struct.
/// </summary>
/// <param name="red"></param>
/// <param name="green"></param>
/// <param name="blue"></param>
public Rgb(float red, float green, float blue)
{
Red = red;
Green = green;
Blue = blue;
}
public override string ToString()
{
return $"{nameof(Rgb)}(Red: {Red}, Green: {Green}, Blue: {Blue})";
}
}
public partial struct Rational
{
/// <summary>
/// Initialize instance of <see cref="Rational"/> struct.
/// </summary>
/// <param name="numerator"></param>
/// <param name="denominator"></param>
public Rational(uint numerator, uint denominator)
{
Numerator = numerator;
Denominator = denominator;
}
public override string ToString()
{
return $"{nameof(Rational)}(Numerator: {Numerator}, Denominator: {Denominator})";
}
}
public partial struct SampleDescription
{
/// <summary>
/// A <see cref="SampleDescription"/> with Count=1 and Quality=0.
/// </summary>
public static readonly SampleDescription Default = new(1, 0);
/// <summary>
/// Create new instance of <see cref="SampleDescription"/> struct.
/// </summary>
/// <param name="count"></param>
/// <param name="quality"></param>
public SampleDescription(uint count, uint quality)
{
Count = count;
Quality = quality;
}
public override string ToString() => $"Count: {Count}, Quality: {Quality}";
}

View File

@@ -0,0 +1,61 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
namespace Win32;
public readonly partial struct HResult
{
/// <unmanaged>S_OK</unmanaged>
public static readonly HResult Ok = 0;
/// <unmanaged>S_FALSE</unmanaged>
public static readonly HResult False = 1;
/// <unmanaged>E_ABORT</unmanaged>
public static readonly HResult Abort = unchecked((int)0x80004004);
/// <unmanaged>E_ACCESSDENIED</unmanaged>
public static readonly HResult AccessDenied = unchecked((int)0x80070005);
/// <unmanaged>E_FAIL</unmanaged>
public static readonly HResult Fail = unchecked((int)0x80004005);
/// <unmanaged>E_HANDLE</unmanaged>
public static readonly HResult Handle = unchecked((int)0x80070006);
/// <unmanaged>E_INVALIDARG</unmanaged>
public static readonly HResult InvalidArg = unchecked((int)0x80070057);
/// <unmanaged>E_NOINTERFACE</unmanaged>
public static readonly HResult NoInterface = unchecked((int)0x80004002);
/// <unmanaged>E_NOTIMPL</unmanaged>
public static readonly HResult NotImplemented = unchecked((int)0x80004001);
/// <unmanaged>E_OUTOFMEMORY</unmanaged>
public static readonly HResult OutOfMemory = unchecked((int)0x8007000E);
/// <unmanaged>E_POINTER</unmanaged>
public static readonly HResult InvalidPointer = unchecked((int)0x80004003);
/// <unmanaged>E_UNEXPECTED</unmanaged>
public static readonly HResult UnexpectedFailure = unchecked((int)0x8000FFFF);
/// <unmanaged>WAIT_ABANDONED</unmanaged>
public static readonly HResult WaitAbandoned = unchecked((int)0x00000080);
/// <unmanaged>WAIT_TIMEOUT</unmanaged>
public static readonly HResult WaitTimeout = unchecked((int)0x00000102);
/// <summary>
/// The data necessary to complete this operation is not yet available.
/// </summary>
/// <unmanaged>E_PENDING</unmanaged>
public static readonly HResult Pending = unchecked((int)0x8000000A);
/// <summary>
/// The data area passed to a system call is too small.
/// </summary>
/// <unmanaged>E_NOT_SUFFICIENT_BUFFER</unmanaged>
public static readonly HResult InsufficientBuffer = unchecked((int)0x8007007A);
}

View File

@@ -0,0 +1,92 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
namespace Win32;
public readonly partial struct HResult : IComparable, IComparable<HResult>, IEquatable<HResult>, IFormattable
{
public readonly int Value;
public HResult(int value)
{
Value = value;
}
public static bool operator ==(HResult left, HResult right) => left.Value == right.Value;
public static bool operator !=(HResult left, HResult right) => left.Value != right.Value;
public static bool operator <(HResult left, HResult right) => left.Value < right.Value;
public static bool operator <=(HResult left, HResult right) => left.Value <= right.Value;
public static bool operator >(HResult left, HResult right) => left.Value > right.Value;
public static bool operator >=(HResult left, HResult right) => left.Value >= right.Value;
public static implicit operator HResult(byte value) => new HResult(value);
public static explicit operator byte(HResult value) => (byte)(value.Value);
public static implicit operator HResult(short value) => new HResult(value);
public static explicit operator short(HResult value) => (short)(value.Value);
public static implicit operator HResult(int value) => new HResult(value);
public static implicit operator int(HResult value) => value.Value;
public static explicit operator HResult(long value) => new HResult(unchecked((int)(value)));
public static implicit operator long(HResult value) => value.Value;
public static explicit operator HResult(nint value) => new HResult(unchecked((int)(value)));
public static implicit operator nint(HResult value) => value.Value;
public static implicit operator HResult(sbyte value) => new HResult(value);
public static explicit operator sbyte(HResult value) => (sbyte)(value.Value);
public static implicit operator HResult(ushort value) => new HResult(value);
public static explicit operator ushort(HResult value) => (ushort)(value.Value);
public static explicit operator HResult(uint value) => new HResult(unchecked((int)(value)));
public static explicit operator uint(HResult value) => (uint)(value.Value);
public static explicit operator HResult(ulong value) => new HResult(unchecked((int)(value)));
public static explicit operator ulong(HResult value) => (ulong)(value.Value);
public static explicit operator HResult(nuint value) => new HResult(unchecked((int)(value)));
public static explicit operator nuint(HResult value) => (nuint)(value.Value);
public int CompareTo(object? obj)
{
if (obj is HResult other)
{
return CompareTo(other);
}
return (obj is null) ? 1 : throw new ArgumentException("obj is not an instance of HResult.");
}
public int CompareTo(HResult other) => Value.CompareTo(other.Value);
public override bool Equals(object? obj) => (obj is HResult other) && Equals(other);
public bool Equals(HResult other) => Value.Equals(other.Value);
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value.ToString("X8");
public string ToString(string? format, IFormatProvider? formatProvider) => Value.ToString(format, formatProvider);
public bool Failure => Value < 0;
public bool Success => Value >= 0;
}

View File

@@ -0,0 +1,65 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
using System.Runtime.CompilerServices;
using static Win32.Apis;
namespace Win32;
[Guid("00000000-0000-0000-C000-000000000046")]
public unsafe partial struct IUnknown : IUnknown.Interface
{
public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID_IUnknown));
public void** lpVtbl;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[VtblIndex(0)]
public HResult QueryInterface([NativeTypeName("const IID &")] Guid* riid, void** ppvObject)
{
return ((delegate* unmanaged[Stdcall]<IUnknown*, Guid*, void**, int>)(lpVtbl[0]))((IUnknown*)Unsafe.AsPointer(ref this), riid, ppvObject);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[VtblIndex(1)]
[return: NativeTypeName("ULONG")]
public uint AddRef()
{
return ((delegate* unmanaged[Stdcall]<IUnknown*, uint>)(lpVtbl[1]))((IUnknown*)Unsafe.AsPointer(ref this));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[VtblIndex(2)]
[return: NativeTypeName("ULONG")]
public uint Release()
{
return ((delegate* unmanaged[Stdcall]<IUnknown*, uint>)(lpVtbl[2]))((IUnknown*)Unsafe.AsPointer(ref this));
}
public interface Interface
{
[VtblIndex(0)]
HResult QueryInterface([NativeTypeName("const IID &")] Guid* riid, void** ppvObject);
[VtblIndex(1)]
[return: NativeTypeName("ULONG")]
uint AddRef();
[VtblIndex(2)]
[return: NativeTypeName("ULONG")]
uint Release();
}
public partial struct Vtbl<TSelf>
where TSelf : unmanaged, Interface
{
[NativeTypeName("HRESULT (const IID &, void **) __attribute__((stdcall))")]
public delegate* unmanaged[Stdcall]<TSelf*, Guid*, void**, int> QueryInterface;
[NativeTypeName("ULONG () __attribute__((stdcall))")]
public delegate* unmanaged[Stdcall]<TSelf*, uint> AddRef;
[NativeTypeName("ULONG () __attribute__((stdcall))")]
public delegate* unmanaged[Stdcall]<TSelf*, uint> Release;
}
}

View File

@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Diagnostics.CodeAnalysis;
/// <summary>
/// Used to indicate a byref escapes and is not scoped.
/// </summary>
/// <remarks>
/// <para>
/// There are several cases where the C# compiler treats a <see langword="ref"/> as implicitly
/// <see langword="scoped"/> - where the compiler does not allow the <see langword="ref"/> to escape the method.
/// </para>
/// <para>
/// For example:
/// <list type="number">
/// <item><see langword="this"/> for <see langword="struct"/> instance methods.</item>
/// <item><see langword="ref"/> parameters that refer to <see langword="ref"/> <see langword="struct"/> types.</item>
/// <item><see langword="out"/> parameters.</item>
/// </list>
/// </para>
/// <para>
/// This attribute is used in those instances where the <see langword="ref"/> should be allowed to escape.
/// </para>
/// <para>
/// Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
/// API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
/// </para>
/// </remarks>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter,
AllowMultiple = false,
Inherited = false)]
internal sealed class UnscopedRefAttribute : Attribute
{
}

110
src/Vortice.Win32/Win32.cs Normal file
View File

@@ -0,0 +1,110 @@
// Copyright © Amer Koleci and Contributors.
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Win32;
public static partial class Apis
{
public static ref readonly Guid IID_IUnknown
{
get
{
ReadOnlySpan<byte> data = new byte[] {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0xC0,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x46
};
Debug.Assert(data.Length == Unsafe.SizeOf<Guid>());
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
/// <summary>Retrieves the GUID of of a specified type.</summary>
/// <param name="value">A value of type <typeparamref name="T"/>.</param>
/// <typeparam name="T">The type to retrieve the GUID for.</typeparam>
/// <returns>A <see cref="UuidOfType"/> value wrapping a pointer to the GUID data for the input type. This value can be either converted to a <see cref="Guid"/> pointer, or implicitly assigned to a <see cref="Guid"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe UuidOfType __uuidof<T>(T value) // for type inference similar to C++'s __uuidof
where T : unmanaged
{
return new UuidOfType(UUID<T>.RIID);
}
/// <summary>Retrieves the GUID of of a specified type.</summary>
/// <param name="value">A pointer to a value of type <typeparamref name="T"/>.</param>
/// <typeparam name="T">The type to retrieve the GUID for.</typeparam>
/// <returns>A <see cref="UuidOfType"/> value wrapping a pointer to the GUID data for the input type. This value can be either converted to a <see cref="Guid"/> pointer, or implicitly assigned to a <see cref="Guid"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe UuidOfType __uuidof<T>(T* value) // for type inference similar to C++'s __uuidof
where T : unmanaged
{
return new UuidOfType(UUID<T>.RIID);
}
/// <summary>Retrieves the GUID of of a specified type.</summary>
/// <typeparam name="T">The type to retrieve the GUID for.</typeparam>
/// <returns>A <see cref="UuidOfType"/> value wrapping a pointer to the GUID data for the input type. This value can be either converted to a <see cref="Guid"/> pointer, or implicitly assigned to a <see cref="Guid"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe UuidOfType __uuidof<T>()
where T : unmanaged
{
return new UuidOfType(UUID<T>.RIID);
}
/// <summary>A proxy type that wraps a pointer to GUID data. Values of this type can be implicitly converted to and assigned to <see cref="Guid"/>* or <see cref="Guid"/> parameters.</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly unsafe ref struct UuidOfType
{
private readonly Guid* riid;
internal UuidOfType(Guid* riid)
{
this.riid = riid;
}
/// <summary>Reads a <see cref="Guid"/> value from the GUID buffer for a given <see cref="UuidOfType"/> instance.</summary>
/// <param name="guid">The input <see cref="UuidOfType"/> instance to read data for.</param>
public static implicit operator Guid(UuidOfType guid) => *guid.riid;
/// <summary>Returns the <see cref="Guid"/>* pointer to the GUID buffer for a given <see cref="UuidOfType"/> instance.</summary>
/// <param name="guid">The input <see cref="UuidOfType"/> instance to read data for.</param>
public static implicit operator Guid*(UuidOfType guid) => guid.riid;
}
/// <summary>A helper type to provide static GUID buffers for specific types.</summary>
/// <typeparam name="T">The type to allocate a GUID buffer for.</typeparam>
private static unsafe class UUID<T>
where T : unmanaged
{
/// <summary>The pointer to the <see cref="Guid"/> value for the current type.</summary>
/// <remarks>The target memory area should never be written to.</remarks>
public static readonly Guid* RIID = CreateRIID();
/// <summary>Allocates memory for a <see cref="Guid"/> value and initializes it.</summary>
/// <returns>A pointer to memory holding the <see cref="Guid"/> value for the current type.</returns>
private static Guid* CreateRIID()
{
#if NET6_0_OR_GREATER
var p = (Guid*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(T), sizeof(Guid));
#else
var p = (Guid*)Marshal.AllocHGlobal(sizeof(Guid));
#endif
*p = typeof(T).GUID;
return p;
}
}
}