当主线程被阻塞时显示活动指示器(继续)

发布于 2024-12-23 07:07:34 字数 6710 浏览 2 评论 0原文

继续上一个问题 我希望能够显示一些活动指示器即使主线程被阻塞。 (基于本文)。

基于所附代码的问题:

  • 使用 Synchronize(PaintTargetWindow); 不会绘制窗口
  • 我有时会收到错误: Canvas 不允许绘图。 在行中:{FBitmap.}StretchDraw(Rect(Left, ImageRect.Top, Right, ImageRect.Bottom), FfgPattern)

这里是我用来创建指标线程的代码:

unit AniThread;

interface

uses Windows, Classes, Graphics, Controls, Math;

const
  ANI_GRAD_FG_COLOR_BAGIN = $00CDFFCD;
  ANI_GRAD_FG_COLOR_END   = $0024B105;
  ANI_GRAD_BK_COLOR_BAGIN = $00F5F5F5;
  ANI_GRAD_BK_COLOR_END   = $00BDBDBD;

type
  TAnimationThread = class(TThread)
  private
    FWnd: HWND;
    FPaintRect: TRect;
    FInterval: Integer;
    FfgPattern, FbkPattern: TBitmap;
    FBitmap: TBitmap;
    FImageRect: TRect;
    procedure UpdatePattern(Pattern: TBitmap; ColorBegin, ColorEnd: TColor);
    function CreatePatternBitmap(AColorBegin, AColorEnd: TColor): TBitmap;
    procedure PaintTargetWindow;
  protected
    procedure Execute; override;
  public
    procedure Animate;
    constructor Create(PaintSurface: TWinControl; { Control to paint on }
      PaintRect: TRect;          { area for animation bar }
      Interval: Integer          { wait in msecs between paints}
      );
    destructor Destroy; override;
  end;

implementation

constructor TAnimationThread.Create(PaintSurface: TWinControl;
  PaintRect: TRect;
  Interval: Integer);
begin
  inherited Create(True); { suspended }
  FreeOnterminate := True;
  Priority := tpHigher;
  FInterval := Interval;
  FWnd := PaintSurface.Handle;
  FPaintRect := PaintRect;
  FfgPattern := CreatePatternBitmap(ANI_GRAD_FG_COLOR_BAGIN, ANI_GRAD_FG_COLOR_END);
  FbkPattern := CreatePatternBitmap(ANI_GRAD_BK_COLOR_BAGIN, ANI_GRAD_BK_COLOR_END);
end;

destructor TAnimationThread.Destroy;
begin
  inherited Destroy;
  FfgPattern.Free;
  FbkPattern.Free;
end;

procedure TAnimationThread.Animate;
begin
  Resume;
  Sleep(0);
end;

function TAnimationThread.CreatePatternBitmap(AColorBegin, AColorEnd: TColor): TBitmap;
begin
  Result := TBitmap.Create;
  Result.PixelFormat := pf24bit;
  UpdatePattern(Result, AColorBegin, AColorEnd);
end;

type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..32767] of TRGBTriple;
  TGradientColors = array[0..255] of TRGBTriple;

procedure PatternBuilder(const Colors: TGradientColors; Pattern: TBitmap);
var
  Y: Integer;
  Row: PRGBTripleArray;
begin
  Pattern.Width := 1;
  Pattern.Height := 256;
  for Y := 0 to 127 do
  begin
    Row := PRGBTripleArray(Pattern.ScanLine[Y]);
    Row[0] := Colors[Y];
    Row := PRGBTripleArray(Pattern.ScanLine[Y + 128]);
    Row[0] := Colors[255 - Y];
  end;
end;

procedure TAnimationThread.UpdatePattern(Pattern: TBitmap; ColorBegin, ColorEnd: TColor);
var
  Colors: TGradientColors;
  dRed, dGreen, dBlue: Integer;
  RGBColor1, RGBColor2: TColor;
  RGB1, RGB2: TRGBTriple;
  Index: Integer;
