关键在于对WM_ENTERIDLE消息的处理 在菜单状态下移动鼠标会产生WM_ENTERIDLE消息 这时用TempPoint、WindowFromPoint可以取得当前鼠标所指窗体的句柄 再用GetClassName取得类名,与"#32768"(菜单窗体的类名)进行比较 再等待1秒钟,用keybd_event发送VK_ESCAPE取消菜单状态
但是还是有一个的缺点:无法在鼠标不移动的时候自动隐藏 这时需要Timer控件的帮忙
将下列文件粘贴到记事本,并保存为相应文件
AutoHidePopupMenu.vbp ==================================================================== Type=Exe Form=Form1.frm Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\..\..\WINDOWS\SYSTEM\stdole2.tlb#OLE Automation Module=Module1; Module1.bas Startup="Form1" ExeName32="AutoHidePopupMenu.exe" Command32="" Name="AutoHidePopupMenu" HelpContextID="0" CompatibleMode="0" MajorVer=1 MinorVer=0 RevisionVer=0 AutoIncrementVer=0 ServerSupportFiles=0 VersionCompanyName="zyl910" CompilationType=0 OptimizationType=0 FavorPentiumPro(tm)=0 CodeViewDebugInfo=0 NoAliasing=0 BoundsCheck=0 OverflowCheck=0 FlPointCheck=0 FDIVCheck=0 UnroundedFP=0 StartMode=0 Unattended=0 Retained=0 ThreadPerObject=0 MaxNumberOfThreads=1
Form1.frm ==================================================================== VERSION 5.00 Begin VB.Form Form1 BorderStyle = 1 'Fixed Single Caption = "AutoHidePopupMenu" ClientHeight = 3225 ClientLeft = 45 ClientTop = 330 ClientWidth = 4710 LinkTopic = "Form1" MaxButton = 0 'False ScaleHeight = 3225 ScaleWidth = 4710 StartUpPosition = 3 '窗口缺省 Begin VB.Timer Timer1 Interval = 1000 Left = 2580 Top = 360 End Begin VB.Label LblNow AutoSize = -1 'True Caption = "LblNow" Height = 180 Left = 1410 TabIndex = 1 Top = 210 Width = 540 End Begin VB.Label LblClick AutoSize = -1 'True Caption = "点击鼠标右键" BeginProperty Font Name = "宋体" Size = 26.25 Charset = 134 Weight = 400 Underline = 0 'False Italic = 0 'False Strikethrough = 0 'False EndProperty Height = 525 Left = 720 TabIndex = 0 Top = 1200 Width = 3150 End Begin VB.Menu mnuPopup Caption = "Popup" Visible = 0 'False Begin VB.Menu mnuItem1 Caption = "Item&1" End Begin VB.Menu mnuItem2 Caption = "Item&2" End Begin VB.Menu mnuItem3 Caption = "Item&3" End End End Attribute VB_Name = "Form1" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False Option Explicit
Private Sub Form_Load() 'MsgBox ClassName(Me.hWnd) LblNow.Caption = Now Hook Me.hWnd End Sub
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) LblClick_MouseUp Button, Shift, X, Y End Sub
Private Sub Form_Unload(Cancel As Integer) UnHook Me.hWnd End Sub
Private Sub LblClick_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) If Button And vbKeyRButton Then 'ShowMsg = True PopupMenu mnuPopup 'ShowMsg = False End If End Sub
Private Sub Timer1_Timer() LblNow.Caption = Now '这样即使不移动鼠标,菜单也会自动隐藏 If ChkTime Then ChkExit End If End Sub
Module1.bas ==================================================================== Attribute VB_Name = "Module1" Option Explicit
'## API ######################################## '== 硬件与系统函数 ============================= Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Public Declare Function GetTickCount Lib "kernel32" () As Long
Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long) Public Const VK_ESCAPE = &H1B Public Const KEYEVENTF_KEYUP = &H2
Type POINTAPI X As Long Y As Long End Type
'== 控件与消息函数 ============================= 'CallWindowProc 把消息信息传递给指定的窗体过程 'GetClassName 为指定的窗口取得类名 'SetWindowLong 在窗体结构中为指定的窗体设置信息。返回值:Long,指定数据的前一个值。 'WindowFromPoint 返回包含了指定点的窗口的句柄。 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 GetClassNameA Lib "user32" (ByVal hWnd As Long, lpClassName As Any, ByVal nMaxCount 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 Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
'-- SetWindowLong ------------------------------ Public Const GWL_WNDPROC = -4
'=============================================== Public Const WM_ENTERIDLE = &H121
'=============================================== Public MeOldWndProc As Long '旧的窗体消息处理程序地址
Public ShowMsg As Boolean
Public OldIn As Boolean Public OldTime As Long Public ChkTime As Boolean
Public Function ClassName(ByVal hWnd As Long) As String Dim StrData(0 To &H100) As Byte Dim Rc As Long Rc = GetClassNameA(hWnd, StrData(0), &H100) If Rc > 0 Then ClassName = StrConv(LeftB(StrData, Rc), vbUnicode) Else ClassName = vbNullString End If End Function
Public Sub Hook(ByVal hWnd As Long) MeOldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc) End Sub
Public Sub UnHook(ByVal hWnd As Long) Call SetWindowLong(hWnd, GWL_WNDPROC, MeOldWndProc) End Sub
'消息处理 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_ENTERIDLE 'Debug.Print "WM_ENTERIDLE" ChkExit Case Else 'If ShowMsg Then Debug.Print uMsg '下级传递消息 WindowProc = CallWindowProc(MeOldWndProc, hWnd, uMsg, wParam, lParam) End Select End Function
Public Sub ChkExit() Dim TempPoint As POINTAPI Dim TemphWnd As Long Dim TempBool As Boolean GetCursorPos TempPoint TemphWnd = WindowFromPoint(TempPoint.X, TempPoint.Y) If TemphWnd Then TempBool = (ClassName(TemphWnd) = "#32768") Else TempBool = False End If 'Debug.Print TempBool If TempBool <> OldIn Then If TempBool Then OldTime = 0 ChkTime = False Else OldTime = GetTickCount ChkTime = True End If OldIn = TempBool End If If ChkTime Then If GetTickCount - OldTime > 1000 Then '大于1秒就退出 'Debug.Print "Exit" keybd_event VK_ESCAPE, 0, 0, 0 keybd_event VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0 ChkTime = False End If End If End Sub

|