【问题标题】:Why does PictureBox.Invalidate() cause program to crash? System.AccessViolationException为什么 PictureBox.Invalidate() 会导致程序崩溃? System.AccessViolationException
【发布时间】:2019-09-08 08:29:20
【问题描述】:

所以我用 C# 编写了一个 Windows 窗体应用程序来打开视频,在 pictureBox_display 中显示视频,并允许用户在图片框上定义(绘制)边界框。唯一的事情是,我可以非常可预测地导致程序抛出 System.AccessViolationException 并通过简单地崩溃: 1)打开视频(任何视频) 2)在图片框上单击并拖动鼠标几秒钟。

这里有一些代码:

VideoCapture VC;
bool IsMouseDown = false;
bool MouseMoved = false;
string VideoFileName;


private void OpenFileButton_Click(object sender, EventArgs e) {
        OpenFileDialog OFD = new OpenFileDialog();
        if (OFD.ShowDialog() == DialogResult.OK) {
                FrameNumber = 0;
                VideoFileName = OFD.FileName;
                VC = new VideoCapture(VideoFileName);

                if (VC.Width<=0 || VC.Height<=0) {
                    System.Windows.Forms.MessageBox.Show("ERROR: Please load a valid video file");
                    return;
                }


        // Initialize picturebox dimensions according to image
        double W_H_ratio = Convert.ToDouble(VC.Width) / VC.Height;
                if (W_H_ratio >= (double)(5 / 4)) {
                    pictureBox_display.Width = Math.Min(VC.Width, 800);
                    pictureBox_display.Height = Math.Min(VC.Height, (int)((1 / W_H_ratio )* 800));
                }
                else {
                    pictureBox_display.Height = Math.Min(VC.Height, 640);
                    pictureBox_display.Width = Math.Min(VC.Width, (int)(W_H_ratio * 640));
                }

                Mat tempimg = GetMat(FrameNumber);
                Console.WriteLine("Size of tempimg: " + tempimg.Size);
                pictureBox_display.Image = tempimg.Bitmap;
        }
}

private Mat GetMat(int someFrameNumber) {

        try {
                Mat m = new Mat();
                VC.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.PosFrames, someFrameNumber);
                VC.Read(m);

                Mat resized = new Mat();
                CvInvoke.ResizeForFrame(m, resized, pictureBox_display.Size);
                m.Dispose();
                return resized;
        }

        catch {
                Mat m = new Mat();
                System.Windows.Forms.MessageBox.Show("ERROR: Cannot read video frame");
                return m;
        }
}

private void pictureBox_display_MouseDown(object sender, MouseEventArgs e) {
        if (VideoFileName != null && checkBox1.Checked == false && VideoPlaying == false) {
                IsMouseDown = true;
                MouseMoved = false;
                ThereIsNewSelection = true;
                StartLocation = e.Location;
                Console.WriteLine(StartLocation);
        }
}


// Constantly alter the end location of the mouse to provide the coordinates for the rectangle
private void pictureBox_display_MouseMove(object sender, MouseEventArgs e) {
        if (IsMouseDown == true) {
                MouseMoved = true;

                EndLocation = e.Location;
                pictureBox_display.Invalidate();
        }
}

private void pictureBox_display_Paint(object sender, PaintEventArgs e) {
        if (!VideoPlaying && ThereIsNewSelection) {
                e.Graphics.DrawRectangle(Pens.Red, GetRectangle());
        }
}

我知道这肯定与 Invalidate() 有关,因为我已逐行注释代码,并且只有在存在 Invalidate() 时才会崩溃。谁能告诉我可能导致问题的原因?

如果有用的话,这里是堆栈跟踪:

在 System.Drawing.SafeNativeMethods.Gdip.GdipDrawImageRectI(HandleRef 图形,HandleRef 图像,Int32 x,Int32 y,Int32 宽度,Int32 高度)在 System.Drawing.Graphics.DrawImage(图像图像,Int32 x, Int32 y,Int32 宽度,Int32 高度)在 System.Drawing.Graphics.DrawImage(图像图像,矩形矩形)在 System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe) 在 System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 层)在 System.Windows.Forms.Control.WmPaint(Message& m)
在 System.Windows.Forms.Control.WndProc(Message& m) 在 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

【问题讨论】:

  • 您设置了pictureBox_display.Image = tempimg.Bitmap;,并且在每次鼠标移动时都会使用pictureBox_display.Invalidate(); 向WinForms 发送垃圾邮件,所以我的猜测是,tempimg.Bitmap 被设置在 GDI+ GdipDrawImageRectI 操作的中间(或者可能之前),所以你有一个AccessViolationException
  • 嗨@vasily.sib,感谢您的回复!我确实考虑过发生这种情况的可能性,但我看不出有任何方法可以保持对 tempimg.Bitmap 的控制。你会建议我如何解决这个问题?无论如何,在你证实了我的怀疑之后,我确实设法找到了解决办法。对于那些想知道的人,我基本上添加了另一个全局变量Mat CurrentFrameMat 和函数OpenFileButton_Click(...),将第0 帧分配给CurrentFrameMat,并在MouseMove 函数的Invalidate() 之前执行了pictureBox_display.Image = CurrentFrameMat.Bitmap。非常感谢!
  • 您只需将tempimg 变量移出方法,使其成为表单类的字段。这样垃圾收集器总是能看到对它的引用并防止它被垃圾收集。

标签: c# .net paint picturebox emgucv


【解决方案1】:

您需要克隆(深拷贝)位图,如下所示:

B.Clone(new Rectangle(0, 0, B.Width, B.Height), B.PixelFormat)

【讨论】:

  • 这个答案有什么问题?如果您使用tempimg.Bitmap 的“克隆”副本而不是pictureBox_display.Image = tempimg.Bitmap; 并将其分配给图片框,则不会发生崩溃。
猜你喜欢
  • 1970-01-01
  • 2014-10-05
  • 2021-06-28
  • 1970-01-01
  • 2021-12-27
  • 2020-05-26
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
相关资源
最近更新 更多