Click here to Skip to main content
15,884,298 members
Articles / Desktop Programming / Win32

Monitoring desktop windows from a Windows service

Rate me:
Please Sign up or sign in to vote.
4.72/5 (17 votes)
2 Jan 2008CPOL4 min read 85.1K   7.2K   115  
Capture and save desktop windows from a Windows service.
/*
 * Please leave this Copyright notice in your code if you use it
 * Written by Decebal Mihailescu [http://www.codeproject.com/script/articles/list_articles.asp?userid=634640]
 */
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using ScreenMonitorLib;

namespace SnapShotManager
{

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ScreenmonitorManager());
        }
    }

    struct UIApp
    {

        static bool EnumThreadCallback(IntPtr hWnd, IntPtr lParam)
        {
            GCHandle gch = GCHandle.FromIntPtr(lParam);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            }
            list.Add(hWnd);
            return true;
        }

        readonly Process _proc;
        readonly IntPtr _RealHWnd;

        public string ProcessName
        {
            get { return _proc.ProcessName; }
        }
        public string Description
        {
            get
            {
                return string.Format("{0}:{1}", _proc.ProcessName, Caption);
            }
        }

        public string WindowClass
        {
            get
            {
                System.Text.StringBuilder classNameBuilder = new System.Text.StringBuilder(256);
                Win32API.GetClassName(this.HWnd, classNameBuilder, classNameBuilder.Capacity);
                return classNameBuilder.ToString();
            }

        }
        public IntPtr HWnd
        {
            get { return _RealHWnd; }
        }


        public string Caption
        {
            get
            {
                // Allocate correct string length first
                int length = Win32API.GetWindowTextLength(this.HWnd);
                StringBuilder sb = new StringBuilder(length + 1);
                Win32API.GetWindowText(this.HWnd, sb, sb.Capacity);
                return sb.ToString();
            }

        }
        readonly List<IntPtr> _windowHandles;

        public List<IntPtr> WindowHandles
        {
            get { return _windowHandles; }
        }

        static RECT _DesktopRect;

        static UIApp()
        {

            IntPtr DesktopHandle = Win32API.GetDesktopWindow();
            Win32API.GetWindowRect(DesktopHandle, out _DesktopRect);

        }
        internal static bool IsValidUIWnd(IntPtr hWnd)
        {
            bool res = false;
            if (hWnd == IntPtr.Zero || !Win32API.IsWindow(hWnd) || !Win32API.IsWindowVisible(hWnd))
                return false;
            if (Win32API.IsIconic(hWnd))
                return true;
            RECT CrtWndRect;
            if (!Win32API.GetWindowRect(hWnd, out CrtWndRect))
                return false;
            if (CrtWndRect.Height > 0 && CrtWndRect.Width > 0)
            {// a valid rectangle means the right window is the mainframe and it intersects the desktop
                RECT visibleRect;//if the rectangle is outside the desktop, it's a dummy window
                if (Win32API.IntersectRect(out visibleRect, ref _DesktopRect, ref CrtWndRect)
                    && !Win32API.IsRectEmpty(ref visibleRect))
                    res = true;
            }
            return res;
        }

        internal UIApp(System.Diagnostics.Process proc)
        {

            _proc = proc;
            _RealHWnd = IntPtr.Zero;
            _windowHandles = new List<IntPtr>();
            GCHandle listHandle = default(GCHandle);
            try
            {
                if (proc.MainWindowHandle == IntPtr.Zero)
                    throw new ApplicationException("Can't add a process with no MainFrame");

                RECT MaxRect = default(RECT);//init with 0
                if (IsValidUIWnd(proc.MainWindowHandle))
                {
                    _RealHWnd = proc.MainWindowHandle;
                    return;
                }
                // the mainFrame is size == 0, so we look for the 'real' window
                listHandle = GCHandle.Alloc(_windowHandles);
                foreach (ProcessThread pt in proc.Threads)
                {
                    Win32API.EnumThreadWindows((uint)pt.Id, new Win32API.EnumThreadDelegate(EnumThreadCallback), GCHandle.ToIntPtr(listHandle));
                }
                //get the biggest visible window in the current proc
                IntPtr MaxHWnd = IntPtr.Zero;
                foreach (IntPtr hWnd in _windowHandles)
                {
                    RECT CrtWndRect;
                    //do we have a valid rect for this window
                    if (Win32API.IsWindowVisible(hWnd) && Win32API.GetWindowRect(hWnd, out CrtWndRect) &&
                        CrtWndRect.Height > MaxRect.Height && CrtWndRect.Width > MaxRect.Width)
                    {   //if the rect is outside the desktop, it's a dummy window
                        RECT visibleRect;
                        if (Win32API.IntersectRect(out visibleRect, ref _DesktopRect, ref CrtWndRect)
                            && !Win32API.IsRectEmpty(ref visibleRect))
                        {
                            MaxHWnd = hWnd;
                            MaxRect = CrtWndRect;
                        }

                    }

                }
                if (MaxHWnd != IntPtr.Zero && MaxRect.Width > 0 && MaxRect.Height > 0)
                {
                    _RealHWnd = MaxHWnd;
                }
                else
                    _RealHWnd = proc.MainWindowHandle;//just add something even if it's a bad window

            }//try ends
            finally
            {
                if (listHandle != default(GCHandle) && listHandle.IsAllocated)
                    listHandle.Free();
            }
        }
    }
    /// <summary>
    /// list with UI procs
    /// </summary>
    class UIApps : List<UIApp>
    {
        internal UIApps(Process[] procs)
        {
            //filter the processes thht don't have UI
            foreach (Process proc in procs)
            {

                if (proc.MainWindowHandle != IntPtr.Zero)
                {
                    UIApp entry = new UIApp(proc);
                    //addd an extra check for the UI window
                    if (UIApp.IsValidUIWnd(entry.HWnd))
                        this.Add(entry);
                }
            }
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Decebal Mihailescu is a software engineer with interest in .Net, C# and C++.

Comments and Discussions