34.進階應用程式
Messages應用程式
你可以發展出一套屬於自己專用的指令,幫助你完成一些特別的工作,Messages應用程式就是一個這樣的實例。在本例中,我們設計了兩個指令,這兩個指令可以被輸入到訊息方塊中,用以控制訊息方塊的外觀和行為。在設計指令集時,我們把指令集設計得很簡單,但是如果你需要其他更具創意的指令,你可以依據同樣的觀念基礎來擴展指令的數目或功能。
Messages應用程式包含了Message物件,它是由MSG.CLS物件類別模組和MSG.FRM表單所共同定義的。Messages應用程式將會利用這個Message物件來顯示一系列的訊息方塊。Message物件有一個FileName屬性,這個屬性的設定值是一個具有特殊格式的文字檔檔名。檔案的附屬檔名為MES (訊息檔),(我們稍後會解釋訊息檔的語法),訊息檔的內容由MSG.CLS和MSG.FRM共同合作將之顯示出來。
訊息檔的語法
每一個訊息檔都包含了一個或是多個預備被顯示的文字段落,在段落與段落之間,我們以三個連續的"~"字元來表示段落的開端。在訊息檔的訊息文字段落會被循序地顯示在訊息方塊中,你可以在段落開端字元"~~~~~~"的後面加上指令,以控制訊息文字的顯示方式。
我們用一個樣本訊息檔MESSAGE.MES來舉例說明訊息檔的運作方式。你可以在隨書光碟中找到這個檔案:
MESSAGE.MES This message file provides a sampling of the features demonstrated in the Messages application. Note that all of these lines appearing before the first text block header will be ignored. This is the first text block in the MESSAGE.MES file. Notice that the display window sizes automatically for the dimensions of the message. Close this display window to proceed to the next text block in this file. Click the Close button in the upper-right corner of this message. ~~~ P 10 This message should automatically disappear in 10 seconds. You may close it manually before then if you want. ~~~ F 2 This message should be in a flashing window, with the flash rate set to 2 times per second. ~~~ F 5 This message should be in a flashing window, with the flash rate set to 5 times per second. ~~~ P 20 F 1 This is the last message in this file. The flash rate is 1 time per second, and the message will disappear automatically in 20 seconds if you don't close it manually before then.
這個檔案定義了五個訊息。第一個訊息會被顯示在一個不閃爍的方塊中,訊息一直停留在這個方塊裡,直到使用者將方塊關閉。第一個訊息方塊關閉後,第二個訊息就會接著出現;由於第二個訊息段落的開端字元後面有一個P10指令,這個訊息方塊將會在暫停10秒後消失。第三個訊息由F指令控制,F指令的功用在於設定訊息方塊的閃爍頻率;在本例中,訊息方塊每隔兩秒就會閃爍一次。最後一個訊息則使用了P和F兩個指令──這個訊息方塊將會每1秒鐘閃爍一次,訊息顯示了20秒後消失。
在這個應用程式裡,我們只設計了兩個指令── P和F,但是你可以很容易地加上其他的指令。例如,你可以加入一個"C"指令來控制訊息文字的顏色。仔細研究P和F指令運作的方式之後,你應該可以加上更多屬於自己的指令。
圖34-1所顯示的是第一個訊息被顯示的情形,圖34-2則是第三個訊息。
圖34-1 由Messages應用程式所顯示的訊息 |
圖34-2 閃爍的訊息方塊 |
為什麼要用訊息檔?
一般說來,把訊息文字內嵌在程式中是一件很自然的事,然而使用外部的ASCII檔案來存放所有的訊息也是有其優點。例如,如果你要修改某段訊息文字時,你只要用文字編輯器來修改訊息檔即可,不用修改程式。另外,在Visual Basic程式中撰寫多行的訊息文字時,你必須用vbCrLf常數來連結字串,而且維護這樣的訊息文字會比較困難。
如果你的應用程式準備在國際間散佈,使用外部訊息檔的好處和使用資源檔的好處是一樣的──你只要把訊息檔的內容翻譯成外國文字即可,不需重新修改和編譯程式。
圖34-3顯示的是Messages應用程式的專案內容。
圖34-3 Messages應用程式的專案內容 |
MESSAGES.FRM
MESSAGES.FRM是Messages應用程式的啟動表單,在這張表單的程式中,我們定義了一個Message物件msgOne。為了要能夠循序地顯示在MESSAGE.MES檔中的所有訊息,我們把這個訊息檔的路徑和檔名指定給msgOne的FileName屬性,然後呼叫msgOne的Display方法,而訊息顯示的方式則由訊息檔中的指令所控制。
我們在這張表單上放置了一個通用對話方塊控制項和指令按鈕控制項,以便你透過通用對話方塊控制項選擇其他的訊息檔。圖34-4顯示的是設計階段中的Messages表單。
圖34-4 設計階段中的Messages表單 |
如果要建立這張表單,請按照以下這兩張表設定表單和控制項的屬性內容,然後加入以下的程式碼。
MESSAGES.FRM功能表項目 |
標題 | 名稱 | 內縮 | 啟用 |
---|---|---|---|
&File | mnuFile | 0 | True |
&New | mnuNew | 1 | False |
&Open... | mnuOpen | 1 | False |
&Save | mnuSave | 1 | False |
Save &As... | mnuSaveAs | 1 | False |
- | mnuFileDash1 | 1 | True |
E&xit | mnuExit | 1 | True |
&Help | mnuHelp | 0 | True |
&Contents | mnuContents | 1 | True |
&Search for Help on... | mnuSearch | 1 | True |
- | mnuHelpDash1 | 1 | True |
&About... | mnuAbout | 1 | True |
MESSAGES.FRM物件與屬性設定 |
屬性 | 值 |
---|---|
Form | |
Name BorderStyle Caption |
frmMessages 3 - Fixed Dialog Display a Message File |
CommandButton | |
Name Caption |
cmdMessages &Display Message File |
CommonDialog | |
Name | cdlOne |
MESSAGES.FRM原始程式碼
Option Explicit Private Sub cmdMessages_Click() `Declare new Message object Dim msgOne As New Message `Prompt user for message file (*.MES) cdlOne.DialogTitle = "Message Files" cdlOne.Flags = cdlOFNHideReadOnly cdlOne.Filter = "Messages(*.mes)|*.mes" cdlOne.CancelError = True On Error Resume Next cdlOne.ShowOpen `Quit if user canceled or closed dialog box If Err Then Exit Sub On Error GoTo 0 `Display message file With msgOne .FileName = cdlOne.FileName .Display End With End Sub Private Sub Form_Load() `Center this form Me.Left = (Screen.Width - Me.Width) \ 2 Me.Top = (Screen.Height - Me.Height) \ 2 End Sub Private Sub mnuAbout_Click() `Set properties About.Application = "Messages" About.Heading = _ "Microsoft Visual Basic 6.0 Developer's Workshop" About.Copyright = "1998 John Clark Craig and Jeff Webb" About.Display End Sub Private Sub mnuExit_Click() Unload Me End Sub Private Sub mnuContents_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpContents cdlOne.ShowHelp End Sub Private Sub mnuSearch_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpPartialKey cdlOne.ShowHelp End Sub
MSG.CLS
MSG.CLS物件類別模組是建立Message物件的藍圖,這個特別的物件類別模組需要一個MSG.FRM檔,以提供Message物件的視覺界面元素。如果你要在你的應用程式中加入Message物件,請務必加入MSG.CLS和MSG.FRM兩個檔。
每一個Message物件都有一個公用屬性FileName,這是原呼叫程式必須設定的屬性。Message物件中唯一的方法是Display方法,Display方法會把訊息檔中的訊息文字按照順序一一顯示出來。
在Display方法中的程式碼相當的多,這些程式碼主要在和frmMsg的屬性和方法進行互動。在訊息檔中的指令會由Display方法來解譯,而frmMsg則更進一步將解譯後的指令表現出來。
MSG.CLS原始程式碼
Option Explicit `Property that defines message file to be displayed Public FileName As String `Method to display message file Public Sub Display() Dim strH As String Dim strJ As String Dim strA As String Dim strB As String Dim strC As String Dim intFilNum As Integer Dim lngNdx As Long Dim lngFlashRate As Long Dim lngPauseTime As Long Dim lngHeight As Long Dim lngWidth As Long Dim lngMaxTextWidth As Long `Get next available file I/O number intFilNum = FreeFile `Trap error if filename is invalid On Error Resume Next Open FileName For Input As #intFilNum If Err Then MsgBox "File not found: " & FileName Exit Sub End If On Error GoTo 0 `Find start of first text block Do Until EOF(intFilNum) Line Input #intFilNum, strH `Skip lines until three tilde characters are found If InStr(strH, "~~~") = 1 Then strJ = UCase$(strH) Exit Do End If Loop `Loop through all text blocks Do Until EOF(intFilNum) strB = "" strH = strJ lngWidth = 0 lngHeight = 0 `Load all of current text block Do Until EOF(intFilNum) Line Input #intFilNum, strA `End of this block is at start of next block If InStr(strA, "~~~") = 1 Then strJ = UCase$(strA) Exit Do End If `Keep track of widest line of text lngMaxTextWidth = frmMsg.TextWidth(strA & "XX") If lngMaxTextWidth > lngWidth Then lngWidth = lngMaxTextWidth End If `Keep track of total height of all lines lngHeight = lngHeight + 1 `Accumulate block of text lines If lngHeight > 1 Then strB = strB & vbCrLf & strA Else strB = strA End If Loop `Check for flash rate in block header lngNdx = InStr(strH, "F") If lngNdx Then lngFlashRate = Val(Mid$(strH, lngNdx + 1)) Else lngFlashRate = 0 End If `Check for pause time in block header lngNdx = InStr(strH, "P") If lngNdx Then lngPauseTime = Val(Mid$(strH, lngNdx + 1)) Else lngPauseTime = 0 End If `Prepare message form's text box With frmMsg.txtMsg .Text = strB .Left = 0 .Top = 0 .Width = lngWidth .Height = (lngHeight + 1) * frmMsg.TextHeight("X") End With `Prepare message form With frmMsg .Width = .txtMsg.Width + (.Width - .ScaleWidth) .Height = .txtMsg.Height + (.Height - .ScaleHeight) .Left = (Screen.Width - .Width) \ 2 .Top = (Screen.Height - .Height) \ 2 `Set flash and pause properties if given If lngPauseTime > 0 Then .Pause = lngPauseTime If lngFlashRate > 0 Then .Flash = lngFlashRate End With `Show message and wait until it closes frmMsg.Show vbModal Loop End Sub
MSG.FRM
frmMsg表單是MSG.CLS物件類別模組的工作夥伴,它們在一起共同形成了Message物件。主要表單frmMessages並不直接設定frmMsg表單的屬性,不呼叫任何frmMsg表單的方法,也不以任何方式直接與frmMsg進行互動。MSG.FRM只和MSG.CLS物件類別模組進行互動,以這種方式,我們便把frmMsg表單變成了Message物件的一部分。
MSG.FRM中有四個控制項:兩個計時器控制項,一個用來顯示訊息的文字方塊控制項,以及一個虛指令按鈕(dummy button)(稍後會加以解釋)。圖34-5顯示的是設計階段中的MSG.FRM。
圖34-5 設計階段中的MSG.FRM |
如果要建立這張表單,請按照下表設定表單和控制項的屬性內容,然後在表單中加入後面的程式碼。
MSG.FRM物件與屬性設定 |
屬性 | 值 |
---|---|
Form | |
Name Caption BorderStyle |
frmMsg Message 3 - Fixed Dialog |
TextBox | |
Name ForeColor MultiLine Locked |
txtMsg &H00FF0000& True True |
Timer | |
Name | tmrTerminate |
Timer | |
Name | tmrFlash |
CommandButton | |
Name Default Caption |
cmdDummy True Dummy |
MSG.FRM原始程式碼
Option Explicit Private Declare Function FlashWindow _ Lib "user32" ( _ ByVal hwnd As Long, _ ByVal bInvert As Long _ ) As Long Private Sub Form_Paint() `Remove focus from text box cmdDummy.Left = Screen.Width * 2 cmdDummy.SetFocus End Sub Private Sub tmrTerminate_Timer() Unload Me End Sub Private Sub tmrFlash_Timer() `Toggle form flashing FlashWindow hwnd, CLng(True) End Sub Property Let Flash(PerSecond As Integer) `Set and activate form flashing rate tmrFlash.Interval = 1000 / PerSecond tmrFlash.Enabled = True End Property Property Let Pause(Seconds As Double) `Set and activate auto-unload timing tmrTerminate.Interval = 1000 * Seconds tmrTerminate.Enabled = True End Property
在tmrFlash_Timer事件程序中,我們呼叫FlashWindows API函式來對frmMsg表單作閃爍視窗的動作,而frmMsg表單閃爍的頻率則由tmrFlash的Interval屬性來決定。
虛指令按鈕cmdDummy在這裡唯一的功用就是用來取得駐點 (Focus),使frmMsg表單在顯示訊息文字時,文字方塊中不會出現閃爍的提示游標。當表單的Paint事件發生時,我們把這個指令按鈕控制項的Left屬性設為螢幕寬的兩倍,使這個指令按鈕不會讓使用者看到。另外,我們把cmdDummy的Default屬性設為True以確使cmdDummy一定能取得駐點。
Flash屬性和Pause屬性並不是由原呼叫程式直接設定,而是透過Message物件來加以設定。frmMsg表單將會依據設定值的不同而有不同的表現方式。
Secret應用程式
資料隱密性和安全性在今天成了愈來愈多人談論的話題,尤其是對在於Internet上面傳輸財務資料或其他專屬資訊這方面,這個話題被討論得更多。Secret應用程式雖然不能符合高度的保密需求,但是它對你的電子郵件或其他個人檔案提供了一個中間等級的保密措施。
注意:
這個應用程式所提供的安全性程度並不是萬無一失的,有些功力高深又鍥而不捨的高手仍然可以破解由這個程式加密後的資訊。然而,實際上說來,你已經阻止了百分之九十九想偷看檔案的人了。
為了使這個應用程式儘量簡單而易於說明起見,我們決定不用精巧但略嫌雜亂的公共鍵值(Public Key)技術,而採用私用鍵值(Private Key)技巧。如果要用這個應用程式來對你的電子郵件加密,那麼收件人必須要和你使用同一個密碼才能對郵件解密。任何一個具有合理長度的密碼字串都可以被用來作為加密處理所需的私用鍵值。
密碼字串會被作雜湊處理(Hash),變成24位元的鍵值資料──低於政府保密機關所設的上限,40位元。Visual Basic的亂數產生器被用來產生擬隨機位元組(Pseudorandom Byte)。一般而言,它的隨機性可以被中等保密程度的密碼技術所採用。但即使有了這種層次的安全措施,仍然有人可以破解由Secret應用程式加密後的訊息和檔案。因此,如果你需要十分嚴格的加密處理,市面上有一些產品可以供你選擇,但是如果你只是要有一些一般性的保護,那麼Secret應用程式就夠用了!
注意:
字串的雜湊處理是一個單向計算,有一點像位元加總檢查(Check Sum),它可以重複但不容易還原。同一個密碼經雜湊處理後可以得到相同的結果,但對於某個經過雜湊處理的結果而言,你很難判斷原來的密碼是什麼。
Secret應用程式如何運作?
你可以選擇任何一種檔案,用Secret應用程式予以加密或解密。在加密後,檔案的開端處會被插入一行識別字串,以便讓Secret應用程式檢查該檔案是否已經經過加密處理。在按下「Encrypt」或「Decrypt」按鈕之前,你必須輸入密碼;如果是加密處理,必須在兩個文字方塊中輸入同樣的密碼;而如果是解密處理,只要輸入一個密碼即可。這是典型的要求密碼輸入方式,用以防止使用者打錯的密碼變成真的密碼。我們用一種可顯示、可列印、可用電子郵件傳送的格式將加密後的檔案儲存起來,即使加密前的原始檔案是二進位檔,加密後仍然可以顯示、列印或用電子郵件傳送。
對同一個檔案加密時,即使每次都使用相同的密碼,我們採用的程式技巧仍然可以使加密後的檔案每次列印或顯示的結果都不一樣。
我們把一個八字元長的隨機"加味"("Salt")字串放進檔案開頭的識別行中,作為識別行的前半段,然後以這個"加味"字串與密碼合併在一起作雜湊處理,經由雜湊處理得到的字串則作為識別行的後半段。這16個字元長的識別字串接下來被用來對檔案進行加密處理,因此,每次同一個檔案加密後的結果都不一樣。另外,在進行解密之前,識別字串可以被用來快速地查驗使用者密碼的正確性;我們把使用者輸入的密碼和"加味"字串做一次雜湊處理,然後把得到的結果與識別字串進行比對,如果密碼正確則對檔案行解密,而由於不正確的密碼無法產生正確的雜湊處理結果,因此錯誤的密碼將不會通過檢查。
從圖34-6中你可以看到,使用者選擇TEST.TXT並且在兩個文字方塊中輸入同一個密碼HARRP (你看不見密碼)。因為這個檔案未經過加密處理,所以「Encrypt」按鈕處於作用狀態而「Decrypt」按鈕在非作用狀態。圖34-7顯示「Encrypt」按鈕被按下之後的情形。由於檔案已經經過加密處理,因此「Decrypt」按鈕現在是在作用狀態,而「Encrypt」按鈕則是在非作用狀態。另外,第二個密碼欄也在非作用狀態,不讓使用者輸入密碼。
圖34-6 Secret應用程式即將對TEST.TXT進行加密處理 |
圖34-7 TEST.TXT已經經過了加密處理 |
圖34-8顯示的是未經過加密前TEST.TXT檔的內容,圖34-9顯示的則是加密後TEST.TXT檔的內容,你可以在加密前和加密後按下「View」按鈕看到以上兩個輸出的結果。我們把加密後的TEST.TXT予以解密,然後再使用相同的密碼做另一次的加密處理,從圖34-10中你可以看到,即使兩次加密處理的對象是同一個原始檔,兩次的結果也會完全不同。
圖34-8 按下「View」按鈕查看TEST.TXT的原始內容 |
圖34-9 按下「View」按鈕查看TEST.TXT加密後的內容 |
圖34-10 用同一個密碼對TEST.TXT做第二次加密處理 |
圖34-11顯示Secret應用程式包含四個檔案。CIPHER.CLS物件類別模組和
第十八章"安全性" 中所介紹的CIPHER.CLS相同;Secret表單中的Hash函式可以把任何字串轉換為一個可重複但無法預測的八字元長字串,這個雜湊處理的結果會被用來驗證使用者在解密時輸入的密碼。View表單則是一個簡單的檔案內容瀏覽器,讓使用者在唯讀模式中可以看見加密前和加密後檔案的內容。
圖34-11 Secret應用程式的專案視窗 |
SECRET.FRM
SECRET.FRM是Secret應用程式的啟始表單。如圖34-12所示,這張表單包含了一個用來輸入檔名的文字方塊、兩個用來輸入密碼的文字方塊,以及四個指令按鈕。表單上的通用對話方塊被用來協助使用者選擇檔案。
圖34-12 設計階段中的Secret表單 |
如果要建立這張表單,請按照以下這兩張表來設定表單及控制項的屬性,然後在表單中加入後面的程式碼。
SECRET.FRM功能表項目 |
標題 | 名稱 | 內縮 | 啟用 |
---|---|---|---|
&File | mnuFile | 0 | True |
&New | mnuNew | 1 | False |
&Open... | mnuOpen | 1 | False |
&Save | mnuSave | 1 | False |
Save &As... | mnuSaveAs | 1 | False |
- | mnuFileDash1 | 1 | True |
E&xit | mnuExit | 1 | True |
&Help | mnuHelp | 0 | True |
&Contents | mnuContents | 1 | True |
&Search for Help on... | mnuSearch | 1 | True |
- | mnuHelpDash1 | 1 | True |
&About... | mnuAbout | 1 | True |
SECRET.FRM物件與屬性設定 |
編號 * | 屬性 | 值 |
---|---|---|
Form |
Name Caption Icon |
frmSecret Secret Secur03.ico |
Label 1 |
Name Caption |
lblFile File: |
TextBox 2 |
Name | txtFile |
CommonDialog 3 |
Name | cdlOne |
CommandButton 4 |
Name Caption |
cmdBrowse &Browse... |
CommandButton 5 |
Name Caption |
cmdView &View |
CommandButton 6 |
Name Caption |
cmdEncrypt &Encrypt |
CommandButton 7 |
Name Caption |
cmdDecrypt &Decrypt |
Label 8 |
Name Caption |
lblPassword1 Enter password: |
Label 9 |
Name Caption |
lblPassword2 Enter password again: |
TextBox 10 |
Name PasswordChar Text |
txtPassword1 * (blank) |
TextBox 11 |
Name PasswordChar Text |
txtPassword2 * (blank) |
*"編號"欄中的號碼用來標示圖34-12中表單上物件的位置。
SECRET.FRM原始程式碼
Option Explicit Private Sub cmdBrowse_Click() `Prompt user for filename cdlOne.DialogTitle = "Secret" cdlOne.Flags = cdlOFNHideReadOnly cdlOne.Filter = "All files (*.*)|*.*" cdlOne.CancelError = True On Error Resume Next cdlOne.ShowOpen `Grab filename If Err = 0 Then txtFile.Text = cdlOne.FileName End If On Error GoTo 0 End Sub Private Sub cmdEncrypt_Click() `Make sure both passwords match exactly If txtPassword1.Text <> txtPassword2.Text Then MsgBox "The two passwords are not the same!", _ vbExclamation, "Secret" Exit Sub End If `Encrypt file MousePointer = vbHourglass cmdEncrypt.Enabled = False cmdDecrypt.Enabled = False cmdView.Enabled = False cmdBrowse.Enabled = False Refresh Encrypt txtFile_Change MousePointer = vbDefault End Sub Private Sub cmdDecrypt_Click() MousePointer = vbHourglass cmdEncrypt.Enabled = False cmdDecrypt.Enabled = False cmdView.Enabled = False cmdBrowse.Enabled = False Refresh Decrypt txtFile_Change MousePointer = vbDefault End Sub Private Sub cmdView_Click() Dim strA As String Dim lngZndx As Long MousePointer = vbHourglass `Get file contents Open txtFile.Text For Binary As #1 strA = Space$(LOF(1)) Get #1, , strA Close #1 Do lngZndx = InStr(strA, Chr$(0)) If lngZndx = 0 Or lngZndx > 5000 Then Exit Do Mid$(strA, lngZndx, 1) = Chr$(1) Loop `Display file contents MousePointer = vbDefault frmView.rtfView.Text = strA frmView.Caption = "Secret - " & txtFile.Text frmView.Show vbModal End Sub Private Sub Form_Load() `Center this form Me.Left = (Screen.Width - Me.Width) \ 2 Me.Top = (Screen.Height - Me.Height) \ 2 `Disable most command buttons cmdEncrypt.Enabled = False cmdDecrypt.Enabled = False cmdView.Enabled = False `Initialize filename field txtFile.Text = "" End Sub Private Sub mnuAbout_Click() `Set properties About.Application = "Secret" About.Heading = _ "Microsoft Visual Basic 6.0 Developer's Workshop" About.Copyright = "1998 John Clark Craig and Jeff Webb" About.Display End Sub Private Sub mnuExit_Click() Unload Me End Sub Private Sub mnuContents_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpContents cdlOne.ShowHelp End Sub Private Sub mnuSearch_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpPartialKey cdlOne.ShowHelp End Sub Private Sub txtFile_Change() Dim lngFileLen As Long Dim strHead As String `Check to see whether file exists On Error Resume Next lngFileLen = Len(Dir(txtFile.Text)) `Disable buttons if filename isn't valid If Err <> 0 Or lngFileLen = 0 Or Len(txtFile.Text) = 0 Then cmdEncrypt.Enabled = False cmdDecrypt.Enabled = False cmdView.Enabled = False lblPassword1.Enabled = False txtPassword1.Enabled = False lblPassword2.Enabled = False txtPassword2.Enabled = False txtPassword2.Text = "" Exit Sub End If `Get first 8 bytes of selected file Open txtFile.Text For Binary As #1 strHead = Space(8) Get #1, , strHead Close #1 `Check to see whether file is already encrypted If strHead = "[Secret]" Then cmdEncrypt.Enabled = False cmdDecrypt.Enabled = True lblPassword2.Enabled = False txtPassword2.Enabled = False txtPassword2.Text = "" Else cmdEncrypt.Enabled = True cmdDecrypt.Enabled = False lblPassword2.Enabled = True txtPassword2.Enabled = True End If lblPassword1.Enabled = True txtPassword1.Enabled = True cmdBrowse.Enabled = True cmdView.Enabled = True End Sub Sub Encrypt() Dim strHead As String Dim strT As String Dim strA As String Dim cphX As New Cipher Dim lngN As Long Open txtFile.Text For Binary As #1 `Load entire file into strA strA = Space$(LOF(1)) Get #1, , strA Close #1 `Prepare header string with salt characters strT = Hash(Date & Str(Timer)) strHead = "[Secret]" & strT & Hash(strT & txtPassword1.Text) `Do the encryption cphX.KeyString = strHead cphX.Text = strA cphX.DoXor cphX.Stretch strA = cphX.Text `Write header Open txtFile.Text For Output As #1 Print #1, strHead `Write encrypted data lngN = 1 Do Print #1, Mid(strA, lngN, 70) lngN = lngN + 70 Loop Until lngN > Len(strA) Close #1 End Sub Sub Decrypt() Dim strHead As String Dim strA As String Dim strT As String Dim cphX As New Cipher Dim lngN As Long `Get header (first 18 bytes of encrypted file) Open txtFile.Text For Input As #1 Line Input #1, strHead Close #1 `Check for correct password strT = Mid(strHead, 9, 8) If InStr(strHead, Hash(strT & txtPassword1.Text)) <> 17 Then MsgBox "Sorry, this is not the correct password!", _ vbExclamation, "Secret" Exit Sub End If `Get file contents Open txtFile.Text For Input As #1 `Read past the header Line Input #1, strHead `Read and build the contents string Do Until EOF(1) Line Input #1, strT strA = strA & strT Loop Close #1 `Decrypted file contents cphX.KeyString = strHead cphX.Text = strA cphX.Shrink cphX.DoXor strA = cphX.Text `Replace file with decrypted version Kill txtFile.Text Open txtFile.Text For Binary As #1 Put #1, , strA Close #1 End Sub Function Hash(strA As String) As String Dim cphHash As New Cipher cphHash.KeyString = strA & "123456" cphHash.Text = strA & "123456" cphHash.DoXor cphHash.Stretch cphHash.KeyString = cphHash.Text cphHash.Text = "123456" cphHash.DoXor cphHash.Stretch Hash = cphHash.Text End Function
VIEW.FRM
這張表單很簡單,只含一個RichTextBox控制項,用來在唯讀模式下顯示檔案內容。圖34-13顯示的是設計階段中的View表單。
圖34-13 設計階段中的View表單 |
如果要建立這張表單,請按照下表來設定表單和控制項的屬性,然後在表單中加入後面的程式碼。
VIEW.FRM物件與屬性設定 |
屬性 | 值 |
---|---|
Form | |
Name Caption MaxButton MinButton |
frmView Secret - View File False False |
RichTextBox | |
Name Scrollbars Locked |
rtfView 3 - Both True |
VIEW.FRM原始程式碼
Option Explicit Dim mblnBeenHereDoneThis As Boolean Private Sub Form_Load() mblnBeenHereDoneThis = False Private Sub Form_Resize() `Center this form, but only the first time If mblnBeenHereDoneThis = False Then Me.Left = (Screen.Width - Me.Width) \ 2 Me.Top = (Screen.Height - Me.Height) \ 2 mblnBeenHereDoneThis = True End If `Size RichTextBox to fill form rtfView.Move 0, 0, Me.ScaleWidth, Me.ScaleHeight End Sub
CIPHER.CLS
這個物件類別模組是Secret應用程式的核心,在介紹
第十八章"安全性" 時,我們曾經詳細地討論過這些Cipher物件,在這裡我們還是把它們列出來,以方便讀者閱讀。`CIPHER.CLS Option Explicit Private mstrKey As String Private mstrText As String `~~~.KeyString `A string (key) used in encryption and decryption Public Property Let KeyString(strKey As String) mstrKey = strKey Initialize End Property `~~~.Text `Write text to be encrypted or decrypted Public Property Let Text(strText As String) mstrText = strText End Property `Read text that was encrypted or decrypted Public Property Get Text() As String Text = mstrText End Property `~~~.DoXor `Exclusive-or method to encrypt or decrypt Public Sub DoXor() Dim lngC As Long Dim intB As Long Dim lngN As Long For lngN = 1 To Len(mstrText) lngC = Asc(Mid(mstrText, lngN, 1)) intB = Int(Rnd * 256) Mid(mstrText, lngN, 1) = Chr(lngC Xor intB) Next lngN End Sub `~~~.Stretch `Convert any string to a printable, displayable string Public Sub Stretch() Dim lngC As Long Dim lngN As Long Dim lngJ As Long Dim lngK As Long Dim lngA As Long Dim strB As String lngA = Len(mstrText) strB = Space(lngA + (lngA + 2) \ 3) For lngN = 1 To lngA lngC = Asc(Mid(mstrText, lngN, 1)) lngJ = lngJ + 1 Mid(strB, lngJ, 1) = Chr((lngC And 63) + 59) Select Case lngN Mod 3 Case 1 lngK = lngK Or ((lngC \ 64) * 16) Case 2 lngK = lngK Or ((lngC \ 64) * 4) Case 0 lngK = lngK Or (lngC \ 64) lngJ = lngJ + 1 Mid(strB, lngJ, 1) = Chr(lngK + 59) lngK = 0 End Select Next lngN If lngA Mod 3 Then lngJ = lngJ + 1 Mid(strB, lngJ, 1) = Chr(lngK + 59) End If mstrText = strB End Sub `~~~.Shrink `Inverse of the Stretch method; `result can contain any of the 256-byte values Public Sub Shrink() Dim lngC As Long Dim lngD As Long Dim lngE As Long Dim lngA As Long Dim lngB As Long Dim lngN As Long Dim lngJ As Long Dim lngK As Long Dim strB As String lngA = Len(mstrText) lngB = lngA - 1 - (lngA - 1) \ 4 strB = Space(lngB) For lngN = 1 To lngB lngJ = lngJ + 1 lngC = Asc(Mid(mstrText, lngJ, 1)) - 59 Select Case lngN Mod 3 Case 1 lngK = lngK + 4 If lngK > lngA Then lngK = lngA lngE = Asc(Mid(mstrText, lngK, 1)) - 59 lngD = ((lngE \ 16) And 3) * 64 Case 2 lngD = ((lngE \ 4) And 3) * 64 Case 0 lngD = (lngE And 3) * 64 lngJ = lngJ + 1 End Select Mid(strB, lngN, 1) = Chr(lngC Or lngD) Next lngN mstrText = strB End Sub `Initializes random numbers using the key string Private Sub Initialize() Dim lngN As Long Randomize Rnd(-1) For lngN = 1 To Len(mstrKey) Randomize Rnd(-Rnd * Asc(Mid(mstrKey, lngN, 1))) Next lngN End Sub
BitPack應用程式
雖然Visual Basic提供了機器碼編譯器,但是在面對某些種類的問題時,C語言的DLL在速度上還是比Visual Basic略勝一籌。BitPack應用程式呼叫由C語言建立的DLL,展示了這種DLL在Visual Basic應用程式中執行時驚人的處理速度。
BITPACK.DLL被設計來處理位元組陣列中的每一個單獨的位元,它包含了三個函式:BitGet傳回某個位元的狀態;BitSet把某個位置上的位元設為1;BitClr設定某個位元為0。只要把位元組陣列和位元的序號傳給這些函式,C程式碼會為你完成工作。例如,要取得某個位元組陣列中第542個位元的狀態,BitGet函式可以很快地找到第67位元組後面的第6個位元,取得該位元的值,如果其值為1則傳回1,其值為0則傳回0。在32位元的Visual Basic裡,Byte陣列可以宣告得很大,因此我們可以把近乎無限大的位元集合放在一個Byte陣列中,用上述這些函式加以存取處理。
建立一個質數表
以上這些函式最能發揮它們效用的地方在資料擷取和開關控制處理這兩方面。數以千計的控制開關可以用位元集合來代表,把這個位元集合放在一個位元組陣列中,我們可以用這些函式來加以維護。在本書的範例中,我們用一個小程式建立了一個質數表,用以展示這些函式的用法。
我們以一個位元陣列代表這個質數表,陣列中的每一個位元都代表奇數(所有質數皆為奇數);如果某個位元的內含值是0,這個位元所代表的奇數就是一個質數,如果是1則代表非質數。因為DLL速度的關係,我們可以在很短的時間內建立一個龐大的質數表,例如範圍從1到1,000,000的質數表。了解到我們呼叫了多少次BitSet函式和BitGet函式之後,你就可以感覺得到C語言DLL函式的威力了。
建立BitPack DLL專案檔
在發展BitPack應用程式之前,必須先建C立語言的DLL,才能用BitPack應用程式產生上述的質數表。
注意:
因為本書著重於使用Microsoft的工具來建立在Windows 95下執行的應用程式,所以我們簡化了下面這個範例DLL的程式碼,這樣應該可以幫你了解建立DLL的重點步驟。如果你用的C語言編譯器並不是Microsoft Visual C++ 6.0,你可能要對以下的發展步驟和檔案內容作一些修改。關於建立DLL的詳細資訊,請參考相關的技術文件。
以下這三個檔是你在建立Viasual C++ 專案時唯一需要用到的檔案。請在32位元版的C++ 編譯器中新增一個專案,選擇DLL作為專案的類別,輸入BITPACK做為專案名稱。當你被問到要建立哪一種DLL時,請選擇"A DLL That Exports Some Symbols"選項。接下來,在專案中建立一個DEF檔,把下面這幾行輸入到檔案中,然後把檔案存成BITPACK.DEF:
;BitPack.def LIBRARY BitPack EXPORTS BitGet BitSet BitClr
DEF把這個DLL專案所要提供的函式名稱向外界宣佈。也就是說,DEF檔所列出的函式就是你可以在Visual Basic應用程式中所呼叫的函式。
你必須修改兩個由Visual C++ 自動產生的檔案。BITPACK.CPP包含了三個只有一行程式碼的函式,這些函式執行所有必要的定址、遮罩以及其他的位元運算,以便能夠對位元組陣列中某個特定的位元作存取動作。請將下面的程式碼輸入到一個新檔案中,然後把這個檔案存成BITPACK.CPP,而且讓這個檔案包括在你的Visual C++ 專案中。
// BitPack.cpp : Defines the entry point for the DLL application // #include "stdafx.h" #include "BitPack.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } BITPACK_API int _stdcall BitGet(LPBYTE bytes, int bitpos) { return( bytes[bitpos >> 3] & (1 << (bitpos % 8)) ? 1: 0); } BITPACK_API int _stdcall BitSet(LPBYTE bytes, int bitpos) { return( bytes[bitpos >> 3] |= (1 << (bitpos % 8))); } BITPACK_API int _stdcall BitClr(LPBYTE bytes, int bitpos) { return( bytes[bitpos >> 3] &= ~(1 << (bitpos % 8))); }
另一個需要修改的是BITPACK.H檔,它用來宣告BitPack的所有函式。修改後的BITPACK.H內容如下:
// The following ifdef block is the standard way of creating // macros that make exporting from a DLL simpler. All files // within this DLL are compiled with the BITPACK_EXPORTS // symbol defined on the command line. This symbol should not // be defined on any project that uses this DLL. In this way, any // other project whose source files include this file see // BITPACK_API functions as being imported from a DLL, whereas // this DLL sees symbols defined with this macro as being exported. #ifdef BITPACK_EXPORTS #define BITPACK_API __declspec(dllexport) #else #define BITPACK_API __declspec(dllimport) #endif BITPACK_API int _stdcall BitGet(LPBYTE bytes, int bitpos); BITPACK_API int _stdcall BitSet(LPBYTE bytes, int bitpos); BITPACK_API int _stdcall BitClr(LPBYTE bytes, int bitpos);
在Visual C++ 環境中按下「Build All」的按鈕,編譯器會把上面的兩個檔案加以連結編譯,然後產生一個BITPACK.DLL模組。現在,把BITPACK.DLL模組複製到Windows的SYSTEM目錄下,這樣,在Visual Basic中使用的Declare陳述式就可以自動地找到這個DLL檔,BitPack裡的三個函式才能在Visual Basic的應用程式中被宣告和使用。
BITPACK.FRM
這張表單的用途在於要求使用者輸入預期中最大質數的上限值,然後表單會呼叫BitPack DLL中的函式,產生一個以位元組陣列為代表的質數表,並且列出所有的質數。我們在表單上放置了一個進度指示器,這樣你可以觀察程式在處理質數計算時的速度。在筆者的電腦上,建立質數表的速度比列印結果的速度還快。圖34-14顯示的是執行中的BitPack表單,它正在計算1到1,000,000之間所有的質數。
圖34-14 執行中的BitPack表單 |
程式的輸出結果被寫到C:\WINDOWS\DESKTOP\PRIMES.TXT這個檔案中,當然,你可以改變這個路徑或是檔名。我們在BITPACK.FRM程式碼的開頭不遠處定義了一個常數FileName,用這個常數來代表檔名和路徑。如果你建立的質數表是一個很大的質數表,這個輸出檔可能也會相當地大。如果要約略估算輸出檔的大小,你可以把上限值除以2,例如,你要找所有小於200,000的質數,PRIME.TXT的檔案大小約為100,000位元組。
圖34-15顯示的是PRIME.TXT的內容,這是BitPack應用程式在計算所有小於1,000,000的質數時所找到前面部分的質數。
BitPack表單提供了一個ProgressBar控制項的實作範例,在這張表單中,這個ProgressBar控制項被用來顯示質數表產生的進度,也用來顯示輸出檔PRIMES.TXT產生的進度。我們讓ProgressBar控制項的Visible屬性和指令按鈕控制項的Visible屬性相互切換,這樣你不是看到ProgressBar控制項,就是看到指令按鈕,但決不會同時看到兩個控制項。
圖34-15 PRIMES.TXT的內容 |
圖34-16 顯示的是設計中的BitPack表單 |
如果要建立BITPACK.FRM,請按照以下這兩張表來設定表單及控制項的屬性,然後在表單中加入後面的程式碼。
BITPACK.FRM功能表項目 |
標題 | 名稱 | 內縮 | 啟用 |
---|---|---|---|
&File | mnuFile | 0 | True |
&New | mnuNew | 1 | False |
&Open... | mnuOpen | 1 | False |
&Save | mnuSave | 1 | False |
Save &As... | mnuSaveAs | 1 | False |
- | mnuFileDash1 | 1 | True |
E&xit | mnuExit | 1 | True |
&Help | mnuHelp | 0 | True |
&Contents | mnuContents | 1 | True |
&Search for Help on... | mnuSearch | 1 | True |
- | mnuHelpDash1 | 1 | True |
&About... | mnuAbout | 1 | True |
BITPACK.FRM物件與屬性設定 |
編號 * | 屬性 | 值 |
---|---|---|
Form |
Name BorderStyle Caption |
frmBitPack 3 - Fixed Dialog BitPack - Prime Numbers |
Label 1 |
Name Caption |
lblPrompt Enter prime number ceiling... |
TextBox 2 |
Name | txtMaxPrime |
CommonDialog 3 |
Name | cdlOne |
Label 4 |
Name | lblStatus |
CommandButton 5 |
Name Caption |
cmdPrimes Generate PRIMES.TXT |
ProgressBar 6 |
Name | prgOne |
*"編號"欄中的號碼用來標示圖34-16中表單上物件的位置。
BITPACK.FRM原始程式碼
Option Explicit Private Declare Function BitGet _ Lib "BitPack.dll" ( _ ByRef bytB As Byte, _ ByVal lngN As Long _ ) As Long Private Declare Function BitSet _ Lib "BitPack.dll" ( _ ByRef bytB As Byte, _ ByVal lngN As Long _ ) As Long Private Declare Function BitClr _ Lib "BitPack.dll" ( _ ByRef bytB As Byte, _ ByVal lngN As Long _ ) As Long `Change output path or filename here Const FileName = "C:\Windows\Desktop\Primes.txt" Private Sub cmdPrimes_Click() Dim lngN As Long Dim lngI As Long Dim lngJ As Long Dim lngK As Long Dim lngNext As Long Dim lngLast As Long Dim bytAry() As Byte Dim strP As String `Show hourglass while busy MousePointer = vbHourglass cmdPrimes.Visible = False prgOne.Visible = True prgOne.Value = 0 `Get largest prime number specified lngN = Abs(Val(txtMaxPrime.Text)) `Match only odd numbers to bits in byte array ReDim bytAry(lngN \ 16) `Keep user informed of progress lblStatus.Caption ="Generating prime numbers tableDear John, How Do I... " Refresh `Process byte array; 0 bits represent prime numbers lngK = (lngN - 3) \ 2 For lngI = 0 To lngK `If next number is primeDear John, How Do I... If BitGet(bytAry(0), lngI) = 0 Then `Dear John, How Do I... set bits that are multiples For lngJ = 3 * lngI + 3 To lngK Step 2 * lngI + 3 BitSet bytAry(0), lngJ Next lngJ `Update progress bar, but not too often lngNext = Int(100 * lngI / lngK) If lngNext <> lngLast Then lngLast = lngNext prgOne.Value = lngNext End If End If Next lngI `Keep user informed lblStatus.Caption = "Writing prime numbers fileDear John, How Do I... " lngLast = 0 prgOne.Value = 0 Refresh `Write primes to file on desktop Open FileName For Output As #1 `Bit table starts at 3, so output 2 as prime Print #1, "Prime numbers up to" & Str$(lngN) & vbCrLf strP = "2" For lngI = 0 To lngK `If prime numberDear John, How Do I... If BitGet(bytAry(0), lngI) = 0 Then `Concatenate number to string for output strP = strP & Str$(lngI + lngI + 3) `If string is long enoughDear John, How Do I... If Len(strP) > 65 Then `Output string to file Print #1, LTrim$(strP) `Prepare for next line of output strP = "" `Update progress bar, but not too often lngNext = Int(100 * lngI / lngK) If lngNext > lngLast Then lngLast = lngNext prgOne.Value = lngNext End If End If End If Next lngI `Print any last-line primes Print #1, LTrim$(strP) Close #1 `Set form to original visible state lblStatus.Caption = "" cmdPrimes.Visible = True prgOne.Visible = False MousePointer = vbDefault End Sub Private Sub Form_Load() txtMaxPrime.Text = "" lblStatus.Caption = "" prgOne.Visible = False End Sub Private Sub mnuAbout_Click() `Set properties About.Application = "BitPack" About.Heading = _ "Microsoft Visual Basic 6.0 Developer's Workshop" About.Copyright = "1998 John Clark Craig and Jeff Webb" About.Display End Sub Private Sub mnuExit_Click() Unload Me End Sub Private Sub mnuContents_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpContents cdlOne.ShowHelp End Sub Private Sub mnuSearch_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpPartialKey cdlOne.ShowHelp End Sub
在BitPack表單中,我們把位元組陣列bytAry() 的第一個元素傳給BITPACK.DLL裡的函式,你也可以傳遞一個不在陣列中的單一Byte變數給這些函式,在這種情形下,BitPos參數應該在0到7的範圍之間。為了得到最快的速度,我們在DLL的函式中故意不做數值範圍檢查的動作。你可以在Visual Basic應用程式中做這個動作,防止BitPos超過範圍。我們知道,位元組陣列中的元素個數是100,那麼BitPos的合法範圍應該是從0到807。
為了要用質數表計算質數,我們把奇數3,5,7.... 以位元0,1,2..... 來代表。這樣,位元組陣列中的每8個位元就可以涵蓋16個整數的範圍。另一方面,由於Visual Basic本身支援巨大的Byte陣列,因此,理論上你可以用這個程式計算出相當大的質數。
Dialogs應用程式
通用對話方塊控制項提供了許多威力強大的選項,讓使用者與系統可以透過一些標準的界面交談。Dialogs應用程式舉例說明了如何運用通用對話方塊控制項,呼叫五種由它所提供的對話方塊。我們在一個工具列上設計了五個按鈕,分別可以啟動「開啟舊檔」、「另存新檔」、「色彩」、「字型」以及「列印」等五個對話方塊。使用者選擇了這些選項之後,對應的對話方塊會被顯示出來,但是不會影響任何檔案或設定。例如,你可以用Dialogs應用程式叫出「另存新檔」對話方塊,選擇系統中的任何一個檔案,但是實際上不會有任何檔案受到影響。
圖34-17顯示的是執行中的Dialogs應用程式,圖34-18到圖34-22顯示的是由Dialogs工具列上的五個按鈕所啟動的五個不同的標準對話方塊。
圖34-17 執行中的Dialogs應用程式 |
圖34-18 由通用對話方塊叫出的「開啟舊檔」對話方塊 |
圖34-19 由通用對話方塊叫出的「另存新檔」對話方塊 |
圖34-20 由通用對話方塊叫出的「色彩」對話方塊 |
圖34-21 由通用對話方塊叫出的「字型」對話方塊 |
圖34-22 由通用對話方塊叫出的「列印」對話方塊 |
筆者在發展其他的應用程式時,經常利用Dialogs應用程式。例如,如果需要在新的應用程式中加入選擇色彩的功能時,只要從Dialogs應用程式的程式碼中,複製相關的部分到新的應用程式中,在新的應用程式中加入一個通用對話方塊即可,若有特殊需要再修改程式。這種方式可以省下很多的時間,比查詢線上說明再一步步建立通用對話方塊來得快多了!
特殊功能
Dialogs應用程式運用許多靈活的技巧提供了一些特殊的功能。
About和About2
這個應用程式展示了兩種不同的「About」對話方塊,它的「Help」功能表中包含了「About」和「About2」兩個選項。第一種「About」對話方塊是我們在本書中使用得最多的「關於」對話方塊,而「About2」則是在
第三十一章"日期與時間" 中介紹的另一種「關於」對話方塊。雖然這兩種對話方塊在外觀上相似,但事實上它們背後的設計技巧卻全然不同。
日落景色的背景
我們在 第十四章"繪圖技巧" 中曾經介紹過製作表單背景的技巧,我們把這個技巧加以修改,使原來"藍黑漸層"的背景變為"紅黃漸層",就像日落景色一樣。你可以仿此方法,很容易地就可以改變背景的色調。
隱藏的訊息方塊
在 第十八章"安全性" 中,我們曾經介紹過"復活節彩蛋"訊息方塊,在這裡我們也把這項功能添加在Dialogs應用程式裡。隱藏式的訊息方塊可以用很多種方式啟動,在這個應用程式中,我們把四次滑鼠按鍵事件(Click)所發生的位置記錄下來,如果說這四次按鍵的位置和順序剛好符合程式所要求的按鍵位置和順序,隱藏的訊息方塊就會顯示出來。也就是說,如果你在表單中央圖片方塊的左上角、右上角、右下角和左下角這四個位置上,依序按下滑鼠左鍵,那麼隱藏的訊息方塊就會出現,並且停留五秒鐘後自動消失。
表單定位
在整本書的範例中,我們一直在表單的Load事件程序中把表單定位在螢幕中央,如果稍微修改這個表單定位技巧,你可以把表單定位在螢幕的任何一處。如果要知道這個修改後的螢幕定位技巧實際的運作情形,請在Dialogs應用程式主表單中央的圖形上隨處按下滑鼠左鍵;如果在這張圖形上按鍵的位置是圖形由左至右的四分之一、由頂到底的四分之三處,那麼表單的中心點會跳到整個螢幕上相同的相對位置上(在螢幕由左至右的四分之一、由頂到底的四分之三處),兩秒鐘之後,表單又會跳回原來螢幕中央的位置。這些表單重新定位的動作都在picScreen_Click事件程序中,你可以看看這裡的程式碼以了解這一切動作是如何運作的。
圖34-23顯示滑鼠游標在上述的位置上,圖34-24顯示表單被暫時移到了新的位置上。
圖34-23 點選圖形的任何一處使表單暫時移到新的位置上 |
圖34-24 整張表單會暫時移動到指定的位置上 |
應用程式的所有檔案
Dialogs應用程式中有四個檔案,除了主表單之外,還有兩張「About」表單和一張隱藏的訊息表單。圖34-25顯示Dialogs應用程式的專案內容。
圖34-25 Dialogs應用程式的專案內容 |
DIALOGS.FRM
Dialogs應用程式把Windows 95的桌面圖片放在表單中央的圖形方塊中(前面提過,程式執行時,若在這張圖形的任何一處按下滑鼠左鍵,表單就會移動到真實桌面上相同的對應位置上)。圖34-26顯示的是設計中的Dialog表單。
圖34-26 設計中的Dialog表單 |
如果要建立DIALOGS.FRM,請按照以下這兩張表來設定表單及控制項的屬性,然後在表單中加入後面的程式碼。
DIALOGS.FRM功能表項目 |
標題 | 名稱 | 內縮 | 啟用 |
---|---|---|---|
&File | mnuFile | 0 | True |
&New | mnuNew | 1 | False |
&Open... | mnuOpen | 1 | False |
&Save | mnuSave | 1 | False |
Save &As... | mnuSaveAs | 1 | False |
- | mnuFileDash1 | 1 | True |
E&xit | mnuExit | 1 | True |
&Help | mnuHelp | 0 | True |
&Contents | mnuContents | 1 | True |
&Search for Help on... | mnuSearch | 1 | True |
- | mnuHelpDash1 | 1 | True |
&About... | mnuAbout | 1 | True |
About&2... | mnuAbout2 | 1 | True |
DIALOGS.FRM物件與屬性設定 |
編號 * | 屬性 | 值 |
---|---|---|
Form |
Name Caption BorderStyle Icon |
frmDialogs Dialogs 3 - Fixed Dialog Pc01.ico |
PictureBox 1 |
Name Align |
picTop 1 - Align Top |
CommandButton 2 |
Name Caption |
cmdOpen &Open |
CommandButton 3 |
Name Caption |
cmdSave &Save |
CommandButton 4 |
Name Caption |
cmdColor &Color |
CommandButton 5 |
Name Caption |
cmdFont &Font |
CommandButton 6 |
Name Caption |
cmdPrint |
PictureBox 7 |
Name AutoSize Picture |
picScreen True DESKTOP.BMP |
CommonDialog 8 |
Name | cdlOne |
Timer 9 |
Name Interval |
tmrClock 100 |
Timer 10 |
Name Enabled Interval |
tmrRelocate False 2000 |
StatusBar 11 |
Name Align Style |
stbBottom 2 - vbAlignBottom 1 - sbrSimple |
*"編號"欄中的號碼用來標示圖34-26中表單上物件的位置
DIALOGS.FRM原始程式碼
Option Explicit Dim mvntX, mvntY Dim mvntLastSec Dim mvntEggX(1 To 4) Dim mvntEggY(1 To 4) Private Sub Form_Click() Dim intI As Integer `Keep track of last four clicks on form For intI = 1 To 3 mvntEggX(intI) = mvntEggX(intI + 1) mvntEggY(intI) = mvntEggY(intI + 1) Next intI mvntEggX(4) = mvntX mvntEggY(4) = mvntY `Check for correct sequence and position If Abs(mvntEggX(1) - 70) < 30 And _ Abs(mvntEggY(1) - 60) < 30 And _ Abs(mvntEggX(2) - 360) < 30 And _ Abs(mvntEggY(2) - 60) < 30 And _ Abs(mvntEggX(3) - 360) < 30 And _ Abs(mvntEggY(3) - 290) < 30 And _ Abs(mvntEggX(4) - 70) < 30 And _ Abs(mvntEggY(4) - 290) < 30 Then `Display hidden message dlgEgg.Show vbModal End If End Sub Private Sub Form_Load() `Center this form Me.Left = (Screen.Width - Me.Width) \ 2 Me.Top = (Screen.Height - Me.Height) \ 2 `Adjust button bar height picTop.Height = cmdOpen.Height + _ (picTop.Height - picTop.ScaleHeight) End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) `Signal timer to update status bar mvntLastSec = -1 `Keep track of mouse location mvntX = X mvntY = Y End Sub Private Sub Form_Paint() Dim lngN As Long With Me .ScaleMode = vbPixels .DrawStyle = 5 `Transparent .DrawWidth = 1 End With `Draw sunset background (fade from red to yellow) For lngN = 0 To ScaleHeight Step ScaleHeight \ 16 Line (-1, lngN - 1) - (ScaleWidth, lngN + ScaleHeight \ 16), _ RGB(255, lngN * 255 \ ScaleHeight, 0), BF Next lngN End Sub Private Sub mnuAbout_Click() `Set properties for the About dialog About.Application = "Dialogs" About.Heading = _ "Microsoft Visual Basic 6.0 Developer's Workshop" About.Copyright = "1998 John Clark Craig and Jeff Webb" About.Display End Sub Private Sub mnuAbout2_Click() `Display the About2 dialog frmAbout2.Display End Sub Private Sub mnuExit_Click() Unload Me End Sub Private Sub mnuContents_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpContents cdlOne.ShowHelp End Sub Private Sub mnuSearch_Click() cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp" cdlOne.HelpCommand = cdlHelpPartialKey cdlOne.ShowHelp End Sub Private Sub picScreen_Click() Dim vntXpct Dim vntYpct `Determine mouse's relative position in picture vntXpct = 100 * mvntX \ picScreen.ScaleWidth vntYpct = 100 * mvntY \ picScreen.ScaleHeight `Move form's center to same relative position on screen Me.Left = Screen.Width * vntXpct \ 100 - Me.Width \ 2 Me.Top = Screen.Height * vntYpct \ 100 - Me.Height \ 2 `Set timer to move form back later tmrRelocate.Enabled = True End Sub Private Sub picScreen_MouseMove(Button As Integer, _ Shift As Integer, X As Single, Y As Single) `Keep track of mouse location mvntX = X mvntY = Y `Update status message at bottom of form stbBottom.SimpleText = "Click to temporarily relocate " & _ "center of this form on desktop" `Signal timer not to display date and time in status bar mvntLastSec = -2 End Sub Private Sub cmdColor_Click() `Set flags for Color dialog box cdlOne.Flags = cdlCCRGBInit `Show Color dialog box cdlOne.ShowColor `Display selected color value MsgBox "&H" & Hex$(cdlOne.Color), , _ "Selected colorDear John, How Do I... " End Sub Private Sub cmdFont_Click() Dim strTab2 As String strTab2 = vbTab & vbTab `Set flags for Font dialog box cdlOne.Flags = cdlCFWYSIWYG + cdlCFBoth + cdlCFScalableOnly `Show Font dialog box cdlOne.ShowFont `Display selected font values MsgBox _ "Font Name:" & vbTab & cdlOne.FontName & vbCrLf & _ "Font Size:" & strTab2 & cdlOne.FontSize & vbCrLf & _ "Bold:" & strTab2 & cdlOne.FontBold & vbCrLf & _ "Italic:" & strTab2 & cdlOne.FontItalic, , _ "Selected fontDear John, How Do I... " End Sub Private Sub cmdOpen_Click() `Set up sample filter for Open dialog box Dim strBat As String Dim strTxt As String Dim strAll As String strBat = "Batch Files (*.bat)|*.bat" strTxt = "Text Files (*.txt)|*.txt" strAll = "All Files (*.*)|*.*" cdlOne.Filter = strBat & "|" & strTxt & "|" & strAll `Set default filter to third one listed cdlOne.FilterIndex = 3 `Hide "ReadOnly" check box cdlOne.Flags = cdlOFNHideReadOnly `Deselect previously selected file, if any cdlOne.FileName = "" `Show Open dialog box cdlOne.ShowOpen `Display selected filename If cdlOne.FileName = "" Then Exit Sub MsgBox cdlOne.FileName, , "Selected fileDear John, How Do I... " End Sub Private Sub cmdPrint_Click() Dim strPrintToFile As String `Set flags for Print dialog box cdlOne.Flags = cdlPDAllPages + cdlPDNoSelection `Set imaginary page range cdlOne.Min = 1 cdlOne.Max = 100 cdlOne.FromPage = 1 cdlOne.ToPage = 100 `Show Print dialog box cdlOne.ShowPrinter `Extract some printer data If cdlOne.Flags And cdlPDPrintToFile Then strPrintToFile = "Yes" Else strPrintToFile = "No" End If `Display selected print values MsgBox _ "Begin Page:" & vbTab & cdlOne.FromPage & vbCrLf & _ "End Page:" & vbTab & cdlOne.ToPage & vbCrLf & _ "No. Copies:" & vbTab & cdlOne.Copies & vbCrLf & _ "Print to File:" & vbTab & strPrintToFile _ , , "Selected print informationDear John, How Do I... " End Sub Private Sub cmdSave_Click() `Set up filter for Save As dialog box Dim strBat As String Dim strTxt As String Dim strAll As String strBat = "Batch Files (*.bat)|*.bat" strTxt = "Text Files (*.txt)|*.txt" strAll = "All Files (*.*)|*.*" cdlOne.Filter = strBat & "|" & strTxt & "|" & strAll `Set default filter to third one listed cdlOne.FilterIndex = 3 `Hide ReadOnly check box cdlOne.Flags = cdlOFNHideReadOnly `Deselect previously selected file, if any cdlOne.FileName = "" `Show the Save As dialog box cdlOne.ShowSave `Display the selected file If cdlOne.FileName = "" Then Exit Sub MsgBox cdlOne.FileName, , "`Save As' fileDear John, How Do I... " End Sub Private Sub tmrRelocate_Timer() `Relocate form once per move tmrRelocate.Enabled = False `Center this form Me.Left = (Screen.Width - Me.Width) \ 2 Me.Top = (Screen.Height - Me.Height) \ 2 End Sub Private Sub tmrClock_Timer() Dim vntSec vntSec = Second(Now) If vntSec = mvntLastSec Then Exit Sub If mvntLastSec = -2 Then Exit Sub mvntLastSec = vntSec `Update date and time in status line stbBottom.SimpleText = Format(Date, "Long Date") & _ Space$(5) & Format(Time, "hh:mm AMPM") End Sub
DLGEGG.FRM
DLGEGG.FRM是一張很簡單的表單,用來顯示預先隱藏的訊息,我們用一個計時器控制項讓表單出現後五秒鐘自動關閉。你可以改變表單中的訊息以及表單關閉的方式。
圖34-27顯示的是設計階段中的dlgEgg表單。
圖34-27 設計階段中的dlgEgg表單 |
DLGEGG.FRM物件與屬性設定 |
屬性 | 值 |
---|---|
Form | |
Name Caption BackColor BorderStyle ControlBox MaxButton MinButton WindowState |
dlgEgg dlgEgg &H0000FFFF& 1 - Fixed Single False False False 0 - Normal |
Label | |
Name Alignment Caption Font BackColor |
lblEgg 2 - Center This "Easter egg" (hidden message) will disappear in 5 seconds. Arial - Italic - 14 &H0000FFFF& |
Timer | |
Name Enabled Interval |
tmrQuit True 5000 |
DLGEGG.FRM原始程式碼
Option Explicit Private Sub tmrQuit_Timer() Unload Me End Sub