WinAppDriver icon indicating copy to clipboard operation
WinAppDriver copied to clipboard

Which alternative property can be used for attaching to app's window

Open vnabokit opened this issue 6 years ago • 4 comments

Hi,

I'm using Appium, WinAppDriver 1.1, Python, Windows 10. Need to test a Qt application. For the first launch the app is shown as a dialog with 'Continue' and 'Cancel' buttons. The problem is inspect.exe doesn't provide NativeWindowHandle attribute for the tested app (to be more exact NativeWindowHandle = 0 due to console log). How is it possible to catch of the app?

Maybe the answer is to use Window property in MSAA mode (this property exists and has value according to inspect.exe output). But I did not find how to turn WinAppDriver to such mode.

There are two screens from inspect.exe (one screen for 'UI Automation' mode, another for 'MSAA' mode).

UI Automation:

How found:	Mouse move (652,343)
	hwnd=0x03820700 32bit class="Qt5QWindowIcon" style=0x96080000 ex=0x80000
BoundingRectangle:	{l:359 t:272 r:663 b:495}
ProcessId:	908
Name:	""
AccessKey:	""
HasKeyboardFocus:	false
IsKeyboardFocusable:	false
IsEnabled:	true
HelpText:	""
IsPassword:	false
IsOffscreen:	false
ProviderDescription:	"[pid:908,providerId:0x0 Main(parent link):Microsoft: MSAA Proxy (unmanaged:uiautomationcore.dll)]"
IsDockPatternAvailable:	false
IsExpandCollapsePatternAvailable:	false
IsGridItemPatternAvailable:	false
IsGridPatternAvailable:	false
IsInvokePatternAvailable:	false
IsMultipleViewPatternAvailable:	false
IsRangeValuePatternAvailable:	false
IsScrollPatternAvailable:	false
IsScrollItemPatternAvailable:	false
IsSelectionItemPatternAvailable:	false
IsSelectionPatternAvailable:	false
IsTablePatternAvailable:	false
IsTableItemPatternAvailable:	false
IsTextPatternAvailable:	false
IsTogglePatternAvailable:	false
IsTransformPatternAvailable:	false
IsValuePatternAvailable:	false
IsWindowPatternAvailable:	false
IsItemContainerPatternAvailable:	false
IsVirtualizedItemPatternAvailable:	false
FirstChild:	"" 
LastChild:	"" 
Next:	[null]
Previous:	[null]
Other Props:	Object has no additional properties
Children:	"" 
	"" 
	"" 
Ancestors:	"<font color="red"><b>WARNING!</b></font>" pane
	"Desktop 1" pane
	[ No Parent ]

MSAA:

How found:	Mouse move (652,417)
	hwnd=0x03820700 32bit class="Qt5QWindowIcon" style=0x96080000 ex=0x80000
ChildId:	0
Interfaces:	IOleWindow
Impl:	Remote native IAccessible
Name:	[null]
Value:	[null]
Role:	client (0xA)
State:	normal (0x0)
Location:	{l:359, t:272, w:304, h:223}
Selection:	
Description:	[null]
Kbshortcut:	[null]
DefAction:	[null]
Help:	[null]
HelpTopic:	""
ChildCount:	3
Window:	0x3820700
FirstChild:	none : client : normal
LastChild:	none : client : normal
Next:	[null]
Previous:	[null]
Left:	[null]
Up:	[null]
Right:	[null]
Down:	[null]
Other Props:	Object has no additional properties
Children:	none : client : normal
	none : client : normal
	none : client : normal
Ancestors:	"<font color="red"><b>WARNING!</b></font>" : dialog : sizeable,moveable
	"My app" : application : normal
	[ No Parent ]

Also, there is the test:

import unittest
from appium import webdriver

class SimpleTests(unittest.TestCase):
	def setUp(self):
		path_to_app = "C:\\Program Files (x86)\\My app\\myapp.exe"
		app_name = "My app"
		desired_caps = {}
		desired_caps["app"] = path_to_app
		desired_caps["platformName"] = "Windows"
		desired_caps["deviceName"] = "WindowsPC"
		try:
			self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
		except:
			print("An exception is caught. Possible cannot locate the app. Continue execution.")
			# Create desktop session
			desired_caps["app"] = "Root"
			desktop_session = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
			app_window = desktop_session.find_element_by_name(app_name)
			# Trying to find a handle of the app
			app_top_level_handle = app_window.get_attribute("NativeWindowHandle")
			app_top_level_handle = hex(int(app_top_level_handle))
			desired_caps2 = {}
			desired_caps2["appTopLevelWindow"] = app_top_level_handle
			desired_caps2["platformName"] = "Windows"
			desired_caps2["deviceName"] = "WindowsPC"
			# Initialize app session
			self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps2)

	def tearDown(self):
		self.driver.quit()

	def test_simple_actions(self):
		#just exit from the app by clicking cancel button
		self.driver.find_element_by_name("Cancel").click()

if __name__ == '__main__':
	suite = unittest.TestLoader().loadTestsFromTestCase(SimpleTests)
	unittest.TextTestRunner(verbosity=2).run(suite)

Originally posted by @vnabokit in https://github.com/Microsoft/WinAppDriver/issues/283#issuecomment-456845359

vnabokit avatar Jan 25 '19 13:01 vnabokit

It seems, no way to attach to existed app's window if impossible to get capability appTopLevelWindow.

WinAppDriver accepts either app or appTopLevelWindow capability (due to my console log): [selenium.common.exceptions.InvalidArgumentException: Message: Bad capabilities. Specify either app or appTopLevelWindow to create a session]. appTopLevelWindow can be taken only from NativeWindowHandle app's property.

Also, WinAppDriver cannot interact with properties from MSAA mode: https://github.com/Microsoft/WinAppDriver/issues/363

Still hope some workaround exists but I've finished to explore abilities WinAppDriver for testing Qt apps.

Thanks! :)

vnabokit avatar Jan 25 '19 16:01 vnabokit

I too am looking for an alternative capability. Since Microsoft choose to terminate CodedUI without providing an equal alternative I am stuck trying to make the WinAppDriver work, unfortunately it is proving difficult to attach to existing windows by titles or by anything else that is consistent. And the NativeWindowHandle changes on every run.

jeremeguenther avatar Jan 31 '19 00:01 jeremeguenther

For a bit more clarification, Inspect does show the NativeWindowHandle for the IE browser I want to hook into. And using System.Diagnostics.Process.GetProcessesByName("iexplorer") and looping through the results I can actually get one.

Unfortunately, when I pass this value, which is actually called MainWindowHandle on the process, it still blows up saying "cannot find active window specified by capabilities: appTopLevelWindow"

EDIT: After looking through all the test examples I modified the code to process.MainWindowHandle.ToString("x"), I had not been using the ToString("x") prior. This worked. Not a great solution, but it is functioning.

jeremeguenther avatar Jan 31 '19 01:01 jeremeguenther

Followed a detour to solve my problem (almost same as yours) and it helped

``import pygetwindow as gw import keyboard import time

time.sleep(5) dialog_title = "Your MSAA Dialog Title"
dialog_window = gw.getWindowsWithTitle(dialog_title)

if dialog_window: dialog_window[0].activate() keyboard.press_and_release('enter') else: print(f"Window with title '{dialog_title}' not found.")``

Syed-Irtza avatar Aug 22 '23 08:08 Syed-Irtza