Capture screenshot Including Semitransparent windows in .NET
Forms that have the TransparencyKey or Opacity property set are so-called layered windows. They are shown using the "overlay" feature of the video adapter. Which make them being able to have their transparency effects.
Capturing them requires turning on the CopyPixelOperation.CaptureBlt option in the CopyFromScreen overload that accepts the CopyPixelOperation argument.
Unfortunately, this overload has a critical bug that prevents this from working. It doesn't validate the value properly. Still not fixed in .NET 4.0. There is no other good fix but fall back to using P/Invoke to make the screen shot. Here's an example:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
Size sz = Screen.PrimaryScreen.Bounds.Size;
IntPtr hDesk = GetDesktopWindow();
IntPtr hSrce = GetWindowDC(hDesk);
IntPtr hDest = CreateCompatibleDC(hSrce);
IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
IntPtr hOldBmp = SelectObject(hDest, hBmp);
bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
Bitmap bmp = Bitmap.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
bmp.Save(@"c:\temp\test.png");
bmp.Dispose();
}
// P/Invoke declarations
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
}
}
Fwiw, a later Windows version provided a workaround for this bug. Not exactly sure which, I think it was Win7 SP1. The BitBlt() function will now do what you want if you pass only the CopyPixelOperation.CaptureBlt option. But of course that workaround wasn't applied retro-actively to earlier Windows versions so you can't really depend on it.
Capture screenshot that includes BaloonTip
As Hans Passant mentions in a commet to the question, using CopyPixelOperation.CaptureBlt
is the key to the solution.
As it did not work with the solutions I already tried I found a similar question Capture screenshot Including Semitransparent windows in .NET, that deals with semi-transparent windows.
All-in-all the solutions that enables me to take a screenshot that includes the balloon tip from a notification icon looks like this:
class ScreenCapture
{
public void CaptureScreenToFile(string fileName)
{
Size sz = Screen.PrimaryScreen.Bounds.Size;
IntPtr hDesk = GetDesktopWindow();
IntPtr hSrce = GetWindowDC(hDesk);
IntPtr hDest = CreateCompatibleDC(hSrce);
IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
IntPtr hOldBmp = SelectObject(hDest, hBmp);
bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
Bitmap bmp = Bitmap.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
bmp.Save(fileName);
bmp.Dispose();
}
// P/Invoke declarations
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
}
Taking a PNG screenshot of a window, but keeping the aero transparency
I think you meant this link. It describes how to revert alpha blending to get the "true" alpha values back.
Basically you have to put a white rectangle below the window, take a screenshot and do the same with a black rectangle. Then you can regain the alpha channel of the window, as described in the blog post linked above.
EDIT: Sorry, now i posted the corrected link.
Is CopyFromScreen a right way to get screenshots?
- Yes, it is right way
- There are a few possibilities. I recommend sending it via email or FTP (as it's simple in c#).
- No, personally I don't think you need additional compression. Your screenshots are already saved as JPEG, so they are already compressed.
Code snippet for sending email with attachment:
MailMessage mail = new MailMessage();
SmtpClient SmtpServer = new SmtpClient(YOUR SMTP SERVER ADDRESS);
mail.From = new MailAddress(SENDER ADDRESS);
mail.To.Add(RECEIVER ADDRESS);
mail.Subject = "Test Mail - 1";
mail.Body = "mail with attachment";
System.Net.Mail.Attachment attachment;
attachment = new System.Net.Mail.Attachment("YOURFILE");
mail.Attachments.Add(attachment);
SmtpServer.Port = 587;
SmtpServer.Credentials = new System.Net.NetworkCredential(YOUR_SMTP_USER_NAME, YOUR_SMTP_PASSWORD);
SmtpServer.EnableSsl = true;
SmtpServer.Send(mail);
(based on this blog)
Code snippet for FTP:
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://www.contoso.com/test.htm");
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential ("anonymous","janeDoe@contoso.com");
StreamReader sourceStream = new StreamReader("testfile.txt");
byte [] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
requestStream.Close();
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
response.Close();
(based on MSDN)
Related Topics
Splitting a String/Number Every Nth Character/Number
Generate a PDF That Automatically Prints
Why Use Simple Properties Instead of Fields in C#
How to Resize Multidimensional (2D) Array in C#
Why Can't We Change Access Modifier While Overriding Methods in C#
Passing a Value from One Form to Another Form
How to Delete Cookies from Windows.Form
Strip the Byte Order Mark from String in C#
ASP.NET Core MVC:How to Get Raw JSON Bound to a String Without a Type
Service Hangs Up at Waitforexit After Calling Batch File
How to Kill a Thread Instantly in C#
Reading Excel File Using Oledb Data Provider
How to Stop Backgroundworker Correctly
Find Recursive Group Membership (Active Directory) Using C#