我想在应用程序加载时显示闪屏。但是,一些第三方组件在初始化期间会阻止主线程几秒钟,这导致所有窗体都无法更新。是否可能使用自己的线程来实现闪屏,以便在主线程忙碌时也能更新?
该应用程序是Win32和Delphi版本2007。
编辑:我正在尝试避免“未绘制闪屏”效果,如果其他应用程序的窗口(例如通过alt-tab切换到另一个应用程序并返回)位于闪屏的顶部,则会出现此效果。
我想在应用程序加载时显示闪屏。但是,一些第三方组件在初始化期间会阻止主线程几秒钟,这导致所有窗体都无法更新。是否可能使用自己的线程来实现闪屏,以便在主线程忙碌时也能更新?
该应用程序是Win32和Delphi版本2007。
编辑:我正在尝试避免“未绘制闪屏”效果,如果其他应用程序的窗口(例如通过alt-tab切换到另一个应用程序并返回)位于闪屏的顶部,则会出现此效果。
实际上,只要使用对话框资源,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;
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
PBS_*
定义。我必须添加它们,因为Delphi 7不知道这些常量。
以及常量的定义(Dlg.inc
)const IDD_WAITDLG = 1000;
const IDC_PGB = 1002;
const IDC_LABEL = 1003;
首先在DPR中创建启动界面,但不要使用 Application.CreateForm 方法。以下是一些简单的代码:
begin
Application.Initialize;
SplashForm := TSplashForm.Create(nil);
try
SplashForm.FormStyle := fsStayOnTop;
SplashForm.Show;
Application.ProcessMessages;
Application.CreateForm(TForm14, Form14);
// Other Form Creation here . . . .
Application.Run;
finally
if assigned(SplashForm) then
SplashForm.Release;
end;
end.
然后将以下代码放在您的MainFrom(在本例中为Form14)的Show事件处理程序中(或稍后 - 当您的初始化完成时):
SplashForm.Close;
SplashForm.Release;
SplashForm := nil;
由于您的闪屏窗体是FormStyle:=fsStayOnTop,因此当您的主线程阻塞时,它不会收到绘图消息也不会有问题。然后,当主线程解除阻塞时,您发送更新消息(以更改进度条等)。尽管我同意Gamecat的观点,您可能需要联系第三方组件供应商,让他们停止在主线程上阻塞。
或者,您可以在单独的线程中创建第三方组件(如果它们不是视觉化的话,那可能会更加困难。)这种方法也适用于设置为true的Application.MainFormOnTaskBar。
我在启动代码中创建了闪屏,设置了始终置顶,并在适当的位置使用frmSplash.Update来确保它可见且更新。主窗体的创建是其中一个调用它的地方。
问题在于Delphi 2007假定第一个窗体现在是主窗体,在核心代码中没有办法替换主窗体,因此闪屏不再那么好用了。也许旧的Visual Basic解决方案——拥有一个快速的小闪屏应用程序,然后运行主应用程序——实际上可能更好!
在单独的线程中运行启动屏幕并不能解决主线程阻塞的问题,因为它需要主线程进行屏幕更新。
如果启动屏幕不会改变,那么这就不是一个问题。
也许你应该联系你的第三方组件供应商,因为像这样长时间的阻塞确实是一个真正的问题。