NSIS進階教程(一)~(五)

這問是在學NSIS的時候發現的,一篇絕頂好文,當時沒轉過來,現在美化完程序了,想着不美化安裝包也太不厚道了點。。。所以。。。

NSIS進階教程(一)

來自:http://www.pylife.net/post/2012-06-12/40027112705

自定義界面之無邊框窗體移動貼圖

**前言**

在Windows下,有很多人想做一個完全自己把控的安裝程序,想過很多種途徑去實現,有人說MFC可以實現,有人說C#可以實現,有人說Delphi可以實現,有人說VB又未嘗不可呢。MFC,Delphi,VB,C#都需要自己去實現打包壓縮,釋放,釋放過程中的業務邏輯跟界面功能,是一項比較麻煩的工作,甚至於C#程序需要運行的話,還需要裝dotnet Framework的runtime。NSIS製作的安裝包可以運行在Win9x下,完全是WinAPI的調用,不需要額外裝任何的runtime,安裝包雙擊就能運行,本身封裝了很多Win的函數,方便調用與開放接口。功能部分也是實現了基本的安裝過程所需的操作,NSIS的很多Editor做到了嚮導模式的腳本生成,很是方便。

這麼好的工具能否定製開發呢,答案是肯定的。

本篇主要講講以下幾點:

  1. 如何消除普通的NSIS腳本生成的窗體的邊框

  2. 如何使得無邊框窗體能夠移動

  3. 如何給這個窗體貼上一張大小合適的背景圖

所用到的NSIS插件:

  • nsDialogs

  • nsWindows

  • WinProc

  • System

**講義**:

首先貼出一個今天教程的完整的例子(附帶圖片) 猛擊這裏

題外話,本來想用新浪愛問做文件分享平臺的,發現上傳後一直在審覈中……練葵花寶典能力誰也比過性浪呀,用CSDN也不好,還要登錄,本人就因爲積分太少而不得不去做無聊的工作贏得積分,用於CSDN下載,自從CSDN把我的密碼明碼保存還被黑客給搞了之後,我不再上此網站。115雖然下載頁廣告多的一筆,但是後臺上傳頁相當的乾淨,還不用審覈以及無登陸下載,極致方便大家。(115被政府搞了,轉性浪愛問)

使用時:把插件DLL跟頭文件分別放入到你的NSIS本地對應的安裝目錄中,然後編譯源碼即可。

下文都用%NSIS_Install_DIR%來替代你本地安裝路徑

  1. 去除窗體Border

    在去除窗體邊框之前有一項工作是必須做的,那就是更改默認窗體的大小,因爲每個人想做的打包窗體不可能都一樣大,更改窗體大小有兩種方法,也可以兩種方法並用

    1. 修改NSIS內部的UI

      NSIS的默認UI放在"%NSIS_Install_DIR%\Contrib\UIs"中,其中常常見到的創建自定義窗體的1018,1044都在此路徑的modern.exe中。我們只要修改modern.exe裏面的資源文件即可,做過MFC的都知道,VC在創建程序的時候是有Resources的,只要找到一些能更改Resources裏面Dialog的工具即可,本文推薦ResHacker 。

      修改的時候寧可大點,也絕不小,因爲開發過程中我遇到用nsWindows命令擴大窗體的時候,出現不起作用的情況,但是默認窗體比需要的窗體小的時候可以用nsWindows命令控制。

      打開ResHacker工具拖入modern.exe,操作前請備份modern.exe,拖動資源窗體或者直接修改你想要的大小。默認的1044跟1018窗體都在105分類下。



    2. 通過nsWindows命令

      nsDialogs::Create 1044
          Pop $0
          ${If} $0 == error
              Abort
          ${EndIf}
          SetCtlColors $0 ""  transparent ;背景設成透明
                                                                                                              
          ${NSW_SetWindowSize} $HWNDPARENT 513 354 ;改變窗體大小
          ${NSW_SetWindowSize} $0 513 354 ;改變Page大小
      該腳本添加在自定義窗體的創建Function中,創建的是1044類型窗口,修改命令是兩條,分別是對$HWNDPARENT的窗體跟創建的1044page的修改,確保默認的modern.exe的窗口大小比這個要大!

    修改好窗體大小後,直接在初始化的Function中直接填入以下代碼即可去除邊框

    Function onGUIInit
        ;消除邊框
        System::Call `user32::SetWindowLong(i$HWNDPARENT,i${GWL_STYLE},0x9480084C)i.R0`
        ;隱藏一些既有控件
        GetDlgItem $0 $HWNDPARENT 1034
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1035
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1036
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1037
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1038
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1039
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1256
        ShowWindow $0 ${SW_HIDE}
        GetDlgItem $0 $HWNDPARENT 1028
        ShowWindow $0 ${SW_HIDE}
    FunctionEnd

    用System::Call命令調用SetWindowLong的API函數改變GWL_STYLE的樣式即可,System是NSIS官方插件用於幫助用戶調用系統函數,是相當重要的自動安裝程序的插件!程序中其餘的代碼是把創建的1004頁面上其餘的控件給隱藏掉,後面攜帶的ID都是可以通過ResHacker在105包中查詢到。

  2. 貼一張大小合適的背景圖

    貼圖需要用到nsDialogs插件的命令:

    ;貼背景大圖
        ${NSD_CreateBitmap} 0 0 100% 100% ""
        Pop $BGImage
        ${NSD_SetImage} $BGImage $PLUGINSDIR\bg.bmp $ImageHandle
                                                                       
        ${NSD_FreeImage} $ImageHandle

    ${NSD_CreateBitmap}命令創建一個跟窗體一樣大小的圖片區域,後面的五個參數分別是x,y,width,height,text,座標,寬高,文字。緊接着給這張圖貼上一張合適的圖片bg.bmp,貼圖片之前需要把這個圖片打包到安裝程序中,這個是基本的操作,源碼包中有,這裏就不做說明了。最後還要通過${NSD_FreeImage}去釋放該圖片內存區。

  3. 無標題移動

    做到無標題移動的潛臺詞是把原本傳遞給標題欄的Message通過你定義的元素回調傳遞給標題欄,所以只要給你添加的資源加上傳遞信息的回調函數就可以了。這裏是通過WinProc這個插件完成的,WinProc這個插件在官方的插件庫中沒有,Google一下就可以查詢到,這裏的源碼包中也有。除了WinProc,第三方插件SkinBtn也可以幫助實現。

    Function onGUICallback
      ${If} $MSG = ${WM_LBUTTONDOWN}
        SendMessage $HWNDPARENT ${WM_NCLBUTTONDOWN} ${HTCAPTION} $0
      ${EndIf}
    FunctionEnd

    以上是回調函數,判斷鼠標左鍵的Down事件,並且傳遞消息給標題欄。

    GetFunctionAddress $0 onGUICallback
        WndProc::onCallback $BGImage $0 ;處理無邊框窗體移動

    以上是把當前的$BGImage作爲回調主體,當用戶左鍵點擊$BGImage的時候,消息就傳遞給了窗體標題欄,實現了無邊框的移動。

