?? 49.asp
字號:
<P> </P>
</DIR>
</DIR>
<P>在以上程式中參數一 folder 最值得注意, 由於「啟動」資料夾是「程式集」的子資料夾,
所以將此一參數寫成 "\啟動", 再舉個例子, 假設我們想把同樣的捷徑建立在「桌面」上,
則此一參數應設定為 "..\..\Desktop"(NT 中文版則為 "..\..\桌面"),
因為 "..\.." 代表 Windows 的所在目錄, 而所謂「桌面」其實就是
Windows 底下的 Desktop(桌面)子資料夾, 所以將此一參數寫成 "..\..\Desktop"("..\..\桌面")。</P>
<P> </P>
<P>除了 4 個參數之外, fCreateShellLink 的傳回值表示「是否成功地建立了捷徑」,
如果等於 1, 表示成功, 等於 0, 表示失敗。</P>
<P> </P>
<P>在 VB5 里面使用 fCreateShellLink, 必須撰寫的 API 宣告式如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Declare Function fCreateShellLink Lib "vb5stkit.DLL"
(ByVal folder As String, ByVal ShortCutName As String, ByVal ExePath As
String, ByVal Params As String) As Long</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>但如果您使用的是 VB4 32-bit 版, 則必須將以上的 vb5stkit.dll 改成 stkit432.dll。最後請注意,
vb5stkit.dll(stkit432.dll) 不是 Windows 所提供 API 函數, 呼叫之前, 必須將此一檔案復制到
Windows、Windows 的 System 目錄、或應用程式所在目錄, 但如果您使用 VB 的「安裝精靈」安裝應用程式,
則「安裝精靈」會自動復制此一檔案到 Windows 的 System 目錄。</P>
<P> </P>
<P>為了讓您進一步體驗 fCreateShellLink 函數的使用, 筆者特別準備了如圖-1的表單(放置於范例程式的
Form3):</P>
<P> </P>
<CENTER><P><A HREF="49-1.gif">圖-1 筆者所撰寫的 fCreateShellLink 試驗程式</A></P></CENTER>
<P> </P>
<P>您可以利用此一表單設定不同的參數(參數與表單上各欄位的對應如圖-1之標示),
然後檢測建立捷徑的情況, 檢測時, 筆者必須說明的是, 呼叫建立 fCreateShellLink
之後, 再按下「開始」工具列時, 被建立的捷徑不一定會馬上出現在其中, 這是因為「開始」工具列未即時更新的緣故,
但您可以利用檔案總管功能表的「檢視/重新整理」讓「開始」工具列立即更新。</P>
<P> </P>
<P>附帶說明:中文 Windows 的「啟動」資料夾名稱是 "啟動", 但英文
Windows 卻是 "StartUp", 而不同語言的 Windows 可能又所不同,
「桌面」的情況亦然, 因此如果您要在「啟動」資料夾中或「桌面」上建立捷徑,
必須考慮不同語言的問題。</P>
<P> </P>
<H3><A NAME="Q7"></A>問題7:如何啟動 Windows 預設的執行檔開啟某一文件?
<HR WIDTH="100%"></H3>
<P> </P>
<P>舉例來說, .txt 的文件希望用「記事本」開啟、.doc 的文件用 Word 開啟、.bmp
的文件用「小畫家」開啟…, 就好像利用「檔案總管」開啟文件一樣。</P>
<P> </P>
<P>當我們想在 VB 程式中執行某一個程式時, 最簡單的方法是呼叫 Shell 敘述,
例如「Shell "Notepadc:\test.txt"」, 但 Shell 敘述必須指定好執行檔,
所以并不適用於此一問題。想要像檔案總管一樣開啟文件, 需呼叫 ShellExecute
API 函數, 先舉個簡單的例子, 假設想開啟 c:\Windows 目錄的 general.txt
文件, 則方法如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=-1>Call ShellExecute(Me.hwnd, "open", "c:\Windows\general.txt",
"", "", SW_SHOW)</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>以上敘述筆者省略了參數四及參數五, 其中參數四表示傳遞給執行檔的參數,
但由於此一 ShellExcute 敘述已經是用來開啟文件, 所以此一參數通常設定為
"", 參數五則表示工作目錄, 若設定為 "", 則以文件的所在目錄為工作目錄。此外,
參數六表示文件開啟後顯示的方式, SW_SHOW 表示正常大小, 若設定成 SW_SHOWMINIMIZED,
則以最小化的視窗來顯示, 若設定成 SW_SHOWMAXIMIZED, 則以最大化的視窗來顯示。</P>
<P> </P>
<H3><A NAME="Q8"></A>問題8:如何在啟動某一個程式之後, 等待此一程式結束執行後才繼續執行。
<HR WIDTH="100%"></H3>
<P> </P>
<P>當我們呼叫 Shell 時, 會傳回一個數值, 此一數值稱為 Process Id, 利用此一
Process Id, 我們可以呼叫 OpenProcess API 取得 Process Handle, 然後再利用
Process Handle 呼叫 WaitForSingleObject, 即可等待被 Shell 執行的程式執行完畢後,
才繼續向下執行。程式如下:(以執行 Notepad 程式為例)</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim pId As Long ' 宣告</FONT> Process Id 變數</P>
<P><FONT SIZE=+0>Dim pHndn As Long ' 宣告</FONT> Process Handle 變數</P>
<P><FONT SIZE=+0>pId = Shell("Notepad", vbNormalFocus) ' Shell
傳回</FONT> Process Id</P>
<P><FONT SIZE=+0>pHnd = OpenProcess(SYNCHRONIZE, 0, pId) ' 取得</FONT>
Process Handle</P>
<P><FONT SIZE=+0>If pHnd <> 0 Then </FONT></P>
<P><FONT SIZE=+0>Call WaitForSingleObject(pHnd, INFINITE) ' 無限等待,
直到程式結束</FONT></P>
<P><FONT SIZE=+0>Call CloseHandle(pHnd) </FONT></P>
<P><FONT SIZE=+0>End If</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>至於程式的工作原理, 由於故事很長, 原諒筆者暫時不做進一步的解說。使用此一方法時,
請特別注意, 在等待的時候, 原來的程式是完全不能操作的, 因此筆者建議在呼叫
WaitForSingleObject 之前, 先將原程式的視窗隱藏起來, 直到等待結束時(也就是
WaitForSingleObject 之後), 才重新顯示視窗。</P>
<P> </P>
<H3><A NAME="Q9"></A>問題9:在多行的 TextBox 中, 如何計算行數?
<HR WIDTH="100%"></H3>
<P> </P>
<P>這個問題如果不使用 Windows API, 使用 VB, 則方法如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim S As String, N As Integer, pos As Integer</FONT></P>
<P><FONT SIZE=+0>S = Text1.Text</FONT></P>
<P><FONT SIZE=+0>pos = InStr(S, vbCr + vbLf) ' vbCr + vbLf 為</FONT> TextBox
的斷行字元</P>
<P><FONT SIZE=+0>While pos > 0</FONT></P>
<P><FONT SIZE=+0>N = N + 1</FONT></P>
<P><FONT SIZE=+0>S = Mid(S, pos + 2)</FONT></P>
<P><FONT SIZE=+0>pos = InStr(S, vbCr + vbLf)</FONT></P>
<P><FONT SIZE=+0>Wend</FONT></P>
<P><FONT SIZE=+0>N = N + 1</FONT></P>
<P><FONT SIZE=+0>' N 即等於</FONT> Text1 的行數</P>
<P> </P>
</DIR>
</DIR>
<P>但以上程式遇到 TextBox 行數很多時, 執行效能會比較差一點, 因此可以考慮使用以下的
API 方法:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim N As Long </FONT></P>
<P><FONT SIZE=+0>N = SendMessage(Text1.hwnd, EM_GETLINECOUNT, 0, ByVal
0&)</FONT></P>
<P><FONT SIZE=+0>' N 即等於</FONT> Text1 的行數</P>
<P> </P>
</DIR>
</DIR>
<H3><A NAME="Q10"></A>問題10:如何判斷某一個 Drive 是否為光碟機?
<HR WIDTH="100%"></H3>
<P> </P>
<P>須呼叫 Windows API 的 GetDriveType 函數, 假設我們想判斷 "D:"
碟是否為光碟機, 則方法如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>DriveType = GetDriveType ( "D:\")</FONT></P>
<P><FONT SIZE=+0>If DriveType = DRIVE_CDROM Then ' 表示光碟機</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>請注意 GetDriveType 的參數不可以寫成 "D" 或 "D:",
必須寫成 "D:\"。GetDriveType 除了可以用判斷光碟機之外, 以下是各種傳回值的意義:</P>
<P> </P>
<CENTER><TABLE CELLSPACING=2 WIDTH="399" >
<TR>
<TD WIDTH="53%" VALIGN="TOP" BGCOLOR="#ffff00"><FONT SIZE=+0>傳回值</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP" BGCOLOR="#ffff00"><FONT SIZE=+0>意義</FONT></TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>0</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>無從判斷</FONT></TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>1</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>根目錄不存在</FONT></TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>DRIVE_REMOVABLE(= 2)</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>可移式磁碟, 例如軟碟</FONT></TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>DRIVE_FIXED(= 3)</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>硬碟</FONT></TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>DRIVE_REMOTE(= 4)</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>遠端</FONT>(網路)儲存裝置</TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>DRIVE_CDROM(= 5)</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>光碟機</FONT></TD>
</TR>
<TR>
<TD WIDTH="53%" VALIGN="TOP"><FONT SIZE=+0>DRIVE_RAMDISK(= 6)</FONT></TD>
<TD WIDTH="47%" VALIGN="TOP"><FONT SIZE=+0>RAM Disk</FONT></TD>
</TR>
</TABLE></CENTER>
<P> </P>
<P>如果我們想列舉出所有磁碟機的類型, 則可先在表單上布置一個 DriveListBox(假設它的名稱是
Drive1) 控制元件, 然後再利用以下程式列舉:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim dTypeStr(0 To 6) As String, dType As Long</FONT></P>
<P><FONT SIZE=+0> </FONT></P>
<P><FONT SIZE=+0>dTypeStr(0) = "無從判斷</FONT>" : dTypeStr(1)
= "根目錄不存在"</P>
<P><FONT SIZE=+0>dTypeStr(2) = "軟碟</FONT>" : dTypeStr(3) =
"硬碟"</P>
<P><FONT SIZE=+0>dTypeStr(4) = "遠端</FONT>(網路)儲存裝置"</P>
<P><FONT SIZE=+0>dTypeStr(5) = "光碟機</FONT>" : dTypeStr(6)
= "RAM Disk"</P>
<P><FONT SIZE=+0>For I = 0 To Drive1.ListCount - 1</FONT></P>
<P><FONT SIZE=+0>Drv = Left(Drive1.List(I), 2) & "\"</FONT></P>
<P><FONT SIZE=+0>dType = GetDriveType(Drv)</FONT></P>
<P><FONT SIZE=+0>Debug.Print Drv & " is " & dTypeStr(dType)</FONT></P>
<P><FONT SIZE=+0>Next</FONT></P>
<P> </P>
</DIR>
</DIR>
<H3><A NAME="Q11"></A>問題11:如何讀取檔案的建立時間及存取時間?
<HR WIDTH="100%"></H3>
<P> </P>
<P>如果我們利用 VB 所提供的 FileDateTime 來讀取檔案的時間, 則所得到的是檔案最後一次被修改的時間,
但是當我們利用檔案總管來檢視某一個檔案時, 除了檔案「修改時間」之外, 卻還可以看到檔案的「建立時間」與「存取時間」,
如圖-2。</P>
<P> </P>
<CENTER><P><A HREF="49-2.gif">圖-2 檔案總管所顯示的檔案內容除了「修改時間」之外,
還有「建立時間」與「存取時間」。</A></P></CENTER>
<P> </P>
<P>想要進一步讀取檔案的相關資訊, 必須先呼叫 API 函數的 OpenFile 取得檔案的
Handle, 然後再利用 Handle 呼叫 GetFileInformationByHandle 讀取檔案的相關資訊,
而在讀取的檔案相關資訊中便含有檔案建立、修改、及存取時間, 程式執行過程如下:(假設想讀取的檔案是
"c:\autoexec.bat")</P>
<P> </P>
<UL>
<P><FONT SIZE=+0>Dim FileHandle As Long</FONT></P>
<P><FONT SIZE=+0>Dim FileInfo As BY_HANDLE_FILE_INFORMATION</FONT></P>
<P><FONT SIZE=+0>Dim lpReOpenBuff As OFSTRUCT, ft As SYSTEMTIME</FONT></P>
<P><FONT SIZE=+0>Dim tZone As TIME_ZONE_INFORMATION</FONT></P>
</UL>
<P><FONT SIZE=+0> </FONT></P>
<UL>
<P><FONT SIZE=+0>Dim dtCreate As Date ' 建立時間</FONT></P>
<P><FONT SIZE=+0>Dim dtAccess As Date ' 存取日期</FONT></P>
<P><FONT SIZE=+0>Dim dtWrite As Date ' 修改時間</FONT></P>
<P><FONT SIZE=+0>Dim bias As Long</FONT></P>
<P><FONT SIZE=+0> </FONT></P>
<P><FONT SIZE=+0>' 先取得</FONT> autoexec.bat 的 File Handle</P>
<P><FONT SIZE=+0>FileHandle = OpenFile("c:\autoexec.bat", lpReOpenBuff,
OF_READ)</FONT></P>
<P><FONT SIZE=+0>' 利用</FONT> File Handle 讀取檔案資訊</P>
<P><FONT SIZE=+0>Call GetFileInformationByHandle(FileHandle, FileInfo)</FONT></P>
<P><FONT SIZE=+0>Call CloseHandle(FileHandle)</FONT></P>
</UL>
<P><FONT SIZE=+0> </FONT></P>
<UL>
<P><FONT SIZE=+0>' 讀取</FONT> Time Zone 資訊, 因為上一步驟的檔案時間是「格林威治」時間</P>
<P><FONT SIZE=+0>Call GetTimeZoneInformation(tZone)</FONT></P>
<P><FONT SIZE=+0>bias = tZone.bias ' 時間差, 以「分」為單位</FONT></P>
</UL>
<P><FONT SIZE=+0> </FONT></P>
<UL>
<P><FONT SIZE=+0>Call FileTimeToSystemTime(FileInfo.ftCreationTime, ft)
' 轉換時間資料結構</FONT></P>
<P><FONT SIZE=+0>dtCreate = DateSerial(ft.wYear, ft.wMonth, ft.wDay) +
TimeSerial(ft.wHour, ft.wMinute - bias, ft.wSecond)</FONT></P>
<P><FONT SIZE=+0>Call FileTimeToSystemTime(FileInfo.ftLastAccessTime, ft)</FONT></P>
<P><FONT SIZE=+0>dtAccess = DateSerial(ft.wYear, ft.wMonth, ft.wDay) +
TimeSerial(ft.wHour, ft.wMinute - bias, ft.wSecond)</FONT></P>
<P><FONT SIZE=+0>Call FileTimeToSystemTime(FileInfo.ftLastWriteTime, ft)</FONT></P>
<P><FONT SIZE=+0>dtWrite = DateSerial(ft.wYear, ft.wMonth, ft.wDay) + TimeSerial(ft.wHour,
ft.wMinute - bias, ft.wSecond)</FONT></P>
</UL>
<P> </P>
<P>執行以上程式所得到的 dtCreate、dtWrite、及 dtAccess 變數, 即分別為檔案建立、修改、及存取時間。</P>
<P> </P>
<H3><A NAME="Q12"></A><FONT SIZE=+1>問題</FONT>12:如何以程式控制多行 TextBox
的卷動?
<HR WIDTH="100%"></H3>
<P> </P>
<P>首先請回顧問題-9的程式, 在問題-9 的程式中, 我們利用 SendMessage 傳送
EM_GETLINECOUNT 訊息給 TextBox, 而 TextBox 收到訊息時, 會判斷訊息的編號,
然後計算行數并且回傳, 此一工作模式, 我們可以把傳送給 TextBox 的訊息當成對
TextBox 所下的指令, 而對於控制 TextBox 的卷動來說, 所傳送的訊息(下達的指令)是
EM_LINESCROLL, 程式則如下:</P>
<P> </P>
<UL>
<P><FONT SIZE=+0>Dim N As Long <BR>
Call SendMessage(Text1.hwnd, EM_LINESCROLL, 0&, ByVal N ) ' 下卷</FONT>N行</P>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -