Вопрос

I'm trying to set the owner (not parent) of a Win32 windows via python and ctypes.

When I call:

GWL_HWNDPARENT = -8
ctypes.windll.user32.SetWindowLongW(hWndChild, GWL_HWNDPARENT, hWndParent)

it fails and ctypes.GetLastError() returns 1413 - ERROR_INVALID_INDEX (link).

Both the hWndChild and hWndParent seem valid because I can use ctypes.windll.user32.SetParent successfully.

I'm confused. What does the Invalid Index refer to?


Updated according to comments.

This requires two instances of notepad editing OwnerWindow.txt and NonOwnerWindow.txt respectively

import ctypes

GWL_HWNDPARENT      = -8 

_FindWindow          = ctypes.windll.user32.FindWindowW
_FindWindow.argtypes = [ctypes.c_wchar_p,ctypes.c_wchar_p]
_FindWindow.restype  = ctypes.c_void_p

_SetWindowLong          = ctypes.windll.user32.SetWindowLongW
_SetWindowLong.argtypes = [ctypes.c_long, ctypes.c_long, ctypes.c_long]
_SetWindowLong.restype  = ctypes.c_void_p

oHWnd = _FindWindow('Notepad', 'OwnerWindow.txt - Notepad')
nHWnd = _FindWindow('Notepad', 'NonOwnerWindow.txt - Notepad')

print "Last Error: {0}".format(str(ctypes.GetLastError()))
_SetWindowLong(ctypes.c_int(nHWnd), ctypes.c_int(GWL_HWNDPARENT), ctypes.c_int(oHWnd))
print "Last Error: {0}".format(str(ctypes.GetLastError()))
Это было полезно?

Решение

EDIT: I made some test and (re)read documentation, taking into account comments from @eryksun.

The only way to know if the SetWindowLong failed or succeeded is to use SetLastError(0) just before the call, and then to use GetLastError after the call, if the call returned 0.

If the function succeeds, the return value is the previous value of the specified 32-bit integer.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

If the previous value of the specified 32-bit integer is zero, and the function succeeds, the return value is zero, but the function does not clear the last error information. This makes it difficult to determine success or failure. To deal with this, you should clear the last error information by calling SetLastError with 0 before calling SetWindowLong. Then, function failure will be indicated by a return value of zero and a GetLastError result that is nonzero.

If the C/C++ code from ctypes does insert Win32 APIs call beetween your SetLastError call and the final SetWindowLong call, you can't know if there is success or failure.

ctypes use_last_error example:

from ctypes import *
from ctypes.wintypes import *
from ctypes import _SimpleCData

user32le = WinDLL('user32', use_last_error=True)
user32le.SetWindowLongW.restype = LONG
user32le.SetWindowLongW.argtypes = [HWND, c_int, LONG]
LONG_PTR = HWND  # should be an integer, but a pointer is simpler
if sizeof(HWND) == sizeof(c_long):  # WIN32
    # WIN32: this is a macro in winuser.h
    user32le.SetWindowLongPtrW = user32le.SetWindowLongW
elif sizeof(HWND) == sizeof(c_longlong):  # WIN64
    user32le.SetWindowLongPtrW.restype = LONG_PTR
    user32le.SetWindowLongPtrW.argtypes = [HWND, c_int, LONG_PTR]

def SetWindowLongPtr(hWnd, nIndex, dwNewLong):
    if isinstance(dwNewLong, _SimpleCData):
        # avoid ArgumentError with LONG, etc
        dwNewLong = dwNewLong.value
    # set last_error to 0, and save the previous value
    last_error = set_last_error(0)
    try:
        # ctypes calls GetLastError / SetLastError to swap 
        # LastError and last_error
        result = user32le.SetWindowLongPtrW(hWnd, nIndex, dwNewLong)
        # ctypes again swaps LastError and last_error
    finally:
        last_error = set_last_error(last_error)          
    if not result and last_error:
        raise WinError(last_error)
    return result
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top