There are so many fun things you can do in Windows when your scripting language allows you to make PInvoke calls to Win32 APIs … but I have to say it’s amazing how many things have been added to Windows recently and still left out of the .Net framework …

Anyway, on to the Aero Peek stuff. If you haven’t seen it, Aero Peek is a feature of Windows 7, which lets you get a peek at your desktop, or at a single window for a moment. Basically, you can press Win+Space (the Windows logo key and the space bar) and all of your open windows instantly turn transparent, revealing … whatever was on your desktop: wallpaper, icons, and gadgets. You can also use it by hovering your mouse on the right corner of the taskbar, or you can peek at a single window by hovering over it’s taskbar button and then over it’s thumbnail.

In any case, I have a couple of windows which I would like to have stay visible on the desktop when I hit the aero peek hotkey: Rainlendar and Miranda. It turns out there’s a simple API call for this: DwmSetWindowAttribute which lets you set the DWMWA_EXCLUDED_FROM_PEEK attribute to ENABLED … causing a window to no longer hide when you press that hotkey. Of course, that API call should be made by those apps, in response to a user setting (so I’ve told their authors about it), but it doesn’t have to be (so I wrote a script to do it myself).

In the old days, I would have written a little systray app which would give you a popup list of all windows, or perhaps added a menu item to a window’s right-click menu … and I would have had to deal with creating some way to persist which apps you wanted to apply this to, and then I could have applied the setting to them whenever you opened them.

But now, I have PowerShell. I don’t need to give you menus and store settings, because I can just let you edit a little script instead.

So here’s a script which will let you turn off Aero Peek transparency for windows by window title and/or process name … Once you have this function available, you can keep Rainlendar’s calendar, tasks, and event windows all visible by just running Remove-AeroPeek -Process Rainlendar2 or you can keep your Miranda contact list visible by running Remove-AeroPeek "Miranda IM" (although you should not that depends on the window title matching just that one window — and Miranda lets you change what your title is, so you may have to adjust it).

Of course, that script really deserves explanation, because it’s showing off quite a few advanced things…

The first thing is that I’m using a Try/Catch block in the BEGIN block to make sure I only execute that code once. You can’t call Add-Type with the same code multiple times in a single PowerShell session, because the type will already exist when you call it the second time. So the code in the try block will throw an exception if the type doesn’t already exist, and in the catch handler, we’ll create the type, and define the other function we need.

Add-Type is a super-powerful cmdlet which compiles code on the fly (or imports types from pre-compiled assemblies). In this case we’re using it to import a little class called Dwm which I started writing myself from PInvoke.net and the MSDN documentation, but then eventually copied most of from a NeoWin forum thread… All this class really does is define the API function and the flags we need to pass to it, and then provides a wrapper for the DwmSetWindowAttribute call. We could have written that call in PowerShell, but at the end of the day, once you start compiling C# code in PowerShell, it’s hard to know when to stop ;)

The Select-Window function is (yet another customized version of) a function I wrote awhile back on PoshCode as part of my (still in progress) rewrite of WASP to use the UIAutomationClient … I’ve just modified it to add only the three properties of the window that I’m interested in: Title and ProcessId (for identifying the correct windows) and Handle (for passing to the DwmSetWindowAttribute call). It uses the RootElement property of System.Windows.Automation.AutomationElement to do a search, and then a series of GetCurrentPropertyValue calls to determine the Name, ProcessId, and NativeWindowHandle of the windows it finds.

That’s pretty much all there is to it, other than filtering out the window(s) that we want and actually calling the API. I think I’m going to have to play a little bit more with this to see what else we can do — I’ve already realized that this means we can make little widgets with PowerBoots and set them to stick around just like regular desktop gadgets …

Reblog this post [with Zemanta]

5 Responses to “Fun with PInvoke and Aero Peek”

  • karl prosser says:

    sweet, all sort of goodies could come from this one

  • Shane Girish says:

    This was what I was searching for. ( A C# code to disable aero peek for Rainlender.)
    Thank you very much mate…

  • Mike says:

    I’ve been looking for a good way to do this, but I’ve never used PowerShell before. I’ve set it so I can run scripts, but running this script doesn’t create the function so I can use it. I’ve tried modifying the part of the script that names processes, but that didn’t help me either. Then I tried just pasting the whole script into PowerShell. That did seem to work because it made it so I could use it as a function, but it just broke Aero Peek. Is there a proper way to use this?

    • Ok, the simple answer is, when you see a function { ... } in PowerShell, and you paste it into a script, you have to invoke the script by “dot sourcing” it if you want the function to stay defined after the script exits. Basically that just means you have to write a dot (a period) followed by a space, and the (path and) name of the script:

      . .\scripts\AeroPeekStuff.ps1

      Not sure what you mean that “it just broke” Aero Peek, so I’m just going to not answer that :)

      • Mike says:

        Thanks, it works now. By “broke Aero Peek,” I just meant that nothing would disappear except for the inside of a few widgets (basically, the exact opposite of what I wanted). Now it works just fine, but doesn’t seem to work with Google Desktop. I appreciate your comment.