早在Ver0.9推出之時,我就在想,Screen Saver Maker自己使用的話, 還算挺方便的軟體,但是如果把眼光放遠一點呢? 就會發覺,這個軟體缺乏一個很重要的功能-互換性, 螢幕保護程式,雖然只是在自己的螢幕上使用的小程式, 但是,通常大家都會想要將好玩的、有趣的、漂亮的螢幕程式與好朋友分享, 或放在自己的網站上給人下載, 而Screen Saver Maker致命的缺點就在這裡,它所製作出來的SPM、SPC檔, 其實只是一個簡單的檔案索引,指示程式在適當的流程中, 應該去作什麼樣的動作, 但是,這一份檔案索引,並不見得能適用於別人的電腦, 難不成你想要求每一個收到你檔案的人說, 你!就是你!在E磁碟建一個xxx的目錄,再把所有檔案copy進去... 當然,我們不可能要求別人這麼做的, 那麼,我們該怎麼辦? 這就是SPF檔案的由來了, SPF檔案的原理其實很簡單, 我們先想想看,為了達到互換性的要求, 我們必須要做到什麼樣的功能? 其實我們要的功能很簡單,只有一個,那就是~不論到那一臺電腦, 這一個螢幕保護程式都能執行, 而要達到這個要求,使用者所必須做的步驟,當然,要越少越好囉! 其它方面,我們也必須想到, 當你EMail一個螢幕保護程式給別人時, 那~~~~麼多的檔案,難不成你要使用者一個個的加進去嗎? 嗯~~那可不大好,所以,我們應該想辦法,創造一種規格, 能將所有的檔案cat在一起,最好只有一個檔案, 那麼動作就簡單多了, 整個大略的流程大概就成形了

1.使用者已建立SPM專案檔
2.使用者利用SPF檔案管理將SPM專案檔轉成SPF封包檔
3.另一使用者收到SPF封包檔
4.另一使用者利用SPF檔案管理將SPF封包檔轉換回SPM專案檔

這樣看起來是不是跟傳ZIP檔案給別人的步驟差不多? 我就是從那裡聯想出來這個程序的,其實也沒有什麼大道理
整個SPF檔,其實就是將所有SPM檔須要的檔案cat在一起的一個大檔案, 然後再加上一些必要的資訊集合而成的, 只要懂一些檔案讀寫的技巧,就可以很容易瞭解了, 那麼我們來看一下整個SPF檔案的架構

首先,在SPF的檔頭,照例寫下一些資訊,以做為程式的判別
1.Head Code
40Byte                                  
========================================
This is Screen Saver Maker Package File 


接下來兩個Byte是標示檔案的Packet種數
因為我們在製作及解開檔案時,必須要知道每個檔案的長度
才能在檔案cat在一起之後,仍可正確的解開
因此必須有一個判定的方法
而我所使用的方法則是將檔案拆成無數個小封包
而這裡標示的就是這個SPF檔案所使用的封包總數
而從這裡我們可以看得出來,SPF檔的最大限制為65535個封包資料
而每個封包我制定為1024byte(1K)
因此SPF檔的最大容許容量約為65535K(64M)左右
如果你的SPM專案檔所需的檔案如果超過此容量
就無法成功建立了


Total Packet No.
   檔案的Package總數
2Byte
====
FFFF


接者是16Byte的空白,為什麼要留下16Byte的空白?
好處可多了~~這是為了以後擴充SPF的功能留下的一段空間
如果想要的話,利用這裡可以做不少事喔!這一點我稍後再說明


Not define
   未定義,保留
================
16Byte


前面可以說完全是SPF的一些檔案資訊而已,
開接下來的部份才是要解開的檔案



為了方便程式撰寫
每個封包是設計為1024Byte的格式
但是檔案大小不可能永遠都剛好是1024的倍數
那麼,我們就必須在每個解開封包之前
確定可用資料的Byte數
因此在每一個1024Byte的封包之前,還有兩個Byte的可用資料的長度
而在最前方也有兩個Byte,標明了這一個檔案是用幾個封包組合而成


2.Packet Data Format
Total Packet in this file
  目前要解的檔案用了幾個封包資料(只有第一個需要)
2Byte
====
FFFF

Packet Length(Useful Data Length in this Packet)
   可用資料的長度
2Byte
====
FFFF

Packet Data
   資料內容
1024Byte
============.....=====
0107A324902F.....00000

而在建立起這一些SPM檔的封包資料之時,有一個檔案是最必須優先建立的
那就是所有檔案的明細List檔
否則,就算你的SPF檔建立好了,你也不知道解開時要使用什麼檔名啊!
SPF的大略說明就是這樣子
其它詳細的內容,可以參考當初我放上來的SPF規格表
接下來我們開始來看程式
剛剛我們已經說過了,SPF檔其實只是將檔案cat起來的簡單技巧,因此, 並沒有壓縮檔案的功能,相反的,因為多加了些額外的資訊, 檔案還反而會比原來的大了一點,因此程式裡設計了一個功能, 如果你有安裝winzip的話,在SPF檔製作完成後, 你可以選擇讓程式自動為你呼叫winzip來壓縮SPF封包檔, 而我們就必須檢查出使用者是否有安裝winzip,及查出winzip的執行檔路徑, 來看看這一段程式
Private Sub Form_Load()
ActionType = MAKE_SPF
If IsWinZIP = NOT_DETECT Then Detect_WinZIP
If IsWinZIP = HAVE_WINZIP Then chkUseWinZIP.Enabled = True
End Sub

IsWinZIP是用來判斷是否檢查過WinZIP路徑的布林值
如果檢查出WinZIP的存在,就將自動呼叫WinZIP的控制項致能(Enabled=True)
而至於檢查的方法我們來看看Detect_WinZIP這一段副程式


Private Sub Detect_WinZIP()
Dim hkey As Long
Dim sLen As Long

    檢查的方法其實很簡單,如果WinZIP如果存在的話,
    你應該會在HKEY_CLASSES_ROOT=\WinZip\shell\open\command這個SubKey中
    查到WinZIP的執行路徑
    再經過一些檔案的確認動作
    就可以得知WinZIP的執行檔路徑了
    不過有一點要提醒的
    其實我的動作有點偷工減料
    正確的步驟應該先搜尋.zip是否有被關聯
    再依據.zip取得的值一路追回去才對
    不過,WinZIP的登錄路徑基本上似乎是沒有改變過
    所以就稍稍偷工減料了
    但是該了解的還是要了解才行喔!

RegOpenKey HKEY_CLASSES_ROOT, "WinZip\shell\open\command", hkey
RegQueryValueEx hkey, "", 0, REG_SZ, ByVal vbNullString, sLen
WinZIPCommand = String(sLen, Chr(0))
RegQueryValueEx hkey, "", 0, REG_SZ, ByVal WinZIPCommand, sLen
RegCloseKey hkey

If WinZIPCommand <> "" Then
   WinZIPCommand = Left(WinZIPCommand, InStr(WinZIPCommand, " ") - 1)
   If Dir(WinZIPCommand) <> "" Then
      IsWinZIP = HAVE_WINZIP
   Else
      IsWinZIP = NO_WINZIP
   End If
Else
   IsWinZIP = NO_WINZIP
End If
End Sub

接者便是等待使用者指定要做的動作,然後做壓解檔的動作, 這裡有用到ActionType的變數來判斷使用者的動作,當使用者按下cmdOK時, 再依據Caption及這個變數來判斷要執行的步驟為何?這邊就比較沒有什麼好講的了, 主要步驟就是
製作->呼叫副程式製作SPF檔->製作完成,確定
解開->指定解開路徑,確定解開->呼叫副程式解開SPF檔->完成,確定
就是這樣而已,主要的工作都是在MakeSPFFile及UnPackSPFFile這兩個副程式, 我們就來分析一下MakeSPFFile
Private Sub MakeSPFFile(SPMFile As String)
'略

