使用C ++ Builder在Windows上截取屏幕截图的线程安全性

时间:2016-12-14 18:50:31

标签: c++ thread-safety c++builder vcl

是不是在Windows线程安全上截屏?

我的下面的代码有时需要一些镜头,但在大多数情况下, imgScreenshot (这只是一个TImage)仍然只是纯白色......

我错过了什么吗?

void __fastcall TCaptureThread::Execute()
{
    int CurWidth = 1600;
    int CurHeight = 900;

    std::unique_ptr<TCanvas> Canvas(new TCanvas);
    Canvas->Handle = GetDC(0);    

    FBMP = new TBitmap;                     // private class field
    FBMP->Width     = CurWidth;
    FBMP->Height    = CurHeight;    

    FR = Rect(0, 0, CurWidth, CurHeight);   // private class field

    while(!Terminated)
    {
        FBMP->Canvas->CopyRect(FR, Canvas, FR);       
        Synchronize(&UpdatePicture);

        Sleep(100);                 
    }

    delete FBMP;
    FBMP = NULL;
}

void __fastcall TCaptureThread::UpdatePicture()
{
    FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
}

环境是C ++ Builder 10.1.2 Berlin

1 个答案:

答案 0 :(得分:1)

  

Isn是否在Windows线程安全上截屏?

默认情况下,使用非线程安全的VCL包装类时不行。如果您直接使用普通的Win32 API函数,那么是的,可以编写线程安全的代码。

您的代码失败的主要原因是因为VCL旨在在多个对象之间共享GDI资源,并且主UI线程经常释放未使用/休眠的GDI资源。因此,在您致电TBitmap将其复制到Synchronize()之前,您的工作线程的TImage图片数据可能会被销毁。

话虽如此,如果您在工作线程中的Lock()个对象上调用Unlock() / Canvas,则可以完成 的操作,例如:

struct CanvasLocker
{
    TCanvas *mCanvas;
    CanvasLocker(TCanvas *C) : mCanvas(C) { mCanvas->Lock(); }
    ~CanvasLocker() { mCanvas->Unlock(); }
};

void __fastcall TCaptureThread::Execute()
{
    int CurWidth = 1600;
    int CurHeight = 900;

    std::unique_ptr<TCanvas> Canvas(new TCanvas);
    std::unique_ptr<TBitmap> BMP(new TBitmap);
    FBMP = BMP.get();

    {
    CanvasLocker lock(Canvas); // <-- add this!
    Canvas->Handle = GetDC(0);    
    }

    {
    CanvasLocker lock(BMP->Canvas); // <-- add this!
    BMP->Width = CurWidth;
    BMP->Height = CurHeight;    
    }

    FR = Rect(0, 0, CurWidth, CurHeight);

    while (!Terminated)
    {
        {
        CanvasLocker lock1(Canvas); // <-- add this!
        CanvasLocker lock2(BMP->Canvas); // <-- add this!
        BMP->Canvas->CopyRect(FR, Canvas.get(), FR);
        }

        Synchronize(&UpdatePicture);

        Sleep(100);                 
    }
}

void __fastcall TCaptureThread::UpdatePicture()
{
    CanvasLocker lock1(FBMP->Canvas); // <-- add this!
    CanvasLocker lock2(FMainForm->imgScreenshot->Canvas); // <-- add this!

    FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
    // or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(FBMP);
}

话虽如此,因为TCanvas可以锁定,您可能能够完全删除Synchronize()

void __fastcall TCaptureThread::Execute()
{
    int CurWidth = 1600;
    int CurHeight = 900;

    std::unique_ptr<TCanvas> Canvas(new TCanvas);
    std::unique_ptr<TBitmap> BMP(new TBitmap);

    {
    CanvasLocker lock(Canvas);
    Canvas->Handle = GetDC(0);    
    }

    {
    CanvasLocker lock(BMP->Canvas);
    BMP->Width = CurWidth;
    BMP->Height = CurHeight;    
    }

    TRect r = Rect(0, 0, CurWidth, CurHeight);

    while (!Terminated)
    {
        {
        CanvasLocker lock1(BMP->Canvas);

        {
        CanvasLocker lock2(Canvas);
        BMP->Canvas->CopyRect(r, Canvas.get(), r);
        }

        CanvasLocker lock3(FMainForm->imgScreenshot->Canvas);
        FMainForm->imgScreenshot->Canvas->CopyRect(r, BMP->Canvas, r);
        // or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(BMP);
        }

        Sleep(100);                 
    }
}