208 lines
9.3 KiB
C#
208 lines
9.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Runtime.InteropServices;
|
|
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using System.Reflection;
|
|
|
|
using Foo.Platform;
|
|
using Foo.Platform.Win32;
|
|
|
|
namespace Foo.GUILib
|
|
{
|
|
|
|
public class ScreenCapture
|
|
{
|
|
// Declare the Log4net Variable
|
|
private static log4net.ILog Log = Logger.GetLog4NetInterface(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
/// <summary>
|
|
/// Saves a Window using a DC of the Full Screen, then crops the window out of the
|
|
/// full screen capture. We have to do this because SaveWindowUsingDCNBitBlt() doesn't
|
|
/// work for all programs that overwrite the glass area on Aero (like MS Office)
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// IMP! - This function will work on some multiple monitor configurations but currently NOT all!
|
|
/// If for example, the primary display is on the left and sec on the right, and the window is on the
|
|
/// right monitor, then all x and y values will be positive, (We currently only handle multiple monitors,
|
|
/// where one x or y value is negative!),
|
|
/// ~more calculations must be done for multiple x, multiple y positive configurations
|
|
/// </remarks>
|
|
/// <param name="hWnd">handle to a Window</param>
|
|
public Image SaveWindowUsingFullScreenShotCrop(IntPtr hWnd)
|
|
{
|
|
try
|
|
{
|
|
// First get the Window Rect
|
|
RECT rectWnd;
|
|
Win32Functions.GetWindowRect(hWnd, out rectWnd);
|
|
|
|
// Now figure out which monitor the window is in
|
|
IntPtr hMonitor = Win32Functions.MonitorFromRect(ref rectWnd, (int)Win32_Constants.MONITOR_DEFAULTTONEAREST);
|
|
|
|
// Get the szDevice Name for the Monitor
|
|
MONITORINFOEX monitorinfoex = new MONITORINFOEX();
|
|
monitorinfoex.cbSize = Marshal.SizeOf(monitorinfoex);
|
|
if (!Win32Functions.GetMonitorInfo(hMonitor, ref monitorinfoex))
|
|
return null;
|
|
|
|
// Use szDevice to create a DC
|
|
IntPtr hDC = Win32Functions.CreateDC(monitorinfoex.szDeviceName, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
|
|
|
//Here we make a compatible device context in memory for screen device context.
|
|
IntPtr hMemDC = Win32Functions.CreateCompatibleDC(hDC);
|
|
|
|
// Let's get all the monitor dimensions we need
|
|
Rectangle rectDim = monitorinfoex.rcMonitor.AsRectangle;
|
|
int DisplayHeight = rectDim.Height;
|
|
int DisplayWidth = rectDim.Width;
|
|
|
|
//The .net way to retrieve dimensions
|
|
Screen screen = Screen.FromHandle(hWnd);
|
|
|
|
//We create a compatible bitmap of screen size using screen device context.
|
|
IntPtr hBitmap = Win32Functions.CreateCompatibleBitmap(hDC, DisplayWidth, DisplayHeight);
|
|
|
|
if (hBitmap != IntPtr.Zero)
|
|
{
|
|
//Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
|
|
IntPtr hOld = (IntPtr)Win32Functions.SelectObject(hMemDC, hBitmap);
|
|
|
|
//We copy the Bitmap to the memory device context.
|
|
Win32Functions.BitBlt(hMemDC, 0, 0, DisplayWidth, DisplayHeight, hDC, 0, 0, Win32_Constants.SRCCOPY);
|
|
|
|
//We select the old bitmap back to the memory device context.
|
|
Win32Functions.SelectObject(hMemDC, hOld);
|
|
|
|
//We delete the memory device context.
|
|
Win32Functions.DeleteDC(hMemDC);
|
|
|
|
//Delete the screen device context.
|
|
Win32Functions.DeleteDC(hDC);
|
|
|
|
//Image is created by Image bitmap handle and stored in local variable.
|
|
Image image = (Image)System.Drawing.Image.FromHbitmap(hBitmap);
|
|
|
|
// * For Debugging *
|
|
//string strFileNameNPathWExtPNG = (Env.GetSnapshotDirectory() + @"\" + hWnd.ToString() + ".png");
|
|
//image.Save(strFileNameNPathWExtPNG, System.Drawing.Imaging.ImageFormat.Png);
|
|
|
|
//Release the memory for compatible bitmap.
|
|
Win32Functions.DeleteObject(hBitmap);
|
|
|
|
////
|
|
// Imp! - Now we want to crop the Window out of the Screen Image
|
|
// NOTE! - Here we convert the screen positions into the corresponding
|
|
// image position on that screen
|
|
////
|
|
int height = rectWnd.AsRectangle.Height;
|
|
int width = rectWnd.AsRectangle.Width;
|
|
|
|
rectWnd.left = Math.Abs(rectWnd.left - screen.Bounds.Left);
|
|
rectWnd.right = rectWnd.left + width;
|
|
|
|
rectWnd.top = Math.Abs(rectWnd.top - screen.Bounds.Top);
|
|
rectWnd.bottom = rectWnd.top + height;
|
|
|
|
// Convert the Rectangle for .Net Use
|
|
Rectangle rectangleWnd = rectWnd.AsRectangle;
|
|
|
|
Bitmap bmpImage = new Bitmap(image);
|
|
Bitmap bmpCrop = bmpImage.Clone(rectangleWnd, bmpImage.PixelFormat);
|
|
|
|
//Bitmap bmpCrop = (Bitmap)image;
|
|
//bmpCrop = bmpCrop.Clone(rectangleWnd, bmpCrop.PixelFormat);
|
|
|
|
return (Image)bmpCrop;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(string.Format("{0}() - Error thrown", MethodBase.GetCurrentMethod().Name), e);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the Window using a DC, this works well for bUseClientDC = true, but doesn't
|
|
/// work when using the Window's DC because of Aero drawing via directx, the window frame
|
|
/// won't be in the image
|
|
/// </summary>
|
|
/// <param name="hWnd">handle to a Window</param>
|
|
/// <param name="bUseClientDC">true to use ClientDC, false to use WindowDC</param>
|
|
public Image SaveWindowUsingDCNBitBlt(IntPtr hWnd, bool bUseClientDC)
|
|
{
|
|
try
|
|
{
|
|
SIZE size;
|
|
IntPtr hDC;
|
|
IntPtr hBitmap;
|
|
Image image;
|
|
RECT Rect = new RECT();
|
|
|
|
if (bUseClientDC)
|
|
Win32Functions.GetClientRect(hWnd, out Rect);
|
|
else
|
|
Win32Functions.GetWindowRect(hWnd, out Rect);
|
|
|
|
size.cx = (Rect.right - Rect.left);
|
|
size.cy = (Rect.bottom - Rect.top);
|
|
|
|
// TRY USING GetDCEx() - May actually do better
|
|
//hDC = Win32Functions.GetDCEx(hWnd);
|
|
if (bUseClientDC)
|
|
hDC = Win32Functions.GetDC(hWnd);
|
|
else
|
|
hDC = Win32Functions.GetWindowDC(hWnd);
|
|
|
|
//Here we make a compatible device context in memory for screen device context.
|
|
IntPtr hMemDC = Win32Functions.CreateCompatibleDC(hDC);
|
|
|
|
//We create a compatible bitmap of screen size using screen device context.
|
|
hBitmap = Win32Functions.CreateCompatibleBitmap(hDC, size.cx, size.cy);
|
|
|
|
if (hBitmap != IntPtr.Zero)
|
|
{
|
|
//Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
|
|
IntPtr hOld = (IntPtr)Win32Functions.SelectObject(hMemDC, hBitmap);
|
|
|
|
//We copy the Bitmap to the memory device context.
|
|
Win32Functions.BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, Win32_Constants.SRCCOPY);
|
|
|
|
//We select the old bitmap back to the memory device context.
|
|
Win32Functions.SelectObject(hMemDC, hOld);
|
|
|
|
//We delete the memory device context.
|
|
Win32Functions.DeleteDC(hMemDC);
|
|
|
|
//We release the screen device context.
|
|
Win32Functions.ReleaseDC(hWnd, hDC);
|
|
|
|
//Image is created by Image bitmap handle and stored in local variable.
|
|
image = (Image)System.Drawing.Image.FromHbitmap(hBitmap);
|
|
|
|
// * For Debugging *
|
|
//string strFileNameNPathWExtPNG = (Env.GetSnapshotDirectory() + @"\" + hWnd.ToString() + ".png");
|
|
//image.Save(strFileNameNPathWExtPNG, System.Drawing.Imaging.ImageFormat.Png);
|
|
|
|
//Release the memory for compatible bitmap.
|
|
Win32Functions.DeleteObject(hBitmap);
|
|
return image;
|
|
}
|
|
else
|
|
{
|
|
Log.Error(string.Format("{0}() - hBitmap is Null, something is wrong", MethodBase.GetCurrentMethod().Name));
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(string.Format("{0}() - Error thrown", MethodBase.GetCurrentMethod().Name), e);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|