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.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 {
/// <summary>
@ -410,6 +410,13 @@ namespace Greenshot.Helpers {
private Thread PrepareForCaptureWithFeedback() {
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() {
// Start Enumeration of "active" windows
List<WindowDetails> 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) {

View file

@ -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 {
/// </summary>
public class WindowDetails : IEquatable<WindowDetails>{
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<string, List<string>> classnameTree = new Dictionary<string, List<string>>();
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
@ -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);
}
}
/// <summary>
/// Check if this window is the window of a metro app
/// </summary>
public bool isMetroApp {
get {
return METRO_WINDOWS_CLASS.Equals(ClassName);
return isAppLauncher || isApp;
}
}
@ -555,6 +570,9 @@ namespace GreenshotPlugin.Core {
/// </summary>
public bool Iconic {
get {
if (isMetroApp) {
return !Visible;
}
return User32.IsIconic(this.hWnd) || Location.X <= -32000;
}
set {
@ -579,6 +597,22 @@ namespace GreenshotPlugin.Core {
/// </summary>
public bool Visible {
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);
}
}
@ -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 {
}
}
}
/// <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
}

View file

@ -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);
}
/// <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);
}
@ -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);
}

View file

@ -19,22 +19,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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
}
}

View file

@ -272,6 +272,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;
/// <summary>
/// Stop flashing. The system restores the window to its original state.
/// </summary>
@ -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);
/// <summary>
/// Wrapper for the GetWindowLong which decides if the system is 64-bit or not and calls the right one.