Write text in new Notepad - vba

Could any one help me to write cell value to new instance of Notepad?
Here is the code I tried:
Sub a()
Dim nt As String
nt = Shell("notepad.exe", vbNormalFocus)
Print #1, ActiveSheet.Cells(1, 1).Value
Close #1
End Sub

I had answered a similar question many years ago in vbforums.com but couldn't find it so I quickly re-wrote it for you. I have commented the code so you shall not have a problem understanding it.
Like you and me, we both have names, similarly windows have “handles” (hWnd), Class etc. Once you know what that hWnd is, it is easier to interact with that window. Findwindow API finds the hWnd of a particular window by using the class name. Read up on the rest of the APIs Here
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private 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
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Const WM_SETTEXT = &HC
Private Sub Command1_Click()
Dim Ret As Long, ChildRet As Long
Dim sString As String
'~~> This is the value from the cell which
'~~> you want to send to notepad
sString = Range("A1").Value
'~~> Start Notepad
Ret = Shell("notepad.exe", vbNormalFocus)
'~~> Wait for it to load
DoEvents
'~~> Find notepad
Ret = FindWindow(vbNullString, "Untitled - Notepad")
'~~> Check if found
If Ret = 0 Then
MsgBox "Cannot find Notepad Window"
Exit Sub
End If
'~~> Find the "Edit Window" which is a child window of Notepad window
ChildRet = FindWindowEx(Ret, ByVal 0&, "Edit", vbNullString)
'~~> Send the message
SendMessage ChildRet, WM_SETTEXT, 0, ByVal sString
End Sub
To get the classnames of windows, I usually use Spy ++ or uuSpy. See this is how I got the classname "Edit" of notepad using Spy ++

Related

Close any open .PDF-file using VBA

I use 'Close_by_Caption' to close open .PDFs before they are regenerated. Previously this was easy because I could assume that always 'Foxit Reader' opened the file and that each file was opened in a separate instance of Foxit.
Newly, 'PDF-XChange Editor' should also be used to open .PDF. Now I don't know if I have to close the file with 'Close_By_Caption 'Demo - PDF-XChange Editor' or 'Demo.pdf - Foxit Reader'.
Of course I can run both commands one after the other - but surely someone will come soon who wants to use another viewer....
Is there a way to find all programs that have the word 'Demo' in 'AppCaption'? Of course, it would be even better if I would knew that either the program is a .PDF-viewer or the opened file is a .PDF...
'API Find application by full caption
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) _
As Long
'*****************************************
'API Bring Window to foreground
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
'*****************************************
'API Send message to application
Private Declare Function PostMessage _
Lib "user32" _
Alias "PostMessageA" _
( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any _
) _
As Long
'*****************************************
Const WM_CLOSE = &H10
'*****************************************
Function Close_By_Caption(AppCaption As String)
Dim hWnd As Long
hWnd = FindWindow(vbNullString, AppCaption)
If hWnd Then
'Bring to Front
SetForegroundWindow hWnd
'Close the app nicely
PostMessage hWnd, WM_CLOSE, 0&, 0&
End If
End Function
'*****************************************
Sub Test_Close()
Close_By_Caption "demo.pdf - Foxit Reader"
End Sub
'*****************************************```

VBA program to press a button (e.g 3) in microsoft calculator without using sendkeys

Will a kind soul help to provide a vba solution to press a button (e.g 3) in Microsoft calculator without using sendkeys?
Please pardon my bad work as i am new, I believe i am stuck because the windows are not unique.
If anyone has a solution to this problem, please feel free to post your answers. I welcome all alternatives other than sendkeys. Thank you very much.
Below is my failed code.
Private Const BM_CLICK = &HF5
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As Long, ByVal hWndChildAfter As Long, ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Sub Calculator()
Dim Program As String, TaskID As Double
Program = "calc.exe"
On Error Resume Next
AppActivate "Calculator"
If Err <> 0 Then
Err = 0
TaskID = Shell(Program, 1)
Do
DoEvents
hwindow2 = FindWindow(vbNullString, "Calculator")
Loop Until hwindow2 > 0
main_view = FindWindowEx(hwindow2, 0&, "Calcframe", vbNullString)
sub_view = FindWindowEx(main_view, 0&, "#32770", vbNullString)
sub_window = FindWindowEx(sub_view, 0&, "Button", vbNullString)
'Call SendMessage(sub_window, BM_CLICK, 0, ByVal0&)
If Err <> 0 Then MsgBox "Can't start " & Program
End If
End Sub

How to change printer dialog default name in VBA? (when using the handle of the printer dialog)

