WinForm Control BeginInvoke/Invoke Issue

2019-09-07 02:25发布

问题:

I am trying to write a multithreaded WinForm in C++/CLI app using VS2012.

I know that only the UI thread can update a control and I have been using delegates and the invoke methods. However, I have run into a memory access issue when using BeginInvoke that I do not see when using Invoke.

Delegate Function:

public: delegate void pictureboxShowDelegate(int tChannelNumber,System::Windows::Forms::PictureBox^,System::Drawing::Bitmap^ colorImage);

Called Function:

void DrawCVImageShow(int tChannelNumber, System::Windows::Forms::PictureBox^ PBox, System::Drawing::Bitmap^ b)
{
    if(PBox->InvokeRequired)
    {
        pictureboxShowDelegate^ d = gcnew pictureboxShowDelegate(this,&MyForm::DrawCVImageShow);
        PBox->Invoke(d,tChannelNumber,PBox,b);
    }
    else
    {
        System::Drawing::Graphics^ graphics = PBox->CreateGraphics();
        System::Drawing::RectangleF rect(0,0,(float)PBox->Width,(float)PBox->Height);
    graphics->DrawImage(b,rect);
    }
}

If called this way, it works with no problem. If I substitute BeginInvoke for Invoke, I get an AccessViolationException. Clearly, this has to do with the garbage collection of the parameters but I simply can't figure this one out.

Any help greatly appreciated.

Thanks

回答1:

It sounds to me like whatever is calling DrawCVImageShow in the first place is Disposing the bitmap immediately after DrawCVImageShow returns.

If that's the case, there are a couple possiblities:

  • Make it so that DrawCVImageShow is the one responsible for Disposing the bitmap, not whatever calls DrawCVImageShow. (Simple solution, though probably not the best from an engineering solution: The thing that created the bitmap should generally be responsible for disposing it, and it makes DrawCVImageShow a less general method.)
  • Make a Clone of the bitmap, and dispose that one after it's been used. This is the more proper solution, in my opinion, but it does make things a little bit more complicated. You now need two versions of DrawCVImageShow, one that disposes the bitmap (for BeginInvoking), and one that doesn't (the method you have now). This also means you'll have two copies of the bitmap in memory when a BeginInvoke is needed; hopefully these bitmaps are not so large that this is an issue.