Отображение заставки в 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 автоматически.)

что мы получаем под XP

что лучше таким образом по сравнению с трюками 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). Разместить код для отображения заставки в разделе инициализации этой группы. И убедитесь, что нет никаких блоков с длительными периодами инициализации, которые использует ваш заставка (или те, которые используют его... или где-нибудь в дереве зависимостей.) И тогда надеюсь, что это сработает.

Если проблемы замедления не начинаются до тех пор, пока не будет завершен начальный стек инициализации, а затем перейдите к тому, что сказал Джим.