**結束語**

看看結果是什麼樣子的……哦!kugou也被我山寨了一把,有人精益求精,說,你的程序鼠標放在哪裏都能移動,人家kugou只能標題欄移動……是的呀,你把圖切成幾份,分別貼,有的給回調函數,有的不給就實現了消息部分傳遞的功能。


有一個注意點:貼圖的時候注意了!代碼的運行是自上而下,如果要貼的圖需要在另一張上面的話,需要把代碼寫在前面。

比如:

;貼小圖
    ${NSD_CreateBitmap} 0 34 100% 100% ""
    Pop $MiddleImage
    ${NSD_SetImage} $MiddleImage $PLUGINSDIR\middle.bmp $ImageHandle
                  
    ;貼背景大圖
    ${NSD_CreateBitmap} 0 0 100% 100% ""
    Pop $BGImage
    ${NSD_SetImage} $BGImage $PLUGINSDIR\bg.bmp $ImageHandle

That's all

下回繼續探討

***************************

特別感謝石頭(石頭的qq羣號97208217),夢想吧論壇(雖然我還不是個會員,但是學到了很多)

NSIS進階教程(二)

來自:http://www.pylife.net/post/2012-06-13/40027965518

自定義界面之Button、License窗口實現

**前言**

在上一節中我們粗略的處理一下無邊框窗體、背景貼圖、鼠標移動。這節主要是創建用於響應事件的Button以及能展示軟件License的窗口,還能用Button控制軟件協議的展示與否。

代碼還是延續上一節的。

本篇主要講講以下幾點:

  1. 如何創建一個自己的按鈕

  2. 如何創建一個自己的License窗口

  3. 如何用自己的按鈕控制自己的License窗口事件

所用到的插件:

  • nsDialogs

  • SkinBtn

**講義**

首先貼出一個今天教程的完整的例子(附帶圖片) 猛擊這裏

