HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Capturing and taking a screenshot of a window in a loop

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
capturingloopscreenshotandwindowtaking

Problem

I am capturing a window of my own choice, and taking a screenshot of it constantly in a loop. Latency and speed is everything.

It's very capable, but I hope it can be improved.

var proc = Process.GetProcessesByName(prcname)[0];
IntPtr hwnd = proc.MainWindowHandle;
RECT rc;  
NativeMethods.GetWindowRect(hwnd, out rc);
Bitmap bmp = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
NativeMethods.PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
MemoryStream ms = new MemoryStream();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);

return new MemoryStream(Lz4.CompressBytes(ms.GetBuffer()));


I will set a name prcname which will be the name of a process, for example, Internet Explorer. It will then hook on to that and save it as a bitmap and then .bmp (or other).

I then return the memory stream , and then I can do what I want with it (lz4 is used here to compress, which is very fast and lossless so it works pretty well).

Here are the Imports used:

[DllImport("user32.dll")]
internal static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
internal static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);


Then there is the RECT class, which is enormous, and I did not write it. I took it and reused this code for my own purposes, but RECT has been untouched, as I don't feel safe messing around with it.

Now, is there a way to improve the speed? Preferably I would like to improve the latency, meaning the frame it lags behind.

I am willing to try something else if user32 is not enough for this, so I'm open for suggestions and examples on that.

Can this code be improved?

```
NetSerializer.Serializer.Serialize(tcpcap.GetStream(), PrintWindow(process));
PrintWindow(process).Dispose();

panel1.BackgroundImage = Image.FromStream(new Lz4DecompressionStream

Solution

Well, yes, you're holding on to a bunch of IDisposable objects which should be dealt with by employing the using statement:

IntPtr hwnd;

using (var proc = Process.GetProcessesByName(prcname)[0])
{
    hwnd = proc.MainWindowHandle;
}

RECT rc;
NativeMethods.GetWindowRect(hwnd, out rc);
using (Bitmap bmp = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb))
{
    using (Graphics gfxBmp = Graphics.FromImage(bmp))
    {
        IntPtr hdcBitmap = gfxBmp.GetHdc();
        try
        {
            NativeMethods.PrintWindow(hwnd, hdcBitmap, 0);
        }
        finally
        {
            gfxBmp.ReleaseHdc(hdcBitmap);
        }
    }

    using (MemoryStream ms = new MemoryStream())
    {
        bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
        return new MemoryStream(Lz4.CompressBytes(ms.GetBuffer()));
    }
}


Plus, the MemoryStream you return should be Dispose()'d be the calling code.

Code Snippets

IntPtr hwnd;

using (var proc = Process.GetProcessesByName(prcname)[0])
{
    hwnd = proc.MainWindowHandle;
}

RECT rc;
NativeMethods.GetWindowRect(hwnd, out rc);
using (Bitmap bmp = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb))
{
    using (Graphics gfxBmp = Graphics.FromImage(bmp))
    {
        IntPtr hdcBitmap = gfxBmp.GetHdc();
        try
        {
            NativeMethods.PrintWindow(hwnd, hdcBitmap, 0);
        }
        finally
        {
            gfxBmp.ReleaseHdc(hdcBitmap);
        }
    }

    using (MemoryStream ms = new MemoryStream())
    {
        bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
        return new MemoryStream(Lz4.CompressBytes(ms.GetBuffer()));
    }
}

Context

StackExchange Code Review Q#29364, answer score: 5

Revisions (0)

No revisions yet.