begin
  RGBColor1 := ColorToRGB(ColorBegin);
  RGBColor2 := ColorToRGB(ColorEnd);

  RGB1.rgbtRed := GetRValue(RGBColor1);
  RGB1.rgbtGreen := GetGValue(RGBColor1);
  RGB1.rgbtBlue := GetBValue(RGBColor1);

  RGB2.rgbtRed := GetRValue(RGBColor2);
  RGB2.rgbtGreen := GetGValue(RGBColor2);
  RGB2.rgbtBlue := GetBValue(RGBColor2);

  dRed := RGB2.rgbtRed - RGB1.rgbtRed;
  dGreen := RGB2.rgbtGreen - RGB1.rgbtGreen;
  dBlue := RGB2.rgbtBlue - RGB1.rgbtBlue;

  for Index := 0 to 255 do
    with Colors[Index] do
    begin
      rgbtRed := RGB1.rgbtRed + (Index * dRed) div 255;
      rgbtGreen := RGB1.rgbtGreen + (Index * dGreen) div 255;
      rgbtBlue := RGB1.rgbtBlue + (Index * dBlue) div 255;
    end;

  PatternBuilder(Colors, Pattern);
end;

procedure TAnimationThread.PaintTargetWindow;
var
  DC: HDC;
begin
  DC := GetDC(FWnd);
  if DC <> 0 then
    try
      BitBlt(DC,
        FPaintRect.Left,
        FPaintRect.Top,
        FImageRect.Right,
        FImageRect.Bottom,
        FBitmap.Canvas.handle,
        0, 0,
        SRCCOPY);
    finally
      ReleaseDC(FWnd, DC);
    end;
end;

procedure TAnimationThread.Execute;
var
  Left, Right: Integer;
  Increment: Integer;
  State: (incRight, incLeft, decLeft, decRight);
begin
  InvalidateRect(FWnd, nil, True);
  FBitmap := TBitmap.Create;
  try
    with FBitmap do
    begin
      Width := FPaintRect.Right - FPaintRect.Left;
      Height := FPaintRect.Bottom - FPaintRect.Top;
      FImageRect := Rect(0, 0, Width, Height);
    end;
    Left := 0;
    Right := 0;
    Increment := FImageRect.Right div 50;
    State := Low(State);
    while not Terminated do
    begin
      with FBitmap.Canvas do
      begin
        StretchDraw(FImageRect, FbkPattern);
        case State of
          incRight:
            begin
              Inc(Right, Increment);
              if Right > FImageRect.Right then begin
                Right := FImageRect.Right;
                Inc(State);
              end;
            end;
          incLeft:
            begin
              Inc(Left, Increment);
              if Left >= Right then begin
                Left := Right;
                Inc(State);
              end;
            end;
          decLeft:
            begin
              Dec(Left, Increment);
              if Left <= 0 then begin
                Left := 0;
                Inc(State);
              end;
            end;
          decRight:
            begin
              Dec(Right, Increment);
              if Right <= 0 then begin
                Right := 0;
                State := incRight;
              end;
            end;
        end;

        StretchDraw(Rect(Left, FImageRect.Top, Right, FImageRect.Bottom), FfgPattern);
      end; { with }

      // Synchronize(PaintTargetWindow); // not painting when the main thread is blocked
      PaintTargetWindow;

      SleepEx(FInterval, False);
    end; { While }
  finally
    FBitmap.Free;
  end;
end;

end.

用法:在主窗体上放置一个TButton和一个TPanel

uses AniThread;

procedure TForm1.Button1Click(Sender: TObject);
var
  at: TAnimationThread;
begin
  at := TAnimationThread.Create(Panel1, Panel1.ClientRect, 10);
  Button1.Enabled := False;
  try
    at.Animate;
    Sleep(3000); // sleep 3 sec. block main thread
  finally
    at.Terminate;
    Button1.Enabled := True;
  end;
