我如何处理form1中的位图以及timer1设置为的间隔速度是多少?

时间:2016-04-20 05:51:49

标签: c# .net winforms

在新课程中,我有这种方法:

public Bitmap CaptureWindowToMemory(IntPtr handle)
{
    Image img = CaptureWindow(handle);
    Bitmap bmp = new Bitmap(img);
    bmp.Save("foo.png", System.Drawing.Imaging.ImageFormat.Png);
    return bmp;
}

如果我在几分钟后不会处理bmp,我将在bmp实例行中出现内存异常。

在form1中

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;


namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {

        IntPtr windowHandle;
        ScreenCapture sc;

        public Form1()
        {
            InitializeComponent();

            Process[] processes = Process.GetProcessesByName("GameCapture");

            foreach (Process p in processes)
            {
                windowHandle = p.MainWindowHandle;
            }

            this.pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
            sc = new ScreenCapture();


        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Enabled = true;

        }

        int countimages = 0;
        private void timer1_Tick(object sender, EventArgs e)
        {
            this.pictureBox1.Image = sc.CaptureWindowToMemory(windowHandle);
            countimages += 1;
        }
    }
}

这是来自新类的CaptureWindow方法:

public Image CaptureWindow(IntPtr handle)
        {
            // get te hDC of the target window
            IntPtr hdcSrc = User32.GetWindowDC(handle);
            // get the size
            User32.RECT windowRect = new User32.RECT();
            User32.GetWindowRect(handle, ref windowRect);
            int width = windowRect.right - windowRect.left;
            int height = windowRect.bottom - windowRect.top;
            // create a device context we can copy to
            IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
            // create a bitmap we can copy it to,
            // using GetDeviceCaps to get the width/height
            IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
            // select the bitmap object
            IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
            // bitblt over
            GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
            // restore selection
            GDI32.SelectObject(hdcDest, hOld);
            // clean up 
            GDI32.DeleteDC(hdcDest);
            User32.ReleaseDC(handle, hdcSrc);
            // get a .NET image object for it
            Image img = Image.FromHbitmap(hBitmap);
            // free up the Bitmap object
            GDI32.DeleteObject(hBitmap);
            return img;
        }

如果我将在类中处理位图,那么我将在行

上获得异常无效参数
this.pictureBox1.Image = sc.CaptureWindowToMemory(windowHandle);

那我应该怎么处理呢?

第二个问题是设计器中的定时器间隔设置为1ms。 而且在运行程序时,我看到pictureBox1中的图像就像电影一样,但我认为它还不够流畅。在实际移动中,帧速率是每秒25个图像(帧)?我应该如何使用计时器在我的程序中执行此操作?

2 个答案:

答案 0 :(得分:0)

将代码更改为正确处置的已使用位图的最简单方法是更改​​timer1_Tick方法,如下所示:

private void timer1_Tick(object sender, EventArgs e)
{
    sc.CaptureWindowToMemory(windowHandle, (bitmap) =>
    {
        pictureBox1.Image = bitmap;

        countimages += 1;
    });
}

ScreenCapture以这种方式上课:

private Bitmap currentBitmap;
public void CaptureWindowToMemory(IntPtr handle, Action<Bitmap> action)
{
    using (var img = CaptureWindow(handle))
    {
        var newBitmap = new Bitmap(img);
        newBitmap.Save("foo.png", System.Drawing.Imaging.ImageFormat.Png);

        action(newBitmap);

        if (currentBitmap != null)
            currentBitmap.Dispose();

        currentBitmap = newBitmap;
    }
}

或者您可以尝试通过中间位置提供照片(不知道磁盘是否是最佳选择)。与保存到HDD的第一个相比,这种方法需要大约两到三倍的内存。

private void timer1_Tick(object sender, EventArgs e)
{
    var path = sc.CaptureWindowToMemory(windowHandle);
    pictureBox1.ImageLocation = path;

    countimages += 1;
}

public string CaptureWindowToMemory(IntPtr handle)
{
    var name = "foo.png";
    using (var img = CaptureWindow(handle))
    using (var currentBitmap = new Bitmap(img))
        currentBitmap.Save(name, System.Drawing.Imaging.ImageFormat.Png);

    return name;
}

更新:感谢this comment更新了代码段。

答案 1 :(得分:-1)

  

如果几分钟后我不会处理bmp,我就会离开   bmp实例行上的内存异常。

我试图重新创建你的情况,但我无法重复。

你没有给出CaptureWindow(句柄),所以我在你的项目中使用了这个功能。

    [StructLayout(LayoutKind.Sequential)]
    private struct Rect
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

    public static Bitmap CaptureWindow(IntPtr handle)
    {

        var rect = new Rect();
        GetWindowRect(handle, ref rect);
        var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);

        if (bounds.Width == 0 && bounds.Height == 0)
        {
            bounds.Width = 1;
            bounds.Height = 1;
        }

        var result = new Bitmap(bounds.Width, bounds.Height);

        if(bounds.Width != 1 )
            using (var graphics = Graphics.FromImage(result))
            {
                graphics.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
            }

        return result;
    }

在更新PictureBox之前处理Bitmap的方法,你可以在你的计时器中使用它

        var bmp = sc.CaptureWindowToMemory(windowHandle);
        //Backup old image in pictureBox
        var oldImage = pictureBox1.Image;
        pictureBox1.Image = bmp;
        //Release resources from old image
        if (oldImage != null)
            ((IDisposable)oldImage).Dispose(); 
  

timer1设置为什么间隔速度?

我认为你不需要使用计时器进行更新。更优化将在单独的线程中使用主循环。例如

    int countimages = 0;
    Task tasks;
    bool StartUpdateTask = false;
    private  void UpdateTask()
    {
        if (StartUpdateTask)
        {
            tasks = Task.Run(() =>
            {
               // Action to update paint. 
                 ...
                if(countimages%10 == 0){
                  // Action to save paint
                  ...
                } 
                System.Threading.Interlocked.Increment(ref countimages);
                UpdateTask();
            });
        }
    } 


 private void button_RunTest_Click(object sender, EventArgs e)
 {
        if (!StartUpdateTask &&( tasks == null || tasks.IsCompleted)) { 
           StartUpdateTask = true;
           UpdateTask();                
        }else
           StartUpdateTask = false;

        button_RunTest.BackColor = (StartUpdateTask) ? Color.Green : this.BackColor;              
    }