本文還是在安裝程序的首張Welcome中執行,上一節例子中的背景圖需要做適當的修改,因爲原本需要創建Button的地方背後有圖,適當用PS去抹去就好。本文的前期工具就是PS了一部分元素。加入了幾張Button的背景圖。

  1. 創建一個屬於自己Design的Button

    ;下一步
        ${NSD_CreateButton} 319 303 88 25 ""
            Pop $Btn_Next
            StrCpy $1 $Btn_Next
            Call SkinBtn_Next
            GetFunctionAddress $3 onClickNext
        SkinBtn::onClick $1 $3

    原本nsDialogs插件就可以用${NSD_CreateButton}命令創建一個Button按鈕出來,可惜的是此Button按鈕默認的有點醜。

    下面就要說到如何改觀Button的樣式,再接觸之前我一直想找有沒有用一張圖片來替代Button的區域,並且給Image附上事件之類的想法。等到劉若英的後來,我發現有一個SkinBtn的插件可以完成此類工作。

    SkinBtn是我在夢想吧中下載的一個第三方插件,國人開發,支持Button的五種狀態:Normal,Hover,Click,Disabled,Focus,正常、鼠標懸浮、單擊時、不可用時、獲得焦點時。這五種狀態已經完全夠用了。

    所要注意的就是SkinBtn的五種狀態圖需要豎行排列,寬高沒有限制,但是高度像素最好的5的倍數(我猜是爲了整除,方便取圖)下面貼一張示例Button的貼圖:


    SkinBtn的用法是這樣的:

    在.onInit的初始化圖片資源,並且要用SkinBtn插件的方法初始化一下:

    File `/ONAME=$PLUGINSDIR\btn_next.bmp` `images\btn_next.bmp`
    SkinBtn::Init "$PLUGINSDIR\btn_next.bmp"

    緊接着Call以下方法,給當前的Button附上定義的圖形:

    Function SkinBtn_Next
      SkinBtn::Set /IMGID=$PLUGINSDIR\btn_next.bmp $1
    FunctionEnd

    現在窗體中的按鈕已經被處理成自己想要的效果了,這時候衣服算是穿起來了,我們再試一試他的功能怎麼樣,我們定義一個onClickNext的function,然後調用SkinBtn插件的onClick方法進行調用:

    GetFunctionAddress $3 onClickNext
        SkinBtn::onClick $1 $3
    ;下一步按鈕事件
    Function onClickNext
      MessageBox MB_OK "下一步"
      Abort
    FunctionEnd

    單擊事件裏面只做了彈出消息框這麼一個臨時的操作,後面的教程中會說明怎麼彈到另一張page中,今天這裏不談。

    這樣一個自己的貼圖的Button,從外觀到功能都已經完成了。

  2. 創建顯示License內容的控件

    關於License的框如何顯示的問題,NSIS的默認窗體有一個專門的協議Page,不過在我們的自定義Page中就不說那個了,在我們的Welcome頁面中加入一個協議顯示的窗口,這時候需要準備一個協議按鈕的貼圖,協議窗口。



    協議的按鈕樣子看上去是超鏈接,我們可以通過Button貼圖去實現,不要一味認死理去創建超鏈接的命令。因爲有兩種狀態,所以有兩份貼圖。

    圖片預載入跟貼圖的步驟就不說了,這裏初始化該Button的時候是默認的箭頭向上的圖,我們只要額外的創建一個變量來控制點擊的狀態,並且在按鈕點擊事件中重新給Button貼上第二張箭頭向上的圖就可以實現按鈕的貼圖跟功能的統一:

    Function SkinBtn_Agreement1
      SkinBtn::Set /IMGID=$PLUGINSDIR\btn_agreement1.bmp $1
    FunctionEnd
                                                                                                      
    Function SkinBtn_Agreement2
      SkinBtn::Set /IMGID=$PLUGINSDIR\btn_agreement2.bmp $1
    FunctionEnd
                                                                                                      
    ;協議按鈕事件
    Function onClickAgreement
        ${IF} $Bool_License == 1
            IntOp $Bool_License $Bool_License - 1
            StrCpy $1 $Btn_Agreement
            Call SkinBtn_Agreement1
        ${ELSE}
            IntOp $Bool_License $Bool_License + 1
            StrCpy $1 $Btn_Agreement
            Call SkinBtn_Agreement2
        ${EndIf}
    FunctionEnd

    以上代碼中,onClickAgreement方法是Button的點擊響應事件,變量Bool_License是控制顯示那張Button背景圖的控制單元,它的初始值是0,IF判斷中寫的是當前狀態下需要處理的事件。IntOp命令是執行加減運算。

    這樣,一個根據點擊來更改背景圖片的按鈕就做好了。

  3. 用自定義的按鈕去控制License窗口的顯示

    在做License之前首先要準備一份License的文件,NSIS命令中有讀取文件,顯示文件內容的命令,該文件可以是Txt,也可以是帶有格式的RTF,這裏示例中選用RTF文件。在源碼包的RTF協議用的KuGou的。而用於顯示RTF的控件則是nsDialogs插件提供的RichEdit20A,還要注意引入頭文件LoadRTF.nsh

    ;讀取RTF的文本框
        nsDialogs::CreateControl "RichEdit20A" \
        ${ES_READONLY}|${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN} \
        ${WS_EX_STATICEDGE} \
        5 44 500 203 ''
        Pop $Txt_License
        ${LoadRTF} '$PLUGINSDIR\license.rtf' $Txt_License
        ShowWindow $Txt_License ${SW_HIDE}

    以上代碼就是創建讀取RTF文件的多行文本框,並且設定了一些常用的屬性,具體的樣式屬性可以通過查詢nsDialogs插件的官方文件可以得到,我們讓該控件默認是不顯示的狀態。

    這樣一個,協議的顯示控件就創建完畢了。

    創建好了協議控件$Txt_License,那麼接着就是用剛纔創建的Button控制該窗口的顯示與否,修改剛纔的代碼:

    ;協議按鈕事件
    Function onClickAgreement
        ${IF} $Bool_License == 1
            ShowWindow $MiddleImage ${SW_SHOW}
            ShowWindow $Txt_License ${SW_HIDE}
            IntOp $Bool_License $Bool_License - 1
            StrCpy $1 $Btn_Agreement
            Call SkinBtn_Agreement1
        ${ELSE}
            ShowWindow $MiddleImage ${SW_HIDE}
            ShowWindow $Txt_License ${SW_SHOW}
            IntOp $Bool_License $Bool_License + 1
            StrCpy $1 $Btn_Agreement
            Call SkinBtn_Agreement2
        ${EndIf}
    FunctionEnd

    在事件當中,加入隱藏中間背景圖跟顯示License窗口的代碼,這樣一個修改Button貼圖跟控制協議窗口顯示與否的關聯就做好了。

**結束語**

圖片還需修整修整。

在此文中,核心的部分是如何創建讀取RTF的協議窗口還有Button控制協議窗口的顯示的部分,這部分所用到的代碼邏輯是每個初級Coder都可以寫出來的,本文也就是拋磚引玉。

**困惑**

在源碼中,開頭的部分定義了兩個變量:

Var MSG

Var Dialog

$MSG變量在移動回調函數中用到,$Dialog變量沒有顯式用到,據我的實驗$Dialog變量在按鈕控制MiddleImage中間圖形中用到,如果不聲明$Dialog變量,在Button的單擊事件中控制圖形的顯示與否是失效的!

懷疑:$MSG是NSIS系統默認的消息變量用於記錄消息信息,$Dialog是NSIS默認的對話框變量用於保存窗體中控件的信息。有待高手解答。

*********************************

That's all

下回再探討

NSIS進階教程(三)

來自:http://www.pylife.net/post/2012-07-13/40030360875

自定義MessageBox,自定義頁跳轉,自定義CheckBox樣式

**前言**

上一節中我們處理了Button的自定義以及Button的事件消息、協議框的創建等等,這節中我們要更加完美的要求我們的提示框也要漂亮,CheckBox也要自定義樣式。有人說MessageBox在NSIS默認情況下是帶邊框的API窗口,是一個比較醜雛形,但是NSIS的nsDialogs插件也沒有提供一個可以創建彈出窗的命令行呀,CheckBox系統自帶的只能是那個默認的皮膚,如果想把CheckBox的背景改成藍色,或者把CheckBox的勾改成愛心,把叉改成骷髏頭如何去做呢?這些問題在任何一個面向對象的編程語言中都可以很容易實現,在NSIS這樣侷限的環境中也可以變通實現,接下來我們處理一下這些問題。

本篇主要講講以下幾點:

  1. 如何拋棄系統的MessageBox

  2. 如何實現自定義頁面的跳轉

  3. 如何用自己的貼圖實現CheckBox功能

所用到的插件【新增】:

  • FindProcDLL(查找當前進程插件)

  • KillProcDLL (關閉指定進程插件)

**講義**

首先貼出一個今天教程的完整的例子【已測試】【附帶圖片】 猛擊這裏

題外話,本節的重點是如何把系統的MessageBox替換掉自定義的窗口,一開始這個命題挺難的,原本因爲nsDialogs可以創建一個彈窗,然後給它披上一層皮就可以了,在查了很多資料後,我瞭解到根本不行,nsDialogs插件不能完成這麼簡單的工作,而是通過另一個插件nsWindows來完成。另一個自定義CheckBox功能還是有點討巧的,其實在窗體編程中,Button跟CheckBox本質上是一樣的東西,CheckBox就是Button披上一層皮加上一些單擊事件改變皮膚而存在的,於是就有了CheckBox的自定義的可能性。

還是要講一點不足,此處CheckBox不包含旁邊的說明文字,也就是說,當你促發CheckBox旁邊的文字某些事件的時候CheckBox本事不會有任何反應,這樣就不能構成一個CheckBox的整體,不過對於要求不是非常高的人來說,這點完全可以忽略。

  1. 如何拋棄系統的MessageBox

    通常在使用警告框的時候,我們會調用MessageBox的NSIS命令,然而此窗口無比醜陋,簡直是對我們正在做的窗體的褻瀆。於是我們自己創建警告框,此警告框也要跟我們已有的窗口風格一致,包含幾大問題,“無邊框”、“美觀貼圖”、“移動”、“邏輯判斷功能”,做到以上幾點就可以了。上代碼:

    Function onCancel
        IsWindow $WarningForm Create_End
        !define Style ${WS_VISIBLE}|${WS_OVERLAPPEDWINDOW}
        ${NSW_CreateWindowEx} $WarningForm $hwndparent ${ExStyle} ${Style} "" 1018
                                                                                                                                   
        ${NSW_SetWindowSize} $WarningForm 349 184
        EnableWindow $hwndparent 0
        System::Call `user32::SetWindowLong(i$WarningForm,i${GWL_STYLE},0x9480084C)i.R0`
        ${NSW_CreateButton} 148 122 88 25 ''
        Pop $R0
        StrCpy $1 $R0
        Call SkinBtn_Quit
        ${NSW_OnClick} $R0 OnClickQuitOK
                                                                                                                                   
        ${NSW_CreateButton} 248 122 88 25 ''
        Pop $R0
        StrCpy $1 $R0
        Call SkinBtn_Cancel
        ${NSW_OnClick} $R0 OnClickQuitCancel
                                                                                                                                   
        ${NSW_CreateBitmap} 0 0 100% 100% ""
        Pop $BGImage
      ${NSW_SetImage} $BGImage $PLUGINSDIR\quit.bmp $ImageHandle
        GetFunctionAddress $0 onWarningGUICallback
        WndProc::onCallback $BGImage $0 ;處理無邊框窗體移動
      ${NSW_CenterWindow} $WarningForm $hwndparent
        ${NSW_Show}
        Create_End:
      ShowWindow $WarningForm ${SW_SHOW}
    FunctionEnd

    這個onCancel方法是在我們在主窗體上點擊“取消”或者“關閉”的時候促發的方法,這時會彈出一個命令窗口出來。

    創建跟設置樣式:WS_VISIBLE  是顯示的意思,不加會隱藏掉,WS_OVERLAPPEDWINDOW  是創建一個層疊窗體的屬性也需要加上。NSW_CreateWindowEx  是創建窗體的主命令。

    創建完窗體後,用NSW_SetWindowSize  更改一下窗體的大小。

    EnableWindow $hwndparent 0 這段代碼一定要加上,這是創建一個警告框模式窗體的必要條件,就是讓主窗體不能操作。

    接下來就是消除邊框,用nsWindows命令創建按鈕的一套,跟nsDialogs的創建是一致的,包括無邊框移動的一套,在第一節中已經講過,這裏就不囉嗦了。

    最後把這個窗體展示出來就可以了。把這個onCancel這個方法賦給“取消”按鈕的單擊事件,這樣一個無邊框模式窗體警告框就好了。


    當我們點擊“退出”的時候,就要關掉當前程序,所以要添加一個關閉的方法: 

    Function onClickClose
        FindProcDLL::FindProc "test.exe"
        Sleep 500
        Pop $R0
        ${If} $R0 != 0
        KillProcDLL::KillProc "test.exe"
        ${EndIf}
    FunctionEnd

    通過FindProcDLL插件的FindProc方法找到安裝進程,並且通過KillProcDLL插件的KillProc殺之。

    當我們點擊“取消”的時候,這時候需要關閉當前警告框,並且讓主窗體能夠處於Active狀態

    Function OnClickQuitCancel
      ${NSW_DestroyWindow} $WarningForm
      EnableWindow $hwndparent 1
      BringToFront
    FunctionEnd

    NSW_DestroyWindow 命令銷燬掉警告框,使主窗體能活動,並且Bring到前端。

  2. 如何實現自定義頁面的跳轉

    一開始我們就定義了兩個自定義頁面:

    Page custom WelcomePage
    Page custom InstallationPage

    從WelcomePage跳轉到InstallationPage,這個就是“下一步”按鈕的事件。

    創建一個RelGotoPage方法:

    Function RelGotoPage
      IntCmp $R9 0 0 Move Move
        StrCmp $R9 "X" 0 Move
          StrCpy $R9 "120"
      Move:
      SendMessage $HWNDPARENT "0x408" "$R9" ""
    FunctionEnd

    詳細解釋在這裏 http://nsis.sourceforge.net/Go_to_a_NSIS_page

    • If a number > 0: Goes foward that number of pages. Code of that page will be executed, not returning to this point. If it is bigger than the number of pages that are after that page, it simulates a "Cancel" click.

    • If a number < 0: Goes back that number of pages. Code of that page will be executed, not returning to this point. If it is bigger than the number of pages that are before that page, it simulates a "Cancel" click.

    • If X: Simulates a "Cancel" click. Code will go to callback functions, not returning to this point.

    • If 0: Continues on the same page. Code will still be running after the call.

    在“下一步”的單擊事件中加入以下代碼,跳轉就好了,而且以後的調轉RelGotoPage都適用

    Function onClickNext
      StrCpy $R9 1
      Call RelGotoPage
      Abort
    FunctionEnd
  3. 如何用自己的貼圖實現CheckBox功能

    CheckBox也有系統自帶的那種健全的,但是效果沒有Button貼皮膚後好,所以棄用之,在第二個頁面中創建幾個Button跟標籤:

    ${NSD_CreateButton} 26 150 15 15 ""
        Pop $Ck_ShortCut
        StrCpy $1 $Ck_ShortCut
        Call SkinBtn_Checked
        GetFunctionAddress $3 OnClick_CheckShortCut
        SkinBtn::onClick $1 $3
        StrCpy $Bool_ShortCut 1
        ${NSD_CreateLabel} 45 151 100 15 "添加桌面快捷方式"
        Pop $Lbl_ShortCut
        SetCtlColors $Lbl_ShortCut ""  transparent ;背景設成透明

    創建CheckBox的時候要考慮周全,首先要定義該CheckBox的變量,該CheckBox的皮膚,記錄該CheckBox狀態的變量$Bool_ShortCut ,該CheckBox旁邊的提示文字變量$Lbl_ShortCut



                

    Button的貼圖跟變換方式在第二節中已經介紹,這裏就不囉嗦。

    這裏的OnClick_CheckShortCut 方法不僅僅是一個變換的實現,也通過$Bool_ShortCut記錄了當前CheckBox的狀態

    Function OnClick_CheckShortCut
      ${IF} $Bool_ShortCut == 1
            IntOp $Bool_ShortCut $Bool_ShortCut - 1
            StrCpy $1 $Ck_ShortCut
            Call SkinBtn_UnChecked
        ${ELSE}
            IntOp $Bool_ShortCut $Bool_ShortCut + 1
            StrCpy $1 $Ck_ShortCut
            Call SkinBtn_Checked
        ${EndIf}
    FunctionEnd

    最後我們在完成安裝的時候,可以通過$Bool_ShortCut該變量來了解用戶的選擇


**結束語**

其實不難,邏輯清晰,知道自己朝哪個方向去尋找答案,一切就會雲開霧散。希望大家能學到一些。

***************************

有任何疑問請留言。

That's all

下回繼續探討

NSIS進階教程(四)

來自:http://www.pylife.net/post/2012-11-23/40043392092

自定義目錄選擇,自定義進度條,自定義圖片切換效果

**前言**

上一節中我們已經處理了有關CheckBox自定義貼圖的部分,但是目錄選擇的部分還沒有加上,這節,我們先處理一下目錄的選擇部分,選擇完路徑之後就剩下安裝了,於是進度條的創建的顯得很有必要,但是系統的進度條創建簡單,如何改變進度條的背景色跟進度色呢,這節我們也處理掉。有關圖片切換的效果的插件也有很多,但是大部分都是基於默認安裝窗體進行的,如何在一個完全自定義的頁面上面創建一個圖片切換效果呢,這節也可以得到答案。

本篇主要講講以下幾點:

  1. 創建目錄選擇按鈕與文本框

  2. 創建自定義的進度條

  3. 創建圖片切換效果

所用到的插件【新增】:

  • WebCtrl

  • SkinProgress

  • BgWorker

**講義**

首先貼出今天教程的完整的例子【已測試】【附帶圖片】 猛擊這裏

這次的教程基本是安裝界面的最後的階段的處理了,主要是創建一個目錄選擇控件,用於確定安裝程序安裝的路徑,在安裝過程中等待時間也是比較長的,遇到打廣告的好時機不要錯過,切換多圖是個不錯的選擇;在安裝過程中,釋放文件是一個主要動作。如果不是自定義頁面,該動作是在系統的安裝頁面的Section裏面完成的,如果不做多線程處理,該動作會阻塞主線程也就是主界面的消息傳遞,主界面沒有了消息傳遞也就不會響應拖動、點擊關閉等等操作,這個是本節主要說明的地方。最後說一下圖片切換的實現,以我目前嘗試的,效果最好的還算是直接放一張網頁最實在,通過網頁的js實現切換效果。

  1. 目錄選擇框

    目錄選擇框包括一個文本框跟一個Button按鈕,做過Web的人都知道,html中有一個file的控件與之相吻合,在form中則是兩個不同的component,文本框很好創建,按鈕的單擊事件是一個重點,我們看看代碼:

    ;更改目錄控件創建
        ${NSD_CreateDirRequest} 26 79 358 25 "$INSTDIR"
        Pop $Txt_Browser
        ${NSD_OnChange} $Txt_Browser OnChange_DirRequest
                                                                                                                                                                            
        ${NSD_CreateBrowseButton} 400 79 88 25 ""
        Pop $Btn_Browser
        StrCpy $1 $Btn_Browser
        Call SkinBtn_Browser
        GetFunctionAddress $3 OnClick_BrowseButton
          SkinBtn::onClick $1 $3

    這裏創建了一個$Txt_Browser的文本框,一個$Btn_Browser的按鈕,其中按鈕的單擊事件是 OnClick_BrowserButton,看看按鈕的事件代碼:

    Function OnClick_BrowseButton
      Pop $0
                                                                                                                                                                     
      Push $INSTDIR 
      Call GetParent
      Pop $R0
                                                                                                                                                                     
      Push $INSTDIR
      Push "\"
      Call GetLastPart
      Pop $R1
                                                                                                                                                                     
      nsDialogs::SelectFolderDialog "請選擇 $R0 安裝的文件夾:" "$R0"
      Pop $0
      ${If} $0 == "error" 
        Return
      ${EndIf}
      ${If} $0 != ""
        StrCpy $INSTDIR "$0\$R1"
        system::Call `user32::SetWindowText(i $Txt_Browser, t "$INSTDIR")`
      ${EndIf}
    FunctionEnd
                                                                                                                                                                
    ;得到選中目錄用於拼接安裝程序名稱
    Function GetParent
      Exch $R0
      Push $R1
      Push $R2
      Push $R3
      StrCpy $R1 0
      StrLen $R2 $R0
      loop:
        IntOp $R1 $R1 + 1
        IntCmp $R1 $R2 get 0 get
        StrCpy $R3 $R0 1 -$R1
        StrCmp $R3 "\" get
        Goto loop
      get:
        StrCpy $R0 $R0 -$R1
        Pop $R3
        Pop $R2
        Pop $R1
        Exch $R0
    FunctionEnd
                                                                                                                                                                     
                                                                                                                                                               
    ;截取選中目錄
    Function GetLastPart
      Exch $0 ; chop char
      Exch
      Exch $1 
      Push $2
      Push $3
      StrCpy $2 0
      loop:
        IntOp $2 $2 - 1
        StrCpy $3 $1 1 $2
        StrCmp $3 "" 0 +3
          StrCpy $0 ""
          Goto exit2
        StrCmp $3 $0 exit1
        Goto loop
      exit1:
        IntOp $2 $2 + 1
        StrCpy $0 $1 "" $2
      exit2:
        Pop $3
        Pop $2
        Pop $1
        Exch $0 
    FunctionEnd

    單擊按鈕的時候用nsDialogs創建一個SelectFolderDialog的對話框,選擇需要安裝的路徑。GetParent方法主要是要取得當前選中的路徑,GetLastPart主要是保留當前的安裝程序最底層的目錄名稱,最終兩個部分合起來作爲整體的安裝路徑賦值給$INSTDIR。具體的效果可以運行源碼查看。             

  2. 創建自定義進度條

    nsDialogs有自帶的進度條,該進度條的顏色是那種系統的顏色,如果遇到自己設計的,就會出現問題,於是用SkinProgress就可以給進度條賦上兩張圖片,一張是底圖,一張是進度圖,雖然不是非常的完美,比如圓角,比如透明,比如陰影,比如……但是已經是跟NSIS快捷腳本配合的很好的了。

    ${NSD_CreateProgressBar} 24 265 474 7 ""
        Pop $PB_ProgressBar
        SkinProgress::Set $PB_ProgressBar "$PLUGINSDIR\loading2.bmp" "$PLUGINSDIR\loading1.bmp"

    用法非常簡單,用nsDialogs創建一個ProgressBar,然後用SkinProgress去set一下,後面跟上兩張圖片。ProgressBar的圖片是有講究的,具體的可以看源碼的image文件夾中兩張圖片的切圖,基本要使用什麼效果,自己做兩張圖就可以了。                           

  3. 創建圖片切換

    圖片的切換有很多種,gif、flash、js、甚至自己用c++做插件實現,這裏提供一種很方面,但是又不失功能強大的方式。網頁的形式!你很容易能自己寫一段js實現多張圖片的切換,這裏唯一要解決的就是如何在Form上創建一個瀏覽器用於加載自己的本地html頁面。

    System::Call `*(i,i,i,i)i(1,34,518,200).R0`
        System::Call `user32::MapDialogRect(i$HWNDPARENT,iR0)`
        System::Call `*$R0(i.s,i.s,i.s,i.s)`
        System::Free $R0
        FindWindow $R0 "#32770" "" $HWNDPARENT
        System::Call `user32::CreateWindowEx(i,t"STATIC",in,i${DEFAULT_STYLES}|${SS_BLACKRECT},i1,i34,i518,i200,iR0,i1100,in,in)i.R0`
        StrCpy $WebImg $R0
        WebCtrl::ShowWebInCtrl $WebImg "$PLUGINSDIR/index.htm"

    首先在界面上創建一個STATIC的對象(對話框),通過MapDialogRect來定位該對話框的位置(1,34),大小(518,200),然後通過WebCtrl控件來把自己的網頁index.htm加載進去,WebCtrl插件實現的就是調用本地瀏覽器。這裏定位瀏覽器的位置以及大小是難點,還需多多熟悉纔是。

    網頁裏面的代碼我就不講解了,主要是圖片js切換,你也可以更換js代碼,網絡中很多效果都有。

    如果發現界面上的圖片切換畫面跟外邊框有距離,就去查看網頁裏面的css,是否把margin跟padding都設成了0,這樣就沒有間隙了,看上去跟貼在form上面的一樣。:)                                     

  4. 多線程安裝

    界面都畫好了,現在說到安裝文件的時候釋放了。源碼中我是通過sleep來模擬的,具體的釋放跟這個差不多。首先創建的是一個只運行一次的定時器Timer:

    GetFunctionAddress $0 NSD_TimerFun
        nsDialogs::CreateTimer $0 1

    其中nsDialogs::CreateTimer後面的參數1代表的是1毫秒,正常情況下就是1毫秒執行一次NSD_TimerFun介個方法。

    Function NSD_TimerFun
        GetFunctionAddress $0 NSD_TimerFun
        nsDialogs::KillTimer $0
        !if 1   ;是否在後臺運行,1有效
            GetFunctionAddress $0 InstallationMainFun
            BgWorker::CallAndWait
        !else
            Call InstallationMainFun
        !endif
    FunctionEnd

    本身定時器就是一個異步的功能,如果定時器是同步的,那就沒有意義了對不,那麼既然是異步的,爲什麼在NSD_TimerFun裏面做Sleep操作的時候,主界面會卡死不響應呢。這個就要看看nsDialogs插件如何實現這個定時器的了,我也沒有詳查,應該不是創建子線程Thread 的方式。

    這裏如果拋棄Timer,直接使用BgWorker的話,也可以,但是會有一個缺陷:當你需要同時運行兩個方法的時候,只有通過創建兩個Timer來實現,並且在Timer調用的方法裏面採用BgWorker來實現子線程操作。

    看看上面代碼在Timer執行方法裏面,第一步是停止Timer:因爲我們只用了他的異步的特點,並沒有想做定時器。然後使用BgWorker插件。

    使用BgWorker插件非常簡單,只需用$0接收方法地址,然後調用BgWorker::CallAndWait。顧明思意,BgWorker調用過程是同步的,而且會Wait到InstallationMainFun方法執行結束,如果BgWorker::CallAndWait調用下面還有代碼的話,只有在執行完InstallationMainFun方法後才能執行。

    下面看看InstallationMainFun的實現:

    Function InstallationMainFun
        SendMessage $PB_ProgressBar ${PBM_SETRANGE32} 0 100
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 10 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 20 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 30 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 40 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 50 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 60 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 70 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 80 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 90 0
        Sleep 1000
        SendMessage $PB_ProgressBar ${PBM_SETPOS} 100 0
                                       
        ShowWindow $Btn_Next ${SW_SHOW}
        ShowWindow $Btn_Install ${SW_HIDE}
        EnableWindow $Btn_Cancel 0
    FunctionEnd

    所做的工作主要是操作滾動條的位置。在實際的釋放過程中,這個設置也是這樣的,目前還沒有自定義頁面的釋放插件,能回調得到精確的釋放量,以及釋放文件等等信息。所以只能模擬,儘可能的細化下來,估計着大概多少。這個是一個需要情商的工作,如果你有潔癖,偏執之類的病,我想你還是用系統自帶的釋放好了,也不需要自定義頁面了 。

    最後上幾張圖吧,好看點,也算是階段的結束 。

    (安裝過程)

    (安裝過程結束)

    (安裝結束)

