Вопрос Как сделать таймер

Chopser

Новичок
Помогите кто нибудь.
Как сделать таймер перед перезагрузкой компьютера на последней странице или в отдельном сообщении?
Подробнее: это нужно для атоматической установки .NET Framework и других системных компонентов. Выглядит это примерно так: чтобы в конце установки, на странице "Завершения" или в отдельном сообщении - показало таймер, а по истечении таймера происходила автоматическая перезагрузка компьютера.

Таймер можно или на кнопке или на форме, если на форме, то кнопку скрыть. В качестве кода перезагрузки, можно просто добавить код запуска файла по истечении таймера.
 

Nemko

Дилетант
Модератор
Вот, может поможет пример:

Код:
[Setup]
AppName=My Program
AppVerName=My Program v.1.2
DefaultDirName={pf}\My Program

[Files]
Source: InnoCallback.dll; Flags: dontcopy

[Code]
type
  TTimerProc = procedure(h:longword; msg:longword; idevent:longword; dwTime:longword);
 
var
  Timer: LongWord;
  Vektor: Integer;

function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer@user32.dll stdcall';
function WrapTimerProc(callback: TTimerProc; Paramcount: Integer): Longword; external 'wrapcallback@files:innocallback.dll stdcall';
function KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external 'KillTimer@user32.dll stdcall';

procedure TimerProc(h: Longword; msg: Longword; idevent: Longword; dwTime: Longword);
begin
  Vektor:=Vektor-1;
  WizardForm.NextButton.Caption:=IntToStr(Vektor);
If Vektor = 0 then begin
  KillTimer(0, Timer);
  WizardForm.NextButton.Enabled:=True;
  WizardForm.NextButton.Caption:=('Далее >');
end;
end;

procedure CurPageChanged(CurPageID: Integer);
var
  pfunc: LongWord;
begin
if CurPageID = wpWelcome then
begin
  Timer:=SetTimer(0, 0, 1000, WrapTimerProc(@TimerProc,4));
  WizardForm.NextButton.Enabled:=False;
  Vektor:=6;
end else
  KillTimer(0, Timer);
end;

procedure DeinitializeSetup();
begin
  KillTimer(0, Timer);
end;

P.S: Понимаю что это на странице приветствия, но можно адаптировать на другую станицу.
 

EvilAlex

Старожил
Вот, может поможет пример:

Код:
[Setup]
AppName=My Program
AppVerName=My Program v.1.2
DefaultDirName={pf}\My Program

[Files]
Source: InnoCallback.dll; Flags: dontcopy

[Code]
type
  TTimerProc = procedure(h:longword; msg:longword; idevent:longword; dwTime:longword);
 
var
  Timer: LongWord;
  Vektor: Integer;

function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer@user32.dll stdcall';
function WrapTimerProc(callback: TTimerProc; Paramcount: Integer): Longword; external 'wrapcallback@files:innocallback.dll stdcall';
function KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external 'KillTimer@user32.dll stdcall';

procedure TimerProc(h: Longword; msg: Longword; idevent: Longword; dwTime: Longword);
begin
  Vektor:=Vektor-1;
  WizardForm.NextButton.Caption:=IntToStr(Vektor);
If Vektor = 0 then begin
  KillTimer(0, Timer);
  WizardForm.NextButton.Enabled:=True;
  WizardForm.NextButton.Caption:=('Далее >');
end;
end;

procedure CurPageChanged(CurPageID: Integer);
var
  pfunc: LongWord;
begin
if CurPageID = wpWelcome then
begin
  Timer:=SetTimer(0, 0, 1000, WrapTimerProc(@TimerProc,4));
  WizardForm.NextButton.Enabled:=False;
  Vektor:=6;
end else
  KillTimer(0, Timer);
end;

procedure DeinitializeSetup();
begin
  KillTimer(0, Timer);
end;

P.S: Понимаю что это на странице приветствия, но можно адаптировать на другую станицу.
А как сделать, чтобы после установки игры, инсталлятор закрывался сам по таймеру, допустим через 10 секунд и выдавал сообщение что все установлено нажмите ок?
ты такой пришел увидел. нажал ок и сел играть)

заранее спасибо...
 

nik1967

