Почему элемент управления PictureBox.Invalidate() вызывает сбой программы? Система.AccessViolationException

#c# #.net #paint #picturebox #emgucv

#c# #.net #Краски #picturebox #emgucv

Вопрос:

итак, я написал приложение Windows Form на C #, чтобы открывать видео, отображать видео в pictureBox_display и позволять пользователям определять (рисовать) ограничивающие рамки на picturebox. Единственное, что я могу очень предсказуемо заставить программу сбросить Систему.AccessViolationException и сбой, просто: 1) открываем видео (любое видео) 2) щелкаем перетаскиваем мышь на picturebox в течение нескольких секунд.

Вот некоторый код:

 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 amp;amp; checkBox1.Checked == false amp;amp; 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 amp;amp; ThereIsNewSelection) {
                e.Graphics.DrawRectangle(Pens.Red, GetRectangle());
        }
}
 

Я знаю, что это определенно связано с Invalidate(), поскольку я комментировал код построчно, и он вылетает только при наличии Invalidate() . Кто-нибудь может сказать мне, что может быть причиной проблемы?

Вот трассировка стека, если она полезна:

в системе.Рисование.SafeNativeMethods.Gdip.GdipDrawImageRectI(HandleRef графика, HandleRef изображение, Int32 x, Int32 y, ширина Int32, высота Int32) в системе.Рисование.Графика.drawImage(изображение изображения, Int32 x, Int32 y, ширина Int32, высота Int32) в системе.Рисование.Графика.drawImage (Изображение изображения, прямоугольник прямоугольника) в системе.Windows.Формы.PictureBox.OnPaint(PaintEventArgs pe) в системе.Windows.Формы.Управление.PaintWithErrorHandling(PaintEventArgs e, слой Int16) в системе.Windows.Формы.Управление.WmPaint(сообщение и m)
в системе.Windows.Формы.Управление.WndProc(Messageamp; m) в системе.Windows.Формы.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Комментарии:

1. вы устанавливаете pictureBox_display.Image = tempimg.Bitmap; , а также рассылаете спам WinForms при pictureBox_display.Invalidate(); каждом движении мыши, поэтому я предполагаю, что tempimg.Bitmap это было удалено в середине операции GDI GdipDrawImageRectI (или, может быть, прямо перед этим), так что у вас есть AccessViolationException .

2. Привет @vasily.sib, спасибо за ваш ответ! Я рассматривал возможность этого, но я не вижу никакого способа сохранить контроль над tempimg. Растровое изображение. Как бы вы порекомендовали мне решить проблему? В любом случае мне удалось найти исправление после того, как вы подтвердили мои подозрения. Для тех, кому интересно, я в основном добавил еще одну глобальную переменную Mat CurrentFrameMat и в функции OpenFileButton_Click(...) присвоил Frame 0 CurrentFrameMat и выполнил a pictureBox_display.Image = CurrentFrameMat.Bitmap непосредственно перед Invalidate() в функции MouseMove . Большое спасибо!

3. Вам просто нужно переместить tempimg переменную из метода, чтобы она стала полем класса form. Таким образом, сборщик мусора всегда видит ссылку на него и предотвращает сборку мусора.

Ответ №1:

Вам необходимо клонировать (глубоко копировать) растровое изображение, например, следующее:

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

Комментарии:

1. Что не так с этим ответом? Если вместо pictureBox_display.Image = tempimg.Bitmap; этого вы будете использовать «клонированную» копию tempimg.Bitmap и назначить ее графическому блоку, сбой не произойдет.