Fernando Machado Píriz's Blog

Posts about digital transformation, enterprise architecture and related topics

Give me a handle and I will move the Earth

with one comment

image

This post’s title paraphrases a saying attributed to Archimedes, who is said to have remarked of lever “Give me a place to stand on, and I will move the Earth”.

For people like me who started developing Windows applications a long time ago, handles were the entry door to almost any resource through Win32 API. Do you want to close or minimize a window? You need that window handle. Do you want to write some text into a window client area? You need the font handle, the device context handle, and the window handle. And so on.

Fortunately, in actual .NET Framework times, almost no one cares about using handles, nor cares about knowing what they are. The .NET Framework hides the complexity of handling Windows resources through the Win32 API, and with that, it also hides the handles.

However, once in a while, there appear some problems for which the only solution is to get a handle. Here is an example.

One of the features of the Giving a Presentation demo application is the ability to hide desktop icons during a presentation, showing them back again when the presentation ends. While developing thsi feature, I unsuccessfully searched for a way to do that. As far as I could found, there is no public API function to do it; and most hacks available on the web -for example this one– use handles and Win32 API function calls, but does not work correctly: they disable the entire desktop, not only its icons; as a consequence, the desktop’s pop up menu disappears.

Speaking about desktop’s popup menu, what I needed was a feature like the one in the Show desktop icons command.

image

Then it was when the old knowledge about handles and the like became useful again. I knew that this command ended sooner or later being transformed into a WM_COMMAND sent to the desktop. What I needed to emulate this behavior was to know the arguments of the WM_COMMAND message and –as you can imagine- the desktop handle. Once I get this information, I could send the same WM_COMMAND to the desktop. The desktop would not distinguish between a message sent by the menu command or by my program, so it should handle it in the same way.

For both two things I used an old tool still included in Visual Studio installation: Spy++. This tool allow finding a particular window, and among other things, look at the messages it receives. The following picture show the Spy++ windows list node corresponding to the desktop. Please note that desktop is a window of the SHELLDLL_DefView class; we will see later how this information is used.

clip_image003

The following image shows the messages received by this window when command Show desktop icons is executed. Please not the command ID 29698 of the WM_COMMAND message; we will also see later how to use this information.

clip_image004

The function to hide or show desktop icons finally gets something like this:

/// <summary>
/// Toggles desktop icons visibility by sending a low level command to desktop window in the same way as the popup menu "Show desktop  
/// icons" does. Use <code>AreDesktopIconsVisible</code> to get current desktop icons visibility status.  
/// </summary>  
private static void ToggleDesktopIconsVisibility()  
{  
    IntPtr defaultViewHandle = FindShellWindow();
    UIntPtr resultArgument; 
    IntPtr returnValue = NativeMethods.SendMessageTimeout(  
        defaultViewHandle,  
        NativeMethods.WM_COMMAND,  
        new IntPtr(29698),  
        IntPtr.Zero,  
        NativeMethods.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,  
        1000,   
       out resultArgument);
} 

The NativeMethods class contains all the functions and constants imported from Win32 API; code is not shown here due to simplicity, but you can download it from CodePlex. The function SendMessageTimeout is used to send the WM_COMMAND with ID 29698 to the window whose handle was obtained with the FindShellWindow function.

On the other side, the FindShellWindow function finds and returns the desktop handle, strictlyspeaking, the handle to the window containing desktop icons, whose class is SHELLDLL_DefView. Finding this window was not easy. Due to unknown reasons, in certain systems the window is in some place in the windows’ hierarchy, but in other systems is in another place – both system being Window 7 RTM 32 bits. Because of this, the Giving a Presentation feature to hide and show desktop icons, did not work in every situations. I should thanks to my MVP coleague Elías Mereb for his help to test different candidate solutions until finding the good one.

