Background
I needed to capture from the minimized window for one of my projects. After several tries of using ordinary methods and failing; I searched the Web a lot (OK, not a lot) but couldn't find a solution, so I decided to work on a new one.
First Words
First of all, thank God for saving my head from blowing out, because I needed to do that really.
Maybe this method is not such a reliable one, but it solved my problem and may be useful for some others, so I thought putting it on The Code Project is better than not doing so.
By the way, this is my first CodeProject article, so forgive me if you see any flaws in it, and please help me with your suggestions for my further actions.
The Problem
OK, the core of this method is the PrintWindow
API which draws the face of the window on the specific Device Context.
This method can be used to capture a window regardless of its order and transparency level (it can capture from a partially or fully hidden window), but the main problem is, it cannot capture the minimized window, actually when the window is minimized, it won't be drawn anymore, so if we use PrintWindow
it only gives the title of the window back to us.
I tried several methods for capturing a minimized window but I failed, except this one.
How to Capture
I assumed there is no way to capture from the minimized window, so this assumption changed the approach.
I tried to make the window back to normal and capture it then, and then minimize it again; for having a hidden capture, before we pop-up the window from minimized, it must be transparent and it can be done using the SetLayeredWindowAttributes
API, but there was another problem. Again the animation of popping up window was shown, even if the window was fully transparent.
By using SPY++, I monitored the messages for explorer and then unchecked "Animate Windows when minimizing and maximizing" and found this message SPI_SETANIMATION
then, with a few searches, I found that it can be changed through the SystemParametersInfo
API.
This effect can be disabled through the registry, but it's not real-time.
HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics\MinAnimate
If the value is 0
, the effect is disabled, and for 1
it is enabled.
So here are the steps I followed to capture a minimized window:
- Disable
MinAnimate
effect
- Transparent the window (
PrintWindow
can take a snap from such a window)
- Restore the Window
- Capture
- Minimize it again
- Remove transparency
- Enable
MinAnimate
effect
Now let's check the codes.
1 & 7 - Disable/Enable MinAnimate Effect
I forgot to mention, I say MinAnimate
because it's the name in the Registry.
As I mentioned before, it can be done by using SystemParametersInfo
, here it is:
(This snippet is in XPAppearance.cs)
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SystemParametersInfo(SPI uiAction, uint uiParam,
ref ANIMATIONINFO pvParam, SPIF fWinIni);
SPI
is an enumeration of SPI
messages, we want to disable/enable the MinAnimate
so we must set uiAction
to SPI_SETANIMATION
.
We can get
/set
the effect status in ANIMATIONINFO
structure.
As a note, cbsize
member of ANIMATIONINFO
must be set to the size of the structure, and pvParam
must also be equal to cbSize
.
Here is the structure:
(This snippet is in XPAppearance.cs)
[StructLayout(LayoutKind.Sequential)]
private struct ANIMATIONINFO
{
public ANIMATIONINFO(bool iMinAnimate)
{
this.cbSize = GetSize();
if (iMinAnimate) this.iMinAnimate = 1;
else this.iMinAnimate = 0;
}
public uint cbSize;
private int iMinAnimate;
public bool IMinAnimate
{
get
{
if (this.iMinAnimate == 0) return false;
else return true;
}
set
{
if (value == true) this.iMinAnimate = 1;
else this.iMinAnimate = 0;
}
}
public static uint GetSize()
{
return (uint)Marshal.SizeOf(typeof(ANIMATIONINFO));
}
}
And here is the method I used for disable/enable MinAnimate
:
(This snippet is in XPAppearance.cs)
public static void SetMinimizeMaximizeAnimation(bool status)
{
ANIMATIONINFO animationInfo=new ANIMATIONINFO(status);
SystemParametersInfo(SPI.SPI_GETANIMATION, ANIMATIONINFO.GetSize(),
ref animationInfo, SPIF.None);
if (animationInfo.IMinAnimate != status)
{
animationInfo.IMinAnimate = status;
SystemParametersInfo(SPI.SPI_SETANIMATION, ANIMATIONINFO.GetSize(),
ref animationInfo, SPIF.SPIF_SENDCHANGE);
}
}
I think there is nothing to explain with the code above.
2 & 6 - Add/Remove Transparency
For making the window invisible but with capturing ability, we can make it transparent.
It can be done somewhat like this:
(This snippet is in WindowSnap.cs)
[DllImport("user32")]
private static extern int GetWindowLong(IntPtr hWnd, int index);
[DllImport("user32")]
private static extern int SetWindowLong(IntPtr hWnd, int index, int dwNewLong);
[DllImport("user32")]
private static extern int SetLayeredWindowAttributes(IntPtr hWnd, byte crey,
byte alpha, int flags);
(This snippet is in EnterSpecialCapturing
method in WindowSnap.cs)
winLong = GetWindowLong(hWnd, GWL_EXSTYLE);
SetWindowLong(hWnd, GWL_EXSTYLE, winLong | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, 0, 1, LWA_ALPHA);
For removing applied transparency, we can do it by setting the old winLong
again.
(This snippet is in the ExitSpecialCapturing
method in WindowSnap.cs)
SetWindowLong(hWnd, GWL_EXSTYLE, winLong);
3 & 5 - Restore/Minimize The Window
This part is fairly easy, it can be done using the ShowWindow
API.
(This snippet is in WindowSnap.cs)
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);
private enum ShowWindowEnum{Hide = 0,
ShowNormal = 1,ShowMinimized = 2,ShowMaximized = 3,
Maximize = 3,ShowNormalNoActivate = 4,Show = 5,
Minimize = 6,ShowMinNoActivate = 7,ShowNoActivate = 8,
Restore = 9,ShowDefault = 10,ForceMinimized = 11};
(This snippet is in EnterSpecialCapturing
method in WindowSnap.cs)
ShowWindow(hWnd, ShowWindowEnum.Restore);
And for minimizing it again, we can use:
(This snippet is in the ExitSpecialCapturing
method in WindowSnap.cs)
ShowWindow(hWnd, ShowWindowEnum.Minimize);
4 - Capture
Nothing to say, there are dozens of articles about it; here is the code:
(This snippet is in the GetWindowImage
method in WindowSnap.cs)
Bitmap bmp = new Bitmap(size.Width, size.Height);
Graphics g = Graphics.FromImage(bmp);
IntPtr dc = g.GetHdc();
PrintWindow(hWnd, dc, 0);
g.ReleaseHdc();
g.Dispose();
Capturing Child Windows
Capturing from MDI children is very similar to capturing ordinary windows, but with one exception, if the child window is partially hidden by the parent (and not another child), that part would be drawn as blank. For solving this problem, we can use GetParent
and SetParent
APIs to make it as an ordinary window and back.
[DllImport("user32")]
private static extern IntPtr GetParent(IntPtr hWnd);
[DllImport("user32")]
private static extern IntPtr SetParent(IntPtr child, IntPtr newParent);
We can pass IntPtr.Zero
to newParent
in the GetParent
method for making it normal (not child).
Last Words
That's all, and for the last words, you can take a snap from Windows using WindowSnap
class. It has two methods, GetAllWindows
and GetWindowSnap
. The first one returns a collection of window snaps from all available windows, and the second one returns a snap of the specific window.
Finally, I thank you for spending your time and reading this article. I hope it would be useful. Please help me with your suggestions and ideas for making this article better.
Updates
- 6th October, 2007
- Capturing Child Windows: Support for child windows added. Special thanks to mmjc23 who mentioned that. For more details, see Capturing Child Windows section.
- MinAnimate Bug Fix: In the previous version, if
MinAnimate
effect was disabled, WindowSnap
would enable it after SpecialCapturing
(capturing from minimized window). Special thanks to Kalabuli who notified me about this bug.
- 10th October, 2007
- Child Dialog Capturing Fix: Capturing from Modal child dialogs fixed. Special thanks to Hitesh who notified me about this bug.