diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs index a3c12e21a..20de9b114 100644 --- a/Greenshot/Helpers/CaptureHelper.cs +++ b/Greenshot/Helpers/CaptureHelper.cs @@ -27,7 +27,6 @@ using System.Drawing.Printing; using System.IO; using System.Threading; using System.Windows.Forms; - using Greenshot.Configuration; using Greenshot.Destinations; using Greenshot.Drawing; @@ -37,6 +36,7 @@ using Greenshot.Plugin; using GreenshotPlugin.Core; using GreenshotPlugin.UnmanagedHelpers; using Greenshot.IniFile; +using Greenshot.Interop; namespace Greenshot.Helpers { /// @@ -410,6 +410,13 @@ namespace Greenshot.Helpers { private Thread PrepareForCaptureWithFeedback() { windows = new List(); + // If the App Launcher is visisble, no other windows are active + WindowDetails appLauncherWindow = WindowDetails.GetAppLauncher(); + if (appLauncherWindow != null && appLauncherWindow.Visible) { + windows.Add(appLauncherWindow); + return null; + } + Thread getWindowDetailsThread = new Thread (delegate() { // Start Enumeration of "active" windows List allWindows = WindowDetails.GetMetroApps(); @@ -733,7 +740,7 @@ namespace Greenshot.Helpers { windowCaptureMode = WindowCaptureMode.Screen; // Change to GDI, if allowed - if (WindowCapture.isGDIAllowed(process)) { + if (!windowToCapture.isMetroApp && WindowCapture.isGDIAllowed(process)) { if (!dwmEnabled && isWPF(process)) { // do not use GDI, as DWM is not enabled and the application uses PresentationFramework.dll -> isWPF LOG.InfoFormat("Not using GDI for windows of process {0}, as the process uses WPF", process.ProcessName); @@ -744,12 +751,12 @@ namespace Greenshot.Helpers { // Change to DWM, if enabled and allowed if (dwmEnabled) { - if (WindowCapture.isDWMAllowed(process)) { + if (windowToCapture.isMetroApp || WindowCapture.isDWMAllowed(process)) { windowCaptureMode = WindowCaptureMode.Aero; } } } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) { - if (!dwmEnabled || !WindowCapture.isDWMAllowed(process)) { + if (!dwmEnabled || (!windowToCapture.isMetroApp && !WindowCapture.isDWMAllowed(process))) { // Take default screen windowCaptureMode = WindowCaptureMode.Screen; // Change to GDI, if allowed @@ -828,7 +835,7 @@ namespace Greenshot.Helpers { break; case WindowCaptureMode.Aero: case WindowCaptureMode.AeroTransparent: - if (WindowCapture.isDWMAllowed(process)) { + if (windowToCapture.isMetroApp || WindowCapture.isDWMAllowed(process)) { tmpCapture = windowToCapture.CaptureDWMWindow(captureForWindow, windowCaptureMode, isAutoMode); } if (tmpCapture != null) { diff --git a/GreenshotPlugin/Core/WindowsHelper.cs b/GreenshotPlugin/Core/WindowsHelper.cs index 599d617ec..f879b60d7 100644 --- a/GreenshotPlugin/Core/WindowsHelper.cs +++ b/GreenshotPlugin/Core/WindowsHelper.cs @@ -30,6 +30,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; using Greenshot.IniFile; +using Greenshot.Interop; using Greenshot.Plugin; using GreenshotPlugin.UnmanagedHelpers; @@ -160,6 +161,7 @@ namespace GreenshotPlugin.Core { /// public class WindowDetails : IEquatable{ private const string METRO_WINDOWS_CLASS = "Windows.UI.Core.CoreWindow"; + private const string METRO_APPLAUNCHER_CLASS = "ImmersiveLauncher"; private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WindowDetails)); private static Dictionary> classnameTree = new Dictionary>(); private static CoreConfiguration conf = IniConfig.GetIniSection(); @@ -182,12 +184,25 @@ namespace GreenshotPlugin.Core { private WindowDetails parent = null; private bool frozen = false; + + public bool isApp { + get { + return METRO_WINDOWS_CLASS.Equals(ClassName); + } + } + + public bool isAppLauncher { + get { + return METRO_APPLAUNCHER_CLASS.Equals(ClassName); + } + } + /// /// Check if this window is the window of a metro app /// public bool isMetroApp { get { - return METRO_WINDOWS_CLASS.Equals(ClassName); + return isAppLauncher || isApp; } } @@ -555,6 +570,9 @@ namespace GreenshotPlugin.Core { /// public bool Iconic { get { + if (isMetroApp) { + return !Visible; + } return User32.IsIconic(this.hWnd) || Location.X <= -32000; } set { @@ -579,6 +597,22 @@ namespace GreenshotPlugin.Core { /// public bool Visible { get { + if (isApp) { + // IAppVisibility appVisibility = COMWrapper.CreateInstance(); + // if (appVisibility != null) { + // IntPtr monitor = User32.MonitorFromWindow(Handle, User32.MONITOR_DEFAULTTONULL); + // if (monitor != IntPtr.Zero) { + // LOG.DebugFormat("Monitor = {0}", monitor); + // MONITOR_APP_VISIBILITY monitorAppVisibility = appVisibility.GetAppVisibilityOnMonitor(monitor); + // LOG.DebugFormat("App visible: {0}", monitorAppVisibility); + // return monitorAppVisibility == MONITOR_APP_VISIBILITY.MAV_APP_VISIBLE; + // } + // } + + } + if (isAppLauncher) { + return IsAppLauncherVisible; + } return User32.IsWindowVisible(this.hWnd); } } @@ -837,7 +871,7 @@ namespace GreenshotPlugin.Core { // check if the capture fits if (!doesCaptureFit) { // if GDI is allowed.. (a screenshot won't be better than we comes if we continue) - if (WindowCapture.isGDIAllowed(Process)) { + if (!isMetroApp && WindowCapture.isGDIAllowed(Process)) { // we return null which causes the capturing code to try another method. return null; } @@ -1286,7 +1320,9 @@ namespace GreenshotPlugin.Core { public bool IsGreenshot { get { try { - return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName); + if (!isMetroApp) { + return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName); + } } catch (Exception ex) { LOG.Warn(ex); } @@ -1516,6 +1552,35 @@ namespace GreenshotPlugin.Core { } } } + + /// + /// Get the AppLauncher + /// + /// + public static WindowDetails GetAppLauncher() { + IntPtr appLauncher = User32.FindWindow("ImmersiveLauncher", null); + if (appLauncher != IntPtr.Zero) { + return new WindowDetails (appLauncher); + } + return null; + } + + /// + /// Return true if the metro-app-launcher is visible + /// + /// + public static bool IsAppLauncherVisible { + get { + try { + IAppVisibility appVisibility = COMWrapper.CreateInstance(); + if (appVisibility != null) { + return appVisibility.IsLauncherVisible; + } + } catch {} + return false; + + } + } } #endregion } diff --git a/GreenshotPlugin/Interop/COMWrapper.cs b/GreenshotPlugin/Interop/COMWrapper.cs index ff0cd07bf..eb0e7e476 100644 --- a/GreenshotPlugin/Interop/COMWrapper.cs +++ b/GreenshotPlugin/Interop/COMWrapper.cs @@ -57,6 +57,9 @@ namespace Greenshot.Interop { #endregion [DllImport("ole32.dll")] static extern int ProgIDFromCLSID([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID); + // Converts failure HRESULTs to exceptions: + [DllImport("oleaut32", PreserveSig=false)] + static extern void GetActiveObject(ref Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out Object ppunk); #region Construction @@ -81,35 +84,102 @@ namespace Greenshot.Interop { } string progId = progIDAttribute.Value; + object comObject = null; + // Convert from clsid to Prog ID, if needed if (progId.StartsWith("clsid:")) { Guid guid = new Guid(progId.Substring(6)); int result = ProgIDFromCLSID(ref guid, out progId); if (result != 0) { - LOG.WarnFormat("Error {0} getting progId {1}", result, progIDAttribute.Value); + // Restore progId, as it's overwritten + progId = progIDAttribute.Value; + + try { + GetActiveObject(ref guid, IntPtr.Zero, out comObject); + } catch (Exception) { + LOG.WarnFormat("Error {0} getting instance for class id {1}", result, progIDAttribute.Value); + } + if (comObject == null) { + LOG.WarnFormat("Error {0} getting progId {1}", result, progIDAttribute.Value); + } } else { LOG.InfoFormat("Mapped {0} to progId {1}", progIDAttribute.Value, progId); } } - object comObject = null; - try { - comObject = Marshal.GetActiveObject(progId); - } catch (COMException comE) { - if (comE.ErrorCode == MK_E_UNAVAILABLE) { - LOG.DebugFormat("No current instance of {0} object available.", progId); - } else if (comE.ErrorCode == CO_E_CLASSSTRING) { - LOG.WarnFormat("Unknown progId {0}", progId); - } else { - LOG.Warn("Error getting active object for " + progId, comE); + if (comObject == null) { + try { + comObject = Marshal.GetActiveObject(progId); + } catch (COMException comE) { + if (comE.ErrorCode == MK_E_UNAVAILABLE) { + LOG.DebugFormat("No current instance of {0} object available.", progId); + } else if (comE.ErrorCode == CO_E_CLASSSTRING) { + LOG.WarnFormat("Unknown progId {0}", progId); + } else { + LOG.Warn("Error getting active object for " + progIDAttribute.Value, comE); + } + } catch (Exception e) { + LOG.Warn("Error getting active object for " + progIDAttribute.Value, e); } - } catch (Exception e) { - LOG.Warn("Error getting active object for " + progId, e); } if (comObject != null) { - COMWrapper wrapper = new COMWrapper(comObject, type); - return (T)wrapper.GetTransparentProxy(); + if (comObject is IDispatch) { + COMWrapper wrapper = new COMWrapper(comObject, type); + return (T)wrapper.GetTransparentProxy(); + } else { + return (T)comObject; + } + } + return default(T); + } + + /// + /// A simple create instance, doesn't create a wrapper!! + /// + /// T + public static T CreateInstance() { + Type type = typeof(T); + if (null == type) { + throw new ArgumentNullException("type"); + } + if (!type.IsInterface) { + throw new ArgumentException("The specified type must be an interface.", "type"); + } + + ComProgIdAttribute progIDAttribute = ComProgIdAttribute.GetAttribute(type); + if (null == progIDAttribute || null == progIDAttribute.Value || 0 == progIDAttribute.Value.Length) { + throw new ArgumentException("The specified type must define a ComProgId attribute.", "type"); + } + string progId = progIDAttribute.Value; + Type comType = null; + if (progId.StartsWith("clsid:")) { + Guid guid = new Guid(progId.Substring(6)); + try { + comType = Type.GetTypeFromCLSID(guid); + } catch (Exception ex) { + LOG.Warn("Error type for " + progId, ex); + } + } else { + try { + comType = Type.GetTypeFromProgID(progId, true); + } catch (Exception ex) { + LOG.Warn("Error type for " + progId, ex); + } + } + object comObject = null; + if (comType != null) { + try { + comObject = Activator.CreateInstance(comType); + if (comObject != null) { + LOG.DebugFormat("Created new instance of {0} object.", progId); + } + } catch (Exception e) { + LOG.Warn("Error creating object for " + progId, e); + } + } + if (comObject != null) { + return (T)comObject; } return default(T); } @@ -138,39 +208,59 @@ namespace Greenshot.Interop { object comObject = null; Type comType = null; string progId = progIDAttribute.Value; + Guid guid = Guid.Empty; // Convert from clsid to Prog ID, if needed if (progId.StartsWith("clsid:")) { - Guid guid = new Guid(progId.Substring(6)); + guid = new Guid(progId.Substring(6)); int result = ProgIDFromCLSID(ref guid, out progId); if (result != 0) { - LOG.WarnFormat("Error {0} getting progId {1}", result, progIDAttribute.Value); + // Restore progId, as it's overwritten + progId = progIDAttribute.Value; + try { + GetActiveObject(ref guid, IntPtr.Zero, out comObject); + } catch (Exception) { + LOG.WarnFormat("Error {0} getting instance for class id {1}", result, progIDAttribute.Value); + } + if (comObject == null) { + LOG.WarnFormat("Error {0} getting progId {1}", result, progIDAttribute.Value); + } } else { LOG.InfoFormat("Mapped {0} to progId {1}", progIDAttribute.Value, progId); } } - try { - comObject = Marshal.GetActiveObject(progId); - } catch (COMException comE) { - if (comE.ErrorCode == MK_E_UNAVAILABLE) { - LOG.DebugFormat("No current instance of {0} object available.", progId); - } else if (comE.ErrorCode == CO_E_CLASSSTRING) { - LOG.WarnFormat("Unknown progId {0} (application not installed)", progId); - return default(T); - } else { - LOG.Warn("Error getting active object for " + progId, comE); + if (comObject == null) { + if (!progId.StartsWith("clsid:")) { + try { + comObject = Marshal.GetActiveObject(progId); + } catch (COMException comE) { + if (comE.ErrorCode == MK_E_UNAVAILABLE) { + LOG.DebugFormat("No current instance of {0} object available.", progId); + } else if (comE.ErrorCode == CO_E_CLASSSTRING) { + LOG.WarnFormat("Unknown progId {0} (application not installed)", progId); + return default(T); + } else { + LOG.Warn("Error getting active object for " + progId, comE); + } + } catch (Exception e) { + LOG.Warn("Error getting active object for " + progId, e); + } } - } catch (Exception e) { - LOG.Warn("Error getting active object for " + progId, e); } + // Did we get the current instance? If not, try to create a new if (comObject == null) { try { comType = Type.GetTypeFromProgID(progId, true); } catch (Exception ex) { - LOG.Warn("Error type for " + progId, ex); + if (Guid.Empty != guid) { + comType = Type.GetTypeFromCLSID(guid); + } else { + LOG.Warn("Error type for " + progId, ex); + } } + if (comType != null) { try { comObject = Activator.CreateInstance(comType); @@ -183,8 +273,12 @@ namespace Greenshot.Interop { } } if (comObject != null) { - COMWrapper wrapper = new COMWrapper(comObject, type); - return (T)wrapper.GetTransparentProxy(); + if (comObject is IDispatch) { + COMWrapper wrapper = new COMWrapper(comObject, type); + return (T)wrapper.GetTransparentProxy(); + } else { + return (T)comObject; + } } return default(T); } diff --git a/GreenshotPlugin/Interop/IAppVisibility.cs b/GreenshotPlugin/Interop/IAppVisibility.cs index c5f96364e..963d94099 100644 --- a/GreenshotPlugin/Interop/IAppVisibility.cs +++ b/GreenshotPlugin/Interop/IAppVisibility.cs @@ -19,22 +19,24 @@ * along with this program. If not, see . */ using System; +using System.Runtime.InteropServices; using Greenshot.Interop; -namespace GreenshotPlugin.Interop { +namespace Greenshot.Interop { // This is used for Windows 8 to see if the App Launcher is active // See http://msdn.microsoft.com/en-us/library/windows/desktop/jj554119%28v=vs.85%29.aspx - [ComProgId("7E5FE3D9-985F-4908-91F9-EE19F9FD1514")] - public interface IAppVisibility : Common { - //MONITOR_APP_VISIBILITY GetAppVisibilityOnMonitor(IntPtr hMonitor); + [ComProgId("clsid:7E5FE3D9-985F-4908-91F9-EE19F9FD1514")] + [ComImport, Guid("2246EA2D-CAEA-4444-A3C4-6DE827E44313"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppVisibility { + MONITOR_APP_VISIBILITY GetAppVisibilityOnMonitor(IntPtr hMonitor); bool IsLauncherVisible { get; } } - //public enum MONITOR_APP_VISIBILITY { - // MAV_UNKNOWN = 0, // The mode for the monitor is unknown - // MAV_NO_APP_VISIBLE = 1, - // MAV_APP_VISIBLE = 2 - //} + public enum MONITOR_APP_VISIBILITY { + MAV_UNKNOWN = 0, // The mode for the monitor is unknown + MAV_NO_APP_VISIBLE = 1, + MAV_APP_VISIBLE = 2 + } } diff --git a/GreenshotPlugin/UnmanagedHelpers/User32.cs b/GreenshotPlugin/UnmanagedHelpers/User32.cs index 886e5e808..10981d469 100644 --- a/GreenshotPlugin/UnmanagedHelpers/User32.cs +++ b/GreenshotPlugin/UnmanagedHelpers/User32.cs @@ -271,6 +271,11 @@ namespace GreenshotPlugin.UnmanagedHelpers { public const int PW_DEFAULT = 0x00; public const int PW_CLIENTONLY = 0x01; + + // For MonitorFromWindow + public const int MONITOR_DEFAULTTONULL = 0; + public const int MONITOR_DEFAULTTOPRIMARY = 1; + public const int MONITOR_DEFAULTTONEAREST = 2; /// /// Stop flashing. The system restores the window to its original state. @@ -347,10 +352,14 @@ namespace GreenshotPlugin.UnmanagedHelpers { public static extern bool SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); [DllImport("user32", SetLastError=true)] public extern static uint GetWindowLong(IntPtr hwnd, int index); - [DllImport("user32", SetLastError = true)] - public static extern int SetWindowLong(IntPtr hWnd, int index, uint styleFlags); [DllImport("user32", EntryPoint="GetWindowLongPtr", SetLastError=true)] public extern static uint GetWindowLongPtr(IntPtr hwnd, int nIndex); + [DllImport("user32", SetLastError = true)] + public static extern int SetWindowLong(IntPtr hWnd, int index, uint styleFlags); + [DllImport("user32", SetLastError = true)] + public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); + [DllImport("user32", SetLastError = true)] + public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags); /// /// Wrapper for the GetWindowLong which decides if the system is 64-bit or not and calls the right one.