nsis插件工作原理_第1頁
已閱讀1頁,還剩18頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、<p>  NSIS插件工作原理</p><p>  了解NSIS的童鞋都知道,在NSIS中調(diào)用插件有兩種方式,一種是通過Push將參數(shù)壓入棧中,然后使用CallInstDLL來調(diào)用插件中的函數(shù);另一種方式是使用插件命令,如plugin::function_name param1 param2 param 3...,本文的目的就是深入理解NSIS的插件命令是如何工作的,NSIS腳本是如何和插件dll進(jìn)行

2、交流的</p><p>  接下來我會以一個例子nsDialogs::Create 1018為例,具體講述NSIS究竟是怎樣解析這條語句,又是怎樣調(diào)用nsDialogs.dll中的Create函數(shù)的</p><p>  NSIS腳本代碼是寫在一個.nsi的文件中的,這種文件其實是一個文本文件,并不具備可執(zhí)行代碼的功能,它需要NSIS中的makensis.exe來編譯</p>

3、<p>  這種文件,編譯后會產(chǎn)生一個.exe文件,就是生成的安裝包程序了,這個文件打開后就是一些安裝頁面,引導(dǎo)用戶進(jìn)行下一步操作,從而安裝</p><p>  文件到用戶的電腦上,也就是說NSIS中最厲害的其實是makensis.exe這個NSIS腳本編譯器了,NSIS是開源的,那么我們就來看看它的源碼是如何解析插件命令的調(diào)用的</p><p>  到目前為止,NSIS的最新版

4、本是2.46,它的源碼文件如下圖所示,NSIS是用C/C++寫的</p><p>  其中有幾個文件在解析插件命令時比較重要,build.cpp、Plugins.cpp、script.cpp、tokens.cpp、exehead\exec.c、exehead\plugin.c</p><p>  NSIS中支持的命令、屬性、預(yù)處理指令等都在tokens.cpp中有定義,如下圖所示,只有少數(shù)

5、幾個沒有具體的命令關(guān)鍵字,如插件命令</p><p>  可以看到這里把NSIS支持的關(guān)鍵字和TOK_XXX關(guān)聯(lián)起來了,但是插件命令比較特殊,這里雖然沒有定義,但是在tokens.h中TOK__LAST的后面有一句TOK__PLUGINCOMMAND,也就是說這個其實就是解析插件命令時的token,makensis.exe源碼中的主函數(shù)是定義在makenssi.cpp中的,而解析這其中最關(guān)鍵是一個CEXEBuil

6、d類,NSIS大部分的解析工作都由此類和它的成員來完成的,在makenssi.cpp中的</p><p>  main函數(shù)中構(gòu)造了CEXEBuild的實例build,經(jīng)過一些初始化和預(yù)處理后,開始解析nsi腳本,在使用時是用命令行的方式傳遞nsi腳本的文件名</p><p>  給makensis.exe,在makenssi.cpp的main函數(shù)中的470行開始讀取腳本文件的內(nèi)容:<

7、/p><p>  然后接下來的代碼開始解析腳本文件,主要是調(diào)用了CEXEBuild類的process_script函數(shù)來解析腳本代碼</p><p>  解析完成后,是生成一個exe文件,就是生成的安裝程序,根據(jù)前面解析的信息,最后調(diào)用CEXEBuild類的write_output函數(shù)生成exe程序</p><p>  本文要講的是插件的工作原理,因此我們重點來看看NS

8、IS是怎樣解析nsi腳本的,具體又是怎樣解析插件命令,調(diào)用插件的,來看CEXEBuild</p><p>  類的process_script函數(shù)是怎樣處理的,process_script函數(shù)是定義在script.cpp中的,如下圖所示:</p><p>  函數(shù)的一開始將filepointer文件指針傳遞給了CEXEBuild的成員變量fp,以便后續(xù)的文件讀取,然后解析的工作交給了222

9、行的</p><p>  parseScript函數(shù),代碼如下:</p><p>  可以看到這里有一個for循環(huán),通過fgets函數(shù)讀取nsi腳本中的一行,然后調(diào)用doParse函數(shù)來解析這行腳本代碼,解析完一行for循環(huán)</p><p>  繼續(xù)進(jìn)行,繼續(xù)讀取一行,解析腳本,直到解析完最后一行腳本代碼為止??!那么就來看看doParse是怎樣解析一行腳本代碼的&l

10、t;/p><p>  doParse函數(shù)中又使用了LineParser類來解析具體的一行腳本代碼,這里調(diào)用的是LineParser類的parse函數(shù),parse又調(diào)用了doline</p><p>  函數(shù)來解析一行代碼:</p><p>  這里用了while(*line)來遍歷這一行字符串,解析是否有NSIS指定的命令、屬性等關(guān)鍵字,具體的代碼在它的后面,如下圖所示

11、:</p><p>  m_tokens是一個char**二級指針,它保存了所有掃描到的token關(guān)鍵字,至此,解析完了這一行代碼了,就是把掃描到的關(guān)鍵字保存起來</p><p>  以便后續(xù)使用,當(dāng)然這些處理之后會回到CEXEBuild::doParse函數(shù)中, 就是thisline.parse((char*)str)返回,然后繼續(xù)執(zhí)行后面的代碼,</p><p>

12、;  這里的代碼首先使用get_commandtoken函數(shù)獲取這一行解析到的關(guān)鍵字對應(yīng)的TOK_XXX,如關(guān)鍵字Abort對應(yīng)的token是TOK_ABORT</p><p>  然后來到365行調(diào)用m_plugins.IsPluginCommand來判斷關(guān)鍵字是不是插件命令,如果是,則設(shè)置tkid為TOK__PLUGINCOMMAND</p><p>  然后是處理一些tkid為TOK