Old Men
Проверенный
А типа ты такой пришёл, увидел финиш пэйдж и на ней всё ок, нажал на "Завершить", или как у нас "Начать игру" и сел играть) Не? К чему лишний геммор? Хотя всё это реализуемо.
Установка TVoEKR2.png
 
Последнее редактирование:

Nemko

Дилетант
Модератор
EvilAlex, может так подойдет:

Код:
[Setup]
AppName=My Program
AppVerName=My Program v.1.2
DefaultDirName={pf}\My Program

[Files]
Source: InnoCallback.dll; Flags: dontcopy

[Code]
type
  TTimerProc = procedure(h:longword; msg:longword; idevent:longword; dwTime:longword);

var
  Timer: LongWord;
  Vektor: Integer;

function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer@user32.dll stdcall';
function WrapTimerProc(callback: TTimerProc; Paramcount: Integer): Longword; external 'wrapcallback@files:innocallback.dll stdcall';
function KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external 'KillTimer@user32.dll stdcall';

procedure TimerProc(h: Longword; msg: Longword; idevent: Longword; dwTime: Longword);
begin
  Vektor:=Vektor-1;
  WizardForm.NextButton.Caption:=IntToStr(Vektor);
If Vektor = 0 then begin
  KillTimer(0, Timer);
  WizardForm.NextButton.Enabled:=True;
  WizardForm.NextButton.Caption:=('');
  WizardForm.Hide;
  MsgBox('Программа ' + '{#SetupSetting("AppName")}' + '  установлена!', mbInformation, mb_Ok);
  WizardForm.NextButton.OnClick(WizardForm.NextButton); 
end;
end;

procedure CurPageChanged(CurPageID: Integer);
var
  pfunc: LongWord;
begin
if CurPageID = wpFinished then
begin
  Timer:=SetTimer(0, 0, 1000, WrapTimerProc(@TimerProc,4));
  WizardForm.NextButton.Enabled:=False;
  Vektor:=6;
end else
  KillTimer(0, Timer);
end;

procedure DeinitializeSetup();
begin
  KillTimer(0, Timer);
end;
 

AlexS

Новичок
Мужики, выручайте... Второй день голову ломаю. Пытаюсь сделать следующее...
То есть закрытие формы по обнулению таймера. Сначала пытался прикрутить таймер к MsgBox но, как потом выяснилось, это не возможно. Создал форму и все прочее, но выявилась проблемка. Таймер отсчитывает время с очень коротким интервалом. Варианты со сторонними библиотеками у меня есть, но хочется без них обойтись. Как решить задачу нормального отсчета?
[Setup]
AppName=Custom Form
AppVersion=1.0
DefaultDirName={pf}\Custom Form
DisableFinishedPage=yes

[Languages]
Name: "RU"; MessagesFile: "compiler:Languages\Russian.isl"

[Files]
Source: InnoSetup.ico; DestDir: {tmp}; Flags: dontcopy;

[*Code]
var
i:integer;
Timer1: TTimer;
Form: TSetupForm;
FormLabel: TLabel;
OkButton: TNewButton;
FormClose: Boolean;
IconImg: TNewIconImage;

procedure MyOnTimer(Sender: TObject);
begin
i:=i-1; // отнимаем по одной сек.
OkButton.Caption := 'Ок' + #32 + #40+IntToStr(i)+ #99 +#41;
if i=0 then
begin
Timer1.Enabled:=false;
Form.Close;
end;
end;
function CreateTimer(): Boolean;
begin
i:=10; // отсчет начинать с 10 сек.
Timer1 := TTimer.Create(Form)
Timer1.Interval := 1000; // интервал 1 сек.
Timer1.OnTimer := @MyOnTimer;
Timer1.Enabled := true;
end;

procedure FormButtonClick(Sender: TObject);
begin
case TNewButton(Sender) of
OkButton: FormClose := True;
end;
Form.Close;
end;

function ShowCancelMessage(): Boolean;
begin
Form := CreateCustomForm();
with Form do
begin
ClientWidth := ScaleX(300);
ClientHeight := ScaleY(100);
Caption := SetupMessage(msgStatusRunProgram);
CenterInsideControl(WizardForm, False);
end;

FormLabel := TLabel.Create(Form);
with FormLabel do
begin
Parent := Form;
SetBounds(ScaleX(60), ScaleY(15), ScaleX(280), ScaleY(80));
Caption := ' Программа установлена.';
Font.Height := -12;
end;

