[python-win32] hook to obtain WM_GESTURE

John Grant cyrfer at gmail.com
Fri May 10 02:12:57 CEST 2013


Hi Tim, et al,

I am testing the ctypes technique to intercept messages for my process, as
Tim pointed out I need to do using the SetWindowsLongPtr function.

I am seeing slightly unexpected results. My goal was to make a pass-through
implementation, so my callback does not disrupt the application. I wanted
to print to the shell to show my callback was involved. I am seeing print
statements, but the behavior of the application is slightly different.
Reverse mouse wheel seems to behave differently in the application. Also,
sometimes I see an exception in the shell (OSError: exception...access
violation reading 0xblahblah).

Does anyone see an error with the way I am calling the "oldWndProc" or the
print statement in the code below? Maybe the way I have defined the return
argument for SetWindowLongPtrW is incorrect?

Thanks for any help,
John


from ctypes import *
import ctypes
import ctypes.wintypes
from ctypes import c_long, c_int

# some shortcut definitions
GetActiveWindow = windll.user32.GetActiveWindow
GetActiveWindow.restype = ctypes.wintypes.HWND

LRESULT = ctypes.wintypes.LPARAM
WNDPROC = WINFUNCTYPE(LRESULT, ctypes.wintypes.HWND, ctypes.wintypes.UINT,
ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM)

SetWindowLongPtrW = ctypes.windll.user32.SetWindowLongPtrW
SetWindowLongPtrW.restype = WNDPROC

CallWindowProc = ctypes.windll.user32.CallWindowProcW
CallWindowProc.restype = LRESULT

#proc type
GWL_WNDPROC = int(-4)

#msg types in case we need them
WM_DESTROY = int('0x0002', 16)
WM_KEYDOWN = int('0x0100', 16)
WM_SIZE = int('0x0005', 16)
WM_CLOSE = int('0x0010', 16)
WM_GESTURE = int('0x0119', 16)
WM_TOUCH = int('0x0240', 16)
WM_GESTURENOTIFY = int('0x011A', 16)
WM_TIMER = int('0x0113', 16)
WM_MOUSEMOVE = int('0x0200', 16)
WM_MOUSEWHEEL = int('0x020A', 16)
WM_KEYUP = int('0x0101', 16)
WM_KEYDOWN = int('0x0101', 16)

# middle mouse button from GetAsyncKeyState()
VK_MBUTTON = int('0x04',16)


# ---- Multi Touch Support Class --- #
# you should have only one instance of this class per OS window,
# because it adds a member function as the Windows message handler for the
hWnd passed.
class MTHandler(object):
    def __init__(self, hWnd):
        self.mHWND = hWnd
        self.newWndProc = WNDPROC(self.MyWndProc)
        self.oldWndProc = SetWindowLongPtrW(self.mHWND, GWL_WNDPROC,
self.newWndProc)
        self.inControl = True

    def _mouse_handler(self, msg, wParam, lParam):
        print('hello mouse move')

    def _gesture_handler(msg, wParam, lParam):
        print('hello gesture')

    def _touch_handler(msg, wParam, lParam):
        print('hello touch')

    def MyWndProc(self, hWnd, msg, wParam, lParam):
        print('hello wndproc', msg, wParam, lParam)

        #if (msg == WM_MOUSEMOVE) or (msg==WM_MOUSEWHEEL):
        #    _mouse_handler(msg, wParam, lParam)

        #elif msg == WM_GESTURE:
        #    _gesture_handler(msg, wParam, lParam)

        #elif msg == WM_TOUCH:
        #    _touch_handler(msg, wParam, lParam)

        # source:
        # http://wiki.wxpython.org/HookingTheWndProc
        #
        # Restore the old WndProc.  Notice the use of wxin32api
        # instead of win32gui here.  This is to avoid an error due to
        # not passing a callable object.
        if msg == WM_CLOSE:
            if self.inControl:
                self.cleanup()

        if msg == WM_DESTROY:
            if self.inControl:
                self.cleanup()

        return CallWindowProc(self.oldWndProc, self.mHWND, msg, wParam,
lParam)

    def cleanup(self):
        SetWindowLongPtrW(self.mHWND, GWL_WNDPROC, self.oldWndProc)
        self.inControl = False

    def __del__(self):
        if self.inControl:
            self.cleanup()
