Close any open .PDF-file using VBA - 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
'*****************************************```

Related

Open ODBC from button

I have an Access 2019 database and want to include a button to open the ODBC administrator. The event procedure on click is written as
Private Sub Command210_Click()
Dim RetVal
RetVal = Shell("odbcad32.exe", 1)
End Sub
however this does not work, if I replace odbcad32.exe with notepad.exe it will open notepad on clicking but odbcad32 does not work - any ideas why?
Based on one of my previous answers you could call the ODBC administrator like that
Option Compare Database
Option Explicit
Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias _
"ShellExecuteA" (ByVal hwnd As LongPtr, _
ByVal lpOperation As String, _
ByVal lpFile As String, _
ByVal lpParameters As String, _
ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As LongPtr
Private Declare PtrSafe Function Wow64EnableWow64FsRedirection _
Lib "kernel32.dll" (ByVal Enable As Boolean) As Boolean
Private Sub RunODBC_on64Bit()
Const SW_SHOWNORMAL = 1
On Error Resume Next
Wow64EnableWow64FsRedirection False
ShellExecute 0, "open", "odbcad32.exe", "", "C:\windows\system32\odbcad32.exe", SW_SHOWNORMAL
Wow64EnableWow64FsRedirection True
End Sub
I got it in the end - I replaced line
RetVal = Shell("odbcad32.exe", 1)
with
RetVal = Shell("Explorer.exe ""C:\Windows\SysWOW64\odbcad32.exe""", 1)
and that sorted it.

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

Can some one explain what this is actually doing?

I have been searching for a way to close windows explorer using vba, and i have found something that works. However i actually have no idea what it is actually doing, or what any of it means. Could someone please explain what is happening below?
Private Const CLOSE_WIN = &H10
Dim Hwnd As Long
Private Declare Function apiFindWindow _
Lib "user32" Alias "FindWindowA" _
(ByVal lpClassname As String, _
ByVal lpWindowName As String) _
As Long
Private Declare Function apiPostMessage _
Lib "user32" Alias "PostMessageA" _
(ByVal Hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) _
As Long
Hwnd = apiFindWindow("CabinetWClass", vbNullString)
Dim retval As Long
If (Hwnd) Then
retval = apiPostMessage(Hwnd, CLOSE_WIN, 0, ByVal 0&)
End If
Thank You

Write text in new Notepad

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 ++

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"