eric256 has asked for the wisdom of the Perl Monks concerning the following question:

Yo....So i'm still trying to get this hot key thing to work and i've gotten a little farther...maybe. I found the right API calls to hook a windows messages. I'm getting all sorts of messages about a Win32::GUI window I created, keystrokes etc. The problem is the hotkey message still isn't coming through. So i think there is a problem specificaly with either the hWnd returnd by Win32::GUI or the constants I've used, or my understanding of the problem at all. So if any Win32 gurus could lend me an eyeball and maybe a few brain cells i'd appreciate it.

use warnings; use strict; use Tk; use Win32::API; use Win32::GUI; use Win32::API::Callback; use constant WH_MSGFILTER => -1; use constant WH_JOURNALRECORD => 0; use constant WH_JOURNALPLAYBACK => 1; use constant WH_KEYBOARD => 2; use constant WH_GETMESSAGE => 3; use constant WH_CALLWNDPROC => 4; use constant WH_CBT => 5; use constant WH_SYSMSGFILTER => 6; use constant WH_MOUSE => 7; use constant WH_HARDWARE => 8; use constant WH_DEBUG => 9; use constant WH_SHELL => 10; use constant WH_FOREGROUNDIDLE => 11; use constant WH_CALLWNDPROCRET => 12; use constant WH_KEYBOARD_LL => 13; use constant WH_MOUSE_LL => 14; use constant MOD_CONTROL => 2; use constant KEY_A => 65; my $mw_win32 = new Win32::GUI::Window( -title => 'This is a test!', -width => 100, -height => 100, -name => 'MainWindow'); my $GetCurrentThreadId = new Win32::API('kernel32', 'GetCurrentThrea +dId', '', 'N'); my $SetWindowsHookEx = new Win32::API('user32' , 'SetWindowsHookE +x' , 'NKNN', 'N'); my $CallNextHookEx = new Win32::API('user32' , 'CallNextHookEx' + , 'PNNN', 'N'); Win32::API->Import('user32', 'BOOL RegisterHotKey(HWND hWnd, int id, U +INT fsModifiers, UINT vk)') or die "import RegisterHotKey: $! ($^W)"; sub KeyboardHook($$$) { my ($nCode, $wParam, $lParam) = @_; print "nCode=$nCode, wParam=$wParam, lParam=$lParam\n"; $CallNextHookEx->Call(0, $nCode, $wParam, $lParam); } # LRESULT CALLBACK CallWndProc(int nCode,WPARAM wParam, LPARAM lParam) +; sub WindowProc($$$) { my ($nCode, $wParam, $lParam) = @_; print "nCode=$nCode, wParam=$wParam, lParam=$lParam\n"; print "***************" if $wParam == 273; $CallNextHookEx->Call(0, $nCode, $wParam, $lParam); } my $WinProc = new Win32::API::Callback(\&WindowProc , ' +NNN', 'N'); my $KeyboardHookCallback = new Win32::API::Callback(\&KeyboardHook, ' +NNN', 'N'); my $ThreadId = $GetCurrentThreadId->Call() or die; my $Hook = $SetWindowsHookEx->Call(WH_KEYBOARD , $KeyboardHookC +allback, 0 , $ThreadId) or die $^E; my $ProcHook = $SetWindowsHookEx->Call(WH_GETMESSAGE , $WinProc, 0 , $ +ThreadId) or die $^E; #WH_CALLWNDPROC print $mw_win32->{-handle}; my $hotkey = RegisterHotKey ($mw_win32->{-handle}, 99, MOD_CONTROL, +KEY_A) or die $^E; $mw_win32->Show(); Win32::GUI::Dialog();

___________
Eric Hodges

Replies are listed 'Best First'.
Re: Continueing the Quest to get RegisterHotKey to work!
by Util (Priest) on Jul 16, 2006 at 02:02 UTC

    In sub WindowProc, you need to unpack $lParam, which is a pointer to a struct of type MSG.
    You then need to check the message field in MSG to see if the message is of type WM_HOTKEY.

    Here are the minimum changes to make your code work.
    Add this to your constant section:

    use constant WM_HOTKEY => 0x0312;
    Add this to the middle of sub WindowProc:
    my ($hwnd, $message, $msg_wParam, $msg_lParam, $time, $pt) = unpack 'L L L l L L ', unpack( 'P24', pack('L', $lParam) ); if ( $message == WM_HOTKEY ) { print "**************************\n"; }

    These are some thoughts I had during my research of this problem:

    1. use Tk; is not needed in your example code.
    2. Sub KeyboardHook, and all of the code to hook it to the WH_KEYBOARD message, can be removed.
    3. If you read 'When to use Prototypes?', I think you will want to remove the ($$$) from your subs.
    4. According to this MSDN article, you should unregister your hotkey before exiting your program.
    5. You don't have to cheat and use $mw_win32->{-handle}; you can get the window handle with GetActiveWindow() as long as it is run *after* Show().
    6. The MSG structure could be decoded using Win32::API::Struct, but WindowProc gets called for *all* the window's messages and needs to perform quickly, so manual unpacking seems best.
    7. Your WindowProc() is a callback function of type GetMsgProc. The MSDN page on GetMsgProc seems to say that parameters $nCode and $wParam, along with the return value, should be considered (by comparing with HC_* and PM_* constants) to determine whether to call CallNextHookEx or not. You are not doing so, but your code is working, and the MS docs are very unclear to me, so I can't recommend a more "correct" solution. If the program gets twitchy under a heavy load, this is a good place to start looking for the problem.

    Refactored, working, tested code:

      OMG Thank you!. For your points. I was using Tk for other stuff before I started pairing down to find this specific issue. That is also why the keyboard hook was still in there, it was kind of my "yes its still running and catching input" test. ;) Prototyps and some of the other gruff was collected from other examples I'd found, all used prototypes so I made the assumption that they were needed. I'm so friggin happy you don't even know! lol. I'm off to play with this, thank you very very much. Now to figure out what you did differently that made it work! BTW I knew i would need to decode, but as far as i could tell it wasn't even getting messages when it didn't have focus so I hadn't even tried decoding them yet.


      ___________
      Eric Hodges