OkButton := TNewButton.Create(Form);
with OkButton do
begin
Parent := Form;
SetBounds(ScaleX(110), ScaleY(70), ScaleX(75), ScaleY(23));
OnClick := @FormButtonClick;
Caption := 'Ок' + #32 + #40+IntToStr(i)+ #99 +#41;
end;

IconImg := TNewIconImage.Create(Form);
with IconImg do
begin
Parent := Form;
SetBounds(ScaleX(5), ScaleY(12), ScaleX(32), ScaleY(32));
ExtractTemporaryFile('InnoSetup.ico');
Icon.LoadFromFile(ExpandConstant('{tmp}\InnoSetup.ico'));
end;

Form.ShowModal;
Result := FormClose;
Form.Free;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
if (CurStep = ssDone) then
if not wizardsilent then
ShowCancelMessage;
CreateTimer;
end;
 

Вложения

Последнее редактирование:

nik1967

Old Men
Проверенный
А не проще использовать?
Код:
function MsgBoxEx(AWnd: HWND; AText, ACaption: string; AType, AIcon: UINT; ATimeOut: Integer): Integer;
Код:
#ifndef IS_ENHANCED
  #error Enhanced edition of Inno Setup (restools) is required to compile this script
#endif

[Setup]
AppName=exit v1.0
AppVerName=exit v1.0
OutputDir=.
CreateAppDir=no
CreateUninstallRegKey=no

[Code]

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
  Confirm:= false;
  Cancel:= false;
  if MsgBoxEx(WizardForm.Handle, SetupMessage(msgExitSetupMessage), SetupMessage(msgExitSetupTitle), MB_YESNO or MB_ICONINFORMATION, 0, 10) = IDYES then Cancel:= true;
end;
 

MISHAWIN

Новичок
Как что-то сделать параллельно, как я понимаю есть главный поток, это наша программа и можно вызывать параллельный поток в которым можно будет что-то делать, типа функцию в параллельном потоке? Не подскажите как будет выглядеть такая функция, функция которая будет выполнятся параллельно не тормозящая сановной поток?
К примеру изменять текст один на другой каждую 0.1 секунду.
 

Nemko

Дилетант
Модератор
MISHAWIN, не знаю какая версия Inno есть, вот пример на Inno EE. Таймер - повторяющаяся функция по времени (типа цикла), а callback функция для перехвата тех самых сообщений к окнам Windows\процессам с последующей их обработкой в коде.
Думаю, уместно использовать Timer, можно сделать вызов WinApi функций и на оригинальный Inno, используйте в место CallbackAddr - CreateCallBack, удачи. надеюсь понятно, а то я уставший сегодня... может отжег чушь, как обычно :D
Код:
[Setup]
AppName=My Program
AppVerName=My Program
DefaultDirName=My Program

[Code]
var
  TimerID: LongWord;

function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer@user32.dll stdcall';
function KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external 'KillTimer@user32.dll stdcall';

procedure OnTimer(HandleW, msg, idEvent, TimeSys: LongWord);
begin
  with WizardForm do begin
    Caption:= Copy(Caption, Length(Caption), 1) + Copy(Caption, 1, Length(Caption)-1);
  end;
end;

procedure InitializeWizard;
begin
  WizardForm.Caption:= '> - - - - - - - - - - - - - - -';
  TimerID:= SetTimer(0, 0, 100, CallbackAddr('OnTimer'));
end;

procedure DeinitializeSetup;
begin
  KillTimer(0, TimerID)
end;
 
Последнее редактирование:

MISHAWIN

Новичок
MISHAWIN, не знаю какая версия Inno есть, вот пример на Inno EE. Таймер - повторяющаяся функция по времени (типа цикла), а callback функция для перехвата тех самых сообщений к окнам Windows\процессам с последующей их обработкой в коде.
Думаю, уместно использовать Timer, можно сделать вызов WinApi функций и на оригинальный Inno, используйте в место CallbackAddr - CreateCallBack, удачи. надеюсь понятно, а то я уставший сегодня... может отжег чушь, как обычно :D
Код:
[Setup]
AppName=My Program
AppVerName=My Program
DefaultDirName=My Program

[Code]
var
  TimerID: LongWord;

