⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 49.asp

📁 ASPWeb数据库范例总览
💻 ASP
📖 第 1 页 / 共 4 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
   <TITLE>VB 与 Windows API 讲座(四) ─ VB 无解问题与 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 (王国荣)">
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">

<P>VB 与 Windows API 讲座(四)</P>

<CENTER><P><B><FONT SIZE=+3>VB 无解问题与 API 的解决方案</FONT></B> 
<HR WIDTH="100%"></P></CENTER>

<DIV ALIGN=right><P> 王国荣</P></DIV>

<P>上一期介绍 Windows 的讯息系统, 对有些读者来说可能比较艰涩(或者说比较无趣),
这是笔者想极力避免却又无法避免的事情, 毕竟讯息的观念在 Windows API 的程式设计中是不可或缺的,
以本期所提出的 15 则问题, 就有 2 则的解决方案与讯息有关系。除了讯息的应用之外,
由於本期预定刊载於 Run!PC 2 月号, 属於新春期间, 笔者想来点轻松而又实用的东西,
所以特别挑选读者问过而 VB 无法解决的问题, 以 Windows API 来加以解决,
除了第 4 则与第 15 则之外, 这些解决方案大抵上没有太艰涩的技术, 您只要跟著笔者所介绍的方法,
就可以将它们应用於您的 VB 程式中。</P>

<P><A HREF="#Q1">问题1:我只是想将档案丢到「资源回收筒」, 而不是从硬碟中删除。
</A></P>

<P><A HREF="#Q2">问题2:如何复制整个目录(包含子目录及其所有档案)? </A></P>

<P><A HREF="#Q3">问题3:如何快速改变档案的所在目录? </A></P>

<P><A HREF="#Q4">问题4:如何让 TextBox 在按下滑鼠右钮时不显示快显功能表?
</A></P>

<P><A HREF="#Q5">问题5:如何读取 Windows 的所在目录? </A></P>

<P><A HREF="#Q6">问题6:如何将程式建立成「启动」资料夹的捷径? </A></P>

<P><A HREF="#Q7">问题7:如何启动 Windows 预设的执行档开启某一文件? </A></P>

<P><A HREF="#Q8">问题8:如何在启动某一个程式之後, 等待此一程式结束执行後才继续执行。
</A></P>

<P><A HREF="#Q9">问题9:在多行的 TextBox 中, 如何计算行数? </A></P>

<P><A HREF="#Q10">问题10:如何判断某一个 Drive 是否为光碟机? </A></P>

<P><A HREF="#Q11">问题11:如何读取档案的建立时间及存取时间? </A></P>

<P><A HREF="#Q12">问题12:如何以程式控制多行 TextBox 的卷动? </A></P>

<P><A HREF="#Q13">问题13:如何像一般的绘图软体一样填满某一区域的颜色?
</A></P>

<P><A HREF="#Q14">问题14:如何读取磁碟的空间及可用空间? </A></P>

<P><A HREF="#Q15">问题15:将表单缩小时, 希望它的图示显示在工作列的右下角。</A></P>

<CENTER><P><FONT SIZE=+1>阅读本文以前:</FONT> 
<HR WIDTH="100%"></P></CENTER>

<P>在 Windows API 的呼叫过程中, 我们必须事先宣告所呼叫的 API 函数及其相关常数、自订型别,
但这些宣告式通常是又臭又长, 有碍阅读, 所以笔者把它们集中放在最後的附录,
当然, 为了方便您引用, 这些宣告式亦收录於笔者的网站, 请自行下载。此外,
本文讨论的所有问题也都附有范例程式, 一样包含在下载的档案中。</P>

<P> </P>

<P><A NAME="Q1"></A><B><FONT SIZE=+1>问题1:我只是想将档案丢到「资源回收筒」,
而不是从硬碟中删除。</FONT></B> 
<HR WIDTH="100%"></P>

<P> </P>

<P>这个绝对不能呼叫 VB 所提供的 Kill 叙述, Kill 叙述只會将档案从磁碟中删除,
若要将档案丢到资源回收筒, 必须呼叫 SHFileOperation API 函数, 假设我们想将
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 = &quot;c:\test.txt&quot; + 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 表示被删除的档案将来可以还原, 此一设定值是绝对必要的。</LI>

<LI>FOF_NOCONFIRMATION 表示不显示交谈窗询问使用者「是否将档案丢到资源回收筒」,
若要询问使用者, 则应取消此一设定值。</LI>

<LI>请注意 &quot;c:\test.txt&quot; 之後必须加上 Chr(0)。</LI>
</OL>

<P> </P>
</DIR>

<P>利用以上方法也可以一次删除多个档案, 此时只要将多个档案名称串在一起,
并且以 Chr(0) 分隔即可, 假设М彃pQ删除 c:\test1.txt、c:\test2.txt、及
c:\test3.txt 等三个档案, 则程式如下:</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 = &quot;c:\test1.txt&quot; + Chr(0) + &quot;c:\test2.txt&quot;
+ Chr(0) + &quot;c:\test3.txt&quot; + 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>问题2:如何复制整个目录(包含子目录及其所有档案)?</FONT></B>

<HR WIDTH="100%"></H2>

<P> </P>

<P>如果使用 VB 所提供的功能, 必须使用的叙述及函数大致上有 Dir、Mkdir、及
FileCopy 等几个, 而所需撰写的程式也不算简单, 在此就不做介绍, 若呼叫
SHFileOperation API, 则只需短短的几行, 假设我们想将 c:\temp 目录的所有档案(包含其子目录)复制到
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 = &quot;c:\temp\*.*&quot;</FONT></P>

<P><FONT SIZE=+0>SHFileOp.pTo = &quot;c:\temp2\*.*&quot;</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>在以上叙述中请注意一件事情:FOF_NOCONFIRMMKDIR 表示不显示交谈窗询问使用者「是否建立目录」,
如果取消此一设定值, 而当 SHFileOp.pTo 所指定的目录不存在时, 则 Windows
會询问使用者是否建立目录。(SHFileOp.pTo = &quot;c:\temp2\*.*&quot; 写成
SHFileOp.pTo = &quot;c:\temp2 亦可)</P>

<P> </P>

<H3><A NAME="Q3"></A>问题3:如何快速改变档案的所在目录? 
<HR WIDTH="100%"></H3>

<P> </P>

<P>当我们想改变某一个档案的所在目录, 若使用 VB 所提供的功能, 必须先执行
FileCopy 将档案复制到另一个目录, 然後才将原档案删除, 例如:</P>

<P> </P>

<DIR>
<DIR>
<P><FONT SIZE=+0>FileCopy Path1 &amp; FileName, Path2 &amp; FileName</FONT></P>

<P><FONT SIZE=+0>Kill Path1 &amp; FileName</FONT></P>

<P> </P>
</DIR>
</DIR>