**結束語**

四節的自定義教程也就到此爲止了,日後會補充一些技巧性的東西,也準備搞搞,修修插件,發佈發佈。That's all,Thanx!       

NSIS進階教程(五)

來自:http://www.pylife.net/post/2013-05-29/40051693246

在線下載,下載後的安裝,本地解壓安裝

前言

想做在線安裝包教程已經很久了,一直找不到合適的時間,如今在一個慵懶的週末早晨終於開始了。今天晚上還要去虹口體育場看申花跟國安的比賽,還是要抓緊時間呀。 在線安裝是以前教程的一個延續。

意義一:國內很多的IT廠商在做安裝包的時候首先考慮的是用戶下載的時間可接受度,而一個動輒100mb的包會把用戶嚇跑,而且下載的入口還控制在諸如迅雷,旋風,IE,等等,稍等,還有360安全瀏覽器。。。你如果沒有給360打個招呼,它說不定會報你的程序有毒。。。太可拍了。入口最重要,掌握在任何人手裏都會成爲收門票的工具。

意義二:用戶對着迅雷,旋風的界面,千遍一律,沒有任何信息的展示,就是一個進度條,你喪失了在第一時間佔領用戶意識的契機。如果你在安裝的時候有動畫播放,並且讓用戶在安裝的過程中獲取產品的使用技巧,或者像廣告植入一樣的嚴肅的對待這個不起眼的時間,你會收穫很多。