function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer@user32.dll stdcall';
function KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external 'KillTimer@user32.dll stdcall';

procedure OnTimer(HandleW, msg, idEvent, TimeSys: LongWord);
begin
  with WizardForm do begin
    Caption:= Copy(Caption, Length(Caption), 1) + Copy(Caption, 1, Length(Caption)-1);
  end;
end;

procedure InitializeWizard;
begin
  WizardForm.Caption:= '> - - - - - - - - - - - - - - -';
  TimerID:= SetTimer(0, 0, 100, CallbackAddr('OnTimer'));
end;

procedure DeinitializeSetup;
begin
  KillTimer(0, TimerID)
end;
Спасибо за ответ, у меня 5.5.1 ee2, таймер работает, буду смотреть и разбираться.
 

Leserg

Участник
Как что-то сделать параллельно, как я понимаю есть главный поток, это наша программа и можно вызывать параллельный поток в которым можно будет что-то делать, типа функцию в параллельном потоке? Не подскажите как будет выглядеть такая функция, функция которая будет выполнятся параллельно не тормозящая сановной поток?
К примеру изменять текст один на другой каждую 0.1 секунду.
Для таких задач можно использовать метод Application.ProcessMessage.

Если вы пользуетесь сборкой от ResTools, то смотрите пример в файле Example_Application.iss, где показано использование этого метода.

Официальная версия Inno не поддерживает метод Application.ProcessMessage. В этом случае его можно заменить функциями WinAPI.
Код замещения взят с ресурса stackoverflow.

Вот пример на базе Example_Application.iss, но для официальной версии Inno Setup.
Код:
[Setup]
AppName=My Application
AppVersion=1.5
DefaultDirName={autopf}\My Application
PrivilegesRequired=none
OutputDir=userdocs:Inno Setup Examples Output
DisableWelcomePage=no

[Languages]
Name: ru; MessagesFile: "compiler:Languages\Russian.isl"

[Code]
// --- Замещение метода "Application.ProcessMessage"
{
   Официальная версия InnoSetup не предоставляет метод Application.ProcessMessage().
   Код, представленный ниже, выполняет замещение метода "Application.ProcessMessages"
   путём использования WinAPI функций PeekMessage(), TranslateMessage() и DispatchMessage().
}
type
  TMsg = record
    hwnd: HWND;
    message: UINT;
    wParam: Longint;
    lParam: Longint;
    time: DWORD;
    pt: TPoint;
  end;

const
  PM_REMOVE = 1;

function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL;
 external 'PeekMessageA@user32.dll stdcall';
function TranslateMessage(const lpMsg: TMsg): BOOL;
 external 'TranslateMessage@user32.dll stdcall';
function DispatchMessage(const lpMsg: TMsg): Longint;
 external 'DispatchMessageA@user32.dll stdcall';

procedure AppProcessMessage;
var
  Msg: TMsg;
begin
  while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
end;

// --- Конец замещения "Application.ProcessMessage"


{ Процедура обработки нажатия на кнопку }
procedure BtnOnClick(Sender: TObject);
var
  i: Integer;
begin
  For i := 0 to 50000 do
  begin
    WizardForm.Caption := 'Счетчик: ' + IntToStr(i);
    { Использование функции "AppProcessMessage" позволяет продолжить
      выполнение программы установки несмотря на запущенную работу счетчика.
      Иначе, пока не будет завершён текущий цикл, ничего нельзя будет сделать
      (приложение будет выглядеть "зависшим").}
    AppProcessMessage;
  end;
end;

{ Функция инициализации GUI программы установки }
procedure InitializeWizard();
begin
  { Добавляем на форму кнопку }
  with TButton.Create(WizardForm) do
  begin
    Caption := 'Запустить счетчик';
    Anchors := [akLeft, akBottom];
    Left := WizardForm.ClientWidth - WizardForm.CancelButton.Left - WizardForm.CancelButton.Width;
    Top := WizardForm.CancelButton.Top;
    Width := ScaleX(130);
    Height := WizardForm.CancelButton.Height;
    Parent := WizardForm;
    OnClick := @BtnOnClick;
  end;
end;
 

MISHAWIN

Новичок
Для таких задач можно использовать метод Application.ProcessMessage.

Если вы пользуетесь сборкой от ResTools, то смотрите пример в файле Example_Application.iss, где показано использование этого метода.

