diff --git a/PInvoke/Shell32/Clipboard.cs b/PInvoke/Shell32/Clipboard.cs index 3d1a1c9e..e278781b 100644 --- a/PInvoke/Shell32/Clipboard.cs +++ b/PInvoke/Shell32/Clipboard.cs @@ -753,7 +753,7 @@ namespace Vanara.PInvoke return charSet; } - private static Encoding GetEncoding(ClipCorrespondingTypeAttribute attr) => (Encoding)Activator.CreateInstance(attr.EncodingType ?? typeof(UnicodeEncoding)); + private static Encoding GetEncoding(ClipCorrespondingTypeAttribute attr) => (Encoding)Activator.CreateInstance(attr?.EncodingType ?? typeof(UnicodeEncoding)); /// /// diff --git a/UnitTests/Windows.Shell/ClipboardTests.cs b/UnitTests/Windows.Shell/ClipboardTests.cs index 8a4a8f12..c8c12290 100644 --- a/UnitTests/Windows.Shell/ClipboardTests.cs +++ b/UnitTests/Windows.Shell/ClipboardTests.cs @@ -1,6 +1,5 @@ using NUnit.Framework; using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -17,396 +16,409 @@ using WFClipboard = System.Windows.Forms.Clipboard; namespace Vanara.Windows.Shell.Tests { - [TestFixture, SingleThreaded] - public class ClipboardTests - { - private const string html = "
“We’ve been here”
"; - readonly string[] files = { TestCaseSources.SmallFile, TestCaseSources.ImageFile, TestCaseSources.LogFile }; - const string txt = @"“0’0©0è0”"; - const string ptxt = "ABC123"; - private const string url = "https://microsoft.com"; - - [Test] - public void zhuxbTest() - { - Thread STA = new(() => - { - Ole32.OleInitialize(); - - try - { - NativeClipboard.Clear(); - - ShellItemArray ShellItemList = new(files.Select(fn => new ShellItem(fn))); - - var Data = NativeClipboard.CreateEmptyDataObject(); - - Data.SetData(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW, new Shell32.FILEGROUPDESCRIPTOR - { - cItems = (uint)ShellItemList.Count, - fgd = ShellItemList.Select((Item) => - { - ShellFileInfo FileInfo = Item.FileInfo; - Shell32.FD_FLAGS Flags = Shell32.FD_FLAGS.FD_ATTRIBUTES - | Shell32.FD_FLAGS.FD_CREATETIME - | Shell32.FD_FLAGS.FD_ACCESSTIME - | Shell32.FD_FLAGS.FD_WRITESTIME - | Shell32.FD_FLAGS.FD_UNICODE; - - if (!Item.IsFolder) - { - Flags |= Shell32.FD_FLAGS.FD_FILESIZE; - } - - return new Shell32.FILEDESCRIPTOR - { - cFileName = Item.Name, - dwFlags = Flags, - dwFileAttributes = (FileFlagsAndAttributes)FileInfo.Attributes, - nFileSize = (ulong)FileInfo.Length, - ftCreationTime = FileInfo.CreationTime.ToFileTimeStruct(), - ftLastAccessTime = FileInfo.LastAccessTime.ToFileTimeStruct(), - ftLastWriteTime = FileInfo.LastWriteTime.ToFileTimeStruct() - }; - }).ToArray() - }); - - for (int Index = 0; Index < ShellItemList.Count; Index++) - { - ShellItem Item = ShellItemList[Index]; - - if (!Item.IsFolder) - { - Data.SetData(Shell32.ShellClipboardFormat.CFSTR_FILECONTENTS, Item.GetHandler(Shell32.ShellUtil.CreateBindCtx(STGM.STGM_READWRITE | STGM.STGM_SHARE_DENY_WRITE)), DVASPECT.DVASPECT_CONTENT, Index); - } - } - - NativeClipboard.SetDataObject(Data); - - foreach (uint FormatId in NativeClipboard.CurrentlySupportedFormats) - { - Debug.WriteLine($"Available format: {NativeClipboard.GetFormatName(FormatId)}"); - } - - if ((NativeClipboard.IsFormatAvailable(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA) || NativeClipboard.IsFormatAvailable(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW)) && NativeClipboard.IsFormatAvailable(Shell32.ShellClipboardFormat.CFSTR_FILECONTENTS)) - { - Shell32.FILEGROUPDESCRIPTOR FileGroupDescriptor = NativeClipboard.IsFormatAvailable(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA) - ? NativeClipboard.CurrentDataObject.GetData(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORA) - : NativeClipboard.CurrentDataObject.GetData(Shell32.ShellClipboardFormat.CFSTR_FILEDESCRIPTORW); - - for (int Index = 0; Index < FileGroupDescriptor.cItems; Index++) - { - Shell32.FILEDESCRIPTOR FileDescriptor = FileGroupDescriptor.fgd[Index]; - - ulong Size = 0; - FileAttributes Attributes = default; - DateTimeOffset CreateTime = default; - DateTimeOffset LastAccessTime = default; - DateTimeOffset LastWriteTime = default; - - if (FileDescriptor.dwFlags.HasFlag(Shell32.FD_FLAGS.FD_ATTRIBUTES)) - { - Attributes = (FileAttributes)FileDescriptor.dwFileAttributes; - } - - if (FileDescriptor.dwFlags.HasFlag(Shell32.FD_FLAGS.FD_FILESIZE)) - { - Size = FileDescriptor.nFileSize; - } - - if (FileDescriptor.dwFlags.HasFlag(Shell32.FD_FLAGS.FD_CREATETIME)) - { - CreateTime = FileDescriptor.ftCreationTime.ToDateTime(); - } - - if (FileDescriptor.dwFlags.HasFlag(Shell32.FD_FLAGS.FD_ACCESSTIME)) - { - LastAccessTime = FileDescriptor.ftLastAccessTime.ToDateTime(); - } - - if (FileDescriptor.dwFlags.HasFlag(Shell32.FD_FLAGS.FD_WRITESTIME)) - { - LastWriteTime = FileDescriptor.ftLastWriteTime.ToDateTime(); - } - - //Throw Invalid FORMATETC structure - var result = NativeClipboard.CurrentDataObject.GetData(Shell32.ShellClipboardFormat.CFSTR_FILECONTENTS, index: Index); - } - } - } - finally - { - Ole32.OleUninitialize(); - } - }); - STA.SetApartmentState(ApartmentState.STA); - STA.Start(); - STA.Join(); - } - - [Test] - public void DumpWFClipboardTest() - { - TestContext.WriteLine($"ContainsAudio: {WFClipboard.ContainsAudio()}"); - TestContext.WriteLine($"ContainsData: {WFClipboard.ContainsData(DataFormats.StringFormat)}"); - TestContext.WriteLine($"ContainsFileDropList: {WFClipboard.ContainsFileDropList()}"); - TestContext.WriteLine($"ContainsImage: {WFClipboard.ContainsImage()}"); - TestContext.WriteLine($"ContainsText: {WFClipboard.ContainsText()}"); - TestContext.WriteLine($"GetAudioStream: {WFClipboard.GetAudioStream()}"); - TestContext.WriteLine($"GetData: {WFClipboard.GetData(DataFormats.StringFormat)}"); - TestContext.WriteLine($"GetDataObject: {WFClipboard.GetDataObject()}"); - TestContext.WriteLine($"GetFileDropList: {string.Join("\n", WFClipboard.GetFileDropList().Cast())}"); - TestContext.WriteLine($"GetImage: {WFClipboard.GetImage()}"); - TestContext.WriteLine($"GetText: {WFClipboard.GetText()}"); - } - - [Test] - public void EnumFormatsTest() - { - SHCreateDataObject(ppv: out var ido).ThrowIfFailed(); - ido.SetData(CLIPFORMAT.CF_UNICODETEXT, "Test"); - - var fmts = ido.EnumFormats().ToArray(); - Assert.That(fmts, Is.Not.Empty); - TestContext.Write(string.Join(", ", fmts.Select(f => Clipboard.GetFormatName((uint)f.cfFormat)))); - - var fmt = fmts.First(); - Assert.IsTrue(ido.IsFormatAvailable((uint)fmt.cfFormat)); - } - - [Test] - public void GetPriorityFormatTest() - { - var fmts = Clipboard.CurrentlySupportedFormats.ToArray(); - Assert.That(Clipboard.GetFirstFormatAvailable(fmts), Is.GreaterThan(0)); - } - - [Test] - public void GetSetShellItems1() - { - ShellItemArray items = new(Array.ConvertAll(files, f => new ShellItem(f))); - var ido = items.ToDataObject(); - var shArray = ShellItemArray.FromDataObject(ido); - Assert.That(shArray.Count, Is.GreaterThan(0)); - CollectionAssert.AreEquivalent(files, shArray.Select(s => s.FileSystemPath)); - } - - [Test] - public void GetSetShellItems2() - { - ShellItem[] items = Array.ConvertAll(files, f => new ShellItem(f)); - Clipboard.SetDataObject(Clipboard.CreateDataObjectFromShellItems(items)); - var shArray = ShellItemArray.FromDataObject(Clipboard.CurrentDataObject); - Assert.That(shArray.Count, Is.EqualTo(items.Length)); - CollectionAssert.AreEquivalent(files, shArray.Select(s => s.FileSystemPath)); - } - - [Test] - public void GetSetDataTest() - { - SHCreateDataObject(ppv: out var ido).ThrowIfFailed(); - - //using var hPal = Gdi32.CreatePalette(new LOGPALETTE() { palNumEntries = 256, palVersion = 0x0300, palPalEntry = new PALETTEENTRY[256] }); - //ido.SetData(CLIPFORMAT.CF_PALETTE, hPal); - //Assert.That((HPALETTE)ido.GetData(CLIPFORMAT.CF_PALETTE), Is.EqualTo((HPALETTE)hPal)); - - using System.Drawing.Bitmap bmp = new(TestCaseSources.BmpFile); - using Gdi32.SafeHBITMAP hBmp = new(bmp.GetHbitmap()); - ido.SetData(CLIPFORMAT.CF_BITMAP, hBmp); - Assert.AreEqual((HBITMAP)ido.GetData(CLIPFORMAT.CF_BITMAP), (HBITMAP)hBmp); - - //using System.Drawing.Imaging.Metafile enhMeta = new System.Drawing.Imaging.Metafile(TestCaseSources.TempChildDirWhack + "test.wmf"); - //using Gdi32.SafeHENHMETAFILE hEnh = new(enhMeta.GetHenhmetafile()); - //ido.SetData(CLIPFORMAT.CF_ENHMETAFILE, hEnh); - //Assert.That((HENHMETAFILE)ido.GetData(CLIPFORMAT.CF_ENHMETAFILE), Is.EqualTo((HENHMETAFILE)hEnh)); - - ido.SetData(CLIPFORMAT.CF_HDROP, files); - ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEMAPA, files); - ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEMAPW, files); - CollectionAssert.AreEquivalent(files, (string[])ido.GetData(CLIPFORMAT.CF_HDROP)); - CollectionAssert.AreEquivalent(files, (string[])ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEMAPA)); - CollectionAssert.AreEquivalent(files, (string[])ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEMAPW)); - - ido.SetData(CLIPFORMAT.CF_OEMTEXT, ptxt); - Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_OEMTEXT), ptxt); - - ido.SetData(CLIPFORMAT.CF_TEXT, txt); - Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_TEXT), txt); - - ido.SetData(CLIPFORMAT.CF_UNICODETEXT, txt); - Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_UNICODETEXT), txt); - - var r = new RECT(0, 8, 16, 32); - ido.SetData("RECT", r); - Assert.AreEqual(ido.GetData("RECT"), r); - - var lcid = Kernel32.GetUserDefaultLCID(); - ido.SetData(CLIPFORMAT.CF_LOCALE, lcid); - //Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_LOCALE), lcid); - //Assert.That(ido.GetData(CLIPFORMAT.CF_LOCALE), lcid); - - const string csv = "a,b,c,d\n1,2,3,4"; - ido.SetData(ShellClipboardFormat.CF_CSV, csv); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_CSV), csv); - - ido.SetData(ShellClipboardFormat.CF_HTML, html); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_HTML), html); - - var rtf = System.IO.File.ReadAllText(TestCaseSources.TempDirWhack + "Test.rtf"); - ido.SetData(ShellClipboardFormat.CF_RTF, rtf); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_RTF), rtf); - - ido.SetData(ShellClipboardFormat.CF_RTFNOOBJS, rtf); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_RTFNOOBJS), rtf); - - DROPDESCRIPTION dropDesc = new() { type = DROPIMAGETYPE.DROPIMAGE_COPY, szMessage = "Move this" }; - ido.SetData(ShellClipboardFormat.CFSTR_DROPDESCRIPTION, dropDesc); - Assert.AreEqual(((DROPDESCRIPTION)ido.GetData(ShellClipboardFormat.CFSTR_DROPDESCRIPTION)).szMessage, dropDesc.szMessage); - - FILE_ATTRIBUTES_ARRAY faa = new() { cItems = 1, rgdwFileAttributes = new[] { 4U } }; - ido.SetData(ShellClipboardFormat.CFSTR_FILE_ATTRIBUTES_ARRAY, faa); - Assert.AreEqual(((FILE_ATTRIBUTES_ARRAY)ido.GetData(ShellClipboardFormat.CFSTR_FILE_ATTRIBUTES_ARRAY)).cItems, faa.cItems); - - FILEGROUPDESCRIPTOR fgd = new() { cItems = (uint)files.Length, fgd = new FILEDESCRIPTOR[files.Length] }; - for (int i = 0; i < files.Length; i++) - { - if (i == 0) { ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEA, files[i]); ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEW, files[i]); } - fgd.fgd[i] = new FileInfo(files[i]); - ShlwApi.SHCreateStreamOnFileEx(fgd.fgd[i].cFileName, STGM.STGM_READ | STGM.STGM_SHARE_DENY_WRITE, 0, false, null, out IStream istream).ThrowIfFailed(); - ido.SetData(ShellClipboardFormat.CFSTR_FILECONTENTS, istream, DVASPECT.DVASPECT_CONTENT, i); - } - ido.SetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW, fgd); - Assert.AreEqual(((FILEGROUPDESCRIPTOR)ido.GetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW)).cItems, fgd.cItems); - Assert.That(() => { var ist = (Ole32.IStreamV)ido.GetData(ShellClipboardFormat.CFSTR_FILECONTENTS, index: 1); ist.Seek(0, Ole32.STREAM_SEEK.STREAM_SEEK_SET, out _).ThrowIfFailed(); }, Throws.Nothing); - Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEA), Is.TypeOf().And.Not.Null); - Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEW), Is.TypeOf().And.Not.Null); - - ido.SetUrl(url, "Microsoft"); - Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_INETURLA), Does.StartWith(url)); - Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_INETURLW), Does.StartWith(url)); - - ido.SetData(ShellClipboardFormat.CFSTR_INDRAGLOOP, true); - Assert.AreEqual((BOOL)ido.GetData(ShellClipboardFormat.CFSTR_INDRAGLOOP), true); - - ido.SetData(ShellClipboardFormat.CFSTR_INVOKECOMMAND_DROPPARAM, ptxt); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_INVOKECOMMAND_DROPPARAM), ptxt); - - ido.SetData(ShellClipboardFormat.CFSTR_LOGICALPERFORMEDDROPEFFECT, Ole32.DROPEFFECT.DROPEFFECT_COPY); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_LOGICALPERFORMEDDROPEFFECT), Ole32.DROPEFFECT.DROPEFFECT_COPY); - - ido.SetData(ShellClipboardFormat.CFSTR_MOUNTEDVOLUME, ptxt); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_MOUNTEDVOLUME), ptxt); - - SafeLPTSTR remName = new("WINSTATION"); - NRESARRAY nres = new() { cItems = 1, nr = new NETRESOURCE[] { new() { lpRemoteName = remName } } }; - ido.SetData(ShellClipboardFormat.CFSTR_NETRESOURCES, nres); - Assert.AreEqual(((NRESARRAY)ido.GetData(ShellClipboardFormat.CFSTR_NETRESOURCES)).cItems, nres.cItems); - - ido.SetData(ShellClipboardFormat.CFSTR_PASTESUCCEEDED, Ole32.DROPEFFECT.DROPEFFECT_COPY); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PASTESUCCEEDED), Ole32.DROPEFFECT.DROPEFFECT_COPY); - - ido.SetData(ShellClipboardFormat.CFSTR_PERFORMEDDROPEFFECT, Ole32.DROPEFFECT.DROPEFFECT_COPY); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PERFORMEDDROPEFFECT), Ole32.DROPEFFECT.DROPEFFECT_COPY); - - ido.SetData(ShellClipboardFormat.CFSTR_PREFERREDDROPEFFECT, Ole32.DROPEFFECT.DROPEFFECT_COPY); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PREFERREDDROPEFFECT), Ole32.DROPEFFECT.DROPEFFECT_COPY); - - ido.SetData(ShellClipboardFormat.CFSTR_PRINTERGROUP, ptxt); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PRINTERGROUP), ptxt); - - var guid = Guid.NewGuid(); - ido.SetData(ShellClipboardFormat.CFSTR_SHELLDROPHANDLER, guid); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_SHELLDROPHANDLER), guid); - - ido.SetData(ShellClipboardFormat.CFSTR_TARGETCLSID, guid); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_TARGETCLSID), guid); - - ido.SetData(ShellClipboardFormat.CFSTR_UNTRUSTEDDRAGDROP, 16U); - Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_UNTRUSTEDDRAGDROP), 16U); - - ido.SetData("ByteArray", new byte[] { 1,2,3,4,5,6,7,8 }); - Assert.AreEqual(((byte[])ido.GetData("ByteArray")).Length, 8); - - //using var fs = File.OpenRead(files[0]); - //ido.SetData("Stream", fs); - //Assert.That(ido.GetData("Stream"), Is.TypeOf()); - - //ido.SetData("StringArray", files); - //CollectionAssert.AreEquivalent(files, ido.GetData("StringArray")); - - // CFSTR_SHELLIDLIST - - ShlwApi.SHCreateStreamOnFileEx(files[0], STGM.STGM_READ | STGM.STGM_SHARE_DENY_WRITE, 0, false, null, out var istream2).ThrowIfFailed(); - ido.SetData("IStream", istream2); - Assert.That(ido.GetData("IStream") as IStream, Is.Not.Null); - - // IStorage - - //var fi = new FileInfo(files[0]); - //ido.SetData("File", fi); - //Assert.AreEqual(ido.GetData("File"), files[0]); - - // ISerializable - ido.SetData("NetBmp", bmp); - Assert.That(ido.GetData("NetBmp"), Is.TypeOf()); - - // SafeAllocated - //SafeCoTaskMemHandle h = SafeCoTaskMemHandle.CreateFromStringList(files); - //ido.SetData("hMem", h); - //Assert.AreEqual(ido.GetData("hMem") as byte[], h.GetBytes()); - } - - [Test] - public void SetNativeTextHtmlTest() - { - SHCreateDataObject(ppv: out var ido).ThrowIfFailed(); - ido.SetData(ShellClipboardFormat.CF_HTML, html); - var outVal = ido.GetData(ShellClipboardFormat.CF_HTML); - Assert.That(outVal, Is.EqualTo(html)); - } - - [Test] - public void SetNativeTextMultTest() - { - const string stxt = "112233"; - var ido = Clipboard.CreateEmptyDataObject(); - ido.SetData(CLIPFORMAT.CF_UNICODETEXT, stxt); - Clipboard.SetDataObject(ido); - Assert.That(Clipboard.CurrentDataObject.GetData(CLIPFORMAT.CF_UNICODETEXT), Is.EqualTo(stxt)); - - ido = Clipboard.CreateEmptyDataObject(); - ido.SetText(txt, txt); - Clipboard.SetDataObject(ido); - Assert.That(Clipboard.CurrentDataObject.GetText(CLIPFORMAT.CF_TEXT), Is.EqualTo(txt)); - Assert.That(Clipboard.CurrentDataObject.GetText(CLIPFORMAT.CF_UNICODETEXT), Is.EqualTo(txt)); - Assert.That(Clipboard.CurrentDataObject.GetText(ShellClipboardFormat.CF_HTML), Is.EqualTo(txt)); - TestContext.WriteLine(Clipboard.CurrentDataObject.GetText(ShellClipboardFormat.CF_HTML)); - } - - [Test] - public void SetNativeTextUnicodeTest() - { - const string txt = @"“0’0©0è0”"; - var ido = Clipboard.CreateEmptyDataObject(); - ido.SetData(CLIPFORMAT.CF_UNICODETEXT, txt); - Clipboard.SetDataObject(ido); - - Assert.That(Clipboard.CurrentDataObject.GetText(CLIPFORMAT.CF_UNICODETEXT), Is.EqualTo(txt)); - } - - //[Test] - public void ChangeEventTest() - { - var sawChange = new ManualResetEvent(false); - Clipboard.ClipboardUpdate += OnUpdate; - Thread.SpinWait(1000); - WFClipboard.SetText("Hello"); - //using var Clipboard = new Clipboard(); - //Clipboard.SetText("Hello"); - Assert.IsTrue(sawChange.WaitOne(5000)); - Clipboard.ClipboardUpdate -= OnUpdate; - - void OnUpdate(object sender, EventArgs e) => sawChange.Set(); - } - } + [TestFixture, SingleThreaded] + public class ClipboardTests + { + private const string html = "
“We’ve been here”
"; + readonly string[] files = { TestCaseSources.SmallFile, TestCaseSources.ImageFile, TestCaseSources.LogFile }; + const string txt = @"“0’0©0è0”"; + const string ptxt = "ABC123"; + private const string url = "https://microsoft.com"; + + [Test] + public void FileRelatedStructsInClipboardTest() + { + Thread STA = new(() => + { + Ole32.OleInitialize(); + + try + { + Clipboard.Clear(); + + ShellItemArray ShellItemList = new(files.Select(fn => new ShellItem(fn))); + + var Data = Clipboard.CreateEmptyDataObject(); + + Data.SetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW, new FILEGROUPDESCRIPTOR + { + cItems = (uint)ShellItemList.Count, + fgd = ShellItemList.Select((Item) => + { + ShellFileInfo FileInfo = Item.FileInfo; + FD_FLAGS Flags = FD_FLAGS.FD_ATTRIBUTES + | FD_FLAGS.FD_CREATETIME + | FD_FLAGS.FD_ACCESSTIME + | FD_FLAGS.FD_WRITESTIME + | FD_FLAGS.FD_UNICODE; + + if (!Item.IsFolder) + { + Flags |= FD_FLAGS.FD_FILESIZE; + } + + return new FILEDESCRIPTOR + { + cFileName = Item.Name, + dwFlags = Flags, + dwFileAttributes = (FileFlagsAndAttributes)FileInfo.Attributes, + nFileSize = (ulong)FileInfo.Length, + ftCreationTime = FileInfo.CreationTime.ToFileTimeStruct(), + ftLastAccessTime = FileInfo.LastAccessTime.ToFileTimeStruct(), + ftLastWriteTime = FileInfo.LastWriteTime.ToFileTimeStruct() + }; + }).ToArray() + }); + + for (int Index = 0; Index < ShellItemList.Count; Index++) + { + ShellItem Item = ShellItemList[Index]; + + if (!Item.IsFolder) + { + Data.SetData(ShellClipboardFormat.CFSTR_FILECONTENTS, Item.GetHandler(ShellUtil.CreateBindCtx(STGM.STGM_READWRITE | STGM.STGM_SHARE_DENY_WRITE)), DVASPECT.DVASPECT_CONTENT, Index); + } + } + + Clipboard.SetDataObject(Data); + + foreach (uint FormatId in Clipboard.CurrentlySupportedFormats) + { + Debug.WriteLine($"Available format: {Clipboard.GetFormatName(FormatId)}"); + } + + if ((Clipboard.IsFormatAvailable(ShellClipboardFormat.CFSTR_FILEDESCRIPTORA) || Clipboard.IsFormatAvailable(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW)) && Clipboard.IsFormatAvailable(ShellClipboardFormat.CFSTR_FILECONTENTS)) + { + FILEGROUPDESCRIPTOR FileGroupDescriptor = Clipboard.IsFormatAvailable(ShellClipboardFormat.CFSTR_FILEDESCRIPTORA) + ? Clipboard.CurrentDataObject.GetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORA) + : Clipboard.CurrentDataObject.GetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW); + + for (int Index = 0; Index < FileGroupDescriptor.cItems; Index++) + { + FILEDESCRIPTOR FileDescriptor = FileGroupDescriptor.fgd[Index]; + + ulong Size = 0; + FileAttributes Attributes = default; + DateTimeOffset CreateTime = default; + DateTimeOffset LastAccessTime = default; + DateTimeOffset LastWriteTime = default; + + if (FileDescriptor.dwFlags.HasFlag(FD_FLAGS.FD_ATTRIBUTES)) + { + Attributes = (FileAttributes)FileDescriptor.dwFileAttributes; + } + + if (FileDescriptor.dwFlags.HasFlag(FD_FLAGS.FD_FILESIZE)) + { + Size = FileDescriptor.nFileSize; + } + + if (FileDescriptor.dwFlags.HasFlag(FD_FLAGS.FD_CREATETIME)) + { + CreateTime = FileDescriptor.ftCreationTime.ToDateTime(); + } + + if (FileDescriptor.dwFlags.HasFlag(FD_FLAGS.FD_ACCESSTIME)) + { + LastAccessTime = FileDescriptor.ftLastAccessTime.ToDateTime(); + } + + if (FileDescriptor.dwFlags.HasFlag(FD_FLAGS.FD_WRITESTIME)) + { + LastWriteTime = FileDescriptor.ftLastWriteTime.ToDateTime(); + } + + //Throw Invalid FORMATETC structure + var result = Clipboard.CurrentDataObject.GetData(ShellClipboardFormat.CFSTR_FILECONTENTS, index: Index); + } + } + } + finally + { + Ole32.OleUninitialize(); + } + }); + STA.SetApartmentState(ApartmentState.STA); + STA.Start(); + STA.Join(); + } + + [Test] + public void DumpWFClipboardTest() + { + TestContext.WriteLine($"ContainsAudio: {WFClipboard.ContainsAudio()}"); + TestContext.WriteLine($"ContainsData: {WFClipboard.ContainsData(DataFormats.StringFormat)}"); + TestContext.WriteLine($"ContainsFileDropList: {WFClipboard.ContainsFileDropList()}"); + TestContext.WriteLine($"ContainsImage: {WFClipboard.ContainsImage()}"); + TestContext.WriteLine($"ContainsText: {WFClipboard.ContainsText()}"); + TestContext.WriteLine($"GetAudioStream: {WFClipboard.GetAudioStream()}"); + TestContext.WriteLine($"GetData: {WFClipboard.GetData(DataFormats.StringFormat)}"); + TestContext.WriteLine($"GetDataObject: {WFClipboard.GetDataObject()}"); + TestContext.WriteLine($"GetFileDropList: {string.Join("\n", WFClipboard.GetFileDropList().Cast())}"); + TestContext.WriteLine($"GetImage: {WFClipboard.GetImage()}"); + TestContext.WriteLine($"GetText: {WFClipboard.GetText()}"); + } + + [Test] + public void EnumFormatsTest() + { + SHCreateDataObject(ppv: out var ido).ThrowIfFailed(); + ido.SetData(CLIPFORMAT.CF_UNICODETEXT, "Test"); + + var fmts = ido.EnumFormats().ToArray(); + Assert.That(fmts, Is.Not.Empty); + TestContext.Write(string.Join(", ", fmts.Select(f => Clipboard.GetFormatName((uint)f.cfFormat)))); + + var fmt = fmts.First(); + Assert.IsTrue(ido.IsFormatAvailable((uint)fmt.cfFormat)); + } + + [Test] + public void GetPriorityFormatTest() + { + var fmts = Clipboard.CurrentlySupportedFormats.ToArray(); + Assert.That(Clipboard.GetFirstFormatAvailable(fmts), Is.GreaterThan(0)); + } + + [Test] + public void GetSetShellItems1() + { + ShellItemArray items = new(Array.ConvertAll(files, f => new ShellItem(f))); + var ido = items.ToDataObject(); + var shArray = ShellItemArray.FromDataObject(ido); + Assert.That(shArray.Count, Is.GreaterThan(0)); + CollectionAssert.AreEquivalent(files, shArray.Select(s => s.FileSystemPath)); + } + + [Test] + public void GetSetShellItems2() + { + ShellItem[] items = Array.ConvertAll(files, f => new ShellItem(f)); + Clipboard.SetDataObject(Clipboard.CreateDataObjectFromShellItems(items)); + var shArray = ShellItemArray.FromDataObject(Clipboard.CurrentDataObject); + Assert.That(shArray.Count, Is.EqualTo(items.Length)); + CollectionAssert.AreEquivalent(files, shArray.Select(s => s.FileSystemPath)); + } + + [Test] + public void GetSetDataTest() + { + SHCreateDataObject(ppv: out var ido).ThrowIfFailed(); + + //using var hPal = Gdi32.CreatePalette(new LOGPALETTE() { palNumEntries = 256, palVersion = 0x0300, palPalEntry = new PALETTEENTRY[256] }); + //ido.SetData(CLIPFORMAT.CF_PALETTE, hPal); + //Assert.That((HPALETTE)ido.GetData(CLIPFORMAT.CF_PALETTE), Is.EqualTo((HPALETTE)hPal)); + + using System.Drawing.Bitmap bmp = new(TestCaseSources.BmpFile); + using Gdi32.SafeHBITMAP hBmp = new(bmp.GetHbitmap()); + ido.SetData(CLIPFORMAT.CF_BITMAP, hBmp); + Assert.AreEqual((HBITMAP)ido.GetData(CLIPFORMAT.CF_BITMAP), (HBITMAP)hBmp); + + //using System.Drawing.Imaging.Metafile enhMeta = new System.Drawing.Imaging.Metafile(TestCaseSources.TempChildDirWhack + "test.wmf"); + //using Gdi32.SafeHENHMETAFILE hEnh = new(enhMeta.GetHenhmetafile()); + //ido.SetData(CLIPFORMAT.CF_ENHMETAFILE, hEnh); + //Assert.That((HENHMETAFILE)ido.GetData(CLIPFORMAT.CF_ENHMETAFILE), Is.EqualTo((HENHMETAFILE)hEnh)); + + ido.SetData(CLIPFORMAT.CF_HDROP, files); + ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEMAPA, files); + ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEMAPW, files); + CollectionAssert.AreEquivalent(files, (string[])ido.GetData(CLIPFORMAT.CF_HDROP)); + CollectionAssert.AreEquivalent(files, (string[])ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEMAPA)); + CollectionAssert.AreEquivalent(files, (string[])ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEMAPW)); + + ido.SetData(CLIPFORMAT.CF_OEMTEXT, ptxt); + Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_OEMTEXT), ptxt); + + ido.SetData(CLIPFORMAT.CF_TEXT, txt); + Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_TEXT), txt); + + ido.SetData(CLIPFORMAT.CF_UNICODETEXT, txt); + Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_UNICODETEXT), txt); + + var r = new RECT(0, 8, 16, 32); + ido.SetData("RECT", r); + Assert.AreEqual(ido.GetData("RECT"), r); + + var lcid = Kernel32.GetUserDefaultLCID(); + ido.SetData(CLIPFORMAT.CF_LOCALE, lcid); + //Assert.AreEqual(ido.GetData(CLIPFORMAT.CF_LOCALE), lcid); + //Assert.That(ido.GetData(CLIPFORMAT.CF_LOCALE), lcid); + + const string csv = "a,b,c,d\n1,2,3,4"; + ido.SetData(ShellClipboardFormat.CF_CSV, csv); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_CSV), csv); + + ido.SetData(ShellClipboardFormat.CF_HTML, html); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_HTML), html); + + var rtf = System.IO.File.ReadAllText(TestCaseSources.TempDirWhack + "Test.rtf"); + ido.SetData(ShellClipboardFormat.CF_RTF, rtf); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_RTF), rtf); + + ido.SetData(ShellClipboardFormat.CF_RTFNOOBJS, rtf); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CF_RTFNOOBJS), rtf); + + DROPDESCRIPTION dropDesc = new() { type = DROPIMAGETYPE.DROPIMAGE_COPY, szMessage = "Move this" }; + ido.SetData(ShellClipboardFormat.CFSTR_DROPDESCRIPTION, dropDesc); + Assert.AreEqual(((DROPDESCRIPTION)ido.GetData(ShellClipboardFormat.CFSTR_DROPDESCRIPTION)).szMessage, dropDesc.szMessage); + + FILE_ATTRIBUTES_ARRAY faa = new() { cItems = 1, rgdwFileAttributes = new[] { 4U } }; + ido.SetData(ShellClipboardFormat.CFSTR_FILE_ATTRIBUTES_ARRAY, faa); + Assert.AreEqual(((FILE_ATTRIBUTES_ARRAY)ido.GetData(ShellClipboardFormat.CFSTR_FILE_ATTRIBUTES_ARRAY)).cItems, faa.cItems); + + FILEGROUPDESCRIPTOR fgd = new() { cItems = (uint)files.Length, fgd = new FILEDESCRIPTOR[files.Length] }; + for (int i = 0; i < files.Length; i++) + { + if (i == 0) { ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEA, files[i]); ido.SetData(ShellClipboardFormat.CFSTR_FILENAMEW, files[i]); } + fgd.fgd[i] = new FileInfo(files[i]); + ShlwApi.SHCreateStreamOnFileEx(fgd.fgd[i].cFileName, STGM.STGM_READ | STGM.STGM_SHARE_DENY_WRITE, 0, false, null, out IStream istream).ThrowIfFailed(); + ido.SetData(ShellClipboardFormat.CFSTR_FILECONTENTS, istream, DVASPECT.DVASPECT_CONTENT, i); + } + ido.SetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW, fgd); + Assert.AreEqual(((FILEGROUPDESCRIPTOR)ido.GetData(ShellClipboardFormat.CFSTR_FILEDESCRIPTORW)).cItems, fgd.cItems); + Assert.That(() => { var ist = (Ole32.IStreamV)ido.GetData(ShellClipboardFormat.CFSTR_FILECONTENTS, index: 1); ist.Seek(0, Ole32.STREAM_SEEK.STREAM_SEEK_SET, out _).ThrowIfFailed(); }, Throws.Nothing); + Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEA), Is.TypeOf().And.Not.Null); + Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_FILENAMEW), Is.TypeOf().And.Not.Null); + + ido.SetUrl(url, "Microsoft"); + Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_INETURLA), Does.StartWith(url)); + Assert.That(ido.GetData(ShellClipboardFormat.CFSTR_INETURLW), Does.StartWith(url)); + + ido.SetData(ShellClipboardFormat.CFSTR_INDRAGLOOP, true); + Assert.AreEqual((BOOL)ido.GetData(ShellClipboardFormat.CFSTR_INDRAGLOOP), true); + + ido.SetData(ShellClipboardFormat.CFSTR_INVOKECOMMAND_DROPPARAM, ptxt); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_INVOKECOMMAND_DROPPARAM), ptxt); + + ido.SetData(ShellClipboardFormat.CFSTR_LOGICALPERFORMEDDROPEFFECT, Ole32.DROPEFFECT.DROPEFFECT_COPY); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_LOGICALPERFORMEDDROPEFFECT), Ole32.DROPEFFECT.DROPEFFECT_COPY); + + ido.SetData(ShellClipboardFormat.CFSTR_MOUNTEDVOLUME, ptxt); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_MOUNTEDVOLUME), ptxt); + + SafeLPTSTR remName = new("WINSTATION"); + NRESARRAY nres = new() { cItems = 1, nr = new NETRESOURCE[] { new() { lpRemoteName = remName } } }; + ido.SetData(ShellClipboardFormat.CFSTR_NETRESOURCES, nres); + Assert.AreEqual(((NRESARRAY)ido.GetData(ShellClipboardFormat.CFSTR_NETRESOURCES)).cItems, nres.cItems); + + ido.SetData(ShellClipboardFormat.CFSTR_PASTESUCCEEDED, Ole32.DROPEFFECT.DROPEFFECT_COPY); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PASTESUCCEEDED), Ole32.DROPEFFECT.DROPEFFECT_COPY); + + ido.SetData(ShellClipboardFormat.CFSTR_PERFORMEDDROPEFFECT, Ole32.DROPEFFECT.DROPEFFECT_COPY); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PERFORMEDDROPEFFECT), Ole32.DROPEFFECT.DROPEFFECT_COPY); + + ido.SetData(ShellClipboardFormat.CFSTR_PREFERREDDROPEFFECT, Ole32.DROPEFFECT.DROPEFFECT_COPY); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PREFERREDDROPEFFECT), Ole32.DROPEFFECT.DROPEFFECT_COPY); + + ido.SetData(ShellClipboardFormat.CFSTR_PRINTERGROUP, ptxt); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_PRINTERGROUP), ptxt); + + var guid = Guid.NewGuid(); + ido.SetData(ShellClipboardFormat.CFSTR_SHELLDROPHANDLER, guid); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_SHELLDROPHANDLER), guid); + + ido.SetData(ShellClipboardFormat.CFSTR_TARGETCLSID, guid); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_TARGETCLSID), guid); + + ido.SetData(ShellClipboardFormat.CFSTR_UNTRUSTEDDRAGDROP, 16U); + Assert.AreEqual(ido.GetData(ShellClipboardFormat.CFSTR_UNTRUSTEDDRAGDROP), 16U); + + ido.SetData("ByteArray", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }); + Assert.AreEqual(((byte[])ido.GetData("ByteArray")).Length, 8); + + //using var fs = File.OpenRead(files[0]); + //ido.SetData("Stream", fs); + //Assert.That(ido.GetData("Stream"), Is.TypeOf()); + + //ido.SetData("StringArray", files); + //CollectionAssert.AreEquivalent(files, ido.GetData("StringArray")); + + // CFSTR_SHELLIDLIST + + ShlwApi.SHCreateStreamOnFileEx(files[0], STGM.STGM_READ | STGM.STGM_SHARE_DENY_WRITE, 0, false, null, out var istream2).ThrowIfFailed(); + ido.SetData("IStream", istream2); + Assert.That(ido.GetData("IStream") as IStream, Is.Not.Null); + + // IStorage + + //var fi = new FileInfo(files[0]); + //ido.SetData("File", fi); + //Assert.AreEqual(ido.GetData("File"), files[0]); + + // ISerializable + ido.SetData("NetBmp", bmp); + Assert.That(ido.GetData("NetBmp"), Is.TypeOf()); + + // SafeAllocated + //SafeCoTaskMemHandle h = SafeCoTaskMemHandle.CreateFromStringList(files); + //ido.SetData("hMem", h); + //Assert.AreEqual(ido.GetData("hMem") as byte[], h.GetBytes()); + } + + [Test] + public void SetNativeTextHtmlTest() + { + SHCreateDataObject(ppv: out var ido).ThrowIfFailed(); + ido.SetData(ShellClipboardFormat.CF_HTML, html); + var outVal = ido.GetData(ShellClipboardFormat.CF_HTML); + Assert.That(outVal, Is.EqualTo(html)); + } + + [Test] + public void SetNativeTextMultTest() + { + const string stxt = "112233"; + var ido = Clipboard.CreateEmptyDataObject(); + ido.SetData(CLIPFORMAT.CF_UNICODETEXT, stxt); + Clipboard.SetDataObject(ido); + Assert.That(Clipboard.CurrentDataObject.GetData(CLIPFORMAT.CF_UNICODETEXT), Is.EqualTo(stxt)); + + ido = Clipboard.CreateEmptyDataObject(); + ido.SetText(txt, txt); + Clipboard.SetDataObject(ido); + Assert.That(Clipboard.CurrentDataObject.GetText(CLIPFORMAT.CF_TEXT), Is.EqualTo(txt)); + Assert.That(Clipboard.CurrentDataObject.GetText(CLIPFORMAT.CF_UNICODETEXT), Is.EqualTo(txt)); + Assert.That(Clipboard.CurrentDataObject.GetText(ShellClipboardFormat.CF_HTML), Is.EqualTo(txt)); + TestContext.WriteLine(Clipboard.CurrentDataObject.GetText(ShellClipboardFormat.CF_HTML)); + } + + [Test] + public void SetNativeTextUnicodeTest() + { + const string txt = @"“0’0©0è0”"; + var ido = Clipboard.CreateEmptyDataObject(); + ido.SetData(CLIPFORMAT.CF_UNICODETEXT, txt); + Clipboard.SetDataObject(ido); + + Assert.That(Clipboard.CurrentDataObject.GetText(CLIPFORMAT.CF_UNICODETEXT), Is.EqualTo(txt)); + } + + //[Test] + public void ChangeEventTest() + { + var sawChange = new ManualResetEvent(false); + Clipboard.ClipboardUpdate += OnUpdate; + Thread.SpinWait(1000); + WFClipboard.SetText("Hello"); + //using var Clipboard = new Clipboard(); + //Clipboard.SetText("Hello"); + Assert.IsTrue(sawChange.WaitOne(5000)); + Clipboard.ClipboardUpdate -= OnUpdate; + + void OnUpdate(object sender, EventArgs e) => sawChange.Set(); + } + + [Test] + public void SetCustomFormatWithStringDataText() + { + const string Format = "TestFormat"; + const string Data = "Test Data"; + + var ido = Clipboard.CreateEmptyDataObject(); + ido.SetData(Format, Data); + Clipboard.SetDataObject(ido); + + Assert.AreEqual(Data, Clipboard.CurrentDataObject.GetData(Format)); + } + } } \ No newline at end of file