当前位置:编程学习 > VB >>

粘贴事件 可用什么代码表示

粘贴事件 可用什么代码表示 --------------------编程问答-------------------- 粘贴是Ctrl+V,可以考虑在KeyPress中判断一下 --------------------编程问答-------------------- hooK:WM_DRAWCLIPBOARD


在以前的一些杂志里出现过用VC或Delphi监视剪贴板的例子,却没有见过用VB 写的。由于在VC、Delphi 设计环境中对这些消息的处理方式与VB 不同,因此在VB 程序里监视剪贴板似乎成了一个问题。大概现在还有很多人靠使用timer控件不断地读取剪贴板内容并加以对比来判断剪贴板内容的变化吧?这会引发很多问题,比如让程序在NT中运行,最小化后先去做其他事,过一段时间切换到该程序时,该程序就被Dr.waston 抓住,一定得退出才行。如果要做类似ClipMate 的软件,若剪贴板中的文本改变后和改变前内容相同的话,程序会不知道究竟剪贴板内容有没有改变过..种种原因,我们必须使VB程序也能真正监视剪贴板。下面我们就一起来完成这个任务吧。

这里假设你不懂得如何在VB 中使用SubClass(子类),但对一般的API 懂得如何调用(如果不懂,请找找相关资料,不难的),对每一步我都会讲解。如果你已经对这东西很熟悉了,那就把代码抄去自己看吧。Windows 在每发生一件与系统有关的事件时,都会产生一个Windows 消息,剪贴板内容被改变也一样。我们或许会这么想,如果Windows能把剪贴板被改变的消息通知我们的程序的话,那就太好了。事实上,我们就是要让Windows 知道,我们要它把消息告诉我们。这就要通过SubClass。

