日韩精品中文字幕一区二区-日韩精品中文字幕一区-日韩精品中文字幕视频-日韩精品在线一区二区三区-第一页在线-第一福利视频

當前位置: 首頁IT技術 → 便攜軟件制作的系列教程

便攜軟件制作的系列教程

更多

在NSIS中怎么導入注冊表。

這有何難,用registry插件嘛:

${registry::RestoreKey} file.reg $var

可是,如果你經常在 RestoreKey 后面用 ${registry:write} ,就會發現,往往導入注冊表會失敗,或者寫入的鍵值被reg文件中的舊鍵值覆蓋了,這是為什么呢?

原來,${registry::RestoreKey} 這個命令并不會等待導入完成。作者在文檔中寫了:

${registry::RestoreKey} simply exec regedit: regedit /s “[file]“

執行的是 Exec 而非 ExecWait 。那么,可能 regedit.exe 尚未啟動,就開始執行下一行命令了。制作一般的安裝包問題不大,但便攜軟件對執行步驟的順序要求更加精確。所以,有些人的代碼是這樣寫的:

${registry::RestoreKey} file.reg $0

Sleep 200

睡一會。睡多久?睡一秒還是一年,這種盲人摸象的做法,我們完美主義者是不會使用的。因為這個命令,有些朋友凡是用到registry插件,都習慣性地加上個 sleep,這是完全沒有必要的,作者說了:

問:So my question is, what other functions in your plugin behave in the same way (ie do not wait for the registry operation to finish)?

答:registry::RestoreKey is the only one.

那么,用:

ExecWait 'regedit /s "[file]"' $var

不就行了嗎?

你又錯了,我們制作便攜軟件的時候,要對自己嚴格要求,在Vista以上的系統中,不經過UAC驗證,是無法執行 regedit /s 這個命令的(即使導入HKCU中的鍵值也不行)。難道你的每個軟件都要用戶通過UAC驗證以管理員權限運行嗎,完全是別有居心!

可是,在UAC環境的測試中,你會發現,即使不通過UAC驗證,${registry::RestoreKey} 這個命令也可以完成注冊表導入,難道,作者隱瞞了什么?

于是,作為代碼盲的你,充滿狐疑地打開 NSIS\Include\Registry.nsh ,找到這樣一段代碼:

!define registry::RestoreKey !insertmacro registry::RestoreKey

!macro registry::RestoreKey _FILE _ERR

registry::_RestoreKey /NOUNLOAD ${_FILE}

Pop ${_ERR}

IntCmp ${_ERR} -2 0 0 +10 ;REGEDIT4 ansi file

SetDetailsPrint none

IfFileExists "$SYSDIR\reg.exe" 0 +4 ;reg.exe used in Windows2K/XP/Vista/7

nsExec::ExecToStack "$SYSDIR\reg.exe" import "${_FILE}"

Pop ${_ERR}

StrCmp ${_ERR} 0 +5 0

IfFileExists "$WINDIR\regedit.exe" 0 +3 ;regedit.exe used in Wine

ExecWait "$WINDIR\regedit.exe" /s "${_FILE}" ${_ERR}

IfErrors 0 +2

StrCpy ${_ERR} -1

SetDetailsPrint lastused

!macroend

真是狡兔三窟!registry::RestoreKey失敗后,用reg.exe import,失敗后,又用 regedit.exe /s,我們就要有這種不屈不撓的精神,不要讓一次執行的失敗變成Bug。

眼尖的你發現,關鍵在于這一行:

nsExec::ExecToStack "$SYSDIR\reg.exe" import "${_FILE}"

原來,雖然regedit /s需要管理員權限,但reg import命令并不需要,這就是${registry::RestoreKey}成功的秘訣。

但是,${registry::RestoreKey}首先嘗試用插件導入,而插件并不等待導入結束,所以,我們在應用的時候,要把順序顛倒一下:

nsExec::ExecToStack "$SYSDIR\reg.exe" import "${_FILE}"

Pop $0

${IfNot} $0 == 0

${registry::RestoreKey} "${_FILE}" $0

Sleep 500

${IfNotThen} $0 == 0 ${|} StrCpy ${_OutVar} Error ${|}

${Endif}

nsExec::ExecToStack是等待運行結束的,首先執行,假如失敗,再用${registry::RestoreKey},并暫停0.5秒(比較安全的數值)。當以上動作始終返回Error的時候,我們就應該考慮做個標記,在便攜軟件結束的時候跳過這一次軟件運行中的注冊表修改,不覆蓋原先的reg文件了。

不過,當你翻閱 PortableApps.com Launcher 的源代碼時,卻發現關于注冊表導入,僅僅用了一行:

${registry::RestoreKey} $DataDirectory\settings\$0.reg $R9

可為什么感覺上PAL那么穩定,極少出錯呢?我猜是因為PAL的代碼非常繁雜,每個實際動作以前都有一堆工作,又是讀Launcher.ini,又是轉換變量,又是檢測PAF平臺,慢悠悠的,慢工出細活吧!

例1:

設想某一天,某妞將可移動磁盤插入電腦A,電腦A為她的U盤分配了盤符 F: 。該妞使用U盤上的便攜軟件打開了儲存于U盤上的幾個文檔:

F:\1.doc

F:\2.doc

……

在拔出U盤的時候,她甚至沒有為最后一個文件存檔,反正所有進度都會自動保存嘛!

過了幾日,該妞試圖在電腦B繼續她的工作,插入U盤以后,電腦B為她的U盤分配了盤符G: 。當她打開便攜軟件的時候,她會看到“最近文檔”列表那里顯示著什么呢?

仍然是:

F:\1.doc

F:\2.doc

……

當她試圖恢復上一次”自動保存“的文檔,卻提示“找不到……文件”。于是,她發怒了,發誓再也不來你的網站。

這正常嗎?太正常了,上次你就是在F盤打開文件的嘛。可是你還能完美地使用“最近文件”列表、繼續上一次的工作嗎?

我們干革命,就是要勇于把正常變為不正常。于是,我們需要盤符替換。

什么是盤符替換

所謂盤符替換 (Driver Letter Replacement),就是在檢測到盤符相對上一次運行時改變的時候,將某些文件中的舊盤符替換為新盤符,以實現用戶數據的完美銜接。以上述例子為例,就是在盤符轉變為 G: 的時候,將最近文檔列表替換為:

G:\1.doc

G:\2.doc

……

讓用戶體會不到盤符改變帶來的變化,而順利繼續上一次的工作進程。

什么是路徑替換

例2:

設想某一個軟件,在配置文件中保存了大量包含軟件路徑的數據,這些數據在軟件首次運行時生成,指向軟件的插件、模板等目錄,假如這些目錄設置錯誤,該軟件便無法正常運行。

而某一天,某妞將該便攜軟件從同盤符的一個目錄移動到另一個目錄,例如:從 f:\XXXPortable 移動到 f:\PortableApps\XXXPortable 。

那么,如何保證該軟件正常運行呢?假如包含路徑的設置項不多,我們可以一個個地寫入,而假如類似設置很多(例如ACDSee),或者數量不定,難道也要一個個寫入嗎?所以,我們需要在檢測到路徑改變時,將所有的 XXXPortable 替換為 PortableApps\XXXPortable 。

目錄格式

在 PortableApps.com Launcher 中,提供了四種類型的目錄格式,分別是:

%VARIABLE% : 正向單斜杠。例如:%PAL:AppDir% = x:\portableapps\xxxportable\app 。主要應對ini、xml等普通配置文件。

%VARIABLE:ForwardSlash% : 反向單斜杠。例如:%PAL:AppDir:ForwardSlash% = x:/portableapps/xxxportable/app

%VARIABLE:DoubleBackslash% : 正向雙斜杠。例如:%PAL:AppDir:DoubleBackslash% = x:\\portableapps\\xxxportable\\app 。主要應用于注冊表(.reg)文件。

%VARIABLE:java.util.prefs% : 反向多斜杠。例如: %PAL:AppDir:java.util.prefs% = /X:///Portable/Apps///App/Name/Portable///App 。主要應用于java程序。

我們需要根據替換文件的類型選擇相應的目錄形式。假如遇到這四種情況以外的目錄形式,則要靠 Custom Code 解決。

實現原理與流程

為了兼顧例1與例2的兩種情況,避免兩種情況同時發生,我們要將盤符替換與路徑替換分開,那就是:先替換盤符,再替換不帶盤符的路徑。

在引導過程中,讀取上一次記錄的INI文件,判斷是否盤符/路徑改變。

若改變,則讀取上一次的盤符/路徑,轉換為正確形式。

讀取當前的盤符/路徑,轉換為正確形式。

在文件中替換舊盤符為新盤符。

在文件中替換舊路徑為新路徑。

將當前的盤符、路徑寫入INI文件,以便下一次讀取。

在 PortableApps.com Launcher 中實現:

以ACDSee Portable為例,我們需要在引導過程中替換注冊表文件 HKCU.reg 中的舊盤符\路徑為新。

[FileWrite1]

Type=Replace

File=%PAL:DataDir%\settings\HKCU.reg

