有一应用程序正运行,其一窗口上有一控件:Combo1(就是下拉框),我知道其句柄。
我编写的程序中需要对该Combo1进行监控,如果其Text(就是下拉框显示值)发生变化,我就要
执行一段代码。
   请问如何捕捉Combo1中值发生变化的这个事件?
   我现在用的是最最土的方法:使用Timer,每隔10ms就通过Combo1的句柄来读取其值,然后判断是否变化了,这显然是不好的方法。
   希望高手指点一二。

解决方案 »

  1.   

    Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" _
    (ByVal idHook As Long, _
    ByVal lpfn As Long, _
    ByVal hmod As Long, _
    ByVal dwThreadId As Long) As Long'idHook代表是何种Hook,有以下几种
    Public Const WH_CALLWNDPROC = 4
    Public Const WH_CALLWNDPROCRET = 12
    Public Const WH_CBT = 5
    Public Const WH_DEBUG = 9
    Public Const WH_FOREGROUNDIDLE = 11
    Public Const WH_GETMESSAGE = 3
    Public Const WH_HARDWARE = 8
    Public Const WH_JOURNALPLAYBACK = 1
    Public Const WH_JOURNALRECORD = 0
    Public Const WH_KEYBOARD = 2
    Public Const WH_MOUSE = 7
    Public Const WH_MSGFILTER = (-1)
    Public Const WH_SHELL = 10
    Public Const WH_SYSMSGFILTER = 6
    Public Const WM_LBUTTONDOWN = &H201
    Public Const CB_SETCURSEL = &H14E
    Public Declare Function CallNextHookEx Lib "user32" _
    (ByVal hHook As Long, _
    ByVal ncode As Long, _
    ByVal wParam As Long, _
    lParam As Any) As Long
    Public Declare Function UnhookWindowsHookEx Lib "user32" _
    (ByVal hHook As Long) As Long
    Public hHook As LongPublic Sub UnHookKBD()
    If hHook <> 0 Then
    UnhookWindowsHookEx hHook
    hHook = 0
    End If
    End SubPublic Function EnableKBDHook()
    If hHook <> 0 Then
    Exit Function
    End If
    hHook = SetWindowsHookEx(WH_MOUSE, AddressOf MyKBHFunc, App.hInstance, App.ThreadID)
    End FunctionPublic Function MyKBHFunc(ByVal iCode As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long
    MyKBHFunc = 0 '表示要处理这个讯息
    If wParam = 513 Then
    Form1.Text1 = "左键点击"
    End If
    If wParam = 516 Then
    Form1.Text1 = "右键点击"
    End If
    Form1.Text2 = lParamEnd Function
      

  2.   

    Private Sub Form_Load()
    Call EnableKBDHook
    End Sub
    Private Sub Form_Unload(Cancel As Integer)
    Call UnHookKBD
    End Sub鼠标钩子的一个例子可以分辨鼠标左右键点击的例子
    你需要知道combo被修改的时候触发的windows消息即可同样的写一个出来
      

  3.   

    Option Explicit
    Private hwnd5 As LongPrivate Sub Form_Load()
    Dim ret As Long
    '取得Combo内EditBox的hwnd
    hwnd5 = FindEditInCombo(Combo1)
    '记录原本的Window Procedure的位址
    preWinProc = GetWindowLong(hwnd5, GWL_WNDPROC)
    '设定EditBox的window Procedure到wndproc
    ret = SetWindowLong(hwnd5, GWL_WNDPROC, AddressOf wndproc)
    End SubPrivate Sub Form_Unload(Cancel As Integer)
    Dim ret As Long
    '取消Message的截取,而使之又只送往原来的Window Procedure
    ret = SetWindowLong(hwnd5, GWL_WNDPROC, preWinProc)
    End Sub'以下程式在.bas module
    Option ExplicitDeclare Function EnumChildWindows Lib "user32" _
    (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, _
    ByVal lParam As Long) As Long
    Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
    (ByVal hwnd As Long, ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long
    Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
    (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
    (ByVal hwnd As Long, ByVal nIndex As Long) As Long
    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 LongPublic Const GWL_WNDPROC = (-4)
    Public Const WM_MOUSEMOVE = &H200
    Public Const WM_RBUTTONDOWN = &H204
    Public preWinProc As Long
    Private hEditWnd As LongPublic Function wndproc(ByVal hwnd As Long, ByVal Msg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long
    '以下会截取mouse Rbutton Down
    If Msg = WM_RBUTTONDOWN Then
    Debug.Print "Combol Mouse RButton Down "
    Else
    '将之送往原来的Window Procedure
    wndproc = CallWindowProc(preWinProc, hwnd, Msg, wParam, lParam)
    End If
    End Function
    Public Function FindEditInCombo(ctl As ComboBox) As Long
    Call EnumChildWindows(ctl.hwnd, AddressOf EnumFunc, 0)
    FindEditInCombo = hEditWnd
    End FunctionPublic Function EnumFunc(ByVal hwnd As Long, ByVal lParam As Long) As Long
    Dim ClsName As String
    Dim len5 As Long
    If hwnd = 0 Then
    EnumFunc = 0
    Else
    ClsName = String(255, 0)
    len5 = GetClassName(hwnd, ClsName, 256)
    ClsName = Left(ClsName, len5)
    If ClsName = "Edit" Then
    hEditWnd = hwnd
    EnumFunc = 0
    Else
    EnumFunc = 1
    End If
    End If
    End Function 拦截ComboBox的mouse右键  的代码
      

  4.   

    楼上的真是高手。我明白了原理,无非就是拦截消息。
    楼上的代码测试OK,通理我可以拦截ComboBox的各种消息。
    楼上的代码在测试本程序中的ComboxBox很正常。
    但是如果我要拦截其它应用程序(不知道何种语言,通过Spy++可以确定上面有一个ComboBox)的ComboBox的消息就不行了,SetWindowLong和GetWindowLong全部失败,返回0。
    具体如下:
    FindEditInCombo稍作修改:
    Public Function FindEditInCombo2(ctl As long) As Long
    Call EnumChildWindows(ctl, AddressOf EnumFunc, 0)
    FindEditInCombo = hEditWnd
    End Functionhwnd5 = FindEditInCombo(Combo1)改为 hwnd5 = FindEditInCombo2(handle)
    其中handle是通过Spy++查到的外部应用程序中ComboBox的句柄。为何不行?是不是不能拦截外部消息?听说要写在DLL里面什么的,请楼上的高手再指点一下,小弟万分感激。
      

  5.   

    漏打了一个字:
    Public Function FindEditInCombo2(ctl As long) As Long
    Call EnumChildWindows(ctl, AddressOf EnumFunc, 0)
    FindEditInCombo2 = hEditWnd
    End Function
      

  6.   

    两个办法一是hook住鼠标Option Explicit
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
    Private Declare Function PtInRect Lib "user32" (lpRect As RECT, ByVal x As Long, ByVal y As Long) As Long
    Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
    Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
    Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lparam As Any) As Long
    Private Const HC_ACTION = 0
    Private Const WH_MOUSE_LL As Long = 14
    Private Const WM_MOUSEMOVE = &H200
    Private Const WM_MOUSELEAVE = &H2A3
    Public Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
    End TypePrivate Type POINTAPI
       x As Long
       y As Long
    End Type
    Private Type MSLLHOOKSTRUCT
        pt As POINTAPI
        mouseData As Long
        Flags As Long
        time As Long
        dwExtraInfo As Long
    End Type
    Public hHook As Long
    Public hRT As RECT
    Public Sub EnableHook(hRect As RECT)
        hRT = hRect
        If hHook = 0 Then
           hHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf HookProc, App.hInstance, 0)
        End If
    End Sub
    Public Sub FreeHook()
    If hHook <> 0 Then
       Call UnhookWindowsHookEx(hHook)
       hHook = 0
    End If
    End SubPublic Function HookProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lparam As Long) As Long
        Dim typMHS As MSLLHOOKSTRUCT, pt As POINTAPI
        If nCode < 0 Then
           HookProc = CallNextHookEx(hHook, nCode, wParam, lparam)
           Exit Function
        End If
        
        
        If wParam = 513 Then
         Call CopyMemory(typMHS, ByVal lparam, LenB(typMHS))
            pt = typMHS.pt
            If PtInRect(hRT, pt.x, pt.y) <> 0 Then
              Form1.Print "在指定范围内点击"
                HookProc = 1 '取消原本要完成的动作
            Else
                Form1.Caption = "mouse Cursor at " + CStr(pt.x) + "," + CStr(pt.y)
                HookProc = 0 '令待完成的动作继续完成
            End If    End IfEnd Function
    窗体代码
    Option Explicit
    Const HWND_TOPMOST = -1
    Const HWND_NOTOPMOST = -2
    Const SWP_NOSIZE = &H1
    Const SWP_NOMOVE = &H2
    Const SWP_NOACTIVATE = &H10
    Const SWP_SHOWWINDOW = &H40
    Private Declare Sub SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long)Private Sub Form_Activate()
       '设置窗口为TOPMOST窗口,目的是便于观察
        SetWindowPos Me.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE
    End Sub
    Private Sub Form_Load()
        Dim RT As RECT
        With RT
        .Left = 0
        .Right = 400
        .Top = 0
        .Bottom = 300
        End With
        EnableHook RT
    End SubPrivate Sub Form_Unload(Cancel As Integer)
        FreeHook
    End Sub这样就可以监控所有在范围 400.300内的鼠标左键点击
    你可以获取你所要控制的控件的位置 然后监视在这个范围内的点击
      

  7.   

    另外一个是
    SetWindowsHookEx(WH_MOUSE,MouseProc,0,GetCurrentThreadId())
    GetCurrentThreadId()修改为你的程序的线程id就可以了
      

  8.   

    需要跨进程子类化啊....高难度自己用C写个DLL再在VB里调用吧
      

  9.   

    钩别的程序vb做不到
    vb是不能做系统级的钩子的,要么lz就用c++写个dll给vb调用
      

  10.   

    有个方法不知道可不可用,直接用GetMessage 得到它的信息,然后再做处理
    Private Declare Function SendMessageByString& Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String)
    Public Const WM_GETTEXT = &HD以下的是在按钮上的
    SendMessageByString& 句柄, WM_GETTEXT, 0, 0应该是可以的,我都可以拿到它的信息!记得比分
      

  11.   

    GetMessage 好象不能跨进程吧????
      

  12.   

    谢谢各位捧场。
    我要监控的Combo1,其Text的改变不是通过键盘输入的,所以不能用键盘钩子来实现。
    其Text是Combo1所在进程内部改写的,我就是要捕捉那改写的一刹那。
    好像有点难度。
      

  13.   

    Top  
     myjian(小马--现在已经80KG 只有跨进程子类化......
    说具体点怎么监视控吧?
      

  14.   

    这个有些麻烦....子类化可以拦截消息,使用API:SetWindowLong就可以.但它只能对本进程句柄有效.于是就只能把这个函数在目标进程里执行了.然后把别人的消息都发回来就成.以前有个代码,不过忘了放哪里了,使用了一个VC的DLL