diff --git a/Core/Extensions/StringHelper.cs b/Core/Extensions/StringHelper.cs
index d3962c80..b30acf1e 100644
--- a/Core/Extensions/StringHelper.cs
+++ b/Core/Extensions/StringHelper.cs
@@ -70,12 +70,22 @@ namespace Vanara.Extensions
/// The character set.
/// The method used to allocate the memory.
/// The allocated memory block, or 0 if is null.
- public static IntPtr AllocString(string s, CharSet charSet, Func memAllocator)
+ public static IntPtr AllocString(string s, CharSet charSet, Func memAllocator) => AllocString(s, charSet, memAllocator, out _);
+
+ ///
+ /// Copies the contents of a managed String to a block of memory allocated from a supplied allocation method.
+ ///
+ /// A managed string to be copied.
+ /// The character set.
+ /// The method used to allocate the memory.
+ /// Returns the number of allocated bytes for the string.
+ /// The allocated memory block, or 0 if is null.
+ public static IntPtr AllocString(string s, CharSet charSet, Func memAllocator, out int allocatedBytes)
{
- if (s == null) return IntPtr.Zero;
+ if (s == null) { allocatedBytes = 0; return IntPtr.Zero; }
var b = s.GetBytes(true, charSet);
var p = memAllocator(b.Length);
- Marshal.Copy(b, 0, p, b.Length);
+ Marshal.Copy(b, 0, p, allocatedBytes = b.Length);
return p;
}
@@ -139,11 +149,36 @@ namespace Vanara.Extensions
/// The size of a standard character, in bytes, from .
public static int GetCharSize(CharSet charSet = CharSet.Auto) => charSet == CharSet.Auto ? Marshal.SystemDefaultCharSize : (charSet == CharSet.Unicode ? 2 : 1);
- /// Allocates a managed String and copies all characters up to the first null character from a string stored in unmanaged memory into it.
+ ///
+ /// Allocates a managed String and copies all characters up to the first null character or the end of the allocated memory pool from a string stored in unmanaged memory into it.
+ ///
/// The address of the first character.
/// The character set of the string.
- /// A managed string that holds a copy of the unmanaged string if the value of the parameter is not null; otherwise, this method returns null.
- public static string GetString(IntPtr ptr, CharSet charSet = CharSet.Auto) => IsValue(ptr) ? null : (charSet == CharSet.Auto ? Marshal.PtrToStringAuto(ptr) : (charSet == CharSet.Unicode ? Marshal.PtrToStringUni(ptr) : Marshal.PtrToStringAnsi(ptr)));
+ /// If known, the total number of bytes allocated to the native memory in .
+ ///
+ /// A managed string that holds a copy of the unmanaged string if the value of the parameter is not null;
+ /// otherwise, this method returns null.
+ ///
+ public static string GetString(IntPtr ptr, CharSet charSet = CharSet.Auto, long allocatedBytes = long.MaxValue)
+ {
+ if (IsValue(ptr)) return null;
+ var sb = new System.Text.StringBuilder();
+ unsafe
+ {
+ var chkLen = 0L;
+ if (GetCharSize(charSet) == 1)
+ {
+ for (var uptr = (byte*)ptr; chkLen < allocatedBytes && *uptr != 0; chkLen++, uptr++)
+ sb.Append((char)*uptr);
+ }
+ else
+ {
+ for (var uptr = (ushort*)ptr; chkLen < allocatedBytes && *uptr != 0; chkLen += 2, uptr++)
+ sb.Append((char)*uptr);
+ }
+ }
+ return sb.ToString();
+ }
///
/// Allocates a managed String and copies all characters up to the first null character or at most characters from a string stored in unmanaged memory into it.
@@ -155,8 +190,7 @@ namespace Vanara.Extensions
/// A managed string that holds a copy of the unmanaged string if the value of the parameter is not null;
/// otherwise, this method returns null.
///
- public static string GetString(IntPtr ptr, int length, CharSet charSet = CharSet.Auto) =>
- IsValue(ptr) ? null : (charSet == CharSet.Auto ? Marshal.PtrToStringAuto(ptr, length) : (charSet == CharSet.Unicode ? Marshal.PtrToStringUni(ptr, length) : Marshal.PtrToStringAnsi(ptr, length)));
+ public static string GetString(IntPtr ptr, int length, CharSet charSet = CharSet.Auto) => GetString(ptr, charSet, length * GetCharSize(charSet));
/// Refreshes the memory block from the unmanaged COM task allocator and copies the contents of a new managed String.
/// The address of the first character.
diff --git a/UnitTests/Core/Extensions/StringHelperTests.cs b/UnitTests/Core/Extensions/StringHelperTests.cs
index 9d5b7d3c..bbc6b22f 100644
--- a/UnitTests/Core/Extensions/StringHelperTests.cs
+++ b/UnitTests/Core/Extensions/StringHelperTests.cs
@@ -87,5 +87,28 @@ namespace Vanara.Extensions.Tests
{
Assert.That(StringHelper.GetByteCount(value, nullTerm, cs), Is.EqualTo(ret));
}
+
+ [TestCase("BOO", CharSet.Ansi)]
+ [TestCase("BOO", CharSet.Unicode)]
+ [TestCase("", CharSet.Ansi)]
+ [TestCase("", CharSet.Unicode)]
+ [TestCase(null, CharSet.Ansi)]
+ [TestCase(null, CharSet.Unicode)]
+ public void GetStringTest1(string value, CharSet cs)
+ {
+ IntPtr ptr = default;
+ try
+ {
+ ptr = StringHelper.AllocString(value, cs, Marshal.AllocCoTaskMem, out var count);
+ Assert.That(count, Is.EqualTo((value?.Length + 1) * StringHelper.GetCharSize(cs) ?? 0));
+ Assert.That(StringHelper.GetString(ptr, cs), Is.EqualTo(value));
+ Assert.That(StringHelper.GetString(ptr, cs, count * 2), Is.EqualTo(value));
+ Assert.That(StringHelper.GetString(ptr, cs, count / 2), Is.EqualTo(string.IsNullOrEmpty(value) ? value : value.Substring(0, (value.Length + 1) / 2)));
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(ptr);
+ }
+ }
}
}
\ No newline at end of file