Отображение заставки в Delphi, когда основной поток занят
Я хотел бы отобразить заставку во время загрузки приложения. Однако некоторые сторонние компоненты блокируют основной поток во время инициализации в течение нескольких секунд, что заставляет все формы не обновляться. Возможно ли иметь заставку с собственным потоком, чтобы она обновлялась также, когда основной поток занят?
приложение win32 и Delphi версии 2007.
Edit: я пытаюсь избежать эффекта" undrawn splash screen", что происходит, если некоторые другие окна (из другие приложения) находятся в верхней части экрана заставки, например, alt-tabbing в другое приложение и обратно.
6 ответов
вы можете запустить заставку в другом потоке, но тогда вам нужно будет использовать необработанные вызовы API Windows или стороннюю библиотеку (например,Библиотека Ключевых Объектов), который реализует VCL-подобные классы. Однако не обращайтесь к материалам VCL из потока splash.
Если вы идете по этому маршруту (что я не думаю, что вы должны, так как это много работы для небольшой выгоды), обязательно соблюдайте правила доступа к Windows API из нескольких потоков. Google, например, для "пользовательского интерфейса темы" для получения дополнительной информации.
Edit:
Я не знал об этом раньше, но на самом деле есть компонент, реализующий резьбовой Splashscreen для Delphi на CodeCentral. Используя этот компонент, он может (не пробовал) на самом деле легко иметь заставку в другом потоке, но предупреждение о доступе VCL из вторичных потоков остается.
на самом деле путь 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;
(Я использую редактор ресурсов RadAsm, который генерирует файл include автоматически.)
что лучше таким образом по сравнению с трюками VCL (порядок создания форм и т. д.), Так это то, что вы можете использовать его несколько раз, когда вашему приложению нужно некоторое время, чтобы подумать.
сначала создайте заставку в DPR, но не используйте приложение.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.
затем поместите следующий код в обработчик событий Show (или позже - когда ваша инициализация будет выполнена) для вашего MainFrom (в данном случае Form14):
SplashForm.Close;
SplashForm.Release;
SplashForm := nil;
(вы вызываете Release в форме вместо Free, и вы назначаете его nil, чтобы DRP не вызывал release снова. Релиз в DRP только в если ваша mainform не удается создать.)
Так как ваша форма всплеска FormStyle := fsStayOnTop это не должно быть проблемой, что он не получает сообщения краски, когда основной поток блокируется. Затем, когда основной поток разблокируется, вы отправляете ему сообщение об обновлении(чтобы изменить индикатор выполнения и т. д.) Хотя я согласен с Gamecat, что вы можете связаться со своими сторонними поставщиками компонентов и заставить их прекратить блокировать основной поток на вас.
вы можно создать сторонние компоненты в отдельном потоке (при условии, что они не являются визуальными, так как это было бы немного сложнее.)
это будет работать с приложение.MainFormOnTaskBar установите значение true.
Я создаю всплеск в коде запуска, всегда на верхнем наборе, а затем использую frmSplash.Обновление в соответствующих местах, чтобы обеспечить его видимость и обновление. Основной формой создания является одним из таких мест назвать.
проблема в том, что Delphi 2007 предполагает, что первая форма теперь является основной формой, и нет способа заменить основную форму в основном коде, поэтому брызги уже не так хороши. Возможно, старое решение visual basic с быстрым небольшим приложением splash, которое тогда запускает основное приложение на самом деле может быть лучше!
проблема блокировки основного потока не решается путем запуска заставки в отдельном потоке, потому что для обновления экрана потребуется основной поток.
Это заставка не меняется, это не проблема.
Возможно, вам следует связаться с поставщиком компонентов 3rd party, потому что такой длинный блок является реальной проблемой.
У Джима Маккита есть отличная идея, но он не рассматривает одну вещь, которая может быть или не быть проблемой. Вы говорите о том, что для инициализации компонентов требуется много времени. Под этим вы подразумеваете инициализации раздел или что-то, что происходит позже, например, во время создания ваших форм? Потому что все разделы инициализации выполняются до запуска любого кода в DPR. Это часть занимает много времени, вам придется сделать некоторые хитрые вещи, чтобы получить всплеск экран, чтобы показать перед всем этим:
поставить блок формы как можно ближе к верхней части .ДНР, как можете. (Но не раньше, чем вещи, которые должны идти первыми, как FastMM). Разместить код для отображения заставки в разделе инициализации этой группы. И убедитесь, что нет никаких блоков с длительными периодами инициализации, которые использует ваш заставка (или те, которые используют его... или где-нибудь в дереве зависимостей.) И тогда надеюсь, что это сработает.
Если проблемы замедления не начинаются до тех пор, пока не будет завершен начальный стек инициализации, а затем перейдите к тому, что сказал Джим.