Refactored code to use SafeHandle where possible, this should fix potential resource leaks and make the code more clear.

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2429 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2013-01-14 13:45:41 +00:00
commit 201ee7082e
7 changed files with 365 additions and 233 deletions

View file

@ -493,7 +493,7 @@ EndSelection:<<<<<<<4
/// </summary>
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) {

View file

@ -428,6 +428,14 @@ namespace GreenshotPlugin.Core {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WindowCapture));
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
/// <summary>
/// Used to cleanup the unmanged resource in the iconInfo for the CaptureCursor method
/// </summary>
/// <param name="hObject"></param>
/// <returns></returns>
[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<Screen> screensInsideCapture = new List<Screen>();
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<Screen> screensInsideCapture = new List<Screen>();
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;
}

View file

@ -1224,15 +1224,15 @@ namespace GreenshotPlugin.Core {
/// Get the region for a window
/// </summary>
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) {