From 5779d2ac2bda427e8d1540de6bcb3df1c584409e Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 28 Sep 2016 21:50:36 +0200 Subject: [PATCH] Made the Win10 share work, at least somewhat. --- .../GreenshotWin10Plugin.csproj | 10 +- .../MemoryRandomAccessStream.cs | 69 +++++++++ .../Native/DataTransferManagerHelper.cs | 64 ++++++++ .../Native/IDataTransferManagerInterOp.cs | 57 +++++++ GreenshotWin10Plugin/Win10OcrDestination.cs | 13 +- GreenshotWin10Plugin/Win10Plugin.cs | 10 ++ GreenshotWin10Plugin/Win10ShareDestination.cs | 146 ++++++++++++++++++ 7 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 GreenshotWin10Plugin/MemoryRandomAccessStream.cs create mode 100644 GreenshotWin10Plugin/Native/DataTransferManagerHelper.cs create mode 100644 GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs create mode 100644 GreenshotWin10Plugin/Win10ShareDestination.cs diff --git a/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj b/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj index 9aa9b6a71..3830b6f20 100644 --- a/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj +++ b/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj @@ -12,6 +12,7 @@ GreenshotWin10Plugin v4.5 512 + true @@ -35,6 +36,8 @@ + + @@ -43,16 +46,19 @@ + + + + - + False ..\Greenshot\lib\log4net.dll - diff --git a/GreenshotWin10Plugin/MemoryRandomAccessStream.cs b/GreenshotWin10Plugin/MemoryRandomAccessStream.cs new file mode 100644 index 000000000..4b3476a04 --- /dev/null +++ b/GreenshotWin10Plugin/MemoryRandomAccessStream.cs @@ -0,0 +1,69 @@ +using System.IO; +using Windows.Storage.Streams; + +namespace GreenshotWin10Plugin +{ + public sealed class MemoryRandomAccessStream : MemoryStream, IRandomAccessStream + { + public MemoryRandomAccessStream() + { + } + + public MemoryRandomAccessStream(byte[] bytes) + { + Write(bytes, 0, bytes.Length); + } + + public IInputStream GetInputStreamAt(ulong position) + { + Seek((long)position, SeekOrigin.Begin); + + return this.AsInputStream(); + } + + public IOutputStream GetOutputStreamAt(ulong position) + { + Seek((long)position, SeekOrigin.Begin); + + return this.AsOutputStream(); + } + + ulong IRandomAccessStream.Position => (ulong)Position; + + public ulong Size + { + get { return (ulong)Length; } + set { SetLength((long)value); } + } + + public IRandomAccessStream CloneStream() + { + var cloned = new MemoryRandomAccessStream(); + CopyTo(cloned); + return cloned; + } + + public void Seek(ulong position) + { + Seek((long)position, SeekOrigin.Begin); + } + + public Windows.Foundation.IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) + { + var inputStream = GetInputStreamAt(0); + return inputStream.ReadAsync(buffer, count, options); + } + + Windows.Foundation.IAsyncOperation IOutputStream.FlushAsync() + { + var outputStream = GetOutputStreamAt(0); + return outputStream.FlushAsync(); + } + + public Windows.Foundation.IAsyncOperationWithProgress WriteAsync(IBuffer buffer) + { + var outputStream = GetOutputStreamAt(0); + return outputStream.WriteAsync(buffer); + } + } +} diff --git a/GreenshotWin10Plugin/Native/DataTransferManagerHelper.cs b/GreenshotWin10Plugin/Native/DataTransferManagerHelper.cs new file mode 100644 index 000000000..cca7d08c3 --- /dev/null +++ b/GreenshotWin10Plugin/Native/DataTransferManagerHelper.cs @@ -0,0 +1,64 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel.DataTransfer; + +namespace GreenshotWin10Plugin.Native +{ + public class DataTransferManagerHelper + { + private const string DataTransferManagerId = "a5caee9b-8708-49d1-8d36-67d25a8da00c"; + private readonly IDataTransferManagerInterOp _dataTransferManagerInterOp; + private readonly IntPtr _windowHandle; + + public DataTransferManager DataTransferManager + { + get; + private set; + } + + public DataTransferManagerHelper(IntPtr handle) + { + //TODO: Add a check for failure here. This will fail for versions of Windows below Windows 10 + IActivationFactory activationFactory = WindowsRuntimeMarshal.GetActivationFactory(typeof(DataTransferManager)); + + // ReSharper disable once SuspiciousTypeConversion.Global + _dataTransferManagerInterOp = (IDataTransferManagerInterOp)activationFactory; + + _windowHandle = handle; + var riid = new Guid(DataTransferManagerId); + DataTransferManager dataTransferManager; + _dataTransferManagerInterOp.GetForWindow(_windowHandle, riid, out dataTransferManager); + DataTransferManager = dataTransferManager; + } + + /// + /// Show the share UI + /// + public void ShowShareUi() + { + _dataTransferManagerInterOp.ShowShareUIForWindow(_windowHandle); + } + } + +} diff --git a/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs b/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs new file mode 100644 index 000000000..f350e7372 --- /dev/null +++ b/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs @@ -0,0 +1,57 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Runtime.InteropServices; +using Windows.ApplicationModel.DataTransfer; + +namespace GreenshotWin10Plugin.Native +{ + /// + /// The IDataTransferManagerInterOp is documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/jj542488(v=vs.85).aspx. + /// This interface allows an app to tie the share context to a specific + /// window using a window handle. Useful for Win32 apps. + /// + [ComImport, Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IDataTransferManagerInterOp + { + /// + /// Get an instance of Windows.ApplicationModel.DataTransfer.DataTransferManager + /// for the window identified by a window handle + /// + /// The window handle + /// ID of the DataTransferManager interface + /// The DataTransferManager instance for this window handle + /// HRESULT + [PreserveSig] + uint GetForWindow([In] IntPtr appWindow, [In] ref Guid riid, [Out] out DataTransferManager pDataTransferManager); + + /// + /// Show the share flyout for the window identified by a window handle + /// + /// The window handle + /// HRESULT + [PreserveSig] + uint ShowShareUIForWindow(IntPtr appWindow); + } + +} diff --git a/GreenshotWin10Plugin/Win10OcrDestination.cs b/GreenshotWin10Plugin/Win10OcrDestination.cs index 9b2730455..e12bcf0fc 100644 --- a/GreenshotWin10Plugin/Win10OcrDestination.cs +++ b/GreenshotWin10Plugin/Win10OcrDestination.cs @@ -39,6 +39,9 @@ namespace GreenshotWin10Plugin public override string Designation { get; } = "OCR"; public override string Description { get; } = "Windows 10 OCR"; + /// + /// Constructor, this is only debug information + /// public Win10OcrDestination() { var languages = OcrEngine.AvailableRecognizerLanguages; @@ -58,7 +61,7 @@ namespace GreenshotWin10Plugin /// ExportInformation public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); + var exportInformation = new ExportInformation(Designation, Description); try { var text = Task.Run(async () => @@ -76,7 +79,13 @@ namespace GreenshotWin10Plugin return ocrResult.Text; } }).Result; - ClipboardHelper.SetClipboardData(text); + + // Check if we found text + if (!string.IsNullOrWhiteSpace(text)) + { + // Place the OCR text on the + ClipboardHelper.SetClipboardData(text); + } exportInformation.ExportMade = true; } catch (Exception ex) diff --git a/GreenshotWin10Plugin/Win10Plugin.cs b/GreenshotWin10Plugin/Win10Plugin.cs index 38826a0cf..527411d8f 100644 --- a/GreenshotWin10Plugin/Win10Plugin.cs +++ b/GreenshotWin10Plugin/Win10Plugin.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using Greenshot.Plugin; +using GreenshotPlugin.Core; namespace GreenshotWin10Plugin { @@ -48,9 +49,18 @@ namespace GreenshotWin10Plugin throw new NotImplementedException(); } + /// + /// yields the windows 10 destinations if Windows 10 is detected + /// + /// IEnumerable with the destinations public IEnumerable Destinations() { + if (!Environment.OSVersion.IsWindows10()) + { + yield break; + } yield return new Win10OcrDestination(); + yield return new Win10ShareDestination(); } public IEnumerable Processors() diff --git a/GreenshotWin10Plugin/Win10ShareDestination.cs b/GreenshotWin10Plugin/Win10ShareDestination.cs new file mode 100644 index 000000000..5eb16487c --- /dev/null +++ b/GreenshotWin10Plugin/Win10ShareDestination.cs @@ -0,0 +1,146 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://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 Windows.Storage.Streams; +using Greenshot.Plugin; +using GreenshotPlugin.Core; +using GreenshotWin10Plugin.Native; +using System.Threading.Tasks; +using Color = Windows.UI.Color; + +namespace GreenshotWin10Plugin +{ + /// + /// This uses the Share from Windows 10 to make the capture available to apps. + /// + public class Win10ShareDestination : AbstractDestination + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10ShareDestination)); + + public override string Designation { get; } = "Share"; + public override string Description { get; } = "Windows 10 share"; + + /// + /// Share the screenshot with a windows app + /// + /// + /// + /// + /// ExportInformation + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + var exportInformation = new ExportInformation(Designation, Description); + try + { + var handle = PluginUtils.Host.GreenshotForm.Handle; + + var exportTarget = Task.Run(async () => + { + var taskCompletionSource = new TaskCompletionSource(); + + using (var imageStream = new MemoryRandomAccessStream()) + using (var logoStream = new MemoryRandomAccessStream()) + using (var thumbnailStream = new MemoryRandomAccessStream()) + { + // Create capture for export + ImageOutput.SaveToStream(surface, imageStream, new SurfaceOutputSettings()); + imageStream.Position = 0; + var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); + RandomAccessStreamReference thumbnailRandomAccessStreamReference; + RandomAccessStreamReference logoRandomAccessStreamReference; + + // Create thumbnail + using (var tmpImageForThumbnail = surface.GetImageForExport()) + { + using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160)) + { + ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, new SurfaceOutputSettings()); + thumbnailStream.Position = 0; + thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); + } + } + // Create logo + using (var logo = GreenshotResources.getGreenshotIcon().ToBitmap()) + { + using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30)) + { + ImageOutput.SaveToStream(logoThumbnail, null, logoStream, new SurfaceOutputSettings()); + logoStream.Position = 0; + logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); + } + } + string applicationName = null; + var dataTransferManagerHelper = new DataTransferManagerHelper(handle); + dataTransferManagerHelper.DataTransferManager.TargetApplicationChosen += (dtm, args) => + { + Log.DebugFormat("Trying to share with {0}", args.ApplicationName); + applicationName = args.ApplicationName; + }; + dataTransferManagerHelper.DataTransferManager.DataRequested += (sender, args) => + { + var deferral = args.Request.GetDeferral(); + args.Request.Data.OperationCompleted += (dp, eventArgs) => + { + Log.DebugFormat("OperationCompleted: {0}, shared with", eventArgs.Operation); + taskCompletionSource.TrySetResult(applicationName); + }; + var dataPackage = args.Request.Data; + dataPackage.Properties.Title = captureDetails.Title; + dataPackage.Properties.ApplicationName = "Greenshot"; + dataPackage.Properties.Description = "Share a screenshot"; + dataPackage.Properties.Thumbnail = thumbnailRandomAccessStreamReference; + dataPackage.Properties.Square30x30Logo = logoRandomAccessStreamReference; + dataPackage.Properties.LogoBackgroundColor = Color.FromArgb(0xff, 0x3d, 0x3d, 0x3d); + dataPackage.SetBitmap(imageRandomAccessStreamReference); + dataPackage.Destroyed += (dp, o) => + { + Log.Debug("Destroyed."); + taskCompletionSource.TrySetCanceled(); + }; + deferral.Complete(); + }; + dataTransferManagerHelper.ShowShareUi(); + return await taskCompletionSource.Task; + } + }).Result; + if (string.IsNullOrWhiteSpace(exportTarget)) + { + exportInformation.ExportMade = false; + } + else + { + exportInformation.ExportMade = true; + exportInformation.DestinationDescription = exportTarget; + } + } + catch (Exception ex) + { + exportInformation.ExportMade = false; + exportInformation.ErrorMessage = ex.Message; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + + } + } +}