-
Jul 11th, 2020, 02:41 PM
#1
Thread Starter
Hyperactive Member
[RESOLVED] Subclassing application
I want to get the text entered in a textbox using subclassing. I also want to get certain other information also using subclassing. Here is my code snippet for subclassing:
Code:
Public Function MyWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByRef wParam As Long, ByRef lParam As Long, ByRef defCall As Boolean) As Long
Select Case uMsg
Case WM_CHAR
'
' Here I get text from textbox as Chr(wParam)
'
Case WM_COMMAND
'
' I want to capture other events here
'
End Select
End Function
'
'
It appears that I cannot do both in the above WndProc because to get text data using WM_CHAR I have to subclass with hWnd = handle of the textbox and to do WM_COMMAND I have to subclass with hWnd = handle of my application.
Is there anyway to have one WndProc and do both
-
Jul 11th, 2020, 02:56 PM
#2
Re: Subclassing application
Originally Posted by Ordinary Guy
I want to get the text entered in a textbox using subclassing. I also want to get certain other information also using subclassing. Here is my code snippet for subclassing:
Code:
Public Function MyWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByRef wParam As Long, ByRef lParam As Long, ByRef defCall As Boolean) As Long
Select Case uMsg
Case WM_CHAR
'
' Here I get text from textbox as Chr(wParam)
'
Case WM_COMMAND
'
' I want to capture other events here
'
End Select
End Function
'
'
It appears that I cannot do both in the above WndProc because to get text data using WM_CHAR I have to subclass with hWnd = handle of the textbox and to do WM_COMMAND I have to subclass with hWnd = handle of my application.
Is there anyway to have one WndProc and do both
Hello Ordinary Guy,
To better understand your question could you answer a few questions?
1. What are you trying to do, rather than how you are doing it in code?
2. Which certain other information are you referring to?
3. Perhaps you could post more code, as what you have posted isn't even a complete procedure?
Thank you. Yours,
Peter Swinkels
-
Jul 11th, 2020, 03:27 PM
#3
Re: Subclassing application
You need to subclass both Windows.
You can use the same procedure to handle both subclassed windows and compare the hWnd parameter to see to which one the message belongs.
Last edited by Eduardo-; Jul 11th, 2020 at 03:35 PM.
-
Jul 11th, 2020, 03:56 PM
#4
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by Peter Swinkels
Hello Ordinary Guy,
To better understand your question could you answer a few questions?
1. What are you trying to do, rather than how you are doing it in code?
2. Which certain other information are you referring to?
3. Perhaps you could post more code, as what you have posted isn't even a complete procedure?
Thank you. Yours,
Peter Swinkels
Why do you say it is not a complete procedure? A WndProc can be as simple as catching just one WM_ statement or many. My example is complete as possible. Under WM_CHAR is where I capture the text entered in a text box so I don't need to show the underlying code for that and with WM_COMMAND I will capture the menu clicks for one thing and again the code for that isn't necessary to show because it has nothing to do with my request.
-
Jul 11th, 2020, 03:57 PM
#5
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by Eduardo-
You need to subclass both Windows.
You can use the same procedure to handle both subclassed windows and compare the hWnd parameter to see to which one the message belongs.
The problem I have with this is that hWnd will only be either the handle of the textbox or it will be the handle of the application so how can I compare hWnd to anything.
-
Jul 11th, 2020, 04:02 PM
#6
Re: Subclassing application
Originally Posted by Ordinary Guy
The problem I have with this is that hWnd will only be either the handle of the textbox or it will be the handle of the application so how can I compare hWnd to anything.
Using If
Code:
If hWnd = FormName.TexBoxName.hWnd Then
Select Case uMsg
Case WM_CHAR
'
' Here I get text from textbox as Chr(wParam)
'
End Select
ElseIf hWnd = FormName.hWnd Then
Select Case uMsg
Case WM_COMMAND
'
' I want to capture other events here
'
End Select
End If
-
Jul 11th, 2020, 04:42 PM
#7
Thread Starter
Hyperactive Member
Re: Subclassing application
That's not going to work because you are testing for hWnd and hWnd can only be one of the two handles so it will always be FormName.hWnd or it will be FormName.TexBoxName.hWnd but it will never be both. To start the subclass I use this:
To subclass the app:
Code:
Private Sub Command1_Click()
MainOldWindPoc = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf MyWndProc)
End Sub
or if I want to subclass the textbox:
Code:
Private Sub Command2_Click()
MainOldWindPoc = SetWindowLong(Text1.hwnd, GWL_WNDPROC, AddressOf MyWndProc)
End Sub
I can't do it for both at the same time. I have to stop the subclassing and click on the other button
-
Jul 11th, 2020, 04:45 PM
#8
Re: Subclassing application
Originally Posted by Ordinary Guy
I can't do it for both at the same time.
Why not? Yes, you can.
But don't use the same variable MainOldWindPoc for the textbox, use another one.
-
Jul 11th, 2020, 05:19 PM
#9
Re: Subclassing application
If using modern subclassing with comctl32.dll vs user32.dll, made subclassing a bit easier
1. You don't need to keep track of "old" window procedures, the dll does that for you
2. An extra parameter is sent to the subclass procedure. You can stuff whatever you want into it and that parameter is per subclassed item
3. But most importantly, you don't need to worry about unsubclassing in any specific order. Typically an issue when a window is subclassed more than once. And it is more possible than you'd think since other code in your project (i.e., compiled usercontrols, etc) may be subclassing without you being aware.
If interested, you can see my subclassing suite I posted in the code bank: https://www.vbforums.com/showthread....re-A-new-breed
-
Jul 11th, 2020, 06:19 PM
#10
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by Eduardo-
Why not? Yes, you can.
But don't use the same variable MainOldWindPoc for the textbox, use another one.
OK, I used two WndProc's. One for WM_CHAR and the other for WM_COMMAND.
Code:
Public Function MyWndProc1(ByVal hWnd As Long, ByVal uMsg As Long, ByRef wParam As Long, ByRef lParam As Long, ByRef defCall As Boolean) As Long
Select Case uMsg
Case WM_CHAR
'
'
'
End Select
End Function
Code:
Public Function MyWndProc2(ByVal hWnd As Long, ByVal uMsg As Long, ByRef wParam As Long, ByRef lParam As Long, ByRef defCall As Boolean) As Long
Select Case uMsg
Case WM_COMMAND
'
'
'
End Select
End Function
It works very nice. I guess from all the replies that I won't be able to use the same WndProc, is that correct
-
Jul 11th, 2020, 06:28 PM
#11
Thread Starter
Hyperactive Member
Re: Subclassing application
Now I have another question. I'm now going to use this code in another project where I hook into an external process. I use:
hWndApp = FindWindow(vbNullString, "Test App") to get the handle of the other app. This is where I use WM_COMMAND to capture the clicks on the menu items.
For the textbox on the other app I use:
hWndTextbox = FindWindowEx(hWndApp, 0, vbNullString, "SOME TEXT HERE"). This works fine but how would I get the handle of the textbox if it did not have any text in it.
-
Jul 11th, 2020, 06:34 PM
#12
Re: Subclassing application
Originally Posted by Ordinary Guy
I guess from all the replies that I won't be able to use the same WndProc, is that correct
No, that's what I said before, that you can use the same WindowProc if you want. Or you can use two, whatever you prefer.
-
Jul 11th, 2020, 06:41 PM
#13
Re: Subclassing application
Originally Posted by Ordinary Guy
hWndTextbox = FindWindowEx(hWndApp, 0, vbNullString, "SOME TEXT HERE"). This works fine but how would I get the handle of the textbox if it did not have any text in it.
Is this other app going to be an app you create and will always be used in your project? Or is the textbox just an example, and you may need to find the hWnd of any control on that other app? More details could be helpful
Oh, BTW, ignore my post regarding that thunk suite -- it is not compatible with DLL injection; not that I have a better idea where this thread is going.
-
Jul 11th, 2020, 07:26 PM
#14
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by LaVolpe
Is this other app going to be an app you create and will always be used in your project? Or is the textbox just an example, and you may need to find the hWnd of any control on that other app? More details could be helpful
Oh, BTW, ignore my post regarding that thunk suite -- it is not compatible with DLL injection; not that I have a better idea where this thread is going.
The other app is my own for now just so I can test my hooking easier but definitely I will want to hook some other app, Notepad, for example
-
Jul 11th, 2020, 07:47 PM
#15
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by Eduardo-
No, that's what I said before, that you can use the same WindowProc if you want. Or you can use two, whatever you prefer.
That's fine for subclassing my own app but as stated in post 11 but I'm not sure that will work in the project I making for hooking. I got some code on hooking that I'm making some changes but the hooking code I'm leaving alone since I don't know enough to mess with it. In the hooking project I use this to hook and subclass what ever app I'm hooking:
Code:
Private Sub cmdHookApp_Click()
SetSubclass hWndApp, AddressOf MyWndProc1
SetSubclass hWndTextbox, AddressOf MyWndProc2
End Sub
I can't change to code for the SetSubclass. SetSubclass is a function call:
Code:
Private Declare Function SetSubclass Lib "HookDLL" (ByVal hWnd As Long, ByVal pWndProc As Long) As Boolean
to SetSubclass in a DLL which has all the hooking code. The code for that function is this:
Code:
Public Function SetSubclass(ByVal hwnd As Long, ByVal pWndProc As Long) As Boolean
Dim pid As Long
Dim tid As Long
Dim hHook As Long
' Check initialization
If Not isInitialized Then Exit Function
' Get thread identifier
tid = GetWindowThreadProcessId(hwnd, pid)
If tid = App.ThreadID Then
'Local
If SetWindowSubclass(hwnd, pWndProcAddress, pWndProc, ByVal hWndReceiver) Then
SetSubclass = True
End If
Else
' Install hook
hHook = SetWindowsHookEx(WH_CALLWNDPROC, AddressOf CallWndProc, App.hInstance, tid)
If hHook Then
' Send message for subclassing
' This message will be received in the CallWndProc procedure in the needed process
SendMessage hwnd, WM_SUBCLASS, hWndReceiver, ByVal pWndProc
' Success
SetSubclass = True
UnhookWindowsHookEx hHook
End If
End If
End Function
I don't have any idea how that code works so it is best I leave it as is. Now calling SetSubclass twice with two different handles and two different WndProc's works but I don't know how to call just one SetSubclass and use just one WndProc procedure to handle both WM_CHAR and WM_COMMAND.
-
Jul 11th, 2020, 08:18 PM
#16
Re: Subclassing application
Took a little bit to try to find the source for your subclassing, but I found it. A project by "the Trick"
Using the same window procedure shouldn't be that difficult. I see where you are having problems relating one hWnd to another. I think you should have told everyone up front which source code you were using and its location. We could have probably resolved this much earlier...
You are sending to SetSubclass two hWnds. Add a collection to your module and use it as a cross-reference
Code:
' in your module: Public mSubclassKeys As Collection
' collection item value of 1 = main app
' collection item value of 2 = texbox
Private Sub cmdHookApp_Click()
Set mSubclassKeys = New Collection
mSubclassKeys.Add 1, CStr(hWndApp)
SetSubclass hWndApp, AddressOf MyWndProc
mSubclassKeys.Add 2, CStr(hWndTextbox)
SetSubclass hWndTextbox, AddressOf MyWndProc
End Sub
' in your subclass routine
Public Function MyWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByRef wParam As Long, ByRef lParam As Long, ByRef defCall As Boolean) As Long
Select Case uMsg
Case WM_CHAR
If mSubclassKeys(CStr(hWnd)) = 2 Then ' WM_CHAR is for textbox else for app
'
'
End If
Case WM_COMMAND
If mSubclassKeys(CStr(hWnd)) = 1 Then ' WM_COMMAND is for app else for textbox
'
'
End If
End Select
End Function
edited: If you only care to know which hWnd is main app and all others (controls) are treated equally, then the cross-referencing is easier & won't use a collection. This is where I suspect you are headed.
Code:
' in your module: Public mHwndMainApp As Long
Private Sub cmdHookApp_Click()
mHwndMainApp = hWndApp
SetSubclass hWndApp, AddressOf MyWndProc
SetSubclass hWndTextbox, AddressOf MyWndProc
End Sub
' in your subclass routine
Public Function MyWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByRef wParam As Long, ByRef lParam As Long, ByRef defCall As Boolean) As Long
Select Case uMsg
Case WM_CHAR
If hWnd <> mHwndMainApp Then ' WM_CHAR is for some control else for app
'
'
End If
Case WM_COMMAND
If hWnd = mHwndMainApp Then ' WM_COMMAND is for app else for some control
'
'
End If
End Select
End Function
Last edited by LaVolpe; Jul 11th, 2020 at 08:35 PM.
-
Jul 11th, 2020, 10:02 PM
#17
Re: Subclassing application
Why try to puppeteer Notepad at all?
It's just a RichEdit control wrapped with some menus as a stand alone program. Just make a Form with a RichTextBox, set that to text-only, and add what little you need in the way of menus.
Far less fiddling, far easier to work with, far more flexible, and far less likely to get your program detected as malware.
-
Jul 12th, 2020, 08:21 AM
#18
Re: Subclassing application
Originally Posted by Ordinary Guy
Why do you say it is not a complete procedure? A WndProc can be as simple as catching just one WM_ statement or many. My example is complete as possible. Under WM_CHAR is where I capture the text entered in a text box so I don't need to show the underlying code for that and with WM_COMMAND I will capture the menu clicks for one thing and again the code for that isn't necessary to show because it has nothing to do with my request.
Hello Ordinary Guy,
Thank you for your reply. My apologies for my hasty conclusion about your code's completeness. I see you have some placeholders that need to filled in. But, shouldn't proper function procedures always return at least one default value?
yours,
Peter Swinkels
-
Jul 12th, 2020, 01:35 PM
#19
Thread Starter
Hyperactive Member
Re: Subclassing application
From post #11
Originally Posted by Ordinary Guy
...how would I get the handle of the textbox if it did not have any text in it.
For my test app I use this:
hWndTextbox = FindWindowEx(hWndTestApp, 0, "ThunderRT6Textbox", vbNullString)
and Notepad I use this:
hWndTextbox = FindWindowEx(hWndNotepad, 0, "Edit", vbNullString)
I got these ClassNames by using Spy++ but isn't there a way to get the handle by doing a mouse down on the textbox
-
Jul 12th, 2020, 01:42 PM
#20
Re: Subclassing application
Originally Posted by Ordinary Guy
but isn't there a way to get the handle by doing a mouse down on the textbox
GetCursorPos and WindowFromPoint and for the mouse down a timer and GetAsyncKeyState.
-
Jul 12th, 2020, 03:29 PM
#21
Re: Subclassing application
Here's an option if you want to play with it.
Test project. Add 1 command button, 1 textbox, 1 picturebox (visible=false)
add this code
Code:
Private Declare Function SetCapture Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseCapture Lib "user32.dll" () As Long
Private Declare Sub mouse_event Lib "user32.dll" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Declare Function WindowFromPoint Lib "user32.dll" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
Private Declare Function GetCursorPos Lib "user32.dll" (ByRef lpPoint As POINTAPI) As Long
Private Type POINTAPI
X As Long
Y As Long
End Type
Const MOUSEEVENTF_LEFTDOWN = &H2
Private Declare Function SetCursor Lib "user32.dll" (ByVal hCursor As Long) As Long
Private Sub Command1_Click()
If Picture1.MouseIcon.Handle <> 0 Then
SetCursor Picture1.MouseIcon.Handle
End If
SetCapture Picture1.hwnd
mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
End Sub
Private Sub Picture1_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyEscape Then
ReleaseCapture
If Picture1.MouseIcon.Handle <> 0 Then SetCursor 0
Command1.SetFocus
End If
End Sub
Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim pt As POINTAPI
GetCursorPos pt
Text1.Text = WindowFromPoint(pt.X, pt.Y)
End Sub
Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Picture1.MouseIcon.Handle <> 0 Then SetCursor 0
Command1.SetFocus
End Sub
Click the button, then move the cursor around the screen. To stop the capture, simply click the mouse on the target window. Pressing ESC also stops the capture.
For some added fluff. If the picturebox has its MouseIcon property set, the cursor will use that icon while capture is in play.
Note: the hidden picturebox is just used for SetCapture, mouse/keyboard events. Any control with an hWnd & those events can be used instead.
-
Jul 12th, 2020, 03:54 PM
#22
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by LaVolpe
Or get creative with a drag-type action like Spy++ does.
I just now found a VB project in my code collections and it uses an Up Arrow icon which you drag over any window and when you release the drag pointer it gets the handle of that window and prints it in a listbox. It uses the WindowFromPoint API as suggested by Eduardo but it doesn't use GetCursorPos API and the GetAsyncKeyState API. I already made modifications to it to keep the code at a minimum. Looks like a better way to get the handles than looking them up in Spy++
Here's the stripped down code of that project
Code:
Option Explicit
Private Type POINT
X As Long
Y As Long
End Type
Private Declare Function SetCapture Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetCapture Lib "user32" () As Long
Private Declare Sub ClientToScreen Lib "user32" (ByVal hWnd As Long, lpPoint As POINT)
Private Declare Function WindowFromPoint Lib "user32" (ByVal ptY As Long, ByVal ptX As Long) As Long
Public mlngHwndCaptured As Long ' Holds the handle to the captured window
Private Sub Form_MouseDown(Button%, Shift%, X As Single, Y As Single)
SetCapture Me.hWnd
End Sub
Private Sub Form_MouseMove(Button%, Shift%, X As Single, Y As Single)
Dim pt As POINT ' Holds the location of the window.
If GetCapture() Then
' Convert the current mouse position to Screen coordinates.
pt.X = CLng(X)
pt.Y = CLng(Y)
ClientToScreen Me.hWnd, pt
' Pass that value to WindowFromPoint to find out what window we are pointing to.
mlngHwndCaptured = WindowFromPoint(pt.X, pt.Y)
End If
End Sub
Private Sub Form_MouseUp(Button%, Shift%, X As Single, Y As Single)
If mlngHwndCaptured Then
List1.AddItem "Window handle = " & mlngHwndCaptured
' Clear our module-level variable
mlngHwndCaptured = False
End If
End Sub
-
Jul 12th, 2020, 04:03 PM
#23
Thread Starter
Hyperactive Member
Re: Subclassing application
NOTE: I'm wondering if Eduardo's suggestion about using GetCursorPos and GetAsyncKeyState would be a better way to do this. Using the code above I have to mouse down on my Form and then drag the mouse over to the window and then release the mouse pointer to get the handle I want. What would be better would be if I didn't have to drag the mouse pointer; just move it to the window and mouse down/mouse up on the window.
-
Jul 12th, 2020, 04:10 PM
#24
Re: Subclassing application
Perhaps you missed post #21 from LaVolpe, he uses GetCursorPos there.
-
Jul 12th, 2020, 04:23 PM
#25
Thread Starter
Hyperactive Member
Re: Subclassing application
Yes I did to the post as it now appears but what happened to his post where he states Or get creative with a drag-type action like Spy++ does. which I captured in my post #22. That's what I saw in his post #21
-
Jul 12th, 2020, 04:25 PM
#26
Re: Subclassing application
Originally Posted by Ordinary Guy
Yes I did to the post as it now appears but what happened to his post where he states Or get creative with a drag-type action like Spy++ does. which I captured in my post #22. That's what I saw in his post #21
I simply deleted it and wanted to post a sample vs suggestion. In that sample of mine, no dragging is required.
-
Jul 12th, 2020, 05:09 PM
#27
Thread Starter
Hyperactive Member
Re: Subclassing application
Your sample code works better than the code I posted because there is no dragging involved. Was wondering one thing, however. Is it possible that when I move the cursor over to the other window (textbox) and click it will also cause a normal click on that window so I can just type text into it. As it works now I move the cursor to other window and click to capture the handle but I have to click a second time to activate that window before I can type into it. Would be nice if just one mouse click could handle both situations. Just a thought
-
Jul 12th, 2020, 05:44 PM
#28
Re: Subclassing application
Try using SetForegroundWindow API on the target hWnd
Code:
Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
-
Jul 12th, 2020, 07:20 PM
#29
Thread Starter
Hyperactive Member
Re: Subclassing application
I tried two approaches:
Approach 1
Code:
Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Picture1.MouseIcon.Handle <> 0 Then SetCursor 0
SetForegroundWindow FindWindow(vbNullString, "Test App")
'Command1.SetFocus
End Sub
Approach 2
Code:
Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim pt As POINTAPI
If Picture1.MouseIcon.Handle <> 0 Then SetCursor 0
GetCursorPos pt
SetForegroundWindow WindowFromPoint(pt.X, pt.Y)
'Command1.SetFocus
End Sub
Both approaches work if I comment out Command1.SetFocus. Approach 2 is better since I don't need to know the Titlebar caption of the other app however when I start my app I do use FindWindow(vbNullString, "Test App") to get the handle of the app so I guess either approach is just as good as the other
-
Jul 15th, 2020, 02:16 PM
#30
Thread Starter
Hyperactive Member
Re: Subclassing application
Originally Posted by LaVolpe
Click the button, then move the cursor around the screen. To stop the capture, simply click the mouse on the target window. Pressing ESC also stops the capture.
For some added fluff. If the picturebox has its MouseIcon property set, the cursor will use that icon while capture is in play.
Note: the hidden picturebox is just used for SetCapture, mouse/keyboard events. Any control with an hWnd & those events can be used instead.
I see I have to click on the button for each window. Can it be coded to click the button only once
-
Jul 15th, 2020, 02:17 PM
#31
Re: Subclassing application
See my reply to your most recent thread on this topic
-
Jul 15th, 2020, 03:39 PM
#32
Thread Starter
Hyperactive Member
Re: Subclassing application
I'm assuming you are referring to this one
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|