VB与API学习笔记(9)程序间的会话


下面一个A程序向B程序发送一个消息 。

在发送前,在Clipbroad中设置文本,B程序接到消息后,取出ClipBroad的文本

发送很好处理:Findwindow找到B的句柄

                            SendMessage发送消息。

接受就有点麻烦了。

按VB的惯性,这个消息来的时候,是什么事件来触发B知道消息来了呢?

这就会进入死胡同。因为VB是事件触发。而API是消息触发。

那怎么知道API发送的消息来了呢?


一个消息要进入一个程序B(图中打成了A)。它必须统一通过入口函数处来进入。如果我们把入口函数的地址变

成下面的winPro,那么消息就只能从winPro进入。于是我们用SetWindowLong来改变入口函数的地址。这样消息

就会自动传到我们自己定义的一个函数WinPro中。


注意:自己定义的函数winPro的参数肯定是和发送来时传送的参数是一致的。只是过程加入我们自己的判断。

这样,消息一来,自动就会到WinPro,于是我们就起到了“响应”的作用。就好像是事件响应一样。


另一个细节是,我既要处理自己定义消息的函数,但又不想破坏其它现在的情况怎么办?

用API,CallwindowProc。它可以临时指定入口地址,但并不改变入口地址值,意思是入口地址仍然是原处,

只是这个时候进行“转发”消息到另一个函数入口地址了。


下面两个程序,同时打开时,可以看到效果。

程序A模块:

Public Const WM_USER = &H400 '自定义范围的消息值

Declare Function FindWindow _
        Lib "user32" _
        Alias "FindWindowA" (ByVal lpClassName As String, _
                             ByVal lpWindowName As String) As Long
Declare Function SendMessage _
        Lib "user32" _
        Alias "SendMessageA" (ByVal hWnd As Long, _
                              ByVal wMsg As Long, _
                              ByVal wParam As Long, _
                              lParam As Any) As Long

A主程序:

Private Sub Command1_Click()
    Dim hWndRecv As Long

    Clipboard.SetText Text1.Text
    hWndRecv = FindWindow(vbNullString, "Receive1 - 资料接收者")
    SendMessage hWndRecv, WM_USER + 1001, 0, ByVal 0&
End Sub

Private Sub Command2_Click()
    Dim hWndRecv As Long

    Clipboard.SetText Text2.Text
    hWndRecv = FindWindow(vbNullString, "Receive1 - 资料接收者")
    SendMessage hWndRecv, WM_USER + 1002, 0, ByVal 0&
End Sub



程序B模块:

Public Const GWL_WNDPROC = (-4)
Public Const WM_USER = &H400

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
Declare Function GetWindowLong _
        Lib "user32" _
        Alias "GetWindowLongA" (ByVal hwnd As Long, _
                                ByVal nIndex 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

Public prevWndProc As Long

Function WndProc(ByVal hwnd As Long, _
                 ByVal Msg As Long, _
                 ByVal wParam As Long, _
                 ByVal lParam As Long) As Long

    If Msg = WM_USER + 1001 Then
        Form1.Text1.Text = Clipboard.GetText
    ElseIf Msg = WM_USER + 1002 Then
        Form1.Text2.Text = Clipboard.GetText
    Else
        WndProc = CallWindowProc(prevWndProc, hwnd, Msg, wParam, lParam) '转发
    End If
End Function

程序B主程序:

Private Sub Form_Load()
    prevWndProc = GetWindowLong(Me.hwnd, GWL_WNDPROC) '取出原入口地址值
    Call SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf WndProc)  '改变现有入口地址值
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Call SetWindowLong(Me.hwnd, GWL_WNDPROC, prevWndProc)  '恢复成原来的入口地址值
End Sub


 线程

'取得当前线程伪句柄
Private Declare Function GetCurrentThread Lib "kernel32" () As Long
'取得线程优级级,1-31,31等级最高
Private Declare Function GetThreadPriority Lib "kernel32" (ByVal hThread As Long) As Long
'设置纯种优先级
Private Declare Function SetThreadPriority _
                Lib "kernel32" (ByVal hThread As Long, _
                                ByVal nPriority As Long) As Long

Private Sub Command1_Click()
    Dim p As Long
    p = GetThreadPriority(GetCurrentThread) '取得当前线程优先级
    Text1.Text = p
    SetThreadPriority GetCurrentThread, 3 '设置当前线程优先级
    Text2.Text = GetThreadPriority(GetCurrentThread) '再取
    SetThreadPriority GetCurrentThread, p '恢复
End Sub



===========================


再看回调函数:


一般我们是调用函数是从A处调用B处,然后就完了。


回调函数,回调,就是回来再调用一个函数。和上面不同的是A处调用B处后,这不算完,紧接着还要从B处“回来”调用另一个函数C。这一连串动作完成,就是整个回调函数的实现过程。


那怎么,B怎么知道是调用了C呢?

于是在A处调用B时,就得从A处把C函数的地址传给B,这样B就知道,完成自己功能后,还得再紧接着调用C函数。


怎么取得C函数的地址?  VB中有一个AddressOf来取得一个函数的地址。(在C++中叫函数指针)


