完全采用 VB 自身功能实现对 Windows 消息的拦截!

Phoenix2000 2001-08-03 06:29:16
众所周知,VB 的功能没有 VC++、Delphi 这样的全功能开发平台强大,但她也足以完成我们绝大部分的工作,
只要你开动脑筋,敢想敢干,我们可以让 VB 发挥最大的效能,做出许多令人惊叹的软件。开发高难度软件,
并不只是 VC++ 和 Delphi 的专利!

过去普遍认为 VB 无法自定义拦截 Windows 的消息,只能靠 VB 本身提供的几个有限的事件来编程,这有很
大的局限性。缺少消息捕获,同时又被认为不支持回调函数机制(主要是因为 VB 没有指针,更谈不上函数指
针),这造成了 VB 编程的很大局限性。事实上,VB 可以采用别的办法变相地实现这一机制。从 VB 5.0 开始
就提供了 AddressOf 操作符,利用这个操作符可以获取 VB 自定义函数的地址。有了函数地址就可以采用回调
函数的机制了。当然,VB 仍然无法实现 VB 函数之间的地址传递,她只支持 VB 函数到 DLL 的函数抵制传递。
但是,这已经足够了。下面这个程序,就是采用了这一方法,程序中只有一个主窗体,通过设置属性,使得主窗
体没有边框,没有标题栏,不能改变大小,不能通过标题栏托动。但是通过拦截 Windows 消息可以使得鼠标处在
窗体中的任意位置都可以托动它,就像按住标题栏托动一样。这个程序没有用到任何附加的控件,全部采用 VB 代
码完成。注意,请增加一个公共模块,以便声明一些函数和常数。以下代码在 VB 6.0 中通过。

' ===================================
' 这是公共模块的代码
Attribute VB_Name = "Module1"
Option Explicit

Public Const WM_NCHITTEST = &H84
Public Const VK_LBUTTON = &H1
Public Const HTCAPTION = 2
Public Const HTCLIENT = 1


Public Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
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 SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Public Const GWL_WNDPROC = -4
Global lpPrevWndProc As Long
Global gHW As Long

' 这里是关键,我自定义了一个窗口函数(回调函数),以替代 VB 窗体自己的默认窗口函数。
' 窗口函数是干什么的?它就是负责处理 Windows 发送给它的消息,并加以过滤,筛选出它感兴趣
' 的消息,映射成为事件供我们使用。VB 中每个窗口都有一个默认的窗口函数,我们是看不到的。
' 有很多消息都被 VB 的默认窗口函数过滤掉了。了解 C/C++/Delphi 程序设计的朋友应该知道这些。
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' 我们也进行消息过滤,不过我们指拦截我们感兴趣的消息
' 其他消息我们懒得处理,交给 VB 默认的窗口函数去处理吧。
Select Case uMsg
Case WM_NCHITTEST ' 拦截 WM_NCHITTEST 消息
If GetAsyncKeyState(VK_LBUTTON) < 0 Then ' 是否有鼠标左键在窗体客户区按下?
' 如果是,函数返回值被设置为 HTCAPTION,欺骗 Windows,让它以为鼠标是按在标题栏
' Windows 是通过窗口函数的返回值进行判断处理的
WindowProc = HTCAPTION
Exit Function
Else
' 其他的我们不管,还是规规矩矩的该怎么样就怎么样
WindowProc = HTCLIENT
Exit Function
End If
End Select
' 这里又是关键,因为其他我们不关心的消息我们自己不处理,所以必须由 VB 的默认处理函数处理
' lpPrevWndProc 其实就是一个函数指针,它指向 VB 默认窗口函数
WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)
End Function

' ===================================
' 这是窗体的代码
VERSION 5.00
Begin VB.Form Form1
BorderStyle = 0 'None
ClientHeight = 3195
ClientLeft = 0
ClientTop = 0
ClientWidth = 4680
LinkTopic = "Form1"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 3195
ScaleWidth = 4680
ShowInTaskbar = 0 'False
StartUpPosition = 3 '窗口缺省
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 375
Left = 2160
TabIndex = 0
Top = 720
Width = 1575
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

Private Sub Command1_Click()
Unload Me ' 按下这个按钮就退出了
End Sub

Private Sub Form_Load()
gHW = Me.hwnd ' 保存窗体的句柄
' 下面是关键,完成两个工作:1、将我们自己的全局函数替换为新的窗体回调函数
' 2、保存原来的 VB 默认窗户口函数地址
lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)
End Sub

其实,一切就这么简单。有了这种办法,Windows 中需要回调函数的 API 函数我们都可以调用了,有很多的
功能我们都可以用 VB 来实现了。注意,我们自定义的回调函数只能是模块中定义的全局函数!不能在窗体中
定义。

写出来只是想抛砖引玉,其实有很多功能不需要到处去找控件的。我现在在研究用 VB 6 + DirectX 7/8 写游
戏。游戏速度当然不可能达到 C++ 的程度,但是足够应付一些中小型的游戏题材了,比如 RPG 的。这又有很
多值得写出来的了。虽然不是什么新鲜的话题,但是其中仍不乏许多的技巧和编程思想。希望对此有研究的朋
友能够多多出来交流指点一二。
...全文
155 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
WQ771211 2002-06-30
  • 打赏
  • 举报
回复
不用API,就用ASM去写VXD去吧
heyijpn 2001-08-05
  • 打赏
  • 举报
回复
vb调用API怎么了?!!!
vc其实不也是直接调用API(SDK编程)或者间接调用API(MFC编程)吗?
只不过有些API函数VB不能调用罢了(VB没有指针),但VB。NET据说已经
具有指针的概念了,到那时,呵呵。。。。。
sunsatan 2001-08-04
  • 打赏
  • 举报
回复
又是API VB离开api不能活吗
aikill 2001-08-04
  • 打赏
  • 举报
回复
还有别的方法吗?
bucher 2001-08-04
  • 打赏
  • 举报
回复
是指没有使用第三方控件
windows底层编程不用API能行么?
xyjdn 2001-08-03
  • 打赏
  • 举报
回复
good! ^_^
littleholly 2001-08-03
  • 打赏
  • 举报
回复
我没仔细看,不过好象调用了API,不知道算不算你说的用VB自身功能解决!
交个朋友!
(
|
(
VB拦截Windows Explorer删除进程,内含API HOOK,源代码:倒霉蛋儿,程序有时候也会窗口勾挂失败!   勾住了SHFileOperation等函数,DLL用Delphi写的C会的太少,查了半天才知道原来explorer是用SHFileOperation删除文件,经过测试很稳定,没有出现崩溃的情况,由于只勾住了SHFileOperation函数,所以别的程序要是调用DeleteFile删除文件,拦截不到,要是想拦截DeleteFile自己接着写吧。      mod_Inject.bas类的注释摘录:   Dim MyAddr As Long ‘执行远程线程代码的起始地址。这里等于LoadLibraryA的地址   ‘dll文件路径   MyDllFileLength = LenB(StrConv(MyDllFileName, vbFromUnicode)) + 1    ‘这里把dll文件名从Unicode转换成Ansi,否则英文字母是2个字节。 _   顺便说一下,学过C的应该知道字符串要以/0标志结尾,所以dll文件名长度要加上1个字节存放Chr(0)   ‘得到进程的句柄   在目标进程中申请分配一块空白内存区域。内存的起始地址保存在MyDllFileBuffer中。 _   这块内存区域我们用来存放dll文件路径,并作为参数传递给LoadLibraryA。   在分配出来的内存区域中写入dll路径径。注意第二个参数传递的是MyDllFileBuffer的内容, _   而不是MyDllFileBuffer的内存地址?   If MyReturn = 0 Then Inject = False   MyAddr = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA")   ‘得到LoadLibraryA函数的起始地址。他的参数就是我们刚才写入的dll路径。但是LoadLibraryA本身是不知道参数在哪里的。 _   接下来我们就用CreateRemoteThread函数告诉他参数放在哪里了? If MyAddr = 0 Then Inject = False   MyResult = CreateRemoteThread(ProcessHandle, 0, 0, MyAddr, MyDllFileBuffer, 0, 0)   好了,现在用CreateRemoteThread在目标进程创建一个线程,线程起始地址指向LoadLibraryA, _   参数就是MyDllFileBuffer中保存的dll路径?    If MyResult = 0 Then    Inject = False    Else    Inject = True    End If    ‘接下来你可以使用WaitForSingleObject等待线程执行完毕。 _    并用GetExitCodeThread得到线程的退出代码,用来判断时候正确执行了dll中的代码。    CloseHandle MyResult    CloseHandle ProcessHandle    ‘扫地工作   End Function

741

社区成员

发帖
与我相关
我的任务
社区描述
VB 版八卦、闲侃,联络感情地盘,禁广告帖、作业帖
社区管理员
  • 非技术类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