/// <summary>
/// Called by EnumWindows. Sets <code>shellWindowHandle</code> if a window with class "SHELLDLL_DefView" is found during enumeration.
/// </summary>
/// <param name="handle">The handle of the window being enumerated.</param>
/// <param name="param">The argument passed to <code>EnumWindowsProc</code>; not used in this application.</param>
/// <returns>Allways returns 1.</returns>
private static int EnumWindowsProc(IntPtr handle, int param)
{
    try
    {
        IntPtr foundHandle = NativeMethods.FindWindowEx(handle, IntPtr.Zero, "SHELLDLL_DefView", null);
        if (!foundHandle.Equals(IntPtr.Zero))
        {
            shellWindowHandle = foundHandle;
            return 0;
        }
    }
    catch
    {
        // Intentionally left blank
    }

    return 1;
}

/// <summary>
/// Finds the window containing desktop icons.
/// </summary>
/// <returns>The handle of the window.</returns>
private static IntPtr FindShellWindow()
{
    IntPtr progmanHandle;
    IntPtr defaultViewHandle = IntPtr.Zero;
    IntPtr workerWHandle;
    int errorCode = NativeMethods.ERROR_SUCCESS;

    // Try the easy way first. "SHELLDLL_DefView" is a child window of "Progman".
    progmanHandle = NativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Progman", null);

    if (!progmanHandle.Equals(IntPtr.Zero))
    {
        defaultViewHandle = NativeMethods.FindWindowEx(progmanHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
        errorCode = Marshal.GetLastWin32Error();
    }

    if (!defaultViewHandle.Equals(IntPtr.Zero))
    {
        return defaultViewHandle;
    }
    else if (errorCode != NativeMethods.ERROR_SUCCESS)
    {
        Marshal.ThrowExceptionForHR(errorCode);
    }

    // Try the not so easy way then. In some systems "SHELLDLL_DefView" is a child of "WorkerW".
    errorCode = NativeMethods.ERROR_SUCCESS;
    workerWHandle = NativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "WorkerW", null);
    Debug.WriteLine("FindShellWindow.workerWHandle: {0}", workerWHandle);

    if (!workerWHandle.Equals(IntPtr.Zero))
    {
        defaultViewHandle = NativeMethods.FindWindowEx(workerWHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
        errorCode = Marshal.GetLastWin32Error();
    }

    if (!defaultViewHandle.Equals(IntPtr.Zero))
    {
        return defaultViewHandle;
    }
    else if (errorCode != NativeMethods.ERROR_SUCCESS)
    {
        Marshal.ThrowExceptionForHR(errorCode);
    }

    shellWindowHandle = IntPtr.Zero;

    // Try the hard way. In some systems "SHELLDLL_DefView" is a child or a child of "Progman".
    if (NativeMethods.EnumWindows(EnumWindowsProc, progmanHandle) == 0)
    {
        errorCode = Marshal.GetLastWin32Error();
        if (errorCode != NativeMethods.ERROR_SUCCESS)
        {
            Marshal.ThrowExceptionForHR(errorCode);
        }
    }

    // Try the even more harder way. Just in case "SHELLDLL_DefView" is in another desktop.
    if (shellWindowHandle.Equals(IntPtr.Zero))
    {
        if (NativeMethods.EnumDesktopWindows(IntPtr.Zero, EnumWindowsProc, progmanHandle))
        {
            errorCode = Marshal.GetLastWin32Error();
            if (errorCode != NativeMethods.ERROR_SUCCESS)
            {
                Marshal.ThrowExceptionForHR(errorCode);
            }
        }
    }

    return shellWindowHandle;
}

In certain situations, even in the .NET Framework era, it is still useful to get a handle: give me one, and I will move the Earth. Or at least, I will hide and show desktop icons. See you later.

Advertisements

Written by fernandomachadopiriz

August 9, 2010 at 2:46 pm

Posted in C# 4.0

Tagged with

One Response

Subscribe to comments with RSS.

  1. […] Fernando Machado Píriz’s Blog findet sich ein Eintrag, der ziemlich genau erklärt, wie man an ein IntPtr handle zu dem Window […]

    Like


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: