Fix for clipboard issues #307, this caused all kind of problems throughout the editor whenever the clipboard contents where checked. [skip ci]

This commit is contained in:
Robin Krom 2021-05-18 10:56:39 +02:00
commit 8e121f25f0
No known key found for this signature in database
GPG key ID: BCC01364F1371490
4 changed files with 215 additions and 96 deletions

View file

@ -299,39 +299,24 @@ EndSelection:<<<<<<<4
return true; return true;
} }
var fileDescriptor = (MemoryStream) dataObject.GetData("FileGroupDescriptorW"); foreach (var fileData in IterateClipboardContent(dataObject))
var files = FileDescriptorReader.Read(fileDescriptor);
var fileIndex = 0;
foreach (var fileContentFile in files)
{ {
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 try
{ {
//Do something with the fileContent Stream using (ImageHelper.FromStream(fileData))
if (IsValidStream(fileData))
{ {
fileData.Position = 0; // If we get here, there is an image
using (ImageHelper.FromStream(fileData)) return true;
{
// If we get here, there is an image
return true;
}
} }
} }
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
}
finally finally
{ {
fileData?.Dispose(); fileData?.Dispose();
} }
fileIndex++;
} }
if (dataObject.GetDataPresent(FORMAT_FILECONTENTS)) if (dataObject.GetDataPresent(FORMAT_FILECONTENTS))
@ -357,28 +342,116 @@ EndSelection:<<<<<<<4
// Try to get the image from the HTML code // Try to get the image from the HTML code
var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
if (textObject != null) if (textObject == null)
{ {
var doc = new HtmlDocument(); return false;
doc.LoadHtml(textObject); }
var imgNodes = doc.DocumentNode.SelectNodes("//img");
if (imgNodes != null) 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) return true;
{
var srcAttribute = imgNode.Attributes["src"];
var imageUrl = srcAttribute.Value;
if (!string.IsNullOrEmpty(imageUrl))
{
return true;
}
}
} }
} }
return false; return false;
} }
/// <summary>
/// Iterate the clipboard content
/// </summary>
/// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{MemoryStream}</returns>
private static IEnumerable<MemoryStream> IterateClipboardContent(IDataObject dataObject)
{
var fileDescriptors = AvailableFileDescriptors(dataObject);
if (fileDescriptors == null) yield break;
foreach (var fileData in IterateFileDescriptors(fileDescriptors, dataObject))
{
yield return fileData;
}
}
/// <summary>
/// Retrieve the FileDescriptor on the clipboard
/// </summary>
/// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{FileDescriptor}</returns>
private static IEnumerable<FileDescriptor> 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<FileDescriptor>();
}
/// <summary>
/// Iterate the file descriptors on the clipboard
/// </summary>
/// <param name="fileDescriptors">IEnumerable{FileDescriptor}</param>
/// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{MemoryStream}</returns>
private static IEnumerable<MemoryStream> IterateFileDescriptors(IEnumerable<FileDescriptor> 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++;
}
}
/// <summary> /// <summary>
/// Get the specified IDataObject format as a string /// Get the specified IDataObject format as a string
/// </summary> /// </summary>
@ -403,10 +476,10 @@ EndSelection:<<<<<<<4
/// Simple helper to check the stream /// Simple helper to check the stream
/// </summary> /// </summary>
/// <param name="memoryStream"></param> /// <param name="memoryStream"></param>
/// <returns></returns> /// <returns>true if there is a valid stream</returns>
private static bool IsValidStream(MemoryStream memoryStream) private static bool IsValidStream(MemoryStream memoryStream)
{ {
return memoryStream != null && memoryStream.Length > 0; return memoryStream?.Length > 0;
} }
/// <summary> /// <summary>
@ -426,7 +499,7 @@ EndSelection:<<<<<<<4
} }
/// <summary> /// <summary>
/// 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! /// Returned images must be disposed by the calling code!
/// </summary> /// </summary>
/// <param name="dataObject"></param> /// <param name="dataObject"></param>
@ -442,6 +515,26 @@ EndSelection:<<<<<<<4
} }
else 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 // check if files are supplied
foreach (string imageFile in GetImageFilenames(dataObject)) foreach (string imageFile in GetImageFilenames(dataObject))
{ {
@ -478,7 +571,7 @@ EndSelection:<<<<<<<4
string[] retrieveFormats; string[] retrieveFormats;
// Found a weird bug, where PNG's from Outlook 2010 are clipped // 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)) if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib))
{ {
// Outlook ?? // Outlook ??
@ -729,7 +822,7 @@ EndSelection:<<<<<<<4
/// This method will place images to the clipboard depending on the ClipboardFormats setting. /// 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 /// 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 /// 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! /// 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 /// For this problem the user should not use the direct paste (=Dib), but select Bitmap
/// </summary> /// </summary>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}
}

View file

@ -24,8 +24,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes; using System.Runtime.InteropServices.ComTypes;
using System.Text;
using Greenshot.Base.UnmanagedHelpers.Structs;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
@ -33,60 +31,12 @@ namespace Greenshot.Base.Core
/// Specifies which fields are valid in a FileDescriptor Structure /// Specifies which fields are valid in a FileDescriptor Structure
/// </summary> /// </summary>
[Flags] [Flags]
internal enum FileDescriptorFlags : uint public enum FileDescriptorFlags : uint
{ {
} }
internal static class FileDescriptorReader 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<FileDescriptor> Read(Stream fileDescriptorStream) public static IEnumerable<FileDescriptor> Read(Stream fileDescriptorStream)
{ {
if (fileDescriptorStream == null) if (fileDescriptorStream == null)

View file

@ -55,12 +55,12 @@ namespace Greenshot.Destinations
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) 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 outputMade;
bool overwrite; bool overwrite;
string fullPath; string fullPath;
// Get output settings from the configuration // Get output settings from the configuration
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(); var outputSettings = new SurfaceOutputSettings();
if (captureDetails?.Filename != null) if (captureDetails?.Filename != null)
{ {
@ -79,7 +79,7 @@ namespace Greenshot.Destinations
if (CoreConfig.OutputFilePromptQuality) if (CoreConfig.OutputFilePromptQuality)
{ {
QualityDialog qualityDialog = new QualityDialog(outputSettings); var qualityDialog = new QualityDialog(outputSettings);
qualityDialog.ShowDialog(); qualityDialog.ShowDialog();
} }