end;

我知道你们中的许多人会不赞成这种方法。 但现在对我来说主要是一个挑战,让它运作良好。 任何有关此问题的帮助将不胜感激。

编辑:

这是原始文章(作者:Peter Below ,B队)。我只实现了渐变绘画。

Continue with previous question
I want to be able to show some activity indicator even if the main thread is blocked.
(based on this article).

Problems based on the attached code:

  • Using Synchronize(PaintTargetWindow); does not paint the window
  • I sometimes get an error:
    Canvas does not allow drawing. In the line: {FBitmap.}StretchDraw(Rect(Left, ImageRect.Top, Right, ImageRect.Bottom), FfgPattern)

here is the code I use to create the indicator thread:

unit AniThread;

interface

uses Windows, Classes, Graphics, Controls, Math;

const
  ANI_GRAD_FG_COLOR_BAGIN = $00CDFFCD;
  ANI_GRAD_FG_COLOR_END   = $0024B105;
  ANI_GRAD_BK_COLOR_BAGIN = $00F5F5F5;
  ANI_GRAD_BK_COLOR_END   = $00BDBDBD;

type
  TAnimationThread = class(TThread)
  private
    FWnd: HWND;
    FPaintRect: TRect;
    FInterval: Integer;
    FfgPattern, FbkPattern: TBitmap;
    FBitmap: TBitmap;
    FImageRect: TRect;
    procedure UpdatePattern(Pattern: TBitmap; ColorBegin, ColorEnd: TColor);
    function CreatePatternBitmap(AColorBegin, AColorEnd: TColor): TBitmap;
    procedure PaintTargetWindow;
  protected
    procedure Execute; override;
  public
    procedure Animate;
    constructor Create(PaintSurface: TWinControl; { Control to paint on }
      PaintRect: TRect;          { area for animation bar }
      Interval: Integer          { wait in msecs between paints}
      );
    destructor Destroy; override;
  end;

implementation

constructor TAnimationThread.Create(PaintSurface: TWinControl;
  PaintRect: TRect;
  Interval: Integer);
begin
  inherited Create(True); { suspended }
  FreeOnterminate := True;
  Priority := tpHigher;
  FInterval := Interval;
  FWnd := PaintSurface.Handle;
  FPaintRect := PaintRect;
  FfgPattern := CreatePatternBitmap(ANI_GRAD_FG_COLOR_BAGIN, ANI_GRAD_FG_COLOR_END);
  FbkPattern := CreatePatternBitmap(ANI_GRAD_BK_COLOR_BAGIN, ANI_GRAD_BK_COLOR_END);
end;

destructor TAnimationThread.Destroy;
begin
  inherited Destroy;
  FfgPattern.Free;
  FbkPattern.Free;
end;

procedure TAnimationThread.Animate;
begin
  Resume;
  Sleep(0);
end;

function TAnimationThread.CreatePatternBitmap(AColorBegin, AColorEnd: TColor): TBitmap;
begin
  Result := TBitmap.Create;
  Result.PixelFormat := pf24bit;
  UpdatePattern(Result, AColorBegin, AColorEnd);
end;

type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..32767] of TRGBTriple;
  TGradientColors = array[0..255] of TRGBTriple;

procedure PatternBuilder(const Colors: TGradientColors; Pattern: TBitmap);
var
  Y: Integer;
  Row: PRGBTripleArray;
begin
  Pattern.Width := 1;
  Pattern.Height := 256;
  for Y := 0 to 127 do
  begin
    Row := PRGBTripleArray(Pattern.ScanLine[Y]);
    Row[0] := Colors[Y];
    Row := PRGBTripleArray(Pattern.ScanLine[Y + 128]);
    Row[0] := Colors[255 - Y];
  end;
end;