On Error GoTo MakeSPFError

'Get Current Path & SPM FileName,if SPM file exist,ask user delete?
首先我們先找出要建立的SPM檔的正確完整路徑,然後,Check SPF的檔案是否已經
存在,如果已經存在,記得詢問使用者是否確定要覆蓋原檔
'略

'Get a temp file name for SPM
接下來要建立一個暫存檔
用來存放要建立在SPF檔中的SPM檔
'略

txtStatus.Text = "開始解析" & SPMFile & vbCrLf
DoEvents

fNum = FreeFile
Open readFile For Input As #fNum
fNum2 = FreeFile
Open tmpSPMFile For Output As #fNum2

'Check Head Data
撿查SPM檔的檔頭
'略

totalFileNo = 0
txtStatus.Text = txtStatus.Text & "處理中"
DoEvents
ReDim SPM_DATA(1 To 301) As String
'SPM File Data Array (MAX file in SPM 100*3+1 (wav,mid and picture + tmpfile))
建立一個陣列存放要用到的檔案資訊
一個SPM專案檔,最多可以設定100張圖片,而每一個圖片設定,最多
可能會包含有Wave,Midi及圖片本身三個檔案,加上SPM專案檔
因此我們最多會需要用到100*3+1個檔案

接下來就要讀取SPM檔的資料寫到暫存檔中
在檔案資訊方面,我將路徑方面的資料都拿掉了,
因為到時候你在解SPF檔的時候,這個路徑根本不能使用,
因此我們只要寫下檔名就可以了
然後我們再把要加入SPF檔的檔名存到SPM_DATA這個陣列中

Do Until EOF(fNum)
   Line Input #fNum, rBuf$
   Print #fNum2, rBuf
   
   For i = 1 To 3
      '略
   Next i
   For i = 1 To 4
      '略
   Next i
   'Nothing,Just show process
   '略
Loop
SPM_DATA(totalFileNo + 1) = tmpSPMFile
Close #fNum
Close #fNum2

'If no file,stop handle
假如沒有任何檔案?那還要做什麼?離開吧!
If totalFileNo = 0 Then
   txtStatus.Text = txtStatus.Text & "程式強制中斷,沒有找到任何需要處理的檔案"
   Exit Sub
End If

txtStatus.Text = txtStatus.Text & vbCrLf

'Create a temp directory
接下來要準備將檔案製作成SPF的檔案格式了
我們設立一個暫存目錄,避免與其它檔案發生衝突
'略

txtStatus.Text = txtStatus.Text & "開始壓縮作業"
DoEvents
ReDim SPF_DATA(0 To totalFileNo + 1) As String
'SPF file data array (ini file+file of SPM +list file)
SPF_DATA(0) = "scrpack.ini"

在SPF檔案中,第一個放的一定是檔案總表,記載所有要解開的檔名
不然的話,你就不知道要將解開來的檔案取什麼名字囉!
接下來的算是硬功夫,將用到的檔案一個一個寫下來存到暫存檔中,
請自己參考相關書籍

For i = 1 To totalFileNo + 1
    '略
Next i
txtStatus.Text = txtStatus.Text & vbCrLf
txtStatus.Text = txtStatus.Text & "壓縮結束,建立List資料檔" & vbCrLf
DoEvents

'Create scrpack.ini,write filename information in file,and create 1024 format packet file
建立List檔,並製作成1024Byte格式的封包檔
'略


txtStatus.Text = txtStatus.Text & "建立List資料檔成功,壓縮資料檔"
DoEvents

fName = tmpPath & "SPF000.tmp"
readFile = tmpPath$ & "scrpack.ini"

