spyoungtech / ahk

Python wrapper for AutoHotkey with full type support. Harness the automation power of AutoHotkey with the beauty of Python.
MIT License
885 stars 66 forks source link

Why is window.id a string? #269

Closed michaelmesser closed 7 months ago

michaelmesser commented 8 months ago

I would expect it to be an int.

spyoungtech commented 8 months ago

Hi @michaelmesser thanks for the question.

It may not be a satisfying answer, but the type is a str mostly for arbitrary and historical reasons (the internal APIs of the library use strings to communicate to/from AHK exclusively) and there hasn't been a compelling reason to introduce a potentially-breaking change to make it an int or anything else instead. Specifically, because window handles aren't really used in the same way as integers anyhow, I'm not sure users would benefit from such a change. That is to say: I don't think there is any case where you would do integer arithmetic, like add/subtract/divide on a window handle.

One other possible argument for the string type (though perhaps not initially intentional) is that it produces behavior that more closely resembles the behavior you would get in AutoHotkey scripts:

; AutoHotkey v1
WinGet, hwnd, ID, Untitled - Notepad

; Shows the ID in a message box as a hexadecimal string
MsgBox,% hwnd 

This can be reflected in a python script:

win = ahk.win_get(title='Untitled - Notepad')
ahk.msg_box(win.id)  # shows the same hexadecimal string as the AutoHotkey version

If win.id were an integer, the ultimate behavior would be different.

It's perhaps arguable int would be a more correct type to use as opposed to str and, if we didn't care about backwards compatibility, it may be more reasonable to use int instead of str. In a perfectly pedantic world, window.id (and other uses of window handles) would probably use a Python type that reflects the HWND type (an alias for HANDLE, which is an alias for PVOID) from the underlying Windows APIs, as pywin32 does with its PyHandle type, since HWND pointers are meant to be opaque pointers, as the underlying PVOID type definition implies.

So, for now, I think the string probably serves just as well for its intended purposes and, in absence of a compelling use-case behind such a change, I would lean in favor of preserving compatibility than making the change for correctness sake alone.

michaelmesser commented 8 months ago

AHKv2 says WinGetID returns an integer. https://www.autohotkey.com/docs/v2/lib/WinGetID.htm Also https://learn.microsoft.com/en-us/dotnet/api/envdte.window.hwnd?view=visualstudiosdk-2022#envdte-window-hwnd uses int for the hwnd as well.

spyoungtech commented 8 months ago

Yeah. In AutoHotkey v1, HWNDs are assigned as strings. In V2, it just takes the HWND pointer and casts it to an integer. You can read more about that here. In the referenced C++ code, HWND refers to a HANDLE, which is a PVOID type, as documented here.

So, yes, it's true AutoHotkey v2 returns an integer. There are also some other inconsistencies like this that exist. Something to keep in mind is that most of the methods in this library were written before AHK v2 was released. Also, this library supports both versions of AutoHotkey simultaneously. So, when such differences between AHKv1 and AHKv2 arise, the library has a decision to make and, in the case of return types, it tends to not consider what AHKv2 does and favor interoperability of the Python API between AutoHotkey versions and backwards compatiblity with previous versions of this library (written originally for v1 only) and thusly, return types will usually have the v1 behavior for purpose of backwards compatibility.

The Python API could just as easily smooth this difference over in the other direction and always cast to an integer, like the V2 behavior, but it just so happens that the v1 behavior was implemented first and did not cast the hexadecimal string to an int, and changing this now would be a potentially breaking change. Because of this, the Python return value will likely stay a string, at least until the next major version of this library, unless there's some compelling reason to change it sooner.

Like I mentioned, I know that answer may not be satisfying, it's just how things happened to land over the course of development, which was initially against AHKv1 only.

Hope that helps explain things.

michaelmesser commented 8 months ago

The explanation that this library provides an AHKv1 Python API to AHKv1 and AHKv2 makes sense.

That's not quite the same thing as HWND in the win32 sense, as found in the Win32 GetWindow function, or other APIs used by AutoHotkey.

Are they not the same? I was able to match the Visual Studio hwnd to an AHK id to find the AHK window of a Visual Studio solution.

spyoungtech commented 7 months ago

Glad that helps explain it.

And yeah, they represent the same thing, but are different types. Pointers can be converted to integers and vice-versa, though there are problems with that approach, as the maintainer of AutoHotkey pointed out in the link shared above and discussed briefly here. But it's a small distinction, I suppose 🤷‍♂️