Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ private static bool TryGetDataFromHGLOBAL<T>(

object? value = request.Format switch
{
DataFormatNames.Text or DataFormatNames.Rtf or DataFormatNames.OemText =>
ReadStringFromHGLOBAL(hglobal, unicode: false),
DataFormatNames.Text or DataFormatNames.OemText => ReadStringFromHGLOBAL(hglobal, unicode: false),
DataFormatNames.Rtf => ReadRegisteredFormatStringFromHGLOBAL(hglobal, Encoding.Default),
DataFormatNames.Html or DataFormatNames.Xaml => ReadUtf8StringFromHGLOBAL(hglobal),
DataFormatNames.UnicodeText => ReadStringFromHGLOBAL(hglobal, unicode: true),
DataFormatNames.FileDrop => ReadFileListFromHDROP((HDROP)(nint)hglobal),
Comment thread
SimonZhao888 marked this conversation as resolved.
Expand Down Expand Up @@ -197,10 +197,6 @@ private static unsafe string ReadStringFromHGLOBAL(HGLOBAL hglobal, bool unicode
//
// CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT, and CFSTR_FILENAME are supposed to have a null terminator.
// If we cannot find one in the buffer, assume it is corrupted and return an empty string.
//
// Can't find the explicit docs for CF_RTF, but we've always treated it as null terminated.
// The RichText control itself null terminates but looks like it doesn't require it.
// Given our prior and "normal" behavior, we'll continue to expect a null terminator.

try
{
Expand Down Expand Up @@ -242,6 +238,40 @@ private static unsafe string ReadStringFromHGLOBAL(HGLOBAL hglobal, bool unicode
}
}

private static unsafe string ReadRegisteredFormatStringFromHGLOBAL(HGLOBAL hglobal, Encoding encoding)
{
void* buffer = PInvokeCore.GlobalLock(hglobal);
if (buffer is null)
{
throw new Win32Exception();
}

try
{
int size = checked((int)PInvokeCore.GlobalSize(hglobal));
if (size == 0)
{
throw new Win32Exception();
}

ReadOnlySpan<byte> bytes = new((byte*)buffer, size);

// Registered format strings may be null-terminated, but the terminator is optional.
// If present, stop at the first null byte rather than decoding the entire allocation.
int nullIndex = bytes.IndexOf((byte)0);
if (nullIndex >= 0)
{
bytes = bytes[..nullIndex];
}

return bytes.IsEmpty ? string.Empty : encoding.GetString(bytes);
}
finally
{
PInvokeCore.GlobalUnlock(hglobal);
}
}

private static string ReadUtf8StringFromHGLOBAL(HGLOBAL hglobal)
{
void* buffer = PInvokeCore.GlobalLock(hglobal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.ComponentModel;
using System.Formats.Nrbf;
using System.Private.Windows.BinaryFormat;

using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
Expand All @@ -19,6 +20,8 @@
System.Private.Windows.Ole.TestFormat>;
using DataFormats = System.Private.Windows.Ole.DataFormatsCore<System.Private.Windows.Ole.TestFormat>;

using System.Text;

namespace System.Private.Windows.Ole;

public unsafe class NativeToManagedAdapterTests
Expand Down Expand Up @@ -99,6 +102,19 @@ public void GetData_CustomType_BinaryFormattedData_AsSerializationRecord()
data!.TypeName.AssemblyQualifiedName.Should().Be("System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
}

[Fact]
public void GetData_RtfWithoutNullTerminator_ReturnsRtfText()
{
const string rtf = "{\\rtf1\\ansi Test}";
MemoryStream stream = new(Encoding.Default.GetBytes(rtf));
using HGlobalNativeDataObject dataObject = new(stream, (ushort)DataFormats.GetOrAddFormat(DataFormatNames.Rtf).Id);
Comment thread
SimonZhao888 marked this conversation as resolved.

var composition = Composition.Create(ComHelpers.GetComPointer<IDataObject>(dataObject));
object? data = composition.GetData(DataFormatNames.Rtf);

data.Should().Be(rtf);
}

#if NET
[Fact]
public void GetData_CustomType_BinaryFormattedJson_AsSerializationRecord()
Expand Down