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