procedure TAnimationThread.UpdatePattern(Pattern: TBitmap; ColorBegin, ColorEnd: TColor);
var
  Colors: TGradientColors;
  dRed, dGreen, dBlue: Integer;
  RGBColor1, RGBColor2: TColor;
  RGB1, RGB2: TRGBTriple;
  Index: Integer;
begin
  RGBColor1 := ColorToRGB(ColorBegin);
  RGBColor2 := ColorToRGB(ColorEnd);

  RGB1.rgbtRed := GetRValue(RGBColor1);
  RGB1.rgbtGreen := GetGValue(RGBColor1);
  RGB1.rgbtBlue := GetBValue(RGBColor1);

  RGB2.rgbtRed := GetRValue(RGBColor2);
  RGB2.rgbtGreen := GetGValue(RGBColor2);
  RGB2.rgbtBlue := GetBValue(RGBColor2);

  dRed := RGB2.rgbtRed - RGB1.rgbtRed;
  dGreen := RGB2.rgbtGreen - RGB1.rgbtGreen;
  dBlue := RGB2.rgbtBlue - RGB1.rgbtBlue;

  for Index := 0 to 255 do
    with Colors[Index] do
    begin
      rgbtRed := RGB1.rgbtRed + (Index * dRed) div 255;
      rgbtGreen := RGB1.rgbtGreen + (Index * dGreen) div 255;
      rgbtBlue := RGB1.rgbtBlue + (Index * dBlue) div 255;
    end;

  PatternBuilder(Colors, Pattern);
end;

procedure TAnimationThread.PaintTargetWindow;
var
  DC: HDC;
begin
  DC := GetDC(FWnd);
  if DC <> 0 then
    try
      BitBlt(DC,
        FPaintRect.Left,
        FPaintRect.Top,
        FImageRect.Right,
        FImageRect.Bottom,
        FBitmap.Canvas.handle,
        0, 0,
        SRCCOPY);
    finally
      ReleaseDC(FWnd, DC);
    end;
end;

procedure TAnimationThread.Execute;
var
  Left, Right: Integer;
  Increment: Integer;
  State: (incRight, incLeft, decLeft, decRight);
begin
  InvalidateRect(FWnd, nil, True);
  FBitmap := TBitmap.Create;
  try
    with FBitmap do
    begin
      Width := FPaintRect.Right - FPaintRect.Left;
      Height := FPaintRect.Bottom - FPaintRect.Top;
      FImageRect := Rect(0, 0, Width, Height);
    end;
    Left := 0;
    Right := 0;
    Increment := FImageRect.Right div 50;
    State := Low(State);
    while not Terminated do
    begin
      with FBitmap.Canvas do
      begin
        StretchDraw(FImageRect, FbkPattern);
        case State of
          incRight:
            begin
              Inc(Right, Increment);
              if Right > FImageRect.Right then begin
                Right := FImageRect.Right;
                Inc(State);
              end;
            end;
          incLeft:
            begin
              Inc(Left, Increment);
              if Left >= Right then begin
                Left := Right;
                Inc(State);
              end;
            end;
          decLeft:
            begin
              Dec(Left, Increment);
              if Left <= 0 then begin
                Left := 0;
                Inc(State);
              end;
            end;
          decRight:
            begin
              Dec(Right, Increment);
              if Right <= 0 then begin
                Right := 0;
                State := incRight;
              end;
            end;
        end;

        StretchDraw(Rect(Left, FImageRect.Top, Right, FImageRect.Bottom), FfgPattern);
      end; { with }

      // Synchronize(PaintTargetWindow); // not painting when the main thread is blocked
      PaintTargetWindow;

      SleepEx(FInterval, False);
    end; { While }
  finally
    FBitmap.Free;
  end;
end;

end.

Usage: drop a TButton and a TPanel on the main form.

uses AniThread;

procedure TForm1.Button1Click(Sender: TObject);
var
  at: TAnimationThread;
