Aus Guy

Aus Guy

  • NA
  • 1
  • 3.9k

MultiThreading PictureBox- Loses image after time?

May 12 2013 2:18 PM
First off let me state that I put this in multi-threading because it is indeed a threaded process that's producing this problem.

Alright, I have a basic StretchBlt function, running in a separate thread, that's ripping a screen image to Bitmap() and throwing it in a PictureBox.  (Delegated, of course)

This is working fine at 60+fps for anywhere from about 2-5 minutes at which point the PictureBox 'loses' its image (white square with red X through it).  Nothing changes internally, it just seems after several hundred updates, the PictureBox simply gives up.


I'm wondering if I'm running into an overflow problem with the Control or if there's some bizarre way I'm running 'out of sync' with my thread vs GUI thread?


I'll pastebin a shortened sample below.. Threading a function to StretchBlt and inlining the delegate to update the PictureBox.

(Sorry in advance for any foul coding practices, this is merely my proof of concept code, it'll be rewritten and cleaned later.)

        private void manualScreenCap(Stopwatch sw)
        {
            GetWindowRect(wndLiveHWND, ref gRect);


            IntPtr bufferHdc;


            Bitmap _bmpBuffer = new Bitmap(400, 300);
            Graphics g = Graphics.FromImage(_bmpBuffer);


            IntPtr tgtHdc = GDI32.GetWindowDC(wndLiveHWND);






            int tgtWidth = gRect.Right - gRect.Left;
            int tgtHeight = gRect.Bottom - gRect.Top;


            // centering and sizing our maximum bounding box based on a 4:3 ratio
            Size aspectedSize = new Size(4, 3);
            Size tgtFormSize = new Size(tgtWidth, tgtHeight);
            Size maxFitSize = CalculateResizeToFit(aspectedSize, tgtFormSize);
            int leftOffset, topOffset;
            leftOffset = (tgtWidth - maxFitSize.Width) / 2;
            topOffset = (tgtHeight - maxFitSize.Height) / 2;


            // again for selection window, but only if it's currently active
            if (_f_SelectionIndicator)
            {
                tgtWinLeft = gRect.Left + leftOffset - 4;
                tgtWinTop = gRect.Top + topOffset - 4;
                tgtWinWidth = maxFitSize.Width + 8;
                tgtWinHeight = maxFitSize.Height + 8;
            }


            // rip tgt's image, inserting into a buffer, resized properly. :)
            bufferHdc = g.GetHdc();
            GDI32.SetStretchBltMode(bufferHdc, 3); // don't know why 3's best.. and FAST, wow.
            GDI32.StretchBlt(bufferHdc, 0, 0, 400, 300, tgtHdc, leftOffset, topOffset, maxFitSize.Width, maxFitSize.Height, GDI32.TernaryRasterOperations.SRCCOPY);


            // release the bufferBitmap so we can draw on it internally
            g.ReleaseHdc(bufferHdc);




            if (opts._f_ApplyTimeStamp)
            {
                // create our brush shit up here so it's ready for a fast-write to avoid flickering (I hope);
                SolidBrush sb = new SolidBrush(Color.FromArgb(127, 127, 127, 127));
                String dtString = DateTime.Now.ToString(@"ddd, MMM dd, yyyy - HH\:mm\:ss");
                dtString += " " + (captures + 1).ToString().PadLeft(2, '0') + " [" + Global.sTitle + "]";
                Font font = new Font("Lucida Console", 9f, FontStyle.Regular);
                SizeF sf = g.MeasureString(dtString, font);
                int lOffset = 400 - (int)sf.Width;
                // draw our rectangle and timestmap (including current frame 1-30)
                g.FillRectangle(sb, lOffset, 0, sf.Width, sf.Height);
                g.DrawString(dtString, font, Brushes.White, lOffset + 1, 1);
                sb.Dispose();
                dtString = null;
                font.Dispose();
            }


            #region setPicture;
            if (pictureBox1.InvokeRequired)
            {
                pictureBox1.Invoke(new MethodInvoker(
                delegate()
                {
                    if (pictureBox1.Image != null)
                    {
                        Image oldImg = pictureBox1.Image;
                        pictureBox1.Image = null;
                        oldImg.Dispose();
                    }
                    pictureBox1.Image = (Bitmap)_bmpBuffer.Clone();
                    pictureBox1.Update();
                }));
            }
            else
            {
                if (pictureBox1.Image != null)
                {
                    Image oldImg = pictureBox1.Image;
                    pictureBox1.Image = null;
                    oldImg.Dispose();
                }
                pictureBox1.Image = (Bitmap)_bmpBuffer.Clone();
                pictureBox1.Update();
            }
            #endregion setPicture;












            #region DISPOSE GDI Objects;
            {
                _bmpBuffer.Dispose();
                GDI32.DeleteDC((int)tgtHdc);
                GDI32.ReleaseDC(IntPtr.Zero, tgtHdc);
                g.Dispose();
                GDI32.DeleteObject((int)bufferHdc);
            }
            #endregion DISPOSE GDI Objects;




        }