diff --git a/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj b/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj index 0769a926a..85d3f19f9 100644 --- a/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj +++ b/GreenshotWin10Plugin/GreenshotWin10Plugin.csproj @@ -11,8 +11,10 @@ PreserveNewest + + diff --git a/GreenshotWin10Plugin/MemoryRandomAccessStream.cs b/GreenshotWin10Plugin/MemoryRandomAccessStream.cs index 4b3476a04..7b18328ca 100644 --- a/GreenshotWin10Plugin/MemoryRandomAccessStream.cs +++ b/GreenshotWin10Plugin/MemoryRandomAccessStream.cs @@ -1,66 +1,106 @@ -using System.IO; +/* + * 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.IO; using Windows.Storage.Streams; namespace GreenshotWin10Plugin { + /// + /// This is an IRandomAccessStream implementation which uses a MemoryStream + /// public sealed class MemoryRandomAccessStream : MemoryStream, IRandomAccessStream { + /// + /// Default constructor + /// public MemoryRandomAccessStream() { } + /// + /// Constructor where also bytes are already passed + /// + /// byte array public MemoryRandomAccessStream(byte[] bytes) { Write(bytes, 0, bytes.Length); } - public IInputStream GetInputStreamAt(ulong position) + /// + public IInputStream GetInputStreamAt(ulong position) { Seek((long)position, SeekOrigin.Begin); return this.AsInputStream(); } - public IOutputStream GetOutputStreamAt(ulong position) + /// + public IOutputStream GetOutputStreamAt(ulong position) { Seek((long)position, SeekOrigin.Begin); return this.AsOutputStream(); } - ulong IRandomAccessStream.Position => (ulong)Position; + /// + ulong IRandomAccessStream.Position => (ulong)Position; - public ulong Size + /// + public ulong Size { get { return (ulong)Length; } set { SetLength((long)value); } } - public IRandomAccessStream CloneStream() + /// + public IRandomAccessStream CloneStream() { var cloned = new MemoryRandomAccessStream(); CopyTo(cloned); return cloned; } - public void Seek(ulong position) + /// + public void Seek(ulong position) { Seek((long)position, SeekOrigin.Begin); } - public Windows.Foundation.IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) + /// + 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() + /// + Windows.Foundation.IAsyncOperation IOutputStream.FlushAsync() { var outputStream = GetOutputStreamAt(0); return outputStream.FlushAsync(); } - public Windows.Foundation.IAsyncOperationWithProgress WriteAsync(IBuffer buffer) + /// + 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 index b20725bf5..734d5f73f 100644 --- a/GreenshotWin10Plugin/Native/DataTransferManagerHelper.cs +++ b/GreenshotWin10Plugin/Native/DataTransferManagerHelper.cs @@ -1,20 +1,20 @@ /* * 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 . */ @@ -36,12 +36,18 @@ namespace GreenshotWin10Plugin.Native private readonly IDataTransferManagerInterOp _dataTransferManagerInterOp; private readonly IntPtr _windowHandle; + /// + /// The DataTransferManager + /// public DataTransferManager DataTransferManager { get; private set; } - + /// + /// Constructor which takes a handle to initialize + /// + /// public DataTransferManagerHelper(IntPtr handle) { //TODO: Add a check for failure here. This will fail for versions of Windows below Windows 10 @@ -52,8 +58,8 @@ namespace GreenshotWin10Plugin.Native _windowHandle = handle; var riid = new Guid(DataTransferManagerId); - var hresult = _dataTransferManagerInterOp.GetForWindow(_windowHandle, riid, out var dataTransferManager); - if (hresult != 0) + var hresult = _dataTransferManagerInterOp.GetForWindow(_windowHandle, riid, out var dataTransferManager); + if (hresult.Failed()) { Log.WarnFormat("HResult for GetForWindow: {0}", hresult); } @@ -66,9 +72,9 @@ namespace GreenshotWin10Plugin.Native public void ShowShareUi() { var hresult = _dataTransferManagerInterOp.ShowShareUIForWindow(_windowHandle); - if (hresult != 0) - { - Log.WarnFormat("HResult for ShowShareUIForWindow: {0}", hresult); + if (hresult.Failed()) + { + Log.WarnFormat("HResult for ShowShareUIForWindow: {0}", hresult); } else { diff --git a/GreenshotWin10Plugin/Native/HResult.cs b/GreenshotWin10Plugin/Native/HResult.cs new file mode 100644 index 000000000..9a36be173 --- /dev/null +++ b/GreenshotWin10Plugin/Native/HResult.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GreenshotWin10Plugin.Native +{ + /// + /// The HRESULT represents Windows error codes + /// See wikipedia + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum HResult + { +#pragma warning disable 1591 + S_OK = 0, + S_FALSE = 1, + E_FAIL = unchecked((int)0x80004005), + E_INVALIDARG = unchecked((int)0x80070057), + E_NOTIMPL = unchecked((int)0x80004001), + E_POINTER = unchecked((int)0x80004003), + E_PENDING = unchecked((int)0x8000000A), + E_NOINTERFACE = unchecked((int)0x80004002), + E_ABORT = unchecked((int)0x80004004), + E_ACCESSDENIED = unchecked((int)0x80070006), + E_HANDLE = unchecked((int)0x80070006), + E_UNEXPECTED = unchecked((int)0x8000FFFF), + E_FILENOTFOUND = unchecked((int)0x80070002), + E_PATHNOTFOUND = unchecked((int)0x80070003), + E_INVALID_DATA = unchecked((int)0x8007000D), + E_OUTOFMEMORY = unchecked((int)0x8007000E), + E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A), + WSAECONNABORTED = unchecked((int)0x80072745), + WSAECONNRESET = unchecked((int)0x80072746), + ERROR_TOO_MANY_CMDS = unchecked((int)0x80070038), + ERROR_NOT_SUPPORTED = unchecked((int)0x80070032), + TYPE_E_ELEMENTNOTFOUND = unchecked((int)0x8002802B) +#pragma warning restore 1591 + } +} diff --git a/GreenshotWin10Plugin/Native/HResultExtensions.cs b/GreenshotWin10Plugin/Native/HResultExtensions.cs new file mode 100644 index 000000000..55ec38073 --- /dev/null +++ b/GreenshotWin10Plugin/Native/HResultExtensions.cs @@ -0,0 +1,64 @@ +// Greenshot - a free and open source screenshot tool +// Copyright (C) 2007-2020 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.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace GreenshotWin10Plugin.Native +{ + /// + /// Extensions to handle the HResult + /// + public static class HResultExtensions + { + /// + /// Test if the HResult respresents a fail + /// + /// HResult + /// bool + [Pure] + public static bool Failed(this HResult hResult) + { + return hResult < 0; + } + + /// + /// Test if the HResult respresents a success + /// + /// HResult + /// bool + [Pure] + public static bool Succeeded(this HResult hResult) + { + return hResult >= HResult.S_OK; + } + + /// + /// Throw an exception on Failure + /// + /// HResult + public static void ThrowOnFailure(this HResult hResult) + { + if (Failed(hResult)) + { + throw Marshal.GetExceptionForHR((int)hResult); + } + } + } +} diff --git a/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs b/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs index f350e7372..470c03f6b 100644 --- a/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs +++ b/GreenshotWin10Plugin/Native/IDataTransferManagerInterOp.cs @@ -1,23 +1,21 @@ -/* - * 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 . - */ +// Greenshot - a free and open source screenshot tool +// Copyright (C) 2007-2020 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 System.Runtime.InteropServices; @@ -43,7 +41,7 @@ namespace GreenshotWin10Plugin.Native /// The DataTransferManager instance for this window handle /// HRESULT [PreserveSig] - uint GetForWindow([In] IntPtr appWindow, [In] ref Guid riid, [Out] out DataTransferManager pDataTransferManager); + HResult GetForWindow([In] IntPtr appWindow, [In] ref Guid riid, [Out] out DataTransferManager pDataTransferManager); /// /// Show the share flyout for the window identified by a window handle @@ -51,7 +49,7 @@ namespace GreenshotWin10Plugin.Native /// The window handle /// HRESULT [PreserveSig] - uint ShowShareUIForWindow(IntPtr appWindow); + HResult ShowShareUIForWindow(IntPtr appWindow); } } diff --git a/GreenshotWin10Plugin/Native/WindowMessageInfo.cs b/GreenshotWin10Plugin/Native/WindowMessageInfo.cs new file mode 100644 index 000000000..231e3ec14 --- /dev/null +++ b/GreenshotWin10Plugin/Native/WindowMessageInfo.cs @@ -0,0 +1,70 @@ +// Greenshot - a free and open source screenshot tool +// Copyright (C) 2007-2020 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 GreenshotPlugin.UnmanagedHelpers; +using System; + +namespace Greenshot.Addon.Win10.Native +{ + /// + /// Container for the windows messages + /// + public class WindowMessageInfo + { + /// + /// IntPtr with the Handle of the window + /// + public IntPtr Handle { get; private set; } + + /// + /// WindowsMessages which is the actual message + /// + public WindowsMessages Message { get; private set; } + + /// + /// IntPtr with the word-param + /// + public IntPtr WordParam { get; private set; } + + /// + /// IntPtr with the long-param + /// + public IntPtr LongParam { get; private set; } + + /// + /// Factory method for the Window Message Info + /// + /// IntPtr with the Handle of the window + /// WindowsMessages which is the actual message + /// IntPtr with the word-param + /// IntPtr with the long-param + /// + /// WindowMessageInfo + public static WindowMessageInfo Create(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + return new WindowMessageInfo + { + Handle = hwnd, + Message = (WindowsMessages)msg, + WordParam = wParam, + LongParam = lParam + }; + } + } +} \ No newline at end of file diff --git a/GreenshotWin10Plugin/Native/WindowMessageMonitor.cs b/GreenshotWin10Plugin/Native/WindowMessageMonitor.cs new file mode 100644 index 000000000..81e40ec66 --- /dev/null +++ b/GreenshotWin10Plugin/Native/WindowMessageMonitor.cs @@ -0,0 +1,137 @@ +// Greenshot - a free and open source screenshot tool +// Copyright (C) 2007-2020 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 System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Windows; +using System.Windows.Interop; + +namespace Greenshot.Addon.Win10.Native +{ + + /// + /// A monitor for window messages + /// + public static class WindowMessageMonitor + { + /// + /// Create a HwndSource for the specified Window + /// + /// Window + /// HwndSource + private static HwndSource ToHwndSource(this Window window) + { + IntPtr windowHandle = new WindowInteropHelper(window).Handle; + if (windowHandle == IntPtr.Zero) + { + return null; + } + return HwndSource.FromHwnd(windowHandle); + } + + /// + /// Create an observable for the specified window + /// + public static IObservable WindowMessages(this Window window) + { + return WindowMessages(window, null); + } + + /// + /// Create an observable for the specified HwndSource + /// + public static IObservable WindowMessages(this HwndSource hwndSource) + { + return WindowMessages(null, hwndSource); + } + + /// + /// Create an observable for the specified window or HwndSource + /// + /// Window + /// HwndSource + /// IObservable + private static IObservable WindowMessages(Window window, HwndSource hwndSource) + { + if (window == null && hwndSource == null) + { + throw new NotSupportedException("One of Window or HwndSource must be supplied"); + } + if (window != null && hwndSource != null) + { + throw new NotSupportedException("Either Window or HwndSource must be supplied"); + } + + return Observable.Create(observer => + { + // This handles the message, and generates the observable OnNext + IntPtr WindowMessageHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + observer.OnNext(WindowMessageInfo.Create(hwnd, msg, wParam, lParam, ref handled)); + // ReSharper disable once AccessToDisposedClosure + if (hwndSource.IsDisposed) + { + observer.OnCompleted(); + } + return IntPtr.Zero; + } + + void HwndSourceDisposedHandle(object sender, EventArgs e) + { + observer.OnCompleted(); + } + + void RegisterHwndSource() + { + hwndSource.Disposed += HwndSourceDisposedHandle; + hwndSource.AddHook(WindowMessageHandler); + } + + if (window != null) + { + hwndSource = window.ToHwndSource(); + } + if (hwndSource != null) + { + RegisterHwndSource(); + } + else + { + // No, try to get it later + window.SourceInitialized += (sender, args) => + { + hwndSource = window.ToHwndSource(); + RegisterHwndSource(); + }; + } + + return Disposable.Create(() => + { + hwndSource.Disposed -= HwndSourceDisposedHandle; + hwndSource.RemoveHook(WindowMessageHandler); + hwndSource.Dispose(); + }); + }) + // Make sure there is always a value produced when connecting + .Publish() + .RefCount(); + } + } +} diff --git a/GreenshotWin10Plugin/Win10Plugin.cs b/GreenshotWin10Plugin/Win10Plugin.cs index 5f440ea38..815ebc787 100644 --- a/GreenshotWin10Plugin/Win10Plugin.cs +++ b/GreenshotWin10Plugin/Win10Plugin.cs @@ -1,20 +1,20 @@ /* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 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 . */ @@ -31,7 +31,6 @@ namespace GreenshotWin10Plugin /// public class Win10Plugin : IGreenshotPlugin { - public void Dispose() { Dispose(true); @@ -67,7 +66,7 @@ namespace GreenshotWin10Plugin { yield break; } - + /// /// Implementation of the IGreenshotPlugin.Initialize /// diff --git a/GreenshotWin10Plugin/Win10ShareDestination.cs b/GreenshotWin10Plugin/Win10ShareDestination.cs index da8191fba..cb6d3f3cc 100644 --- a/GreenshotWin10Plugin/Win10ShareDestination.cs +++ b/GreenshotWin10Plugin/Win10ShareDestination.cs @@ -1,178 +1,261 @@ /* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 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 System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Interop; +using Windows.ApplicationModel.DataTransfer; +using Windows.Storage; using Windows.Storage.Streams; +using Greenshot.Addon.Win10.Native; +using Color = Windows.UI.Color; +using System.Reactive.Linq; +using GreenshotPlugin.UnmanagedHelpers; using Greenshot.Plugin; using GreenshotPlugin.Core; -using GreenshotWin10Plugin.Native; -using System.Threading.Tasks; -using Windows.Storage; -using Color = Windows.UI.Color; -using System.Collections.Generic; using System.Drawing; +using GreenshotWin10Plugin.Native; 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)); + /// + /// 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; } = "WIN10Share"; - public override string Description { get; } = "Windows 10 share"; + public override string Designation { get; } = "WIN10Share"; + public override string Description { get; } = "Windows 10 share"; - /// - /// Icon for the App-share, the icon was found via: http://help4windows.com/windows_8_shell32_dll.shtml - /// - public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(FilenameHelper.FillCmdVariables(@"%windir%\system32\shell32.dll"), 238); + /// + /// Icon for the App-share, the icon was found via: http://help4windows.com/windows_8_shell32_dll.shtml + /// + public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(FilenameHelper.FillCmdVariables(@"%windir%\system32\shell32.dll"), 238); - /// - /// Share the screenshot with a windows app - /// - /// - /// - /// - /// ExportInformation + private class ShareInfo + { + public string ApplicationName { get; set; } + public bool AreShareProvidersRequested { get; set; } + public bool IsDeferredFileCreated { get; set; } + public DataPackageOperation CompletedWithOperation { get; set; } + public string AcceptedFormat { get; set; } + public bool IsDestroyed { get; set; } + public bool IsShareCompleted { get; set; } + + public TaskCompletionSource ShareTask { get; } = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + public bool IsDataRequested { get; set; } + } + + /// + /// 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 exportInformation = new ExportInformation(Designation, Description); + try + { + var triggerWindow = new Window + { + WindowState = WindowState.Normal, + WindowStartupLocation = WindowStartupLocation.CenterScreen, + Width = 400, + Height = 400 + }; - var exportTarget = Task.Run(async () => - { - var taskCompletionSource = new TaskCompletionSource(); + triggerWindow.Show(); + var shareInfo = new ShareInfo(); - using var imageStream = new MemoryRandomAccessStream(); - using var logoStream = new MemoryRandomAccessStream(); - using var thumbnailStream = new MemoryRandomAccessStream(); - var outputSettings = new SurfaceOutputSettings(); - outputSettings.PreventGreenshotFormat(); - - // Create capture for export - ImageOutput.SaveToStream(surface, imageStream, outputSettings); - imageStream.Position = 0; - Log.Info("Created RandomAccessStreamReference for the image"); - var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); - RandomAccessStreamReference thumbnailRandomAccessStreamReference; - RandomAccessStreamReference logoRandomAccessStreamReference; - - // Create thumbnail - using (var tmpImageForThumbnail = surface.GetImageForExport()) + // This is a bad trick, but don't know how else to do it. + // Wait for the focus to return, and depending on the state close the window! + triggerWindow.WindowMessages() + .Where(m => m.Message == WindowsMessages.WM_SETFOCUS).Delay(TimeSpan.FromSeconds(1)) + .Subscribe(info => { - using var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160); - ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); - thumbnailStream.Position = 0; - thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); - Log.Info("Created RandomAccessStreamReference for the thumbnail"); - } - // Create logo - using (var logo = GreenshotResources.getGreenshotIcon().ToBitmap()) - { - using var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30); - ImageOutput.SaveToStream(logoThumbnail, null, logoStream, outputSettings); - logoStream.Position = 0; - logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); - Log.Info("Created RandomAccessStreamReference for the logo"); - } - string applicationName = null; - var dataTransferManagerHelper = new DataTransferManagerHelper(handle); - dataTransferManagerHelper.DataTransferManager.TargetApplicationChosen += (dtm, args) => - { - Log.InfoFormat("Trying to share with {0}", args.ApplicationName); - applicationName = args.ApplicationName; - }; - var filename = FilenameHelper.GetFilename(OutputFormat.png, captureDetails); - var storageFile = await StorageFile.CreateStreamedFileAsync(filename, async streamedFileDataRequest => - { - // Information on how was found here: https://socialeboladev.wordpress.com/2013/03/15/how-to-use-createstreamedfileasync/ - Log.DebugFormat("Creating deferred file {0}", filename); - try + if (shareInfo.ApplicationName != null) { - using (var deferredStream = streamedFileDataRequest.AsStreamForWrite()) - { - await imageStream.CopyToAsync(deferredStream).ConfigureAwait(false); - await imageStream.FlushAsync().ConfigureAwait(false); - } - // Signal that the stream is ready - streamedFileDataRequest.Dispose(); + return; } - catch (Exception) - { - streamedFileDataRequest.FailAndClose(StreamedFileFailureMode.Incomplete); - } - // Signal transfer ready to the await down below - taskCompletionSource.TrySetResult(applicationName); - }, imageRandomAccessStreamReference).AsTask().ConfigureAwait(false); - - dataTransferManagerHelper.DataTransferManager.DataRequested += (sender, args) => + + shareInfo.ShareTask.TrySetResult(false); + }); + var windowHandle = new WindowInteropHelper(triggerWindow).Handle; + + Share(shareInfo, windowHandle, surface, captureDetails).GetAwaiter().GetResult(); + Log.Debug("Sharing finished, closing window."); + triggerWindow.Close(); + if (string.IsNullOrWhiteSpace(shareInfo.ApplicationName)) + { + exportInformation.ExportMade = false; + } + else + { + exportInformation.ExportMade = true; + exportInformation.DestinationDescription = shareInfo.ApplicationName; + } + } + catch (Exception ex) + { + exportInformation.ExportMade = false; + exportInformation.ErrorMessage = ex.Message; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + + } + /// + /// Share the surface by using the Share-UI of Windows 10 + /// + /// ShareInfo + /// IntPtr with the handle for the hosting window + /// ISurface with the bitmap to share + /// ICaptureDetails + /// Task with string, which describes the application which was used to share with + private async Task Share(ShareInfo shareInfo, IntPtr handle, ISurface surface, ICaptureDetails captureDetails) + { + using (var imageStream = new MemoryRandomAccessStream()) + using (var logoStream = new MemoryRandomAccessStream()) + using (var thumbnailStream = new MemoryRandomAccessStream()) + { + var outputSettings = new SurfaceOutputSettings(); + outputSettings.PreventGreenshotFormat(); + + // Create capture for export + ImageOutput.SaveToStream(surface, imageStream, outputSettings); + imageStream.Position = 0; + Log.Debug("Created RandomAccessStreamReference for the image"); + var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); + + // Create thumbnail + RandomAccessStreamReference thumbnailRandomAccessStreamReference; + using (var tmpImageForThumbnail = surface.GetImageForExport()) + using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160)) + { + ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); + thumbnailStream.Position = 0; + thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); + Log.Debug("Created RandomAccessStreamReference for the thumbnail"); + } + + // Create logo + RandomAccessStreamReference logoRandomAccessStreamReference; + using (var logo = GreenshotResources.getGreenshotIcon().ToBitmap()) + using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30)) + { + ImageOutput.SaveToStream(logoThumbnail, null, logoStream, outputSettings); + logoStream.Position = 0; + logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); + Log.Info("Created RandomAccessStreamReference for the logo"); + } + + var dataTransferManagerHelper = new DataTransferManagerHelper(handle); + dataTransferManagerHelper.DataTransferManager.ShareProvidersRequested += (sender, args) => + { + shareInfo.AreShareProvidersRequested = true; + Log.DebugFormat("Share providers requested: {0}", string.Join(",", args.Providers.Select(p => p.Title))); + }; + dataTransferManagerHelper.DataTransferManager.TargetApplicationChosen += (dtm, args) => + { + shareInfo.ApplicationName = args.ApplicationName; + Log.DebugFormat("TargetApplicationChosen: {0}", args.ApplicationName); + }; + var filename = FilenameHelper.GetFilename(OutputFormat.png, captureDetails); + var storageFile = await StorageFile.CreateStreamedFileAsync(filename, async streamedFileDataRequest => + { + shareInfo.IsDeferredFileCreated = true; + // Information on the "how" was found here: https://socialeboladev.wordpress.com/2013/03/15/how-to-use-createstreamedfileasync/ + Log.DebugFormat("Creating deferred file {0}", filename); + try { - var deferral = args.Request.GetDeferral(); - args.Request.Data.OperationCompleted += (dp, eventArgs) => + using (var deferredStream = streamedFileDataRequest.AsStreamForWrite()) + { + await imageStream.CopyToAsync(deferredStream).ConfigureAwait(false); + await imageStream.FlushAsync().ConfigureAwait(false); + } + // Signal that the stream is ready + streamedFileDataRequest.Dispose(); + // Signal that the action is ready, bitmap was exported + shareInfo.ShareTask.TrySetResult(true); + } + catch (Exception) + { + streamedFileDataRequest.FailAndClose(StreamedFileFailureMode.Incomplete); + } + }, imageRandomAccessStreamReference).AsTask().ConfigureAwait(false); + + dataTransferManagerHelper.DataTransferManager.DataRequested += (dataTransferManager, dataRequestedEventArgs) => + { + var deferral = dataRequestedEventArgs.Request.GetDeferral(); + try + { + shareInfo.IsDataRequested = true; + Log.DebugFormat("DataRequested with operation {0}", dataRequestedEventArgs.Request.Data.RequestedOperation); + var dataPackage = dataRequestedEventArgs.Request.Data; + dataPackage.OperationCompleted += (dp, eventArgs) => { Log.DebugFormat("OperationCompleted: {0}, shared with", eventArgs.Operation); - taskCompletionSource.TrySetResult(applicationName); + shareInfo.CompletedWithOperation = eventArgs.Operation; + shareInfo.AcceptedFormat = eventArgs.AcceptedFormatId; + + shareInfo.ShareTask.TrySetResult(true); + }; + dataPackage.Destroyed += (dp, o) => + { + shareInfo.IsDestroyed = true; + Log.Debug("Destroyed"); + shareInfo.ShareTask.TrySetResult(true); + }; + dataPackage.ShareCompleted += (dp, shareCompletedEventArgs) => + { + shareInfo.IsShareCompleted = true; + Log.Debug("ShareCompleted"); + shareInfo.ShareTask.TrySetResult(true); }; - 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.SetStorageItems(new List { storageFile }); + dataPackage.SetStorageItems(new[] {storageFile}); dataPackage.SetBitmap(imageRandomAccessStreamReference); - dataPackage.Destroyed += (dp, o) => - { - Log.Debug("Destroyed."); - }; + } + finally + { deferral.Complete(); - }; - dataTransferManagerHelper.ShowShareUi(); - return await taskCompletionSource.Task.ConfigureAwait(false); - }).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; - - } - } + Log.Debug("Called deferral.Complete()"); + } + }; + dataTransferManagerHelper.ShowShareUi(); + Log.Debug("ShowShareUi finished."); + await shareInfo.ShareTask.Task.ConfigureAwait(false); + } + } + } }