我以前看过一篇E文,依稀记得一点。希望有用。
首先,在user control中是可以subclass 和 hook的。
如果我没有记错的话,addressof操作符要求相应的回调函数放在一个模块中。该模块被创建的控件实例们(如果你创建多个空间实例的话)共享,这样,当然,模块中的函数和变量对于空间实例也是共享的。
每创建一个孔件实例,都会setwindowlong一次,返回old wndproc函数的指针(好像把它储存在一个在模块级别中申明的全局变量中)。利用这个指针你可以而且必须把不处理的消息传递给old wndproc.
当创建第一个空间实例的时候,该全局变量中储存的是正确的 old proc的地址,当你创建第二个空间实例的时候,由于前面讲到,模块对于所有空间实例是共享的,所以模块中储存old wndproc地址的变量内容会被新的空间实例的old wndproc的地址所覆盖,于是,您的可爱的第一个控件估计就会死翘翘啦。(消息传不过去喽)您说是吗?
考虑一下吧。
由于我在网吧,没有vb,所以随便写了写,如果有误,敬请朋友们指正。
首先,在user control中是可以subclass 和 hook的。
如果我没有记错的话,addressof操作符要求相应的回调函数放在一个模块中。该模块被创建的控件实例们(如果你创建多个空间实例的话)共享,这样,当然,模块中的函数和变量对于空间实例也是共享的。
每创建一个孔件实例,都会setwindowlong一次,返回old wndproc函数的指针(好像把它储存在一个在模块级别中申明的全局变量中)。利用这个指针你可以而且必须把不处理的消息传递给old wndproc.
当创建第一个空间实例的时候,该全局变量中储存的是正确的 old proc的地址,当你创建第二个空间实例的时候,由于前面讲到,模块对于所有空间实例是共享的,所以模块中储存old wndproc地址的变量内容会被新的空间实例的old wndproc的地址所覆盖,于是,您的可爱的第一个控件估计就会死翘翘啦。(消息传不过去喽)您说是吗?
考虑一下吧。
由于我在网吧,没有vb,所以随便写了写,如果有误,敬请朋友们指正。
你说的没错,addressof所指向的回调函数被放于模块中,而且被需要subclass和hook的控件实例共享,当对每一个控件实例进行setwindowlong()时,都需记录old wndproc函数指针(这个指针在公共模块的全局变量中),以便在CallWindowProc()时进行相应恢复,否则就如你所说,当一个进程中有多个控件实例时,后面的wndproc地址会替代前面的wndproc地址而导致前面的控件实例hook失败,但按道理来说,这种情况一般会发生在多个控件实例时,一个控件实例应该不会有什么问题,然而当我在一个exe项目的form上放置一个UserControl时,我发现程序根本没有进入setwindowlong()所调用的wndproc!但我现在已对这段程序进行了修改,我已将原来单一的mlWndProc指针改成mlWndProc数组指针,并在wndproc中严格区分这些指针并相应CallWindowProc(),我想这样可能会解决old wndproc互相替代的问题,但我发现仍然无效!
谢谢你,欢迎继续参与讨论!
建议您吧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"
这么多天了,解决了吗?
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分!
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
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
所以我想实现的方法就是:自己做个一comboEx控件,用createwindowEx来创建!不要再用hook,但是这样的话焦点问题就会出现(详见昨天给你的E文).
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,我跟你没完……”……)欲知后事如何,且听下回分解…………
COMBOLBOX是一个COMBOBOX的下拉列表部分。这是为什么你上次能够绘制下拉框而无法绘制COMBOBOX的原因。
你彻底搞定了?
if(TRUE)
佩服佩服。
else
偶自己做一个和你完全不同思路的SlefDraw-Combo给你看看。
COMBOLBOX是一个COMBOBOX的下拉列表部分。这是为什么你上次能够绘制下拉框而无法绘制COMBOBOX的原因。
你彻底搞定了?
if(TRUE)
佩服佩服。
else
偶自己做一个和你不同思路的SlefDraw-Combo给你看看。
是的,我已完全搞定,就用“ComboBox”,而且是用我们上次探讨的代码,其实是我们想得过于复杂了,即使moudle里的main()、fAppHook、fSetStyle全都不要,就用moudle里的fAppWndProc就可以了!自绘得很漂亮!(这真是令我也大跌眼镜)
前几天一直想和你谈谈,但一直碰不到你,明天(2000-11-15)晚上(20:00)我在OICQ上等你!