主线程繁忙时在Delphi中显示启动画面

Har*_*riv 12 delphi multithreading splash-screen delphi-2007

我想在加载应用程序时显示启动画面.但是,某些第三方组件在启动期间会阻塞主线程几秒钟,这会导致所有表单都不更新.是否可以使用自己的线程启动屏幕,这样当主线程忙时它也会更新?

该应用程序是win32和Delphi 2007版.

编辑:我正在尝试避免"未绘制的启动画面"效果,如果某些其他窗口(来自其他应用程序)位于启动画面的顶部,例如alt-tabbing到另一个应用程序并返回,则会发生这种情况.

mgh*_*hie 9

您可以在另一个线程中运行启动屏幕,但随后您将需要使用原始Windows API调用或实现类似VCL类的第三方库(如密钥对象库).但是,不要从启动线程访问VCL内容.

如果你走这条路线(我认为你不应该这样做,因为这是一项很少有收获的工作),请务必遵守多线程的Windows API访问规则.谷歌例如为"用户界面线程"提供更多信息.

编辑:

我以前没有意识到这一点,但实际上有一个组件在CodeCentral上为Delphi实现了Threaded Splashscreen.使用这个组件它可能(没有尝试过)实际上很容易将启动画面放在不同的线程中,但是对辅助线程的VCL访问的警告仍然存在.


Fr0*_*0sT 7

其实 WinApi 的方式很简单,只要你使用对话框资源。检查这个(即使在 D7 和 XP 上也能工作):

type
  TDlgThread = class(TThread)
  private
    FDlgWnd: HWND;
    FCaption: string;
  protected
    procedure Execute; override;
    procedure ShowSplash;
  public
    constructor Create(const Caption: string);
  end;

{ TDlgThread }

// Create thread for splash dialog with custom Caption and show the dialog
constructor TDlgThread.Create(const Caption: string);
begin
  FCaption := Caption;
  inherited Create(False);
  FreeOnTerminate := True;
end;

procedure TDlgThread.Execute;
var Msg: TMsg;
begin
  ShowSplash;
  // Process window messages until the thread is finished
  while not Terminated and GetMessage(Msg, 0, 0, 0) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
  EndDialog(FDlgWnd, 0);
end;

procedure TDlgThread.ShowSplash;
const
  PBM_SETMARQUEE = WM_USER + 10;
  {$I 'Dlg.inc'}
begin
  FDlgWnd := CreateDialogParam(HInstance, MakeIntResource(IDD_WAITDLG), 0, nil, 0);
  if FDlgWnd = 0 then Exit;
  SetDlgItemText(FDlgWnd, IDC_LABEL, PChar(FCaption));           // set caption
  SendDlgItemMessage(FDlgWnd, IDC_PGB, PBM_SETMARQUEE, 1, 100);  // start marquee
end;

procedure TForm1.Button3Click(Sender: TObject);
var th: TDlgThread;
begin
  th := TDlgThread.Create('Connecting to DB...');
  Sleep(3000); // blocking wait
  th.Terminate;
end;
Run Code Online (Sandbox Code Playgroud)

当然,您必须准备对话资源 ( Dlg.rc) 并将其添加到您的项目中:

#define IDD_WAITDLG 1000
#define IDC_PGB 1002
#define IDC_LABEL 1003

#define PBS_SMOOTH  0x00000001
#define PBS_MARQUEE 0x00000008

IDD_WAITDLG DIALOGEX 10,10,162,33
STYLE WS_POPUP|WS_VISIBLE|WS_DLGFRAME|DS_CENTER
EXSTYLE WS_EX_TOPMOST
BEGIN
  CONTROL "",IDC_PGB,"msctls_progress32",WS_CHILDWINDOW|WS_VISIBLE|PBS_SMOOTH|PBS_MARQUEE,9,15,144,15
  CONTROL "",IDC_LABEL,"Static",WS_CHILDWINDOW|WS_VISIBLE,9,3,144,9
END
Run Code Online (Sandbox Code Playgroud)

注意这些PBS_*定义。我不得不添加它们,因为 Delphi 7 对这些常量一无所知。和常量的定义 ( Dlg.inc)

const IDD_WAITDLG = 1000;
const IDC_PGB = 1002;
const IDC_LABEL = 1003;
Run Code Online (Sandbox Code Playgroud)

(我使用自动生成包含文件的 RadAsm 资源编辑器)。

我们在 XP 下得到了什么

与 VCL 技巧(表单创建顺序等)相比,这种方式更好的是,当您的应用程序需要一些时间思考时,您可以多次使用它。