我以前看过一篇E文,依稀记得一点。希望有用。
首先,在user control中是可以subclass 和 hook的。
如果我没有记错的话,addressof操作符要求相应的回调函数放在一个模块中。该模块被创建的控件实例们(如果你创建多个空间实例的话)共享,这样,当然,模块中的函数和变量对于空间实例也是共享的。
每创建一个孔件实例,都会setwindowlong一次,返回old wndproc函数的指针(好像把它储存在一个在模块级别中申明的全局变量中)。利用这个指针你可以而且必须把不处理的消息传递给old wndproc.
当创建第一个空间实例的时候,该全局变量中储存的是正确的 old proc的地址,当你创建第二个空间实例的时候,由于前面讲到,模块对于所有空间实例是共享的,所以模块中储存old wndproc地址的变量内容会被新的空间实例的old wndproc的地址所覆盖,于是,您的可爱的第一个控件估计就会死翘翘啦。(消息传不过去喽)您说是吗?
考虑一下吧。
由于我在网吧,没有vb,所以随便写了写,如果有误,敬请朋友们指正。

解决方案 »

  1.   

    to shyguy:
    你说的没错,addressof所指向的回调函数被放于模块中,而且被需要subclass和hook的控件实例共享,当对每一个控件实例进行setwindowlong()时,都需记录old wndproc函数指针(这个指针在公共模块的全局变量中),以便在CallWindowProc()时进行相应恢复,否则就如你所说,当一个进程中有多个控件实例时,后面的wndproc地址会替代前面的wndproc地址而导致前面的控件实例hook失败,但按道理来说,这种情况一般会发生在多个控件实例时,一个控件实例应该不会有什么问题,然而当我在一个exe项目的form上放置一个UserControl时,我发现程序根本没有进入setwindowlong()所调用的wndproc!但我现在已对这段程序进行了修改,我已将原来单一的mlWndProc指针改成mlWndProc数组指针,并在wndproc中严格区分这些指针并相应CallWindowProc(),我想这样可能会解决old wndproc互相替代的问题,但我发现仍然无效!
    谢谢你,欢迎继续参与讨论!
      

  2.   

    你用数组储存address of wndproc是可以的,但是随之而来的问题是,这样就需要严格区分每个控件实例的hwnd,才能吧相应的消息一一传给对应的控件,您说是吗?
      

  3.   

    你用数组储存address of wndproc是可以的,但是随之而来的问题是,这样就需要严格区分每个控件实例的hwnd,才能吧相应的消息一一传给对应的控件,您说是吗?
    建议您吧hwnd储存在窗口自带的数据结构中,大致代码如下:
    Declare Function GetProp Lib "user32" Alias "GetPropA" _
        (ByVal hwnd As Long, ByVal lpString As String) As Long
      Declare Function SetProp Lib "user32" Alias "SetPropA" _
        (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
      Declare Function RemoveProp Lib "user32" Alias "RemovePropA" _
        (ByVal hwnd As Long, ByVal lpString As String) As Long  ' To set a property called 'NumberOfInstances' to 3 for a form:
      SetProp Me.hWnd, "NumberOfInstances", 3  ' To get the 'NumberOfInstances' value:
      lNumber = GetProp(Me.hWnd, "NumberOfInstances")  ' To delete the property when finished with it 
      ' (Windows does this automatically when the application is ended):
      RemoveProp Me.hWnd, "NumberOfInstances"
      

  4.   

    TMD 网度太慢了,不玩了。
      

  5.   

    to nicefeather:
    这么多天了,解决了吗?
      

  6.   

    哦,ShyGuy,对不起,前一段时间我有些“不务正业”,现在该言归正传了!过一会再谈你说的GetProp/SetProp/RemoveProp,我先说一有趣的现象,当我在UserControl上放一个(或以上)的DriveListBox或DirListBox(注意他们都是自绘型ListBox)时,无需加载任何代码,加载在其他控件(指我要subclass和hook的控件)的代码会自动转嫁到他们身上,DriveListBox subclass和hook得很成功,自绘得很漂亮,这时不论DriveListBox的个数和tab次序都如此,而我要hook的控件(普通的combobox)未发生任何变化,这使我考虑到可能是这个combobox的style转换成CBS_OWNERDRAWFIXED未成功,而DriveListBox本身为CBS_OWNERDRAWFIXED,所以成功。对combobox的style转换工作我已作了,并且是用GetClassName来进行对控件类型的探测和转换的,不能对普通combobox进行class定位可能使问题的关键,看这段设置控件style的代码(注意看注解):
    Public Function fAppHook(ByVal lHookID As Long, _
            ByVal wParam As Long, ByVal lParam As Long) As Long    Static bCombo As Boolean
        Dim CWP       As CWPSTRUCT
        Dim k         As Long
        Dim sClass    As String
        '
        ' This hook procedure is a callback function invoked by the
        ' SetWindowsHookEx API issued in Sub Main.  The system calls
        ' this function whenever a message is sent to this application.
        ' Before passing the message to the destination window procedure,
        ' the system passes the message to this procedure. This procedure
        ' can examine the message but cannot modify it.
        '
        ' The lParam parameter is the address of a CWPSTRUCT structure
        ' that contains details about the message that was sent.  Using
        ' the address, the message data is copied into a local copy.
        '
        Call CopyMemory(CWP, ByVal lParam, Len(CWP))
        '
        ' Now that we have the message structure, see what message was sent.
        '
        Select Case CWP.message
            '
            ' We are only interested in the Create message.  When this
            ' message is sent we want to modify how the control is
            ' created.
            '
            Case WM_CREATE
                mlSetStyle = 0
                '
                ' Get the name of the class the window belongs to.
                '
                sClass = Space$(128)
                k = GetClassName(CWP.hwnd, ByVal sClass, 128)
                sClass = Left$(sClass, k)
                '
                ' See if the class matches that for one of our
                ' controls.  The best way to determine the class
                ' name is by uncommenting this debug command or
                ' by using Spy++ that comes with VB enterprise.
                '
                ' NOTE:
                '   To use the VB6 version of Microsoft Common Controls,
                '   change the class names to those indicated below.
                '
                'Debug.Print sClass
                Select Case sClass
                    Case "ComboLBox"
                        mlSetStyle = GetWindowLong(CWP.hwnd, GWL_STYLE)
                        If bCombo Then
                            mlSetStyle = mlSetStyle Or LBS_USETABSTOPS Or CBS_OWNERDRAWFIXED
                        Else
                            mlSetStyle = mlSetStyle Or CBS_OWNERDRAWFIXED
                            bCombo = True
                        End If
                End Select
                '
                ' Subclass the control by setting a new address for
                ' its associated window procedure.  Now when a message is
                ' sent to the control, our callback procedure will be called.
                '
                ' The SetWindowLong returns the address of the original
                ' window procedure.
                '
                If mlSetStyle Then
                    mlHookWndProc = SetWindowLong(CWP.hwnd, GWL_WNDPROC, AddressOf fSetStyle)
                End If
        End Select
        '
        ' Pass the message information to the original window procedure.
        '
        fAppHook = CallNextHookEx(mlHook, lHookID, wParam, ByVal lParam)
    End Function关于上面fSetStyle很简单,是这样的:
    Public Function fSetStyle(ByVal hwnd As Long, _
        ByVal Msg As Long, ByVal wParam As Long, _
        ByVal lParam As Long) As Long    Dim C As CREATESTRUCT
        '
        ' This callback routine is called by Windows whenever a message
        ' is sent to the control indicated by hwnd. We are only interested
        ' in the create message.
        '
        Select Case Msg
            Case WM_CREATE
                '
                ' When a create message is sent, the lParam parameter has the
                ' address of a CreateStruct structure containing style
                ' information for the control being created.  This structure
                ' is copied locally, modified and then copied back so that the
                ' control is created with our desired style.
                '
                Call CopyMemory(C, ByVal lParam, Len(C))
                C.style = mlSetStyle
                Call CopyMemory(ByVal lParam, C, Len(C))
                '
                ' Set the new style.
                '
                Call SetWindowLong(hwnd, GWL_STYLE, C.style)
                '
                ' Unsub-class the control by assigning it the address
                ' of its original window procedure.
                '
                Call SetWindowLong(hwnd, GWL_WNDPROC, mlHookWndProc)
        End Select
        '
        ' Call the original window procedure.
        '
        fSetStyle = CallWindowProc(mlHookWndProc, hwnd, Msg, wParam, lParam)
    End Function看完这段代码,我认为问题就在于那个“ComboLBox”上,我不知什么叫“ComboLBox”,但你说他不正确吧,这段代码在EXE项目中对普通ComboBox进行hook完全没问题!再来谈谈你说的GetProp/SetProp/RemoveProp,没错,假如在一个SubClass()过程中GetProp/SetProp,在UnSubclass()中GetProp/RemoveProp,然后在Form_Load()中SubClass(),在Form_Unload()中UnSubclass()会区分每一个要hook的控件WinProc,
    但在独立控件项目中是否可行,我还未证实。然而现在最紧迫的是怎样对控件的起码一个普通ComboBox能hook成功,所以怎样区分多个控件的WinProc的问题那是后话!所以关于这个问题我以后可能还要和你探讨!我觉得你Win API水平不俗,能告诉我你的OICQ号吗?希望我们以后能经常探讨此类问题!欢迎所有其他Win API高手来此探讨此类问题,我将有更多高分相送!我有的是分数哦!!
    为了表示我的诚意,我现在就将这个问题的分数加到300分!
      

  7.   

    哦,对了,我的OICQ号:11488994
      

  8.   

    To NiceFeather:
    Try This
      Select Case sClass
                'Case "ComboLBox"
                Case "ThunderComboBox"
                    mlSetStyle = GetWindowLong(CWP.hwnd, GWL_STYLE)
                    If bCombo Then
                        mlSetStyle = mlSetStyle Or CBS_OWNERDRAWFIXED 'Or LBS_USETABSTOPS Or CBS_AUTOHSCROLL
                    Else
                        mlSetStyle = mlSetStyle Or CBS_OWNERDRAWFIXED
                        bCombo = True
                    End If
            End Select
      

  9.   

    To NiceFeather: I'm going to resolve the mishooking problem...
    Try This!!!!!!!!!!!!!!! it's ok now(in some extend:-)
      Select Case sClass
                'Case "ComboLBox"
                'Case "ThunderComboBox"
                 Case "ThunderRT6ComboBox"
                    mlSetStyle = GetWindowLong(CWP.hwnd, GWL_STYLE)
                    If bCombo Then
                        mlSetStyle = mlSetStyle Or CBS_OWNERDRAWFIXED 'Or LBS_USETABSTOPS Or CBS_AUTOHSCROLL
                    Else
                        mlSetStyle = mlSetStyle Or CBS_OWNERDRAWFIXED
                        bCombo = True
                    End If
            End Select
      

  10.   

    我想,VB中的COMBOBOX根本不是正宗的COMBOBOX,用SPY++可以发现是ThunderR6ComboBox.而我用vc做了一个测试程序,用spy++看,发现里边的ComboBox是ComboBox.
    所以我想实现的方法就是:自己做个一comboEx控件,用createwindowEx来创建!不要再用hook,但是这样的话焦点问题就会出现(详见昨天给你的E文).
      

  11.   

    to ShyGuy:
    1、你说的动态创建(CreateWindowEx)一个ComboBox控件,我用Spy++看了一下,确实是“ComboBox”,但不管他是什么,如果要自绘和探测鼠标消息并做出响应,一样要采用hook技术!
    2、你给我大那篇e文,我随便看了一下。那篇e文中提到,如果是用CreateWindowEx()创建non-OCX的ComboBox控件,则很难捕捉到此控件的焦点,确实是这样!作者提到要用到OLE IOLEInPlaceActiveObject interface技术能解决此类问题(文中提到Mike Gainer, Matt Curland and Bill Storage的代码,你那里有吧?怎不见发过来?:));但我是在User_Control里用CreateWindowEx()创建ComboBox子控件,因为User_Control的CanGetFocus属性可调,所以在User_Control中创建动态ComboBox应该不存在这个问题!
    3、你似乎在建议我动态创建“ComboBoxEx32”(就是ImageComboBox控件,用Spy++察看也是“ComboBox”),我发现如在User_Control中这样做,则SetWindowLong()无法进入指定的WinProc fAppWndProc,而创建“ComboBox”(标准ComboBox,用Spy++察看是“ComboBox”),则SetWindowLong()能顺利进入指定的WinProc fAppWndProc………………(……NiceFeather终于忍不住这痛苦的煎熬,冲进盥洗室,狂吐了一阵血,ShyGuy赶忙讲其扶住,劝道:“兄弟,条条大路通罗马,何必跟ComboBox这厮过不去呢!”,NiceFeather心有不甘,甫一站定,咬牙切齿地仰天骂道:“狗日的ComboBox,我跟你没完……”……)欲知后事如何,且听下回分解…………
      

  12.   

    To NiceFeather:
      COMBOLBOX是一个COMBOBOX的下拉列表部分。这是为什么你上次能够绘制下拉框而无法绘制COMBOBOX的原因。
      你彻底搞定了?
    if(TRUE)
       佩服佩服。
    else
       偶自己做一个和你完全不同思路的SlefDraw-Combo给你看看。
      

  13.   

    To NiceFeather:
      COMBOLBOX是一个COMBOBOX的下拉列表部分。这是为什么你上次能够绘制下拉框而无法绘制COMBOBOX的原因。
      你彻底搞定了?
    if(TRUE)
       佩服佩服。
    else
       偶自己做一个和你不同思路的SlefDraw-Combo给你看看。
      

  14.   

    to shyguy:
    是的,我已完全搞定,就用“ComboBox”,而且是用我们上次探讨的代码,其实是我们想得过于复杂了,即使moudle里的main()、fAppHook、fSetStyle全都不要,就用moudle里的fAppWndProc就可以了!自绘得很漂亮!(这真是令我也大跌眼镜)
    前几天一直想和你谈谈,但一直碰不到你,明天(2000-11-15)晚上(20:00)我在OICQ上等你!
      

  15.   

    http://www.banasoft.com/DownLoad/BNHkLib.exe