begin
  at := TAnimationThread.Create(Panel1, Panel1.ClientRect, 10);
  Button1.Enabled := False;
  try
    at.Animate;
    Sleep(3000); // sleep 3 sec. block main thread
  finally
    at.Terminate;
    Button1.Enabled := True;
  end;
end;

I know many of you will disapprove with this approach.
But now it's mainly a challenge for me to MAKE IT WORK well.
Any help with this issue will be much appreciated.

EDIT:

This is the original article (by Peter Below, TeamB). I only implemented the gradient painting.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最笨的告白 2024-12-30 07:07:34

Canvas 不允许绘图。 行中的异常:

FBitmap.StretchDraw(Rect(Left, ImageRect.Top, Right, ImageRect.Bottom), FfgPattern)

是由于 TBitmap canvas 不是线程安全的除非你锁定它(甚至在主 UI 线程中)。根据我的经验,即使您将画布锁定在工作线程中,它的 DC 可能 会被 Graphics.pas 垃圾收集/GDI 缓存释放,而消息在主 UI TWinControl.MainWndProc 中处理。正在访问的每个位图画布都需要锁定,包括我的代码中的 FBitmap + FbkPattern + FfgPattern

请参阅 Graphis.pas 中的 FreeMemoryContexts

{ FreeMemoryContexts is called by the VCL main winproc to release
  memory DCs after every message is processed (garbage collection).
  Only memory DCs not locked by other threads will be freed.
}

可能的解决方案不是直接使用 TBitmap.Canvas 而是使用 CreateCompatibleDC ,如下所述这里:如何在后台从磁盘加载图像(多线程)[AKA:TBitmap 不是线程安全] 或锁定您使用的每个 TCanvas

更多参考:
TBitmap 的线程安全性
在第二个线程中使用 TGIFImage 进行 GDI 句柄泄漏< br>
QC:TJPEGImage.Draw() 不是线程安全的


代码为我工作确保每个 TBitmap.Canvas 都被锁定在工作线程上下文中:
工作 TAnimationThread
无论主 UI 线程是否被阻塞,这都可以正常工作。

procedure TForm1.Button1Click(Sender: TObject);
var
  at1, at2, at3, at4, at5: TAnimationThread;
begin
  at1 := TAnimationThread.Create(Panel1, Panel1.ClientRect, 10);
  at2 := TAnimationThread.Create(Panel2, Panel2.ClientRect, 10);
  at3 := TAnimationThread.Create(Panel3, Panel3.ClientRect, 10);
  at4 := TAnimationThread.Create(Panel4, Panel4.ClientRect, 10);
  at5 := TAnimationThread.Create(Panel5, Panel5.ClientRect, 10);
  // Sleep(5000); // do some work for 5 seconds, block main thread
  // at1.Terminate; at2.Terminate; at3.Terminate; at4.Terminate; at5.Terminate;
end;

在此处输入图像描述

现在,如果我省略例如锁定 FfgPattern.Canvas.Lock;,当我移动 UI 表单时,TBitmap 的 DC 被终止(如果我不阻塞主线程,即不休眠 5 秒且不终止线程)。

在此处输入图像描述

我的结论:

  1. “你不能在除了主线程之外的任何地方都可以控制 VCL”(摘自评论)。不是真的!任何主 VCL 窗口控制 DC 都可以从工作线程访问,不会出现任何问题(事实上,例如,许多应用程序直接绘制到桌面窗口 DC)。

  2. TBitmap canvas 线程安全的,如果您知道在哪里/何时锁定它。

  3. 由于我不确定在何处/何时锁定它,最好不要在工作线程中使用 TBitmap 画布。使用API​​位图操作,使用CreateCompatibleDC/CreateBitmap; TWICImage 位于 Windows 成像组件之上。 TBitmap 垃圾收集是邪恶的!

  4. 我不推荐这种方法。更好的方法是在工作线程的上下文中创建一个纯 API 窗口并在那里显示活动指示器,例如 当主线程繁忙时在Delphi中显示启动屏幕

  5. 最好的方法(如评论中已经提到的)是在工作线程中完成艰苦的工作并在主窗口中显示活动指示器工作线程工作时的 UI 线程。

