* Derived SafeCoTaskMemString from base class SafeMemString derived from SafeMemHandle<T>.

* BREAKING CHANGE: Removed CharCapacity and made Capacity show char allocation. Size (from parent) shows byte capacity. Mimics StringBuilder.
pull/60/head
David Hall 2019-06-25 17:10:57 -06:00
parent f4110f89d5
commit 7ce7b96209
5 changed files with 129 additions and 66 deletions

View File

@ -11,13 +11,14 @@ namespace Vanara.Extensions
{
/// <summary>Allocates a block of memory allocated from the unmanaged COM task allocator sufficient to hold the number of specified characters.</summary>
/// <param name="count">The number of characters, inclusive of the null terminator.</param>
/// <param name="memAllocator">The method used to allocate the memory.</param>
/// <param name="charSet">The character set.</param>
/// <returns>The address of the block of memory allocated.</returns>
public static IntPtr AllocChars(uint count, CharSet charSet = CharSet.Auto)
public static IntPtr AllocChars(uint count, Func<int, IntPtr> memAllocator, CharSet charSet = CharSet.Auto)
{
if (count == 0) return IntPtr.Zero;
var sz = GetCharSize(charSet);
var ptr = Marshal.AllocCoTaskMem((int)count * sz);
var ptr = memAllocator((int)count * sz);
if (count > 0)
{
if (sz == 1)
@ -28,6 +29,12 @@ namespace Vanara.Extensions
return ptr;
}
/// <summary>Allocates a block of memory allocated from the unmanaged COM task allocator sufficient to hold the number of specified characters.</summary>
/// <param name="count">The number of characters, inclusive of the null terminator.</param>
/// <param name="charSet">The character set.</param>
/// <returns>The address of the block of memory allocated.</returns>
public static IntPtr AllocChars(uint count, CharSet charSet = CharSet.Auto) => AllocChars(count, Marshal.AllocCoTaskMem, charSet);
/// <summary>Copies the contents of a managed <see cref="SecureString"/> object to a block of memory allocated from the unmanaged COM task allocator.</summary>
/// <param name="s">The managed object to copy.</param>
/// <param name="charSet">The character set.</param>
@ -45,10 +52,19 @@ namespace Vanara.Extensions
/// <param name="charSet">The character set.</param>
/// <param name="memAllocator">The method used to allocate the memory.</param>
/// <returns>The address, in unmanaged memory, where the <paramref name="s"/> parameter was copied to, or 0 if a null object was supplied.</returns>
public static IntPtr AllocSecureString(SecureString s, CharSet charSet, Func<int, IntPtr> memAllocator)
public static IntPtr AllocSecureString(SecureString s, CharSet charSet, Func<int, IntPtr> memAllocator) => AllocSecureString(s, charSet, memAllocator, out _);
/// <summary>Copies the contents of a managed <see cref="SecureString"/> object to a block of memory allocated from a supplied allocation method.</summary>
/// <param name="s">The managed object to copy.</param>
/// <param name="charSet">The character set.</param>
/// <param name="memAllocator">The method used to allocate the memory.</param>
/// <param name="allocatedBytes">Returns the number of allocated bytes for the string.</param>
/// <returns>The address, in unmanaged memory, where the <paramref name="s"/> parameter was copied to, or 0 if a null object was supplied.</returns>
public static IntPtr AllocSecureString(SecureString s, CharSet charSet, Func<int, IntPtr> memAllocator, out int allocatedBytes)
{
allocatedBytes = 0;
if (s == null) return IntPtr.Zero;
var chSz = StringHelper.GetCharSize(charSet);
var chSz = GetCharSize(charSet);
var encoding = chSz == 2 ? System.Text.Encoding.Unicode : System.Text.Encoding.ASCII;
var hMem = AllocSecureString(s, charSet);
var str = chSz == 2 ? Marshal.PtrToStringUni(hMem) : Marshal.PtrToStringAnsi(hMem);
@ -57,6 +73,7 @@ namespace Vanara.Extensions
var b = encoding.GetBytes(str);
var p = memAllocator(b.Length);
Marshal.Copy(b, 0, p, b.Length);
allocatedBytes = b.Length;
return p;
}

View File

@ -1,96 +1,53 @@
using Microsoft.Win32.SafeHandles;
using System;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.Extensions;
namespace Vanara.InteropServices
{
/// <summary>Safely handles an unmanaged memory allocated Unicode string.</summary>
/// <seealso cref="Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid"/>
public class SafeCoTaskMemString : SafeHandleZeroOrMinusOneIsInvalid
public class SafeCoTaskMemString : SafeMemString<CoTaskMemoryMethods>
{
private readonly CharSet chSet = CharSet.Unicode;
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemString"/> class.</summary>
/// <param name="s">The string value.</param>
/// <param name="charSet">The character set.</param>
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during finalization; <c>false</c> to prevent it.</param>
public SafeCoTaskMemString(string s, CharSet charSet = CharSet.Unicode, bool ownsHandle = true) : this(StringHelper.AllocString(s, charSet), charSet, ownsHandle)
public SafeCoTaskMemString(string s, CharSet charSet = CharSet.Unicode) : base(s, charSet)
{
}
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemString"/> class.</summary>
/// <param name="s">The string value.</param>
/// <param name="capacity">The size of the buffer in characters.</param>
/// <param name="charSet">The character set.</param>
public SafeCoTaskMemString(string s, int capacity, CharSet charSet = CharSet.Unicode) : base(s, capacity, charSet)
{
Capacity = StringHelper.GetByteCount(s, true, charSet);
}
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemString"/> class.</summary>
/// <param name="s">The string value.</param>
/// <param name="charSet">The character set.</param>
public SafeCoTaskMemString(SecureString s, CharSet charSet = CharSet.Unicode) : this(StringHelper.AllocSecureString(s, charSet), charSet)
public SafeCoTaskMemString(SecureString s, CharSet charSet = CharSet.Unicode) : base(s, charSet)
{
Capacity = s == null ? 0 : StringHelper.GetCharSize(charSet) * (s.Length + 1);
}
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemString"/> class.</summary>
/// <param name="charLen">The size of the buffer in characters, including the null character terminator.</param>
/// <param name="charSet">The character set.</param>
public SafeCoTaskMemString(int charLen, CharSet charSet = CharSet.Unicode) : this(StringHelper.AllocChars((uint)charLen, charSet), charSet)
public SafeCoTaskMemString(int charLen, CharSet charSet = CharSet.Unicode) : base(charLen, charSet)
{
Capacity = charLen * StringHelper.GetCharSize(charSet);
}
/// <summary>Prevents a default instance of the <see cref="SafeCoTaskMemString"/> class from being created.</summary>
[ExcludeFromCodeCoverage]
private SafeCoTaskMemString() : base(true) { }
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemString"/> class.</summary>
/// <param name="ptr">The PTR.</param>
/// <param name="charSet">The character set.</param>
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during finalization; <c>false</c> to prevent it.</param>
[ExcludeFromCodeCoverage]
private SafeCoTaskMemString(IntPtr ptr, CharSet charSet = CharSet.Unicode, bool ownsHandle = true) : base(ownsHandle)
private SafeCoTaskMemString(IntPtr ptr, CharSet charSet = CharSet.Unicode, bool ownsHandle = true) : base(ptr, charSet, ownsHandle)
{
chSet = charSet;
SetHandle(ptr);
}
/// <summary>Represents a <c>null</c> value. Used primarily for comparrison.</summary>
/// <value>A null value.</value>
public static SafeCoTaskMemString Null => new SafeCoTaskMemString(IntPtr.Zero, CharSet.Unicode, false);
/// <summary>Gets the number of allocated bytes or -1 if the size is unknown (for example if it is holding a <see cref="SecureString"/>.</summary>
/// <value>The number of allocated bytes.</value>
public int Capacity { get; } = -1;
/// <summary>Gets the number of allocated characters or -1 if the size is unknown (for example if it is holding a <see cref="SecureString"/>.</summary>
/// <value>The number of characters bytes.</value>
public int CharCapacity => Capacity == -1 ? -1 : Capacity / StringHelper.GetCharSize(chSet);
/// <summary>Returns the value of the <see cref="SafeHandle.handle"/> field.</summary>
/// <param name="s">The <see cref="SafeCoTaskMemString"/> instance.</param>
/// <returns>
/// An <see cref="IntPtr"/> representing the value of the handle field. If the handle has been marked invalid with
/// <see cref="SafeHandle.SetHandleAsInvalid"/>, this method still returns the original handle value, which can be a stale value.
/// </returns>
public static explicit operator IntPtr(SafeCoTaskMemString s) => s.DangerousGetHandle();
/// <summary>Returns the string value held by a <see cref="SafeCoTaskMemString"/>.</summary>
/// <param name="s">The <see cref="SafeCoTaskMemString"/> instance.</param>
/// <returns>A <see cref="System.String"/> value held by the <see cref="SafeCoTaskMemString"/> or <c>null</c> if the handle or value is invalid.</returns>
public static implicit operator string(SafeCoTaskMemString s) => s?.ToString();
/// <summary>Returns the string value held by this instance.</summary>
/// <returns>A <see cref="System.String"/> value held by this instance or <c>null</c> if the handle is invalid.</returns>
public override string ToString() => IsInvalid ? null : StringHelper.GetString(handle, chSet);
/// <summary>When overridden in a derived class, executes the code required to free the handle.</summary>
/// <returns>
/// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a
/// releaseHandleFailed MDA Managed Debugging Assistant.
/// </returns>
protected override bool ReleaseHandle()
{
StringHelper.FreeString(handle, chSet);
return true;
}
}
}

View File

@ -0,0 +1,89 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.Extensions;
namespace Vanara.InteropServices
{
/// <summary>Base abstract class for a string handler based on <see cref="SafeMemoryHandle{TMem}"/>.</summary>
/// <typeparam name="TMem">The type of the memory.</typeparam>
/// <seealso cref="Vanara.InteropServices.SafeMemoryHandle{TMem}"/>
public abstract class SafeMemString<TMem> : SafeMemoryHandle<TMem> where TMem : IMemoryMethods, new()
{
private readonly CharSet chSet = CharSet.Unicode;
/// <summary>Initializes a new instance of the <see cref="SafeMemString{TMem}"/> class.</summary>
/// <param name="s">The string value.</param>
/// <param name="charSet">The character set.</param>
protected SafeMemString(string s, CharSet charSet = CharSet.Unicode) : this(s, s is null ? 0 : s.Length + 1, charSet)
{
}
/// <summary>Initializes a new instance of the <see cref="SafeMemString{TMem}"/> class.</summary>
/// <param name="s">The string value.</param>
/// <param name="capacity">The capacity of the buffer, in characters.</param>
/// <param name="charSet">The character set.</param>
protected SafeMemString(string s, int capacity, CharSet charSet = CharSet.Unicode) : this(capacity, charSet)
{
StringHelper.Write(s, handle, out _, true, charSet, Size);
}
/// <summary>Initializes a new instance of the <see cref="SafeMemString{TMem}"/> class.</summary>
/// <param name="s">The string value.</param>
/// <param name="charSet">The character set.</param>
protected SafeMemString(SecureString s, CharSet charSet = CharSet.Unicode) : this(IntPtr.Zero, charSet)
{
SetHandle(StringHelper.AllocSecureString(s, charSet, mm.AllocMem, out var sz));
base.sz = sz;
}
/// <summary>Initializes a new instance of the <see cref="SafeMemString{TMem}"/> class.</summary>
/// <param name="charLen">The size of the buffer in characters, including the null character terminator.</param>
/// <param name="charSet">The character set.</param>
protected SafeMemString(int charLen, CharSet charSet = CharSet.Unicode) : base(charLen * StringHelper.GetCharSize(charSet))
{
chSet = charSet;
}
/// <summary>Prevents a default instance of the <see cref="SafeMemString{TMem}"/> class from being created.</summary>
[ExcludeFromCodeCoverage]
protected SafeMemString() : base(0) { }
/// <summary>Initializes a new instance of the <see cref="SafeMemString{TMem}"/> class.</summary>
/// <param name="ptr">The PTR.</param>
/// <param name="charSet">The character set.</param>
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during finalization; <c>false</c> to prevent it.</param>
[ExcludeFromCodeCoverage]
protected SafeMemString(IntPtr ptr, CharSet charSet = CharSet.Unicode, bool ownsHandle = true) : base(ptr, 0, ownsHandle)
{
chSet = charSet;
}
/// <summary>Gets the number of allocated characters or 0 if the size is unknown (for example if it is holding a <see cref="SecureString"/>.</summary>
/// <value>The number of allocated characters.</value>
public int Capacity => Size / StringHelper.GetCharSize(chSet);
/// <summary>Gets the number of characters in the current <see cref="SafeMemString{TMem}"/> object.</summary>
public int Length => ToString().Length;
/// <summary>Returns the value of the <see cref="SafeHandle.handle"/> field.</summary>
/// <param name="s">The <see cref="SafeMemString{TMem}"/> instance.</param>
/// <returns>
/// An <see cref="IntPtr"/> representing the value of the handle field. If the handle has been marked invalid with
/// <see cref="SafeHandle.SetHandleAsInvalid"/>, this method still returns the original handle value, which can be a stale value.
/// </returns>
public static explicit operator IntPtr(SafeMemString<TMem> s) => s.DangerousGetHandle();
/// <summary>Returns the string value held by a <see cref="SafeMemString{TMem}"/>.</summary>
/// <param name="s">The <see cref="SafeMemString{TMem}"/> instance.</param>
/// <returns>
/// A <see cref="System.String"/> value held by the <see cref="SafeMemString{TMem}"/> or <c>null</c> if the handle or value is invalid.
/// </returns>
public static implicit operator string(SafeMemString<TMem> s) => s?.ToString();
/// <summary>Returns the string value held by this instance.</summary>
/// <returns>A <see cref="System.String"/> value held by this instance or <c>null</c> if the handle is invalid.</returns>
public override string ToString() => IsInvalid ? null : StringHelper.GetString(handle, chSet, Size);
}
}

View File

@ -69,9 +69,9 @@ namespace Vanara.Security
var pUserName = new SafeCoTaskMemString(CRED_MAX_USERNAME_LENGTH);
var pDomainName = new SafeCoTaskMemString(CRED_MAX_USERNAME_LENGTH);
var pPassword = new SafeCoTaskMemString(CREDUI_MAX_PASSWORD_LENGTH);
var userNameSize = pUserName.CharCapacity;
var domainNameSize = pDomainName.CharCapacity;
var passwordSize = pPassword.CharCapacity;
var userNameSize = pUserName.Capacity;
var domainNameSize = pDomainName.Capacity;
var passwordSize = pPassword.Capacity;
if (!CredUnPackAuthenticationBuffer(decryptProtectedCredentials ? CredPackFlags.CRED_PACK_PROTECTED_CREDENTIALS : 0x0, DangerousHandle, Size,
(IntPtr)pUserName, ref userNameSize, (IntPtr)pDomainName, ref domainNameSize, (IntPtr)pPassword, ref passwordSize))

View File

@ -849,12 +849,12 @@ namespace Vanara.Windows.Shell
var result = new StringBuilder(512);
if (!SHGetPathFromIDList(PIDL, result))
throw new ArgumentException();
return new SafeCoTaskMemString(result.ToString(), CharSet.Unicode, false);
return new SafeCoTaskMemString(result.ToString(), CharSet.Unicode);
}
var parentFolder = InternalGetParent().GetIShellFolder();
var child = PIDL.LastId;
return new SafeCoTaskMemString(parentFolder.GetDisplayNameOf(child, (SHGDNF)((int)sigdnName & 0xffff)), CharSet.Unicode, false);
return new SafeCoTaskMemString(parentFolder.GetDisplayNameOf(child, (SHGDNF)((int)sigdnName & 0xffff)), CharSet.Unicode);
}
public IShellItem GetParent()