Thursday, October 05, 2006 8:20 PM bart

Programming the Windows Vista DWM in C#

Introduction

I hope everyone realizes the numerous features that live in Vista. One of those great features is the new windowing system based on DWM which stands for Desktop Window Manager. The idea is to have a process (dwm.exe) that collects full-sized bitmaps of each and every window that's active in the current user session instead of having Windows ask each individual window to paint a certain region on the screen (WM_PAINT messages). More information can be found on Greg Schechter's Blog. The cool thing about DWM is its API exposure you can take benefit of to create some cool things. That's what I want to show you in today's post.

Discovery of the API

The starting point of our discovery will be the API of course. The DWM functionality that we're going to show right now is the thumbnail stuff. Since the DWM has a picture of each process's window at every moment in time, we can ask the DWM to hand out that data to us. Basically there are four functions to serve us for this purpose.

But before we dig into these, it's good to know the basics of DWM thumbnail rendering. How it works is explained below:

  1. Our application registers its interesting in receiving a thumbnail from some window on the system.
  2. Our application sets some properties associated with the thumbnail it wants to receive, e.g. the opacity, a rectangle area of the source window to display, a rectangle area indicating where to draw the thumbnail in our window, etc. A supporting function helps in receiving information about the window size of the to-be-thumbnailed window.
  3. When our applicatin doesn't want to receive the thumbnail rendering anymore, it unregisters its interest in the thumbnail.

Anyway, here are the functions, all exported by dwmapi.dll with headers in dwmapi.h.

DwmRegisterThumbnail

Used to register interest in receiving a thumbnail from hwndSource (the target window's handle). The hwndDestination parameter is used to specify the handle of the window where the thumbnail has to be painted to (in the region specified by DwmUpdateThumbnailProperties, see further). Last but not least, there's a (output) parameter that gives us a handle to the thumbnail object, used for further API calls.

HRESULT DwmRegisterThumbnail(      
    HWND hwndDestination,
    HWND *hwndSource,
    PHTHUMBNAIL phThumbnailId );

Notice the Windows SDK mentions a third reserved (unused) parameter that was dropped in the dwmapi.h file and the dwmapi.dll library in Vista RC1.

We translate this into C# interop code:

[DllImport("dwmapi.dll")]
static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb);

DwmUnregisterThumbnail

Used to unregister interest in receiving the thumbnail that was obtained earlier through DwmRegisterThumbnail.

HRESULT DwmUnregisterThumbnail(      
    HTHUMBNAIL hThumbnailId
);

Again translation to C# will prove useful:

[DllImport("dwmapi.dll")]
static extern int DwmUnregisterThumbnail(IntPtr thumb);

DwmQueryThumbnailSourceSize

Want to know the size of the target window associated with a given thumbnail (in order to decide on scaling of the thumbnail in the call to DwmUpdateThumbnailProperties)? Use this function:

HRESULT DwmQueryThumbnailSourceSize(      
    HTHUMBNAIL hThumbnail,
    PSIZE pSize
);

Or in C# speak:

[DllImport("dwmapi.dll")]
static extern int DwmQueryThumbnailSourceSize(IntPtr thumb, out PSIZE size);

The PSIZE object is a struct containing the width (x) and height (y) of the window associated with the thumbnail (thumb) and serves as the function's output value:

[StructLayout(LayoutKind.Sequential)]
internal struct
PSIZE
{
   public int
x;
   public int
y;
}

DwmUpdateThumbnailProperties

The last and maybe the most important function is this one. It's used to configure the thumbnail properties. By doing so, the DWM knows what kind of thumbnail you want to receive (opaque, format, source window region, etc).

HRESULT DwmUpdateThumbnailProperties(      
    HTHUMBNAIL hThumbnailId,
    const DWM_THUMBNAIL_PROPERTIES *ptnProperties
);

Its translation to C# looks like this:

[DllImport("dwmapi.dll")]
static extern int DwmUpdateThumbnailProperties(IntPtr hThumb, ref DWM_THUMBNAIL_PROPERTIES props);

where DWM_THUMBNAIL_PROPERTIES is defined as a struct:

[StructLayout(LayoutKind.Sequential)]
internal struct DWM_THUMBNAIL_PROPERTIES
{
   public int dwFlags;
   public Rect rcDestination;
   public Rect rcSource;
   public byte opacity;
   public bool fVisible;
   public bool fSourceClientAreaOnly;
}

[
StructLayout(LayoutKind.Sequential)]
internal struct Rect
{
   internal Rect(int left, int top, int right, int bottom)
   {
      Left = left;
      Top = top;
      Right = right;
      Bottom = bottom;
   }

   public int Left;
   public int Top;
   public int Right;
   public int Bottom;
}

