在新课程中,我有这种方法:
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个图像(帧)?我应该如何使用计时器在我的程序中执行此操作?
答案 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;
}