Click here to Skip to main content
15,881,413 members
Please Sign up or sign in to vote.
5.00/5 (3 votes)
Hello all :)
(Sorry for my bad English)

Recently I've started a small application that can show live window previews using DWM. One of the features is to render only part of source window. That can be achieved using DWM API, and that is all working...
End-user can enter the RECT values (X, Y, Width and Height) but that is not practical and is very uncomfortable. My idea was to let user to select (with cursor or other pointing device) visually which part of the source window will be rendered. So, I need image of source window. Selecting and etc. is not a problem, I've finished that...
The problem I'm facing is how to correctly capture a window?

I need help with writing some sort of function that accepts hWnd/Handle of window and returns its image.

Of course I've tried to solve problem by naive capturing (BitBlt direct to Graphics.HDC) [1] or by PrintWindow function [2]. After that, tried "advanced" capturing to CompatibleBitmap (created with CreateCompatibleBitmap function) [3] and that is closest to what I want. But... It is closest, but too far to accept it as solution.
I also tried with Form.DrawToBitmap() [4] but unsuccessfully.

Problems:
[1] it doesn't capture very well (anyway, much more better is [3] method)
[2] some applications are ignoring the WM_PRINT and WM_PRINTCLIENT messages. Also, PrintWindow will print black image when window is minimized.
[3] Well, There are many problems... When window is minimized I can only get its titlebar (or for some windows, nothing, blank image).
Furthermore, BitBlt (IMHO) cannot capture some Aero portions, image 1 [Link^]
Also, when window is maximized, BitBlt doesn't capture its titlebar, image 2 [Link^]
In Image 1 you can see in the bottom right corner a little bit of taskbar, and black border around VS window (that black border is real Aero border)
Image 2: you can also see portions of taskbar, but VS titlebar is seen behind the window (where Aero titlebar should be)

That are problems I can't solve because I think it is a BitBlt problem. My program requires full window image, not the client area only. Also, problems posted are only when window is visible. In minimized state blank image is shown.

[4] I've tried this because DWM renders minimized windows very well. Create new form, render with DWM to it, and call .DrawToBitmap function. But this won't work because DWM is rendering on a higher layer. So, if I have a button on a form, DMW will render over it.

I will post VB.NET code of all 'solutions' I tried. I think it is easy to convert to C# or any other language...
But, before I do that, I want to mention that I want to capture window without using DicetX/OpenGL or and other 3rd party libraries. Also, solution that consists of Showing window, then capturing it, and minimizing it again is very unpractical, and my application is there to show content of window(s) when minimized.

Try [1]:
VB
Dim B As New Bitmap(Rc.Width, Rc.Height) 'Rc is Rectangle that represents window placement
Using G As Graphics = Graphics.FromImage(B)
    Dim I As IntPtr = G.GetHdc
    Dim U As IntPtr = GetWindowDC(H)
    BitBlt(I, 0, 0, Rc.Width, Rc.Height, U, Rc.Left, Rc.Top, &HCC0020) ' SrcCpy
    'ReleaseDC(H, U)
    G.ReleaseHdc(I)
End Using

Try [2]:
VB
Dim B As New Bitmap(Rc.Width, Rc.Height)
Using G As Graphics = Graphics.FromImage(B)
    Dim I As IntPtr = G.GetHdc
    PrintWindow(H, I, 0)
    G.ReleaseHdc(I)
End Using

Try [3]
I'm not author of this particularly code, but is not hard to implement
VB
Public Function CaptureWindow(handle As IntPtr) As Image
        ' get te hDC of the target window
        Dim hdcSrc As IntPtr = GetWindowDC(handle)
        ' get the size
        Dim windowRect As New RECT()
        GetWindowRect(handle, windowRect)
        Dim R As Rectangle = GetAeroRect(handle) ' ------> NOTE 1#
        Dim width As Integer = windowRect.Right - windowRect.Left
        Dim height As Integer = windowRect.Bottom - windowRect.Top
        ' create a device context we can copy to
        Dim hdcDest As IntPtr = CreateCompatibleDC(hdcSrc)
        ' create a bitmap we can copy it to,
        ' using GetDeviceCaps to get the width/height
        Dim hBitmap As IntPtr = CreateCompatibleBitmap(hdcSrc, width, height)
        ' select the bitmap object
        Dim hOld As IntPtr = SelectObject(hdcDest, hBitmap)
        ' bitblt over
        BitBlt(hdcDest, 0, 0, width, height, hdcSrc, _
            0, 0, CopyPixelOperation.SourceCopy)
        ' restore selection
        SelectObject(hdcDest, hOld)
        ' clean up
        DeleteDC(hdcDest)
        ReleaseDC(handle, hdcSrc)
        ' get a .NET image object for it
        Dim img As Image = Image.FromHbitmap(hBitmap)
        ' free up the Bitmap object
        DeleteObject(hBitmap)
        Return img
    End Function