Find=%PAL:LastDrive%\\

Replace=%PAL:Drive%\\

[FileWrite2]

Type=Replace

File=%PAL:DataDir%\settings\HKCU.reg

Find=%PAL:LastPackagePartialDir:DoubleBackslash%

Replace=%PAL:PackagePartialDir:DoubleBackslash%

效果如下:

x:\\

替換為:

y:\\

\\xxx\\AppNamePortable

替換為:

\\yyy\\AppNamePortable

請注意,在 [FileWrite1] (盤符替換)中,我在 %PAL:LastDrive% 后面加上了雙斜杠。這是因為,%PAL:LastDrive% 是不帶斜杠的(x:)。可能出現這種情況:替換 D:,把 DWORD: 的最后兩個字母也替換了。難道PortableApps.com的人不擔心這種情況嗎?我認為使用PAL替換盤符的時候都要注意這一點,替換盤符一定要加斜杠。

在 Custom Code 中實現:

PortableApps.com Launcher 是一個死板的網站的死板的程序員做出的死板的工具,在險峻難料的革命事業中,我們要堅決摒棄教條主義思想。許多時候稍有變化,我們就需要用到 Custom Code 。那么,在NSIS語言中怎樣實現呢?

例如,一個程序以這樣的形式在 Data\File.txt 記錄路徑:

F__PortableApps_App_Portable

“:”、“\”、“空格”三種符號都轉換為下劃線。讓我們來寫一段 Custom Code 來解決它。

${SegmentPrePrimary}

; 替換盤符

; 首先讀取Ini文件中的記錄

ReadINIStr $0 $DataDirectory\settings\$AppIDSettings.ini $AppIDSettings LastDrive

; 替換三種符號為下劃線

${WordReplace} "$0\" "\" "_" "+" "$R0"

${WordReplace} "$R0" ":" "_" "+" "$R0"

${WordReplace} "$R0" " " "_" "+" "$R0"

; 得到當前盤符

StrCpy $R1 "$AppDirectory" 3

; 替換三種符號為下劃線

${WordReplace} "$R1" "\" "_" "+" "$R1"

${WordReplace} "$R1" ":" "_" "+" "$R1"

${WordReplace} "$R1" " " "_" "+" "$R1"

; 在文件中替換

${ReplaceInFileCS} "$DataDirectory\File.txt" $R0 $R1

; 替換路徑

; 首先讀取Ini文件中的記錄

ReadINIStr $0 $DataDirectory\settings\$AppIDSettings.ini $AppIDSettings LastDirectory

; 替換兩種符號為下劃線

${WordReplace} "$0" "\" "_" "+" "$R0"

${WordReplace} "$R0" " " "_" "+" "$R0"

; 得到當前路徑(不帶盤符)

StrCpy $R1 "$AppDirectory" "" 2

; 替換兩種符號為下劃線

${WordReplace} "$R1" "\" "_" "+" "$R1"

${WordReplace} "$R1" " " "_" "+" "$R1"

; 在文件中替換

${ReplaceInFileCS} "$DataDirectory\File.txt" $R0 $R1

!macroend

請注意文件的編碼,如果是 UTF-16LE 編碼,用 ${ReplaceInFileUTF16LECS} 。若需要忽略大小寫,取消最后的“CS”。

在原始NSIS腳本中使用,需要另外:

!include "TextReplace.nsh"

!include "ReplaceInFileWithTextReplace.nsh"

注意事項

盤符與路徑替換是一種簡單地銜接工作環境的方法,但我認為,在應用中需要注意以下幾點:

一定要確定你替換的是盤符/路徑,而非別的東西。例如,使用PAL的時候,在盤符后加入斜杠,替換 x:\ 而不是 x: 。

自行撰寫代碼時,注意所替換文件的編碼。

在替換大文件或多次替換之間,加入Sleep。否則可能遇到替換失敗。

對于重要路徑,最好在替換后手動寫入一次,以保障無誤。盤符替換依賴INI文件中的記錄,假如一次記錄與實際銜接不上,可能從此都銜接不上了。

自從去年心血來潮開了個頭,這個教程就一直沒了下文,我要用實際行動粉碎虎頭蛇尾的謠言,同志們,今天來談談 DefaultData。

剛開始制作便攜軟件的朋友常犯的一個錯誤是,將軟件的默認配置保存到 Data 目錄中。何以說是錯誤呢:

PortableApps.com格式便攜軟件在安裝后,Data目錄必須是空的。Data目錄中的文件必須在首次運行后生成。

合格的P.A格式便攜軟件,用戶可以隨時刪除Data目錄,將便攜軟件恢復到初始狀態。

