新手:为什么我的自制屏幕录音机会崩溃?(c#和windows窗体)

新手:为什么我的自制屏幕录音机会崩溃?(c#和windows窗体),c#,video,screenshot,C#,Video,Screenshot,[已解决]奥菲尔的解决方案开箱即用。目前在24华氏度下运行五分钟 我正试图从屏幕上保存图像 基本上,我的问题是,当我减少Thread.sleep(创建一个合适的帧速率)时,程序崩溃。程序崩溃的速度越快,thread.sleep接近0,但我甚至找不到问题所在,因为我从未处理过此类问题(如果是unity3d,我会处理这一切) 我也有一些甜蜜的错误向你走来 System.Drawing.dll中发生类型为“System.ComponentModel.Win32Exception”的未处理异常 错误代码

[已解决]奥菲尔的解决方案开箱即用。目前在24华氏度下运行五分钟

我正试图从屏幕上保存图像

基本上,我的问题是,当我减少Thread.sleep(创建一个合适的帧速率)时,程序崩溃。程序崩溃的速度越快,thread.sleep接近0,但我甚至找不到问题所在,因为我从未处理过此类问题(如果是unity3d,我会处理这一切)

我也有一些甜蜜的错误向你走来

System.Drawing.dll中发生类型为“System.ComponentModel.Win32Exception”的未处理异常

错误代码:-2147467259

我建立了一个try-catch(我对它知之甚少),但在几次测试后它也断了,让我看看我是否能得到那个日志

    System.ComponentModel.Win32Exception(0x80004005): The operation completed successfully as System.Drawing.Graphics.CopyFromScreen(Int32 sourceX, Int32 sourceY, Int32 destinationX,Int32 destinationY, Size blockRegionSize)
 at Screen_Recorder.Form1.ScreenCapture() in C:Users\Jupiter\Desktop\visual studio experiments\Screen Recorder\ScreenRecorder\Form1.cs:line 35
如果单击“确定”,则会显示:

System.ArgumentException: Parameter is not valid.
 at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format)
 at System.Drawing.Bitmap..ctor(int32 width, Int32 height)
 at Screen_Recorder.Form1.ScreenCapture() C:Users\Jupiter\Desktop\visual studio experiments\Screen Recorder\ScreenRecorder\Form1.cs:line 32
然后它会无限重复这个错误


不管怎样,我要去睡觉了,明天再去睡觉,但在此之前的任何建议都将不胜感激

内存泄漏

图形
位图
是一次性对象,使用完后应将其丢弃。
您只能使用块将本例中的
图形
放入
块中,最后一个位图应在循环的每次迭代中处理。
因此,您的代码应该如下所示:

private void ScreenCapture()
{
    while (true)
    {
        var bm = new Bitmap((int)Math.Round(Screen.PrimaryScreen.Bounds.Width * 1.5), 
                           (int)Math.Round(Screen.PrimaryScreen.Bounds.Height * 1.5));
        using (Graphics g = Graphics.FromImage(bm)) g.CopyFromScreen(0, 0, 0, 0, bm.Size);

        // As per the comment by HansPassant - the following would cause
        // a thread race with the UI thread.
        //this.pictureBox1.Image?.Dispose();
        //this.pictureBox1.Image = bm;

        // Instead we use beginInvoke to run this on the UI thread
        Action action = () =>
            {
                this.pictureBox1.Image?.Dispose();
                this.pictureBox1.Image = bm;
            };

        this.BeginInvoke(action);

        Thread.Sleep(250);
    }
}

这个循环永远不会结束(因为<代码>(true)”,也许您应该考虑添加<代码>取消标记->代码>,使用<代码>任务<代码>,而不是<代码>线程< /代码>。


还有最后一件事,完成后也应该处理picturebox本身。

这是一个原因,一个非常常见的原因,但不是唯一的原因。Image属性不是线程安全的,在UI线程绘制图像的同时调用Dispose()将不会有好的结果。失败的几率要低得多,但不是零,因此调试起来要困难得多。@HansPassant我不确定我是否完全理解您的意思,但是如果我们保存对映像的引用,设置新映像,然后处理旧映像呢?这能解决问题吗?不,这仍然是一场线程竞赛,因为Paint()方法可能正在运行中,并且正在使用旧的引用。冷硬的事实是,您必须使用BeginInvoke()以便代码在UI线程上运行。或者调用该部分?必须在UI线程上运行映像属性分配和Dispose调用。另外一个问题是firehose问题,当工作线程创建位图的速度快于UI线程显示位图的速度时,会发生此问题。但是这很容易识别,OP会看到UI线程没有响应。
private void ScreenCapture()
{
    while (true)
    {
        var bm = new Bitmap((int)Math.Round(Screen.PrimaryScreen.Bounds.Width * 1.5), 
                           (int)Math.Round(Screen.PrimaryScreen.Bounds.Height * 1.5));
        using (Graphics g = Graphics.FromImage(bm)) g.CopyFromScreen(0, 0, 0, 0, bm.Size);

        // As per the comment by HansPassant - the following would cause
        // a thread race with the UI thread.
        //this.pictureBox1.Image?.Dispose();
        //this.pictureBox1.Image = bm;

        // Instead we use beginInvoke to run this on the UI thread
        Action action = () =>
            {
                this.pictureBox1.Image?.Dispose();
                this.pictureBox1.Image = bm;
            };

        this.BeginInvoke(action);

        Thread.Sleep(250);
    }
}