Подтвердить что ты не робот

Отображение заставки в Delphi, когда основной поток занят

Я хочу отображать заставку во время загрузки приложения. Однако некоторые сторонние компоненты блокируют основной поток во время инициализации в течение нескольких секунд, что заставляет все формы не обновляться. Возможно ли иметь заставку с собственным потоком, чтобы он также обновлялся, когда основной поток занят?

Приложением является win32 и версия Delphi 2007.

Изменить: я стараюсь избегать эффекта "неиспользованный эффект заставки", который происходит, если некоторые другие окна (из других приложений) находятся на верхней части экрана заставки, например, alt-tabbing в другое приложение и обратно.

4b9b3361

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

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