为什么 要有回调函数?

     因为程序讲究模块化,一个模块定后一般是不会再变化。然后有些功能用模块无法实现这样的功能。怎么办?

      回调函数,就是加入自己定义的功能达到丰富、完善,或者说满足自己的要求,而又不影响模块的功能。

      如上面A调用B处,是固定的东西,并不能满足自己的要求,于是在C中加入自己的东西,这样,最后的结果就符合自己了。




再看一下VB的机制。

VB是事件驱动, windows是消息驱动,那么消息是怎么传递到VB中而产生事件的呢?


VB程序中有一个窗口程序(window procedure),它就象一个院子的大门。所有windows消息要进入VB就得从中经过。

这个消息进来后,就对应相应的事件,如果这个事件有代码,就会对事件进行实现,如果没有代码,尽管有消息来,也会被

扔掉,因为没有写实现的代码。


有两个注意点:

1、大门很关键,如果我们控制了大门这个战略要点,就可以“吃掉消息 ”,或者假传消息 ,激发我们自己的东西。

      吃掉消息 :尽管有消息来,我们在门处,把消息扔掉,可以导致本来的事件不会触发。

     假传消息 :如果有吃饭的消息来了,我们在大门处可以假传是干活的消息,可触发干活的事件发生。

      转发消息 :如果有按键的消息来了,我们可以转发到自己的函数,这样,按键没触发原来的事件,只是触发了我们的函数。

2、VB本身不会处理全部的消息 ,很多消息 都是由windows来代替,即由DefWindowProc这个API来处理。这样大大地减轻

      了VB自身的工作量,减少了资源的浪费。

      当我们在窗体上按下了左健,windows系统检测到这个消息,并将其传递给VB的大门,VB再根据这一消息触发对应的消息。

可以参看上面的例子中B程序。B程序就是在大门处设置了一个间谍(回调函数),这个间谍就来过滤windows的消息 。

如果是我们想要的消息 ,就处理,不是我们的消息 ,就放行让VB自己去处理。

仔细看一下B程序过程:


上面三个框。第一个是原来的传递消息的过程。

B程序是怎么做到过滤消息的呢?(第二个框)

             从A处传来windows消息,由于B处的用API:  setwindowlong把大门的地址E处改向了C处,C就是我们自己处理的函数地址。

             这样我们在C处就把要过滤的消息 进行处理(想怎么样就怎么样),然后在C中用CallWindowsproc(拷我又标成了C),把地址

            再次转向E,E就是VB的窗口程序。

           由此可以看到C实际上起到插队、拦截的作用。

第三个框,就象网上上监听一样,在不影响别的程序(无论是不是VB窗口程序),监听里面的东西,查看别人的数据。至于用什么API, 此处略过。








下面是一个回调函数例子。

例举窗体,回调函数是EnumWindowsProc.


模块:

Option Explicit

'列举所有窗体句柄
'lpEnumFunc是回调函数的地址
Public Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
'取得窗体的标题
Public Declare Function GetWindowText _
               Lib "user32" _
               Alias "GetWindowTextA" (ByVal hwnd As Long, _
                                       ByVal lpString As String, _
                                       ByVal cch As Long) As Long
'回调函数,与API:EnumWindow保持一致,且必须放置于标准模块中
Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Boolean
    Dim s As String

    s = String$(80, 0)
    GetWindowText hwnd, s, 80
    If InStr(1, s, 0) <> 0 Then
        Form1.List1.AddItem hwnd & " " & Left$(s, InStr(1, s, 0) - 1)
    End If
    
    EnumWindowsProc = True
End Function


主程序:

Option Explicit

Private Sub Command1_Click()
    EnumWindows AddressOf EnumWindowsProc, 0
End Sub





==================================================


常见消息 :


一、键盘消息:

Public Const WM_KEYDOWN = &H100 '按键按下KeyDown
Public Const WM_KEYUP = &H101   '按键弹上(放开)KeyUP
Public Const WM_CHAR = &H102     '按键按下  KeyPress
Public Const WM_SYSKEYDOWN = &H104 'Alt键按下
Public Const WM_SYSKEYUP = &H105   'Alt键弹起
Public Const WM_SYSCHAR = &H106    '在Alt键按下时,另一个被按的键,如ALT+a  指的是a

二、鼠标消息 

Public Const WM_SETCURSOR = &H20 '设置鼠标形状,发生在移动之前。即先设置鼠标指针再捕获位置
Public Const WM_MOUSEMOVE = &H200   '鼠标移动
Public Const WM_LBUTTONDOWN = &H201 '左键按下
Public Const WM_LBUTTONUP = &H202
Public Const WM_LBUTTONDBLCLK = &H203 '左键双击
Public Const WM_MBUTTONDOWN = &H207   '中键按下
Public Const WM_MBUTTONUP = &H208     '中键弹上
Public Const WM_MBUTTONDBLCLK = &H209  '中键双击
Public Const WM_RBUTTONDOWN = &H204     '右键按下
Public Const WM_RBUTTONUP = &H205
Public Const WM_RBUTTONDBLCLK = &H206   '右键双击









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值