#region License // Desktop 1.1 // * // Copyright (C) 2004 http://www.onyeyiri.co.uk // Coded by: Nnamdi Onyeyiri // // This code may be used in any way you desire except for commercial use. // The code can be freely redistributed unmodified as long as all of the authors // copyright notices remain intact, as long as it is not being sold for profit. // Although I have tried to ensure the stability of the code, I will not take // responsibility for any loss, damages, or death it may cause. // // This software is provided "as is" without any expressed or implied warranty. // // ------------------------- // Change Log // // Version 1.0: -First Release // // Version 1.1: -Added Window and WindowCollection classes // 6/6/2004 -Added another GetWindows overload, that used WindowCollection // -Added GetInputProcesses method to retrieve processes on Input desktop // -Changed GetWindows and GetDesktops to return arrays, instead of them being passed by ref // // Version 1.2 -Implemented IDisposable // 8/7/2004 -Implemented ICloneable // -Overrided ToString to return desktop name // // Version 2.0 -Switched to Generic Collections // 7/19/2010 -Added enumeration of process from alternate desktops // Joel Bennett -Renamed a lot of methods to remove duplication #endregion using System; using System.Threading; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; using System.Collections.Generic; using System.ComponentModel; /// /// Encapsulates the Desktop API. /// public class Desktop : IDisposable, ICloneable { #region Imports [DllImport("kernel32.dll")] private static extern int GetThreadId(IntPtr thread); [DllImport("kernel32.dll")] private static extern int GetProcessId(IntPtr process); // // Imported winAPI functions. // [DllImport("user32.dll")] private static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, long dwDesiredAccess, IntPtr lpsa); [DllImport("user32.dll")] private static extern bool CloseDesktop(IntPtr hDesktop); [DllImport("user32.dll")] private static extern IntPtr OpenDesktop(string lpszDesktop, int dwFlags, bool fInherit, long dwDesiredAccess); [DllImport("user32.dll")] private static extern IntPtr OpenInputDesktop(int dwFlags, bool fInherit, long dwDesiredAccess); [DllImport("user32.dll")] private static extern bool SwitchDesktop(IntPtr hDesktop); [DllImport("user32.dll")] private static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll")] private static extern IntPtr GetProcessWindowStation(); [DllImport("user32.dll")] private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDesktopWindowsProc lpfn, IntPtr lParam); [DllImport("user32.dll")] private static extern bool SetThreadDesktop(IntPtr hDesktop); [DllImport("user32.dll")] public static extern IntPtr GetThreadDesktop(int dwThreadId); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, IntPtr pvInfo, int nLength, ref int lpnLengthNeeded); [DllImport("user32.dll")] private static extern int GetWindowText(IntPtr hWnd, IntPtr lpString, int nMaxCount); private delegate bool EnumDesktopProc(string lpszDesktop, IntPtr lParam); private delegate bool EnumDesktopWindowsProc(IntPtr desktopHandle, IntPtr lParam); // The Unicode version of this function, CreateProcessW, can modify the contents of the lpCommandLine string. // Therefore, you can't just pass a string (because it's a constant, so it becomes a pointer to read-only memory). [return: MarshalAs(UnmanagedType.Bool)][DllImport("kernel32.dll")] private static extern bool CreateProcess( string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, [Out]out PROCESS_INFORMATION lpProcessInformation); [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential)] private struct STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } #endregion #region Constants /// /// Size of buffer used when retrieving window names. /// public const int MaxWindowNameLength = 100; // // winAPI constants. // private const short SW_HIDE = 0; private const short SW_NORMAL = 1; private const int STARTF_USESTDHANDLES = 0x00000100; private const int STARTF_USESHOWWINDOW = 0x00000001; private const int UOI_NAME = 2; private const int STARTF_USEPOSITION = 0x00000004; private const int NORMAL_PRIORITY_CLASS = 0x00000020; private const long DESKTOP_CREATEWINDOW = 0x0002L; private const long DESKTOP_ENUMERATE = 0x0040L; private const long DESKTOP_WRITEOBJECTS = 0x0080L; private const long DESKTOP_SWITCHDESKTOP = 0x0100L; private const long DESKTOP_CREATEMENU = 0x0004L; private const long DESKTOP_HOOKCONTROL = 0x0008L; private const long DESKTOP_READOBJECTS = 0x0001L; private const long DESKTOP_JOURNALRECORD = 0x0010L; private const long DESKTOP_JOURNALPLAYBACK = 0x0020L; private const long AccessRights = DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_READOBJECTS; #endregion #region Structures /// /// Stores window handles and titles. /// public struct Window { #region Private Variables private IntPtr m_handle; private string m_text; #endregion #region Public Properties /// /// Gets the window handle. /// public IntPtr Handle { get { return m_handle; } } /// /// Gets teh window title. /// public string Text { get { return m_text; } } #endregion #region Construction /// /// Creates a new window object. /// /// Window handle. /// Window title. public Window(IntPtr handle, string text) { m_handle = handle; m_text = text; } #endregion } #endregion #region Private Variables private IntPtr m_desktop; private string m_desktopName; private static List m_sc; private List m_windows; private bool m_disposed; #endregion #region Public Properties /// /// Gets if a desktop is open. /// public bool IsOpen { get { return (m_desktop != IntPtr.Zero); } } /// /// Gets the name of the desktop, returns null if no desktop is open. /// public string DesktopName { get { return m_desktopName; } } /// /// Gets a handle to the desktop, IntPtr.Zero if no desktop open. /// public IntPtr DesktopHandle { get { return m_desktop; } } /// /// Opens the default desktop. /// public static readonly Desktop Default = Desktop.OpenDefault(); /// /// Opens the desktop the user if viewing. /// public static readonly Desktop Input = Desktop.OpenInput(); #endregion #region Construction/Destruction /// /// Creates a new Desktop object. /// public Desktop() { // init variables. m_desktop = IntPtr.Zero; m_desktopName = String.Empty; m_windows = new List(); m_disposed = false; } /// /// Creates a new Desktop object. /// public Desktop(string name) { // init variables. m_desktop = IntPtr.Zero; m_desktopName = name; m_windows = new List(); m_disposed = false; } // constructor is private to prevent invalid handles being passed to it. private Desktop(IntPtr desktop) { // init variables. m_desktop = desktop; m_desktopName = Desktop.GetName(desktop); m_windows = new List(); m_disposed = false; } ~Desktop() { // clean up, close the desktop. Close(); } #endregion #region Methods /// /// Closes the handle to a desktop. /// /// True if an open handle was successfully closed. public bool Close() { // make sure object isnt disposed. CheckDisposed(); // check there is a desktop open. if (m_desktop != IntPtr.Zero) { // close the desktop. bool result = CloseDesktop(m_desktop); if (result) { m_desktop = IntPtr.Zero; m_desktopName = String.Empty; } return result; } // no desktop was open, so desktop is closed. return true; } /// /// Opens a desktop. /// /// The name of the desktop to open. /// True if the desktop was successfully opened. public bool Open() { // make sure object isnt disposed. CheckDisposed(); // close the open desktop. if (m_desktop != IntPtr.Zero) { // attempt to close the desktop. if (!Close()) return false; } // open the desktop. m_desktop = OpenDesktop(m_desktopName, 0, true, AccessRights); // something went wrong. if (m_desktop == IntPtr.Zero) return false; return true; } /// /// Switches input to the currently opened desktop. /// /// True if desktops were successfully switched. public bool Show() { // make sure object isnt disposed. CheckDisposed(); // make sure there is a desktop to open. if (m_desktop == IntPtr.Zero) return false; // attempt to switch desktops. bool result = SwitchDesktop(m_desktop); return result; } /// /// Enumerates the windows on a desktop. /// /// Array of Desktop.Window objects to recieve windows. /// A window colleciton if successful, otherwise null. public List GetWindows() { // make sure object isnt disposed. CheckDisposed(); // make sure a desktop is open. if (!IsOpen) return null; // init the List. m_windows.Clear(); List windows = new List(); // get windows. bool result = EnumDesktopWindows(m_desktop, new EnumDesktopWindowsProc(DesktopWindowsProc), IntPtr.Zero); // check for error. if (!result) return null; // get window names. IntPtr ptr = Marshal.AllocHGlobal(MaxWindowNameLength); foreach(IntPtr wnd in m_windows) { GetWindowText(wnd, ptr, MaxWindowNameLength); windows.Add(new Window(wnd, Marshal.PtrToStringAnsi(ptr))); } Marshal.FreeHGlobal(ptr); return windows; } private bool DesktopWindowsProc(IntPtr wndHandle, IntPtr lParam) { // add window handle to colleciton. m_windows.Add(wndHandle); return true; } /// /// Creates a new process in a desktop. /// /// Path to application. /// The process object for the newly created process. public Process CreateProcess(string path) { return CreateProcess(path, null); } /// /// Creates a new process in a desktop. /// /// Path to application. /// Arguments for the application. /// The process object for the newly created process. public Process CreateProcess(string path, string commandLineParameters) { // make sure object isnt disposed. CheckDisposed(); return Desktop.CreateProcess(path,commandLineParameters,m_desktopName); } /// /// Prepares a desktop for use by launching the Explorer Shell. For use only on newly created desktops, call straight after CreateDesktop. /// public void Prepare() { // make sure object isnt disposed. CheckDisposed(); // make sure a desktop is open. if (IsOpen) { // load explorer. CreateProcess("explorer.exe"); } } /// /// Gets an array of all the processes running on this desktop. /// /// An array of the processes. public Process[] GetProcesses() { return GetProcesses( m_desktopName, StringComparison.InvariantCulture); } #endregion #region Static Methods /// /// Enumerates all of the desktops. /// /// String array to recieve desktop names. /// True if desktop names were successfully enumerated. public static string[] GetDesktops() { // attempt to enum desktops. IntPtr windowStation = GetProcessWindowStation(); // check we got a valid handle. if (windowStation == IntPtr.Zero) return new string[0]; string[] desktops; // lock the object. thread safety and all. lock(m_sc = new List()) { bool result = EnumDesktops(windowStation, new EnumDesktopProc(DesktopProc), IntPtr.Zero); // something went wrong. if (!result) return new string[0]; // // turn the collection into an array. desktops = new string[m_sc.Count]; for(int i = 0; i < desktops.Length; i++) desktops[i] = m_sc[i]; } return desktops; } private static bool DesktopProc(string lpszDesktop, IntPtr lParam) { // add the desktop to the collection. m_sc.Add(lpszDesktop); return true; } /// /// Switches to the specified desktop. /// /// Name of desktop to switch input to. /// True if desktops were successfully switched. public static bool Show(string name) { // attmempt to open desktop. bool result = false; using (Desktop d = new Desktop(name)) { result = d.Open(); // something went wrong. if (!result) return false; // attempt to switch desktops. result = d.Show(); } return result; } /// /// Gets the desktop of the calling thread. /// /// Returns a Desktop object for the valling thread. public static Desktop GetCurrent() { // get the desktop. return new Desktop(GetThreadDesktop(System.Threading.Thread.CurrentThread.ManagedThreadId)); } /// /// Sets the desktop of the calling thread. /// NOTE: Function will fail if thread has hooks or windows in the current desktop. /// /// Desktop to put the thread in. /// True if the threads desktop was successfully changed. public static bool SetCurrent(Desktop desktop) { // set threads desktop. if (!desktop.IsOpen) return false; return SetThreadDesktop(desktop.DesktopHandle); } /// /// Opens a desktop. /// /// The name of the desktop to open. /// If successful, a Desktop object, otherwise, null. public static Desktop Open(string name) { // open the desktop. Desktop desktop = new Desktop(name); bool result = desktop.Open(); // something went wrong. if (!result) return null; return desktop; } /// /// Opens the current input desktop. /// /// If successful, a Desktop object, otherwise, null. public static Desktop OpenInput() { // open the desktop. IntPtr deskptr = OpenInputDesktop(0, true, AccessRights); if (deskptr == IntPtr.Zero) return null; return new Desktop(deskptr); } /// /// Opens the default desktop. /// /// If successful, a Desktop object, otherwise, null. public static Desktop OpenDefault() { // opens the default desktop. return Desktop.Open("Default"); } /// /// Creates a new desktop. /// /// The name of the desktop to create. Names are case sensitive. /// If successful, a Desktop object, otherwise, null. public static Desktop Create(string name) { // make sure desktop doesnt already exist. if (Desktop.Exists(name)) { // it exists, so open it. return Open(name); } // attempt to create desktop. IntPtr deskptr = CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, AccessRights, IntPtr.Zero); if (deskptr == IntPtr.Zero) return null; // open the desktop. return Open(name); } /// /// Gets the name of a given desktop. /// /// Desktop object whos name is to be found. /// If successful, the desktop name, otherwise, null. public static string GetName(Desktop desktop) { // get name. if (desktop.IsOpen) return null; return GetName(desktop.DesktopHandle); } public static Win32Exception LastError; /// /// Gets the name of a desktop from a desktop handle. /// /// /// If successful, the desktop name, otherwise, null. public static string GetName(IntPtr desktopHandle) { // check its not a null pointer. // null pointers wont work. if (desktopHandle == IntPtr.Zero) return null; // get the length of the name. int needed = 0; string name = null; // always returns false, because we pass 0 for available size GetUserObjectInformation(desktopHandle, UOI_NAME, IntPtr.Zero, 0, ref needed); // get the name. IntPtr ptr = Marshal.AllocHGlobal(needed); if(!GetUserObjectInformation(desktopHandle, UOI_NAME, ptr, needed, ref needed)) { Marshal.FreeHGlobal(ptr); LastError = new Win32Exception(); } else { name = Marshal.PtrToStringAnsi(ptr); Marshal.FreeHGlobal(ptr); } return name; } /// /// Checks if the specified desktop exists (using a case sensitive search). /// /// The name of the desktop. /// True if the desktop exists, otherwise false. public static bool Exists(string name) { return Desktop.Exists(name, StringComparison.InvariantCultureIgnoreCase); } /// /// Checks if the specified desktop exists. /// /// The name of the desktop. /// The type of string comparison to do. /// True if the desktop exists, otherwise false. public static bool Exists(string name, StringComparison comparisonType) { // enumerate desktops. string[] desktops = Desktop.GetDesktops(); // return true if desktop exists. foreach(string desktop in desktops) { if(desktop.Equals( name, comparisonType )) return true; } return false; } /// /// Creates a new process on the specified desktop. /// /// Path to application. /// Arguments for the application. /// Desktop name. /// A Process object for the newly created process, otherwise, null. public static Process CreateProcess(string path, string commandLineParameters, string desktop) { if (!Desktop.Exists(desktop)) return null; // set startup parameters. STARTUPINFO si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); si.lpDesktop = desktop; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); //StringBuilder lpPath = new StringBuilder(path); StringBuilder lpCommandLine = new StringBuilder(); lpCommandLine.Append("\""); lpCommandLine.Append(path); lpCommandLine.Append("\" "); lpCommandLine.Append(commandLineParameters); // lpPath.EnsureCapacity(256); // create the process. if(!CreateProcess(null, lpCommandLine.ToString(), IntPtr.Zero, IntPtr.Zero, true, NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref si, out pi) ) { throw new Win32Exception(); } // Get the process. return Process.GetProcessById(pi.dwProcessId); } /// /// Gets an array of all the processes running on the Input desktop. /// /// An array of the processes. public static Process[] GetInputProcesses() { return GetProcesses( GetName(Desktop.Input.DesktopHandle), StringComparison.InvariantCulture); } /// /// Gets an array of all the processes running on the specified desktop. /// /// The name of the desktop for which to return processes /// An array of the processes. public static Process[] GetProcesses(string desktop) { return GetProcesses( desktop, StringComparison.InvariantCultureIgnoreCase); } /// /// Gets an array of all the processes running on the specified desktop (using the speficied string comparison) /// /// The name of the desktop for which to return processes /// The type of string comparison to do /// An array of the processes. public static Process[] GetProcesses(string desktop, StringComparison comparisonType) { // get all processes. Process[] processes = Process.GetProcesses(); List procs = new List(); // cycle through the processes. foreach(Process process in processes) { // check the threads of the process - are they in this one? foreach(ProcessThread pt in process.Threads) { string deskname = GetName(GetThreadDesktop(pt.Id)); if(deskname == null) continue; // check for a desktop name match. if (deskname.Equals(desktop,comparisonType)) { // found a match, add to list, and bail. procs.Add(process); break; } else { Console.WriteLine("Wow, Really? The '" + deskname + "' desktop showed up!"); } } } return procs.ToArray(); } #endregion #region IDisposable /// /// Dispose Object. /// public void Dispose() { // dispose Dispose(true); // suppress finalisation GC.SuppressFinalize(this); } /// /// Dispose Object. /// /// True to dispose managed resources. public virtual void Dispose(bool disposing) { if (!m_disposed) { // dispose of managed resources, // close handles Close(); } m_disposed = true; } private void CheckDisposed() { // check if disposed if (m_disposed) { // object disposed, throw exception throw new ObjectDisposedException(""); } } #endregion #region ICloneable /// /// Creates a new Desktop object with the same desktop open. /// /// Cloned desktop object. public object Clone() { // make sure object isnt disposed. CheckDisposed(); Desktop desktop = new Desktop(m_desktopName); // if a desktop is open, make the clone open it. if (IsOpen) desktop.Open(); return desktop; } #endregion #region Overrides /// /// Gets the desktop name. /// /// The desktop name, or a blank string if no desktop open. public override string ToString() { // return the desktop name. return m_desktopName; } #endregion }