Re: it cannot find a User32 function
eryk sun <[email protected]> Thu, 4 May 2017 02:53:22 +0000
| Newsgroups | gmane.comp.python.ctypes |
|---|---|
| Message-ID | <CACL+1asw3WmBV-8GoJd5tdDbwsMWgBUaaY2WpGse0ZV373Pn4w@mail.gmail.com> |
On Wed, May 3, 2017 at 11:19 PM, Michael C <[email protected]> wrote: > Hi all, I am trying to move the location and change the size of a window, > but the User32.FindWindow function cannot be found! > > AttributeError: function 'FindWindow' not found Here's the prototype from MSDN [1]: HWND WINAPI FindWindow( _In_opt_ LPCTSTR lpClassName, _In_opt_ LPCTSTR lpWindowName ); [1]: https://msdn.microsoft.com/en-us/library/ms633499 The string type is "LPCTSTR". That's a [L]ong [P]ointer (just a pointer nowadays; there's no segmented addressing) to a [C]onstant [T]CHAR [STR]ing. A TCHAR is a [T]ext CHAR ("T" could also mean "typed) that depends on defining the UNICODE macro in C. If the macro is defined, a TCHAR is a two-byte WCHAR, i.e. a [W]ide CHAR, and otherwise it's a single-byte CHAR. The macro also determines the definition of the "T" types. If defined, an LPCTSTR is in turn defined as an LPCWSTR, and otherwise it's an LPCSTR. Historically this split is because single-byte and double-byte code pages were the way text was localized in the 80s and 90s in MS-DOS and versions of Windows that extend DOS, i.e. Windows 3.x and 9x. The newer NT system (developed in 1988-93 and released in 1993 as Windows NT 3.1) is based on Unicode internally and uses 16-bit WCHARs. When the NT designers updated the existing Win16 API to Win32, they needed to support porting legacy Windows applications that use code pages, so they developed this typing system based on the UNICODE macro. Also, when the Win32 API itself was ported to DOS-based Windows 95, there was only rudimentary Unicode support, so for a long time even new applications were designed around code pages. Starting with Windows XP, all versions of Windows use the NT kernel, and by Windows Vista new functions appear that only support Unicode (e.g. SetFileInformationByHandle). What the FindWindow prototype is not showing is that when Windows is built it has to support programs compiled for both CHAR and WCHAR. For most functions Windows NT uses WCHAR, so the CHAR implementation decodes the string arguments and calls a common internal function. The code page it decodes from is usually the system-locale ANSI codepage (e.g. 1252 in the U.S. and Western Europe), with a couple of exceptions. The file-system 'ANSI' API can be set to the system-locale OEM codepage (e.g. 850 in Western Europe) via SetFileApisToOEM. Also, the console 'ANSI' API defaults to OEM and can be set to any single-byte or multi-byte code page separately for input and screen buffers via SetConsoleCP and SetConsoleOutputCP. Whenever possible you should prefer calling the [W]ide-character function to avoid writing locale-dependent code. That said, here are the actual FindWindow prototypes from the WinUser.h header: WINUSERAPI HWND WINAPI FindWindowA( _In_opt_ LPCSTR lpClassName, _In_opt_ LPCSTR lpWindowName); WINUSERAPI HWND WINAPI FindWindowW( _In_opt_ LPCWSTR lpClassName, _In_opt_ LPCWSTR lpWindowName); #ifdef UNICODE #define FindWindow FindWindowW #else #define FindWindow FindWindowA #endif // !UNICODE Since ctypes works at the ABI (application binary interface) level rather than API (application programmng interface), you need to use the function names a module actually links with, i.e. either FindWindowA for the [A]NSI implementation or FindWindowW for the [W]ide-character implementation. Let's take a look at your code. > import ctypes > User32 = ctypes.WinDLL('User32', use_last_error=True) > window_name = 'Star Wars' > handle = User32.FindWindow(None, window_name ) If you're using Python 3, then 'Star Wars' is a Unicode string, and you should call FindWindowW. If you're using Python 2, then it's a byte string, and you should call FindWindowA. But I don't recommend the latter. You should be working with Unicode, i.e. u'Star Wars'. For example: import ctypes from ctypes import wintypes user32 = ctypes.WinDLL('user32', use_last_error=True) user32.FindWindowW.restype = wintypes.HWND user32.FindWindowW.argtypes = (wintypes.LPCWSTR, wintypes.LPCWSTR) if __name__ == '__main__': import os kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) window_name = u'Spam Wars' os.system('title {}'.format(window_name)) handle = user32.FindWindowW(None, window_name) if handle is None: handle = user32.FindWindowW(None, u'Administrator: {}'.format(window_name)) assert handle == kernel32.GetConsoleWindow() Another thing to consider is that if you've defined the FindWindowW prototype, ctypes in Python 2 will automatically convert a byte-string argument to Unicode. It uses the encoding and errors that are set by ctypes.set_conversion_mode(encoding, errors), which defaults to ('mbcs', 'ignore'). Python's 'mbcs' encoding is the system-locale ANSI code page. This automatic conversion is not implemented in Python 3 ctypes, in which case you should definitely be using Unicode. ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot