Я хочу отображать заставку во время загрузки приложения. Однако некоторые сторонние компоненты блокируют основной поток во время инициализации на несколько секунд, из-за чего все формы не обновляются. Можно ли иметь заставку с собственным потоком, чтобы он обновлялся также, когда основной поток занят?
Это приложение win32 и Delphi версии 2007.
Обновлено: я пытаюсь избежать эффекта «невырисованного экрана-заставки», который происходит, если некоторые другие окна (из других приложений) находятся в верхней части экрана-заставки, например, переход с помощью alt-tab в другое приложение и обратно.





Я создаю заставку в коде запуска, всегда сверху, а затем использую frmSplash.Update в соответствующих местах, чтобы убедиться, что он виден и обновлен. Основная форма create - одно из таких мест, чтобы ее назвать.
Проблема в том, что Delphi 2007 предполагает, что первая форма теперь является основной, и нет возможности заменить основную форму в основном коде, поэтому брызги уже не так хороши. Возможно, старое базовое визуальное решение с небольшим приложением-заставкой, которое затем запускает основное приложение, может оказаться лучше!
Проблема в том, что frmSplash.Update мало помогает, если основной поток заблокирован в течение длительного периода времени. Мне нужен экран-заставка, чтобы сообщить, что «да, эта программа запущена, пожалуйста, подождите»
Проблема блокировки основного потока не решается запуском экрана-заставки в отдельном потоке, потому что ему потребуется основной поток для обновлений экрана.
Если заставка не меняется, это не проблема.
Возможно, вам стоит обратиться к стороннему поставщику компонентов, потому что такой длинный блок - настоящая проблема.
Использование вторичного потока для всплеска решит проблему. К сожалению, в этом случае об использовании VCL не может быть и речи. Но вторичные потоки пользовательского интерфейса со своим собственным насосом сообщений возможны с использованием необработанных вызовов Windows API.
Мне нужен "живой" экран-заставка, чтобы указать, что приложение действительно запущено.
Сначала создайте заставку в 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 больше не вызывал release. Release в DRP выполняется на случай, если ваша основная форма не может создать.)
Поскольку ваша форма заставки - FormStyle: = fsStayOnTop, не должно быть проблемой, что она не получает сообщения рисования, когда ваш основной поток блокируется. Затем, когда основной поток разблокируется, вы отправляете ему сообщение об обновлении (для изменения индикатора выполнения и т. д.). Хотя я согласен с Gamecat, что вы можете связаться со сторонними поставщиками компонентов и заставить их перестать блокировать основной поток для вас.
В качестве альтернативы вы можете создать свои сторонние компоненты в отдельном потоке (при условии, что они не являются визуальными, поскольку это будет немного сложнее).
Это будет работать и с Application.MainFormOnTaskBar, установленным в true.
> Поскольку ваша форма-заставка - FormStyle: = fsStayOnTop, не должно быть проблемой, что она не получает сообщения отрисовки, когда ваш основной поток блокируется. <br> Я не согласен, переход к другому приложению и обратно приведет к появлению экрана-заставки признать недействительным.
Мне действительно нужен "живой" экран, поэтому важно получать эти сообщения с краской.
@mghie Полагаю, технически верно.
Вы можете запустить заставку в другом потоке, но тогда вам нужно будет использовать необработанные вызовы Windows API или стороннюю библиотеку (например, Библиотека ключевых объектов), которая реализует классы, подобные VCL. Однако не обращайтесь к материалам VCL из потока-заставки.
Если вы пойдете по этому пути (что я не думаю, что вам следует, поскольку это большая работа с небольшой прибылью), обязательно соблюдайте правила доступа к Windows API из нескольких потоков. Google, например, для "потоков пользовательского интерфейса" для получения дополнительной информации.
Редактировать:
Я не знал об этом раньше, но на самом деле есть компонент, реализующий Резьбовой экран-заставка для Delphi на CodeCentral. Используя этот компонент, может быть (не пробовал) на самом деле легко получить заставку в другом потоке, но предупреждение о доступе к VCL из вторичных потоков остается.
У Джима МакКита есть отличная идея, но он не затрагивает одну вещь, которая может быть, а может и не быть проблемой. Вы говорите о компонентах, для инициализации которых требуется много времени. Под этим вы имеете в виду раздел инициализация или что-то, что происходит позже, например, при создании ваших форм? Потому что все разделы инициализации выполняются до запуска любого кода в DPR. Если эта часть занимает много времени, вам придется проделать несколько хитрых вещей, чтобы заставка отображалась поверх всего экрана:
Поместите блок формы как можно ближе к верхнему краю .DPR. (Но не перед тем, что нужно сделать в первую очередь, например, FastMM). Поместите код для отображения заставки в разделе инициализации этого устройства. И убедитесь, что на вашем экране-заставке нет модулей с длинными периодами инициализации (или что те, которые его используют ... или где-нибудь в дереве зависимостей). И затем надейтесь, что это сработает.
Если же проблемы с замедлением не возникают до тех пор, пока не будет завершен стек начальной инициализации, тогда следуйте тому, что сказал Джим.
Раздел инициализации не является проблемой, долгое ожидание происходит, когда я устанавливаю компонент активным в FormCreate (или позже).
На самом деле способ 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, который автоматически генерирует включаемый файл).
Что лучше в этом способе по сравнению с трюками VCL (упорядочение создания форм и т. д.), Так это то, что вы можете использовать его несколько раз, когда вашему приложению нужно время, чтобы подумать.
Формы заставки работают в Delphi 2007 так же, как и в предыдущих версиях, когда вы создаете их с помощью строки SplashForm: = TSplashForm.Create (nil); и позволь им освободиться. Первый Application.CreateForm () устанавливает Application.MainForm, но это всегда работало таким образом, AFAIK.