免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
e(易精經第二章)

 

第二章、字節(jié)集

 

 

       字節(jié)集是易語言獨有的基本數(shù)據(jù)類型,按字面的意思來理解,所謂“字節(jié)集”就是“字節(jié)的集合”,其本質就是字節(jié)數(shù)組。從計算機基礎知識中,我們知道,一個字節(jié)就是8(bit),也就是8個“0或“1。計算機中所有的東西(指令和數(shù)據(jù))都是用01表示的,而以字節(jié)作為保存數(shù)據(jù)的最小單位,所以,字節(jié)集可以保存任何的數(shù)據(jù)——數(shù)字、文本、聲音、圖像、可執(zhí)行文件等等;反過來,一段字節(jié)集數(shù)據(jù)具體表示什么,關鍵看你是如何解讀它——你可以認為它是一段文本、一張圖片或是一首mp3中的一段。

       易語言的核心支持庫提供了很多字節(jié)集相關的函數(shù)(2-a),這些函數(shù)使得我們對字節(jié)集的處理異常方便。我們先來看看易語言本身對這些函數(shù)的簡要介紹。

 

字節(jié)集操作命令

簡要說明

取字節(jié)集長度

取字節(jié)集型數(shù)據(jù)的長度。

到字節(jié)集

將指定數(shù)據(jù)轉換為字節(jié)集后返回轉換結果。

取字節(jié)集數(shù)據(jù)

取出字節(jié)集中指定位置指定數(shù)據(jù)類型的數(shù)據(jù)。

取字節(jié)集左邊

返回一個字節(jié)集,其中包含指定字節(jié)集中從左邊算起指定數(shù)量的字節(jié)。

取字節(jié)集右邊

返回一個字節(jié)集,其中包含指定字節(jié)集中從右邊算起指定數(shù)量的字節(jié)。

取字節(jié)集中間

返回一個字節(jié)集,其中包含指定字節(jié)集中從指定位置算起指定數(shù)量的字節(jié)。

尋找字節(jié)集

返回一字節(jié)集在另一字節(jié)集中最先出現(xiàn)的位置,位置值從 1 開始。如果未找到,返回 -1。

倒找字節(jié)集

返回一字節(jié)集在另一字節(jié)集中最后出現(xiàn)的位置,位置值從 1 開始。如果未找到,返回 -1。

字節(jié)集替換

將指定字節(jié)集的某一部分用其它的字節(jié)集替換,然后返回替換后的結果。

子字節(jié)集替換

返回一個字節(jié)集,該字節(jié)集中指定的子字節(jié)集已被替換成另一子字節(jié)集,并且替換發(fā)生的次數(shù)也是被指定的。

取空白字節(jié)集

返回具有特定數(shù)目 0 字節(jié)的字節(jié)集。

取重復字節(jié)集

返回一個字節(jié)集,其中包含指定次數(shù)的字節(jié)集重復結果。

分割字節(jié)集

將指定字節(jié)集進行分割,返回分割后的一維字節(jié)集數(shù)組。

指針到字節(jié)集

返回指定內存指針所指向地址處的一段數(shù)據(jù),注意調用本命令前一定要確保所提供的內存地址段真實有效。本命令的最佳使用場合就是在易語言回調子程序和易語言DLL公開子程序用作獲取外部數(shù)據(jù)。

 

這些函數(shù)的使用都很簡單,但有些函數(shù)依然會使人迷惑,或者想更深入地了解其中的相關細節(jié)。所以我們先圍繞某些函數(shù)作一點深入的討論。

2.1 深入討論字節(jié)集相關函數(shù)

 

2.1.1 取字節(jié)集長度

       首先我們來研究一下“取字節(jié)集長度”函數(shù)是如何取得一個字節(jié)集長度的,因為它的效率決定了我們是否適合把它放在循環(huán)體中執(zhí)行。系統(tǒng)要計算一段字節(jié)集的長度,不外乎有兩個方法:①逐一累計,也就是把字節(jié)一個一個地數(shù)出來。 把字節(jié)集的長度存放在某個特殊的地方,需要的時候讀取出來,在字節(jié)集操作的過程中即時更新該長度數(shù)據(jù)。系統(tǒng)具體采用的是哪一種方法,我們作一個簡單的測驗便知。

       新建一個易語言程序,在窗體上放一個按鈕,為該按鈕寫如下代碼:

 

.版本 2

 

.子程序 _按鈕測試計算方式_被單擊

.局部變量 數(shù)據(jù), 字節(jié)集

.局部變量 上次時間

 

數(shù)據(jù) = 取空白字節(jié)集 (1) ' 字節(jié)集的長度不論是1還是1000000,計算的時間不變

上次時間 = 取啟動時間 ()

.計次循環(huán)首 (1000000, )

    取字節(jié)集長度 (數(shù)據(jù))

.計次循環(huán)尾 ()

輸出調試文本 (取啟動時間 () - 上次時間)

 

       這段代碼先分配一定長度的字節(jié)集數(shù)據(jù),然后執(zhí)行“取字節(jié)集長度”函數(shù)一百萬次,我的機器測得所花的時間是31毫秒左右。如果你的機器速度很快,測得的時間是0毫秒,請將循環(huán)次數(shù)增加。然后我把字節(jié)集數(shù)據(jù)的長度改為1000000,再次運行該程序,測得的結果依然大約是31毫秒。由此可見,易語言的“取字節(jié)集長度”函數(shù)并不是蠢蠢地一個一個字節(jié)字節(jié)地數(shù),而是把字節(jié)集的長度存放在了某個特殊的地方,需要的時候就把它讀出來。那么具體存放在何處呢?這也有幾種可能:① 存放在字節(jié)集的開始處。② 存放在字節(jié)集的末尾處。 存放在字節(jié)集開始處更前面的位置。 存放在內存堆棧中的某個表中,然后與指定的字節(jié)集變量建立聯(lián)系。很顯然,存放在字節(jié)集末尾的可能性很小,不然系統(tǒng)如何知道一段字節(jié)集到何處結束?而如果存放在內存中的表中,需要進行額外的查表操作,顯得過于煩瑣,

       我們先來測試簡單的,這也需要做試驗。首先我們需要獲得字節(jié)集數(shù)據(jù)的內存地址,這個我們可以通過“取變量地址”函數(shù)獲得——就像第一章中“自定義數(shù)據(jù)類型的內存存儲”一節(jié)中那樣,如果不太清楚,請先轉回去看那一節(jié)。不過字節(jié)集的變量地址更簡單——我們只用取字節(jié)集的第一個元素的地址就得到了,不用轉來轉去那么麻煩。得到地址之后,我們就看該地址的第一個整數(shù)型數(shù)據(jù)是否是字節(jié)集的長度,如果不是,那顯然沒有把長度信息放在開頭;如果不在開頭,我們再把地址指針向內存的低地址方向移動一個整數(shù)長度,也就是4個字節(jié),我猜想長度信息也有可能存放在那里。具體的試驗代碼如下:

 

.版本 2

.支持庫 spec

 

.子程序 _按鈕存儲位置_被單擊

.局部變量 數(shù)據(jù), 字節(jié)集

.局部變量 地址, 整數(shù)型

.局部變量 臨時地址, 整數(shù)型

.局部變量 臨時字節(jié)集, 字節(jié)集

.局部變量 長度, 整數(shù)型

 

數(shù)據(jù) = 到字節(jié)集 (abcdefg)  ' 長度7,你可以更改此字符串,觀察其他長度

地址 = 取變量地址 (數(shù)據(jù) [1])  ' 獲得字節(jié)集數(shù)據(jù)的內存地址

輸出調試文本 (指針到文本 (地址))  ' 此處輸出正確的字符串,證明地址是正確的

 

臨時字節(jié)集 = 指針到字節(jié)集 (地址, 4)

長度 = 取字節(jié)集數(shù)據(jù) (臨時字節(jié)集, #整數(shù)型, )

輸出調試文本 (長度)  ' 此處輸出錯誤的結果。

 

地址 = 地址 - 4 ' 將內存指針回退一個整數(shù)型的數(shù)據(jù)長度,也就是4個字節(jié)

臨時字節(jié)集 = 指針到字節(jié)集 (地址, 4)

長度 = 取字節(jié)集數(shù)據(jù) (臨時字節(jié)集, #整數(shù)型, )

輸出調試文本 (長度)  ' 此處輸出正確的字節(jié)集長度7

' 由此可見,字節(jié)集的長度存在該字節(jié)的首地址前面的一個整數(shù)中。

 

       試驗的結果再一次證實了我的猜想。知道了易語言取字節(jié)集長度的核心方式,我們就可以大膽地在循環(huán)中使用“取字節(jié)集長度”函數(shù)而不用擔心影響效率了,同時也深入地理解了字節(jié)集的存儲方式。

 

2.1.2 取字節(jié)集數(shù)據(jù)

       前面我們已經說過,字節(jié)集中保存的是什么數(shù)據(jù),關鍵看我們如何轉譯它。因此我們可以從一個“文本”字節(jié)集中讀取整數(shù)型數(shù)據(jù),也可以從一個“整數(shù)型”字節(jié)集中讀取日期時間型數(shù)據(jù),也可以在文本和字節(jié)集類型中方便地相互轉換。此外,系統(tǒng)還提供其他的諸如“到文本”、“到數(shù)值”函數(shù),但到底何時采用哪個函數(shù),這是一個問題。

       我的建議是對字節(jié)集數(shù)據(jù)不要使用“到數(shù)值”函數(shù),應該使用“取字節(jié)集數(shù)據(jù)”函數(shù),根據(jù)需要傳遞#整數(shù)型、#字節(jié)型、#小數(shù)型等參數(shù)。否則你會發(fā)現(xiàn)程序運行的結果不對頭,但即使是拿了放大鏡也看不出源代碼問題在哪里,請看下面的代碼片斷,輸出結果是什么:

 

.版本 2

 

.子程序 _按鈕字節(jié)集到數(shù)值_被單擊

.局部變量 某字節(jié)集, 字節(jié)集

 

某字節(jié)集 = 到字節(jié)集 (1234)

輸出調試文本 (到數(shù)值 (某字節(jié)集))

某字節(jié)集 = 到字節(jié)集 (1234)

輸出調試文本 (到數(shù)值 (某字節(jié)集))

 

       也許你覺得至少有一個要輸出1234吧,但很不幸,輸出結果卻都是0。但使用“輸出調試文本(取字節(jié)集數(shù)據(jù)(某字節(jié)集,#整數(shù)型,))”可以得到可預見的正確數(shù)據(jù)。

 

       其次是關于“到文本()”和“取字節(jié)集數(shù)據(jù)(...,#文本型,)”的區(qū)別問題。對于字節(jié)集參數(shù)來說,“到文本()”相當于“取字節(jié)集數(shù)據(jù)(...,#文本型,1)”,顯然,“取字節(jié)集數(shù)據(jù)()”功能要強大些,可以取指定位置的文本數(shù)據(jù)。我們知道,在易語言中,文本變量是以0作為文本的結尾的,而一段字節(jié)集中可能有多個0分割的文本,“到文本()”函數(shù)只能取出第一個文本。請看示例代碼:

 

.版本 2

 

.子程序 _按鈕字節(jié)集到文本_被單擊

.局部變量 某字節(jié)集, 字節(jié)集

 

某字節(jié)集 = 到字節(jié)集 (abcdefghijklmnopq)

輸出調試文本 (到文本 (某字節(jié)集))  ' 輸出全部

 

某字節(jié)集 [8] 0  ' 插入一個0

輸出調試文本 (到文本 (某字節(jié)集))  ' 文本被截斷,只輸出abcdefg

輸出調試文本 (取字節(jié)集數(shù)據(jù) (某字節(jié)集, #文本型, 1)) ' 與上一行輸出結果相同

輸出調試文本 (取字節(jié)集數(shù)據(jù) (某字節(jié)集, #文本型, 9)) ' 輸出后一半,ijklmnopq

 

       由代碼的運行結果可以看出,“取字節(jié)集數(shù)據(jù)(...#文本型,)”很靈活,可以任意指定從指定位置開始的文本,直到遇到0結束。

 

       此外,“取字節(jié)集數(shù)據(jù)”函數(shù)還允許我們像讀取文件那樣按順序讀取一段數(shù)據(jù),請看易語言集成開發(fā)環(huán)境中對“取字節(jié)集數(shù)據(jù)”函數(shù)第三個參數(shù)的描述:

 

“參數(shù)<3>的名稱為“起始索引位置”,類型為“整數(shù)型(int)”,可以被省略。指定從字節(jié)集的什么地方開始取數(shù)據(jù),索引值從1開始。如果被省略,默認為數(shù)值1。如果為本參數(shù)提供一個整數(shù)型變量,則命令執(zhí)行后將自動修改該變量內容,將其索引值移動到下一個讀入位置。如果移動后到達字節(jié)集的末尾,將修改該變量的內容為-1。”

 

具體的使用代碼如下:

 

.版本 2

 

.子程序 _按鈕順序讀取_被單擊

.局部變量 某字節(jié)集, 字節(jié)集

.局部變量 指針位置, 整數(shù)型

 

某字節(jié)集 = 到字節(jié)集 (http://goomoo.cn) { 0 } ' 注意文本后面添加一個0表示文本結束

某字節(jié)集 = 某字節(jié)集 + 到字節(jié)集 (12345)

某字節(jié)集 = 某字節(jié)集 + 到字節(jié)集 ([2003年10月1])

 

指針位置 = 1

輸出調試文本 (取字節(jié)集數(shù)據(jù) (某字節(jié)集, #文本型, 指針位置))  ' 輸出 http://goomoo.cn

輸出調試文本 (取字節(jié)集數(shù)據(jù) (某字節(jié)集, #整數(shù)型, 指針位置))  ' 輸出 12345

輸出調試文本 (取字節(jié)集數(shù)據(jù) (某字節(jié)集, #日期時間型, 指針位置))  ' 輸出 2003年10月1

輸出調試文本 (指針位置)  ' 輸出 -1 ,表示到字節(jié)集尾。

 

2.1.3 指針到字節(jié)集

       易語言自身對此函數(shù)的附加說明是這樣的:“本命令的最佳使用場合就是在易語言回調子程序和易語言DLL公開子程序用作獲取外部數(shù)據(jù)。”,其實“指針到字節(jié)集”函數(shù)的實質是讀取本進程內指定內存位置的指定長度的數(shù)據(jù),因此,我們可以使用此函數(shù)遍歷本進程的所有內存空間,當然,這需要你對Windows的內存管理機制有足夠的了解才行。

       因為還沒有涉及到回調函數(shù)和DLL編程,這里我們以一個小例子來試驗一下“指針到字節(jié)集”的神奇之處。Windows在執(zhí)行一個可執(zhí)行文件的時候,首先會把該文件映射到該進程內存的0x400000地址處,這是一個十六進制的地址,轉換成十進制是4194304,我們就從該地址讀取10KB的數(shù)據(jù)看看是什么,具體代碼如下:

 

.版本 2

 

.子程序 _按鈕從內存讀取本執(zhí)行文件映像_被單擊

.局部變量 某字節(jié)集, 字節(jié)集

 

某字節(jié)集 = 指針到字節(jié)集 (4194304, 10 × 1024)  ' 4194304處讀取10KB的數(shù)據(jù)。

寫到文件 (c:\~asdf.dat, 某字節(jié)集) ' 將該數(shù)據(jù)寫到一個臨時文件中

運行 (notepad.exec:\~asdf.dat, , )  ' 用記事本打開查看

刪除文件 (c:\~asdf.dat)

 

       運行結果如圖2.1.3-a所示:

2.1.3-a 指針到字節(jié)集試驗運行結果

       如果你是一位有心人,你就會發(fā)現(xiàn)這些“亂碼”很眼熟——這不就是一個exe文件的內容嗎?!正是,這個小試驗就證實了“指針到字節(jié)集”實際上讀取的是本進程的指定內存地址的數(shù)據(jù)。

       關于“指針到字節(jié)集”的更多用法,請參閱后續(xù)的DLL編程相關章節(jié)。

 

2.2 [] 十六進制查看器

       首先我們演示一下字節(jié)集的字節(jié)數(shù)組特性,我們來寫一個小程序,把任何文件的內容以十六進制的形式顯示出來。市面上早就有很多這樣的軟件了,而且已經非常成熟,比如WinHexUltraEdit,HexWorkShop 等(圖2.1-aUltraEdit 顯示的一個exe文件的部分數(shù)據(jù)),但有時候我們的軟件也需要我們自己來實現(xiàn)這樣的功能,比如我們要寫串口通信程序、內存查看器等。

       先進行問題分析。

為了便于代碼重用,我們決定把這個功能寫成一個函數(shù),該函數(shù)接收三個參數(shù):

①要轉換的字節(jié)集。

②是否顯示數(shù)據(jù)地址。

③是否顯示文本。

2.2-aUltraEdit 中顯示一個Exe文件的16進制數(shù)據(jù)

 

函數(shù)返回轉換后的文本,這樣我們就可以很方便地顯示到編輯框等控件中了。在轉換的過程中,需要把字節(jié)集掰成一個個的字節(jié),我們使用易語言自帶的“取十六進制文本”把它轉換成十六進制的形式。但這里有一個問題:如果字節(jié)的值小于或等于16,那么轉換后就是一位,而大于16的轉換后就是兩位,要是文本整齊美觀,對于小于一位的,就需要在前面補0;還有,如果要顯示數(shù)據(jù)地址,則也需要補齊長度;我們決定每行顯示16個字節(jié),那么字節(jié)集的長度有可能不是16的整數(shù)倍數(shù),那么也需要在文本的后面補上空白文本以對齊。顯然,我們需要寫一個這樣的函數(shù),該函數(shù)能夠在指定的文本前后添加重復的字符,使該文本達到指定的長度。這個函數(shù)的代碼如下:

 

.版本 2

 

.子程序 填充重復文本, 文本型, , 將文本的左側或右側添加字符到指定的長度

.參數(shù) 參原始文本, 文本型

.參數(shù) 參要達到的長度, 整數(shù)型

.參數(shù) 參填充的字符, 文本型, 可空, 取第一個字符

.參數(shù) 參在左側, 邏輯型, 可空, 不在左側就在右側

.局部變量 要增加的長度, 整數(shù)型

.局部變量 字符, 文本型

.局部變量 文本, 文本型

 

.如果真 (是否為空 (參填充的字符))

    參填充的字符= “ ”  ' 默認使用空格填充

.如果真結束

.如果真 (是否為空 (參在左側))

    參在左側= 真  ' 默認填充左側

.如果真結束

.如果真 (參填充的字符 = “”)

    返回 (參原始文本)

.如果真結束

要增加的長度 = 參要達到的長度 - 取文本長度 (參原始文本)

.如果真 (要增加的長度 ≤ 0)

    返回 (參原始文本)

.如果真結束

字符 = 取文本左邊 (參填充的字符, 1)  ' 只要第一個字符

文本 = 取重復文本 (要增加的長度, 字符)

.如果真 (參在左側)

    返回 (文本 + 參原始文本)

.如果真結束

返回 (參原始文本 + 文本)

 

       我們的計劃是每行顯示16個字節(jié),可以指定是否顯示地址和文本。所以我們需要把一行文本分為三段:左側的地址、中間的十六進制文本、右邊的普通文本。左側的地址我們可以直接通過循環(huán)的計數(shù)器得到;中間的我們把一個個字節(jié)轉換成16進制文本后累加;右側的文本則麻煩一些,因為有些控制字符是無法顯示出來的(比如空字符、回車、換行、響鈴、退格等),這些字符的ASCII碼都小于32,這里我們按照慣例,統(tǒng)一把它們轉換成“.”,其ASCII碼是46。當計數(shù)器與16的余數(shù)是0的時候,就表示已完成一行,我們在其中插入換行符。具體的實現(xiàn)代碼如下:

 

.版本 2

 

.子程序 字節(jié)集到十六進制文本, 文本型

.參數(shù) 參數(shù)據(jù), 字節(jié)集

.參數(shù) 參是否顯示地址, 邏輯型

.參數(shù) 參是否顯示文本, 邏輯型

.局部變量 數(shù)據(jù)長度, 整數(shù)型

.局部變量 i, 整數(shù)型

.局部變量 結果文本, 文本型

.局部變量 一行文本, 文本型

.局部變量 某字節(jié), 字節(jié)型

.局部變量 右側文本, 文本型

 

數(shù)據(jù)長度 = 取字節(jié)集長度 (參數(shù)據(jù))

.計次循環(huán)首 (數(shù)據(jù)長度, i)

    某字節(jié)= 參數(shù)據(jù) [i]

    一行文本= 一行文本 + “ ” + 填充重復文本 (取十六進制文本 (某字節(jié)), 2, 0)

    .如果真 (參是否顯示文本)

        .如果真 (某字節(jié) < 32)

            某字節(jié)= 46  ' 原點符號

        .如果真結束

        右側文本= 右側文本 + 字符 (某字節(jié))

    .如果真結束

    .如果真 (i 16 0)  ' 假定每行16個字節(jié)

        .如果真 (參是否顯示地址)

            一行文本= 填充重復文本 (取十六進制文本 (i 16), 4, 0, ) + “ | ” + 一行文本

        .如果真結束

        .如果真 (參是否顯示文本)

            一行文本= 一行文本 + “ | ” + 右側文本

        .如果真結束

        結果文本= 結果文本 + 一行文本 + #換行符  ' 滿一行就插入回車符并添加到結果文本中

        一行文本= “”

        右側文本= “”

    .如果真結束

 

.計次循環(huán)尾 ()

 

' 最后一行有可能數(shù)據(jù)剛好不能被16整除,則不滿足條件i%16=0,所以要補上最后的一段

.如果真 (一行文本 ≠ “”)

    .如果真 (取文本長度 (一行文本) 48)

        一行文本= 填充重復文本 (一行文本, 48, “ ”, )

    .如果真結束

    .如果真 (參是否顯示地址)

        一行文本= 填充重復文本 (取十六進制文本 (i i 16), 4, 0, ) + “ | ” + 一行文本

    .如果真結束

    .如果真 (參是否顯示文本)

        一行文本= 一行文本 + “ | ” + 右側文本

    .如果真結束

    結果文本= 結果文本 + 一行文本 + #換行符

.如果真結束

返回 (結果文本)

 

       在窗體上添加一個按鈕,標題為“打開文件”;添加兩個選擇框,分別命名為“顯示地址”和“顯示文本”,默認均選中;添加一個編輯框,“是否允許多行”屬性為“真”,“滾動條”選擇“縱向滾動條”;添加一個通用對話框用來打開文件;添加一個程序集變量“集字節(jié)集”,我們需要把讀入的字節(jié)集保存到其中。為按鈕、選擇框添加以下的代碼:

 

.版本 2

 

.子程序 _按鈕打開文件_被單擊

.如果真 (通用對話框1.打開 ())

    集字節(jié)集= 取字節(jié)集左邊 (讀入文件 (通用對話框1.文件名), 23 × 1024)

    編輯框1.內容 = 字節(jié)集到十六進制文本 (集字節(jié)集, 選擇框顯示地址.選中, 選擇框顯示文本.選中)

.如果真結束

 

=====================================================

.子程序 _選擇框顯示地址_被單擊

編輯框1.內容 = 字節(jié)集到十六進制文本 (集字節(jié)集, 選擇框顯示地址.選中, 選擇框顯示文本.選中)

=====================================================

.子程序 _選擇框顯示文本_被單擊

編輯框1.內容 = 字節(jié)集到十六進制文本 (集字節(jié)集, 選擇框顯示地址.選中, 選擇框顯示文本.選中)

 

       在用戶單擊“打開文件”按鈕后,我們使用“讀入文件”函數(shù)讀取文件,“讀入函數(shù)”返回的是文件的字節(jié)集數(shù)據(jù)。這里我們考慮到速度的原因,只取其前面的23KB,如果不這樣的話,超過100KB的文件就很慢了。而專業(yè)的軟件如WinHex、UltraEdit等則可以讀取多達幾百兆甚至上G的文件,而且依然很快,顯然是經過優(yōu)化了的。其優(yōu)化的方法是這樣的:并不是一次將所有的文件讀出,而是讀取文件的一段數(shù)據(jù),比如800個字節(jié),按每行16個字節(jié)計,也就是50行。拖動滾動條的時候,將文件的讀寫指針以每16字節(jié)為單位進行偏移讀取,轉換后更新編輯框。這樣每次只處理800個字節(jié),速度當然就很快了(具體的讀取方法,請參看下一例:文件分割機)。當然這樣的話,就要去掉編輯框的滾動條,然后加上我們自己的滾動條進行額外的處理。這個優(yōu)化的過程就留給你作為課外練習了,如果有疑問,請上本書的論壇(http://goomoo.cn/)討論。

       最后我們運行程序,隨便打開一個文件,得到如圖2.2-b的運行結果,跟UltraEdit顯示的結果是一致的。

2.2-b 文件的16進制顯示

 

 

2.3 [] 無需合并程序的文件分割機

       在軟盤流行的時代,也廣泛流傳著一類軟件:文件分割機。這些軟件能夠把指定的文件分割成指定大小的單個文件,這些單個文件的大小剛好小于或等于軟盤的容量,因此可以把它們拷貝入多張軟盤。再以軟盤為媒介,拷貝到目標機器上,然后用軟件自帶的功能或僅僅使用DOS批處理文件將它們拼裝起來,生成和原始文件一樣的文件。雖然現(xiàn)在U盤和大容量移動硬盤的方便、廉價和穩(wěn)定,早已把軟驅和軟盤推進了歷史,但把寫這種軟件作為一個練習還是蠻不錯的。

       今天我們就來寫一個這樣的軟件,我們的目標是:

              ①可以由用戶指定要分割的文件。

              ②可以有用戶指定分割后的單個文件的大小。

              ③使用批處理文件來組裝文件。其原因是一方面你不能指望目標機器也安裝了你的分割機,另一方面批處理文件的尺寸非常小,便于軟盤拷貝。

       為了方便生成最后的批處理文件,我們先來練習一個DOS命令:Copy。Copy 命令用來復制文件,其最簡單的用法是:

       copy源文件名 目的文件名

       執(zhí)行之后就將源文件名所指的文件復制到目標文件名所指的文件。Copy命令不光能復制文件,還能夠將多個文件合并起來。請執(zhí)行如下操作:

       ①點擊開始菜單,點擊“運行...”菜單項。

       ②在運行窗口中,輸入“cmd”,如果是Windows98 WindowsME,則輸入command,輸入完畢按回車鍵。

       ③在彈出的命令提示符窗口中輸copy /? ,然后按回車鍵,我們得到了關于Copy命令的詳細幫助,如下:

C:\Documents and Settings\Administrator>copy/?

將一份或多份文件復制到另一個位置。

 

COPY [/D] [/V] [/N] [/Y | /-Y] [/Z] [/A | /B ]source [/A | /B]

     [+source [/A | /B] [+ ...]] [destination [/A | /B]]

 

 source       指定要復制的文件。

  /A           表示一個 ASCII 文本文件。

  /B           表示一個二進位文件。

  /D           允許解密要創(chuàng)建的目標文件

 destination  為新文件指定目錄和/或文件名。

  /V           驗證新文件寫入是否正確。

  /N           復制帶有非 8dot3 名稱的文件時,

              盡可能使用短文件名。

  /Y           不使用確認是否要覆蓋現(xiàn)有目標文件

              的提示。

  /-Y          使用確認是否要覆蓋現(xiàn)有目標文件

              的提示。

  /Z           用可重新啟動模式復制已聯(lián)網的文件。

 

命令行開關 /Y 可以在 COPYCMD 環(huán)境變量中預先設定。

這可能會被命令行上的 /-Y替代。除非 COPY

命令是在一個批處理腳本中執(zhí)行的,默認值應為

在覆蓋時進行提示。

 

要附加文件,請為目標指定一個文件,為源指定

數(shù)個文件(用通配符或 file1+file2+file3 格式)。

 

       在幫助文件的最后,我們看到一句話“要附加文件,請為目標指定一個文件,為源指定

數(shù)個文件(用通配符或 file1+file2+file3 格式)。”也就是說,我們可以通過“+”把多個文件合并拷貝為一個文件。在合并拷貝的時候,我們要采用二進制模式,所以還需要加上“/B”開關選項,所以,最后生成的批處理文件應該類似這樣的樣子:

       copy/b 文件名.擴展名.1+文件名.擴展名.2+...+文件名.擴展名.n 文件名.擴展名

 

開工了,我們新建一個易語言程序,設計如下的窗體:

2.3-a 文件分割機窗體

 

       其中,長編輯框命名為“編輯框待分割的文件名”;短編輯框的名稱為“編輯框塊大小”,“輸入方式”設為“整數(shù)文本輸入”;組合框名稱為“組合框單位”,有三項:字節(jié)、KBMB,供用戶選擇;按鈕的命名均為“按鈕”+ 按鈕的標題;“取消”按鈕的“禁止”屬性設為真。

       在寫代碼之前,先簡要介紹一下文件讀寫方面的操作。在易語言中,讀寫文件最簡單的是使用“讀入文件”和“寫出文件”函數(shù),“讀入文件”直接把文件內容讀入到字節(jié)集變量,而“寫出文件”則直接把字節(jié)集寫出到指定的文本,很是方便,當然這兩個函數(shù)不適合操縱大文件,否則太占內存,效率低。

       讀寫文件的另一種方式是標準的“打開文件-讀取數(shù)據(jù)-關閉文件”三步曲。該方式需要先使用“打開文件”得到一個文件號,然后圍繞該文件號進行“移動讀寫位置”和讀寫數(shù)據(jù)操作,在讀寫文件操作完畢后要記得關閉文件號,以免影響或許有其他進程對該文件的讀寫操作。關于讀寫文件的詳細介紹,請參看本書的“文件格式編程”一章。

       此程序中還涉及到文件名、文件大小和目錄等操作,這些函數(shù)都很簡單,看看易語言編程環(huán)境提示窗口中的信息就能理解。其中經常需要得到去除路徑的文件名,于是寫了一個函數(shù)“去除文件名路徑”(2.3-b),如果文件名中有“\”,則返回“\”后的文本,否則返回原始文本。

 

2.3-b 去除文件名路徑函數(shù)

 

       分割文件整個步驟是這樣的:

              創(chuàng)建保存分割后文件的目錄。

              打開要分割的文件。

              從文件讀取指定長度的數(shù)據(jù)塊。

              將數(shù)據(jù)塊保存到按規(guī)則生成的文件名的文件中。

              將新生成文件的文件名添加到批處理文本后面,更新進度條。

              循環(huán)③至⑤直到文件讀完。

              關閉文件。

              生成批處理文件并保存。

 

       下面我們看看整個程序的源代碼。

 

.版本 2

 

.程序集 窗口程序集1

.程序集變量 集已取消, 邏輯型

=========================================================

.子程序 _按鈕開始分割_被單擊

.局部變量 文件大小, 整數(shù)型

.局部變量 分塊大小, 整數(shù)型

.局部變量 分塊文件名, 文本型

.局部變量 批處理文本, 文本型

.局部變量 目錄, 文本型

.局部變量 文件名, 文本型

.局部變量 文件號, 整數(shù)型

.局部變量 臨時字節(jié)集, 字節(jié)集

.局部變量 i, 整數(shù)型

 

文件名 = 編輯框待分割的文件.內容

.如果真 (文件名 = “”)

    信息框 (“請指定要分割的文件!”, #錯誤圖標, )

    返回 ()

.如果真結束

.如果真 (文件是否存在 (文件名) = 假)

    信息框 (“指定要分割的文件不存在!”, #錯誤圖標, )

    返回()

.如果真結束

文件大小 = 取文件尺寸 (文件名)

分塊大小 = 到數(shù)值 (編輯框塊大小.內容)

.如果真 (分塊大小 = 0)

    信息框 (“請指定分塊大小,并選擇合適的單位。”, #信息圖標, )

    返回 ()

.如果真結束

.判斷開始 (組合框單位.內容 = “KB)

    分塊大小= 分塊大小 × 1024

.判斷 (組合框單位.內容 = “MB)

    分塊大?。?分塊大小 × 1024 × 1024

.默認

 

.判斷結束

.如果真 (分塊大小 ≥ 文件大小)

    信息框 (“文件本身比分塊大小還小,不用分割。”, #信息圖標, )

    返回 ()

.如果真結束

集已取消 = 假

按鈕開始分割.禁止 = 真

按鈕取消.禁止 = 假

目錄 = 文件名 + “.部分”

創(chuàng)建目錄 (目錄)  ' 創(chuàng)建一個目錄用來保存分割后的文件們。

文件號 = 打開文件 (文件名, #讀入, #禁止寫)

.如果真 (文件號 = 0)

    信息框 (“打開文件失敗!”, #錯誤圖標,)

    返回 ()

.如果真結束

文件名 = 去除文件名路徑 (文件名)

.循環(huán)判斷首 ()

    臨時字節(jié)集= 讀入字節(jié)集 (文件號, 分塊大小)  ' 讀入指定長度的數(shù)據(jù),文件讀寫指針會自動移位

    i i 1

    分塊文件名= 目錄 + “\” + 文件名 + “.” + 到文本 (i)  ' 生成分塊文件名

    寫到文件 (分塊文件名, 臨時字節(jié)集)  ' 將數(shù)據(jù)寫到該分塊文件

    分塊文件名= #引號 + 去除文件名路徑 (分塊文件名) #引號

    ' 去掉路徑,絕對路徑在別的機器上可能無法合并;加上引號,防止文件名中有空格

    .如果 (批處理文本 = “”)

        批處理文本= 分塊文件名

    .否則

        批處理文本= 批處理文本 + “+” + 分塊文件名  ' 拼裝批處理文本的中間部分

    .如果結束

    進度條1.位置 = 取讀寫位置 (文件號) × 100 ÷ 文件大小 ' 計算進度條位置

    處理事件 ()  ' 用戶更新進度條和響應用戶單擊取消按鈕。

    .如果真 (集已取消)

        關閉文件 (文件號)

        按鈕開始分割.禁止 = 假

        按鈕取消.禁止 = 真

        返回 ()

    .如果真結束

 

.循環(huán)判斷尾 (是否在文件尾 (文件號, ) = 假)

' 組合批處理命令

' 在批處理命令中加上引號,防止文件名中有空格;最后加上pause暫停,讓用戶可以看清楚屏幕顯示

批處理文本 = “copy /b ” + 批處理文本 + “ ” + #引號 + 文件名 + #引號 + #換行符+ “pause” + #換行符

寫到文件 (目錄 + “\合并文件.bat, 到字節(jié)集 (批處理文本))  ' 直接將字節(jié)集寫入文件就可以了。

信息框 (“分割文件完畢!”, #信息圖標, )

按鈕開始分割.禁止 = 假

按鈕取消.禁止 = 真

運行 (explorer.exe ” +目錄, , )  ' 用資源管理器瀏覽分割后的文件。

 

=========================================================

.子程序 去除文件名路徑, 文本型, , 此函數(shù)用來去除文件名中的路徑。

.參數(shù) 參帶路徑文件名, 文本型

 

返回 (取文本右邊 (參帶路徑文件名, 取文本長度 (參帶路徑文件名) -倒找文本 (參帶路徑文件名, \, , )))

 

=========================================================

.子程序 _按鈕瀏覽_被單擊

 

.如果真 (通用對話框1.打開 ())

    編輯框待分割的文件.內容 = 通用對話框1.文件名

.如果真結束

 

=========================================================

.子程序 _按鈕取消_被單擊

 

集已取消 = 真

 

 

       2.3-c文件分割機的執(zhí)行結果

 

 

2.4 [] 生成EXE文件并在其中寫入配置信息

       假如你使用過木馬軟件,你一定對其這樣的功能不會陌生:木馬主程序會根據(jù)你填寫的某些信息,比如服務器地址、端口號和密碼等生成一個特定的EXE文件,該EXE文件能夠根據(jù)填入的參數(shù)執(zhí)行特定的操作。難道木馬主程序有編譯功能?! 非也,其實該EXE文件是事先就編譯好了的,然后作為資源放入EXE文件中。木馬主程序在生成木馬的時候,先把該資源寫到磁盤文件,然后修改該文件中的某些數(shù)據(jù),將配置信息寫入。木馬程序運行的時候,先從自身把這些配置信息讀取出來,然后執(zhí)行相應的操作。

       一般來說,有兩種方法把配置信息寫入EXE文件中,① 將配置信息寫到EXE文件的末尾,很多木馬采用的是這種方法。② 改寫EXE文件的中間部分。第一種方法實現(xiàn)起來比較簡單,但不幸的是由于易語言獨立編譯后EXE文件的特殊性,在其后添加數(shù)據(jù)后就無法運行了,所以此法并不實用。非獨立編譯的EXE文件雖然可以把數(shù)據(jù)寫在尾部,但運行時要帶支持庫,那還不如把配置信息存在一個獨立的文件中,因此也沒有什么意義。這里我們就討論第二種方法,修改EXE文件的中間數(shù)據(jù)。但具體修改哪個位置呢?況且EXE文件可不是隨便能修改的,修改不正確會導致程序無法運行,還真有點麻煩。這里我給大家介紹一個小技巧。

       還是先來做一個試驗。新建一個易程序,在窗體上添加一個按鈕。雙擊該按鈕,添加如下代碼:

 

.版本 2

 

.子程序 _按鈕顯示_被單擊

.局部變量 配置信息, 文本型

 

配置信息 = “http://goomoo.cn

信息框 (配置信息, 0, )

 

       運行該程序,單擊按鈕,就顯示出變量的值。

       將該程序編譯成EXE文件,再用UltraEdit打開,我們可以看到變量的值就在EXE文件中(圖2.4-a 如果沒看到,請使用搜索功能)。

 

 

2.4-a 字符串被編譯程序存在EXE文件中

 

       既然能找到它,那就好辦了。我們把該字符串修改成別的字符串試試,比如:“易容大師”,注意字符串的長度不要超過原來變量的長度哦,否則程序就有可能不能運行了。如果字符串比原來的短,請在左側的十六進制輸入?yún)^(qū)用0補足。修改完畢如圖2.4-b所示。在修改的過程中,有時中文被拆分成兩半而顯示成亂碼,不去理會。修改完畢保存文件。

       再次運行程序,點擊按鈕,哈哈,一句代碼不寫,就在外部修改了變量的內容。

 

 

2.4-b 修改EXE文件中的字符串

 

       既然能夠手動修改了,那么改成程序修改也不是難事,只要進行一個字節(jié)集替換就可以了。請按下面的步驟操作:

 

1> 新建一個易程序,添加一個按鈕,為按鈕寫如下代碼:

 

.版本 2

 

.子程序 _按鈕顯示_被單擊

.局部變量 配置信息, 文本型

.局部變量 臨時文本數(shù)組, 文本型, , "0"

 

配置信息=“www.goomoo.cn|4567|admin|goomoo |                          

' 注意在后面加些空格占用存儲空間,以免替換的信息超過長度

臨時文本數(shù)組 = 分割文本 (配置信息, |, )

信息框 (“主機:” + 臨時文本數(shù)組 [1] #換行符 + “端口:” + 臨時文本數(shù)組 [2] #換行符 + “用戶名:” + 臨時文本數(shù)組 [3] #換行符 + “密碼:” + 臨時文本數(shù)組 [4], 0, )

 

       運行該程序,單擊按鈕,應該得到這樣的運行結果:

2.4-c 程序被配置前的運行結果

 

       將該程序編譯成“待修改程序.exe”。

 

2> 再新建一個易程序。點擊菜單“插入>資源>圖片或圖片組...”,在資源表格中雙擊“內容”下的空白格,出現(xiàn)“圖片或圖片組資源屬性”對話框,點擊其中的 [導入新圖片] 按鈕,出現(xiàn)一個瀏覽文件對話框,在“文件類型”旁邊選擇“所有文件(*.*)”,選中剛才編譯的“待修改程序.exe”文件,把它作為圖片資源添加進來(見圖2.4-c)。將該資源重命名為“待修改程序”。

 

2.4-c EXE文件作為圖片資源加入

 

3> _啟動窗口上添加4個標簽控件、4個編輯框控件和1個通用對話框控件。布局如圖2.4-d所示。

2.4-d 窗體布局

       其中編輯框的名字設置為“編輯框”+前面標簽的名字。通用對話框的“類型”設置為“保存文件”,“過濾器”設置為“EXE文件(*.exe)|*.exe”,“默認后綴”設為“exe”。雙擊按鈕,添加以下代碼:

 

.版本 2

 

.子程序 _按鈕生成_被單擊

.局部變量 文件內容, 字節(jié)集

.局部變量 要替換成的文本, 文本型

.局部變量 原始文本, 文本型

.局部變量 長度差, 整數(shù)型

 

.如果真 (通用對話框1.打開 () = 假)

    返回 ()

.如果真結束

原始文本 = “www.goomoo.cn|4567|admin|goomoo |                             ' !??!注意這里的文本要和被配置程序里的文本一樣?。?!

要替換成的文本 = 編輯框主機.內容 + “|”+ 編輯框端口.內容 + “|” + 編輯框用戶名.內容 + “|” + 編輯框密碼.內容+ “|

長度差 = 取文本長度 (原始文本) -取文本長度 (要替換成的文本)

.判斷開始 (長度差 > 0)

    ' 需要在右側補空格

    要替換成的文本= 要替換成的文本 + 取重復文本 (長度差, “ ”)

.判斷 (長度差 < 0)

    信息框 (“配置信息太長,可能會導致生成的程序無法運行。”, #錯誤圖標, )

    信息框 (“生成文件失?。?#8221;, #錯誤圖標,)

    返回 ()

.默認

 

.判斷結束

文件內容 = 子字節(jié)集替換 (#待修改程序, 到字節(jié)集 (原始文本), 到字節(jié)集 (要替換成的文本), , )  ' 進行字節(jié)集替換

.如果 (寫到文件 (通用對話框1.文件名, 文件內容))

    信息框 (“生成文件成功!”, #信息圖標,)

.否則

    信息框 (“生成文件出錯!”, #錯誤圖標,)

 

       很驚訝只有這么短的代碼,是不是?其中最關鍵的只有一句“文件內容 子字節(jié)集替換 (#待修改程序, 到字節(jié)集 (原始文本), 到字節(jié)集 (要替換成的文本), , ) ,其他的只是一些輔助性的工作如打開對話框、錯誤提示等。這段代碼中要注意的是“原始文本”變量的內容一定要和被配置程序中的一致,包括大小寫!否則替換無法成功。

       運行生成的程序,點擊按鈕,我們看到我們在配置程序中填的參數(shù)顯示出來了(2.4-e)!

 

       一切就這么簡單! ——當魔術的秘密被揭穿的時候,魔術也就不再神奇了。

2.4-e 運行生成的EXE,我們看到了我們在配置程序中填入的信息

 

 

2.5文件捆綁機

       不知大家有沒有用過多媒體制作軟件如Authorware、Director、Multimedia Builder 、Flash等,如果沒有用過,也一定聽說過或使用過EXE文件捆綁器、WinRAR之類的程序,以及其他的電子賀卡之類的小程序。這類程序都有一些共同的特定:① 能夠生成EXE文件;② 能夠把多個其他的文件捆綁在該EXE文件中,生成的EXE文件在運行時能夠解讀它們。

       用前面的思路來實現(xiàn)這個功能可就難了:你不知道用戶要捆綁多少個文件,你也不知道這些文件有多大。所以用預留空間然后進行替換的方法顯然是行不通了,我們得另辟蹊徑。

       前面我們說過一個方案,就是把數(shù)據(jù)附加在EXE文件的尾部,但這個方法在易語言中對于獨立編譯的EXE文件完全行不通。你可以做一個這樣的試驗:新建一個空的易語言程序,然后獨立編譯成EXE文件,用UltraEdit打開,修改尾部的一個字節(jié)的數(shù)據(jù),或者在尾部添加一個字節(jié),保存,再運行,你就得到這樣的信息提示然后程序退出:

 

2.5-a 修改獨立編譯的EXE尾部的數(shù)據(jù)后運行的錯誤提示

 

       根據(jù)日常使用易語言獨立編譯程序的觀察,我們發(fā)現(xiàn)易語言獨立編譯的程序在執(zhí)行的時候,會把相關的支持庫文件釋放到臨時文件夾中。也就是說,易語言獨立編譯的EXE文件中既包含了非獨立編譯的EXE程序,也包含了支持庫,同時支持庫是壓縮了之后捆綁到EXE程序的尾部的。所以一旦我們修改了其尾部的數(shù)據(jù),程序就不能正確運行了。既然不能附加到尾部,那么我們能不能把文件插在它們之間呢?如果插在它們之間程序仍能夠運行,那我們就幾乎成功了。這里關鍵是要找到它們之間的交界處在哪里,如果找到了,事情就成功了一半。有的朋友可能會說:“既然如此,那我們先編譯一個非獨立的exe文件,得到其大小,不就可以知道他們在何處交界的嗎?”。我也曾這么想過,但通過觀察發(fā)現(xiàn)事實并沒有這么簡單。我們可以隨便編譯一個易程序,一個非獨立編譯的,一個獨立編譯的,然后用UltraEdit打開看看,就會發(fā)現(xiàn)前面的開始處不遠,就有不同的地方。既然如此,就不能簡單地依據(jù)文件大小來判斷了,差之毫厘,謬以千里?。∥也孪胛募念^部雖然不同,但文件的尾部應該還是一樣的,能夠找到非獨立編譯程序的尾部也是一樣的。嗯,不要吝惜你自己的想像力!大膽地猜想吧!“想像力比知識更重要。”,這是愛因斯坦說的。還有的朋友可能會說:“既然非獨立編譯文件的大小不能確定,為什么不找運行庫的頭呢?那樣不是更簡單?”事實上我們根本無法找到運行庫的頭。易語言的運行庫是Krnln.fnr文件,該文件有0.97MB,而獨立編譯的exe文件一般為500KB左右,顯然運行庫只是部分放在了文件后面或是被壓縮了然后放在后面的,那要找運行庫的頭是根本找不到的。

    一旦我們找到了它們的交界處,我們就可以把獨立編譯后的EXE從此處一分為二,然后分別當作圖片資源插入到主程序中。在寫出的時候先寫出非獨立編譯的那塊,然后寫一個分割符字符串,這個標志是為了便于生成的EXE程序在讀取自身時方便分割自身。緊接著我們在后面寫上要插入的文件的相關信息,比如文件名、文件起始位置、文件大小等,再寫一個分割字符串,然后就是文件的內容,再寫個分割串,最后再把易語言編譯時附加的運行庫數(shù)據(jù)加在后面就完事了。所以,最終生成的EXE文件應該具有如下的結構:

 

非獨立編譯塊一

編譯程序插入的分割符

非獨立編譯塊二

 

文件信息描述文本

 

 

文件一

文件二

。。

 

文件N

 

壓縮的支持庫塊

 

2.5-a 最終生成的文件結構,其中灰色塊為分割字符串

 

       從表中我們可以看到,文件會被分隔字符串分成5塊——連續(xù)的白色的部分。如果分隔的塊數(shù)小于5,則說明未插入任何文件。文件的描述信息在第三塊,被插入的文件連續(xù)地存放在第4塊。最終捆綁的程序在運行的時候,先讀取文件描述信息,并將該信息顯示于列表框中,用戶從列表框中選擇要釋放的文件,然后點擊“釋放并打開”按鈕,程序就從第四塊中讀出指定的字節(jié)集存到文件中并打開。

       根據(jù)這個思路,我們在編程時需要寫三個程序:① 要被作為資源包含進另一個程序的程序,我們稱之為“被包程序”;②被包程序在被作為資源插入前要被一分為二,所以還要一個“分割程序”;③ 顯然還需要一個生成EXE的“主程序”。 在發(fā)布時只需要發(fā)布主程序就行了。該主程序允許用戶添加多個文件,然后生成一個EXE文件。

       1>我們先來寫“被包程序”。該程序在運行是先將自身按分隔字符串分塊,從數(shù)據(jù)塊中讀出被插入文件的信息,然后根據(jù)用戶的需求來釋放文件,并打開釋放的文件。

       因此,我們設計如下的窗體(圖2.5-b),中間哪個白色的區(qū)域是個列表框。

2.5-b 被包程序的窗體

 

       給程序添加一個常量“分割符”,如圖2.5-c所示。我們假定程序的各個部分是通過這個文本分割的。當然你也可以使用自己認為好的分隔字符串,只要保證在編譯完畢的EXE文件中的其他位置不會有該文本。

2.5-c 用來分割文件的分割符

 

       添加一個程序集變量“集字節(jié)集數(shù)組”,用來保存文件被分割后的各部分。

       _啟動窗口創(chuàng)建完畢后,需要將插入的文件信息讀取到列表框中,所以雙擊_啟動窗口,添加以下代碼:

 

.版本 2

 

.子程序 __啟動窗口_創(chuàng)建完畢

.局部變量 文件信息, 文本型

.局部變量 臨時文本數(shù)組, 文本型, , "0"

.局部變量 i, 整數(shù)型

 

集字節(jié)集數(shù)組 = 分割字節(jié)集 (讀入文件 (取執(zhí)行文件名 ()), 到字節(jié)集 (#分割符), )

.如果真 (取數(shù)組成員數(shù) (集字節(jié)集數(shù)組) 4)

    ' 如果已插入文件,則應該有5

    信息框 (“該程序尚未包含任何文件。”, 0, )

    結束 ()

.如果真結束

' 從文件結構圖中可以知道,文件信息在第3塊中

文件信息 = 到文本 (集字節(jié)集數(shù)組 [3])

' 將文件信息添加到列表框中

臨時文本數(shù)組 = 分割文本 (文件信息, |, ) ' 文件信息之間以“|”分隔

.計次循環(huán)首 (取數(shù)組成員數(shù) (臨時文本數(shù)組), i)

    列表框文件列表.加入項目 (臨時文本數(shù)組 [i], )

.計次循環(huán)尾 ()

 

       在用戶單擊〔釋放并打開選中的文件〕的時候,程序從列表框的文本中讀取文件名、文件的起始位置和長度等信息,然后從“集字節(jié)集數(shù)組[4]”中讀出文件數(shù)據(jù),寫到磁盤文件并打開。具體代碼如下:

 

.版本 2

 

.子程序 _按鈕釋放打開_被單擊

.局部變量 文件信息, 文本型

.局部變量 臨時文本數(shù)組, 文本型, , "0"

.局部變量 文件數(shù)據(jù), 字節(jié)集

 

.如果真 (列表框文件列表.現(xiàn)行選中項 = -1)

    信息框 (“請先選擇要釋放的文件。”, #信息圖標, )

    返回 ()

.如果真結束

 

文件信息 = 列表框文件列表.取項目文本 (列表框文件列表.現(xiàn)行選中項)

臨時文本數(shù)組 = 分割文本 (文件信息, *, ) ' 文件名、起始位置和長度之間以“*”分隔

文件數(shù)據(jù) = 取字節(jié)集中間 (集字節(jié)集數(shù)組 [4], 到數(shù)值 (臨時文本數(shù)組 [2]), 到數(shù)值 (臨時文本數(shù)組 [3]))

' 文件數(shù)據(jù)在第四塊字節(jié)集中

寫到文件 (臨時文本數(shù)組 [1], 文件數(shù)據(jù))

' 運行 (臨時文本數(shù)組 [1], , )'此句只有可執(zhí)行文件才會被執(zhí)行。

運行 (cmd /c start ” +臨時文本數(shù)組 [1], , #隱藏窗口)  ' 此句可以打開任何文件。

' 此處本應使用API ShellExecute ,但未講到,所以先用此法代替。

 

       被包程序寫這么多代碼就夠了。程序的最后一行“運行”函數(shù)的參數(shù)“欲執(zhí)行的命令行”被設置為“cmd /c start ”加上文件名。我們知道,cmd.exe WindowsNT、XP2003的命令行解釋程序;“/c”參數(shù)表示在執(zhí)行后續(xù)的命令字符串后自動關閉解釋程序;“start”是個內部命令,后面帶一個文件名,執(zhí)行該命令會用與該文件關聯(lián)的程序打開該文件。

現(xiàn)在運行它,會提示“該程序尚未包含任何文件。”,然后退出。當然呢,還沒插入文件呢。請將該程序編譯成一個獨立編譯版本的EXE文件,一個非獨立編譯版本的EXE文件。

 

       2>下面開始寫分割程序,該程序需要根據(jù)非獨立編譯的EXE文件的尾部1KB的數(shù)據(jù)找到分割處,然后把獨立編譯的EXE文件一分為二。

       新建一個易程序,設計如下的窗體(2.5-c )。其中上下編輯框的名稱分別為“編輯框非獨立編譯文件名”和“編輯框獨立編譯文件名”,上下兩個“瀏覽...”按鈕的名稱分別為“按鈕瀏覽非獨立編譯EXE”和“按鈕瀏覽獨立編譯EXE”。

 

2.5-c 分割程序的窗體

 

       分割程序的代碼很簡單,全部代碼如下:

 

.版本 2

 

.程序集 窗口程序集1

======================================================

.子程序 _按鈕瀏覽非獨立編譯EXE_被單擊

 

.如果真 (通用對話框1.打開 ())

    編輯框非獨立編譯文件名.內容 = 通用對話框1.文件名

.如果真結束

 

======================================================

.子程序 _按鈕瀏覽獨立編譯EXE_被單擊

 

.如果真 (通用對話框1.打開 ())

    編輯框獨立編譯文件名.內容 = 通用對話框1.文件名

.如果真結束

 

 

.子程序 _按鈕分割_被單擊

.局部變量 非獨立編譯文件數(shù)據(jù), 字節(jié)集

.局部變量 獨立編譯文件數(shù)據(jù), 字節(jié)集

.局部變量 尾部數(shù)據(jù), 字節(jié)集

.局部變量 交接位置, 整數(shù)型

.局部變量 臨時數(shù)據(jù), 字節(jié)集

 

.如果真 (取文件尺寸 (編輯框獨立編譯文件名.內容) 0 或 取文件尺寸 (編輯框非獨立編譯文件名.內容) 0)

    信息框 (“有一個文件不存在!”, #錯誤圖標,)

    返回 ()

.如果真結束

.如果真 (取文件尺寸 (編輯框非獨立編譯文件名.內容) ≥取文件尺寸 (編輯框獨立編譯文件名.內容))

    信息框 (“文件尺寸不對,文件是不是載入反了?”, #詢問圖標, )

    返回 ()

.如果真結束

非獨立編譯文件數(shù)據(jù) = 讀入文件 (編輯框非獨立編譯文件名.內容)

獨立編譯文件數(shù)據(jù) = 讀入文件 (編輯框獨立編譯文件名.內容)

尾部數(shù)據(jù) = 取字節(jié)集右邊 (非獨立編譯文件數(shù)據(jù),1024)  ' 取尾部1KB的數(shù)據(jù)

交接位置 = 尋找字節(jié)集 (獨立編譯文件數(shù)據(jù), 尾部數(shù)據(jù), )

.如果真 (交接位置 ≤ 0)

    信息框 (“找不到交界處,分割失?。?#8221;, #錯誤圖標, )

    返回 ()

.如果真結束

交接位置 = 交接位置 + 1024

臨時數(shù)據(jù) = 取字節(jié)集左邊 (獨立編譯文件數(shù)據(jù), 交接位置)

寫到文件 (01.dat, 臨時數(shù)據(jù))

臨時數(shù)據(jù) = 取字節(jié)集右邊 (獨立編譯文件數(shù)據(jù), 取字節(jié)集長度 (獨立編譯文件數(shù)據(jù)) - 交接位置)

寫到文件 (02.dat, 臨時數(shù)據(jù))

信息框 (“分割完畢。”, #信息圖標, )

 

       這段代碼也全是字節(jié)集操作。運行該程序,點擊“瀏覽”按鈕們選擇你剛才編譯的那個非獨立的EXE文件和獨立編譯的EXE文件,點擊“分割”按鈕把獨立編譯的EXE文件分割成01.dat02.dat兩個文件。

       我們可以通過一個小試驗來檢測分割的位置是否正確:用UltraEdit打開01.dat文件,在尾部胡亂增加一些數(shù)據(jù)(把前面的數(shù)據(jù)復制粘貼一些到尾部即可),然后保存文件。再在Windows命令提示符窗口中用 copy /b 01.dat 02.dat asdf.exeDOS命令把這兩個文件合并成一個asdf.exe 文件,再運行asdf.exe ,我們可以發(fā)現(xiàn),程序仍能正確運行。成功!

       【注意】測驗完畢,請重新生成01.dat02.dat文件。

 

       3>接下來寫主程序。新建一個易程序,設計如下的窗體(圖):

 

2.5-d 主程序窗體

 

       窗體上的白色區(qū)域是個列表框,用來顯示用戶要插入的文件名,將其“允許多項選擇”屬性設為“真”,以方便用戶選擇多個文件進行刪除操作。有兩個通用對話框,把其中一個通用對話框的類型改為“保存文件”,“過濾器”設置為“可執(zhí)行文件(*.exe)|*.exe”,“默認文件后綴”設為“exe”。

       按照2.4 節(jié)中所講述的方法,把分割程序生成的01.dat文件和02.dat文件作為圖片資源加入,并分別命名為“文件頭”和“文件尾”,如圖2.5-e。

 

2.5-e 把分割的文件作為圖片資源加入

 

       還要注意要把那個“分割符”的常量從被包程序中復制粘貼過來,以確保一致。

       這段程序的代碼也不多,全部列出如下:

 

.版本 2

 

.程序集 窗口程序集1

======================================================

.子程序 去除文件名路徑, 文本型, , 此函數(shù)用來去除文件名中的路徑。

.參數(shù) 參帶路徑文件名, 文本型

 

返回 (取文本右邊 (參帶路徑文件名, 取文本長度 (參帶路徑文件名) -倒找文本 (參帶路徑文件名, \, , )))

 

======================================================

.子程序 _按鈕添加文件_被單擊

 

.如果真 (通用對話框1.打開 ())

    列表框1.加入項目 (通用對話框1.文件名, )

.如果真結束

 

======================================================

.子程序 _按鈕刪除選擇_被單擊

.局部變量 已選擇項目們, 整數(shù)型, , "0"

.局部變量 i, 整數(shù)型

 

已選擇項目們 = 列表框1.取所有被選擇項目 ()

.變量循環(huán)首 (取數(shù)組成員數(shù) (已選擇項目們), 1, -1, i)  ' 注意要反向刪除

    列表框1.刪除項目 (已選擇項目們 [i])

.變量循環(huán)尾 ()

 

======================================================

.子程序 _按鈕捆綁_被單擊

.局部變量 插入數(shù)據(jù), 字節(jié)集

.局部變量 文件信息, 文本型

.局部變量 i, 整數(shù)型

.局部變量 文件名, 文本型

.局部變量 文件數(shù)據(jù), 字節(jié)集

.局部變量 當前位置, 整數(shù)型

 

.如果真 (列表框1.取項目數(shù) () 1)

    信息框 (“你未選擇插入任何文件。”, #錯誤圖標, )

    返回 ()

.如果真結束

.如果真 (通用對話框保存文件.打開 () = 假)

    返回 ()

.如果真結束

當前位置 = 1

.計次循環(huán)首 (列表框1.取項目數(shù) (), i)

    文件名= 列表框1.取項目文本 (i 1)

    文件數(shù)據(jù)= 讀入文件 (文件名)

    文件信息= 文件信息 + 去除文件名路徑 (文件名) + “*” + 到文本 (當前位置) +“*” + 到文本 (取字節(jié)集長度 (文件數(shù)據(jù))) + “|

    ' 文件之間以“|”作為分隔,文件名、起始位置、文件長度之間以“*”分隔

    插入數(shù)據(jù)= 插入數(shù)據(jù) + 文件數(shù)據(jù)

    當前位置= 當前位置 + 取字節(jié)集長度 (文件數(shù)據(jù))

.計次循環(huán)尾 ()

文件數(shù)據(jù) = #文件頭 + 到字節(jié)集 (#分割符) + 到字節(jié)集 (文件信息) +到字節(jié)集 (#分割符) + 插入數(shù)據(jù) + 到字節(jié)集 (#分割符) #文件尾

.如果 (寫到文件 (通用對話框保存文件.文件名, 文件數(shù)據(jù)))

    信息框 (“捆綁文件成功!”, #信息圖標,)

.否則

    信息框 (“寫出文件失敗!”, #錯誤圖標,)

.如果結束

 

       這段代碼的核心部分是“_按鈕捆綁_被單擊”子程序。該程序順次讀取列表框中的文件,把文件名、文件的起始位置和大小遞增地存入到“文件信息”變量中,而文件內容則遞增地存入到“插入數(shù)據(jù)”變量中。最后獨立編譯EXE的文件頭、文件信息、文件數(shù)據(jù)、獨立編譯的文件尾全部用分隔符分隔合并起來,存入一個文件中,這個文件就是捆綁后的文件了。

       運行該程序,隨便添加幾個文件。點擊〔捆綁...〕按鈕生成一個EXE文件,建議保存在一個空目錄中。運行該程序,選中列表框中的一個文件,點擊〔釋放并打開選中的文件〕按鈕,該程序會把指定的文件釋放到當前目錄下并打開,見圖2.5-g。

 

2.5-f 主程序的運行界面

 

 

2.5-g 生成的捆綁程序的運行效果

 

       嗯。這個例程到這里就要結束了。從頭至尾看一看,想一想,其實代碼都是很簡單的,關鍵是如何突破常規(guī)思維,出奇制勝。

       當然,這個程序也存在問題,就是捆綁大文件時太占用內存,因為所有的數(shù)據(jù)要讀到字節(jié)集變量中進行操作,如果要捆綁幾百兆的視頻文件,采用字節(jié)集來操作的方法顯然是不合適的,直接用文件讀寫操作效率會更高、占用內存會更少。因為本節(jié)主要演示字節(jié)集的操作技巧,采用文件讀寫的方式就留給有興趣的讀者作為練習了。

 

2.6 [] 程序的自校驗

       在因特網上舉目四望,破解軟件泛濫成災。雖然軟件開發(fā)者始終應該把軟件的功能和易用性放在首位,但基本的防破解措施還是必要的,正所謂“防君子不防小人”。如果說軟件開發(fā)出來后,因為盜版的泛濫而導致開發(fā)商利潤微薄或無利潤,那么該軟件甚至企業(yè)的前途都將令人憂慮。雖然要做到完全不被破解是不可能的,但我們仍可以采取一些有效的措施來給破解者施加難度,延遲其破解的時間,或者讓一些低水平的破解者放棄破解。在這些防破解措施中,給程序加上自校驗無疑是行之有效的方法之一。程序的自校驗功能使程序在運行時能夠檢查自身的完整性與正確性,如果發(fā)現(xiàn)自身被修改,就拒絕運行、以某種方式警告用戶或自動銷毀等。

       下面我們以字節(jié)集操作的方式介紹一個簡單的給程序加自校驗的方法。我們的思路是這樣的:把程序編譯成非獨立編譯的EXE文件之后,通過另一個程序計算其校驗數(shù)據(jù)并加到其尾部。校驗數(shù)據(jù)可以通過易語言的“數(shù)據(jù)操作支持庫一”中的“取數(shù)據(jù)摘要”獲得。程序在運行的時候根據(jù)特定的字符串將自身分為兩部分,前部分為可執(zhí)行文件數(shù)據(jù),后部分為自校驗數(shù)據(jù)。程序計算前半部分的校驗數(shù)據(jù),并和后半部分的數(shù)據(jù)進行比較,如果不同則表示程序已被非法修改從而執(zhí)行相應的操作。

       根據(jù)這個思路,我們需要寫兩個程序,一個是用來給用戶使用的“主程序”,另一個則是用來給“主程序”添加自校驗信息的“添加校驗程序”。這里我們的主程序不實現(xiàn)任何有用的功能,只是在啟動時檢查自身的完整性。

       1>新建一個空的易語言程序。先增加一個常量,如圖2.6-a。我們需要用這個字符串來分割文件。注意此常量會被編譯程序編譯到EXE文件中,后面我們還要用校驗程序加上一個分割符,所以實際上最終的文件會被此分割符分成三段。

2.6-a 加入分割符文本常量

 

       雙擊啟動窗體,添加以下代碼。注意,此段代碼中的“取數(shù)據(jù)摘要”函數(shù)和“解壓數(shù)據(jù)”需要用到“數(shù)據(jù)操作支持庫一”,請確認支持庫配置對話框中勾中了這個支持庫。

 

.版本 2

.支持庫 dp1

 

.子程序 __啟動窗口_創(chuàng)建完畢

.局部變量 文件數(shù)據(jù), 字節(jié)集

.局部變量 校驗數(shù)據(jù), 字節(jié)集

.局部變量 臨時數(shù)據(jù)數(shù)組, 字節(jié)集, , "0"

 

.如果真 (是否為調試版 () = 假) ' 此處加一判斷,以方便調試程序

    文件數(shù)據(jù)= 讀入文件 (取執(zhí)行文件名 ())

    臨時數(shù)據(jù)數(shù)組= 分割字節(jié)集 (文件數(shù)據(jù), 到字節(jié)集 (#分割符), )

    .如果真 (取數(shù)組成員數(shù) (臨時數(shù)據(jù)數(shù)組) 3)  ' 分割后應該有三塊

        信息框 (“程序已被非法修改或感染病毒!”, #錯誤圖標, “警告”)

        結束 ()

    .如果真結束

    ' 把前面兩塊加上分割符拼接成完整的EXE文件

    文件數(shù)據(jù)= 臨時數(shù)據(jù)數(shù)組 [1] + 到字節(jié)集 (#分割符) + 臨時數(shù)據(jù)數(shù)組 [2]

    校驗數(shù)據(jù)= 解壓數(shù)據(jù) (臨時數(shù)據(jù)數(shù)組 [3])  ' 第三塊為校驗數(shù)據(jù),已被壓縮,所以先解壓一下

    .如果真 (取數(shù)據(jù)摘要 (文件數(shù)據(jù)) ≠到文本 (校驗數(shù)據(jù)))

        信息框 (“程序已被非法修改或感染病毒!”, #錯誤圖標, “警告”)

        結束 ()

    .如果真結束

 

.如果真結束

 

       現(xiàn)在在易語言編程環(huán)境中運行這個程序,程序會正常運行。請將其編譯成非獨立編譯的“主程序.exe”文件,在資源管理器中運行,程序會提示“程序已被非法修改或感染病毒!”,當然羅,我們還沒有給它加自校驗數(shù)據(jù)。

 

       2>再新建一個易語言程序,保存為“添加校驗碼.e”,設計如圖2.6-b所示的窗體。

2.6-b 添加校驗信息的窗體

 

       然后添加代碼。該程序的代碼很短,全部代碼如下:

 

.版本 2

.支持庫 dp1

 

.程序集 窗口程序集1

=====================================================

.子程序 _按鈕瀏覽_被單擊

 

.如果真 (通用對話框1.打開 ())

    編輯框文件名.內容 = 通用對話框1.文件名

.如果真結束

 

=====================================================

.子程序 _按鈕添加校驗_被單擊

.局部變量 文件數(shù)據(jù), 字節(jié)集

.局部變量 校驗數(shù)據(jù), 字節(jié)集

 

.如果真 (編輯框文件名.內容 = “” 或 取文件尺寸 (編輯框文件名.內容) 0)

    信息框 (“指定的文件不正確!”, #錯誤圖標,)

    返回 ()

.如果真結束

文件數(shù)據(jù) = 讀入文件 (編輯框文件名.內容)

校驗數(shù)據(jù) = 壓縮數(shù)據(jù) (到字節(jié)集 (取數(shù)據(jù)摘要 (文件數(shù)據(jù))))

文件數(shù)據(jù) = 文件數(shù)據(jù) + 到字節(jié)集 (#分割符) +校驗數(shù)據(jù)

.如果 (寫到文件 (編輯框文件名.內容, 文件數(shù)據(jù)))

    信息框 (“添加自校驗信息成功!”, #信息圖標, )

.否則

    信息框 (“改寫文件失敗!”, #錯誤圖標,)

 

       運行該程序,點擊〔瀏覽〕按鈕,選擇剛才編譯的“主程序.exe”文件,然后單擊〔添加校驗〕按鈕。程序提示修改成功后再到資源管理器運行主程序,我們發(fā)現(xiàn)程序可以正常運行了。使用UltraEdit打開主程序,修改其中的任何一個字節(jié),比如把開始處不遠的“This program 改成 That program”,保存后再運行主程序,則再次得到警告信息(2.6-d),

 

2.6-d 修改程序之后運行的效果

 

       這樣這個自校驗程序就完成了。當然這還是最簡單的自校驗方式,你還可以做得更復雜一些:比如你可以把自校驗信息寫在EXE文件的其他位置;把EXE文件用UPX等可執(zhí)行文件壓縮工具壓縮后或其他加殼工具加殼后再加上自校驗信息,這樣只要一脫殼,程序就無法運行。如果你有更巧妙的方法,別忘記給我發(fā)一封郵件啊,我的郵箱是:zeng_goomoo@yahoo.com.cn。

 

2.7 使用字節(jié)集的注意事項

       從前面的幾個實例中,我們可以看到易語言所提供的字節(jié)集的功能是很強大、很方便的。但在使用字節(jié)集的時候,也要注意一些問題,除了前面提到的“到數(shù)值”函數(shù)之外,要注意的就是字節(jié)集操作的效率。主要集中在一下兩點:

       1>不要用字節(jié)集來操作大的文件。字節(jié)集型的變量需要把所有的數(shù)據(jù)讀入到內存中進行處理,如果文件大小多達幾十上百兆,那么內存的占用是可想而知的。請看下面的代碼:

 

.版本 2

 

.程序集 窗口程序集1

.程序集變量 數(shù)據(jù), 字節(jié)集

===============================================

.子程序 __啟動窗口_創(chuàng)建完畢

 

數(shù)據(jù) = 取空白字節(jié)集 (300 × 1024 × 1024)

 

       這段代碼在運行時分配300MB字節(jié)集的內存空間,運行之后你看看你的任務管理器的內存占用率吧。注意在運行前請保存你的工作文件,以免意外死機。

 

2> 不要頻繁地進行字節(jié)集相加。字節(jié)集的相加操作會導致內存的重新分配,從而極大地影響運行效率。請看下面的代碼:

 

.版本 2

 

.子程序 取隨機字節(jié)集, 字節(jié)集, , 通過字節(jié)集相加的方式

.參數(shù) 參長度, 整數(shù)型

.局部變量 結果, 字節(jié)集

 

.計次循環(huán)首 (參長度, )

    結果= 結果 + 到字節(jié)集 (取隨機數(shù) (0, 255))

.計次循環(huán)尾 ()

返回 (結果)

 

==========================================================

.子程序 取隨機字節(jié)集一, 字節(jié)集, , 通過預先分配的方式

.參數(shù) 參長度, 整數(shù)型

.局部變量 結果, 字節(jié)集

.局部變量 i, 整數(shù)型

 

結果 = 取空白字節(jié)集 (參長度)

.計次循環(huán)首 (參長度, i)

    結果 [i] = 取隨機數(shù) (0, 255)

.計次循環(huán)尾 ()

返回 (結果)

 

==========================================================

.子程序 _按鈕測試效率_被單擊

.局部變量 上次時間, 整數(shù)型

.局部變量 長度, 整數(shù)型

 

長度 = 15 × 1024

上次時間 = 取啟動時間 ()

取隨機字節(jié)集 (長度)

輸出調試文本 (取啟動時間 () - 上次時間)  ' 輸出2657

上次時間 = 取啟動時間 ()

取隨機字節(jié)集一 (長度)

輸出調試文本 (取啟動時間 () - 上次時間)  ' 輸出0

 

       這段代碼中有兩個函數(shù)實現(xiàn)同樣的功能:“取隨機字節(jié)集”和“取隨機字節(jié)集一”,都是用來取得指定長度的隨機數(shù)據(jù),但實現(xiàn)的細節(jié)不一樣,前一個函數(shù)使用字節(jié)集相加的方式,而后一個則先分配內存,然后逐一為數(shù)據(jù)中的每個字節(jié)賦值。從運行的結果可以看到,采用字節(jié)集相加的方式運行效率低得嚇人,取15KB的隨機數(shù)據(jù)居然需要將近3秒。而預先分配內存的函數(shù)顯然快很多,耗時基本為0。

 

 

 

 

本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
簡單的易語言讀取網頁文本程序
數(shù)組傳遞摘抄
易語言模擬曲線圖軟件源代碼
※§同創(chuàng)夢想☆◇
【新提醒】【圖】求用易語言讀取大智慧自定義數(shù)據(jù)文件的數(shù)據(jù)的方法
易語言入門基礎-制作一個簡單記事本
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服