My application (script) is a robot that automates certain user actions in SAPGUI .
Due to the fact that some operations on the part of SAP are delayed for minutes, I have to create several sessions inside SAPGUI to save time. Each session in SAPGUI is one window (with one handle) inside which the forms are replaced. In the process of work, dialog boxes belonging to this window are called.
Threads are synchronized via threading.RLock() . This is basically a simple classic multi-stream. Naturally, every time you enter the acquire()/release() block, you have to switch the focus to the required session window, which the set_focus() method basically copes with until it comes to dialog boxes.

The following multi-thread wait function is used:

 def wait_dialog(self, name, timeout=SAP_OPERATION_TIMEOUT): ActionLogger.log(f'Wait for "{name}"') def check_state(window): with lock: new_window = window.window(title_re=name, class_name=class_names.dialog) new_window.verify_actionable() return(new_window) new_window = pywinauto.timings.wait_until_passes(timeout, 0.1, lambda: check_state(self.window)) return(SapWindow(new_window)) 

where lock is an instance of the RLock class.

So in the next piece of code, set_focus() makes the dialog active and supposedly focused ( is_active() == True ), but there is no focus on any element and type_keys() cannot do anything and does not issue errors. While Button2.click() works fine:

 dlg4 = sess.wait_dialog(sess.name, timeout=SAPbot.SAP_SPOOL_TIMEOUT) with SAPbot.lock: dlg4.set_focus() dlg4.window.type_keys(filename + '{TAB 6}' + workDir) dlg4.window.Button2.click() 

If you replace dlg4.set_focus() with SetForegroundWindow(dlg4.window.wrapper_object().handle) , then everything will work fine under Windows 10 , but not on Windows Server 2012 via RDP . Under server Windows, when executing by the third thread (why I’m not sure why during the third run) of this code, the SAPGUI icon flashes and the same story as with set_focus() , only the window does not even appear. I understand that blinking means that the Windows cannot switch the focus because I supposedly work with another window, in fact there isn’t - access to the windows is atomic via RLock , as I already wrote above.

But there is one interesting solution to LockSetForegroundWindow : if you use the following code for focusing, everything works correctly:

 if not dlg4.window.is_active(): dlg4.window.type_keys('{VK_MENU}') SetForegroundWindow(dlg4.window.wrapper_object().handle) 
  • In general, SetForegroundWindow is called inside .set_focus() (this is probably understandable). But the feature of Python is that its threads are not executed in parallel, but in the time-sharing mode. Accordingly, context switches occur at arbitrary moments. If the action under the focus is not one, then the switch may occur in the middle and the focus will go to another window in another thread. LockSetForegroundWindow can really solve part of the problem, but not 100% of the time. - Vasily Ryabov
  • The ideal option is to do without type_keys() and click_input() , so that focus is not required. For example, for backend="win32" there is a .send_keystrokes() method that uses WM_KEYDOWN and WM_KEYUP , as well as WM_ACTIVATE . It also supports keyboard shortcuts (although their work is not guaranteed for all applications), and even uses the active layout (you can try ^N on the Russian layout, by the way, it will suddenly work, although it is unlikely). - Vasily Ryabov
  • Well, or in the stream under the lock, everything that is atomic (between the focus and the end of the atomic sequence) should be, and not just the expectation of .verify_actionable() . - Vasily Ryabov

0