0

Based on this article:

I use this code to make some animation on a given Window Handle while doing some job on my database:

while not Terminated do 
begin
// some code....

// draw onto the Window DC
DC := GetDC(FWnd); // FWnd is the Window Handle
// DC := GetDCEx(FWnd, 0, DCX_VALIDATE or DCX_LOCKWINDOWUPDATE);
if DC <> 0 then
  try
    BitBlt(DC,
      FPaintRect.Left,
      FPaintRect.Top,
      ImageRect.Right,
      ImageRect.Bottom,
      Bitmap.Canvas.handle,
      0, 0,
      SRCCOPY);
  finally
    ReleaseDC(FWnd, DC);
  end;

  // more code....

end; // end while

Is it thread safe, or should I somehow Lock the DC?

Also, Can I use the GetDCEx? Thanks.

kobik
  • 21,001
  • 4
  • 61
  • 121

1 Answers1

2

No, your code is not threadsafe assuming that the window handle (FWnd) is created in main (GUI) thread. A standard VCL approach is to call all GDI functions in GUI thread, via Synchronize or Queue methods of TThread class.

kludg
  • 27,213
  • 5
  • 67
  • 118
  • how to implement `Synchronize` inside my TThread? do I need to move all local variables in `Execute` method to private section of my TThread, write a method for example `SyncPaintWindow` and use Synchronize(SyncPaintWindow)? – kobik Dec 26 '11 at 13:52
  • @kobik - yes, exactly; the alternative to moving local variables to `TThread` is to use `Syncronize` with anonymous method (available since Delphi 2009) – kludg Dec 26 '11 at 13:58
  • I understand the part about drawing on window handle. but, just to be clear: if I use a local variable `Bitmap: TBitmap;` and then create it inside my Execute method, then Draw something on it, is that also not threadsafe? – kobik Dec 26 '11 at 14:09
  • @kobik It's fine to create a bitmap in a thread and draw to it. But you have a window here. Your biggest problem is that you are drawing to your window's DC outside the paint cycle. I've no idea why you want to do that. Painting should be done in the UI thread in response to WM_PAINT messages. – David Heffernan Dec 26 '11 at 14:17
  • @kobik: GDI is not threadsafe; if GDI handles are created, used and released in the same thread, you are safe; if you create a GDI handle in one thread and use or release it in another thread, the code should be considered unsafe. – kludg Dec 26 '11 at 14:18
  • @Serg, this is exactly what I do. The bitmap is local to the thread, but still I get an exception `Canvas does not allow drawing.` when I use: `Bitmap.StretchDraw(Rect(Left, ImageRect.Top, Right, ImageRect.Bottom), FfgPattern)` (FfgPattern is also local)... – kobik Dec 26 '11 at 14:34
  • @kobik - I can't say, that is a different question; do you have the same problem in the main thread or it is specific to background thread? You are better to ask a new question and show the code. – kludg Dec 26 '11 at 14:45
  • @David, I'm doing that because I want to display some animation (in my case it a progress bar moving right and left), while the MAIN THREAD is blocked (for example DataSet.Open, or Connection.Execute which can take a few seconds). – kobik Dec 26 '11 at 14:47
  • @kobik You have the wrong solution. Don't block the UI thread. Run the blocking code in a separate thread. There's nothing much to debate about that, it's simply *the* right way to do it. Also, don't animate a progress bar yourself. Use a TProgressBar in marquee style. – David Heffernan Dec 26 '11 at 15:10
  • @DavidHeffernan, Thanks, I understand what you are saying. but I just can't turn every DataSet.Open (or other synchronic method) into a separate thread. :/ I only want to to display some kind of activity indicator during the process so the user knows that the app isn't frozen... – kobik Dec 26 '11 at 15:24
  • I'm also reading [this about TAnimate](http://blogs.msdn.com/b/oldnewthing/archive/2006/03/16/552821.aspx). which confirms your approach. – kobik Dec 26 '11 at 15:27
  • If you want responsive UI then you need to do exactly what you say you can't do – David Heffernan Dec 26 '11 at 15:45