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); /// /// 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) /// /// /// 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 /// /// handle to a Window 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; } /// /// 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 /// /// handle to a Window /// true to use ClientDC, false to use WindowDC 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; } } }