I'm trying to make a Net IME keyboard application. This app will suggest me relevant topics when typing or searching. But I can't get the location of an active textbox control from other applications.
The FindWindowEx function works though, but I'm looking for the location of an active textbox so i can change the location of the suggestion control to that textbox. Thanks!
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Integer, ByVal hWnd2 As Integer, ByVal lpsz1 As String,
ByVal lpsz2 As String) As Integer
Related
So I have a method I am using to which I can integrate powerpoint into a panel. I use the FindWindow and SetParent functions to achieve this:
Dim proc as integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long
Private Declare Function SetParent Lib "user32" Alias "SetParent" (ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As Integer
Public Sub embed_Window()
Do Until proc <> 0
proc = FindWindow(vbNullString, window_name)
Loop
SetParent(proc, Panel1.Handle)
End Sub
This part works fine for embedding another window into my panel control. My question is, how can I close the window that is now in my panel? I can no longer use the FindWindow method as it is not a window in the task bar anymore.
In order to close an opened window you need to use PostMessage:
Private Declare Auto Function PostMessage Lib "user32" (ByVal hwnd As Integer, ByVal message As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
Public Const WM_CLOSE = &H10
Public Sub CloseWindow()
PostMessage(proc, WM_CLOSE, 0, 0)
End Sub
I have an MS Access form that contains Button to open an application. The application is created using c#. I want to get the TextBox in the Form so that I will set a value on it using the MS Access project.
I am using the following code:
hwndParent = FindWindow(vbNullString, "Form1")
If hwndParent <> 0 Then
TextBoxHandle = FindWindowEx(hwndParent, 0&, "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1", vbNullString)
SendMessage TextBoxHandle, WM_SETTEXT, 0&, ByVal strText
End If
Above code is working on my workstation: Windows 10 Pro.
When I open the MS Access in windows 8. it can't find the TextBox.
TextBoxHandle always return 0 in Windows 8. I am sure that the issue is with 3rd parameter in FinWindowEx. I used spy++ from Microsoft to get the value WindowsForms10.EDIT.app.0.3cb5890_r6_ad1 cause when I try to just enter "Edit", it does not work.
Edit: Adjusted answer using information about dynamic name of class from Hans Passant.
First, we're going to declare WinAPI functions to be able to iterate through all windows and get their class name.
Declare PtrSafe Function FindWindowExW Lib "user32" (ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As LongPtr, Optional ByVal lpszWindow As LongPtr) As LongPtr
Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameW" (ByVal hWnd As LongPtr, ByVal lpClassName As LongPtr, ByVal nMaxCount As Long) As Long
Then, we're going to declare a helper function to get the class name from a hWnd:
Public Function GetWindowClass(hWnd As LongPtr) As String
Dim buf(512) As Byte
GetClassName hWnd, varPtr(buf(0)), 255
GetWindowClass = Replace(CStr(buf), Chr(0), "")
End Function
Then, we're going to iterate through all top-level windows, and return the hWnd from the one matching that class name:
Public Function getThehWnd(hWndParent) As LongPtr
Dim hWnd As LongPtr
hWnd = FindWindowExW(hWndParent)
Do While hWnd <> 0
If GetWindowClass(hWnd) Like "WindowsForms10.EDIT.app.0.*" Then
getThehWnd = hWnd
Exit Function
End If
hWnd = FindWindowExW(hWndParent, hWnd)
Loop
End Function
Old answer:
There are numerous things that can go wrong when calling WinAPI functions from VBA with strings. These include passing a string that's not terminated by a null string, and passing a string that's in the wrong encoding.
For that first case, you get unstable behavior. If the string happens to be stored somewhere where there are a lot of zero's in memory, it works. Else, it continues reading bytes from memory and appending them to the string until it finds two bytes that happen to both be 0.
The first case is easily fixed by appending a null character to the end of your string:
TextBoxHandle = FindWindowEx(hwndParent, 0&, "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" & Chr(0), vbNullString)
Note that you should probably also make that last argument optional. Entering vbNullString there passes a pointer to a zero-length string, that might also not be delimited by a null character, causing WinAPI to read subsequent characters till it finds 2 null bytes. Setting the type to LongPtr and passing 0 (the default value) passes an actual null pointer, which WinAPI expects when no string gets put in.
The second code is more difficult. I tend to use bytearrays to make sure VBA doesn't do weird things
Dim className As Byte(1024)
className = "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" 'Yes, this is valid, and assigns the first part of the bytearray to a string
FindWindowExW(hwndParent, 0&, VarPtr(className(0)))
The corresponding declaration of FindWindowExW:
Declare PtrSafe Function FindWindowExW Lib "user32" (ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As LongPtr, Optional ByVal lpszWindow As String) As LongPtr
To debug problems and identify specific windows, I use the following function to iterate through all top and child windows, instead of Spy++. This one has the advantage of running in VBA, so you can set breakpoints and watches, which means you can very easily determine the class name and parent window of all open windows:
Public Sub IterateAllWindows(Optional hWnd As LongPtr, Optional EnumLevel = 0)
Dim hwndChild As LongPtr
If hWnd <> 0 Then
Debug.Print String(EnumLevel, "-");
Debug.Print hWnd & ":";
Debug.Print GetWindowName(hWnd);
Debug.Print "(" & GetWindowClass(hWnd) & ")"
hwndChild = FindWindowExW(hWnd)
Do While hwndChild <> 0
IterateAllWindows hwndChild, EnumLevel:=EnumLevel + 1
hwndChild = FindWindowExW(hWnd, hwndChild)
Loop
Else
Dim hWndTopLevel As LongPtr
hWndTopLevel = GetTopWindow
Do While hWndTopLevel <> 0
Debug.Print String(EnumLevel, "-");
Debug.Print hWndTopLevel & ":";
Debug.Print GetWindowName(hWndTopLevel);
Debug.Print "(" & GetWindowClass(hWndTopLevel) & ")"
hwndChild = FindWindowExW(hWndTopLevel)
Do While hwndChild <> 0
IterateAllWindows hwndChild, EnumLevel:=EnumLevel + 1
hwndChild = FindWindowExW(hWndTopLevel, hwndChild)
Loop
hWndTopLevel = GetWindow(hWndTopLevel, 2)
Loop
End If
End Sub
This uses the following 2 helper functions:
Public Function GetWindowName(hWnd As LongPtr) As String
Dim buf(512) As Byte
GetWindowText hWnd, varPtr(buf(0)), 255
GetWindowName = Replace(CStr(buf), Chr(0), "")
End Function
Public Function GetWindowClass(hWnd As LongPtr) As String
Dim buf(512) As Byte
GetClassName hWnd, varPtr(buf(0)), 255
GetWindowClass = Replace(CStr(buf), Chr(0), "")
End Function
Corresponding WinAPI declarations for that sub:
Declare PtrSafe Function GetTopWindow Lib "user32" (Optional ByVal hWnd As LongPtr) As LongPtr
Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal wCmd As Integer) As LongPtr
Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextW" (ByVal hWnd As LongPtr, ByVal lpString As Any, ByVal nMaxCount As Long) As Long
Declare PtrSafe Function FindWindowExW Lib "user32" (ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As LongPtr, Optional ByVal lpszWindow As LongPtr) As LongPtr
Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameW" (ByVal hWnd As LongPtr, ByVal lpClassName As LongPtr, ByVal nMaxCount As Long) As Long
Running this function with a watch on that class name should help you identify if it's top-level or a child window, and if it's a child window, which class it belongs to. You can also modify it to return the hWnd independent of nesting (by using an If getWindowClass = "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" Then or by checking the title).
I think you should use Spy to conduct the same investigations on Windows 8 as you (presumably) did on Windows 10. Something must be different there, else your code would work.
Sidenote (because it bit me in the past): make sure you run the version of Spy whose 'bitness' (32 bit / 64 bit) matches the application you're interested in, otherwise message logging doesn't work.
Also, sorry for my previous post, it was a load of cr#p.
Edit Ah ha! Hans comments above that the class name is dynamically generated, so that's your problem. So now we know.
I want to send "{TAB}" Key to another application window(send the key to the window not to textbox).
I tried:
SendMessage(hWnd, WM_SETHOTKEY, VK_TAB, 0)
Nothing happened.
my goal is:
send tab key to my application Or other application when the application window is not in focus.
(i know that sendkey is not professional in this case there is no choice(This is the first time that I'm using it).)
I made many attempts and I always returned to the same result:
Nothing happened.
Does anyone know the answer?
SendKeys requires the application that you are sending the Keys to, to be active.
From above Link:
Use SendKeys to send keystrokes and keystroke combinations to the active application.
I order to get around this limitation you will have to resort to using the WinApi Functions.
FindWindow pInvoke.net
FindWindowEx pInvoke.net
sendMessage pInvoke.net
See this MSDN Forum Post for an example
Here is a modified example from that Posting:
Public Class Form1
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd As IntPtr, ByVal hWndChildAfterA As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
Const WM_SETTEXT As Integer = &HC
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim destination As IntPtr = FindWindow(Nothing, "Untitled - Notepad")
Dim destControl As IntPtr = FindWindowEx(destination, IntPtr.Zero, "Edit", Nothing)
SendMessage(destControl, WM_SETTEXT, IntPtr.Zero, "Hello" & vbTab & "GoodBye" & vbCrLf)
End Sub
End Class
Added an Additional Example using WM_KEYDOWN I created another small application with the Window Title set to TestForm and overrode the WndProc Method to determine if the application got the TabKey.
Sending Form
Public Class Form1
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
Const WM_KEYDOWN As Integer = &H100
Const VK_TAB As Integer = &H9
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim destination As IntPtr = FindWindow(Nothing, "TestForm")
SendMessage(destination, WM_KEYDOWN, VK_TAB, 0)
End Sub
End Class
Test Form
Put a breakpoint on MyBase.WndProc(m) and look at m to see what has been sent.
Public Class Form1
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
End Sub
End Class
Having struggled with this type of this a few times before, i would suggest a couple of things to look at.
The 1st is autoit which includes a dll you can reference from vb.net, and is very simple you use, and well documented. I tend to use that whenever i need to control a 3rd party program.
The other is the ui automation classes
See this for an example:
http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html
you need make the other window active first. check Change focus to another window in VB.NET . then use send key.
im a complet noob in vb . i have been trying to figure out how to send keystrokes back to my own application , while minimized/ Or focused on an other window.
i think i need to use PostMessageA . i read about it on forums. But its like chinese for me.
my goal is to run these little programs by the 100's on 1 pc. and they just press a key in their own application , over and over.
can someone help me out please.
thanks
i was thinking something like this
Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Const WM_CHAR As Long = &H102
PostMessage("notepad", WM_CHAR, "T", 1)
as a test. what am i doing wrong
A few things, for one you have found your code in a VB6 Forum. The size of integers has changed since then. A Long in VB6 is equivalant to an Integer in VB.Net(see this Msdn link). The second issue is that you are supplying a String to PostMessage where it is expecting the handle to your window. I would suggest that you look at this CodeProject article about how to Send strings to another application by using Windows messages
Your PostMessage declaration should look like this.
Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
I have developed some code which creates excel macros and each time after creation of new macro I want to digitally sign the macro programmatically. Can you let me know if there is any way using which I can create new digital certificates and assign those to macro programmatically. Thanks.
It is not possible to do this with VBA code. I think the best you could hope to do it would be by making Win32 API calls to simulate all the needed steps to make this happen. It would be sloppy but you could make it work. Basically, you would have open the needed windows and applications leveraging shortcut keys on the keyboard and for selecting the correct certs.
Some example code would be
Public Declare Function SetFocus Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, _
ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Sub SetFocusFormulaBar()
SetFocus FindWindowEx(FindWindow("XLMAIN", Application.Caption), _
0&, "EXCEL<", vbNullString)
End Sub
http://www.cpearson.com/excel/FormulaBarShortcut.htm