I've obtained some data from SAP and then I've pressed Print button from SAP in order to print the data.
Then PRINT window appeared:
I've handled it into hWnd variable (please find in LINE 1 comment);
Then I want to change default printer name into "Microsoft Print to PDF"(I don't know how to change it);
Then I've pressed button OK (please find LINE 4 comment);
Here's the code:
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private 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
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Const BM_CLICK = &HF5
'''
hWnd = FindWindow("#32770", "Print") 'LINE 1 comment;
Childhwnd = FindWindowEx(hWnd, ByVal 0&, "Button", "OK")';
'ON THIS LINE NEED TO INSERT CODE THAT CHANGES PRINTER NAME;
SendMessage Childhwnd, BM_CLICK, 0, ByVal 0& 'LINE 4 comment;
Please help me to change printer name.
PROBLEM SOLVED (for Windows 10):
Please notice that the code which was needed is:
'Declarations
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function GetWindow Lib "user32" _
(ByVal hwnd As Long, ByVal wCmd As Long) As Long
Dim i 'index to the selected item
Dim getPrintName As String ' the text of the selected item
Dim getPrintNameLength As Long ' the length of the selected item's text
Dim printPDFFound As Boolean
Dim ChildhwndPrint11 As Long
Dim ChildhwndPrint1 As Long
Dim ChildhwndPrint As Long
Dim hwndPrint As Long
Const GW_CHILD = 5
Const GW_HWNDNEXT = 2
Const CB_GETLBTEXTLEN = &H149
Const CB_GETLBTEXT = &H148
getPrintName = Space(20)
'Code Between Previous Lines LINE1 and LINE4 Used To Change The Print Name
hwndPrint = hwnd
ChildhwndPrint = GetWindow(hwndPrint, GW_CHILD)
If ChildhwndPrint = 0 Then
MsgBox "ChildhwndPrint not found."
Exit Sub
End If
ChildhwndPrint1 = GetWindow(ChildhwndPrint, GW_HWNDNEXT)
If ChildhwndPrint1 = 0 Then
MsgBox "ChildhwndPrint1 not found."
Exit Sub
End If
ChildhwndPrint11 = GetWindow(ChildhwndPrint1, GW_HWNDNEXT)
If ChildhwndPrint11 = 0 Then
MsgBox "ChildhwndPrint11 not found."
Exit Sub
End If
Call SendMessage(ChildhwndPrint11, CB_SHOWDROPDOWN, True, 0)
i = 0
printPDFFound = False
'getPrintName = ""
Do Until (i = 30) Or (getPrintName = "Microsoft Print to PDF")
Call SendMessage(ChildhwndPrint11, CB_SETCURSEL, i, 0)
'Call SendMessage(ChildhwndPrint11, CB_GETLBTEXT, 2, buffer)
getPrintNameLength = SendMessage(ChildhwndPrint11, CB_GETLBTEXTLEN, ByVal CLng(i), ByVal CLng(0))
getPrintName = Space(getPrintNameLength) & vbNullChar
getPrintNameLength = SendMessage(ChildhwndPrint11, CB_GETLBTEXT, ByVal CLng(i), ByVal getPrintName)
getPrintName = Left(getPrintName, getPrintNameLength)
'MsgBox getPrintName
If getPrintName = "Microsoft Print to PDF" Then
printPDFFound = True
End If
i = i + 1
Loop
If printPDFFound = False Then
MsgBox "<Microsoft Print to PDF> print name was not found."
Exit Sub
End If
Explaining the CODE:
I supposed that they can be maximum of 30 printers:
I've searched for the handler of the print-name drop-down= ChildhwndPrint11 (I've used a Visual Studio Tool named Spy++, separately for my macro, and this tool shows all childs of open windows on desktop) [also, please find above standard declarations in Windows API Viewer for MS Excel x64, I,ve downloaded it from the Internet)
I've showed the drop-down with CB_SHOWDROPDOWN
so I looped until meeting the maximum printer (until 30 printer) or loop until print name is "Microsoft Print to PDF"
I used scroll down drop down list that contains printer names
for every CURSEL (element from drop-down list), I extracted printer name and printer length name
if printer is found, I set printPDFFound to TRUE

VBA stopped running after executing popup button in IE?

I Have create VBA code which open website and click upload button but after executing upload button its still running same line but it should run next line of my API program for fill the popup upload form but its not running.
Below is my VBA code:
IE.Navigate "https://XXX.my.XXXX.com/home/home.jsp"
Set filee = mydoc.getElementById("file")
filee.Click 'here only paused
call uploadAPI
My API upload program:
Public Declare PtrSafe Function SendMessageByString Lib "user32" Alias "SendMessageA" ( _
ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias _
"GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Dim strBuff As String, ButCap As String
Public Const WM_SETTEXT = &HC
Public Const BM_CLICK = &HF5
Sub uploadAPI()
hw = FindWindow(vbNullString, "Choose File to Upload")
op = FindWindowEx(hw, 0&, "Button", vbNullString)
strBuff = String(GetWindowTextLength(op) + 1, Chr$(0))
GetWindowText op, strBuff, Len(strBuff)
ButCap = strBuff
Do While op <> 0
If InStr(1, ButCap, "Open") Then
OpenRet = op
Exit Do
End If
Loop
hw1 = FindWindowEx(hw, 0&, "ComboBoxEx32", vbNullString)
hw2 = FindWindowEx(hw1, 0&, "ComboBox", vbNullString)
hw3 = FindWindowEx(hw2, 0&, "Edit", vbNullString)
Call SendMessageByString(hw3, WM_SETTEXT, 0, _
"C:\Users\kk\Documents\ka\H\2015\MAY\410.pdf")
Call SendMessage(OpenRet, BM_CLICK, 0, 0)
End Sub
I have tried like this also
filee.Click : call uploadAPI
Kindly advice me to run my Upload API program after click upload Popup link.
I fixed this issue by running external VBScript which contain file path to set it on 'Choose File to Upload' pop up window using SendKeys method after that I send Enter Key to close this pop up, and this run successfully because the external VBScript will run on another thread so it will not stuck on VBA code.
Notes:
1- I dynamically create the external VBScript from VBA code and save it on Temp folder after that I run this script using WScript.Shell.Run to excutet it on another thread
1- At the beginning of the external VBScript I set 1 sec delay to be sure the 'Choose File to Upload' pop up window already opened from VBA.
And here is the complete code:
....
....
IE.Navigate "https://XXX.my.XXXX.com/home/home.jsp"
Set filee = mydoc.getElementById("file")
CompleteUploadThread MyFilePath
filee.Foucs
filee.Click
....
....
Private Sub CompleteUploadThread(ByVal fName As String)
Dim strScript As String, sFileName As String, wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
'---Create VBscript String---
strScript = "WScript.Sleep 1000" & vbCrLf & _
"Dim wsh" & vbCrLf & _
"Set wsh = CreateObject(""WScript.Shell"")" & vbCrLf & _
"wsh.SendKeys """ & fName & """" & vbCrLf & _
"wsh.SendKeys ""{ENTER}""" & vbCrLf & _
"Set wsh = Nothing"
'---Save the VBscript String to file---
sFileName = wsh.ExpandEnvironmentStrings("%Temp%") & "\zz_automation.vbs"
Open sFileName For Output As #1
Print #1, strScript
Close #1
'---Execute the VBscript file asynchronously---
wsh.Run """" & sFileName & """"
Set wsh = Nothing
End Sub

Intercepting Adobe Reader "Print to File" dialog box

I am currently attempting to print a PDF to a file using a printer set up with a file port. This works however it brings up the an Adobe Reader dialog box asking for a file path. This is something I want to avoid and want to enter the file path myself and press the OK button through code so there is no user interaction for the print operation whatsoever. I have been experimenting with some code I found but have had little success. I was hoping someone could have a look and see where I am going wrong with my code or other an alternative method to try:
I have used the following API functions:
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Public Declare Function SendDlgItemMessage Lib "user32" Alias "SendDlgItemMessageA" _
(ByVal hDlg As Long, _
ByVal nIDDlgItem As Long, _
ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As String) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As Long) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As String) As Long
Public Declare Function GetDlgItem Lib "user32" _
(ByVal hDlg As Long, _
ByVal nIDDlgItem As Long) 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
Public Const WM_SETTEXT = &HC
Public Const WM_SHOWWINDOW = &H18
Public Const WM_LBUTTONUP = &H202
Public Const WM_LBUTTONDOWN = &H201
Public Const BM_CLICK = &HF5
Public Const IDOK = 1
The main operation for finding and filling the window is held within a timer which searches for the window periodically and if it exists fills it and simulates the user pressing OK. There are a couple of lines commented out these are other methods I have tried with no success:
Dim filePath As String = String.Empty
Dim MessageSender As Long = Nothing
Dim printToFileWindowHandle As Long = Nothing
Dim textboxHandle As Long = Nothing
Dim okButtonHandle As Long = Nothing
printToFileWindowHandle = FindWindow(vbNullString, "Print to File")
If printToFileWindowHandle <> 0 Then
filePath = "C:\test.prn"
MessageSender = SendDlgItemMessage(printToFileWindowHandle, 201, WM_SETTEXT, 0, filePath)
MessageSender = SendMessage(printToFileWindowHandle, WM_SHOWWINDOW, 0, 0)
'okButtonHandle = GetDlgItem(printToFileWindowHandle, IDOK)
okButtonHandle = FindWindowEx(printToFileWindowHandle, 0, "Button", "OK")
'textboxHandle = FindWindowEx(printToFileWindowHandle, 0, "Edit", vbNullString)
'MessageSender = SendMessage(textboxHandle, WM_SETTEXT, 0, filePath)
MessageSender = SendMessage(okButtonHandle, BM_CLICK, 0, 0)
Application.DoEvents()
End if
Thanks
You can setup adobe to use a different port. For example, you can create an Adobe port to "C:\TEMP\PDFOUTPUT\" and set your Adobe printer to use it. There are also settings in the Adobe printer to control where the default location is - set this to your directory/port - and whether to overwrite/prompt user for filenames - turn this off so it just writes into the folder.
One thing we have done is leave the Adobe printer alone and setup a new local printer using the driver "Adobe PDF Converter" which gives another PDF printer. This one we will setup to go to specific folders with no user interaction. We name the printer accodingly so tha the user knows this printer will print to a location. for example "Report Printer" vs "Adobe PDF 2"