2016-12-14 89 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