Some fixes for capturing those nasty Apps, code is not finished yet!

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2267 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2012-11-11 19:57:55 +00:00
commit f006718d2a
5 changed files with 228 additions and 51 deletions

View file

@ -27,7 +27,6 @@ using System.Drawing.Printing;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Configuration; using Greenshot.Configuration;
using Greenshot.Destinations; using Greenshot.Destinations;
using Greenshot.Drawing; using Greenshot.Drawing;
@ -37,6 +36,7 @@ using Greenshot.Plugin;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using GreenshotPlugin.UnmanagedHelpers; using GreenshotPlugin.UnmanagedHelpers;
using Greenshot.IniFile; using Greenshot.IniFile;
using Greenshot.Interop;
namespace Greenshot.Helpers { namespace Greenshot.Helpers {
/// <summary> /// <summary>
@ -410,6 +410,13 @@ namespace Greenshot.Helpers {
private Thread PrepareForCaptureWithFeedback() { private Thread PrepareForCaptureWithFeedback() {
windows = new List<WindowDetails>(); windows = new List<WindowDetails>();
// 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() { Thread getWindowDetailsThread = new Thread (delegate() {
// Start Enumeration of "active" windows // Start Enumeration of "active" windows
List<WindowDetails> allWindows = WindowDetails.GetMetroApps(); List<WindowDetails> allWindows = WindowDetails.GetMetroApps();
@ -733,7 +740,7 @@ namespace Greenshot.Helpers {
windowCaptureMode = WindowCaptureMode.Screen; windowCaptureMode = WindowCaptureMode.Screen;
// Change to GDI, if allowed // Change to GDI, if allowed
if (WindowCapture.isGDIAllowed(process)) { if (!windowToCapture.isMetroApp && WindowCapture.isGDIAllowed(process)) {
if (!dwmEnabled && isWPF(process)) { if (!dwmEnabled && isWPF(process)) {
// do not use GDI, as DWM is not enabled and the application uses PresentationFramework.dll -> isWPF // 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); 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 // Change to DWM, if enabled and allowed
if (dwmEnabled) { if (dwmEnabled) {
if (WindowCapture.isDWMAllowed(process)) { if (windowToCapture.isMetroApp || WindowCapture.isDWMAllowed(process)) {
windowCaptureMode = WindowCaptureMode.Aero; windowCaptureMode = WindowCaptureMode.Aero;
} }
} }
} else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) { } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) {
if (!dwmEnabled || !WindowCapture.isDWMAllowed(process)) { if (!dwmEnabled || (!windowToCapture.isMetroApp && !WindowCapture.isDWMAllowed(process))) {
// Take default screen // Take default screen
windowCaptureMode = WindowCaptureMode.Screen; windowCaptureMode = WindowCaptureMode.Screen;
// Change to GDI, if allowed // Change to GDI, if allowed
@ -828,7 +835,7 @@ namespace Greenshot.Helpers {
break; break;
case WindowCaptureMode.Aero: case WindowCaptureMode.Aero:
case WindowCaptureMode.AeroTransparent: case WindowCaptureMode.AeroTransparent:
if (WindowCapture.isDWMAllowed(process)) { if (windowToCapture.isMetroApp || WindowCapture.isDWMAllowed(process)) {
tmpCapture = windowToCapture.CaptureDWMWindow(captureForWindow, windowCaptureMode, isAutoMode); tmpCapture = windowToCapture.CaptureDWMWindow(captureForWindow, windowCaptureMode, isAutoMode);
} }
if (tmpCapture != null) { if (tmpCapture != null) {

View file

@ -30,6 +30,7 @@ using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.IniFile; using Greenshot.IniFile;
using Greenshot.Interop;
using Greenshot.Plugin; using Greenshot.Plugin;
using GreenshotPlugin.UnmanagedHelpers; using GreenshotPlugin.UnmanagedHelpers;
@ -160,6 +161,7 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
public class WindowDetails : IEquatable<WindowDetails>{ public class WindowDetails : IEquatable<WindowDetails>{
private const string METRO_WINDOWS_CLASS = "Windows.UI.Core.CoreWindow"; 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 log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WindowDetails));
private static Dictionary<string, List<string>> classnameTree = new Dictionary<string, List<string>>(); private static Dictionary<string, List<string>> classnameTree = new Dictionary<string, List<string>>();
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>(); private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
@ -182,12 +184,25 @@ namespace GreenshotPlugin.Core {
private WindowDetails parent = null; private WindowDetails parent = null;
private bool frozen = false; private bool frozen = false;
public bool isApp {
get {
return METRO_WINDOWS_CLASS.Equals(ClassName);
}
}
public bool isAppLauncher {
get {
return METRO_APPLAUNCHER_CLASS.Equals(ClassName);
}
}
/// <summary> /// <summary>
/// Check if this window is the window of a metro app /// Check if this window is the window of a metro app
/// </summary> /// </summary>
public bool isMetroApp { public bool isMetroApp {
get { get {
return METRO_WINDOWS_CLASS.Equals(ClassName); return isAppLauncher || isApp;
} }
} }
@ -555,6 +570,9 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
public bool Iconic { public bool Iconic {
get { get {
if (isMetroApp) {
return !Visible;
}
return User32.IsIconic(this.hWnd) || Location.X <= -32000; return User32.IsIconic(this.hWnd) || Location.X <= -32000;
} }
set { set {
@ -579,6 +597,22 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
public bool Visible { public bool Visible {
get { get {
if (isApp) {
// IAppVisibility appVisibility = COMWrapper.CreateInstance<IAppVisibility>();
// 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); return User32.IsWindowVisible(this.hWnd);
} }
} }
@ -837,7 +871,7 @@ namespace GreenshotPlugin.Core {
// check if the capture fits // check if the capture fits
if (!doesCaptureFit) { if (!doesCaptureFit) {
// if GDI is allowed.. (a screenshot won't be better than we comes if we continue) // 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. // we return null which causes the capturing code to try another method.
return null; return null;
} }
@ -1286,7 +1320,9 @@ namespace GreenshotPlugin.Core {
public bool IsGreenshot { public bool IsGreenshot {
get { get {
try { try {
return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName); if (!isMetroApp) {
return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName);
}
} catch (Exception ex) { } catch (Exception ex) {
LOG.Warn(ex); LOG.Warn(ex);
} }
@ -1516,6 +1552,35 @@ namespace GreenshotPlugin.Core {
} }
} }
} }
/// <summary>
/// Get the AppLauncher
/// </summary>
/// <returns></returns>
public static WindowDetails GetAppLauncher() {
IntPtr appLauncher = User32.FindWindow("ImmersiveLauncher", null);
if (appLauncher != IntPtr.Zero) {
return new WindowDetails (appLauncher);
}
return null;
}
/// <summary>
/// Return true if the metro-app-launcher is visible
/// </summary>
/// <returns></returns>
public static bool IsAppLauncherVisible {
get {
try {
IAppVisibility appVisibility = COMWrapper.CreateInstance<IAppVisibility>();
if (appVisibility != null) {
return appVisibility.IsLauncherVisible;
}
} catch {}
return false;
}
}
} }
#endregion #endregion
} }

View file

@ -57,6 +57,9 @@ namespace Greenshot.Interop {
#endregion #endregion
[DllImport("ole32.dll")] [DllImport("ole32.dll")]
static extern int ProgIDFromCLSID([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID); 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 #region Construction
@ -81,35 +84,102 @@ namespace Greenshot.Interop {
} }
string progId = progIDAttribute.Value; string progId = progIDAttribute.Value;
object comObject = null;
// Convert from clsid to Prog ID, if needed // Convert from clsid to Prog ID, if needed
if (progId.StartsWith("clsid:")) { if (progId.StartsWith("clsid:")) {
Guid guid = new Guid(progId.Substring(6)); Guid guid = new Guid(progId.Substring(6));
int result = ProgIDFromCLSID(ref guid, out progId); int result = ProgIDFromCLSID(ref guid, out progId);
if (result != 0) { 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 { } else {
LOG.InfoFormat("Mapped {0} to progId {1}", progIDAttribute.Value, progId); LOG.InfoFormat("Mapped {0} to progId {1}", progIDAttribute.Value, progId);
} }
} }
object comObject = null; if (comObject == null) {
try { try {
comObject = Marshal.GetActiveObject(progId); comObject = Marshal.GetActiveObject(progId);
} catch (COMException comE) { } catch (COMException comE) {
if (comE.ErrorCode == MK_E_UNAVAILABLE) { if (comE.ErrorCode == MK_E_UNAVAILABLE) {
LOG.DebugFormat("No current instance of {0} object available.", progId); LOG.DebugFormat("No current instance of {0} object available.", progId);
} else if (comE.ErrorCode == CO_E_CLASSSTRING) { } else if (comE.ErrorCode == CO_E_CLASSSTRING) {
LOG.WarnFormat("Unknown progId {0}", progId); LOG.WarnFormat("Unknown progId {0}", progId);
} else { } else {
LOG.Warn("Error getting active object for " + progId, comE); 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) { if (comObject != null) {
COMWrapper wrapper = new COMWrapper(comObject, type); if (comObject is IDispatch) {
return (T)wrapper.GetTransparentProxy(); COMWrapper wrapper = new COMWrapper(comObject, type);
return (T)wrapper.GetTransparentProxy();
} else {
return (T)comObject;
}
}
return default(T);
}
/// <summary>
/// A simple create instance, doesn't create a wrapper!!
/// </summary>
/// <returns>T</returns>
public static T CreateInstance<T>() {
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); return default(T);
} }
@ -138,39 +208,59 @@ namespace Greenshot.Interop {
object comObject = null; object comObject = null;
Type comType = null; Type comType = null;
string progId = progIDAttribute.Value; string progId = progIDAttribute.Value;
Guid guid = Guid.Empty;
// Convert from clsid to Prog ID, if needed // Convert from clsid to Prog ID, if needed
if (progId.StartsWith("clsid:")) { if (progId.StartsWith("clsid:")) {
Guid guid = new Guid(progId.Substring(6)); guid = new Guid(progId.Substring(6));
int result = ProgIDFromCLSID(ref guid, out progId); int result = ProgIDFromCLSID(ref guid, out progId);
if (result != 0) { 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 { } else {
LOG.InfoFormat("Mapped {0} to progId {1}", progIDAttribute.Value, progId); LOG.InfoFormat("Mapped {0} to progId {1}", progIDAttribute.Value, progId);
} }
} }
try { if (comObject == null) {
comObject = Marshal.GetActiveObject(progId); if (!progId.StartsWith("clsid:")) {
} catch (COMException comE) { try {
if (comE.ErrorCode == MK_E_UNAVAILABLE) { comObject = Marshal.GetActiveObject(progId);
LOG.DebugFormat("No current instance of {0} object available.", progId); } catch (COMException comE) {
} else if (comE.ErrorCode == CO_E_CLASSSTRING) { if (comE.ErrorCode == MK_E_UNAVAILABLE) {
LOG.WarnFormat("Unknown progId {0} (application not installed)", progId); LOG.DebugFormat("No current instance of {0} object available.", progId);
return default(T); } else if (comE.ErrorCode == CO_E_CLASSSTRING) {
} else { LOG.WarnFormat("Unknown progId {0} (application not installed)", progId);
LOG.Warn("Error getting active object for " + progId, comE); 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 // Did we get the current instance? If not, try to create a new
if (comObject == null) { if (comObject == null) {
try { try {
comType = Type.GetTypeFromProgID(progId, true); comType = Type.GetTypeFromProgID(progId, true);
} catch (Exception ex) { } 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) { if (comType != null) {
try { try {
comObject = Activator.CreateInstance(comType); comObject = Activator.CreateInstance(comType);
@ -183,8 +273,12 @@ namespace Greenshot.Interop {
} }
} }
if (comObject != null) { if (comObject != null) {
COMWrapper wrapper = new COMWrapper(comObject, type); if (comObject is IDispatch) {
return (T)wrapper.GetTransparentProxy(); COMWrapper wrapper = new COMWrapper(comObject, type);
return (T)wrapper.GetTransparentProxy();
} else {
return (T)comObject;
}
} }
return default(T); return default(T);
} }

View file

@ -19,22 +19,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
using System; using System;
using System.Runtime.InteropServices;
using Greenshot.Interop; using Greenshot.Interop;
namespace GreenshotPlugin.Interop { namespace Greenshot.Interop {
// This is used for Windows 8 to see if the App Launcher is active // 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 // See http://msdn.microsoft.com/en-us/library/windows/desktop/jj554119%28v=vs.85%29.aspx
[ComProgId("7E5FE3D9-985F-4908-91F9-EE19F9FD1514")] [ComProgId("clsid:7E5FE3D9-985F-4908-91F9-EE19F9FD1514")]
public interface IAppVisibility : Common { [ComImport, Guid("2246EA2D-CAEA-4444-A3C4-6DE827E44313"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
//MONITOR_APP_VISIBILITY GetAppVisibilityOnMonitor(IntPtr hMonitor); public interface IAppVisibility {
MONITOR_APP_VISIBILITY GetAppVisibilityOnMonitor(IntPtr hMonitor);
bool IsLauncherVisible { bool IsLauncherVisible {
get; get;
} }
} }
//public enum MONITOR_APP_VISIBILITY { public enum MONITOR_APP_VISIBILITY {
// MAV_UNKNOWN = 0, // The mode for the monitor is unknown MAV_UNKNOWN = 0, // The mode for the monitor is unknown
// MAV_NO_APP_VISIBLE = 1, MAV_NO_APP_VISIBLE = 1,
// MAV_APP_VISIBLE = 2 MAV_APP_VISIBLE = 2
//} }
} }

View file

@ -271,6 +271,11 @@ namespace GreenshotPlugin.UnmanagedHelpers {
public const int PW_DEFAULT = 0x00; public const int PW_DEFAULT = 0x00;
public const int PW_CLIENTONLY = 0x01; 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;
/// <summary> /// <summary>
/// Stop flashing. The system restores the window to its original state. /// 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); public static extern bool SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32", SetLastError=true)] [DllImport("user32", SetLastError=true)]
public extern static uint GetWindowLong(IntPtr hwnd, int index); 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)] [DllImport("user32", EntryPoint="GetWindowLongPtr", SetLastError=true)]
public extern static uint GetWindowLongPtr(IntPtr hwnd, int nIndex); 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);
/// <summary> /// <summary>
/// Wrapper for the GetWindowLong which decides if the system is 64-bit or not and calls the right one. /// Wrapper for the GetWindowLong which decides if the system is 64-bit or not and calls the right one.