Try 4 is simple, and I won't post it.

In Try [3], line commented with Note 1#
VB
Dim R As Rectangle = GetAeroRect(handle)

That was another problem I was facing. The standard GetWindowRect function for some windows returned wrong results (ex. Skype window), because of Aero glass pixel padding Link (new window/tab).
I created GetAeroRect function that uses DwmGetWindowAttribute API, and it is working for now (haven't noticed wrong results)


So, that is it. I'm really, really sorry for grammar mistakes and for the very big question. But I haven't no choice, I want to post a crystal clear question, don't want to get questions like 'Have you tried PrintWindow?'; 'How you tried BitBlt?'; 'This is DirectX solution' etc... and to post again and again.

Solution in VB.NET, C#, or even C/C++ language is appreciated, but that is not important, important is a concept and set of API calls.

I will appreciate any help :)


Thanks in advance, Xson
Posted
Updated 15-Mar-14 9:25am
v3
Comments
Philippe Mori 15-Mar-14 16:34pm    
Well a minimized Windows does not really have any content...
XSON-NEON 15-Mar-14 17:04pm    
Thank you for response...

So, I can't capture content of minimized window without showing it first? So saaad :(

And you sad that minimized window does not have and content? Well, that is interesting...

I will give yourself 5 days to find solution (if any), and if unsuccessful, will try to solve it by showing and hiding the window and disabling the animate effect.

Again, thanks for response :)
Philippe Mori 16-Mar-14 19:08pm    
This is what I think... In Windows 8.1, if I hover the taskbar for a program like VLC, the displayed image is the one that was displayed just before the Windows was minimized.

Some programs like Media player display live video on that thumbnail even when the player is minimized but as far as I understand that application would uses functions added for that purpose in Vista and later...
banato 26-Dec-14 7:34am    
Hi Xson,
Did you have found the solution?

I have the same task...

Thank you.
XSON-NEON 9-Feb-15 12:32pm    
Hi banato
No, unfortunately. But I've used this workaround:
http://www.codeproject.com/Articles/20651/Capturing-Minimized-Window-A-Kid-s-Trick

I doesn't work in all cases, but it works in almost enough cases for me.

1 solution

The solution is simple. Put your code within the appropriate case statement.

C++
//To obtain the position coordinates in screen coordinates, use the following code:

xPos = GET_X_LPARAM(lParam);    // horizontal position 
yPos = GET_Y_LPARAM(lParam);    // vertical position



     CASE WM_SYSCOMMAND:
     {
	switch(WMNUM)
	{
	case SC_MINIMIZE:
				
		
		
		
		break;
	case SC_MAXIMIZE:
		
		
		
		
		break;
	case SC_SIZE:
		
		
		
		
		break;
	case SC_RESTORE:
		
		
		
		
		break;
	case SC_MOVE:
		
		
		
		
		break;
	case SC_CLOSE:
		
		
		
		
		break;
	default:
	}

     }
 
Share this answer
 
Comments
XSON-NEON 16-Mar-14 10:43am    
The_Inventor, thank you for your effort. I appreciate it.

But problem is much bigger. As I already said in Question, I want to capture window, but that window 100% is not my application's window.
As an example I wrote about problems with Skype and Explorer windows...

So, accessing WndProc procedure of another process window is almost impossible. It is very hard (IMHO).
Using NativeWindow from .NET and assigning it a handle doesn't work because it is another process window.

Anyway, thank you again.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900