moel@176: /*
moel@176:   
moel@176:   Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@176: 
moel@176:   The contents of this file are subject to the Mozilla Public License Version
moel@176:   1.1 (the "License"); you may not use this file except in compliance with
moel@176:   the License. You may obtain a copy of the License at
moel@176:  
moel@176:   http://www.mozilla.org/MPL/
moel@176: 
moel@176:   Software distributed under the License is distributed on an "AS IS" basis,
moel@176:   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@176:   for the specific language governing rights and limitations under the License.
moel@176: 
moel@176:   The Original Code is the Open Hardware Monitor code.
moel@176: 
moel@176:   The Initial Developer of the Original Code is 
moel@176:   Michael Möller <m.moeller@gmx.ch>.
moel@176:   Portions created by the Initial Developer are Copyright (C) 2010
moel@176:   the Initial Developer. All Rights Reserved.
moel@176: 
moel@176:   Contributor(s):
moel@176: 
moel@176:   Alternatively, the contents of this file may be used under the terms of
moel@176:   either the GNU General Public License Version 2 or later (the "GPL"), or
moel@176:   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@176:   in which case the provisions of the GPL or the LGPL are applicable instead
moel@176:   of those above. If you wish to allow use of your version of this file only
moel@176:   under the terms of either the GPL or the LGPL, and not to allow others to
moel@176:   use your version of this file under the terms of the MPL, indicate your
moel@176:   decision by deleting the provisions above and replace them with the notice
moel@176:   and other provisions required by the GPL or the LGPL. If you do not delete
moel@176:   the provisions above, a recipient may use your version of this file under
moel@176:   the terms of any one of the MPL, the GPL or the LGPL.
moel@176:  
moel@176: */
moel@176: 
moel@176: using System;
moel@176: using System.Runtime.InteropServices;
moel@176: using System.Windows.Forms;
moel@176: 
moel@176: namespace OpenHardwareMonitor.GUI {
moel@176:   public class ShowDesktop {
moel@176:     private static ShowDesktop instance = new ShowDesktop();
moel@176: 
moel@176:     public delegate void ShowDesktopChangedEventHandler(bool showDesktop);
moel@176:     
moel@176:     private event ShowDesktopChangedEventHandler ShowDesktopChangedEvent;
moel@176: 
moel@176:     private System.Threading.Timer timer;
moel@176:     private bool showDesktop = false;   
moel@176:     private NativeWindow referenceWindow;
moel@176:     private string referenceWindowCaption =
moel@176:       "OpenHardwareMonitorShowDesktopReferenceWindow";
moel@176: 
moel@176:     private ShowDesktop() {
moel@176:       // create a reference window to detect show desktop
moel@176:       referenceWindow = new NativeWindow();
moel@176:       CreateParams cp = new CreateParams();
moel@176:       cp.ExStyle = GadgetWindow.WS_EX_TOOLWINDOW;
moel@176:       cp.Caption = referenceWindowCaption;
moel@176:       referenceWindow.CreateHandle(cp);
moel@176:       NativeMethods.SetWindowPos(referenceWindow.Handle, 
moel@176:         GadgetWindow.HWND_BOTTOM, 0, 0, 0, 0, GadgetWindow.SWP_NOMOVE | 
moel@176:         GadgetWindow.SWP_NOSIZE | GadgetWindow.SWP_NOACTIVATE | 
moel@176:         GadgetWindow.SWP_NOSENDCHANGING);
moel@176: 
moel@176:       // start a repeated timer to detect "Show Desktop" events 
moel@176:       timer = new System.Threading.Timer(OnTimer, null,
moel@176:         System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
moel@176:     }
moel@176: 
moel@176:     private void StartTimer() {
moel@176:       timer.Change(0, 200);
moel@176:     }
moel@176: 
moel@176:     private void StopTimer() {
moel@176:       timer.Change(System.Threading.Timeout.Infinite,
moel@176:         System.Threading.Timeout.Infinite);
moel@176:     }
moel@176: 
moel@176:     // the desktop worker window (if available) can hide the reference window
moel@176:     private IntPtr GetDesktopWorkerWindow() {
moel@176:       IntPtr shellWindow = NativeMethods.GetShellWindow();
moel@176:       if (shellWindow == IntPtr.Zero)
moel@176:         return IntPtr.Zero;
moel@176: 
moel@176:       int shellId;
moel@176:       NativeMethods.GetWindowThreadProcessId(shellWindow, out shellId);
moel@176: 
moel@176:       IntPtr workerWindow = IntPtr.Zero;
moel@176:       while ((workerWindow = NativeMethods.FindWindowEx(
moel@176:           IntPtr.Zero, workerWindow, "WorkerW", null)) != IntPtr.Zero) {
moel@176: 
moel@176:         int workerId;
moel@176:         NativeMethods.GetWindowThreadProcessId(workerWindow, out workerId);
moel@176:         if (workerId == shellId) {
moel@176:           IntPtr window = NativeMethods.FindWindowEx(
moel@176:             workerWindow, IntPtr.Zero, "SHELLDLL_DefView", null);
moel@176:           if (window != IntPtr.Zero) {
moel@176:             IntPtr desktopWindow = NativeMethods.FindWindowEx(
moel@176:               window, IntPtr.Zero, "SysListView32", null);
moel@176:             if (desktopWindow != IntPtr.Zero)
moel@176:               return workerWindow;
moel@176:           }
moel@176:         }
moel@176:       }
moel@176:       return IntPtr.Zero;
moel@176:     }
moel@176: 
moel@176:     private void OnTimer(Object state) {
moel@176:       bool showDesktopDetected;
moel@176: 
moel@176:       IntPtr workerWindow = GetDesktopWorkerWindow();
moel@176:       if (workerWindow != IntPtr.Zero) {
moel@176:         // search if the reference window is behind the worker window
moel@176:         IntPtr reference = NativeMethods.FindWindowEx(
moel@176:           IntPtr.Zero, workerWindow, null, referenceWindowCaption);
moel@179:         showDesktopDetected = reference != IntPtr.Zero;
moel@176:       } else {
moel@176:         // if there is no worker window, then nothing can hide the reference
moel@176:         showDesktopDetected = false;
moel@176:       }
moel@176: 
moel@176:       if (showDesktop != showDesktopDetected) {
moel@176:         showDesktop = showDesktopDetected;
moel@176:         if (ShowDesktopChangedEvent != null) {
moel@176:           ShowDesktopChangedEvent(showDesktop);
moel@176:         }
moel@176:       }
moel@176:     }
moel@176: 
moel@176:     public static ShowDesktop Instance {
moel@176:       get { return instance; }
moel@176:     }
moel@176: 
moel@176:     // notify when the "show desktop" mode is changed
moel@176:     public event ShowDesktopChangedEventHandler ShowDesktopChanged {
moel@176:       add {
moel@176:         // start the monitor timer when someone is listening
moel@176:         if (ShowDesktopChangedEvent == null)           
moel@176:           StartTimer();
moel@176:         ShowDesktopChangedEvent += value;
moel@176:       }
moel@176:       remove {
moel@176:         ShowDesktopChangedEvent -= value;
moel@176:         // stop the monitor timer if nobody is interested
moel@176:         if (ShowDesktopChangedEvent == null)
moel@176:           StopTimer();
moel@176:       }
moel@176:     }
moel@176: 
moel@176:     private static class NativeMethods {
moel@176:       private const string USER = "user32.dll";
moel@176: 
moel@176:       [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176:       public static extern bool SetWindowPos(IntPtr hWnd,
moel@176:         IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
moel@176: 
moel@176:       [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176:       public static extern IntPtr FindWindowEx(IntPtr hwndParent,
moel@176:         IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
moel@176: 
moel@176:       [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176:       public static extern IntPtr GetShellWindow();
moel@176: 
moel@176:       [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
moel@176:       public static extern int GetWindowThreadProcessId(IntPtr hWnd,
moel@176:         out int processId);
moel@176:     }  
moel@176:   }
moel@176: }