2016-12-14 108 views
2

不是在Windows上执行线程安全的屏幕截图吗?使用C++ Builder在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柏林

回答

1

没有采取在Windows线程安全的屏幕截图?

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

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

话虽这么说,你正在尝试什么可以来,如果你请在您的工作线程Canvas对象Lock()/Unlock()进行,例如:

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);     
    } 
}  
+0

谢谢,雷米,F或者这个非常详细的答案。我通过锁定上述画布来完成并运行,但同步仍然是必须的。没有它我得到了一些随机AV。 (通过最终方式:我绝对喜欢你的“本地部分RAII”。 – FlKo