j = FileLen(readFile) Mod 1024
If j = 0 Then
   PackTime = FileLen(readFile) \ 1024
   FileSize = 1024
Else
   PackTime = (FileLen(readFile) \ 1024) + 1
   FileSize = j
End If
totalPacketNo = totalPacketNo + PackTime

'if total packet no >65535,it can't write in head(more then 2bytes),so we need stop handle.
'before we quit subscript,delete temp file,directory
If totalPacketNo > 65535 Then
   MsgBox "檔案容量超過最大限制(64M),程式強制中斷!", vbOKOnly Or vbExclamation
   txtStatus.Text = txtStatus.Text & vbCrLf & "刪除暫存檔"
   DoEvents
   Kill tmpPath & "*.*"
   Kill tmpSPMFile
   txtStatus.Text = txtStatus.Text & vbCrLf & "刪除暫存目錄"
   DoEvents
   RmDir tmpPath
   txtStatus.Text = txtStatus.Text & vbCrLf & "結束"
   Exit Sub
End If

'Create list file's packet file
'略

前置作業大概就到此為止了,該知道的資訊都讀出來了,
暫存檔也都做好了,確定一下封包數量有沒有超過最大限制
就可以把檔案Cat起來了

txtStatus.Text = txtStatus.Text & vbCrLf & "建立List資料檔成功," & vbCrLf & "開始建立SPF資料檔案"

'if SPF file exist,delete it
假如已經SPF檔存在,記得將它砍掉
fName$ = pathBuf$ & Left$(SPMFile, InStrRev(SPMFile, ".")) & "SPF"
If Dir(fName$) <> "" Then Kill fName$
DoEvents


開始製作SPF封包檔
將檔頭等資訊寫入,再將所有的暫存檔一個個cat起來
這樣就行了

'略


'Check Use Winzip,If Used,Call WinZip & Make SPZ File
如果使用者有勾選呼叫WinZIP的話,就幫它呼叫WinZIP來壓縮檔案
If chkUseWinZIP.Value = vbChecked Then Call CallWinZIP(fName)


'create over.delete temp file and directory
建立完成囉!記得將剛剛建立的暫存檔及目錄清乾淨
'略

Exit Sub

'when something error,remember close fileNo
當發生錯誤時,記得將檔案關閉,並將暫存檔刪除喔!

MakeSPFError:
    MsgBox "製做SPF檔發生錯誤!請重新檢查你的檔案是否存在,或者有所毀損", vbOKOnly Or vbExclamation
    Close #fNum
    Close #fNum2
    Kill tmpPath & "*.*"
    Kill tmpSPMFile
    RmDir tmpPath
    Resume QuitMakeError
    
QuitMakeError:
    Exit Sub
End Sub
接下來解檔的動作就沒有什麼了,只要等使用者指定了SPF檔, 做相對應的壓解檔動作就行了,這其中的動作算是死工夫, 只要了解binary的讀寫方式,就沒有什麼好講的了 這一部份就不多講了,程式都有相關註解,應該不難懂的, 最後要再提的是,關於那空白的16Byte,這個16Byte可做的東西可多了, 例如,密碼功能, 你可以定下一個密碼寫在這裡,讓使用者必須輸入密碼才能解開檔案
又或者,你認為64M的SPF檔案不夠用?想讓使用者有更多的選擇? 那麼你可以拿兩個Byte來設定每一個封包的大小,只要設成2048,你就 多了一個最大128M的SPF檔案了(當然,程式還是得稍做修改就是了), 又或者,你想用磁片傳給別人,SPF檔又太大? 將SPF的極限設成1.4M(約1000個封包左右),再設定一個檔案序號,做出.SP0,.SP1,.SP2 的多個SPF檔也是可能的, 要怎麼做,能怎麼做,只看你自己的創造力了
這一次的教學教室就到此為止,下次見囉!
回到VB教學教室