From 8e121f25f03fe9d249ad4fabd837d84d2e820e8e Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Tue, 18 May 2021 10:56:39 +0200 Subject: [PATCH] Fix for clipboard issues #307, this caused all kind of problems throughout the editor whenever the clipboard contents where checked. [skip ci] --- src/Greenshot.Base/Core/ClipboardHelper.cs | 177 +++++++++++++----- src/Greenshot.Base/Core/FileDescriptor.cs | 76 ++++++++ .../Core/FileDescriptorReader.cs | 52 +---- src/Greenshot/Destinations/FileDestination.cs | 6 +- 4 files changed, 215 insertions(+), 96 deletions(-) create mode 100644 src/Greenshot.Base/Core/FileDescriptor.cs diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs index 7da99f216..02a7f03f1 100644 --- a/src/Greenshot.Base/Core/ClipboardHelper.cs +++ b/src/Greenshot.Base/Core/ClipboardHelper.cs @@ -299,39 +299,24 @@ EndSelection:<<<<<<<4 return true; } - var fileDescriptor = (MemoryStream) dataObject.GetData("FileGroupDescriptorW"); - var files = FileDescriptorReader.Read(fileDescriptor); - var fileIndex = 0; - foreach (var fileContentFile in files) + foreach (var fileData in IterateClipboardContent(dataObject)) { - if ((fileContentFile.FileAttributes & FileAttributes.Directory) != 0) - { - //Do something with directories? - //Note that directories do not have FileContents - //And will throw if we try to read them - continue; - } - - var fileData = FileDescriptorReader.GetFileContents(dataObject, fileIndex); try { - //Do something with the fileContent Stream - if (IsValidStream(fileData)) + using (ImageHelper.FromStream(fileData)) { - fileData.Position = 0; - using (ImageHelper.FromStream(fileData)) - { - // If we get here, there is an image - return true; - } + // If we get here, there is an image + return true; } } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); + } finally { fileData?.Dispose(); } - - fileIndex++; } if (dataObject.GetDataPresent(FORMAT_FILECONTENTS)) @@ -357,28 +342,116 @@ EndSelection:<<<<<<<4 // Try to get the image from the HTML code var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); - if (textObject != null) + if (textObject == null) { - var doc = new HtmlDocument(); - doc.LoadHtml(textObject); - var imgNodes = doc.DocumentNode.SelectNodes("//img"); - if (imgNodes != null) + return false; + } + + var doc = new HtmlDocument(); + doc.LoadHtml(textObject); + var imgNodes = doc.DocumentNode.SelectNodes("//img"); + + if (imgNodes == null) + { + return false; + } + + foreach (var imgNode in imgNodes) + { + var srcAttribute = imgNode.Attributes["src"]; + var imageUrl = srcAttribute.Value; + if (!string.IsNullOrEmpty(imageUrl)) { - foreach (var imgNode in imgNodes) - { - var srcAttribute = imgNode.Attributes["src"]; - var imageUrl = srcAttribute.Value; - if (!string.IsNullOrEmpty(imageUrl)) - { - return true; - } - } + return true; } } return false; } + /// + /// Iterate the clipboard content + /// + /// IDataObject + /// IEnumerable{MemoryStream} + private static IEnumerable IterateClipboardContent(IDataObject dataObject) + { + var fileDescriptors = AvailableFileDescriptors(dataObject); + if (fileDescriptors == null) yield break; + + foreach (var fileData in IterateFileDescriptors(fileDescriptors, dataObject)) + { + yield return fileData; + } + } + + /// + /// Retrieve the FileDescriptor on the clipboard + /// + /// IDataObject + /// IEnumerable{FileDescriptor} + private static IEnumerable AvailableFileDescriptors(IDataObject dataObject) + { + var fileDescriptor = (MemoryStream) dataObject.GetData("FileGroupDescriptorW"); + if (fileDescriptor != null) + { + try + { + return FileDescriptorReader.Read(fileDescriptor); + } + catch (Exception ex) + { + Log.Error("Couldn't use FileDescriptorReader.", ex); + } + } + + return Enumerable.Empty(); + } + + /// + /// Iterate the file descriptors on the clipboard + /// + /// IEnumerable{FileDescriptor} + /// IDataObject + /// IEnumerable{MemoryStream} + private static IEnumerable IterateFileDescriptors(IEnumerable fileDescriptors, IDataObject dataObject) + { + if (fileDescriptors == null) + { + yield break; + } + + var fileIndex = 0; + foreach (var fileDescriptor in fileDescriptors) + { + if ((fileDescriptor.FileAttributes & FileAttributes.Directory) != 0) + { + //Do something with directories? + //Note that directories do not have FileContents + //And will throw if we try to read them + continue; + } + + MemoryStream fileData = null; + try + { + fileData = FileDescriptorReader.GetFileContents(dataObject, fileIndex); + //Do something with the fileContent Stream + } + catch (Exception ex) + { + Log.Error($"Couldn't read file contents for {fileDescriptor.FileName}.", ex); + } + if (fileData?.Length > 0) + { + fileData.Position = 0; + yield return fileData; + } + + fileIndex++; + } + } + /// /// Get the specified IDataObject format as a string /// @@ -403,10 +476,10 @@ EndSelection:<<<<<<<4 /// Simple helper to check the stream /// /// - /// + /// true if there is a valid stream private static bool IsValidStream(MemoryStream memoryStream) { - return memoryStream != null && memoryStream.Length > 0; + return memoryStream?.Length > 0; } /// @@ -426,7 +499,7 @@ EndSelection:<<<<<<<4 } /// - /// Get all images (multiple if filenames are available) from the dataObject + /// Get all images (multiple if file names are available) from the dataObject /// Returned images must be disposed by the calling code! /// /// @@ -442,6 +515,26 @@ EndSelection:<<<<<<<4 } else { + foreach (var fileData in IterateClipboardContent(dataObject)) + { + Image image; + try + { + image = ImageHelper.FromStream(fileData); + } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); + continue; + } + finally + { + fileData?.Dispose(); + } + // If we get here, there is an image + yield return image; + } + // check if files are supplied foreach (string imageFile in GetImageFilenames(dataObject)) { @@ -478,7 +571,7 @@ EndSelection:<<<<<<<4 string[] retrieveFormats; // Found a weird bug, where PNG's from Outlook 2010 are clipped - // So I build some special logik to get the best format: + // So I build some special logic to get the best format: if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) { // Outlook ?? @@ -729,7 +822,7 @@ EndSelection:<<<<<<<4 /// This method will place images to the clipboard depending on the ClipboardFormats setting. /// e.g. Bitmap which works with pretty much everything and type Dib for e.g. OpenOffice /// because OpenOffice has a bug https://qa.openoffice.org/issues/show_bug.cgi?id=85661 - /// The Dib (Device Indenpendend Bitmap) in 32bpp actually won't work with Powerpoint 2003! + /// The Dib (Device Independent Bitmap) in 32bpp actually won't work with Powerpoint 2003! /// When pasting a Dib in PP 2003 the Bitmap is somehow shifted left! /// For this problem the user should not use the direct paste (=Dib), but select Bitmap /// diff --git a/src/Greenshot.Base/Core/FileDescriptor.cs b/src/Greenshot.Base/Core/FileDescriptor.cs new file mode 100644 index 000000000..5306cf3b8 --- /dev/null +++ b/src/Greenshot.Base/Core/FileDescriptor.cs @@ -0,0 +1,76 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.IO; +using System.Text; +using Greenshot.Base.UnmanagedHelpers.Structs; + +namespace Greenshot.Base.Core +{ + public sealed class FileDescriptor + { + public FileDescriptorFlags Flags { get; set; } + public Guid ClassId { get; set; } + public SIZE Size { get; set; } + public POINT Point { get; set; } + public FileAttributes FileAttributes { get; set; } + public DateTime CreationTime { get; set; } + public DateTime LastAccessTime { get; set; } + public DateTime LastWriteTime { get; set; } + public Int64 FileSize { get; set; } + public string FileName { get; set; } + + public FileDescriptor(BinaryReader reader) + { + //Flags + Flags = (FileDescriptorFlags)reader.ReadUInt32(); + //ClassID + ClassId = new Guid(reader.ReadBytes(16)); + //Size + Size = new SIZE(reader.ReadInt32(), reader.ReadInt32()); + //Point + Point = new POINT(reader.ReadInt32(), reader.ReadInt32()); + //FileAttributes + FileAttributes = (FileAttributes)reader.ReadUInt32(); + //CreationTime + CreationTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64()); + //LastAccessTime + LastAccessTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64()); + //LastWriteTime + LastWriteTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64()); + //FileSize + FileSize = reader.ReadInt64(); + //FileName + byte[] nameBytes = reader.ReadBytes(520); + int i = 0; + while (i < nameBytes.Length) + { + if (nameBytes[i] == 0 && nameBytes[i + 1] == 0) + break; + i++; + i++; + } + + FileName = Encoding.Unicode.GetString(nameBytes, 0, i); + } + } +} diff --git a/src/Greenshot.Base/Core/FileDescriptorReader.cs b/src/Greenshot.Base/Core/FileDescriptorReader.cs index 628e6d81a..69ad0af7d 100644 --- a/src/Greenshot.Base/Core/FileDescriptorReader.cs +++ b/src/Greenshot.Base/Core/FileDescriptorReader.cs @@ -24,8 +24,6 @@ using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; -using System.Text; -using Greenshot.Base.UnmanagedHelpers.Structs; namespace Greenshot.Base.Core { @@ -33,60 +31,12 @@ namespace Greenshot.Base.Core /// Specifies which fields are valid in a FileDescriptor Structure /// [Flags] - internal enum FileDescriptorFlags : uint + public enum FileDescriptorFlags : uint { } internal static class FileDescriptorReader { - internal sealed class FileDescriptor - { - public FileDescriptorFlags Flags { get; set; } - public Guid ClassId { get; set; } - public SIZE Size { get; set; } - public POINT Point { get; set; } - public FileAttributes FileAttributes { get; set; } - public DateTime CreationTime { get; set; } - public DateTime LastAccessTime { get; set; } - public DateTime LastWriteTime { get; set; } - public Int64 FileSize { get; set; } - public string FileName { get; set; } - - public FileDescriptor(BinaryReader reader) - { - //Flags - Flags = (FileDescriptorFlags) reader.ReadUInt32(); - //ClassID - ClassId = new Guid(reader.ReadBytes(16)); - //Size - Size = new SIZE(reader.ReadInt32(), reader.ReadInt32()); - //Point - Point = new POINT(reader.ReadInt32(), reader.ReadInt32()); - //FileAttributes - FileAttributes = (FileAttributes) reader.ReadUInt32(); - //CreationTime - CreationTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64()); - //LastAccessTime - LastAccessTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64()); - //LastWriteTime - LastWriteTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64()); - //FileSize - FileSize = reader.ReadInt64(); - //FileName - byte[] nameBytes = reader.ReadBytes(520); - int i = 0; - while (i < nameBytes.Length) - { - if (nameBytes[i] == 0 && nameBytes[i + 1] == 0) - break; - i++; - i++; - } - - FileName = Encoding.Unicode.GetString(nameBytes, 0, i); - } - } - public static IEnumerable Read(Stream fileDescriptorStream) { if (fileDescriptorStream == null) diff --git a/src/Greenshot/Destinations/FileDestination.cs b/src/Greenshot/Destinations/FileDestination.cs index 677fc0e18..9389c3d26 100644 --- a/src/Greenshot/Destinations/FileDestination.cs +++ b/src/Greenshot/Destinations/FileDestination.cs @@ -55,12 +55,12 @@ namespace Greenshot.Destinations public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); + var exportInformation = new ExportInformation(Designation, Description); bool outputMade; bool overwrite; string fullPath; // Get output settings from the configuration - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(); + var outputSettings = new SurfaceOutputSettings(); if (captureDetails?.Filename != null) { @@ -79,7 +79,7 @@ namespace Greenshot.Destinations if (CoreConfig.OutputFilePromptQuality) { - QualityDialog qualityDialog = new QualityDialog(outputSettings); + var qualityDialog = new QualityDialog(outputSettings); qualityDialog.ShowDialog(); }