Skip to content

Instantly share code, notes, and snippets.

@FichteFoll
Last active February 18, 2016 21:50
Show Gist options
  • Save FichteFoll/b79291f70ca9a1e602f0 to your computer and use it in GitHub Desktop.
Save FichteFoll/b79291f70ca9a1e602f0 to your computer and use it in GitHub Desktop.
WIP hexchat script that hooks its WndProc to listen to hibernate and wakeup messages
import ctypes
from ctypes import byref, c_int, c_long, c_int64, addressof, WINFUNCTYPE
from ctypes.wintypes import (
HWND, DWORD, LPARAM, BOOL, WPARAM, LPDWORD, UINT, HINSTANCE, HHOOK
)
import os
# import hexchat
# script metadata
__module_name__ = "Power"
__module_version__ = "0.1.0"
__module_description__ = "Disconnect and reconnect all servers when Windows hibernates or resumes."
# ctypes aliases
NULL = None
FALSE = 0
TRUE = 1
LRESULT = c_long
LONG_PTR = c_int64 # (c_int32 on 32-bit arch/proc)
class CWPSTRUCT(ctypes.Structure): # noqa
_fields_ = [
('lParam', LPARAM),
('wParam', WPARAM),
('message', UINT),
('hwnd', HWND),
]
# function types for callbacks
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms633498(v=vs.85).aspx
WNDENUMPROC = WINFUNCTYPE(BOOL, HWND, LPARAM) # EnumWindowsProc
# WndProc = WINFUNCTYPE(c_int, HWND, c_int, WPARAM, LPARAM)
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms644975%28v=vs.85%29.aspx
HOOKPROC = WINFUNCTYPE(LRESULT, c_int, WPARAM, LPARAM)
WNDPROC = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
# Win32API functions
# CallWindowProc = user32.CallWindowProcW
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms644974%28v=vs.85%29.aspx
CallNextHookEx = user32.CallNextHookEx
CallNextHookEx.argtypes = (HHOOK, c_int, WPARAM, LPARAM)
CallNextHookEx.restype = LRESULT
CallWindowProc = user32.CallWindowProcW
CallWindowProc.argtypes = (WNDPROC, HWND, UINT, WPARAM, LPARAM)
CallWindowProc.restype = LRESULT
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms633497(v=vs.85).aspx
EnumWindows = user32.EnumWindows
EnumWindows.argtypes = (WNDENUMPROC, LPARAM)
EnumWindows.restype = BOOL
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms683183%28v=vs.85%29.aspx
GetCurrentThreadId = kernel32.GetCurrentThreadId
GetCurrentThreadId.argtypes = ()
GetCurrentThreadId.restype = DWORD
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms633515%28v=vs.85%29.aspx
GetWindow = user32.GetWindow
GetWindow.argtypes = (HWND, UINT)
GetWindow.restype = (HWND)
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms633585%28v=vs.85%29.aspx
GetWindowLongPtr = user32.GetWindowLongPtrW
GetWindowLongPtr.argtypes = (HWND, c_int)
GetWindowLongPtr.restype = LONG_PTR
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms633522(v=vs.85).aspx
GetWindowThreadProcessId = user32.GetWindowThreadProcessId
GetWindowThreadProcessId.argtypes = (HWND, LPDWORD)
GetWindowThreadProcessId.restype = DWORD
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms633530(v=vs.85).aspx
IsWindowVisible = user32.IsWindowVisible
IsWindowVisible.argtypes = (HWND,)
IsWindowVisible.restype = BOOL
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms644898%28v=vs.85%29.aspx
SetWindowLongPtr = user32.SetWindowLongPtrW
SetWindowLongPtr.argtypes = (HWND, c_int, LONG_PTR)
SetWindowLongPtr.restype = LONG_PTR
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx
SetWindowsHookEx = user32.SetWindowsHookExW
SetWindowsHookEx.argtypes = (c_int, HOOKPROC, HINSTANCE, DWORD)
SetWindowsHookEx.restype = HHOOK
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms644993%28v=vs.85%29.aspx
UnhookWindowsHookEx = user32.UnhookWindowsHookEx
UnhookWindowsHookEx.argtypes = (HHOOK,)
UnhookWindowsHookEx.restype = BOOL
# Win32API constants
GWLP_WNDPROC = -4
GW_OWNER = 4
WH_GETMESSAGE = 3
WH_CALLWNDPROC = 4
def get_main_window_handle(pid):
"""Get a process's main window handle.
Algorithm loosely based on http://stackoverflow.com/a/21767578.
"""
handle = None
@WNDENUMPROC
def enum_windows_proc(hwnd, lparam):
nonlocal handle
wnd_pid = DWORD()
GetWindowThreadProcessId(hwnd, byref(wnd_pid))
if pid == wnd_pid.value:
# print("hwnd:", hwnd)
# print("owner:", GetWindow(hwnd, GW_OWNER))
if GetWindow(hwnd, GW_OWNER) is None and IsWindowVisible(hwnd):
# print("visible")
handle = hwnd
return FALSE # stop enumeration
return TRUE
EnumWindows(enum_windows_proc, 0)
return handle
@HOOKPROC
def call_wnd_proc(ncode, wparam, lparam):
msg = CWPSTRUCT(lparam) # TODO do this properly
hexchat.prnt(ncode, wparam, msg.message, msg.wParam, msg.lParam)
# if msg.message == WM_POWERBROADCAST:
# if msg.wParam == PBT_APMSUSPEND:
# pass
# elif msg.wParam == PBT_APMRESUMEAUTOMATIC:
# pass
return CallNextHookEx(None, ncode, wparam, lparam)
old_wnd_proc = None
@WNDPROC
def wnd_proc(hwnd, msg, wparam, lparam):
print(msg, wparam, lparam)
# if msg == WM_POWERBROADCAST:
# if wparam== PBT_APMSUSPEND:
# pass
# elif wparam == PBT_APMRESUMEAUTOMATIC:
# pass
return CallWindowProc(old_wnd_proc, hwnd, msg, wparam, lparam)
def main():
global old_wnd_proc
hc_pid = os.getpid() # 2804
hwnd = get_main_window_handle(hc_pid)
# hwnd = hexchat.get_info("win_ptr") # fixed on 2016-02-18
print("hwnd", hwnd)
old_wnd_proc = GetWindowLongPtr(hwnd, GWLP_WNDPROC)
if not old_wnd_proc:
print("cannot get old WndProc")
return
print("old_wnd_proc", old_wnd_proc)
print("wnd_proc", wnd_proc)
print("addr", ctypes.addressof(wnd_proc))
result = SetWindowLongPtr(hwnd, GWLP_WNDPROC, addressof(wnd_proc))
if not result:
print("cannot set new WndProc")
return
# hhook = SetWindowsHookEx(WH_CALLWNDPROC, call_wnd_proc, None, GetCurrentThreadId())
# if not hhook:
# print("error registering hook")
# return
def unload_cb(_):
# UnhookWindowsHookEx(hhook)
SetWindowLongPtr(hwnd, GWLP_WNDPROC, old_wnd_proc)
print(__module_name__, __module_version__, "unloaded")
hexchat.hook_unload(unload_cb)
print(__module_name__, __module_version__, "loaded")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment