2016-09-28 22 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'一樣。 –

相關問題