Getting Python and AutoIT to work together using win32com: what's up with those window handles?
Asked Answered
S

2

10

I have a program with a GUI interface whose initial set-up I need to do manually. AutoIt has been superbly helpful for that so far, as it provides very easy ways to work even with complex-to-access GUI objects (drop down lists, appear-on-hover menus, etc.).

However, the script that I'll eventually be needing in order to do the program setup will need to be passed a large array/list of variables - there are a lot of different settings that need to be changed.

I've set up the logic for deciding what these set-up variables will be using a Python script. Now I'm trying to figure out how to get Python and AutoIt to talk to each other.

Calling a custom AutoIt script from the command line using Python is mostly out of the question because of the large number of variables that would need to be passed. Doesn't feel pretty. I could try and have Python write an AutoIt "key file", which AutoIt could then read in order to set its initial variables, but I'd like to make sure I've exhausted all options for Python to work directly with AutoIt first.

To that end, I've been trying to use Python along with the win32com library to interface with AutoIt. Things seem to be working well - as long as I reference windows/menus/objects by their string titles rather than their (memory?) handles. This is problematic, as my set-up scripts might be running in parallel, setting up two or more separate files at the same time. If this is the case, opening up a box with a title string "Open file..." in each file at the same time might confuse things.

The obvious way to get around this in AutoIt is to work with the "handles" of the objects in question, which I believe are memory addresses of some kind, rather than their string titles. I'm guessing these are memory addresses as the AutoIt Window Info tool, when pointed to a particular Window/GUI object option lists a hexadecimal number as the object's handle value.

AutoIt has a suite of functions that get the handles of windows, menus, etc. They are implemented in the AutoIt COM dll, but I've not been able to get them to work in Python. The handle functions return a unicode object in Python, not a hex string as in AutoIt. I think this is the reason why functions which then try to use this "handle" in Python don't work.

Example:

autoIt = win32com.client.Dispatch("AutoItX3.Control")
windowHandle = autoIt.WinGetHandle(knownWindowTitle)
returnedWindowTitle = autoIt.WinGetTitle(windowHandle)

Usually, returnedWindowTitle and knownWindowTitle do not match as returnedWindowTitle always seems to be "0". hat's happening here?

Are there other ways for me to call custom AutoIt functions apart from using win32com, the command line, or using an AutoIt keyfile?

Thanks for your help.

EDIT: I forgot to mention that the unicode strings do in fact match the hexadecimal numbers that I get when I print out the handle variable in AutoIt.

For example, in Python, the handle variable when printed out gives me u'000C0326'. In AutoIt it gives me '0x000C0326'.

EDIT: Some trials based on Mat's suggestions:

In: autoIt = win32com.client.Dispatch("AutoItX3.Control")
In: mainWindowTitle = "Untitled"
In: mainWindowHandle = autoIt.WinGetHandle(mainWindowTitle)
In: mainWindowHandle
Out: u'000204AC'
In: testHandle = int(mainWindowHandle, 16)
In: testHandle
Out: 132268
In: autoIt.WinGetTitle(testHandle)
Out: u'0'

EDIT: I found out the type of the window handle object: it's a Microsoft HWND object. AutoIt has a function that can "convert" a base 16 number into an HWND object (i.e. find the HWND object with that base 16 number memory/handle/etc.). Just my luck that they didn't put that function into AutoItX (the COM dll). So, if I really want to run with this, I'll have to try and figure out how to return whatever object it is that's pointed to by the base 16 address, and then transfer it the right way to AutoItX? I'm probably sounding very confused, because all of this is not super clear in my head right now.

Subconscious answered 13/1, 2012 at 0:15 Comment(7)
Could be a problem with types... I am not a python user, but can you run some checks on the type of the windowHandle variable. It should be an integer.Belenbelesprit
@Mat In Python, the type of the windowHandle variable is a unicode string (eg. u'000C0326'). Should the integer be the hexadecimal (eg. '0x000C0326') number (in integer form) that AutoIt prints out if asked to output the windowHandle variable?Subconscious
Yes. If you pass a string then AutoIt will try to find a window whose title matches that string. In this case no window is found as no window exists with a title "000C0326". From my short time with python you'd want to try autoIt.WinGetTitle(int(windowHandle, 16)) or something along those lines, you'll know better than me. If that doesn't work, then you'll need to find some way to get an autoit handle type in python.Belenbelesprit
@Mat I've tried that out before as well, and I didn't have any luck. I've put the results of my trials as a new edit in the original post. Have a look at them if you have some time?Subconscious
@Mat I guess the crux of the matter really is this: what is the type of the AutoIt handle? If I can figure that out, I can figure out some way of replicating it in Python. The AutoIt documentation doesn't give me any hints as to what the type is, but somehow - based on the trials - I doubt it's just an integer.Subconscious
Can you follow out: from ctypes.wintypes import HWND It's a c void pointer, if you then do: print (HWND) ...so not sure if that will get you anywhere, as far as determining the structure.Dote
A handle is a pointer. With python that's an integer unless there is a pointer type. A base 16 number is still an integer, just written differently. I can only recommend you post on the AutoIt forums (there is a section dedicated to AutoItX) and see what advice people with the time and experience can give you. Although I have used AutoIt for a long time now, I have never used AutoItX, so there may be something I am missing.Belenbelesprit
D
2

