2016-09-28 21 views
2

我正在使用我的Pascal脚本添加BASS Audio Project。为非常长的安装添加音乐播放功能对用户来说并不是什么坏事。但是,当用户将WizardForm最小化到任务栏时,停止音乐是一件好事。如果用户从任务栏再次恢复,则自动启动音乐。当WizardForm最小化并恢复时,Inno Setup Detect(得到通知)

我想知道如何检测WizardForm是否已最小化或恢复,并根据WizardForm的窗口状态暂停或开始BASS音乐播放。 (使用类似BASS_Pause,BASS_StopBASS_Start.的功能)

如何以及应该如何选择这样做? TWindowStateWMSYSCOMMAND?

在此先感谢。

回答

2

我不认为有任何事件会在向导窗体最小化或恢复时通知您。 Inno Setup Pascal脚本中也不提供TForm.WindowState


但你可以安排一个频繁的定时器(使用InnoCallback DLL),并检查的形式状态的变化。

请注意,当您单击最小化按钮(没有最小化动画)时,窗体实际上是隐藏的,而不是最小化。因此请使用GetWindowLong WinAPI function检查WS_VISIBLE窗口样式。

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

[Code] 

type 
    TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord); 

const 
    GWL_STYLE = -16; 
    WS_VISIBLE = $10000000; 

function GetWindowLong(hWnd: THandle; nIndex: Integer): LongInt; 
    external '[email protected] stdcall'; 
function SetTimer(
    hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; 
    external '[email protected] stdcall'; 

function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; 
    external '[email protected]:innocallback.dll stdcall'; 

var 
    WasHidden: Boolean; 

procedure HiddenTimerProc(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord); 
var 
    Hidden: Boolean; 
    Style: LongInt; 
begin 
    Style := GetWindowLong(WizardForm.Handle, GWL_STYLE); 
    Hidden := (Style and WS_VISIBLE) = 0; 
    if Hidden and not WasHidden then 
    begin 
    Log('Minimized, stopping music...'); 
    end 
    else 
    if not Hidden and WasHidden then 
    begin 
    Log('Restored, resuming music...'); 
    end; 
    WasHidden := Hidden; 
end; 

procedure InitializeWizard(); 
var 
    HiddenTimerCallback: LongWord; 
begin 
    HiddenTimerCallback := WrapTimerProc(@HiddenTimerProc, 4); 
    WasHidden := False; 
    SetTimer(0, 0, 500, HiddenTimerCallback); 
end; 
+0

该死!我也检查了你的答案。它也在工作。在我得到这个答案之前,我成功地使用了不使用Timer的代码,而是使用'InnoCallBack.dll'。如果我发布这个作为我的问题的更新,你可以检查它的任何错误的东西? – GTAVLover

+0

如果它适合您,请将其作为答案发布。我会看一看。 –

+0

好吧,我会尽快。 – GTAVLover

2

不幸的是,没有任何通知的事件,可以告诉你,如果WizardForm被最小化或恢复,因为这种情况下没有必要建造安装。

如果你真的想检查是否WizardForm恢复或最小化(不检查其可见性),


首先,你需要修改Inno Setup的源代码给最小化和还原转换(动画)的向导窗口。

注意:以下源代码更改已成功通过Inno Setup 5.5.9 Unicode和Ansi版本进行测试。

  1. 隐藏从创新安装程序的安装程序完全德尔福的隐藏的申请表:

    Setup.exe的的Setup.exe>项目选择>应用程序>目标文件扩展名> E32。

    右键单击Setup.e32>查看源代码。

    更改{ Initialize ...科设置程序中是这样的:

    ShowWindow(Application.Handle, SW_SHOW);之前添加if shWindowVisible in SetupHeader.Options then

    更改{ Run }科设置程序中是这样的:

    ... 
    { Run } 
    try 
        Application.MainFormOnTaskBar := False; 
        Application.ShowMainForm := False; 
        ShowWindow(Application.Handle, SW_HIDE); 
        Application.Run; 
    except 
    ... 
    

    大功告成!现在它将被隐藏。

    有关背景信息,请参阅Setup Programs created using Inno Setup Compiler doesn't display Minimize Animation

  2. 添加最小化和还原转换(动画),以创新安装向导形式:

    单位向导,

    变化TWizardForm.CreateParams这样的:

    procedure TWizardForm.CreateParams(var Params: TCreateParams); 
    begin 
        inherited; 
        { Ensure the form is on top of MainForm by making MainForm 
        the "parent" of the form when *MainForm is set to Visible*. } 
        if shWindowVisible in SetupHeader.Options then 
        Params.WndParent := MainForm.Handle 
        else 
        Params.WndParent := GetDesktopWindow; 
    end; 
    

    更改TWizardForm.WMSysCommand这样的:

    procedure TWizardForm.WMSysCommand(var Message: TWMSysCommand); 
    begin 
        if Message.CmdType and $FFF0 = SC_MINIMIZE then begin 
        { A minimize button is shown on the wizard form when (shWindowVisible in 
         SetupHeader.Options). When it is clicked we want to minimize the whole 
         application. } 
        if shWindowVisible in SetupHeader.Options then 
         Application.Minimize 
        else 
         ShowWindow(WizardForm.Handle, SW_MINIMIZE); 
        end 
        else 
        if Message.CmdType and $FFF0 = SC_RESTORE then begin 
        if shWindowVisible in SetupHeader.Options then 
         Application.Restore 
        else 
         ShowWindow(WizardForm.Handle, SW_RESTORE); 
        end; 
        if Message.CmdType = 9999 then 
        MainForm.ShowAboutBox 
        else 
        inherited; 
    end; 
    

    声明一个称为TWizardForm.FormShow如下所示的新程序:

    procedure FormShow(Sender: TObject); 
    

    声明它像下面在Implementation部单元向导。

    procedure TWizardForm.FormShow(Sender: TObject); 
    begin 
        if not(shWindowVisible in SetupHeader.Options) then 
        ShowWindow(Application.Handle, SW_HIDE); 
    end; 
    

    最后,添加此TWizardForm.FormShow为WizardForm的OnShow中形成该事件。

    你差不多完成了!现在,向导窗口应该会按照您的预期显示“还原”和“最小化动画”。

  3. 固定MessageBox的父窗口的问题将最小化转换(动画)的创新安装向导后:

    注:必须这样做,以防止已记录向导消息框(其可以用Inno Setup的编译器登录日志)从有时显示两个任务栏按钮甚至WizardForm是可见的。

    单位主要的部分{ Variables for command line parameters },宣告一个新的布尔变量是这样的:

    IsApplicationRunning: Boolean; 
    

    单位主,

    更改程序AbortInit是这样的:

    procedure AbortInit(const Msg: TSetupMessageID); 
    begin 
        IsApplicationRunning := False; 
        LoggedMsgBox(SetupMessages[Msg], '', mbCriticalError, MB_OK, True, IDOK); 
        Abort; 
    end; 
    

    更改程序AbortInitFmt1这样的:

    procedure AbortInitFmt1(const Msg: TSetupMessageID; const Arg1: String); 
    begin 
        IsApplicationRunning := False; 
        LoggedMsgBox(FmtSetupMessage(Msg, [Arg1]), '', mbCriticalError, MB_OK, True, IDOK); 
        Abort; 
    end; 
    

    更改程序AbortInitServicePackRequired这样的:

    procedure AbortInitServicePackRequired(const ServicePack: Word); 
    begin 
        IsApplicationRunning := False; 
        LoggedMsgBox(FmtSetupMessage(msgWindowsServicePackRequired, ['Windows', IntToStr(Hi(ServicePack))]), '', mbCriticalError, MB_OK, True, IDOK); 
        Abort; 
    end; 
    

    在设备向导,

    修改先前添加的程序TWizardForm.FormShow这样的:

    procedure TWizardForm.FormShow(Sender: TObject); 
    begin 
        if not(shWindowVisible in SetupHeader.Options) then 
        ShowWindow(Application.Handle, SW_HIDE); 
        IsApplicationRunning := True; 
    end; 
    

    单位CmnFunc,

    添加WizardMain的用途科单位CmnFunc的Implementation

    更改程序AppMessageBox这样的:

    function AppMessageBox(const Text, Caption: PChar; Flags: Longint): Integer; 
    var 
        ActiveWindow: HWND; 
        MessageHandler: HWND; 
        WindowList: Pointer; 
    {$IFNDEF IS_D4} 
        DidMove: Boolean; 
        OldRect: TRect; 
    {$ENDIF} 
    begin 
        if MessageBoxRightToLeft then 
        Flags := Flags or (MB_RTLREADING or MB_RIGHT); 
    
        if IsApplicationRunning = False then 
        MessageHandler := Application.Handle 
        else 
        MessageHandler := WizardForm.Handle; 
    
        { If the application window isn't currently visible, show the message box 
        with no owner window so it'll get a taskbar button } 
        if IsIconic(MessageHandler) or (GetWindowLong(MessageHandler, GWL_STYLE) and WS_VISIBLE = 0) or (GetWindowLong(MessageHandler, GWL_EXSTYLE) and WS_EX_TOOLWINDOW <> 0) then begin 
        ActiveWindow := GetActiveWindow; 
        WindowList := DisableTaskWindows(0); 
        try 
         { Note: DisableTaskWindows doesn't disable invisible windows. 
         MB_TASKMODAL will ensure that Application.Handle gets disabled too. } 
         Result := MessageBox(0, Text, Caption, Flags or MB_TASKMODAL); 
        finally 
         EnableTaskWindows(WindowList); 
         SetActiveWindow(ActiveWindow); 
        end; 
        Exit; 
        end; 
    
        TriggerMessageBoxCallbackFunc(Flags, False); 
        try 
        {$IFDEF IS_D4} 
        { On Delphi 4+, simply call Application.MessageBox } 
        Result := Application.MessageBox(Text, Caption, Flags); 
        {$ELSE} 
        { Use custom implementation on Delphi 2 and 3. The Flags parameter is 
         incorrectly declared as a Word on Delphi 2's Application.MessageBox, and 
         there is no support for multiple monitors. } 
        DidMove := MoveAppWindowToActiveWindowMonitor(OldRect); 
        try 
         ActiveWindow := GetActiveWindow; 
         WindowList := DisableTaskWindows(0); 
         try 
         Result := MessageBox(Application.Handle, Text, Caption, Flags); 
         finally 
         EnableTaskWindows(WindowList); 
         SetActiveWindow(ActiveWindow); 
         end; 
        finally 
         if DidMove then 
         SetWindowPos(Application.Handle, 0, 
          OldRect.Left + ((OldRect.Right - OldRect.Left) div 2), 
          OldRect.Top + ((OldRect.Bottom - OldRect.Top) div 2), 
          0, 0, SWP_NOACTIVATE or SWP_NOREDRAW or SWP_NOSIZE or SWP_NOZORDER); 
        end; 
        {$ENDIF} 
        finally 
        TriggerMessageBoxCallbackFunc(Flags, True); 
        end; 
    end; 
    

    现在,所有的登录和任何其他消息框将正常显示!

    现在,使用自述文件中的推荐编译器编译安装程序(Setup.e32),并将其复制到安装Inno Setup的目录中。

    注意:Inno Setup Unicode或Ansi的安装版本必须与您修改为重新编译的Inno Setup源代码的版本相匹配。或者您可以编译整个Inno Setup项目并复制 - 将此修改后的和重新编译的安装程序(Setup.e32)替换为编译Inno Setup的目录。

    重新编译安装程序后,您需要将以下代码添加到Pascal脚本中。立即现在

    [Files] 
    Source: "InnoCallback.dll"; Flags: dontcopy 
    
    [Code] 
    #ifdef UNICODE 
        #define AW "W" 
    #else 
        #define AW "A" 
    #endif 
    
    const 
        GWL_WNDPROC = -4; 
        SC_ABOUTBOX = 9999; 
        SC_RESTORE = $F120; 
        SC_MINIMIZE = $F020; 
        WM_SYSCOMMAND = $0112; 
    
    Type 
        WPARAM = UINT_PTR; 
        LPARAM = LongInt; 
        LRESULT = LongInt; 
        TWindowProc = function(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; 
    
    var 
        PrevWndProc: LongInt; 
    
    function CallWindowProc(lpPrevWndFunc: LongInt; hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; 
        external 'CallWindowProc{#AW}@user32.dll stdcall'; 
    function WrapWindowProc(Callback: TWindowProc; ParamCount: Integer): LongWord; 
        external '[email protected]:InnoCallback.dll stdcall'; 
    function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: LongInt): LongInt; 
        external 'SetWindowLong{#AW}@user32.dll stdcall'; 
    
    function Wizard_WMSYSCOMMAND(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; 
    begin 
        if (uMsg = WM_SYSCOMMAND) and (wParam and $FFF0 = SC_MINIMIZE) then begin 
        //SOMETHING LIKE BASS_Pause();. 
        Log('Wizard Window has been Minimized.'); 
        end; 
        if (uMsg = WM_SYSCOMMAND) and (wParam and $FFF0 = SC_RESTORE) then begin 
        //SOMETHING LIKE BASS_Start();. 
        Log('Wizard Window has been Restored.'); 
        end; 
        if (uMsg = WM_SYSCOMMAND) and (wParam = SC_ABOUTBOX) then begin 
        Result := 0; 
        MainForm.ShowAboutBox; 
        end 
        else 
        Result := CallWindowProc(PrevWndProc, hwnd, uMsg, wParam, lParam); 
    end; 
    
    procedure InitializeWizard(); 
    begin 
        PrevWndProc := SetWindowLong(WizardForm.Handle, GWL_WNDPROC, WrapWindowProc(@Wizard_WMSYSCOMMAND, 4)); 
    end; 
    
    procedure DeinitializeSetup(); 
    begin 
        SetWindowLong(WizardForm.Handle, GWL_WNDPROC, PrevWndProc); 
    end; 
    

    ,你应当通知时,通过编译器日志消息WizardForm最大限度地减少或恢复。您可以通过在功能Wizard_WMSYSCOMMAND中添加它们来根据您的代码暂停或恢复音乐。

+0

这很好.....一切都很好! – Blueeyes789

+0

努力思考是寻找解决方案的最重要途径。 ;-) – GTAVLover

+0

在'Wizard_WMSYSCOMMAND'中,你应该用'$ FFF0'来掩盖'wParam',就像'TWizardForm.WMSysCommand'一样。 –

相关问题