因此,如果某些默認配置在軟件第一次運行時必須導入,我們應該將它保存到DefaultData目錄中。

DefaultData 的誕生

在一個不可考證的從前,John T. Haller 同志(PortableApps.com 的創始人)開始制作他的第一個便攜軟件:Firefox Portable。在移動介質運行的 Firefox 瀏覽器應該有如下調整:關閉磁盤緩存,不檢測默認瀏覽器,不設置默認下載目錄,同時,他希望在Firefox的默認書簽內加入他的網站地址。如何實現以上默認設置的調整呢?

直接修改程序?吃力不討好。于是,他在 App 目錄下新建了 DefaultData 目錄,將一份配置好的最簡化的配置保存于此。在Firefox首次運行時,DefaultData 目錄的內容會被復制到 Data 目錄,以實現設置默認配置的目的。于是,今天的 Firefox Portable(以及所有標準P.A格式便攜軟件)的結構,就成了這個樣子:

-\ <--- Directory with FirefoxPortable.exe

+\App\

+\AppInfo\

+\firefox\

+\DefaultData\

+\profile\

+\settings\

+\plugins\

+\Data\

FirefoxPortable.exe

什么是 DefaultData

通過以上敘述我們已經知道,DefaultData 是 PortableApps.com 格式便攜軟件的標準部件之一,它位于 App\DefaultData ,是軟件的默認配置。在首次運行時,它被復制到 Data 目錄,DefaultData 內部的文件結構應該和 Data 目錄完全一致。

DefaultData 怎樣工作?

DefaultData在首次運行時復制到 Data 目錄,作為初始的程序配置。判斷是否首次運行有幾種不同的方式:

Firefox Portable 的方式:

Firefox Portable 通過 NSIS 語言寫成,它通過檢查 Data\Profile\prefs.js是否存在來判斷是否首次運行,假如 Data\Profile\prefs.js 不存在,則復制默認配置到Data目錄。這種方式較為靈活,可根據不同軟件的具體情況選擇不同的判斷物:

ProfileWork:

;=== Check for an existing profile

IfFileExists "$PROFILEDIRECTORY\prefs.js" ProfileFound

;=== No profile was found

StrCmp $ISDEFAULTDIRECTORY "true" CopyDefaultProfile CreateProfile

CopyDefaultProfile:

CreateDirectory "$EXEDIR\Data"

CreateDirectory "$EXEDIR\Data\plugins"

CreateDirectory "$EXEDIR\Data\profile"

CreateDirectory "$EXEDIR\Data\settings"

CopyFiles /SILENT $EXEDIR\App\DefaultData\plugins\*.* $EXEDIR\Data\plugins

CopyFiles /SILENT $EXEDIR\App\DefaultData\profile\*.* $EXEDIR\Data\profile

PortableApps.com Launcher 的方式:

PortableApps.com Launcher 通過檢查 Data\settings目錄是否存在判斷首次運行,PAL在運行一次以后必然創建 Data\settings 目錄,如果此目錄不存在,則判斷為首次運行,并復制 DefaultData:

${IfNot} ${FileExists} $EXEDIR\Data\settings

CreateDirectory $EXEDIR\Data\settings

${If} ${FileExists} $EXEDIR\App\DefaultData\*.*

CopyFiles /SILENT $EXEDIR\App\DefaultData\*.* $EXEDIR\Data

${EndIf}

${EndIf}

第三種方式:

在制作具有中國特色的便攜軟件時,有時會碰到更為復雜的情況。為了保證軟件始終從默認配置的基礎上啟動,我們可以分別判斷多個目錄,缺少哪一個,就復制哪一個:

例一,

若Data\Profile不存在則復制DefaultData\Profile,若Data\Plugins不存在則復制DefaultData\Plugins:

${IfNot} ${FileExists} $EXEDIR\Data\Profile

CreateDirectory $EXEDIR\Data\Profile

CopyFiles /Silent $EXEDIR\App\DefaultData\Profile\*.* $EXEDIR\Data\Profile

${EndIf}

${IfNot} ${FileExists} $EXEDIR\Data\Plugins

CreateDirectory $EXEDIR\Data\Plugins

CopyFiles /Silent $EXEDIR\App\DefaultData\Plugins\*.* $EXEDIR\Data\Plugins

${EndIf}

例二,

在迅雷便攜版中應用到的,檢測任何一個 DefaultData 中的目錄,如果在 Data 目錄中不存在,都復制過去:

Section Main

; ......

; CopyDefaultData:

StrLen $R0 "$EXEDIR\App\DefaultData\"