For the sake of search, I'll post the solution that I've found.

Here's the code:

In: autoIt = win32com.client.Dispatch("AutoItX3.Control")
In: autoIt.AutoItSetOption("WinTitleMatchMode", 4)
In: mainWindowTitle = "Untitled"
In: mainWindowHandle = autoIt.WinGetHandle(mainWindowTitle)
In: mainWindowHandle
Out: u'000204AC'
In: testHandle = "[HANDLE:%s]" % mainWindowHandle
In: autoIt.WinGetTitle(testHandle)
Out: u'Untitled - Notepad'

autoIt.AutoItSetOption("WinTitleMatchMode", 4) tells autoit to use advanced title matching, which allows us to specify a window handle with the [HANDLE:000204AC] string.

No need for the actual window handle here.

By the way, I discovered this solution by stumbling on this forum post. I've found that it's often helpful to not restrict my searches to the specific language I'm looking for. Most of the time, a solution can be found in a different language which can be easily ported to the language of your choice.

Delois answered 22/6, 2012 at 2:40 Comment(0)
K
2

The type of window handles is string. The reason for that is WinList returns both the window handle and the window title. A title cannot be fit into a handle type, but a handle can be fit into a string type (for title). My guess is that they took that design descision and applied it to other functions as well. If you look at the documentation for WinGetHandle it will tell you the return type for a handle: It's a string.

AutoIt has a function that can "convert" a base 16 number into an HWND object.

Exactly! That's the key. AutoIt does this for you. You're trying to convert the handle into something useful for AutoIt, but AutoIt can already use those handles stored as strings.

Your test should be:

In: autoIt = win32com.client.Dispatch("AutoItX3.Control")
In: mainWindowTitle = "Untitled"
In: mainWindowHandle = autoIt.WinGetHandle(mainWindowTitle)
In: mainWindowHandle
Out: u'000204AC'
In: autoIt.WinGetTitle(mainWindowHandle)
Out: u'Untitled - Notepad'

When you want to use that handle in other libraries, you may run into some problems. I would recommend then that you try to parse the number like you did in your tests, and pass it along. AutoIt is 'smart' enough to figure out what to do in most cases, maybe the library isn't though.

Kamala answered 19/1, 2012 at 13:1 Comment(1)
Hi Manadar, thanks for your response, but if you look at my first example, you'll see that I've tried out exactly what you mentioned. It spits out the unicode string as the Window handle, but it's unable to return the window title using that unicode string. As I mentioned in my last edit, I think that while AutoIt has a function that can convert a unicode string into a HWND object, AutoItX does not make that function available through the COM dll, so the functions accessible through COM have to be called only through the use of window titles (regardless of how they may handle it internally).Subconscious
D
2

For the sake of search, I'll post the solution that I've found.

Here's the code:

In: autoIt = win32com.client.Dispatch("AutoItX3.Control")
In: autoIt.AutoItSetOption("WinTitleMatchMode", 4)
In: mainWindowTitle = "Untitled"
In: mainWindowHandle = autoIt.WinGetHandle(mainWindowTitle)
In: mainWindowHandle
Out: u'000204AC'
In: testHandle = "[HANDLE:%s]" % mainWindowHandle
In: autoIt.WinGetTitle(testHandle)
Out: u'Untitled - Notepad'

autoIt.AutoItSetOption("WinTitleMatchMode", 4) tells autoit to use advanced title matching, which allows us to specify a window handle with the [HANDLE:000204AC] string.

No need for the actual window handle here.

By the way, I discovered this solution by stumbling on this forum post. I've found that it's often helpful to not restrict my searches to the specific language I'm looking for. Most of the time, a solution can be found in a different language which can be easily ported to the language of your choice.

Delois answered 22/6, 2012 at 2:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.