本篇主要講講以下幾點:

  • 在線下載

  • 下載後的安裝

  • 下載後的解壓

所用到的插件【新增】:

  1. inetc

  2. nsis7z

詳細地址: http://hamletsoft.com/blog/2013/05/26/nsis-slash-learn-nsis-step-by-step-setuponline/

附加其他:Qt之打包發佈

Qt發佈方式  

Qt發佈的時候,通常使用兩種方式:
(1)靜態編譯
(2)動態編譯
靜態編譯:把相關聯的庫一併引入可執行程序,雖然發佈簡單,但可執行程序較大。。。
動態編譯:相關聯的庫,以dll的形式引用,不被包含進可執行程序,發佈不方便,但可執行程序較小。。。
靜態發佈雖然不需要較多的dll,發佈簡單、方便,但是往往會牽扯到授權問題(詳情請查看Qt LGPL授權),動態發佈則可以避免。。。如果附帶了Qt的dll,就相當於發佈了Qt的dll,而這些庫是屬於Qt的,這足以保證使用者知道程序使用了LGPL版本的Qt。

查找依賴項

1、檢測可執行程序依賴模塊
下載工具:DependencyWalker
打開可執行程序,檢測依賴項

 檢測完成之後,將所需依賴庫拷貝進去。。。再次進行檢測,反覆進行。
2、常用依賴庫
(1)Qt模塊庫
    Qt5Cored.dll
    Qt5Guid.dll
    Qt5Widgetsd.dll
(2)ICU依賴庫
    icudt51.dll
    icuin51.dll
    icuuc51.dll
(3)EGL依賴庫
    libEGLd.dll
    libGLESv2d.dll
(4)插件庫(Qt安裝目錄下即可找到D:\Software\Qt\Qt5.1.0\5.1.0\msvc2010\plugins\platforms)
    圖片支持庫:imageformats
   音頻、視頻支持庫:mediaservice
    平臺支持庫:platforms
    等等。。。
    注意:查找對應的插件文件夾,粘貼到安裝目錄(一定要保持目錄結構,例如“platforms/***.dll”),詳細結構見打包發佈準備的文件組織結構。   
(5)VS運行時庫(在VS安裝目錄下即可找到D:\Software\Microsoft VisualStudio\VC\redist)
    msvcp100d.dll
    msvcr100d.dll
   注意:發佈程序的時候注意版本(Debug/Release)
  如果是Debug版本的則是.前面帶d的(Qt5Cored.dll、Qt5Guid.dll、Qt5Widgetsd.dll)
   如果是Release版本的則是.前面不帶d的(Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll)

關於NSIS

1、NSIS簡介
(1)NSIS是什麼?
   一款免費的Win32安裝、卸載系統!
(2)NSIS有什麼特點?
  腳本簡潔高效、系統開銷小,進行安裝、卸載、設置、解壓文件也不在話下,幾乎可以做所有的事情。
2、工具
   NSIS Edit + NSIS
3、使用方式
   腳本嚮導 + 修改代碼 = 個性化安裝包
準備文件
以下是我即將打包的所有文件,安裝包目錄結構(包括:可執行程序、插件庫、運行時庫、授權文件、卸載程序圖標等等!)如下圖所示:

1、利用嚮導製作安裝包:

2、填寫應用程序基本信息

3、指定安裝程序所用選項
   注意:這裏選擇語言爲SimpChinese

4、設置應用程序安裝目錄與授權文件

5、指定應用程序文件

6、指定創建應用程序圖標

7、選擇安裝程序完成後運行的動作

8、指定接觸安裝程序屬性

9、進行腳本編譯、保存

10、等待編譯完成,即可運行打包後的安裝包

   大功告成之後,即可進行安裝!
(1)安裝程序

(2)此處顯示授權文件中的內容

(3)選擇安裝目錄

(4)運行程序,並顯示“自述文件”

(5)運行結果

  關於Qt的打包工具瞭解一些,個人感覺NSIS用起來比較順手,腳本修改起來也方便,所以就推崇一下。。。若想將安裝包變得更加美觀,則需要手動修改腳本,更多信息請查找相關資料,此處不再多做介紹!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章