${Locate} "$EXEDIR\App\DefaultData" "/L=D" CopyDefaultData

; ......

SectionEnd

Function CopyDefaultData

StrCpy $R1 $R9 "" $R0

${IfNot} ${FileExists} "$EXEDIR\Data\$R1"

CreateDirectory "$EXEDIR\Data\$R1"

CopyFiles /Silent "$R9\*.*" "$EXEDIR\Data\$R1"

${Endif}

Push $0

FunctionEnd

DefaultData 能做什么

修改默認配置

例如,在 Evernote Portable 中,將以下內容保存為 App\DefaultData\settings\EvernotePortable.reg:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Evernote\Evernote]

"UpdateToPreReleaseVersion"=dword:00000000

"CheckForUpdatesAtLaunch"=dword:00000000

則會在首次運行時復制為 Data\settings\EvernotePortable.reg ,接著導入注冊表,實現默認關閉自動升級的目的。

程序本身的默認/初始配置

通過 Total Uninstall 監測軟件安裝,可發現某些軟件在首次安裝后會在配置目錄中寫入一些文件,這些文件必須放到 DefaultData 目錄,以保證程序的完整性,以及讓用戶隨時可以刪除 Data 目錄恢復軟件初始配置。

通過 DefaultData 新建文件夾

在 PortableApps.com Launcher 中,如果你希望使用 FilesMove 來移動文件,必須保證 Data 目錄中有這個文件的父目錄,否則移動會失敗,例如:

[FilesMove]

config\file.txt=%PAL:AppDir%\AppName

在此例中,假如Data\config目錄不存在,那么file.txt就無法被移動到Data目錄。

解決辦法是,創建 App\DefaultData\config ,那么,首次運行時,App\DefaultData\config 會被復制為 Data\config ,以實現新建文件夾的目的。

不過,在大多數情況下,將單個文件保存到 Data\settings 中是更好的方法,PAL會自動創建此文件夾,避免了通過 DefaultData 來創建的麻煩。

其它用途

在制作具有中國特色的便攜軟件時,有時我們希望一些軟件配置永遠是“一次性”的(例如廣告目錄),那么,我們將一份干凈的初始配置保存到 DefaultData 中,在每次軟件啟動時復制到配置目錄,在軟件結束時刪除掉復制的副本。以保證軟件的潔凈。

注意事項

DefaultData 是 Data 目錄的初始狀態,其目錄、文件結構必須和 Data 目錄完全一致。假如你在設計便攜軟件時設定將注冊表導出到 Data\settings\AppNamePortable.reg,那么你應該將默認配置保存為 App\DefaultData\settings\AppNamePortable.reg 。否則無法奏效。

DefaultData 是軟件初始配置的一份存檔,應該盡量保持精簡。僅僅保留最必要的部分。你不應該將整個配置好的 Data 保存為 DefaultData,那樣浪費空間,延長首次啟動的時間,而應該找出真正有必要的、不可缺少的修改部分,保存為 DefaultData 。如果你的 DefaultData 超過1M,那么就該想想辦法了。

DefaultData 應該保留最通用的部分,如果你的 DefaultData 中存在關于你的計算機的信息,例如:installdir=c:\Program Files\AppName ,那么是非常不專業的,我們要嚴格要求自己。

通過 Total Uninstall 監視軟件安裝,可發現某些軟件自身的默認配置。有時候這些配置很重要(例如一個初始的數據庫),請別忘了把它們保存到 DefaultData。

 

熱門評論
最新評論
昵稱:
表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
字數: 0/500 (您的評論需要經過審核才能顯示)
主站蜘蛛池模板: 四角号码| pulp fiction| 夫妻最现实的约法三章| 真濑树里| bobo视频| 惊魂| 台湾卫视| 心理健康《微笑的力量》ppt| 周星驰原名| 下海 电视剧| 榜上有名| 2006年全国一卷语文作文| 日本电影怪物| 黑咖啡早上空腹喝还是饭后喝| 警察英雄| 地火电视剧38集| 野孩子美剧| 男士血压标准对照表| 叶子楣作品| 好心人| 日韩免费观看视频| 单依纯个人资料| 芭芭拉·布薛特| 风之谷钢琴谱| 百字明咒注音全文读诵| 爱自有天意| 美丽的邂逅| 电影英雄| 麦子叔| 建设工程档案归档整理规范| 金瓶儿| 《平凡之路》电影| 掐脖子的视频| 成人免费黄色电影| 太太的情人电影| chinesehd国语话对白| 哥也要| 大开眼戒在线观看| 罗中立的《父亲》详案| 饮料超人| 许华升公个人资料身高多少|