0

I have searched a lot for this topic but I didn't find a solution.

I am using IE to login a website and at some point there is a print dialogbox and I need to click on the Print button and then the subsequent 'Save button'; in addition I need to add the pdf filename for the save.

I solved clicking the Print button with:

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

Sub ClickPrintButtonWindowsAPI()
    Dim hw As Long, op As Long
    hw = FindWindow(vbNullString, "Print")
    op = FindWindowEx(hw, 0&, "Button", vbNullString)
    Call SendMessage(op, BM_CLICK, 0, 0)
End Sub

But a new window opened asking for a file name and Save button.

  • The printer is "Microsoft Print to PDF" so the output would be a PDF file.

** I have downloaded spy++ but I have no experience at using it. After many tries I could select the file name field in the dialog box and here's a snapshot

enter image description here

Now I tried such lines

Dim hw1 As Long
hw1 = FindWindow(vbNullString, "Save Print Output As")
Dim hw2 As Long, hw3 As Long
hw2 = FindWindowEx(hw1, 0&, "DUIViewWndClassName", vbNullString)
Dim hw4 As Long, hw5 As Long, hw6 As Long
hw3 = FindWindowEx(hw2, 0&, "DirectUIHWND", vbNullString)
hw4 = FindWindowEx(hw3, 0&, "FloatNotifySink", vbNullString)
hw5 = FindWindowEx(hw4, 0&, "ComboBox", vbNullString)
hw6 = FindWindowEx(hw5, 0&, "Edit", vbNullString)
Call SendMessageByString(hw6, &HC, 0, "Sample Text")
Dim sv As Long
sv = FindWindowEx(hw2, 0&, "Save", vbNullString)
Call SendMessage(sv, BM_CLICK, 0, 0)

A question: is there a way to make such a map simpler?

I am stuck now at clicking on the Save button and entering the file name. Can you someone assist me with this?

YasserKhalil
  • 9,138
  • 7
  • 36
  • 95
  • Hiya! What have you tried? Have you looked at the FindWindow APIs for example? I think, once upon a time, I pointed you to an example regarding using them for interacting with dialogue boxes [here](https://stackoverflow.com/questions/52890015/vba-ie-automation-save-as-pdf-isnt-working/52906215#52906215) – QHarr Jan 10 '21 at 10:07
  • Yes you do and I had a look. But in fact I am totally lost in that aspect. How can I specify the window then specify the `Print` button then finally to click it? Maybe simple for you but as for me I am so weak at windows API. – YasserKhalil Jan 10 '21 at 10:10
  • I have found a code that enables me to click the Print button. Can you please have a look at the updated question? – YasserKhalil Jan 10 '21 at 10:27
  • you should update the question and title as it is now about a different object though similar process. Also, include what you have tried in terms of using the APIs to grab that window and move through it. The print bit is not particularly relevant now except if you use it as a starter for the new task. – QHarr Jan 10 '21 at 11:16
  • I already updated the question and the title and put all my tries till now. As for the last part is there a simpler way to shorten the reference to the needed object? – YasserKhalil Jan 10 '21 at 11:24
  • I couldn't locate the Save button I tried `sv = FindWindowEx(hw2, 0&, "Save", vbNullString)` but sv returns 0. – YasserKhalil Jan 10 '21 at 11:26
  • 1
    Is there a reason you are avoiding [UI Automation](https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32)? – IInspectable Jan 10 '21 at 11:38
  • I have done an initial edit to show you what I meant about updating your question and title. Please feel free to rollback if not suitable. Also, it is confusing that you say in the question you located the Save dialogue but in the comments say you haven't. – QHarr Jan 10 '21 at 11:40
  • I am trying to solve it step by step to learn how the process works. I am not sure of the approach I have used but till now the code works only if I used F8 to run step by step. – YasserKhalil Jan 10 '21 at 11:41
  • Because when your code gets the save button, the new dialog has not been created and generated, but when single-step debugging, it has enough time to generate dialog. – Drake Wu Jan 11 '21 at 09:55
  • 1
    Using the wait function is not reliable, you should use [WinEvents](https://learn.microsoft.com/en-us/windows/win32/winauto/what-are-winevents) to get the dialog creation event, and use [UIAutomation](https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32) to automatically click the save button. Check this [similar question](https://stackoverflow.com/a/65575246/10611792) and answer – Drake Wu Jan 11 '21 at 09:58

1 Answers1

0

Here's my final solution but I welcome any ideas

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 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 Function GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Sub ClickPrintButtonWindowsAPI()
    Dim hw As Long, op As Long
    hw = FindWindow(vbNullString, "Print")
    op = FindWindowEx(hw, 0&, "Button", vbNullString)
    Call SendMessage(op, BM_CLICK, 0, 0)
    
    Dim hw1 As Long
    hw1 = FindWindow(vbNullString, "Save Print Output As")
    Dim hw2 As Long, hw3 As Long
    hw2 = FindWindowEx(hw1, 0&, "DUIViewWndClassName", vbNullString)
    Dim hw4 As Long, hw5 As Long, hw6 As Long
    hw3 = FindWindowEx(hw2, 0&, "DirectUIHWND", vbNullString)
    hw4 = FindWindowEx(hw3, 0&, "FloatNotifySink", vbNullString)
    hw5 = FindWindowEx(hw4, 0&, "ComboBox", vbNullString)
    hw6 = FindWindowEx(hw5, 0&, "Edit", vbNullString)
    Call SendMessageByString(hw6, &HC, 0, "Sample Text")
    Dim sv As Long
    sv = FindWindowEx(hw1, 0, "Button", "&Save")
    Call SendMessage(sv, BM_CLICK, 0, 0)
End Sub
YasserKhalil
  • 9,138
  • 7
  • 36
  • 95
  • This works only if I am using F8 to run the code step by step. Any ideas? – YasserKhalil Jan 10 '21 at 11:39
  • What does it usually mean when F8 solves the problem? – QHarr Jan 10 '21 at 11:40
  • I think I have to put waiting lines .. Maybe! – YasserKhalil Jan 10 '21 at 11:42
  • ^^bingo... or some condition to be met. Conditions are usually more reliable than hard-coded wait times. Worth mentioning what stage fails when F5/Run. – QHarr Jan 10 '21 at 11:42
  • What conditions do you mean? Is there a better way to do that .. Can you guide me if possible because I trust in your approaches? – YasserKhalil Jan 10 '21 at 11:44
  • It depends where your code is failing. For example, you might need a timed loop to get the window handle, or for your text to be input, or for a file to have downloaded (always tricky)..... In my experience, such automation fails on the save because the program completes before file downloaded or not enough time given for dialogues. There are lots of existing questions that discuss these problems. – QHarr Jan 10 '21 at 11:45
  • When trying the code I noticed that the first window `Print` dialogbox works fine but when the second window `Save Print Output As` appears the code doesn't work. It seems the problem is that I am dealing with two windows (I am not sure)!! – YasserKhalil Jan 10 '21 at 11:48
  • Solved finally by putting this line `Application.Wait Now + TimeValue("00:00:05")` after this line `Call SendMessage(op, BM_CLICK, 0, 0)`. Thank you very much for your great assistance. I hope you provide a more compact code from my code or evene a better approach. – YasserKhalil Jan 10 '21 at 11:50
  • 1
    In using UI Automation or any sort of click automation which uses winAPI/AA/UIAutomation etc, the key concept will be to await the element to appear on screen. You should have 'element-awaiting' functions set up as standard – MacroMarc Jan 10 '21 at 16:08