Canvas does not allow drawing. Exception In the line:

FBitmap.StretchDraw(Rect(Left, ImageRect.Top, Right, ImageRect.Bottom), FfgPattern)

Is caused by the fact that TBitmap canvas is not thread safe unless you lock it (even in the main UI thread). in my experience even if you do Lock the canvas in a worker thread it's DC might be freed by Graphics.pas Garbage collection/GDI caching, while messages are processed in the main UI TWinControl.MainWndProc. Every bitmap canvas that is being accessed needs to be locked including FBitmap + FbkPattern + FfgPattern in my code.

See FreeMemoryContexts in Graphis.pas:

{ FreeMemoryContexts is called by the VCL main winproc to release
  memory DCs after every message is processed (garbage collection).
  Only memory DCs not locked by other threads will be freed.
}

Possible solution is NOT using TBitmap.Canvas directly and use a CreateCompatibleDC as described here: How to load images from disk in background (multiple threads) [AKA: TBitmap is not thread-safe] or lock every TCanvas you use.

More references:
How threadsafe is TBitmap
GDI handle leak using TGIFImage in a second thread
QC: TJPEGImage.Draw() is not thread safe


The code that worked for me insured every TBitmap.Canvas is being locked in the worker thread context:
Working TAnimationThread
This works solid whether the main UI thread is blocked or not.

procedure TForm1.Button1Click(Sender: TObject);
var
  at1, at2, at3, at4, at5: TAnimationThread;
begin
  at1 := TAnimationThread.Create(Panel1, Panel1.ClientRect, 10);
  at2 := TAnimationThread.Create(Panel2, Panel2.ClientRect, 10);
  at3 := TAnimationThread.Create(Panel3, Panel3.ClientRect, 10);
  at4 := TAnimationThread.Create(Panel4, Panel4.ClientRect, 10);
  at5 := TAnimationThread.Create(Panel5, Panel5.ClientRect, 10);
  // Sleep(5000); // do some work for 5 seconds, block main thread
  // at1.Terminate; at2.Terminate; at3.Terminate; at4.Terminate; at5.Terminate;
end;

enter image description here

Now, if I omit for example locking FfgPattern.Canvas.Lock;, the DC of the TBitmaps is being killed while I move the UI form (in case where I do NOT block the main thread i.e not Sleeping for 5 seconds and not terminating the threads).

enter image description here

My conclusions:

  1. "you cannot draw on a VCL control from anything but the main thread" (From the comments). Not true! Any main VCL windowed control DC can bee accessed from a worker thread without any problems (in fact, many applications draw directly to the Desktop window DC for example).

  2. TBitmap canvas is thread safe if you know where/when to lock it.

  3. Since I'm not sure where/when to lock it, better NOT to use TBitmap canvas in a worker thread. use API bitmap manipulations, use CreateCompatibleDC/CreateBitmap; TWICImage which stands on top of Windows Imaging Components. TBitmap garbage collection is evil!

  4. I do not recommend this method. a better method would be to create a pure API Window in the context of the worker thread and show activity indicator there e.g. Displaying splash screen in Delphi when main thread is busy

  5. The best approach (as already mentioned in the comments) is to do the hard work in a worker thread and show activity indicator in the main UI tread while the worker thread is working.

因为看清所以看轻 2024-12-30 07:07:34

同样,在窗口上绘图的唯一线程安全方法是从创建窗口的同一线程进行绘图;其他任何东西都是不安全的。

作为可能的解释,为什么您的代码适用于旧的 Windows 版本,但不适用于现代版本,请阅读此 旧事新事文章

Again, the only threadsafe way to draw on a window is to draw from the same thread that created a window; anything else is unsafe.

