Last active
February 18, 2016 21:50
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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