diff --git a/Greenshot/Forms/MovableShowColorForm.cs b/Greenshot/Forms/MovableShowColorForm.cs index f2f788d79..edb3a66cf 100644 --- a/Greenshot/Forms/MovableShowColorForm.cs +++ b/Greenshot/Forms/MovableShowColorForm.cs @@ -86,16 +86,13 @@ namespace Greenshot.Forms { /// Point with the coordinates /// Color at the specified screenCoordinates static private Color GetPixelColor(Point screenCoordinates) { - IntPtr hdc = User32.GetDC(IntPtr.Zero); - try { - uint pixel = GDI32.GetPixel(hdc, screenCoordinates.X, screenCoordinates.Y); - Color color = Color.FromArgb(255, (int)(pixel & 0xFF), (int)(pixel & 0xFF00) >> 8, (int)(pixel & 0xFF0000) >> 16); - return color; - } catch (Exception) { - return Color.Empty; - } finally { - if (hdc != IntPtr.Zero) { - User32.ReleaseDC(IntPtr.Zero, hdc); + using (SafeWindowDCHandle screenDC = SafeWindowDCHandle.fromDesktop()) { + try { + uint pixel = GDI32.GetPixel(screenDC, screenCoordinates.X, screenCoordinates.Y); + Color color = Color.FromArgb(255, (int)(pixel & 0xFF), (int)(pixel & 0xFF00) >> 8, (int)(pixel & 0xFF0000) >> 16); + return color; + } catch (Exception) { + return Color.Empty; } } } diff --git a/GreenshotPlugin/Controls/AnimatingForm.cs b/GreenshotPlugin/Controls/AnimatingForm.cs index 3d5324bd3..b64302054 100644 --- a/GreenshotPlugin/Controls/AnimatingForm.cs +++ b/GreenshotPlugin/Controls/AnimatingForm.cs @@ -48,9 +48,9 @@ namespace GreenshotPlugin.Controls { get { if (vRefresh == 0) { // get te hDC of the desktop to get the VREFRESH - IntPtr hDCDesktop = User32.GetWindowDC(User32.GetDesktopWindow()); - vRefresh = GDI32.GetDeviceCaps(hDCDesktop, DeviceCaps.VREFRESH); - User32.ReleaseDC(hDCDesktop); + using (SafeWindowDCHandle desktopHandle = SafeWindowDCHandle.fromDesktop()) { + vRefresh = GDI32.GetDeviceCaps(desktopHandle, DeviceCaps.VREFRESH); + } } // A vertical refresh rate value of 0 or 1 represents the display hardware's default refresh rate. // As there is currently no know way to get the default, we guess it. diff --git a/GreenshotPlugin/Core/ClipboardHelper.cs b/GreenshotPlugin/Core/ClipboardHelper.cs index 44ca8b023..a4e134d2b 100644 --- a/GreenshotPlugin/Core/ClipboardHelper.cs +++ b/GreenshotPlugin/Core/ClipboardHelper.cs @@ -493,7 +493,7 @@ EndSelection:<<<<<<<4 /// private const int BITMAPFILEHEADER_LENGTH = 14; public static void SetClipboardData(ISurface surface) { - DataObject ido = new DataObject(); + DataObject dataObject = new DataObject(); // This will work for Office and most other applications //ido.SetData(DataFormats.Bitmap, true, image); @@ -515,7 +515,7 @@ EndSelection:<<<<<<<4 ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); pngStream.Seek(0, SeekOrigin.Begin); // Set the PNG stream - ido.SetData(FORMAT_PNG, false, pngStream); + dataObject.SetData(FORMAT_PNG, false, pngStream); } } catch (Exception pngEX) { LOG.Error("Error creating PNG for the Clipboard.", pngEX); @@ -534,7 +534,7 @@ EndSelection:<<<<<<<4 } // Set the DIB to the clipboard DataObject - ido.SetData(DataFormats.Dib, true, dibStream); + dataObject.SetData(DataFormats.Dib, true, dibStream); } } catch (Exception dibEx) { LOG.Error("Error creating DIB for the Clipboard.", dibEx); @@ -544,7 +544,7 @@ EndSelection:<<<<<<<4 if (config.ClipboardFormats.Contains(ClipboardFormat.HTML)) { string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); string html = getHTMLString(surface, tmpFile); - ido.SetText(html, TextDataFormat.Html); + dataObject.SetText(html, TextDataFormat.Html); } else if (config.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL)) { string html; using (MemoryStream tmpPNGStream = new MemoryStream()) { @@ -560,18 +560,18 @@ EndSelection:<<<<<<<4 } html = getHTMLDataURLString(surface, tmpPNGStream); } - ido.SetText(html, TextDataFormat.Html); + dataObject.SetText(html, TextDataFormat.Html); } } finally { // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone! // Check if Bitmap is wanted if (config.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) { - ido.SetImage(imageToSave); + dataObject.SetImage(imageToSave); // Place the DataObject to the clipboard - SetDataObject(ido, true); + SetDataObject(dataObject, true); } else { // Place the DataObject to the clipboard - SetDataObject(ido, true); + SetDataObject(dataObject, true); } if (pngStream != null) { diff --git a/GreenshotPlugin/Core/WindowCapture.cs b/GreenshotPlugin/Core/WindowCapture.cs index 861e335e5..8a2a2c209 100644 --- a/GreenshotPlugin/Core/WindowCapture.cs +++ b/GreenshotPlugin/Core/WindowCapture.cs @@ -428,6 +428,14 @@ namespace GreenshotPlugin.Core { private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WindowCapture)); private static CoreConfiguration conf = IniConfig.GetIniSection(); + /// + /// Used to cleanup the unmanged resource in the iconInfo for the CaptureCursor method + /// + /// + /// + [DllImport("gdi32", SetLastError = true)] + private static extern bool DeleteObject(IntPtr hObject); + private WindowCapture() { } @@ -517,10 +525,10 @@ namespace GreenshotPlugin.Core { } if (iconInfo.hbmMask != IntPtr.Zero) { - GDI32.DeleteObject(iconInfo.hbmMask); + DeleteObject(iconInfo.hbmMask); } if (iconInfo.hbmColor != IntPtr.Zero) { - GDI32.DeleteObject(iconInfo.hbmColor); + DeleteObject(iconInfo.hbmColor); } } } @@ -640,123 +648,109 @@ namespace GreenshotPlugin.Core { // } // capture.Image = capturedBitmap; // capture.Location = captureBounds.Location; - - // "P/Invoke" Solution for capturing the screen - IntPtr hWndDesktop = User32.GetDesktopWindow(); - // get te hDC of the target window - IntPtr hDCDesktop = User32.GetWindowDC(hWndDesktop); - // Make sure the last error is set to 0 - Win32.SetLastError(0); + using (SafeWindowDCHandle desktopDCHandle = SafeWindowDCHandle.fromDesktop()) { + if (desktopDCHandle.IsInvalid) { + // Get Exception before the error is lost + Exception exceptionToThrow = CreateCaptureException("desktopDCHandle", captureBounds); + // throw exception + throw exceptionToThrow; + } - // create a device context we can copy to - IntPtr hDCDest = GDI32.CreateCompatibleDC(hDCDesktop); - // Check if the device context is there, if not throw an error with as much info as possible! - if (hDCDest == IntPtr.Zero) { - // Get Exception before the error is lost - Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds); - // Cleanup - User32.ReleaseDC(hWndDesktop, hDCDesktop); - // throw exception - throw exceptionToThrow; - } + // create a device context we can copy to + using (SafeCompatibleDCHandle safeCompatibleDCHandle = GDI32.CreateCompatibleDC(desktopDCHandle)) { + // Check if the device context is there, if not throw an error with as much info as possible! + if (safeCompatibleDCHandle.IsInvalid) { + // Get Exception before the error is lost + Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds); + // throw exception + throw exceptionToThrow; + } + // Create BitmapInfoHeader for CreateDIBSection + BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24); - // Create BitmapInfoHeader for CreateDIBSection - BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24); + // Make sure the last error is set to 0 + Win32.SetLastError(0); - // Make sure the last error is set to 0 - Win32.SetLastError(0); + // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height + IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap. + using (SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDCHandle, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0)) { + if (safeDibSectionHandle.IsInvalid) { + // Get Exception before the error is lost + Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds); + exceptionToThrow.Data.Add("hdcDest", safeCompatibleDCHandle.DangerousGetHandle().ToInt32()); + exceptionToThrow.Data.Add("hdcSrc", desktopDCHandle.DangerousGetHandle().ToInt32()); - // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height - IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap. - IntPtr hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0); - - if (hDIBSection == IntPtr.Zero) { - // Get Exception before the error is lost - Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds); - exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32()); - exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32()); - - // clean up - GDI32.DeleteDC(hDCDest); - User32.ReleaseDC(hWndDesktop, hDCDesktop); - - // Throw so people can report the problem - throw exceptionToThrow; - } else { - // select the bitmap object and store the old handle - IntPtr hOldObject = GDI32.SelectObject(hDCDest, hDIBSection); - - // bitblt over (make copy) - GDI32.BitBlt(hDCDest, 0, 0, captureBounds.Width, captureBounds.Height, hDCDesktop, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); - - // restore selection (old handle) - GDI32.SelectObject(hDCDest, hOldObject); - // clean up - GDI32.DeleteDC(hDCDest); - User32.ReleaseDC(hWndDesktop, hDCDesktop); - - // get a .NET image object for it - // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try... - bool success = false; - ExternalException exception = null; - for (int i = 0; i < 3; i++) { - try { - // Collect all screens inside this capture - List screensInsideCapture = new List(); - foreach (Screen screen in Screen.AllScreens) { - if (screen.Bounds.IntersectsWith(captureBounds)) { - screensInsideCapture.Add(screen); + // Throw so people can report the problem + throw exceptionToThrow; + } else { + // select the bitmap object and store the old handle + using (SafeSelectObjectHandle selectObject = safeCompatibleDCHandle.SelectObject(safeDibSectionHandle)) { + // bitblt over (make copy) + GDI32.BitBlt(safeCompatibleDCHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDCHandle, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); } - } - // Check all all screens are of an equal size - bool offscreenContent = false; - using (Region captureRegion = new Region(captureBounds)) { - // Exclude every visible part - foreach (Screen screen in screensInsideCapture) { - captureRegion.Exclude(screen.Bounds); - } - // If the region is not empty, we have "offscreenContent" - using (Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow())) { - offscreenContent = !captureRegion.IsEmpty(screenGraphics); - } - } - // Check if we need to have a transparent background, needed for offscreen content - if (offscreenContent) { - using (Bitmap tmpBitmap = Bitmap.FromHbitmap(hDIBSection)) { - // Create a new bitmap which has a transparent background - returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution); - // Content will be copied here - using (Graphics graphics = Graphics.FromImage(returnBitmap)) { - // For all screens copy the content to the new bitmap + + // get a .NET image object for it + // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try... + bool success = false; + ExternalException exception = null; + for (int i = 0; i < 3; i++) { + try { + // Collect all screens inside this capture + List screensInsideCapture = new List(); foreach (Screen screen in Screen.AllScreens) { - Rectangle screenBounds = screen.Bounds; - // Make sure the bounds are offsetted to the capture bounds - screenBounds.Offset(-captureBounds.X, -captureBounds.Y); - graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel); + if (screen.Bounds.IntersectsWith(captureBounds)) { + screensInsideCapture.Add(screen); + } } + // Check all all screens are of an equal size + bool offscreenContent = false; + using (Region captureRegion = new Region(captureBounds)) { + // Exclude every visible part + foreach (Screen screen in screensInsideCapture) { + captureRegion.Exclude(screen.Bounds); + } + // If the region is not empty, we have "offscreenContent" + using (Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow())) { + offscreenContent = !captureRegion.IsEmpty(screenGraphics); + } + } + // Check if we need to have a transparent background, needed for offscreen content + if (offscreenContent) { + using (Bitmap tmpBitmap = Bitmap.FromHbitmap(safeDibSectionHandle.DangerousGetHandle())) { + // Create a new bitmap which has a transparent background + returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution); + // Content will be copied here + using (Graphics graphics = Graphics.FromImage(returnBitmap)) { + // For all screens copy the content to the new bitmap + foreach (Screen screen in Screen.AllScreens) { + Rectangle screenBounds = screen.Bounds; + // Make sure the bounds are offsetted to the capture bounds + screenBounds.Offset(-captureBounds.X, -captureBounds.Y); + graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel); + } + } + } + } else { + // All screens, which are inside the capture, are of equal size + // assign image to Capture, the image will be disposed there.. + returnBitmap = Bitmap.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()); + } + // We got through the capture without exception + success = true; + break; + } catch (ExternalException ee) { + LOG.Warn("Problem getting bitmap at try " + i + " : ", ee); + exception = ee; } } - } else { - // All screens, which are inside the capture, are of equal size - // assign image to Capture, the image will be disposed there.. - returnBitmap = Bitmap.FromHbitmap(hDIBSection); + if (!success) { + LOG.Error("Still couldn't create Bitmap!"); + throw exception; + } } - // We got through the capture without exception - success = true; - break; - } catch (ExternalException ee) { - LOG.Warn("Problem getting bitmap at try " + i + " : ", ee); - exception = ee; } } - if (!success) { - LOG.Error("Still couldn't create Bitmap!"); - throw exception; - } - // free up the Bitmap object - GDI32.DeleteObject(hDIBSection); - } return returnBitmap; } diff --git a/GreenshotPlugin/Core/WindowsHelper.cs b/GreenshotPlugin/Core/WindowsHelper.cs index bd9c79562..6f90f1a93 100644 --- a/GreenshotPlugin/Core/WindowsHelper.cs +++ b/GreenshotPlugin/Core/WindowsHelper.cs @@ -1224,15 +1224,15 @@ namespace GreenshotPlugin.Core { /// Get the region for a window /// private Region GetRegion() { - IntPtr windowRegionPtr = GDI32.CreateRectRgn(0,0,0,0); - RegionResult result = User32.GetWindowRgn(Handle, windowRegionPtr); - Region returnRegion = null; - if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) { - returnRegion = Region.FromHrgn(windowRegionPtr); + using (SafeRegionHandle region = GDI32.CreateRectRgn(0, 0, 0, 0)) { + if (!region.IsInvalid) { + RegionResult result = User32.GetWindowRgn(Handle, region); + if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) { + return Region.FromHrgn(region.DangerousGetHandle()); + } + } } - // Free the region object - GDI32.DeleteObject(windowRegionPtr); - return returnRegion; + return null; } private bool CanFreezeOrUnfreeze(string titleOrProcessname) { diff --git a/GreenshotPlugin/UnmanagedHelpers/GDI32.cs b/GreenshotPlugin/UnmanagedHelpers/GDI32.cs index 583ea4ff3..af334e20b 100644 --- a/GreenshotPlugin/UnmanagedHelpers/GDI32.cs +++ b/GreenshotPlugin/UnmanagedHelpers/GDI32.cs @@ -26,6 +26,13 @@ using Microsoft.Win32.SafeHandles; namespace GreenshotPlugin.UnmanagedHelpers { public static class GDIExtensions { + /// + /// Check if all the corners of the rectangle are visible in the specified region. + /// Not a perfect check, but this currently a workaround for checking if a window is completely visible + /// + /// + /// + /// public static bool AreRectangleCornersVisisble(this Region region, Rectangle rectangle) { Point topLeft = new Point(rectangle.X, rectangle.Y); Point topRight = new Point(rectangle.X + rectangle.Width, rectangle.Y); @@ -38,103 +45,210 @@ namespace GreenshotPlugin.UnmanagedHelpers { return topLeftVisible && topRightVisible && bottomLeftVisible && bottomRightVisible; } + + /// + /// Get a SafeHandle for the GetHdc, so one can use using to automatically cleanup the devicecontext + /// + /// + /// SafeDeviceContextHandle + public static SafeDeviceContextHandle getSafeDeviceContext(this Graphics graphics) { + return SafeDeviceContextHandle.fromGraphics(graphics); + } } - + + /// + /// Abstract class SafeObjectHandle which contains all handles that are cleaned with DeleteObject + /// + public abstract class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid { + [DllImport("gdi32", SetLastError = true)] + private static extern bool DeleteObject(IntPtr hObject); + + protected SafeObjectHandle(bool ownsHandle) : base(ownsHandle) { + } + + protected override bool ReleaseHandle() { + return DeleteObject(handle); + } + } + /// /// A hbitmap SafeHandle implementation /// - public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid { - [SecurityCritical] - private SafeHBitmapHandle(): base(true) { - } + public class SafeHBitmapHandle : SafeObjectHandle { + [SecurityCritical] + private SafeHBitmapHandle() : base(true) { + } - [SecurityCritical] - public SafeHBitmapHandle(IntPtr preexistingHandle) : base(true) { - SetHandle(preexistingHandle); - } - - protected override bool ReleaseHandle() { - return GDI32.DeleteObject(handle); - } + [SecurityCritical] + public SafeHBitmapHandle(IntPtr preexistingHandle) : base(true) { + SetHandle(preexistingHandle); + } } + /// + /// A hRegion SafeHandle implementation + /// + public class SafeRegionHandle : SafeObjectHandle { + [SecurityCritical] + private SafeRegionHandle() : base(true) { + } + + [SecurityCritical] + public SafeRegionHandle(IntPtr preexistingHandle) : base(true) { + SetHandle(preexistingHandle); + } + } + + /// + /// A dibsection SafeHandle implementation + /// + public class SafeDibSectionHandle : SafeObjectHandle { + [SecurityCritical] + private SafeDibSectionHandle() : base(true) { + } + + [SecurityCritical] + public SafeDibSectionHandle(IntPtr preexistingHandle) : base(true) { + SetHandle(preexistingHandle); + } + } + + /// + /// A select object safehandle implementation + /// This impl will select the passed SafeHandle to the HDC and replace the returned value when disposing + /// + public class SafeSelectObjectHandle : SafeHandleZeroOrMinusOneIsInvalid { + [DllImport("gdi32", SetLastError = true)] + private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); + + private SafeHandle hdc; + + [SecurityCritical] + private SafeSelectObjectHandle() : base(true) { + } + + [SecurityCritical] + public SafeSelectObjectHandle(SafeDCHandle hdc, SafeHandle newHandle) : base(true) { + this.hdc = hdc; + SetHandle(SelectObject(hdc.DangerousGetHandle(), newHandle.DangerousGetHandle())); + } + + protected override bool ReleaseHandle() { + SelectObject(hdc.DangerousGetHandle(), handle); + return true; + } + } + + public abstract class SafeDCHandle : SafeHandleZeroOrMinusOneIsInvalid { + protected SafeDCHandle(bool ownsHandle) : base(ownsHandle) { + } + } + /// + /// A CompatibleDC SafeHandle implementation + /// + public class SafeCompatibleDCHandle : SafeDCHandle { + [DllImport("gdi32", SetLastError = true)] + private static extern bool DeleteDC(IntPtr hDC); + + [SecurityCritical] + private SafeCompatibleDCHandle() : base(true) { + } + + [SecurityCritical] + public SafeCompatibleDCHandle(IntPtr preexistingHandle) : base(true) { + SetHandle(preexistingHandle); + } + + public SafeSelectObjectHandle SelectObject(SafeHandle newHandle) { + return new SafeSelectObjectHandle(this, newHandle); + } + + protected override bool ReleaseHandle() { + return DeleteDC(handle); + } + } + + /// + /// A DeviceContext SafeHandle implementation + /// + public class SafeDeviceContextHandle : SafeDCHandle { + private Graphics graphics = null; + [SecurityCritical] + private SafeDeviceContextHandle() : base(true) { + } + + [SecurityCritical] + public SafeDeviceContextHandle(Graphics graphics, IntPtr preexistingHandle) : base(true) { + this.graphics = graphics; + SetHandle(preexistingHandle); + } + + protected override bool ReleaseHandle() { + graphics.ReleaseHdc(handle); + return true; + } + + public SafeSelectObjectHandle SelectObject(SafeHandle newHandle) { + return new SafeSelectObjectHandle(this, newHandle); + } + + public static SafeDeviceContextHandle fromGraphics(Graphics graphics) { + return new SafeDeviceContextHandle(graphics, graphics.GetHdc()); + } + } + /// /// GDI32 Helpers /// public static class GDI32 { [DllImport("gdi32", SetLastError=true)] - public static extern bool BitBlt(IntPtr hObject,int nXDest,int nYDest, int nWidth,int nHeight,IntPtr hObjectSource, int nXSrc,int nYSrc, CopyPixelOperation dwRop); + public static extern bool BitBlt(SafeHandle hObject, int nXDest, int nYDest, int nWidth, int nHeight, SafeHandle hdcSrc, int nXSrc, int nYSrc, CopyPixelOperation dwRop); [DllImport("gdi32", SetLastError=true)] - public static extern bool StretchBlt(IntPtr hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, CopyPixelOperation dwRop ); + public static extern bool StretchBlt(SafeHandle hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, SafeHandle hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, CopyPixelOperation dwRop); [DllImport("gdi32", SetLastError=true)] - public static extern IntPtr CreateCompatibleDC(IntPtr hDC); + public static extern SafeCompatibleDCHandle CreateCompatibleDC(SafeHandle hDC); [DllImport("gdi32", SetLastError=true)] - public static extern bool DeleteDC(IntPtr hDC); + public static extern IntPtr SelectObject(SafeHandle hDC, SafeHandle hObject); [DllImport("gdi32", SetLastError=true)] - public static extern bool DeleteObject(IntPtr hObject); + public static extern SafeDibSectionHandle CreateDIBSection(SafeHandle hdc, ref BitmapInfoHeader bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset); [DllImport("gdi32", SetLastError=true)] - public static extern IntPtr SelectObject(IntPtr hDC,IntPtr hObject); + public static extern SafeRegionHandle CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); [DllImport("gdi32", SetLastError=true)] - public static extern IntPtr CreateDIBSection(IntPtr hdc, ref BitmapInfoHeader bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset); + public static extern uint GetPixel(SafeHandle hdc, int nXPos, int nYPos); [DllImport("gdi32", SetLastError=true)] - public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); - [DllImport("gdi32", SetLastError=true)] - public static extern int GetClipBox(IntPtr hdc, out RECT lprc); - [DllImport("gdi32", SetLastError = true)] - public static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos); - [DllImport("gdi32", SetLastError = true)] - public static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int cx, int cy); - [DllImport("gdi32", SetLastError = true)] - public static extern int GetDeviceCaps(IntPtr hdc, DeviceCaps nIndex); + public static extern int GetDeviceCaps(SafeHandle hdc, DeviceCaps nIndex); /// + /// StretchBlt extension for the graphics object /// Doesn't work? /// /// /// public static void StretchBlt(this Graphics target, Bitmap sourceBitmap, Rectangle source, Rectangle destination) { - IntPtr hDCSrc = IntPtr.Zero; - IntPtr hDCDest = IntPtr.Zero; - try { - hDCDest = target.GetHdc(); - hDCSrc = CreateCompatibleDC(hDCDest); - using (SafeHBitmapHandle hBitmapHandle = new SafeHBitmapHandle(sourceBitmap.GetHbitmap())) { - IntPtr pOrig = SelectObject(hDCSrc, hBitmapHandle.DangerousGetHandle()); - StretchBlt(hDCDest, destination.X, destination.Y, destination.Width, destination.Height, hDCSrc, source.Left, source.Top, source.Width, source.Height, CopyPixelOperation.SourceCopy); - IntPtr pNew = SelectObject(hDCDest, pOrig); - } - } finally { - if (hDCSrc != IntPtr.Zero) { - DeleteDC(hDCSrc); - } - if (hDCDest != IntPtr.Zero) { - target.ReleaseHdc(hDCDest); + using (SafeDeviceContextHandle targetDC = target.getSafeDeviceContext()) { + using (SafeCompatibleDCHandle safeCompatibleDCHandle = CreateCompatibleDC(targetDC)) { + using (SafeHBitmapHandle hBitmapHandle = new SafeHBitmapHandle(sourceBitmap.GetHbitmap())) { + using (SafeSelectObjectHandle selectObject = safeCompatibleDCHandle.SelectObject(hBitmapHandle)) { + StretchBlt(targetDC, destination.X, destination.Y, destination.Width, destination.Height, safeCompatibleDCHandle, source.Left, source.Top, source.Width, source.Height, CopyPixelOperation.SourceCopy); + } + } } } } /// - /// + /// Bitblt extension for the graphics object /// /// /// public static void BitBlt(this Graphics target, Bitmap sourceBitmap, Rectangle source, Point destination) { - IntPtr hDCSrc = IntPtr.Zero; - IntPtr hDCDest = IntPtr.Zero; - try { - hDCDest = target.GetHdc(); - hDCSrc = CreateCompatibleDC(hDCDest); - using (SafeHBitmapHandle hBitmapHandle = new SafeHBitmapHandle(sourceBitmap.GetHbitmap())) { - IntPtr pOrig = SelectObject(hDCSrc, hBitmapHandle.DangerousGetHandle()); - BitBlt(hDCDest, destination.X, destination.Y, source.Width, source.Height, hDCSrc, source.Left, source.Top, CopyPixelOperation.SourceCopy); - IntPtr pNew = SelectObject(hDCDest, pOrig); - } - } finally { - if (hDCSrc != IntPtr.Zero) { - DeleteDC(hDCSrc); - } - if (hDCDest != IntPtr.Zero) { - target.ReleaseHdc(hDCDest); + using (SafeDeviceContextHandle targetDC = target.getSafeDeviceContext()) { + using (SafeCompatibleDCHandle safeCompatibleDCHandle = CreateCompatibleDC(targetDC)) { + using (SafeHBitmapHandle hBitmapHandle = new SafeHBitmapHandle(sourceBitmap.GetHbitmap())) { + using (SafeSelectObjectHandle selectObject = safeCompatibleDCHandle.SelectObject(hBitmapHandle)) { + BitBlt(safeCompatibleDCHandle, destination.X, destination.Y, source.Width, source.Height, safeCompatibleDCHandle, source.Left, source.Top, CopyPixelOperation.SourceCopy); + } + } } } } diff --git a/GreenshotPlugin/UnmanagedHelpers/User32.cs b/GreenshotPlugin/UnmanagedHelpers/User32.cs index 729aff5fe..3628727e3 100644 --- a/GreenshotPlugin/UnmanagedHelpers/User32.cs +++ b/GreenshotPlugin/UnmanagedHelpers/User32.cs @@ -26,6 +26,7 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; +using System.Security; namespace GreenshotPlugin.UnmanagedHelpers { /// @@ -36,38 +37,10 @@ namespace GreenshotPlugin.UnmanagedHelpers { /// public delegate int EnumWindowsProc(IntPtr hwnd, int lParam); - /// - /// Used with SetWinEventHook - /// - /// - /// - /// - /// - /// - /// - /// - public delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvent eventType, IntPtr hwnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime); - - /// - /// A SafeHandle class implementation for the hIcon - /// - public class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid { - private SafeIconHandle(): base(true) { - } - - public SafeIconHandle(IntPtr hIcon) : base(true) { - this.SetHandle(hIcon); - } - - protected override bool ReleaseHandle() { - return User32.DestroyIcon(this.handle); - } - } - /// /// User32 Wrappers /// - public class User32 { + public static class User32 { public const int SC_RESTORE = 0xF120; public const int SC_CLOSE = 0xF060; public const int SC_MAXIMIZE = 0xF030; @@ -166,11 +139,7 @@ namespace GreenshotPlugin.UnmanagedHelpers { [DllImport("user32", SetLastError=true, EntryPoint = "PostMessageA")] public static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam); [DllImport("user32", SetLastError = true)] - public static extern RegionResult GetWindowRgn(IntPtr hWnd, IntPtr hRgn); - [DllImport("user32", SetLastError = true)] - public static extern IntPtr GetWindowDC(IntPtr hWnd); - [DllImport("user32", SetLastError = true)] - public static extern IntPtr ReleaseDC(IntPtr hWnd,IntPtr hDC); + public static extern RegionResult GetWindowRgn(IntPtr hWnd, SafeHandle hRgn); [DllImport("user32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, WindowPos uFlags); @@ -236,8 +205,6 @@ namespace GreenshotPlugin.UnmanagedHelpers { [DllImport("user32", SetLastError = true)] public static extern bool GetIconInfo(SafeIconHandle iconHandle, out IconInfo iconInfo); [DllImport("user32", SetLastError = true)] - public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon); - [DllImport("user32", SetLastError = true)] public static extern IntPtr SetCapture(IntPtr hWnd); [DllImport("user32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] @@ -306,4 +273,64 @@ namespace GreenshotPlugin.UnmanagedHelpers { return exceptionToThrow; } } + + /// + /// Used with SetWinEventHook + /// + /// + /// + /// + /// + /// + /// + /// + public delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvent eventType, IntPtr hwnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime); + + /// + /// A SafeHandle class implementation for the hIcon + /// + public class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid { + private SafeIconHandle() : base(true) { + } + + public SafeIconHandle(IntPtr hIcon) : base(true) { + this.SetHandle(hIcon); + } + + protected override bool ReleaseHandle() { + return User32.DestroyIcon(this.handle); + } + } + + /// + /// A WindowDC SafeHandle implementation + /// + public class SafeWindowDCHandle : SafeHandleZeroOrMinusOneIsInvalid { + [DllImport("user32", SetLastError = true)] + private static extern IntPtr GetWindowDC(IntPtr hWnd); + [DllImport("user32", SetLastError = true)] + private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + + private IntPtr hWnd; + [SecurityCritical] + private SafeWindowDCHandle() : base(true) { + } + + [SecurityCritical] + public SafeWindowDCHandle(IntPtr hWnd, IntPtr preexistingHandle) : base(true) { + this.hWnd = hWnd; + SetHandle(preexistingHandle); + } + + protected override bool ReleaseHandle() { + bool returnValue = ReleaseDC(hWnd, handle); + return returnValue; + } + + public static SafeWindowDCHandle fromDesktop() { + IntPtr hWndDesktop = User32.GetDesktopWindow(); + IntPtr hDCDesktop = GetWindowDC(hWndDesktop); + return new SafeWindowDCHandle(hWndDesktop, hDCDesktop); + } + } }