As a possible explanation why your code worked well with old Windows versions and does not work with modern versions read this The Old New Thing article.

别想她 2024-12-30 07:07:34

最初这总是崩溃。然后我找到了解决方案:

1)使用FBitmap.Canvas.Lock;将while循环包装在try-finally结构内;

FBitmap.Canvas.Lock;
try
  while not Terminated do
  begin
    with FBitmap.Canvas do
    begin
      StretchDraw(FImageRect, FbkPattern);
      case State of
        incRight:
          begin
            Inc(Right, Increment);
            if Right > FImageRect.Right then
            begin
              Right := FImageRect.Right;
              Inc(State);
            end;
          end;
        incLeft:
          begin
            Inc(Left, Increment);
            if Left >= Right then
            begin
              Left := Right;
              Inc(State);
            end;
          end;
        decLeft:
          begin
            Dec(Left, Increment);
            if Left <= 0 then
            begin
              Left := 0;
              Inc(State);
            end;
          end;
        decRight:
          begin
            Dec(Right, Increment);
            if Right <= 0 then
            begin
              Right := 0;
              State := incRight;
            end;
          end;
      end;

      StretchDraw(Rect(Left, FImageRect.Top, Right, FImageRect.Bottom), FfgPattern);
    end; { with }

    // Synchronize(PaintTargetWindow); // not painting when the main thread is blocked
    PaintTargetWindow;

    SleepEx(FInterval, False);
  end; { While }
finally
  FBitmap.Canvas.Unlock;
end;

2)在FormCreate 您的应用程序调用此过程:

procedure DisableProcessWindowsGhosting;
var
  DisableProcessWindowsGhostingProc: procedure;
begin
  DisableProcessWindowsGhostingProc := GetProcAddress(GetModuleHandle('user32.dll'), 'DisableProcessWindowsGhosting');
  if Assigned(DisableProcessWindowsGhostingProc) then
    DisableProcessWindowsGhostingProc;
end;

现在它工作完美 - 到目前为止从未崩溃过!
德尔福XE2、Win7 x64

Initially this always crashed. Then I found the solution:

1) Wrap the while loop inside a try-finally structure with FBitmap.Canvas.Lock;:

FBitmap.Canvas.Lock;
try
  while not Terminated do
  begin
    with FBitmap.Canvas do
    begin
      StretchDraw(FImageRect, FbkPattern);
      case State of
        incRight:
          begin
            Inc(Right, Increment);
            if Right > FImageRect.Right then
            begin
              Right := FImageRect.Right;
              Inc(State);
            end;
          end;
        incLeft:
          begin
            Inc(Left, Increment);
            if Left >= Right then
            begin
              Left := Right;
              Inc(State);
            end;
          end;
        decLeft:
          begin
            Dec(Left, Increment);
            if Left <= 0 then
            begin
              Left := 0;
              Inc(State);
            end;
          end;
        decRight:
          begin
            Dec(Right, Increment);
            if Right <= 0 then
            begin
              Right := 0;
              State := incRight;
            end;
          end;
      end;

      StretchDraw(Rect(Left, FImageRect.Top, Right, FImageRect.Bottom), FfgPattern);
    end; { with }

    // Synchronize(PaintTargetWindow); // not painting when the main thread is blocked
    PaintTargetWindow;

    SleepEx(FInterval, False);
  end; { While }
finally
  FBitmap.Canvas.Unlock;
end;

2) In FormCreate of your application call this procedure:

procedure DisableProcessWindowsGhosting;
var
  DisableProcessWindowsGhostingProc: procedure;
begin
  DisableProcessWindowsGhostingProc := GetProcAddress(GetModuleHandle('user32.dll'), 'DisableProcessWindowsGhosting');
  if Assigned(DisableProcessWindowsGhostingProc) then
    DisableProcessWindowsGhostingProc;
end;

Now it works perfectly - never crashed so far!
Delphi XE2, Win7 x64

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文