diff options
Diffstat (limited to 'src/mscorlib/corefx/System/Security')
5 files changed, 919 insertions, 0 deletions
diff --git a/src/mscorlib/corefx/System/Security/CryptographicException.cs b/src/mscorlib/corefx/System/Security/CryptographicException.cs new file mode 100644 index 0000000000..89cb658aa9 --- /dev/null +++ b/src/mscorlib/corefx/System/Security/CryptographicException.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using System.Runtime.Serialization; + +namespace System.Security.Cryptography +{ + [Serializable] + public class CryptographicException : SystemException + { + public CryptographicException() + : base(SR.Arg_CryptographyException) + { + } + + public CryptographicException(int hr) + : base(SR.Arg_CryptographyException) + { + HResult = hr; + } + + public CryptographicException(string message) + : base(message) + { + } + + public CryptographicException(string message, Exception inner) + : base(message, inner) + { + } + + public CryptographicException(string format, string insert) + : base(string.Format(CultureInfo.CurrentCulture, format, insert)) + { + } + + protected CryptographicException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/mscorlib/corefx/System/Security/SafeBSTRHandle.cs b/src/mscorlib/corefx/System/Security/SafeBSTRHandle.cs new file mode 100644 index 0000000000..19d63d41e4 --- /dev/null +++ b/src/mscorlib/corefx/System/Security/SafeBSTRHandle.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Security +{ + internal sealed class SafeBSTRHandle : SafeBuffer + { + internal SafeBSTRHandle() : base(true) { } + + internal static SafeBSTRHandle Allocate(uint lenInChars) + { + uint lenInBytes = lenInChars * sizeof(char); + SafeBSTRHandle bstr = Interop.OleAut32.SysAllocStringLen(IntPtr.Zero, lenInChars); + if (bstr.IsInvalid) // SysAllocStringLen returns a NULL ptr when there's insufficient memory + { + throw new OutOfMemoryException(); + } + bstr.Initialize(lenInBytes); + return bstr; + } + + override protected bool ReleaseHandle() + { + Interop.NtDll.ZeroMemory(handle, (UIntPtr)(Interop.OleAut32.SysStringLen(handle) * sizeof(char))); + Interop.OleAut32.SysFreeString(handle); + return true; + } + + internal unsafe void ClearBuffer() + { + byte* bufferPtr = null; + try + { + AcquirePointer(ref bufferPtr); + Interop.NtDll.ZeroMemory((IntPtr)bufferPtr, (UIntPtr)(Interop.OleAut32.SysStringLen((IntPtr)bufferPtr) * sizeof(char))); + } + finally + { + if (bufferPtr != null) + { + ReleasePointer(); + } + } + } + + internal unsafe uint Length => Interop.OleAut32.SysStringLen(this); + + internal unsafe static void Copy(SafeBSTRHandle source, SafeBSTRHandle target, uint bytesToCopy) + { + if (bytesToCopy == 0) + { + return; + } + + byte* sourcePtr = null, targetPtr = null; + try + { + source.AcquirePointer(ref sourcePtr); + target.AcquirePointer(ref targetPtr); + + Debug.Assert(source.ByteLength >= bytesToCopy, "Source buffer is too small."); + Buffer.MemoryCopy(sourcePtr, targetPtr, target.ByteLength, bytesToCopy); + } + finally + { + if (targetPtr != null) + { + target.ReleasePointer(); + } + if (sourcePtr != null) + { + source.ReleasePointer(); + } + } + } + } +} diff --git a/src/mscorlib/corefx/System/Security/SecureString.Unix.cs b/src/mscorlib/corefx/System/Security/SecureString.Unix.cs new file mode 100644 index 0000000000..0ef38e40ee --- /dev/null +++ b/src/mscorlib/corefx/System/Security/SecureString.Unix.cs @@ -0,0 +1,295 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Security +{ + // SecureString attempts to provide a defense-in-depth solution. + // + // On Windows, this is done with several mechanisms: + // 1. keeping the data in unmanaged memory so that copies of it aren't implicitly made by the GC moving it around + // 2. zero'ing out that unmanaged memory so that the string is reliably removed from memory when done with it + // 3. encrypting the data while it's not being used (it's unencrypted to manipulate and use it) + // + // On Unix, we do 1 and 2, but we don't do 3 as there's no CryptProtectData equivalent. + + public sealed partial class SecureString + { + private UnmanagedBuffer _buffer; + + internal SecureString(SecureString str) + { + // Allocate enough space to store the provided string + EnsureCapacity(str._decryptedLength); + _decryptedLength = str._decryptedLength; + + // Copy the string into the newly allocated space + if (_decryptedLength > 0) + { + UnmanagedBuffer.Copy(str._buffer, _buffer, (ulong)(str._decryptedLength * sizeof(char))); + } + } + + private unsafe void InitializeSecureString(char* value, int length) + { + // Allocate enough space to store the provided string + EnsureCapacity(length); + _decryptedLength = length; + if (length == 0) + { + return; + } + + // Copy the string into the newly allocated space + byte* ptr = null; + try + { + _buffer.AcquirePointer(ref ptr); + Buffer.MemoryCopy(value, ptr, _buffer.ByteLength, (ulong)(length * sizeof(char))); + } + finally + { + if (ptr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private void DisposeCore() + { + if (_buffer != null && !_buffer.IsInvalid) + { + _buffer.Dispose(); + _buffer = null; + } + } + + private void EnsureNotDisposed() + { + if (_buffer == null) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + private void ClearCore() + { + _decryptedLength = 0; + _buffer.Clear(); + } + + private unsafe void AppendCharCore(char c) + { + // Make sure we have enough space for the new character, then write it at the end. + EnsureCapacity(_decryptedLength + 1); + _buffer.Write((ulong)(_decryptedLength * sizeof(char)), c); + _decryptedLength++; + } + + private unsafe void InsertAtCore(int index, char c) + { + // Make sure we have enough space for the new character, then shift all of the characters above it and insert it. + EnsureCapacity(_decryptedLength + 1); + byte* ptr = null; + try + { + _buffer.AcquirePointer(ref ptr); + ptr += index * sizeof(char); + long bytesToShift = (_decryptedLength - index) * sizeof(char); + Buffer.MemoryCopy(ptr, ptr + sizeof(char), bytesToShift, bytesToShift); + *((char*)ptr) = c; + ++_decryptedLength; + } + finally + { + if (ptr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private unsafe void RemoveAtCore(int index) + { + // Shift down all values above the specified index, then null out the empty space at the end. + byte* ptr = null; + try + { + _buffer.AcquirePointer(ref ptr); + ptr += index * sizeof(char); + long bytesToShift = (_decryptedLength - index - 1) * sizeof(char); + Buffer.MemoryCopy(ptr + sizeof(char), ptr, bytesToShift, bytesToShift); + *((char*)(ptr + bytesToShift)) = (char)0; + --_decryptedLength; + } + finally + { + if (ptr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private void SetAtCore(int index, char c) + { + // Overwrite the character at the specified index + _buffer.Write((ulong)(index * sizeof(char)), c); + } + + internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode) + { + int length = _decryptedLength; + + byte* bufferPtr = null; + IntPtr stringPtr = IntPtr.Zero, result = IntPtr.Zero; + try + { + _buffer.AcquirePointer(ref bufferPtr); + if (unicode) + { + int resultLength = (length + 1) * sizeof(char); + stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength); + Buffer.MemoryCopy( + source: bufferPtr, + destination: (byte*)stringPtr.ToPointer(), + destinationSizeInBytes: resultLength, + sourceBytesToCopy: length * sizeof(char)); + *(length + (char*)stringPtr) = '\0'; + } + else + { + int resultLength = Encoding.UTF8.GetByteCount((char*)bufferPtr, length) + 1; + stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength); + int encodedLength = Encoding.UTF8.GetBytes((char*)bufferPtr, length, (byte*)stringPtr, resultLength); + Debug.Assert(encodedLength + 1 == resultLength, $"Expected encoded length to match result, got {encodedLength} != {resultLength}"); + *(resultLength - 1 + (byte*)stringPtr) = 0; + } + + result = stringPtr; + } + finally + { + // If there was a failure, such that result isn't initialized, + // release the string if we had one. + if (stringPtr != IntPtr.Zero && result == IntPtr.Zero) + { + UnmanagedBuffer.ZeroMemory((byte*)stringPtr, (ulong)(length * sizeof(char))); + MarshalFree(stringPtr, globalAlloc); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + + return result; + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private void EnsureCapacity(int capacity) + { + // Make sure the requested capacity doesn't exceed SecureString's defined limit + if (capacity > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); + } + + // If we already have enough space allocated, we're done + if (_buffer != null && (capacity * sizeof(char)) <= (int)_buffer.ByteLength) + { + return; + } + + // We need more space, so allocate a new buffer, copy all our data into it, + // and then swap the new for the old. + UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(capacity * sizeof(char)); + if (_buffer != null) + { + UnmanagedBuffer.Copy(_buffer, newBuffer, _buffer.ByteLength); + _buffer.Dispose(); + } + _buffer = newBuffer; + } + + /// <summary>SafeBuffer for managing memory meant to be kept confidential.</summary> + private sealed class UnmanagedBuffer : SafeBuffer + { + internal UnmanagedBuffer() : base(true) { } + + internal static UnmanagedBuffer Allocate(int bytes) + { + Debug.Assert(bytes >= 0); + UnmanagedBuffer buffer = new UnmanagedBuffer(); + buffer.SetHandle(Marshal.AllocHGlobal(bytes)); + buffer.Initialize((ulong)bytes); + return buffer; + } + + internal unsafe void Clear() + { + byte* ptr = null; + try + { + AcquirePointer(ref ptr); + ZeroMemory(ptr, ByteLength); + } + finally + { + if (ptr != null) + { + ReleasePointer(); + } + } + } + + internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength) + { + if (bytesLength == 0) + { + return; + } + + byte* srcPtr = null, dstPtr = null; + try + { + source.AcquirePointer(ref srcPtr); + destination.AcquirePointer(ref dstPtr); + Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength); + } + finally + { + if (dstPtr != null) + { + destination.ReleasePointer(); + } + if (srcPtr != null) + { + source.ReleasePointer(); + } + } + } + + protected override unsafe bool ReleaseHandle() + { + Marshal.FreeHGlobal(handle); + return true; + } + + internal static unsafe void ZeroMemory(byte* ptr, ulong len) + { + for (ulong i = 0; i < len; i++) *ptr++ = 0; + } + } + + } +} diff --git a/src/mscorlib/corefx/System/Security/SecureString.Windows.cs b/src/mscorlib/corefx/System/Security/SecureString.Windows.cs new file mode 100644 index 0000000000..5f56353647 --- /dev/null +++ b/src/mscorlib/corefx/System/Security/SecureString.Windows.cs @@ -0,0 +1,310 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Microsoft.Win32; + +namespace System.Security +{ + public sealed partial class SecureString + { + internal SecureString(SecureString str) + { + Debug.Assert(str != null, "Expected non-null SecureString"); + Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null"); + Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings"); + + AllocateBuffer(str._buffer.Length); + SafeBSTRHandle.Copy(str._buffer, _buffer, str._buffer.Length * sizeof(char)); + + _decryptedLength = str._decryptedLength; + _encrypted = str._encrypted; + } + + private unsafe void InitializeSecureString(char* value, int length) + { + Debug.Assert(length >= 0, $"Expected non-negative length, got {length}"); + + AllocateBuffer((uint)length); + _decryptedLength = length; + + byte* bufferPtr = null; + try + { + _buffer.AcquirePointer(ref bufferPtr); + Buffer.MemoryCopy((byte*)value, bufferPtr, (long)_buffer.ByteLength, length * sizeof(char)); + } + finally + { + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + + ProtectMemory(); + } + + private void AppendCharCore(char c) + { + UnprotectMemory(); + try + { + EnsureCapacity(_decryptedLength + 1); + _buffer.Write<char>((uint)_decryptedLength * sizeof(char), c); + _decryptedLength++; + } + finally + { + ProtectMemory(); + } + } + + private void ClearCore() + { + _decryptedLength = 0; + _buffer.ClearBuffer(); + } + + private void DisposeCore() + { + if (_buffer != null) + { + _buffer.Dispose(); + _buffer = null; + } + } + + private unsafe void InsertAtCore(int index, char c) + { + byte* bufferPtr = null; + UnprotectMemory(); + try + { + EnsureCapacity(_decryptedLength + 1); + + _buffer.AcquirePointer(ref bufferPtr); + char* pBuffer = (char*)bufferPtr; + + for (int i = _decryptedLength; i > index; i--) + { + pBuffer[i] = pBuffer[i - 1]; + } + pBuffer[index] = c; + ++_decryptedLength; + } + finally + { + ProtectMemory(); + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private unsafe void RemoveAtCore(int index) + { + byte* bufferPtr = null; + UnprotectMemory(); + try + { + _buffer.AcquirePointer(ref bufferPtr); + char* pBuffer = (char*)bufferPtr; + + for (int i = index; i < _decryptedLength - 1; i++) + { + pBuffer[i] = pBuffer[i + 1]; + } + pBuffer[--_decryptedLength] = (char)0; + } + finally + { + ProtectMemory(); + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private void SetAtCore(int index, char c) + { + UnprotectMemory(); + try + { + _buffer.Write<char>((uint)index * sizeof(char), c); + } + finally + { + ProtectMemory(); + } + } + + internal unsafe IntPtr MarshalToBSTR() + { + int length = _decryptedLength; + IntPtr ptr = IntPtr.Zero; + IntPtr result = IntPtr.Zero; + byte* bufferPtr = null; + + UnprotectMemory(); + try + { + _buffer.AcquirePointer(ref bufferPtr); + int resultByteLength = (length + 1) * sizeof(char); + + ptr = Win32Native.SysAllocStringLen(null, length); + if (ptr == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + + Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); + + result = ptr; + } + finally + { + ProtectMemory(); + + // If we failed for any reason, free the new buffer + if (result == IntPtr.Zero && ptr != IntPtr.Zero) + { + Interop.NtDll.ZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); + Win32Native.SysFreeString(ptr); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + return result; + } + + internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode) + { + int length = _decryptedLength; + IntPtr ptr = IntPtr.Zero; + IntPtr result = IntPtr.Zero; + byte* bufferPtr = null; + + UnprotectMemory(); + try + { + _buffer.AcquirePointer(ref bufferPtr); + if (unicode) + { + int resultByteLength = (length + 1) * sizeof(char); + ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength); + Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); + *(length + (char*)ptr) = '\0'; + } + else + { + uint defaultChar = '?'; + int resultByteLength = 1 + Interop.mincore.WideCharToMultiByte( + Interop.mincore.CP_ACP, Interop.mincore.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, null, 0, (IntPtr)(&defaultChar), IntPtr.Zero); + ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength); + Interop.mincore.WideCharToMultiByte( + Interop.mincore.CP_ACP, Interop.mincore.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, (byte*)ptr, resultByteLength - 1, (IntPtr)(&defaultChar), IntPtr.Zero); + *(resultByteLength - 1 + (byte*)ptr) = 0; + } + result = ptr; + } + finally + { + ProtectMemory(); + + // If we failed for any reason, free the new buffer + if (result == IntPtr.Zero && ptr != IntPtr.Zero) + { + Interop.NtDll.ZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); + MarshalFree(ptr, globalAlloc); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + return result; + } + + private void EnsureNotDisposed() + { + if (_buffer == null) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private const int BlockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE / sizeof(char); + private SafeBSTRHandle _buffer; + private bool _encrypted; + + private void AllocateBuffer(uint size) + { + _buffer = SafeBSTRHandle.Allocate(GetAlignedSize(size)); + } + + private static uint GetAlignedSize(uint size) => + size == 0 || size % BlockSize != 0 ? + BlockSize + ((size / BlockSize) * BlockSize) : + size; + + private void EnsureCapacity(int capacity) + { + if (capacity > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); + } + + if (((uint)capacity * sizeof(char)) <= _buffer.ByteLength) + { + return; + } + + var oldBuffer = _buffer; + SafeBSTRHandle newBuffer = SafeBSTRHandle.Allocate(GetAlignedSize((uint)capacity)); + SafeBSTRHandle.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char)); + _buffer = newBuffer; + oldBuffer.Dispose(); + } + + private void ProtectMemory() + { + Debug.Assert(!_buffer.IsInvalid, "Invalid buffer!"); + + if (_decryptedLength != 0 && + !_encrypted && + !Interop.Crypt32.CryptProtectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + throw new CryptographicException(Marshal.GetLastWin32Error()); + } + + _encrypted = true; + } + + private void UnprotectMemory() + { + Debug.Assert(!_buffer.IsInvalid, "Invalid buffer!"); + + if (_decryptedLength != 0 && + _encrypted && + !Interop.Crypt32.CryptUnprotectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + throw new CryptographicException(Marshal.GetLastWin32Error()); + } + + _encrypted = false; + } + } +} diff --git a/src/mscorlib/corefx/System/Security/SecureString.cs b/src/mscorlib/corefx/System/Security/SecureString.cs new file mode 100644 index 0000000000..9059f90e60 --- /dev/null +++ b/src/mscorlib/corefx/System/Security/SecureString.cs @@ -0,0 +1,189 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Security +{ + public sealed partial class SecureString : IDisposable + { + private const int MaxLength = 65536; + private readonly object _methodLock = new object(); + private bool _readOnly; + private int _decryptedLength; + + public unsafe SecureString() + { + InitializeSecureString(null, 0); + } + + [CLSCompliant(false)] + public unsafe SecureString(char* value, int length) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (length > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Length); + } + + InitializeSecureString(value, length); + } + + public int Length + { + get + { + lock (_methodLock) + { + EnsureNotDisposed(); + return _decryptedLength; + } + } + } + + public void AppendChar(char c) + { + lock (_methodLock) + { + EnsureNotDisposed(); + EnsureNotReadOnly(); + AppendCharCore(c); + } + } + + // clears the current contents. Only available if writable + public void Clear() + { + lock (_methodLock) + { + EnsureNotDisposed(); + EnsureNotReadOnly(); + ClearCore(); + } + } + + // Do a deep-copy of the SecureString + public SecureString Copy() + { + lock (_methodLock) + { + EnsureNotDisposed(); + return new SecureString(this); + } + } + + public void Dispose() + { + lock (_methodLock) + { + DisposeCore(); + } + } + + public void InsertAt(int index, char c) + { + lock (_methodLock) + { + if (index < 0 || index > _decryptedLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); + } + + EnsureNotDisposed(); + EnsureNotReadOnly(); + + InsertAtCore(index, c); + } + } + + public bool IsReadOnly() + { + lock (_methodLock) + { + EnsureNotDisposed(); + return _readOnly; + } + } + + public void MakeReadOnly() + { + lock (_methodLock) + { + EnsureNotDisposed(); + _readOnly = true; + } + } + + public void RemoveAt(int index) + { + lock (_methodLock) + { + EnsureNotDisposed(); + EnsureNotReadOnly(); + + if (index < 0 || index >= _decryptedLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); + } + + RemoveAtCore(index); + } + } + + public void SetAt(int index, char c) + { + lock (_methodLock) + { + if (index < 0 || index >= _decryptedLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); + } + Debug.Assert(index <= Int32.MaxValue / sizeof(char)); + + EnsureNotDisposed(); + EnsureNotReadOnly(); + + SetAtCore(index, c); + } + } + + private void EnsureNotReadOnly() + { + if (_readOnly) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + } + + internal unsafe IntPtr MarshalToString(bool globalAlloc, bool unicode) + { + lock (_methodLock) + { + EnsureNotDisposed(); + return MarshalToStringCore(globalAlloc, unicode); + } + } + + private static void MarshalFree(IntPtr ptr, bool globalAlloc) + { + if (globalAlloc) + { + Marshal.FreeHGlobal(ptr); + } + else + { + Marshal.FreeCoTaskMem(ptr); + } + } + } +} |