Официальная версия Inno не поддерживает метод Application.ProcessMessage. В этом случае его можно заменить функциями WinAPI.
Код замещения взят с ресурса stackoverflow.

Вот пример на базе Example_Application.iss, но для официальной версии Inno Setup.
Код:
[Setup]
AppName=My Application
AppVersion=1.5
DefaultDirName={autopf}\My Application
PrivilegesRequired=none
OutputDir=userdocs:Inno Setup Examples Output
DisableWelcomePage=no

[Languages]
Name: ru; MessagesFile: "compiler:Languages\Russian.isl"

[Code]
// --- Замещение метода "Application.ProcessMessage"
{
   Официальная версия InnoSetup не предоставляет метод Application.ProcessMessage().
   Код, представленный ниже, выполняет замещение метода "Application.ProcessMessages"
   путём использования WinAPI функций PeekMessage(), TranslateMessage() и DispatchMessage().
}
type
  TMsg = record
    hwnd: HWND;
    message: UINT;
    wParam: Longint;
    lParam: Longint;
    time: DWORD;
    pt: TPoint;
  end;

const
  PM_REMOVE = 1;

function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL;
external 'PeekMessageA@user32.dll stdcall';
function TranslateMessage(const lpMsg: TMsg): BOOL;
external 'TranslateMessage@user32.dll stdcall';
function DispatchMessage(const lpMsg: TMsg): Longint;
external 'DispatchMessageA@user32.dll stdcall';

procedure AppProcessMessage;
var
  Msg: TMsg;
begin
  while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
end;

// --- Конец замещения "Application.ProcessMessage"


{ Процедура обработки нажатия на кнопку }
procedure BtnOnClick(Sender: TObject);
var
  i: Integer;
begin
  For i := 0 to 50000 do
  begin
    WizardForm.Caption := 'Счетчик: ' + IntToStr(i);
    { Использование функции "AppProcessMessage" позволяет продолжить
      выполнение программы установки несмотря на запущенную работу счетчика.
      Иначе, пока не будет завершён текущий цикл, ничего нельзя будет сделать
      (приложение будет выглядеть "зависшим").}
    AppProcessMessage;
  end;
end;

{ Функция инициализации GUI программы установки }
procedure InitializeWizard();
begin
  { Добавляем на форму кнопку }
  with TButton.Create(WizardForm) do
  begin
    Caption := 'Запустить счетчик';
    Anchors := [akLeft, akBottom];
    Left := WizardForm.ClientWidth - WizardForm.CancelButton.Left - WizardForm.CancelButton.Width;
    Top := WizardForm.CancelButton.Top;
    Width := ScaleX(130);
    Height := WizardForm.CancelButton.Height;
    Parent := WizardForm;
    OnClick := @BtnOnClick;
  end;
end;
Спасибо за ответ, вариант Nemko мне вполне подошел, вроде работает как надо.
 

MISHAWIN

Новичок
Помогите разобраться, не понимаю почему код не работает, Page10 это страница перед wpInstalling по идеи после нажатия кнопки далее, должен запускается таймер, проверять условия, как только загрузка будет >10, вывести сообщение 1-н раз и всё, но оно спамит сообщениями, что делаю не так?
Код:
procedure MyOnTimer(Sender: TObject);
var
  curValue: Integer;
    Blok1: Boolean;
begin
  with WizardForm.ProgressGauge do begin
     curValue:= (Position-Min)/((Max - Min)/100);
  end;
  if (curValue > 10) and (curValue < 19) and Blok1=false then begin
   Blok1:= true;
   MsgBox('Cообщение', mbError, mb_Ok);
  end;
end;
Код:
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = Page10.ID then begin
 with TTimer.Create(WizardForm) do
  begin
    Interval := 10;
    OnTimer := @MyOnTimer;
  end;
end;
end;
 

Nemko

Дилетант
Модератор
MISHAWIN, переменную типа Boolean (Blok1) нужно сделать глобальной, а не локальной. Проще говоря, объявить ее вне тела процедуры\функции, например в самом начале секции Code.
 

MISHAWIN

Новичок
Сделал глобальной, но не помогло, сделал ещё так и заработало.
if (curValue > 10) and (curValue < 19) and (Blok1=false) then begin
 
Сверху