13、_P_XXX的預(yù)處理指令,最后是調(diào)用doCommand函數(shù)來解析這條命令!!</p><p>  doCommand這個函數(shù)非常大,代碼量超過5000行!一開始是調(diào)用build_plugin_table函數(shù)來建立插件表,就是掃描存放插件的目錄或</p><p>  !addplugindir指定的目錄下的*.dll文件,內(nèi)部會調(diào)用Plugins類的GetExports函數(shù)讀取這些dll文件

14、,讀取其中的導(dǎo)出表,并建立幾個命令</p><p>  對應(yīng)插件路徑或函數(shù)的哈希表(std::map類型),然后是一個大大的switch..case,根據(jù)TOK_XXX來執(zhí)行對應(yīng)的命令,插件命令的case是</p><p>  在5862行開始,這里以nsDialogs::Create 1018這個插件命令為例,就是說line.gettoken_str(0)得到的是nsDialogs::C

15、reate,line.gettoken_str(1)得到的是1018,當(dāng)然它們都是字符串,這里首先調(diào)用m_plugins.NormalizedCommand函數(shù)規(guī)范化插件命令名字</p><p>  也就是說你寫的不規(guī)范的話,它會幫你規(guī)范化(就是說插件::函數(shù)名,如nsDialogs::Create不區(qū)分大小寫哇?。。?,而后一句是得到了這個</p><p>  插件dll的全路徑名稱。然后

16、是調(diào)用內(nèi)部函數(shù)Initialize_____Plugins來初始化插件,接著是把這個dll加入到要生成的exe安裝程序中,它會釋放</p><p>  到%temp%目錄下($PLUGINSDIR)</p><p>  然后執(zhí)行到這里就是調(diào)用這個插件了!這里先掃描nsDialogs::Create后面的token,掃描到就使用EW_PUSHPOP這個which調(diào)用add_entry<

17、/p><p>  函數(shù)完成參數(shù)入棧操作,在NSIS中,腳本代碼的參數(shù)堆棧和插件中的參數(shù)堆棧是同一個,是共享的,因此在nsi腳本中將參數(shù)入棧,在插件中</p><p>  就能取出入棧的參數(shù)!最后是執(zhí)行which為EW_REGISTERDLL的add_entry函數(shù)來執(zhí)行插件命令!</p><p>  add_entry函數(shù)是定義在exehead\exec.c文件中的,其

18、中EW_REGISTERDLL的case如下圖所示</p><p>  很明顯,這里是使用了GetProcAddress來獲取插件dll中的函數(shù)的地址,然后動態(tài)調(diào)用這個函數(shù)?。?!注意的是插件dll中的導(dǎo)出函數(shù)的簽名</p><p>  必須是5個參數(shù),函數(shù)簽名是void (*func)(HWND,int,char*,void*,void*),NSIS給插件函數(shù)傳遞的參數(shù)分別是g_hwnd,

19、NSIS_MAX_STRLEN</p><p>  g_usrvars,&g_st,&plugin_extra_parameters,其中最重要的一個參數(shù)是(void*)&g_st,這個參數(shù)是實現(xiàn)nsi腳本和插件dll傳遞函數(shù)參數(shù)</p><p>  關(guān)鍵,其中g(shù)_st是一個指向_stack_t結(jié)構(gòu)的指針,這個結(jié)構(gòu)的定義如下圖所示</p><p&

20、gt;  nsDialogs::Create函數(shù)在插件dll中的定義是這樣的</p><p>  可以看到,它是符合void (*func)(HWND,int,char*,void*,void*)插件函數(shù)簽名的,g_st是指向_stack_t參數(shù)棧頂?shù)闹羔?,而NSIS將&g_st傳給</p><p>  了stacktop,然后賦值給了全局變量g_stacktop(通過EXDLL_

21、INIT宏),也就是說g_stacktop是指向g_st的指向,是指向參數(shù)堆棧頂?shù)亩壷羔?,使用二級指針的好處是?dāng)g_st的指向改變時,g_stacktop不用改變指向,一旦g_stacktop指向了g_st,不管g_st的指向有沒有改變,它都能找到g_st,進(jìn)而找到參數(shù)堆棧頂,然后你想取出堆棧中的參數(shù)就很容易了??!</p><p>  另一個參數(shù)extra_parameters *extra也很重要,它是實現(xiàn)回

22、調(diào)函數(shù)的關(guān)鍵!就是在nsi腳本中使用插件命令注冊一個回調(diào)函數(shù),當(dāng)事件發(fā)生時</p><p>  會被自動調(diào)用!當(dāng)然這個功能要在dll中使用extra這個參數(shù)來執(zhí)行注冊的回調(diào)函數(shù)才行</p><p>  為了方便取出參數(shù)堆棧中的參數(shù),NSIS的插件API還提供了幾個函數(shù)來操作g_stacktop,取出里面的參數(shù),如nsDialogs::Create 1018中就有</p>&l

23、t;p>  一個參數(shù),注意的是NSIS的插件命令中的參數(shù)全都是字符串,好在NSIS插件API還提供了popint()函數(shù)將參數(shù)字符串轉(zhuǎn)換成整型返回給你使用</p><p>  如本例的hwPlacementRect = GetDlgItem(hwndParent, popint())就是,這里是將字符串"1018"轉(zhuǎn)換成數(shù)字1018,原來它是一個控件ID號</p><

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論