?? 49.asp
字號(hào):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>VB 與 Windows API 講座(四) ─ VB 無(wú)解問(wèn)題與 API 的解決方案</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=big5">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<META NAME="GENERATOR" CONTENT="Mozilla/3.01Gold (Win95; I) [Netscape]">
<META NAME="Author" CONTENT="KJ Wang (王國(guó)榮)">
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
<P>VB 與 Windows API 講座(四)</P>
<CENTER><P><B><FONT SIZE=+3>VB 無(wú)解問(wèn)題與 API 的解決方案</FONT></B>
<HR WIDTH="100%"></P></CENTER>
<DIV ALIGN=right><P> 王國(guó)榮</P></DIV>
<P>上一期介紹 Windows 的訊息系統(tǒng), 對(duì)有些讀者來(lái)說(shuō)可能比較艱澀(或者說(shuō)比較無(wú)趣),
這是筆者想極力避免卻又無(wú)法避免的事情, 畢竟訊息的觀念在 Windows API 的程式設(shè)計(jì)中是不可或缺的,
以本期所提出的 15 則問(wèn)題, 就有 2 則的解決方案與訊息有關(guān)系。除了訊息的應(yīng)用之外,
由於本期預(yù)定刊載於 Run!PC 2 月號(hào), 屬於新春期間, 筆者想來(lái)點(diǎn)輕松而又實(shí)用的東西,
所以特別挑選讀者問(wèn)過(guò)而 VB 無(wú)法解決的問(wèn)題, 以 Windows API 來(lái)加以解決,
除了第 4 則與第 15 則之外, 這些解決方案大抵上沒有太艱澀的技術(shù), 您只要跟著筆者所介紹的方法,
就可以將它們應(yīng)用於您的 VB 程式中。</P>
<P><A HREF="#Q1">問(wèn)題1:我只是想將檔案丟到「資源回收筒」, 而不是從硬碟中刪除。
</A></P>
<P><A HREF="#Q2">問(wèn)題2:如何復(fù)制整個(gè)目錄(包含子目錄及其所有檔案)? </A></P>
<P><A HREF="#Q3">問(wèn)題3:如何快速改變檔案的所在目錄? </A></P>
<P><A HREF="#Q4">問(wèn)題4:如何讓 TextBox 在按下滑鼠右鈕時(shí)不顯示快顯功能表?
</A></P>
<P><A HREF="#Q5">問(wèn)題5:如何讀取 Windows 的所在目錄? </A></P>
<P><A HREF="#Q6">問(wèn)題6:如何將程式建立成「啟動(dòng)」資料夾的捷徑? </A></P>
<P><A HREF="#Q7">問(wèn)題7:如何啟動(dòng) Windows 預(yù)設(shè)的執(zhí)行檔開啟某一文件? </A></P>
<P><A HREF="#Q8">問(wèn)題8:如何在啟動(dòng)某一個(gè)程式之後, 等待此一程式結(jié)束執(zhí)行後才繼續(xù)執(zhí)行。
</A></P>
<P><A HREF="#Q9">問(wèn)題9:在多行的 TextBox 中, 如何計(jì)算行數(shù)? </A></P>
<P><A HREF="#Q10">問(wèn)題10:如何判斷某一個(gè) Drive 是否為光碟機(jī)? </A></P>
<P><A HREF="#Q11">問(wèn)題11:如何讀取檔案的建立時(shí)間及存取時(shí)間? </A></P>
<P><A HREF="#Q12">問(wèn)題12:如何以程式控制多行 TextBox 的卷動(dòng)? </A></P>
<P><A HREF="#Q13">問(wèn)題13:如何像一般的繪圖軟體一樣填滿某一區(qū)域的顏色?
</A></P>
<P><A HREF="#Q14">問(wèn)題14:如何讀取磁碟的空間及可用空間? </A></P>
<P><A HREF="#Q15">問(wèn)題15:將表單縮小時(shí), 希望它的圖示顯示在工作列的右下角。</A></P>
<CENTER><P><FONT SIZE=+1>閱讀本文以前:</FONT>
<HR WIDTH="100%"></P></CENTER>
<P>在 Windows API 的呼叫過(guò)程中, 我們必須事先宣告所呼叫的 API 函數(shù)及其相關(guān)常數(shù)、自訂型別,
但這些宣告式通常是又臭又長(zhǎng), 有礙閱讀, 所以筆者把它們集中放在最後的附錄,
當(dāng)然, 為了方便您引用, 這些宣告式亦收錄於筆者的網(wǎng)站, 請(qǐng)自行下載。此外,
本文討論的所有問(wèn)題也都附有范例程式, 一樣包含在下載的檔案中。</P>
<P> </P>
<P><A NAME="Q1"></A><B><FONT SIZE=+1>問(wèn)題1:我只是想將檔案丟到「資源回收筒」,
而不是從硬碟中刪除。</FONT></B>
<HR WIDTH="100%"></P>
<P> </P>
<P>這個(gè)絕對(duì)不能呼叫 VB 所提供的 Kill 敘述, Kill 敘述只會(huì)將檔案從磁碟中刪除,
若要將檔案丟到資源回收筒, 必須呼叫 SHFileOperation API 函數(shù), 假設(shè)我們想將
c:\test.txt 丟到資源回收筒, 則呼叫的敘述如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim SHFileOp As SHFILEOPSTRUCT</FONT></P>
<P><FONT SIZE=+0> </FONT></P>
<P><FONT SIZE=+0>SHFileOp.wFunc = FO_DELETE</FONT></P>
<P><FONT SIZE=+0>SHFileOp.pFrom = "c:\test.txt" + Chr(0)</FONT></P>
<P><FONT SIZE=+0>SHFileOp.fFlags = FOF_ALLOWUNDO + FOF_NOCONFIRMATION</FONT></P>
<P><FONT SIZE=+0>Call SHFileOperation(SHFileOp)</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>在以上敘述中有幾件值得注意的事情:</P>
<P> </P>
<DIR>
<OL>
<LI>FOF_ALLOWUNDO 表示被刪除的檔案將來(lái)可以還原, 此一設(shè)定值是絕對(duì)必要的。</LI>
<LI>FOF_NOCONFIRMATION 表示不顯示交談窗詢問(wèn)使用者「是否將檔案丟到資源回收筒」,
若要詢問(wèn)使用者, 則應(yīng)取消此一設(shè)定值。</LI>
<LI>請(qǐng)注意 "c:\test.txt" 之後必須加上 Chr(0)。</LI>
</OL>
<P> </P>
</DIR>
<P>利用以上方法也可以一次刪除多個(gè)檔案, 此時(shí)只要將多個(gè)檔案名稱串在一起,
并且以 Chr(0) 分隔即可, 假設(shè)М彃pQ刪除 c:\test1.txt、c:\test2.txt、及
c:\test3.txt 等三個(gè)檔案, 則程式如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim SHFileOp As SHFILEOPSTRUCT<BR>
<U>Dim Files As String</U></FONT></P>
<P><BR>
<U><FONT SIZE=+0>Files = "c:\test1.txt" + Chr(0) + "c:\test2.txt"
+ Chr(0) + "c:\test3.txt" + Chr(0)</FONT></U></P>
<P><U>SHFileOp.wFunc = FO_DELETE</U></P>
<P><U>SHFileOp.pFrom = Files</U></P>
<P><U>SHFileOp.fFlags = FOF_ALLOWUNDO + FOF_NOCONFIRMATION</U></P>
<P><U>Call SHFileOperation(SHFileOp)</U></P>
<P> </P>
</DIR>
</DIR>
<H2><A NAME="Q2"></A><B><FONT SIZE=+1>問(wèn)題2:如何復(fù)制整個(gè)目錄(包含子目錄及其所有檔案)?</FONT></B>
<HR WIDTH="100%"></H2>
<P> </P>
<P>如果使用 VB 所提供的功能, 必須使用的敘述及函數(shù)大致上有 Dir、Mkdir、及
FileCopy 等幾個(gè), 而所需撰寫的程式也不算簡(jiǎn)單, 在此就不做介紹, 若呼叫
SHFileOperation API, 則只需短短的幾行, 假設(shè)我們想將 c:\temp 目錄的所有檔案(包含其子目錄)復(fù)制到
c:\temp2 目錄底下, 則程式如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim SHFileOp As SHFILEOPSTRUCT</FONT></P>
<P><FONT SIZE=+0> </FONT></P>
<P><FONT SIZE=+0>SHFileOp.wFunc = <B>FO_COPY</B></FONT></P>
<P><FONT SIZE=+0>SHFileOp.pFrom = "c:\temp\*.*"</FONT></P>
<P><FONT SIZE=+0>SHFileOp.pTo = "c:\temp2\*.*"</FONT></P>
<P><FONT SIZE=+0>SHFileOp.fFlags = FOF_ALLOWUNDO + FOF_NOCONFIRMMKDIR</FONT></P>
<P><FONT SIZE=+0>Call SHFileOperation(SHFileOp)</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>在以上敘述中請(qǐng)注意一件事情:FOF_NOCONFIRMMKDIR 表示不顯示交談窗詢問(wèn)使用者「是否建立目錄」,
如果取消此一設(shè)定值, 而當(dāng) SHFileOp.pTo 所指定的目錄不存在時(shí), 則 Windows
會(huì)詢問(wèn)使用者是否建立目錄。(SHFileOp.pTo = "c:\temp2\*.*" 寫成
SHFileOp.pTo = "c:\temp2 亦可)</P>
<P> </P>
<H3><A NAME="Q3"></A>問(wèn)題3:如何快速改變檔案的所在目錄?
<HR WIDTH="100%"></H3>
<P> </P>
<P>當(dāng)我們想改變某一個(gè)檔案的所在目錄, 若使用 VB 所提供的功能, 必須先執(zhí)行
FileCopy 將檔案復(fù)制到另一個(gè)目錄, 然後才將原檔案刪除, 例如:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>FileCopy Path1 & FileName, Path2 & FileName</FONT></P>
<P><FONT SIZE=+0>Kill Path1 & FileName</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>此一方法對(duì)於比較大的檔案(假設(shè)是 100MB), 十分浪費(fèi)時(shí)間, 若使用 SHFileOperation
API 函數(shù), 則可以不必復(fù)制檔案, 而直接將檔案移至另一個(gè)目錄, 方法如下:(假設(shè)將
c:\test4.txt 移至 c:\temp 目錄)</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim SHFileOp As SHFILEOPSTRUCT</FONT></P>
<P><FONT SIZE=+0> </FONT></P>
<P><FONT SIZE=+0>SHFileOp.wFunc = FO_MOVE</FONT></P>
<P><FONT SIZE=+0>SHFileOp.pFrom = "c:\test4.txt" + Chr(0)</FONT></P>
<P><FONT SIZE=+0>SHFileOp.pTo = "c:\temp"</FONT></P>
<P><FONT SIZE=+0>SHFileOp.fFlags = FOF_ALLOWUNDO + FOF_NOCONFIRMATION</FONT></P>
<P><FONT SIZE=+0>Call SHFileOperation(SHFileOp)</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>在以上敘述中有幾件值得注意的事情:</P>
<P> </P>
<DIR>
<OL>
<LI>SHFileOp.pTo 所指定的參數(shù)必須是一個(gè)已存在的目錄。</LI>
<LI>檔案可以移到另一個(gè)磁碟機(jī), 但它的作用相當(dāng)於復(fù)制檔案, 再把原檔案刪除,
唯有在同一磁碟中移動(dòng)才具有快速移動(dòng)的效果。</LI>
<LI>如果想一次移動(dòng)多個(gè)檔案, 請(qǐng)參閱本文「問(wèn)題 1」的說(shuō)明。</LI>
</OL>
<P> </P>
</DIR>
<H3><A NAME="Q4"></A><B>問(wèn)題4:如何讓 TextBox 在按下滑鼠右鈕時(shí)不顯示快顯功能表?</B>
<HR WIDTH="100%"></H3>
<P> </P>
<P>當(dāng)我們?cè)?TextBox 上面按下滑鼠右鈕時(shí), TextBox 總是會(huì)顯示含有「復(fù)原、剪下、復(fù)制、貼上…」的快顯功能表,
如何叫 TextBox 不要這麼做呢?</P>
<P> </P>
<P>這個(gè)問(wèn)題有點(diǎn)難, 必須利用我們上一期介紹的「視窗程序的插隊(duì)游戲」。為了不讓
TextBox 顯示預(yù)設(shè)的快顯功能表, 我們必須利用插隊(duì)的視窗程序?qū)?WM_RBUTTONDOWN(表示
Right Button Down)訊息吃掉, 在制作的細(xì)節(jié)上, 則包含以下幾點(diǎn):</P>
<P> </P>
<DIR>
<P><B><FONT SIZE=+1>1</FONT></B>. 設(shè)定插隊(duì)的視窗程序:假設(shè) TextBox 的名稱為
Text1, 而我們所撰寫的視窗程序名稱為 WndProc, 則如下:</P>
<P> </P>
<DIR>
<P><FONT SIZE=+0>Dim ret As Long</FONT></P>
<P><FONT SIZE=+0>prevWndProc = GetWindowLong(Text1.hWnd, GWL_WNDPROC)</FONT></P>
<P><FONT SIZE=+0>ret = SetWindowLong(Text1.hWnd, GWL_WNDPROC, AddressOf
WndProc)</FONT></P>
<P> </P>
</DIR>
<P><B><FONT SIZE=+1>2</FONT></B>. 視窗程序的撰寫:</P>
<P> </P>
<DIR>
<P><FONT SIZE=-1>Function WndProc(ByVal hWnd As Long, ByVal Msg As Long,
ByVal wParam As Long, ByVal lParam As Long) As Long</FONT></P>
<P><FONT SIZE=-1>If Msg = WM_RBUTTONDOWN Then</FONT></P>
<P><FONT SIZE=-1>' 吃掉這個(gè)訊息</FONT></P>
<P><FONT SIZE=-1>Else</FONT></P>
<P><FONT SIZE=-1>WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam,
lParam)</FONT></P>
<P><FONT SIZE=-1>End If</FONT></P>
<P><FONT SIZE=-1>End Function</FONT></P>
<P> </P>
</DIR>
<P>當(dāng)以上視窗程序收到 WM_RBUTTONDOWN 訊息時(shí), 不再呼叫 CallWindowProc,
所以 TextBox 便不會(huì)收到「按下滑鼠右鈕」的訊息, 也就不會(huì)顯示預(yù)設(shè)的快顯功能表。</P>
<P> </P>
<P><B><FONT SIZE=+1>3</FONT></B>. 取消插隊(duì)行為:</P>
<P> </P>
<DIR>
<P><FONT SIZE=+0>' prevWndProc 是插隊(duì)時(shí)所儲(chǔ)存下來(lái)的前一個(gè)視窗程序的位址</FONT></P>
<P><FONT SIZE=+0>ret = SetWindowLong(Text1.hWnd, GWL_WNDPROC, prevWndProc)</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>使用以上解決方案請(qǐng)?zhí)貏e注意, 由於我們的程式把 WM_RBUTTONDOWN 訊息吃掉了,
因此當(dāng)使用者按下滑鼠「右鈕」時(shí), TextBox 也不會(huì)發(fā)生 MouseDown 事件, 這將使得
Text1_MouseDown 事件程序中的程式只有在使用者按下滑鼠「左鈕」時(shí)才會(huì)被執(zhí)行,
此時(shí)的解決方案是在 WndProc 視窗程序收到 WM_RBUTTONDOWN 訊息時(shí)呼叫 Text1_MouseDown
事件程序, 如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=-1>Function WndProc(ByVal hWnd As Long, ByVal Msg As Long,
ByVal wParam As Long, ByVal lParam As Long) As Long</FONT></P>
<P><FONT SIZE=-1>If Msg = WM_RBUTTONDOWN Then</FONT></P>
<P><FONT SIZE=-1>Call Text1_MouseDown( 參數(shù)…</FONT> )</P>
<P><FONT SIZE=-1>Else</FONT></P>
<P><FONT SIZE=-1>WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam,
lParam)</FONT></P>
<P><FONT SIZE=-1>End If</FONT></P>
<P><FONT SIZE=-1>End Function</FONT></P>
<P> </P>
</DIR>
</DIR>
<H3><A NAME="Q5"></A>問(wèn)題5:如何讀取 Windows 的所在目錄?
<HR WIDTH="100%"></H3>
<P><FONT SIZE=+1>雖然</FONT> Windows 安裝的預(yù)設(shè)目錄是 "c:\Windows",
但由於使用者可以自由設(shè)定 Windows 安裝的目錄, 所以不能假設(shè) "c:\Windows"
是 Windows 的所在目錄, 要讀取 Windows 的所在目錄, 需利用 GetWindowsDirectory
API 函數(shù), 以下是呼叫的例子:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>Dim S As String * 80, Length As Long</FONT></P>
<P><FONT SIZE=+0>Dim WinPath As String</FONT></P>
<P><FONT SIZE=+0> </FONT></P>
<P><FONT SIZE=+0>Length = GetWindowsDirectory(S, Len(S)) </FONT></P>
<P><FONT SIZE=+0>WinPath = Left(S, Length)</FONT></P>
<P> </P>
</DIR>
</DIR>
<P>則執(zhí)行之後 WinPath 將等於 Windows 的所在目錄。</P>
<P> </P>
<H3><A NAME="Q6"></A>問(wèn)題6:如何將程式建立成「啟動(dòng)」資料夾的捷徑?
<HR WIDTH="100%"></H3>
<P> </P>
<P>想要建立捷徑, 使用 Windows API 比較麻煩, 因此筆者想藉助一個(gè)附屬於
VB 的 DLL 檔案 ─ vb5stkit.dll(如果是 VB 4.0 32-bit 版, 則為 stkit432.dll),
在安裝有 VB 的機(jī)器里面, 此一檔案會(huì)出現(xiàn)在 Windows 的 System 目錄底下,
此外, 在 VB 的 setupkit\kitfil32 目錄底下也有這一個(gè)檔案。在 vb5stkit.dll
里面有一個(gè)叫做 fCreateShellLink 的函數(shù), 可用來(lái)建立「捷徑」(ShortCut),
此一函數(shù)含有 4 個(gè)參數(shù), 意義如下:</P>
<P> </P>
<DIR>
<P><B><FONT SIZE=+1>1</FONT></B>. folder:資料夾, 此一參數(shù)雖然稱為資料夾,
但與磁碟的資料夾(目錄)略有出入, 它的根目錄("\")表示「開始」工具列底下的「程式集」資料夾,
而 ".." 表示「開始」工具列, "..\.." 表示 Windows 的所在目錄。</P>
<P><B><FONT SIZE=+1>2</FONT></B>. ShortCutName:捷徑名稱。</P>
<P><B><FONT SIZE=+1>3</FONT></B>. ExePath:程式或檔案的完整路徑。</P>
<P><B><FONT SIZE=+1>4</FONT></B>. Params:參數(shù)三 ExePath 的程式參數(shù)。</P>
<P> </P>
</DIR>
<P>假設(shè)我們想把 "c:\Windows\Notepad.exe" 執(zhí)行檔設(shè)定成「啟動(dòng)」資料夾名稱為
"記事本" 的捷徑, 則呼叫的方式如下:</P>
<P> </P>
<DIR>
<DIR>
<P><FONT SIZE=+0>ret = fCreateShellLink("\啟動(dòng)</FONT>", "記事本",
"c:\Windows\Notepad.exe", "")</P>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -