Execute program capturing screenshots from windows service - c

I'm writing a windows service, which will manage some agent program capturing screenshots. Agent program works fine, no problems at all - it just takes screenshot and saves it into bmp file.
But when i try to execute this agent program from my service - it doesnt't work, all i get is black pictures (as if i tried to capture screenshot directly from my service)
My code inside service is:
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
SHELLEXECUTEINFO seInfo;
seInfo.cbSize = sizeof(SHELLEXECUTEINFO);
seInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
seInfo.hwnd = NULL;
seInfo.lpVerb = TEXT("open");
seInfo.lpFile = TEXT("D:\\dev\\work\\agent.exe");
seInfo.lpDirectory = TEXT("D:\\dev\\work\\");
seInfo.nShow = SW_SHOWNORMAL;
seInfo.hInstApp = NULL;
ShellExecuteEx(&seInfo);
I tried to change service properties - to allow it interacting with desktop, change service user from SYSTEM to my local account - nothing helpes.
What can i do to make it work properely?
UPD. Well, i suppose, this happenes because my service executes program in session0. I tried CreateProcess() and CreateProcessAsUser() as well - no result. So how can i create procees NOT in session0?

Aliostad did all the heavy lifting for this. Take a look at his code, it's an excellent start. Keep in mind, you'll need a separate app to take the screenshot since a new process has to be kicked off while impersonating the logged in user.
https://stackoverflow.com/a/4147868/125406
Here is my version of it (fully based on Aliostad's code). It adds command line params, a wait for the process to finish and return of the exit code.
public static class ProcessAsCurrentUser
{
/// <summary>
/// Connection state of a session.
/// </summary>
public enum ConnectionState
{
/// <summary>
/// A user is logged on to the session.
/// </summary>
Active,
/// <summary>
/// A client is connected to the session.
/// </summary>
Connected,
/// <summary>
/// The session is in the process of connecting to a client.
/// </summary>
ConnectQuery,
/// <summary>
/// This session is shadowing another session.
/// </summary>
Shadowing,
/// <summary>
/// The session is active, but the client has disconnected from it.
/// </summary>
Disconnected,
/// <summary>
/// The session is waiting for a client to connect.
/// </summary>
Idle,
/// <summary>
/// The session is listening for connections.
/// </summary>
Listening,
/// <summary>
/// The session is being reset.
/// </summary>
Reset,
/// <summary>
/// The session is down due to an error.
/// </summary>
Down,
/// <summary>
/// The session is initializing.
/// </summary>
Initializing
}
[StructLayout(LayoutKind.Sequential)]
class SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
enum LOGON_TYPE
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK,
LOGON32_LOGON_BATCH,
LOGON32_LOGON_SERVICE,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT,
LOGON32_LOGON_NEW_CREDENTIALS
}
enum LOGON_PROVIDER
{
LOGON32_PROVIDER_DEFAULT,
LOGON32_PROVIDER_WINNT35,
LOGON32_PROVIDER_WINNT40,
LOGON32_PROVIDER_WINNT50
}
[Flags]
enum CreateProcessFlags : uint
{
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_NO_WINDOW = 0x08000000,
CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
DEBUG_PROCESS = 0x00000001,
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000
}
[StructLayout(LayoutKind.Sequential)]
public struct WTS_SESSION_INFO
{
public int SessionID;
[MarshalAs(UnmanagedType.LPTStr)] public string WinStationName;
public ConnectionState State;
}
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Int32 WTSEnumerateSessions(IntPtr hServer, int reserved, int version,
ref IntPtr sessionInfo, ref int count);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserW", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
UInt32 dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 WaitForSingleObject(IntPtr handle, Int32 wait);
public const Int32 INFINITE = -1;
public const Int32 WAIT_ABANDONED = 0x80;
public const Int32 WAIT_OBJECT_0 = 0x00;
public const Int32 WAIT_TIMEOUT = 0x102;
public const Int32 WAIT_FAILED = -1;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetExitCodeProcess(IntPtr hProcess, out uint exitCode);
[DllImport("wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("kernel32.dll")]
private static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern int WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
IntPtr lpTokenAttributes,
int ImpersonationLevel,
int TokenType,
out IntPtr phNewToken);
private const int TokenImpersonation = 2;
private const int SecurityIdentification = 1;
private const int MAXIMUM_ALLOWED = 0x2000000;
private const int TOKEN_DUPLICATE = 0x2;
private const int TOKEN_QUERY = 0x00000008;
/// <summary>
/// Launches a process for the current logged on user if there are any.
/// If none, return false as well as in case of
///
/// ##### !!! BEWARE !!! #### ------------------------------------------
/// This code will only work when running in a windows service (where it is really needed)
/// so in case you need to test it, it needs to run in the service. Reason
/// is a security privileg which only services have (SE_??? something, cant remember)!
/// </summary>
/// <param name="processExe"></param>
/// <returns></returns>
public static uint CreateProcessAsCurrentUser(string processExe, string commandLine)
{
IntPtr duplicate = new IntPtr();
STARTUPINFO info = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
Debug.WriteLine(string.Format("CreateProcessAsCurrentUser. processExe: " + processExe));
IntPtr p = GetCurrentUserToken();
bool result = DuplicateTokenEx(p, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE, IntPtr.Zero,
SecurityIdentification, SecurityIdentification, out duplicate);
Debug.WriteLine(string.Format("DuplicateTokenEx result: {0}", result));
Debug.WriteLine(string.Format("duplicate: {0}", duplicate));
if (result)
{
//NOTE: CREATE_NO_WINDOW will hide the console
//If there are commandline options, pass them AND the exe in
//commandLine and leave processExe empty.
result = CreateProcessAsUser(duplicate, processExe, commandLine,
// IntPtr.Zero, IntPtr.Zero, false, (UInt32) CreateProcessFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null,
IntPtr.Zero, IntPtr.Zero, false, (UInt32)CreateProcessFlags.CREATE_NO_WINDOW, IntPtr.Zero, null,
ref info, out procInfo);
Debug.WriteLine(string.Format("CreateProcessAsUser result: {0}", result));
}
if (p.ToInt32() != 0)
{
Marshal.Release(p);
Debug.WriteLine(string.Format("Released handle p: {0}", p));
}
if (duplicate.ToInt32() != 0)
{
Marshal.Release(duplicate);
Debug.WriteLine(string.Format("Released handle duplicate: {0}", duplicate));
}
//Wait for the process to complete
WaitForSingleObject(procInfo.hProcess, (int)INFINITE);
//Get and return the exit code
uint exitcode;
GetExitCodeProcess(procInfo.hProcess, out exitcode);
return exitcode;
}
public static int GetCurrentSessionId()
{
uint sessionId = WTSGetActiveConsoleSessionId();
Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
if (sessionId == 0xFFFFFFFF)
return -1;
else
return (int)sessionId;
}
public static bool IsUserLoggedOn()
{
List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
Debug.WriteLine(string.Format("Number of sessions: {0}", wtsSessionInfos.Count));
int activeSessionCount = 0;
foreach (var session in wtsSessionInfos)
{
if (session.State == ConnectionState.Active)
activeSessionCount++;
}
//return wtsSessionInfos.Where(x => x.State == ConnectionState.Active).Count() > 0;
return activeSessionCount > 0;
}
private static IntPtr GetCurrentUserToken()
{
List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
int sessionId = 0;
foreach (var session in wtsSessionInfos)
{
if (session.State == ConnectionState.Active)
{
sessionId = session.SessionID;
break;
}
}
//Old Linq method
//int sessionId = wtsSessionInfos.Where(x => x.State == ConnectionState.Active).FirstOrDefault().SessionID;
//int sessionId = GetCurrentSessionId();
Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
if (sessionId == int.MaxValue)
{
return IntPtr.Zero;
}
else
{
IntPtr p = new IntPtr();
int result = WTSQueryUserToken((UInt32)sessionId, out p);
Debug.WriteLine(string.Format("WTSQueryUserToken result: {0}", result));
Debug.WriteLine(string.Format("WTSQueryUserToken p: {0}", p));
return p;
}
}
public static List<WTS_SESSION_INFO> ListSessions()
{
IntPtr server = IntPtr.Zero;
List<WTS_SESSION_INFO> ret = new List<WTS_SESSION_INFO>();
try
{
IntPtr ppSessionInfo = IntPtr.Zero;
Int32 count = 0;
Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (int)ppSessionInfo;
if (retval != 0)
{
for (int i = 0; i < count; i++)
{
WTS_SESSION_INFO si =
(WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current,
typeof(WTS_SESSION_INFO));
current += dataSize;
ret.Add(si);
}
WTSFreeMemory(ppSessionInfo);
}
}
catch (Exception exception)
{
Debug.WriteLine(exception.ToString());
}
return ret;
}
}
We pass a filename as a parameter to the screenshot app and then wait for it to complete. The service then picks up the file and processes and deletes it.
We also found another way around this using named pipes. I'm less familiar with that solution.

Related

WPF CaptureMouse does not capture mouse events outside of the main window

I've created a trivial WPF application that tries to capture the mouse, but it stops getting mousemove events after the mouse leaves the window. Strangely, I do get a mouseup event outside the window.
I've tried a couple variants of mousecapture, but nothing works. I've also tried watching for MouseLost event and I don't see it when the mouse goes outside of the window. It just see it when I release the mouse button.
Here is my MainWindow class. I get Mouse move events as long as the mouse is in the window, but if I click and drag the mouse out of the window, I stop getting move events.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MouseDown += MainWindow_MouseDown;
MouseUp += MainWindow_MouseUp;
MouseMove += MainWindow_MouseMove;
LostMouseCapture += MainWindow_LostMouseCapture;
}
private void MainWindow_LostMouseCapture(object sender, MouseEventArgs e)
{
Debug.WriteLine("Lost Mouse");
}
private void MainWindow_MouseMove(object sender, MouseEventArgs e)
{
Debug.WriteLine("P: " + Mouse.GetPosition(this));
}
private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Releasing");
ReleaseMouseCapture();
}
private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Capturing");
CaptureMouse();
// This does not work either: Mouse.Capture(this, CaptureMode.SubTree);
}
}
I am expecting to see all the mousemove events so that I can drag the window, but I only see the mouseup event outside, and the mousemoves only happen if the cursor is inside the window.
After some research, I have an answer for this question. WPF applications can't see the mouse when it leaves the app window, so if you want to have some custom dragging behavior it is necessary to use interrop to capture the mouse globally. I created the following class to enable DPI-aware window dragging across multiple monitors for any WPF window:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
[assembly: DisableDpiAwareness]
namespace Talisman
{
// --------------------------------------------------------------------------
/// <summary>
/// Enables dragging of a WPF window in a way that is per-monitor DPI sensitive.
///
/// HOW TO USE
/// Add a DraggingLogic member variable and put this code in your window constructor:
/// _draggingLogic = new DraggingLogic(this);
///
/// If you want to do special things when the window moves or when it is clicked:
/// _draggingLogic.OnPositionChanged += (xm, ym) => {/* whatever you want here */};
/// _draggingLogic.OnClick += () => {/* whatever you want here */};
///
/// </summary>
// --------------------------------------------------------------------------
public class DraggingLogic
{
public event Action<double, double> OnPositionChanged;
public event Action OnClick;
/// <summary>
/// Factor to convert Horizontal screen coordinates
/// </summary>
public double DpiCorrectionX { get; set; }
/// <summary>
/// Factor to convertVertical screen coordinates
/// </summary>
public double DpiCorrectionY { get; set; }
public double WpfDpiX { get; set; }
public double WpfDpiY { get; set; }
#region INTERROP - Mouse interaction
private static int _mouseHookHandle;
private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
private static HookProc _mouseDelegate;
private const int WH_MOUSE_LL = 14;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_MOUSEMOVE = 0x0200;
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
#endregion
#region INTERROP - DPI
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
[DllImport("Shcore.dll")]
private static extern IntPtr SetProcessDpiAwareness([In]DpiAwareness dpiAwareness);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public enum DpiAwareness
{
Unaware = 0,
System = 1,
PerMonitor = 2,
}
#endregion
Screen _currentScreen;
Window _dragMe;
bool _dragging = false;
double _dragDelta = 0;
Point _lastMousePosition;
Point _mouseStickyPosition;
// --------------------------------------------------------------------------
/// <summary>
/// Get resource text using a loose naming scheme
/// </summary>
// --------------------------------------------------------------------------
public DraggingLogic(Window dragme)
{
var result = SetProcessDpiAwareness(DpiAwareness.PerMonitor);
dragme.MouseDown += HandleMouseDown;
dragme.MouseMove += HandleMouseMove;
dragme.MouseUp += HandleMouseUp;
dragme.Loaded += Dragme_Loaded;
_dragMe = dragme;
}
// --------------------------------------------------------------------------
/// <summary>
/// Dragme_Loaded - can't find DPI until the window is loaded
/// </summary>
// --------------------------------------------------------------------------
private void Dragme_Loaded(object sender, RoutedEventArgs e)
{
var source = PresentationSource.FromVisual(_dragMe);
WpfDpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
WpfDpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
// --------------------------------------------------------------------------
/// <summary>
/// Figure out scaling for the DPI on a certain monitor
/// </summary>
// --------------------------------------------------------------------------
public void CalculateDpiScaleFactors(Screen screen, DpiType dpiType)
{
var point = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var monitor = MonitorFromPoint(point, 2/*MONITOR_DEFAULTTONEAREST*/);
Debug.WriteLine($"Monitor: {monitor}");
var result = GetDpiForMonitor(monitor, dpiType, out var monitorDpiX, out var monitorDpiY);
if(result != IntPtr.Zero)
{
monitorDpiX = monitorDpiY = 96;
}
DpiCorrectionX = 96.0 / monitorDpiX;
DpiCorrectionY = 96.0 / monitorDpiY;
}
// --------------------------------------------------------------------------
/// <summary>
/// Mouse Down
/// </summary>
// --------------------------------------------------------------------------
private void HandleMouseDown(object sender, MouseButtonEventArgs e)
{
var window = sender as Window;
if (e.LeftButton == MouseButtonState.Pressed)
{
_dragging = true;
_dragDelta = 0;
_mouseStickyPosition = Mouse.GetPosition(window);
_lastMousePosition = window.PointToScreen(Mouse.GetPosition(window));
_currentScreen = GetScreenFromPoint(_lastMousePosition);
CalculateDpiScaleFactors(_currentScreen, DpiType.Effective);
CaptureGlobalMouse();
e.Handled = true;
}
}
// --------------------------------------------------------------------------
/// <summary>
/// Mouse Move
/// </summary>
// --------------------------------------------------------------------------
private void HandleMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_dragging)
{
e.Handled = true;
}
}
// --------------------------------------------------------------------------
/// <summary>
/// HandleGlobalMouseMove
/// </summary>
// --------------------------------------------------------------------------
private void HandleGlobalMouseMove(Point mouseLocation)
{
var newPosition = mouseLocation; // This arrives without DPI correction
var screen = GetScreenFromPoint(newPosition);
// We need to do some fix up when we drag to another screen because
// the DPI on the other screen could be different
if(screen != null && screen.DeviceName != _currentScreen.DeviceName)
{
CalculateDpiScaleFactors(screen, DpiType.Effective);
_lastMousePosition = newPosition;
// Move the window to match the mouse position
_dragMe.Left = (newPosition.X - _mouseStickyPosition.X)* DpiCorrectionX;
_dragMe.Top = (newPosition.Y - _mouseStickyPosition.Y)* DpiCorrectionY;
_currentScreen = screen;
}
var xMove = (newPosition.X - _lastMousePosition.X)* DpiCorrectionX;
var yMove = (newPosition.Y - _lastMousePosition.Y)* DpiCorrectionY;
_dragMe.Left += xMove;
_dragMe.Top += yMove;
_dragDelta += (_lastMousePosition - newPosition).Length;
_lastMousePosition = newPosition;
OnPositionChanged?.Invoke(xMove, yMove);
}
// --------------------------------------------------------------------------
/// <summary>
/// GetScreenFromPoint - return the screen from a raw point (presumably mouse coordinate)
/// </summary>
// --------------------------------------------------------------------------
public Screen GetScreenFromPoint(Point point)
{
foreach (Screen screen in Screen.AllScreens)
{
if (screen.ContainsPoint(point.X, point.Y)) return screen;
}
return null;
}
// --------------------------------------------------------------------------
/// <summary>
/// Mouse Up
/// </summary>
// --------------------------------------------------------------------------
private void HandleMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
var window = sender as Window;
// if the user didn't actually drag, then we want to treat this as a click
if (_dragDelta < 3)
{
OnClick?.Invoke();
}
_dragging = false;
ReleaseGlobalMouse();
if(e != null) e.Handled = true;
}
}
// --------------------------------------------------------------------------
/// <summary>
/// MouseHookProc- allows us to handle global mouse events
/// </summary>
// --------------------------------------------------------------------------
private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
if (nCode >= 0)
{
switch (wParam)
{
case WM_LBUTTONUP: HandleMouseUp(this, null); break;
case WM_MOUSEMOVE:
{
var mouseHookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
HandleGlobalMouseMove(new Point(mouseHookStruct.pt.x, mouseHookStruct.pt.y));
break;
}
}
}
return CallNextHookEx(_mouseHookHandle, nCode, wParam, lParam);
}
// --------------------------------------------------------------------------
/// <summary>
/// CaptureGlobalMouse
/// </summary>
// --------------------------------------------------------------------------
private void CaptureGlobalMouse()
{
if (_mouseHookHandle == 0)
{
_mouseDelegate = MouseHookProc;
_mouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL,
_mouseDelegate,
GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
0);
if (_mouseHookHandle == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
// --------------------------------------------------------------------------
/// <summary>
/// ReleaseGlobalMouse
/// </summary>
// --------------------------------------------------------------------------
private void ReleaseGlobalMouse()
{
if (_mouseHookHandle != 0)
{
int result = UnhookWindowsHookEx(_mouseHookHandle);
_mouseHookHandle = 0;
_mouseDelegate = null;
if (result == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
}
you have to use mouse capture on the object itself so in MouseDown you want ((IInputElement)sender).CaptureMouse() and in MouseUp ((IInputElement)sender).ReleaseMouseCapture().
Alternatively, you can also use MainWindow.CaptureMouse().
private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Releasing");
((IInputElement)sender).ReleaseMouseCapture()
}
private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Capturing");
((IInputElement)sender).CaptureMouse()
// This does not work either: Mouse.Capture(this, CaptureMode.SubTree);
}

How to pass WindowState from desktop shortcut into WPF app?

How can I control the initial WindowState (Normal, Minimized, Maximized) of a WPF main window from a desktop shortcut?
The "Run:" combobox of the shortcut's properties dialog let's me choose between "Normal window", "Minimized" and "Maximized". But this option seems to be completely ignored by WPF apps. With WinForms this was automatically supported with no additional code.
Is there a way to access this option from the launched WPF process? I know I can specify the ProcessStartInfo.WindowStyle property when launching new processes. But how can I access this option from the process being launched?
System.Diagnostics.Process.GetCurrentProcess().StartInfo.WindowStyle
Use NativeMethods.StartupInfo.GetInitialWindowStyle() to get just the initial window state or NativeMethods.StartupInfo.FromCurrentProcess to access the entire information.
static partial class NativeMethods
{
public static class StartupInfo
{
[StructLayout(LayoutKind.Sequential)]
public class STARTUPINFO
{
public readonly UInt32 cb;
private IntPtr lpReserved;
[MarshalAs(UnmanagedType.LPWStr)] public readonly string lpDesktop;
[MarshalAs(UnmanagedType.LPWStr)] public readonly string lpTitle;
public readonly UInt32 dwX;
public readonly UInt32 dwY;
public readonly UInt32 dwXSize;
public readonly UInt32 dwYSize;
public readonly UInt32 dwXCountChars;
public readonly UInt32 dwYCountChars;
public readonly UInt32 dwFillAttribute;
public readonly UInt32 dwFlags;
[MarshalAs(UnmanagedType.U2)] public readonly UInt16 wShowWindow;
[MarshalAs(UnmanagedType.U2)] public readonly UInt16 cbReserved2;
private IntPtr lpReserved2;
public readonly IntPtr hStdInput;
public readonly IntPtr hStdOutput;
public readonly IntPtr hStdError;
}
public readonly static STARTUPINFO FromCurrentProcess = null;
const uint STARTF_USESHOWWINDOW = 0x00000001;
const ushort SW_HIDE = 0;
const ushort SW_SHOWNORMAL = 1;
const ushort SW_SHOWMINIMIZED = 2;
const ushort SW_SHOWMAXIMIZED = 3;
const ushort SW_MINIMIZE = 6;
const ushort SW_SHOWMINNOACTIVE = 7;
const ushort SW_FORCEMINIMIZE = 11;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern void GetStartupInfoW(IntPtr startupInfoPtr);
static StartupInfo() //Static constructor
{
FromCurrentProcess = new STARTUPINFO();
int length = Marshal.SizeOf(typeof(STARTUPINFO));
IntPtr ptr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(FromCurrentProcess, ptr, false);
GetStartupInfoW(ptr);
Marshal.PtrToStructure(ptr, FromCurrentProcess);
Marshal.FreeHGlobal(ptr);
}
public static ProcessWindowStyle GetInitialWindowStyle()
{
if ((FromCurrentProcess.dwFlags & STARTF_USESHOWWINDOW) == 0) return ProcessWindowStyle.Normal;
switch (FromCurrentProcess.wShowWindow)
{
case SW_HIDE: return ProcessWindowStyle.Hidden;
case SW_SHOWNORMAL: return ProcessWindowStyle.Normal;
case SW_MINIMIZE:
case SW_FORCEMINIMIZE:
case SW_SHOWMINNOACTIVE:
case SW_SHOWMINIMIZED: return ProcessWindowStyle.Minimized;
case SW_SHOWMAXIMIZED: return ProcessWindowStyle.Maximized;
default: return ProcessWindowStyle.Normal;
}
}
}
}

WPF : Maximize window with WindowState Problem (application will hide windows taskbar)

I have set my main window state to "Maximized" but the problem is my application will fill the whole screen even task bar. what am i doing wrong ?
I'm using windows 2008 R2 with resolution : 1600 * 900
Here is the Xaml :
<Window x:Class="RadinFarazJamAutomationSystem.wndMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:RadinFarazJamAutomationSystem"
xmlns:mwt="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
Title="MyName" FontFamily="Tahoma" FontSize="12" Name="mainWindow" WindowState="Maximized" xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:my="clr-namespace:RadinFarazJamAutomationSystem.Calendare.UC" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Loaded="mainWindow_Loaded" FlowDirection="LeftToRight"
ResizeMode="NoResize" Closed="mainWindow_Closed">
You can set the MaxHeight property of that window to SystemParameters.MaximizedPrimaryScreenHeight using the constructor.
public MainWindow()
{
InitializeComponent();
this.MaxHeight = SystemParameters.MaximizedPrimaryScreenHeight;
}
To continue with my previous remark. I use the following code in my applications (source: Maximizing WPF windows
using WinInterop = System.Windows.Interop;
using System.Runtime.InteropServices;
public MainWindow()
{
InitializeComponent();
winMain.SourceInitialized += new EventHandler(win_SourceInitialized);
}
#region Avoid hiding task bar upon maximalisation
private static System.IntPtr WindowProc(
System.IntPtr hwnd,
int msg,
System.IntPtr wParam,
System.IntPtr lParam,
ref bool handled)
{
switch (msg)
{
case 0x0024:
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (System.IntPtr)0;
}
void win_SourceInitialized(object sender, EventArgs e)
{
System.IntPtr handle = (new WinInterop.WindowInteropHelper(this)).Handle;
WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
}
private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Adjust the maximized size and position to fit the work area of the correct monitor
int MONITOR_DEFAULTTONEAREST = 0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
}
Marshal.StructureToPtr(mmi, lParam, true);
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
/// <summary>
/// x coordinate of point.
/// </summary>
public int x;
/// <summary>
/// y coordinate of point.
/// </summary>
public int y;
/// <summary>
/// Construct a point of coordinates (x,y).
/// </summary>
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
void win_Loaded(object sender, RoutedEventArgs e)
{
winMain.WindowState = WindowState.Maximized;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
/// <summary>
/// </summary>
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
/// <summary>
/// </summary>
public RECT rcMonitor = new RECT();
/// <summary>
/// </summary>
public RECT rcWork = new RECT();
/// <summary>
/// </summary>
public int dwFlags = 0;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct RECT
{
/// <summary> Win32 </summary>
public int left;
/// <summary> Win32 </summary>
public int top;
/// <summary> Win32 </summary>
public int right;
/// <summary> Win32 </summary>
public int bottom;
/// <summary> Win32 </summary>
public static readonly RECT Empty = new RECT();
/// <summary> Win32 </summary>
public int Width
{
get { return Math.Abs(right - left); } // Abs needed for BIDI OS
}
/// <summary> Win32 </summary>
public int Height
{
get { return bottom - top; }
}
/// <summary> Win32 </summary>
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
/// <summary> Win32 </summary>
public RECT(RECT rcSrc)
{
this.left = rcSrc.left;
this.top = rcSrc.top;
this.right = rcSrc.right;
this.bottom = rcSrc.bottom;
}
/// <summary> Win32 </summary>
public bool IsEmpty
{
get
{
// BUGBUG : On Bidi OS (hebrew arabic) left > right
return left >= right || top >= bottom;
}
}
/// <summary> Return a user friendly representation of this struct </summary>
public override string ToString()
{
if (this == RECT.Empty) { return "RECT {Empty}"; }
return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
}
/// <summary> Determine if 2 RECT are equal (deep compare) </summary>
public override bool Equals(object obj)
{
if (!(obj is Rect)) { return false; }
return (this == (RECT)obj);
}
/// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
public override int GetHashCode()
{
return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
}
/// <summary> Determine if 2 RECT are equal (deep compare)</summary>
public static bool operator ==(RECT rect1, RECT rect2)
{
return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
}
/// <summary> Determine if 2 RECT are different(deep compare)</summary>
public static bool operator !=(RECT rect1, RECT rect2)
{
return !(rect1 == rect2);
}
}
[DllImport("user32")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("User32")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
#endregion
I simplified Geoffrey's answer a bit so you don't have to p/invoke anything.
public MainWindow()
{
InitializeComponent();
winMain.SourceInitialized += new EventHandler(win_SourceInitialized);
}
void win_SourceInitialized( object sender, System.EventArgs e )
{
var handle = (new WindowInteropHelper( _attachedElement )).Handle;
var handleSource = HwndSource.FromHwnd( handle );
if ( handleSource == null )
return;
handleSource.AddHook( WindowProc );
}
private static IntPtr WindowProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
{
switch ( msg )
{
case 0x0024:/* WM_GETMINMAXINFO */
WmGetMinMaxInfo( hwnd, lParam );
handled = true;
break;
}
return (IntPtr)0;
}
private static void WmGetMinMaxInfo( IntPtr hwnd, IntPtr lParam )
{
var mmi = (MINMAXINFO)Marshal.PtrToStructure( lParam, typeof( MINMAXINFO ) );
// Adjust the maximized size and position to fit the work area of the correct monitor
var currentScreen = System.Windows.Forms.Screen.FromHandle( hwnd );
var workArea = currentScreen.WorkingArea;
var monitorArea = currentScreen.Bounds;
mmi.ptMaxPosition.x = Math.Abs( workArea.Left - monitorArea.Left );
mmi.ptMaxPosition.y = Math.Abs( workArea.Top - monitorArea.Top );
mmi.ptMaxSize.x = Math.Abs( workArea.Right - workArea.Left );
mmi.ptMaxSize.y = Math.Abs( workArea.Bottom - workArea.Top );
Marshal.StructureToPtr( mmi, lParam, true );
}
}
[StructLayout( LayoutKind.Sequential )]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
[StructLayout( LayoutKind.Sequential )]
public struct POINT
{
/// <summary>
/// x coordinate of point.
/// </summary>
public int x;
/// <summary>
/// y coordinate of point.
/// </summary>
public int y;
/// <summary>
/// Construct a point of coordinates (x,y).
/// </summary>
public POINT( int x, int y )
{
this.x = x;
this.y = y;
}
}
This one works the best...
public MainWindow()
{
InitializeComponent();
MaxHeight = SystemParameters.VirtualScreenHeight;
MaxWidth = SystemParameters.VirtualScreenWidth;
}
I've implemented solution which considers multiple displays case based on the Mike Weinhardt answer in the thread. If you want to maximize/minimize window programmatically with your own buttons you can use this.
Solution:
// To get a handle to the specified monitor
[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, int dwFlags);
// To get the working area of the specified monitor
[DllImport("user32.dll")]
private static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MonitorInfoEx monitorInfo);
private static MonitorInfoEx GetMonitorInfo(Window window, IntPtr monitorPtr) {
var monitorInfo = new MonitorInfoEx();
monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);
GetMonitorInfo(new HandleRef(window, monitorPtr), monitorInfo);
return monitorInfo;
}
private static void Minimize(Window window) {
if(window == null) {
return;
}
window.WindowState = WindowState.Minimized;
}
private static void Restore(Window window) {
if(window == null) {
return;
}
window.WindowState = WindowState.Normal;
window.ResizeMode = ResizeMode.CanResizeWithGrip;
}
private static void Maximize(Window window) {
window.ResizeMode = ResizeMode.NoResize;
// Get handle for nearest monitor to this window
var wih = new WindowInteropHelper(window);
// Nearest monitor to window
const int MONITOR_DEFAULTTONEAREST = 2;
var hMonitor = MonitorFromWindow(wih.Handle, MONITOR_DEFAULTTONEAREST);
// Get monitor info
var monitorInfo = GetMonitorInfo(window, hMonitor);
// Create working area dimensions, converted to DPI-independent values
var source = HwndSource.FromHwnd(wih.Handle);
if(source?.CompositionTarget == null) {
return;
}
var matrix = source.CompositionTarget.TransformFromDevice;
var workingArea = monitorInfo.rcWork;
var dpiIndependentSize =
matrix.Transform(
new Point(workingArea.Right - workingArea.Left,
workingArea.Bottom - workingArea.Top));
// Maximize the window to the device-independent working area ie
// the area without the taskbar.
window.Top = workingArea.Top;
window.Left = workingArea.Left;
window.MaxWidth = dpiIndependentSize.X;
window.MaxHeight = dpiIndependentSize.Y;
window.WindowState = WindowState.Maximized;
}
Auxilary structs:
// Rectangle (used by MONITORINFOEX)
[StructLayout(LayoutKind.Sequential)]
public struct Rect {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
// Monitor information (used by GetMonitorInfo())
[StructLayout(LayoutKind.Sequential)]
public class MonitorInfoEx {
public int cbSize;
public Rect rcMonitor; // Total area
public Rect rcWork; // Working area
public int dwFlags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public char[] szDevice;
}
p.s. to hide default buttons use:
WindowStyle="None"
in the window XAML.
You can set MaxHeight property in the xaml code, like that:
MaxHeight="{x:Static SystemParameters.MaximizedPrimaryScreenHeight}"
I wanted the opposite (with WindowStyle=None), but reversing this solution also works for your case:
// prevent it from overlapping the taskbar
// "minimize" it
WindowStyle = WindowStyle.SingleBorderWindow;
// Maximize it again. This time it will respect the taskbar.
WindowStyle = WindowStyle.None;
WindowState = WindowState.Maximized;
// app is now borderless fullscreen, showing the taskbar again
What I did for my case:
// make it always overlap the taskbar
// From windowed to maximized without border and window bar
WindowStyle = WindowStyle.None;
WindowState = WindowState.Maximized;
// Now the window does not overlap the taskbar
Hide();
Show();
// Now it does (because it's re-opened)

Making a WPF system context menu item toggleable

I have the following code, which adds an 'Always on Top' item to the system context menu as displayed on the window chrome. It works correctly, but I'd like it to display a check mark or similar to indicate if it's been toggled on/off.
Any idea how I can do this?
public RibbonShell()
{
InitializeComponent();
Loaded += (s,e) =>
{
// Get the Handle for the Forms System Menu
var systemMenuHandle = GetSystemMenu(Handle, false);
// Create our new System Menu items just before the Close menu item
InsertMenu(systemMenuHandle, 5, MfByposition | MfSeparator, 0, string.Empty); // <-- Add a menu seperator
InsertMenu(systemMenuHandle, 6, MfByposition, SettingsSysMenuId, "Always on Top");
// Attach our WindowCommandHandler handler to this Window
var source = HwndSource.FromHwnd(Handle);
source.AddHook(WindowCommandHandler);
};
}
#region Win32 API Stuff
// Define the Win32 API methods we are going to use
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);
/// Define our Constants we will use
private const int WmSyscommand = 0x112;
private const int MfSeparator = 0x800;
private const int MfByposition = 0x400;
#endregion
// The constants we'll use to identify our custom system menu items
private const int SettingsSysMenuId = 1000;
/// <summary>
/// This is the Win32 Interop Handle for this Window
/// </summary>
public IntPtr Handle
{
get { return new WindowInteropHelper(this).Handle; }
}
private IntPtr WindowCommandHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Check if a System Command has been executed
if (msg == WmSyscommand && wParam.ToInt32() == SettingsSysMenuId)
{
Topmost = !Topmost;
handled = true;
}
return IntPtr.Zero;
}
You need to call CheckMenuItem whenever you change Topmost. See the CheckMenuItem documentaton for details. Here's the P/Invoke signature and constants you'll need:
[DllImport("user32.dll")]
private static extern bool CheckMenuItem(IntPtr hMenu, Int32 uIDCheckItem, Int32 uCheck);
private const int MfChecked = 8;
private const int MfUnchecked = 0;
Now to check the item, just:
CheckMenuItem(systemMenuHandle, SettingsSysMenuId, MfChecked);
and to uncheck:
CheckMenuItem(systemMenuHandle, SettingsSysMenuId, MfUnchecked);

finding the zorder of a window

I have a wpf application, for which I need to find out if any other window is over the current window. I can not use IsActive, as that you could have 2 windows side by side (for example IE and the wpf application) and have the other application be active, but not over top of the wpf application.
Essentially I want to know if anything is phsycially over and obscuring part of the wpf window reguardless of activation.
I have tried the following
GetWindowRect
GetTopWindow
and comparing the topwindow process with the current process, this works but only seems to work once.
any ideas?
I've done something similar before and I did not find any managed way of doing this. Hence, it's not really a WPF question, but rather "back to unmanaged" or "good old PInvoke" sort of thing. Here's an extract adapted for your task:
XAML definition.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Label Name="IsSomethingOverLabel">Label</Label>
</Grid>
</Window>
CS code behind.
using System;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Windows.Threading;
namespace WpfApplication1 {
using LONG = System.Int32;
using UINT = System.UInt32;
public partial class Window1: Window {
DispatcherTimer tmr;
public Window1() {
InitializeComponent();
tmr = new DispatcherTimer();
tmr.Tick += new EventHandler(tmr_Tick);
tmr.Start();
}
void tmr_Tick(object sender, EventArgs e) {
RECT Rect2 = GetWindowRect(Handle);
IntPtr h = GetWindow(Handle, GW_HWNDPREV);
while(h != IntPtr.Zero) {
if(IsWindowShown(h))//This is to check that the window found is not minimized etc.
{
RECT Rect1 = GetWindowRect(h);
if(Rect1OverlapsRect2(Rect1, Rect2)) {
IsSomethingOverLabel.Content = GetWindowText(h);
return;
}
}
h = GetWindow(h, GW_HWNDPREV);
}
IsSomethingOverLabel.Content = "Nothing over";
}
bool Rect1OverlapsRect2(RECT Rect1, RECT Rect2) {
return Rect1.left < Rect2.right && Rect1.right > Rect2.left &&
Rect1.top < Rect2.bottom && Rect1.bottom > Rect2.top;
}
private IntPtr Handle {
get { return new WindowInteropHelper(this).Handle; }
}
public const uint GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_MAX = 5;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public LONG left;
public LONG top;
public LONG right;
public LONG bottom;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
public static RECT GetWindowRect(IntPtr hWnd) {
RECT lpRect = new RECT();
if(!GetWindowRect(hWnd, ref lpRect)) {
lpRect.top = lpRect.left = lpRect.bottom = lpRect.right = -1;
}
return lpRect;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowText(IntPtr hWnd, string lpString, int nMaxCount);
public static string GetWindowText(IntPtr hWnd) {
int len = GetWindowTextLength(hWnd) + 1;
string s = new string(' ', len);
if(GetWindowText(hWnd, s, len) != 0) {
return s.Substring(0, len - 1);
} else {
return "";
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool IsWindowVisible(IntPtr hWnd);
public const int SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[StructLayout(LayoutKind.Sequential)]
public struct POINT {
public LONG x;
public LONG y;
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT {
public UINT length;
public UINT flags;
public UINT showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}
public static bool IsWindowShown(IntPtr hWnd) {
bool res = IsWindowVisible(hWnd);
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
if(res && GetWindowPlacement(hWnd, ref placement)) {
switch(placement.showCmd) {
case SW_RESTORE:
case SW_SHOW:
case SW_SHOWMAXIMIZED:
case SW_SHOWNA:
case SW_SHOWNORMAL:
res = true;
break;
default:
res = false;
break;
}
}
return res;
}
}
}
Having a fresh look at the code, GetWindowPlacement could probably be sufficient by itself, eliminating the GetWindowRect call, you can also try that.

Resources