首先我们的程序需要把事件告诉Windows,然后让Windows 知道,当消息发出的时候在哪里可以通知到我们的程序。看下面的声明:
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _(ByVal hwnd As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long SetWindowLong(这个函数有许多用法,这里我只讲SubClass 的用法)可以告诉Windows,当有消息发出时通知我们以及可以在哪里通知到。使用这个API,除了要把我们的程序的句柄(等于程序的标识,以便让Windows知道向谁发消息)传给它外,还需要提供一个函数的地址,这个函数有一定的格式,Windows消息的发出就是对这个函数的参数进行改变,我们根据特定参数的值来判断这个消息是什么消息。函数的格式是:Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByValwParam As Long, ByVal lParam As Long) As Long每当有消息发生时,我们的程序就是在这个函数里得知的。下面我们一边做个示例程序一边讲解吧。新建一个工程,再添加一个标准模块,模块里除了加上SetWindowLong 的声明外,再加上:
Public lpPrevWndProc As Long
Public lClipboardChain As Long
Public Const GWL_WNDPROC = -4
Public Const WM_DRAWCLIPBOARD = &H308
Public Const WM_CHANGECBCHAIN = &H30D
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _
(ByVal lpPrevWndFunc As Long, _
ByVal hwnd As Long, _
ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Public Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
Public Declare Function SetClipboardViewer Lib "user32" _
(ByVal hwnd As Long) As Long
Public Declare Function ChangeClipboardChain Lib "user32" _
(ByVal hwnd As Long, ByVal hWndNext As Long) As Long
接下来需要把函数告诉Windows,在模块中继续加入:
Public Sub Hook(hwnd As Long)
' 在这里使用了 AddressOf,得到的是WindowProc 函数的地址
'GWL_WNDPROC告诉SetWindowLong 函数将使用SubClass
lpPrevWndProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf
WindowProc)
'SubClass需要保存调用时返回的指针以便做其他处理
lClipboardChain = SetClipboardViewer(hwnd) '<1>
End Sub
Public Sub UnHook(hwnd As Long)
SetWindowLong hwnd, GWL_WNDPROC, lpPrevWndProc
ChangeClipboardChain hwnd, lClipboardChain '<2>
End Sub
把WindowProc 函数完成:
Public Function WindowProc(ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Select Case uMsg
Case WM_DRAWCLIPBOARD
SendMessage lClipboardChain, WM_DRAWCLIPBOARD, 0, 0 '<3>
Debug.Print "Clipboard changed"
'剪贴板改变时你要做什么处理就是写在这里了
Case WM_CHANGECBCHAIN
If lClipboardChain = wParam Then '<4>
lClipboardChain = lParam
End If
Case Else
WindowProc = CallWindowProc(lpPrevWndProc, hwnd, uMsg, wParam,
lParam) '<5>
End Select
End Function
接着给什么都还没有的Form1添加下面的东东:
Private Sub Form_Load()
Hook Me.hwnd
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
UnHook Me.hwnd
End Sub
现在程序基本完成,但是我们需要知道这个程序为什么要这么写。先看WindowProc函数吧,里面使用了一个Select Case结构,因为uMsg参数根据不同消息传回不同值。具体消息的值我们可以从API浏览器中得到。对于本程序,我们需要响应两个:WM_DRAWCLIPBOARD(内容改变)和WM_CHANGECBCHAIN(有程序退出剪贴板)。有了不同消息,我们就可以根据需要调用不同的其他子程序做一些事了。接下来看<1>,SetClipboardViewer函数通知Windows有一个程序要得到剪贴板消息的通知,称登录剪贴板。在Windows的剪贴板规则中,每一个登录剪贴板的程序的句柄都会被按顺序记下,以使剪贴板可以区分这些程序。而这个函数的返回值就是上一个登录剪贴板的程序的句柄。不懂为何要返回上一个程序的句柄?接着看。

在<3>那一行,有一个SendMessage对吧?它向上一个程序发送了跟自己接收到的相同的消息(最后两个参数是0,0是因为这两个参数在这个消息中是没用的,用什么值都无所谓)。奇怪么?其实当Windows剪贴板变化时,它并不是通知所有登录剪贴板的程序,而是只通知最后登录剪贴板的程序,这样会出现什么事大家应该就知道了。所以为了我们的程序不太霸道,我们要通知前一个程序,让它也接收到消息。看<4>,为什么我们连其他程序退出剪贴板都要知道呢?假设有这样的情况,在我们的程序启动之前至少已经有一个程序登录剪贴板了,当我们的程序启动后,在我们之前的另一程序退出了剪贴板并结束运行,刚好这时剪贴板发来了通知,所以我们向前一个程序发消息,可是这个程序已经不在了,消息到此中断,那个程序之前的其他程序就什么也收不到。所以在那个程序退出时,我们要得到它的前一个程序的句柄,下次发送消息就可以发送到而不使剪贴板消息中断。

再看<5>,我们使用了Select Case来判断要处理的消息,而Windows把所有适合被我们接收的消息都发到这里来了,因此有很多消息实际上没有被处理。而发送到我们的程序来的所有消息由于有了接收者,所以Windows就不再做进一步处理。这样的结果是,所有本来应该由Windows来处理的(如刷新、改变窗体大小等等)没人理,所以对于不需要自己处理的消息,我们把它重新交给Windows处理,让程序可以和平常一样正常工作。这里的做法几乎是固定的:把前面Hook子程序中SetWindowLong的返回值lpPrevWndProc用CallWindowProc传给Windows,返回值由WindowProc函数来接收。看看我们剩下什么没解决?对了,还有<2>。这里是这个程序的最终部分:Unload之前(当然也可以放在Unload里,只不过为安全起见)。当我们的程序退出时,Windows不会知道我们有没有用过SubClass,所以通知它一下是有必要的(不然非法操作和死机免不了)。另外,既然登录了剪贴板,就要在退出时也告诉它一声,好让其他程序也发现我们退出而把下一次消息发给其他程序。当然你可以不这么做,结果是造成其他运行中的程序从你退出后就收不到剪贴板消息了。不过这么不负责任的事相信你不会做吧?做了SubClass的程序绝对不能让它在移除SubClass之前结束程序。有许多人喜欢用End来结束程序运行,但End不会产生任何QueryUnload或Unload事件,建议不用。除非在你的程序的所有窗体都在End之前解除了SubClass。也不要使用VB IDE里的停止按钮来结束,结果和End是一样的。好!现在这个程序你应该能够完全理解了吧?那么接下来,把它做成一个比ClipMate更优秀的软件的任务就交给你好了!

--------------------编程问答-------------------- subclass需要捕获粘贴事件的控件。

拦截 wm_paste 消息。 --------------------编程问答-------------------- Clipboard.GetData() --------------------编程问答-------------------- 我还是主张拦截 wm_paste 消息,这样简单,操作方便. --------------------编程问答--------------------
补充:VB ,  多媒体
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,