The Rect struct is used to pass rectangle coordinates (left top, right bottom). You could use the Rectangle class of the .NET Framework too because it has the same layout, but this would make things a bit less clear, since the Rectangle class uses Left, Width, Top, Height values.

A simple sample

Time to build a simple demo application. Start by performing the following steps:

  1. Create a new C# Windows Forms project.
  2. Modify Form1 to have the following layout:


    • Title label
    • Combobox lstWindows with DropDownList behavior (anchored to Top and Right)
    • Refresh button btnRefresh (anchored to Top and Right)
    • PictureBox image anchored to Top, Left, Right and Bottom; used to auto-calculate the available space (this is not going to be the drawing region used by the DWM since the DWM always works through the handle to the window itself and not an individual control)
    • TrackBar control opacity for the opacity slider at the bottom, ranging from 0 to 255 with 255 set as the default value (anchored to Left and Bottom).
  3. Add the static interop methods mentioned in the previous paragraph to the class.
  4. Add the interop structs to the project, in a separate code file or at the bottom of the form1.cs file.

Getting all the visible windows

The first thing we need to add to the project is a method that's able to find out about all the visible windows in the user's session and to obtain the required handles to those windows. We use the following function to do this:

private List<Window> windows;

private void
GetWindows()
{
   windows =
new List<Window
>();

   EnumWindows(Callback, 0);

   lstWindows.Items.Clear();
   foreach (Window w in
windows)
      lstWindows.Items.Add(w);
}

private bool Callback(IntPtr hwnd, int
lParam)
{
   if (this
.Handle != hwnd && (GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
   {
      StringBuilder sb = new StringBuilder
(200);
      GetWindowText(hwnd, sb, sb.Capacity);
      Window t = new Window
();
      t.Handle = hwnd;
      t.Title = sb.ToString();
      windows.Add(t);
   }

   return true;
//continue enumeration
}

The GetWindows method is responsible to populate the lstWindows combobox. To do this, it relies on EnumWindows, a user32 function that uses a callback mechanism to list windows (as an alternative to iterator-based functions like GetWindow which can be error-prone):

[DllImport("user32.dll")]
static extern int EnumWindows(EnumWindowsCallback lpEnumFunc, int lParam);

with the callback defined through a delegate (based on the EnumWindowsProc callback in the Windows SDK):

delegate bool EnumWindowsCallback(IntPtr hwnd, int lParam);

Our Callback method matches the delegate's signature and can be passed to EnumWindows. The lParam parameter isn't used (it serves as a parameter passing mechanism to the callback).

In the Callback method we obtain the handle to the window (passed as the hwnd parameter) and query the window title text using GetWindowText:

[DllImport("user32.dll")]
public static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

Notice the use of System.Text.StringBuilder when coping with strings through interop. Another function used is GetWindowLongA to get additional information about the window passed to the callback function:

[DllImport("user32.dll")]
static extern ulong GetWindowLongA(IntPtr hWnd, int nIndex);

Using the GWL_STYLE constant we ask for style properties which are matched against another custom constant "TARGETWINDOW" that combines the visibility (WS_VISIBLE) and border (WS_BORDER) properties of the window:

static readonly int GWL_STYLE = -16;

static readonly ulong WS_VISIBLE = 0x10000000L;
static readonly ulong
WS_BORDER = 0x00800000L;
static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;

Finally there is the Window helper class to keep the handle and the friendly name (i.e. the window title):

internal class Window
{
   public string
Title;
   public IntPtr
Handle;

   public override string
ToString()
   {
      return
Title;
   }
}

Loading and refreshing the window list

We'll display the window list upon loading the form:

private void Form1_Load(object sender, EventArgs e)
{
   GetWindows();
}

and refresh the list when the Refresh button is clicked:

private void btnRefresh_Click(object sender, EventArgs e)
{
   if (thumb != IntPtr
.Zero)
      DwmUnregisterThumbnail(thumb);

   GetWindows();
}

Registering and deregistering for a thumbnail

In order to obtain thumbnails we need to register our interest to receive a thumbnail as explained earlier. We keep the thumbnail in a private variable:

private IntPtr thumb;

To set the thumbnail we use the SelectedIndexChanged event of the combobox:

private void lstWindows_SelectedIndexChanged(object sender, EventArgs e)
{
   Window w = (Window
)lstWindows.SelectedItem;
   if (thumb != IntPtr
.Zero)
      DwmUnregisterThumbnail(thumb);

   int
i = DwmRegisterThumbnail(this.Handle, w.Handle, out thumb);
   if
(i == 0)
      UpdateThumb();
}

In case a thumbnail was already selected, we unregister our further interest in it. This will stop the DWM to render the thumbnail to the current window. Then, we register to receive the thumbnail of the selected window (obtained by means of the SelectedItem property of the combobox) in the current window (this.Handle). This causes a thumbnail pointer to be returned by the output parameter, which sets the thumb private variable.

Configuration and rendering of the thumbnail

Finally, we need to inform the DWM about the desired thumbnail properties. This is implemented in the UpdateThumb method:

static readonly int DWM_TNP_VISIBLE = 0x8;
static readonly int
DWM_TNP_OPACITY = 0x4;
static readonly int DWM_TNP_RECTDESTINATION = 0x1;


private
void UpdateThumb()
{
   if (thumb != IntPtr
.Zero)
   {
      PSIZE
size;
      DwmQueryThumbnailSourceSize(thumb,
out
size);

     
DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES
();
      props.dwFlags = DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY;

      props.fVisible = true
;
      props.opacity = (
byte)opacity.Value;

      props.rcDestination =
new Rect
(image.Left, image.Top, image.Right, image.Bottom);
      if
(size.x < image.Width)
         props.rcDestination.Right = props.rcDestination.Left + size.x;
      if
(size.y < image.Height)
         props.rcDestination.Bottom = props.rcDestination.Top + size.y;

      DwmUpdateThumbnailProperties(thumb,
ref
props);
   }
}

The logic is pretty simple:

  • First we ask the DWM for the size (PSIZE) of the target to-be-thumbnailed window.
  • Next we create a property bag (a DWM_THUMBNAIL_PROPERTIES object). Using the dwFlags field, we indicate which properties will be set. The symbolic constants can be found in the dwmapi.h file. We set the visibility property, the opacity retrieved from the slider control and the destination region based on the image picturebox size (a little trick to obtain the draw region thanks to control anchoring as mentioned before). This region is reduced if it would result in sizing the thumbnail larger than the source window.
  • Finally, the properties are sent to the DWM using DwmUpdateThumbnailProperties.

The UpdateThumb method is called in a few other event handler too:

private void opacity_Scroll(object sender, EventArgs e)
{
   UpdateThumb();
}

private void Form1_Resize(object sender, EventArgs
e)
{
   UpdateThumb();
}

That is, when the form is resized and the opacity value is changed using the slider control.

Done!

Right, we're done. Time to test. You can download the code over here. When you run the application you should see a list of Windows appearing:

When you select an application now, you'll see a live thumbnail in the window. For example, run Windows Media Player with vizualizations turned on or play some video:

Play a bit with the opacity slider. It's the DWM that starts to render the video with opacity right now on our window:

Finally, resize the window. The size of the thumbnail should change accordingly:

Using (CTRL-)ALT-TAB and the taskbar image you can see that all images are synchronized:

Now, that's just one of the things in Vista I consider to be very cool. I hope you do to... Time to DWM!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: ,

Comments

# Faux&#8217;s Blog &raquo; Blog Archive &raquo; C# + DWM -> pretty!

Friday, April 13, 2007 3:22 PM by Faux’s Blog » Blog Archive » C# + DWM -> pretty!

# The VB version of the Flip Task Bar for Vista Desktop Window Manager

Tuesday, May 22, 2007 1:30 PM by Calvin Hsia's WebLog

In this post: Create your own Flip Task Bar with live thumbnails using Vista Desktop Window Manager DWM

# http://community.bartdesmet.net/blogs/bart/archive/2006/10/05/4495.aspx

# Fensterinhalt einer verdeckten Applikation auslesen | hilpers

Pingback from  Fensterinhalt einer verdeckten Applikation auslesen | hilpers

# Joe White&#8217;s Blog &raquo; Blog Archive &raquo; Continuing the browser-testing journey: more automation and Vista thumbnails

Pingback from  Joe White&#8217;s Blog  &raquo; Blog Archive   &raquo; Continuing the browser-testing journey: more automation and Vista thumbnails

# ??????????????? | Hello, Visual Basic program&#8230;

Sunday, February 12, 2012 7:10 PM by ??????????????? | Hello, Visual Basic program…

Pingback from  ??????????????? | Hello, Visual Basic program&#8230;

# Save Live Thumbnail (DWM) C# | MSDN @ EEYOGO

Thursday, November 15, 2012 1:55 PM by Save Live Thumbnail (DWM) C# | MSDN @ EEYOGO

Pingback from  Save Live Thumbnail (DWM) C# | MSDN @ EEYOGO

# C# How to get a screenshot of non visible or minimized window?CopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite query, nodejsquery,

Pingback from  C# How to get a screenshot of non visible or minimized window?CopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite query, nodejsquery, dns query, update query, insert query, kony, mobilesecurity, postquery, queryposts.com, sapquery, jquery, mobilequery,gsm query, cdmaquery, phonegap, appcelerator