<P>此一方法对於比较大的档案(假设是 100MB), 十分浪费时间, 若使用 SHFileOperation
API 函数, 则可以不必复制档案, 而直接将档案移至另一个目录, 方法如下:(假设将
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 = &quot;c:\test4.txt&quot; + Chr(0)</FONT></P>

<P><FONT SIZE=+0>SHFileOp.pTo = &quot;c:\temp&quot;</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 所指定的参数必须是一个已存在的目录。</LI>

<LI>档案可以移到另一个磁碟机, 但它的作用相当於复制档案, 再把原档案删除,
唯有在同一磁碟中移动才具有快速移动的效果。</LI>

<LI>如果想一次移动多个档案, 请参阅本文「问题 1」的说明。</LI>
</OL>

<P> </P>
</DIR>

<H3><A NAME="Q4"></A><B>问题4:如何让 TextBox 在按下滑鼠右钮时不显示快显功能表?</B>

<HR WIDTH="100%"></H3>

<P> </P>

<P>当我们在 TextBox 上面按下滑鼠右钮时, TextBox 总是會显示含有「复原、剪下、复制、贴上…」的快显功能表,
如何叫 TextBox 不要这麼做呢?</P>

<P> </P>

<P>这个问题有点难, 必须利用我们上一期介绍的「视窗程序的插队游戏」。为了不让
TextBox 显示预设的快显功能表, 我们必须利用插队的视窗程序将 WM_RBUTTONDOWN(表示
Right Button Down)讯息吃掉, 在制作的细节上, 则包含以下几点:</P>

<P> </P>

<DIR>
<P><B><FONT SIZE=+1>1</FONT></B>. 设定插队的视窗程序:假设 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>' 吃掉这个讯息</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>当以上视窗程序收到 WM_RBUTTONDOWN 讯息时, 不再呼叫 CallWindowProc,
所以 TextBox 便不會收到「按下滑鼠右钮」的讯息, 也就不會显示预设的快显功能表。</P>

<P> </P>

<P><B><FONT SIZE=+1>3</FONT></B>. 取消插队行为:</P>

<P> </P>

<DIR>
<P><FONT SIZE=+0>' prevWndProc 是插队时所储存下来的前一个视窗程序的位址</FONT></P>

<P><FONT SIZE=+0>ret = SetWindowLong(Text1.hWnd, GWL_WNDPROC, prevWndProc)</FONT></P>

<P> </P>
</DIR>
</DIR>

<P>使用以上解决方案请特别注意, 由於我们的程式把 WM_RBUTTONDOWN 讯息吃掉了,
因此当使用者按下滑鼠「右钮」时, TextBox 也不會发生 MouseDown 事件, 这将使得
Text1_MouseDown 事件程序中的程式只有在使用者按下滑鼠「左钮」时才會被执行,
此时的解决方案是在 WndProc 视窗程序收到 WM_RBUTTONDOWN 讯息时呼叫 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( 参数…</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>问题5:如何读取 Windows 的所在目录? 
<HR WIDTH="100%"></H3>

<P><FONT SIZE=+1>虽然</FONT> Windows 安装的预设目录是 &quot;c:\Windows&quot;,
但由於使用者可以自由设定 Windows 安装的目录, 所以不能假设 &quot;c:\Windows&quot;
是 Windows 的所在目录, 要读取 Windows 的所在目录, 需利用 GetWindowsDirectory
API 函数, 以下是呼叫的例子:</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>则执行之後 WinPath 将等於 Windows 的所在目录。</P>

<P> </P>

<H3><A NAME="Q6"></A>问题6:如何将程式建立成「启动」资料夹的捷径? 
<HR WIDTH="100%"></H3>

<P> </P>

<P>想要建立捷径, 使用 Windows API 比较麻烦, 因此笔者想藉助一个附属於
VB 的 DLL 档案 ─ vb5stkit.dll(如果是 VB 4.0 32-bit 版, 则为 stkit432.dll),
在安装有 VB 的机器里面, 此一档案會出现在 Windows 的 System 目录底下,
此外, 在 VB 的 setupkit\kitfil32 目录底下也有这一个档案。在 vb5stkit.dll
里面有一个叫做 fCreateShellLink 的函数, 可用来建立「捷径」(ShortCut),
此一函数含有 4 个参数, 意义如下:</P>

<P> </P>

<DIR>
<P><B><FONT SIZE=+1>1</FONT></B>. folder:资料夹, 此一参数虽然称为资料夹,
但与磁碟的资料夹(目录)略有出入, 它的根目录(&quot;\&quot;)表示「开始」工具列底下的「程式集」资料夹,
而 &quot;..&quot; 表示「开始」工具列, &quot;..\..&quot; 表示 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:参数三 ExePath 的程式参数。</P>

<P> </P>
</DIR>

<P>假设我们想把 &quot;c:\Windows\Notepad.exe&quot; 执行档设定成「启动」资料夹名称为
&quot;记事本&quot; 的捷径, 则呼叫的方式如下:</P>

<P> </P>

<DIR>
<DIR>
<P><FONT SIZE=+0>ret = fCreateShellLink(&quot;\启动</FONT>&quot;, &quot;记事本&quot;,
&quot;c:\Windows\Notepad.exe&quot;, &quot;&quot;)</P>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -