diff --git a/PInvoke/VirtDisk/VirtDisk.cs b/PInvoke/VirtDisk/VirtDisk.cs
index 13c77f72..2ed1c7c4 100644
--- a/PInvoke/VirtDisk/VirtDisk.cs
+++ b/PInvoke/VirtDisk/VirtDisk.cs
@@ -1014,7 +1014,7 @@ namespace Vanara.PInvoke
///
[PInvokeData("VirtDisk.h")]
[DllImport(Lib.VirtDisk, ExactSpelling = true)]
- public static extern Win32Error EnumerateVirtualDiskMetadata(SafeFileHandle VirtualDiskHandle, ref uint NumberOfItems, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Guid[] Items);
+ public static extern Win32Error EnumerateVirtualDiskMetadata(SafeFileHandle VirtualDiskHandle, ref uint NumberOfItems, IntPtr Items);
/// Increases the size of a fixed or dynamic virtual hard disk (VHD).
/// A handle to the open VHD, which must have been opened using the VIRTUAL_DISK_ACCESS_METAOPS flag.
diff --git a/System/VirtualDisk.cs b/System/VirtualDisk.cs
index dc3d506e..9292a3f7 100644
--- a/System/VirtualDisk.cs
+++ b/System/VirtualDisk.cs
@@ -1,41 +1,34 @@
using Microsoft.Win32.SafeHandles;
using System;
-using System.ComponentModel;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
using System.Linq;
-using System.Threading;
+using System.Runtime.InteropServices;
+using System.Security.AccessControl;
+using System.Text;
#if !(NET20 || NET35 || NET40)
+using System.Threading;
using System.Threading.Tasks;
#endif
-using System.Security.AccessControl;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.VirtDisk;
-using System.Collections.Generic;
-using System.Text;
-using System.Runtime.InteropServices;
-using System.Diagnostics;
-using System.IO;
namespace Vanara.IO
{
public class VirtualDisk : IDisposable
{
+ private static bool IsPreWin8 = Environment.OSVersion.Version < new Version(6, 2);
private bool attached = false;
private SafeFileHandle hVhd;
+ private VirtualDiskMetadata metadata;
private OPEN_VIRTUAL_DISK_VERSION ver;
- private static bool IsPreWin8 = Environment.OSVersion.Version < new Version(6, 2);
-
private VirtualDisk(SafeFileHandle handle, OPEN_VIRTUAL_DISK_VERSION version) { hVhd = handle; ver = version; }
- public enum Subtype : uint
- {
- Fixed = 2,
- Dynamic = 3,
- Differencing = 4
- }
-
public enum DeviceType : uint
{
/// Device type is unknown or not valid.
@@ -56,12 +49,17 @@ namespace Vanara.IO
///
Vhdx = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE.VIRTUAL_STORAGE_TYPE_DEVICE_VHDX,
- ///
- ///
- ///
+ ///
VhdSet = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE.VIRTUAL_STORAGE_TYPE_DEVICE_VHDSET
}
+ public enum Subtype : uint
+ {
+ Fixed = 2,
+ Dynamic = 3,
+ Differencing = 4
+ }
+
/// Indicates whether this virtual disk is currently attached.
public bool Attached => attached;
@@ -105,6 +103,8 @@ namespace Vanara.IO
/// The logical sector size of the physical disk.
public uint LogicalSectorSize => GetInformation(GET_VIRTUAL_DISK_INFO_VERSION.GET_VIRTUAL_DISK_INFO_PHYSICAL_DISK);
+ public VirtualDiskMetadata Metadata => metadata ?? (metadata = new VirtualDiskMetadata(this));
+
///
/// The change tracking identifier for the change that identifies the state of the virtual disk that you want to use as the basis of comparison to
/// determine whether the NewerChanges member reports new changes.
@@ -211,7 +211,10 @@ namespace Vanara.IO
/// A reference to a valid CREATE_VIRTUAL_DISK_PARAMETERS structure that contains creation parameter data.
/// Creation flags, which must be a valid combination of the CREATE_VIRTUAL_DISK_FLAG enumeration.
/// The VIRTUAL_DISK_ACCESS_MASK value to use when opening the newly created virtual disk file.
- /// An optional pointer to a SECURITY_DESCRIPTOR to apply to the virtual disk image file. If this parameter is IntPtr.Zero, the parent directory's security descriptor will be used.
+ ///
+ /// An optional pointer to a SECURITY_DESCRIPTOR to apply to the virtual disk image file. If this parameter is IntPtr.Zero, the parent directory's
+ /// security descriptor will be used.
+ ///
/// If successful, returns a valid instance for the newly created virtual disk.
public static VirtualDisk Create(string path, ref CREATE_VIRTUAL_DISK_PARAMETERS param, CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG.CREATE_VIRTUAL_DISK_FLAG_NONE, VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_NONE, IntPtr securityDescriptor = default(IntPtr))
{
@@ -269,32 +272,6 @@ namespace Vanara.IO
return Create(path, ref param, flags, mask, sd);
}
- /// Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.
- /// A valid file path that represents the path to the new virtual disk image file.
- ///
- /// A fully qualified path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a virtual
- /// disk or a physical disk.
- ///
- /// If successful, returns a valid instance for the newly created virtual disk.
- public static VirtualDisk CreateFromSource(string path, string sourcePath)
- {
- if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
- if (string.IsNullOrEmpty(sourcePath)) throw new ArgumentNullException(nameof(sourcePath));
-
- var param = new CREATE_VIRTUAL_DISK_PARAMETERS(0, IsPreWin8 ? 1U : 2U);
- var mask = IsPreWin8 ? VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_CREATE : VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_NONE;
- var sp = new SafeCoTaskMemString(sourcePath);
- if (sourcePath != null)
- {
- if (IsPreWin8)
- param.Version1.SourcePath = (IntPtr)sp;
- else
- param.Version2.SourcePath = (IntPtr)sp;
- }
- var flags = CREATE_VIRTUAL_DISK_FLAG.CREATE_VIRTUAL_DISK_FLAG_NONE;
- return Create(path, ref param, flags, mask, IntPtr.Zero);
- }
-
/// Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.
/// A valid string that represents the path to the new virtual disk image file.
///
@@ -325,8 +302,35 @@ namespace Vanara.IO
return Create(path, ref param, CREATE_VIRTUAL_DISK_FLAG.CREATE_VIRTUAL_DISK_FLAG_NONE, mask, sd);
}
+ /// Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.
+ /// A valid file path that represents the path to the new virtual disk image file.
+ ///
+ /// A fully qualified path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a virtual disk or a
+ /// physical disk.
+ ///
+ /// If successful, returns a valid instance for the newly created virtual disk.
+ public static VirtualDisk CreateFromSource(string path, string sourcePath)
+ {
+ if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
+ if (string.IsNullOrEmpty(sourcePath)) throw new ArgumentNullException(nameof(sourcePath));
+
+ var param = new CREATE_VIRTUAL_DISK_PARAMETERS(0, IsPreWin8 ? 1U : 2U);
+ var mask = IsPreWin8 ? VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_CREATE : VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_NONE;
+ var sp = new SafeCoTaskMemString(sourcePath);
+ if (sourcePath != null)
+ {
+ if (IsPreWin8)
+ param.Version1.SourcePath = (IntPtr)sp;
+ else
+ param.Version2.SourcePath = (IntPtr)sp;
+ }
+ var flags = CREATE_VIRTUAL_DISK_FLAG.CREATE_VIRTUAL_DISK_FLAG_NONE;
+ return Create(path, ref param, flags, mask, IntPtr.Zero);
+ }
+
///
- /// Detach a virtual disk that was previously attached with the ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME flag or calling and setting autoDetach to false.
+ /// Detach a virtual disk that was previously attached with the ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME flag or calling
+ /// and setting autoDetach to false.
///
/// A valid path to the virtual disk image to detach.
public static void Detach(string path)
@@ -374,7 +378,10 @@ namespace Vanara.IO
/// A valid path to the virtual disk image to open.
/// If TRUE, indicates the file backing store is to be opened as read-only.
/// If TRUE, indicates the handle is only to be used to get information on the virtual disk.
- /// Open the VHD file (backing store) without opening any differencing-chain parents. Used to correct broken parent links. This flag is not supported for ISO virtual disks.
+ ///
+ /// Open the VHD file (backing store) without opening any differencing-chain parents. Used to correct broken parent links. This flag is not supported for
+ /// ISO virtual disks.
+ ///
public static VirtualDisk Open(string path, bool readOnly, bool getInfoOnly = false, bool noParents = false)
{
if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
@@ -477,13 +484,6 @@ namespace Vanara.IO
ExpandVirtualDisk(hVhd, EXPAND_VIRTUAL_DISK_FLAG.EXPAND_VIRTUAL_DISK_FLAG_NONE, ref param, IntPtr.Zero).ThrowIfFailed();
}
- /// Merges a child virtual hard disk (VHD) in a differencing chain with its immediate parent disk in the chain.
- public void MergeWithParent()
- {
- var param = new MERGE_VIRTUAL_DISK_PARAMETERS(1);
- MergeVirtualDisk(hVhd, MERGE_VIRTUAL_DISK_FLAG.MERGE_VIRTUAL_DISK_FLAG_NONE, ref param, IntPtr.Zero).ThrowIfFailed();
- }
-
/// Merges a child virtual hard disk (VHD) in a differencing chain with parent disks in the chain.
/// Depth from the leaf from which to begin the merge. The leaf is at depth 1.
/// Depth from the leaf to target the merge. The leaf is at depth 1.
@@ -493,8 +493,18 @@ namespace Vanara.IO
MergeVirtualDisk(hVhd, MERGE_VIRTUAL_DISK_FLAG.MERGE_VIRTUAL_DISK_FLAG_NONE, ref param, IntPtr.Zero).ThrowIfFailed();
}
+ /// Merges a child virtual hard disk (VHD) in a differencing chain with its immediate parent disk in the chain.
+ public void MergeWithParent()
+ {
+ var param = new MERGE_VIRTUAL_DISK_PARAMETERS(1);
+ MergeVirtualDisk(hVhd, MERGE_VIRTUAL_DISK_FLAG.MERGE_VIRTUAL_DISK_FLAG_NONE, ref param, IntPtr.Zero).ThrowIfFailed();
+ }
+
/// Resizes a virtual disk.
- /// New size, in bytes, for the expansion request. Setting this value to '0' will shrink the disk to the smallest safe virtual size possible without truncating past any existing partitions.
+ ///
+ /// New size, in bytes, for the expansion request. Setting this value to '0' will shrink the disk to the smallest safe virtual size possible without
+ /// truncating past any existing partitions.
+ ///
public void Resize(ulong newSize)
{
if (ver < OPEN_VIRTUAL_DISK_VERSION.OPEN_VIRTUAL_DISK_VERSION_2) throw new NotSupportedException(@"Expansion is only available to virtual disks opened under version 2 or higher.");
@@ -503,8 +513,9 @@ namespace Vanara.IO
ResizeVirtualDisk(hVhd, flags, ref param, IntPtr.Zero).ThrowIfFailed();
}
- /// Resizes a virtual disk without checking the virtual disk's partition table to ensure that this truncation is safe.
- /// This method can cause unrecoverable data loss; use with care.
+ ///
+ /// Resizes a virtual disk without checking the virtual disk's partition table to ensure that this truncation is safe. This method
+ /// can cause unrecoverable data loss; use with care.
///
/// New size, in bytes, for the expansion request.
public void UnsafeResize(ulong newSize)
@@ -515,37 +526,17 @@ namespace Vanara.IO
ResizeVirtualDisk(hVhd, flags, ref param, IntPtr.Zero).ThrowIfFailed();
}
- private T GetInformation(GET_VIRTUAL_DISK_INFO_VERSION info, long offset = 0)
- {
- var sz = 32U;
- using (var mem = new SafeHGlobalHandle((int)sz))
- {
- Marshal.WriteInt32(mem.DangerousGetHandle(), (int)info);
- var err = GetVirtualDiskInformation(hVhd, ref sz, mem, out uint req);
- if (err == Win32Error.ERROR_INSUFFICIENT_BUFFER)
- {
- mem.Size = (int)sz;
- Marshal.WriteInt32(mem.DangerousGetHandle(), (int)info);
- err = GetVirtualDiskInformation(hVhd, ref sz, mem, out req);
- }
- Debug.WriteLineIf(err.Succeeded, $"GetVirtualDiskInformation: Id={info.ToString().Remove(0,22)}; Unk={Marshal.ReadInt32((IntPtr)mem, 4)}; Sz={req}; Bytes={string.Join(" ", mem.ToEnumerable((int)req/4).Select(b => b.ToString("X8")).ToArray())}");
- err.ThrowIfFailed();
-
- var ms = new MarshalingStream(mem.DangerousGetHandle(), mem.Size) { Position = 8 + offset };
- if (typeof(T) == typeof(string)) return (T)(object)System.Text.Encoding.Unicode.GetString(mem.ToArray((int)sz), 8 + (int)offset, (int)sz - 8 - (int)offset).TrimEnd('\0');
- return typeof(T) == typeof(bool) ? (T)Convert.ChangeType(ms.Read() != 0, typeof(bool)) : ms.Read();
- }
- }
-
#if !(NET20 || NET35 || NET40)
/// Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.
/// A valid file path that represents the path to the new virtual disk image file.
///
- /// A fully qualified path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a virtual
- /// disk or a physical disk.
+ /// A fully qualified path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a virtual disk or a
+ /// physical disk.
///
/// A cancellation token that can be used to cancel the operation. This value can be null to disable cancellation.
- /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
+ /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
/// If successful, returns a valid instance for the newly created virtual disk.
public async static Task CreateFromSource(string path, string sourcePath, CancellationToken cancellationToken, IProgress progress)
{
@@ -579,7 +570,9 @@ namespace Vanara.IO
/// Reduces the size of a virtual hard disk (VHD) backing store file.
/// A cancellation token that can be used to cancel the operation. This value can be null to disable cancellation.
- /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
+ /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
/// true if operation completed without error or cancellation; false otherwise.
public async Task Compact(CancellationToken cancellationToken, IProgress progress)
{
@@ -594,7 +587,9 @@ namespace Vanara.IO
/// Increases the size of a fixed or dynamic virtual hard disk (VHD).
/// New size, in bytes, for the expansion request.
/// A cancellation token that can be used to cancel the operation. This value can be null to disable cancellation.
- /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
+ /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
/// true if operation completed without error or cancellation; false otherwise.
public async Task Expand(ulong newSize, CancellationToken cancellationToken, IProgress progress)
{
@@ -610,7 +605,9 @@ namespace Vanara.IO
/// Resizes a virtual disk.
/// New size, in bytes, for the expansion request.
/// A cancellation token that can be used to cancel the operation. This value can be null to disable cancellation.
- /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
+ /// A class that implements that can be used to report on progress. This value can be null to disable progress reporting.
+ ///
/// true if operation completed without error or cancellation; false otherwise.
public async Task Resize(ulong newSize, CancellationToken cancellationToken, IProgress progress)
{
@@ -643,7 +640,8 @@ namespace Vanara.IO
break;
default:
- throw new Win32Exception((int)prog.OperationStatus);
+ new Win32Error((int)prog.OperationStatus).ThrowIfFailed();
+ break;
}
if (prog.CurrentValue == prog.CompletionValue) return true;
if (cancellationToken == null)
@@ -729,5 +727,177 @@ namespace Vanara.IO
void ReportProgress(int percent) { progress.Report(new Tuple(percent, $"Compacting VHD volume \"{loc}\"")); }
}*/
#endif
+
+ private T GetInformation(GET_VIRTUAL_DISK_INFO_VERSION info, long offset = 0)
+ {
+ var sz = 32U;
+ using (var mem = new SafeHGlobalHandle((int)sz))
+ {
+ Marshal.WriteInt32(mem.DangerousGetHandle(), (int)info);
+ var err = GetVirtualDiskInformation(hVhd, ref sz, mem, out uint req);
+ if (err == Win32Error.ERROR_INSUFFICIENT_BUFFER)
+ {
+ mem.Size = (int)sz;
+ Marshal.WriteInt32(mem.DangerousGetHandle(), (int)info);
+ err = GetVirtualDiskInformation(hVhd, ref sz, mem, out req);
+ }
+ Debug.WriteLineIf(err.Succeeded, $"GetVirtualDiskInformation: Id={info.ToString().Remove(0, 22)}; Unk={Marshal.ReadInt32((IntPtr)mem, 4)}; Sz={req}; Bytes={string.Join(" ", mem.ToEnumerable((int)req / 4).Select(b => b.ToString("X8")).ToArray())}");
+ err.ThrowIfFailed();
+
+ var ms = new MarshalingStream(mem.DangerousGetHandle(), mem.Size) { Position = 8 + offset };
+ if (typeof(T) == typeof(string)) return (T)(object)System.Text.Encoding.Unicode.GetString(mem.ToArray((int)sz), 8 + (int)offset, (int)sz - 8 - (int)offset).TrimEnd('\0');
+ return typeof(T) == typeof(bool) ? (T)Convert.ChangeType(ms.Read() != 0, typeof(bool)) : ms.Read();
+ }
+ }
+
+ /// Supports getting and setting metadata on a virtual disk.
+ ///
+ public class VirtualDiskMetadata : IDictionary
+ {
+ private VirtualDisk parent;
+ private bool supported;
+
+ /// Initializes a new instance of the class.
+ /// The VHD.
+ internal VirtualDiskMetadata(VirtualDisk vhd)
+ {
+ parent = vhd;
+ supported = vhd.DiskType == DeviceType.Vhdx;
+ }
+
+ /// Gets a value indicating whether the is read-only.
+ public bool IsReadOnly => false;
+
+ /// Gets an containing the keys of the .
+ public ICollection Keys
+ {
+ get
+ {
+ if (!supported) return new Guid[0];
+ if (parent.Handle.IsClosed) throw new InvalidOperationException("Virtual disk not valid.");
+ uint count = 0;
+ var err = EnumerateVirtualDiskMetadata(parent.Handle, ref count, IntPtr.Zero);
+ if (err != Win32Error.ERROR_MORE_DATA) err.ThrowIfFailed();
+ if (count == 0) return new Guid[0];
+ var mem = new SafeCoTaskMemHandle(Marshal.SizeOf(typeof(Guid)) * (int)count);
+ EnumerateVirtualDiskMetadata(parent.Handle, ref count, (IntPtr)mem).ThrowIfFailed();
+ return mem.ToArray((int)count);
+ }
+ }
+
+ /// Gets an containing the values in the .
+ public ICollection Values => Keys.Select(k => this[k]).ToList();
+
+ /// Gets the number of elements contained in the .
+ public int Count => Keys.Count();
+
+ /// Gets or sets the with the specified key.
+ /// The .
+ /// The key.
+ public SafeCoTaskMemHandle this[Guid key]
+ {
+ get
+ {
+ if (!supported) throw new PlatformNotSupportedException();
+ if (parent.Handle.IsClosed) throw new InvalidOperationException("Virtual disk not valid.");
+ uint sz = 0;
+ var err = GetVirtualDiskMetadata(parent.Handle, key, ref sz, SafeCoTaskMemHandle.Null);
+ if (err != Win32Error.ERROR_MORE_DATA) err.ThrowIfFailed();
+ var ret = new SafeCoTaskMemHandle((int)sz);
+ GetVirtualDiskMetadata(parent.Handle, key, ref sz, ret).ThrowIfFailed();
+ return ret;
+ }
+ set
+ {
+ if (!supported) throw new PlatformNotSupportedException();
+ if (parent.Handle.IsClosed) throw new InvalidOperationException("Virtual disk not valid.");
+ SetVirtualDiskMetadata(parent.Handle, key, (uint)value.Size, (IntPtr)value).ThrowIfFailed();
+ }
+ }
+
+ /// Adds an element with the provided key and value to the .
+ /// The object to use as the key of the element to add.
+ /// The object to use as the value of the element to add.
+ public void Add(Guid key, SafeCoTaskMemHandle value) => this[key] = value;
+
+ /// Adds an item to the .
+ /// The object to add to the .
+ void ICollection>.Add(KeyValuePair item) => Add(item.Key, item.Value);
+
+ /// Removes all items from the .
+ ///
+ void ICollection>.Clear() => throw new PlatformNotSupportedException();
+
+ /// Determines whether the contains a specific value.
+ /// The object to locate in the .
+ /// true if is found in the ; otherwise, false.
+ bool ICollection>.Contains(KeyValuePair item) => ContainsKey(item.Key) && this[item.Key].DangerousGetHandle() == item.Value.DangerousGetHandle();
+
+ /// Determines whether the contains an element with the specified key.
+ /// The key to locate in the .
+ /// true if the contains an element with the key; otherwise, false.
+ public bool ContainsKey(Guid key) => Keys.Contains(key);
+
+ ///
+ /// Copies the elements of the to an , starting at a particular
+ /// index.
+ ///
+ ///
+ /// The one-dimensional that is the destination of the elements copied from
+ /// . The must have zero-based indexing.
+ ///
+ /// The zero-based index in at which copying begins.
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ var a = GetEnum().ToArray();
+ Array.Copy(a, 0, array, arrayIndex, a.Length);
+ }
+
+ /// Returns an enumerator that iterates through the collection.
+ /// A that can be used to iterate through the collection.
+ public IEnumerator> GetEnumerator() => GetEnum().GetEnumerator();
+
+ /// Removes the element with the specified key from the .
+ /// The key of the element to remove.
+ ///
+ /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the
+ /// original .
+ ///
+ public bool Remove(Guid key)
+ {
+ if (!supported) throw new PlatformNotSupportedException();
+ if (parent.Handle.IsClosed) throw new InvalidOperationException("Virtual disk not valid.");
+ return DeleteVirtualDiskMetadata(parent.Handle, key).Succeeded;
+ }
+
+ /// Removes the first occurrence of a specific object from the .
+ /// The object to remove from the .
+ ///
+ /// true if was successfully removed from the ; otherwise, false. This
+ /// method also returns false if is not found in the original .
+ ///
+ bool ICollection>.Remove(KeyValuePair item) => Remove(item.Key);
+
+ /// Gets the value associated with the specified key.
+ /// The key whose value to get.
+ ///
+ /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the
+ /// parameter. This parameter is passed uninitialized.
+ ///
+ ///
+ /// true if the object that implements contains an element with the specified key; otherwise, false.
+ ///
+ public bool TryGetValue(Guid key, out SafeCoTaskMemHandle value)
+ {
+ try { value = this[key]; return true; }
+ catch { value = SafeCoTaskMemHandle.Null; return false; }
+ }
+
+ /// Returns an enumerator that iterates through a collection.
+ /// An object that can be used to iterate through the collection.
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ private IEnumerable> GetEnum() => Keys.Select(k => new KeyValuePair(k, this[k]));
+ }
}
}
\ No newline at end of file
diff --git a/UnitTests/System/VirtualDiskTests.cs b/UnitTests/System/VirtualDiskTests.cs
index 967e1da7..8b9636d8 100644
--- a/UnitTests/System/VirtualDiskTests.cs
+++ b/UnitTests/System/VirtualDiskTests.cs
@@ -226,6 +226,41 @@ namespace Vanara.IO.Tests
}
}
+ [Test]
+ public void GetSetMetadataTest()
+ {
+ const int sz = 0x03010200;
+ var lfn = tmpfn + "x";
+ try
+ {
+ using (var vhd = VirtualDisk.Create(lfn, sz))
+ {
+ vhd.Attach(true, true, false, GetWorldFullFileSecurity());
+
+ // Try enumerate and get
+ foreach (var mkv in vhd.Metadata)
+ {
+ Assert.That(mkv.Key, Is.Not.EqualTo(Guid.Empty));
+ Assert.That(mkv.Value.Size, Is.Not.Zero);
+ TestContext.WriteLine($"{mkv.Key}={mkv.Value.Size}b:{mkv.Value.ToString(-1)}");
+ }
+
+ // Try set and remove
+ var guid = Guid.NewGuid();
+ Assert.That(() => vhd.Metadata.Add(guid, new SafeCoTaskMemHandle("Testing")), Throws.Nothing);
+ Assert.That(vhd.Metadata.TryGetValue(Guid.NewGuid(), out SafeCoTaskMemHandle mem), Is.False);
+ Assert.That(vhd.Metadata.TryGetValue(guid, out mem), Is.True);
+ Assert.That(mem.ToString(-1), Is.EqualTo("Testing"));
+ Assert.That(vhd.Metadata.Remove(guid), Is.True);
+ Assert.That(vhd.Metadata.TryGetValue(guid, out mem), Is.False);
+ }
+ }
+ finally
+ {
+ System.IO.File.Delete(lfn);
+ }
+ }
+
//[Test()]
public void DetachTest()
{