# ---- end Multi Touch Support Class --- #





On Thu, May 9, 2013 at 1:52 PM, John Grant <cyrfer at gmail.com> wrote:

> I believe found access to the HWND.
> mHWND = ctypes.windll.user32.GetActiveWindow()
>
>
>
> On Thu, May 9, 2013 at 11:13 AM, John Grant <cyrfer at gmail.com> wrote:
>
>> Hi Tim,
>>
>> Thanks for the great explanations.
>>
>> Yes, you are right, I am trying to intercept gestures in a window within
>> the same process. The Blender application framework has a WndProc in C, and
>> Blender supports extending itself with user-supplied Python scripts. I am
>> trying to add gesture support with Python so I can avoid modifying the
>> Blender C code.
>>
>> I'm reading through the wxPython article. It looks like a perfect fit for
>> my problem. It shows everything I need to do, and using ctypes! Thanks!
>>
>> Although, I am stuck trying to get the HWND from Blender. It seems they
>> have not exposed the handle yet. I'm looking for win32 API that might
>> provide it to me, or a list of windows associated with the process.
>>
>> I guess I have to remove my callback as soon as the WM_DESTROY message is
>> emitted, and reinstantiate the oldWndProc. That makes sense. I'm glad that
>> is shown in the article.
>>
>> I just browsed the Blender C source code and they also use
>> "SetWindowLongPtr" for GWLP_USERDATA, but not for GWLP_WNDPROC. I think
>> everything will work out fine when I find the HWND.
>>
>> Thanks for the expert help.
>> John
>>
>>
>> On Thu, May 9, 2013 at 9:46 AM, Tim Roberts <timr at probo.com> wrote:
>>
>>> John Grant wrote:
>>> >
>>> > I would like to obtain multi-touch events in my Python code (running
>>> > inside Blender's python environment). I have looked around the web for
>>> > a python module that works with py 3.3 (others work with py 2.x), but
>>> > I have not found one. So, I'm trying to build one.
>>> >
>>> > I believe I can use the 'ctypes' to call the function I need,
>>> > GetGestureInfo.
>>>
>>> That gets you gestures, but not multitouch.  If all you need is the
>>> zoom, pan and rotate gestures and a two-finger tap, this will do it.
>>> For more complicated gestures, most solutions are custom right now.
>>>
>>>
>>> > This function requires 2 parameters as input, the lParam from WndProc
>>> > and a pointer to the GESTUREINFO structure.
>>> >
>>> > * I hope I can use 'ctypes' to declare the GESTUREINFO structure in my
>>> > python code.*
>>> > I see it is possible to pass structures as pointers using ctypes as
>>> well.
>>>
>>> The structure doesn't have any pointers, so this should be
>>> straightforward.
>>>
>>>
>>> > *** The problem seems to be obtaining the lParam from WndProc. ***
>>> >
>>> > My idea is to provide a callback function (again using ctypes to
>>> > declare this callback) and use the SetWindowsHookEx function, passing
>>> > my callback and the WH_CALLWNDPROC hook ID.
>>> >
>>> > Does this sound like it will work?
>>>
>>> No, a Windows hook is the wrong answer.  That lets you intercept
>>> messages from other processes (which is why hooks need to be in a DLL --
>>> the DLL actually gets copied and injected into the processes being
>>> hooked).  In your case, I assume you're trying to intercept gestures in
>>> a window within your own process.  Is that right?  As long as you have
>>> the window handle, all you need to do is subclass the window.  That
>>> means you make yourself the wndproc for that window, so you get first
>>> shot at all the messages.
>>>
>>> Here's an example that shows how to do this in wxPython, but you can
>>> eliminate the wxPython part of it.  The key point is using
>>> win32gui.SetWindowLong to load your own function in
>>> win32con.GWL_WNDPROC, and remembering the return value so you can call
>>> the original function.
>>>     http://wiki.wxpython.org/HookingTheWndProc
>>>
>>> --
>>> Tim Roberts, timr at probo.com
>>> Providenza & Boekelheide, Inc.
>>>
>>> _______________________________________________
>>> python-win32 mailing list
>>> python-win32 at python.org
>>> http://mail.python.org/mailman/listinfo/python-win32
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-win32/attachments/20130509/701f010a/attachment.html>


More information about the python-win32 mailing list