Jump to content

HRC - HotKey Resolution Changer [Updated 2012-May-16]


KaFu
 Share

Recommended Posts

Here's a simple solution or use KaFu's undocumented method to check lock your computer.

#Include <HotKey.au3>

Global $Locked = False

_HotKeyAssign(0x7B, '_Message') ; F12

AdlibRegister('_Timer')

While 1
    Sleep(1000)
WEnd

Func _Timer()
    If _IsWorkstationLocked() Then
        If Not $Locked Then
            _HotKeyDisable()
            $Locked = 1
        EndIf
    Else
        If $Locked Then
            _HotKeyEnable()
            $Locked = 0
        EndIf
    EndIf
EndFunc   ;==>_Timer

Func _IsWorkstationLocked()

    Local $Result = False

    $hDesktop = DllCall('user32.dll', 'handle', 'OpenDesktopW', 'wstr', 'Default', 'dword', 0, 'bool', 0, 'dword', 0x0100)
    If Not @error Then
        $Result = DllCall('user32.dll', 'bool', 'SwitchDesktop', 'handle', $hDesktop[0])
        $Result = Not $Result[0]
        DllCall('user32.dll', 'bool', 'CloseDesktop', 'hanle', $hDesktop[0])
    EndIf
    Return $Result
EndFunc   ;==>_IsWorkstationLocked

Func _Message()
    MsgBox(0, '', 'Hot key has been pressed!')
EndFunc   ;==>_Message

EDIT:

I will fix it in HotKey UDF in the near future.

Edited by Yashied
Link to comment
Share on other sites

  • Replies 73
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

KaFu & Yashied, interesting things to go over.. I don't know what to call the Vista/Win7 Ctrl-Alt-Del screen other than the 'Logon UI' since it runs 'logonui.exe' when Ctrl-Alt-Del is pressed. Yashied - if you haven't used Vista or Win7 you won't know what screen we're seeing. This screen is separate from the locked-workstation screen, so we need to look for both.

Anyway, I did some research into this a while back and have the following code which has a callback that gets messages about session changes. It also looks for the logonui.exe process in a loop (not a good method at present, but it was what I was using in my research). {Esc} exits:

#include-once
; ===============================================================================================================================
; <_LogOnOffFunctions.au3>
;
; Author: Ascend4nt
; ===============================================================================================================================

#include <Misc.au3>
$hGUI=GUICreate("MyGUI",100,100)
;~ GUISetState($hGUI,@SW_HIDE)
ConsoleWrite("Phantom GUI Created:"&$hGUI&@CRLF)

$bLogonUIActive=False
$bSuccess=_LogOnOffRegister($hGUI,"_MY_WM_WTSSESSION_CHANGE")
ConsoleWrite("LogOnOffRegister return:"&$bSuccess&", @error="&@error&", @extended="&@extended&@CRLF)
While 1
    If _IsPressed("1B") Then ExitLoop
    If ProcessExists("logonui.exe") Then
        If Not $bLogonUIActive Then
            $bLogonUIActive=True
            ConsoleWrite("Logon UI interface is active, but session isn't necessarily locked"&@CRLF)
        EndIf
    ElseIf $bLogonUIActive Then
        ConsoleWrite("Logon UI interface is no longer active"&@CRLF)
        $bLogonUIActive=False
    EndIf
    Sleep(10)
WEnd
$bSuccess=_LogOnOffRegister($hGUI,"")
ConsoleWrite("LogOnOffRegister return:"&$bSuccess&", @error="&@error&", @extended="&@extended&@CRLF)



; ===================================================================================================================
; Func _LogOnOffRegister($hWnd,$sFunc)
;
; Register/Unregisters callback for Session-change notifications
;
; Author: Ascend4nt
; ===================================================================================================================

Func _LogOnOffRegister($hWnd,$sFunc)
    If Not IsHWnd($hWnd) Then Return SetError(1,0,False)
    Local $aRet,$iErr

;   UnRegistering?
    If $sFunc="" Then
        $aRet=DllCall("wtsapi32.dll","bool","WTSUnRegisterSessionNotification","hwnd",$hWnd)
        $iErr=@error
        ; WM_WTSSESSION_CHANGE = 0x02B1
        GUIRegisterMsg(0x02B1,"")
        If $iErr Then Return SetError(2,$iErr,False)
        If Not $aRet[0] Then Return SetError(3,0,False)
        Return True
    EndIf

;   Registering New

    ; WM_WTSSESSION_CHANGE = 0x02B1
    If Not GUIRegisterMsg(0x02B1,$sFunc) Then Return SetError(1,0,False)
    $aRet=DllCall("wtsapi32.dll","bool","WTSRegisterSessionNotification","hwnd",$hWnd,"dword",0)
    If @error Or Not $aRet[0] Then
        $iErr=@error
        GUIRegisterMsg(0x02B1,"")
        If $iErr Then Return SetError(2,$iErr,False)
        Return SetError(3,0,False)
    EndIf
    Return True
EndFunc


; ===================================================================================================================
; Func _MY_WM_WTSSESSION_CHANGE($hWnd,$vMsg,$wParam,$lParam)
;
; WM_WTSSession_Change callback
;
; Author: Ascend4nt
; ===================================================================================================================

Func _MY_WM_WTSSESSION_CHANGE($hWnd,$vMsg,$wParam,$lParam)
    Dim $aMsgs[9]=["Session was Connected to the Console Terminal","Session was Disconnected from the Console Terminal", _
        "Session was Connected to the Remote Terminal","Session was Disconnected from the Remote Terminal", _
        "User logged on to session","User logged of session","Session has been locked","Session has been unlocked", _
        "Session changed its remote control status"]
    ConsoleWrite("Session Change notification received (0x"&Hex($vMsg,4)&"). Window receiving Session Change notification:"&$hWnd&@CRLF)
    $wParam=Number($wParam)
    $lParam=Number($lParam)
    If $wParam>0 And $wParam<10 Then
        ConsoleWrite("Message (ID # "&$wParam&") received: '"&$aMsgs[$wParam-1]&"'"&@CRLF)
    Else
        ConsoleWrite("Unknown message received: ID # "&$wParam&@CRLF)
    EndIf
    ConsoleWrite("Message originated from session # "&$lParam&@CRLF)
    Return 'GUI_RUNDEFMSG'  ; $GUI_RUNDEFMSG from <GUIConstantsEx.au3>
EndFunc

KaFu, I'm not sure if putting a value for 'dwThreadId ' would be right, since the keyboard hook is supposed to be system-wide anyway.. hmm, but I really don't know.

*edit: After trying your linked code ($EVENT_OPEN_DESKTOP_CHANGED), it seems like we won't need any polling at all - although the check for locked workstation returns true each time (even on the longon UI screen). However, we can check if 'logonui.exe' exists and combine it with the above WTS Session-Change notification to come up with something.. What a mess of a workaround though? I tell ya.. me and Win 7 is a love/hate relationship..

Edited by Ascend4nt
Link to comment
Share on other sites

After some googeling around I fear that this will not completely solve the issue. I've also read articles mentioning e.g. HD-Video output peaking CPU usage sometimes might slow down hook calls enough that the timeout is triggered... how nice from Win7 :).

I think the way to go is to perform a regular check if the hook still exists:

http://zairon.wordpress.com/2006/12/06/any-application-defined-hook-procedure-on-my-machine/

Or as mentioned in this article: http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_22405364.html

QueryUserCounters / NtUserQueryUserCounters -> this enumerates all user objects

(hWnd, hMenu, hCursor, DeferWindowPos, SetWindowsHookEx, SetTimer, LoadAccelerators and so on)

The last one seems much better (what a nice new source for info ;) ), but also seems undocumented.

Edit:

This seems to be a good example too. In fact it seems to be a working example of the code as drawn out on zairon's page mentioned above.

http://forum.sysinternals.com/enumerate-windows-hooks_topic23877.html

http://www.ntinternals.org/other/EnumWindowsHooks.zip

Edited by KaFu
Link to comment
Share on other sites

Oy, that's no good. Maybe setting that registry timeout setting is a good idea?

Hmm on the undocumented front.. well I've already mucked about with the TEB, but had issues finding the right alignment to get 'Win32ThreadInfo'. I could experiment some more to try to get it 'just right', but then I'd need to delve into those other structures. I'm sure Manko might have a bit of info in that area since he's done driver-level stuff. Problem is finding out how the x64 versions align.. and oy, overall too much trouble (and probably too much time wasted)

An alternative would definitely be the way to go. . Unfortunately I don't have access to the experts-exchange, and don't know NtUserQueryUserCounters etc.. (more undocumented stuff, woo!) :)

Well, for me I think using the logon and lock-workstation detection will suffice for the time being, since I've not seen it fail for me personally in other scenarios. Hmm.. maybe I'll stress my CPU to the max and see what happens and report back..

*edit: Fixed alignment issues with TEB, but Win32ThreadInfo gives an invalid pointer under x64 (negative 64-bit pointer). I suppose that means it can't be used in x64 mode

Edited by Ascend4nt
Link to comment
Share on other sites

OK. But you can reset the hook with any changes.

#Include <HotKey.au3>

Global $Locked = False

_HotKeyAssign(0x7B, '_Message') ; F12

AdlibRegister('_Timer')

While 1
    Sleep(1000)
WEnd

Func _Timer()
    Local $State = _IsWorkstationLocked()
    If $State <> $Locked
        _HotKeyDisable()
        _HotKeyEnable()
        $Locked = $State
    EndIf
EndFunc   ;==>_Timer

Func _IsWorkstationLocked()

    Local $Result = False

    $hDesktop = DllCall('user32.dll', 'handle', 'OpenDesktopW', 'wstr', 'Default', 'dword', 0, 'bool', 0, 'dword', 0x0100)
    If Not @error Then
        $Result = DllCall('user32.dll', 'bool', 'SwitchDesktop', 'handle', $hDesktop[0])
        $Result = Not $Result[0]
        DllCall('user32.dll', 'bool', 'CloseDesktop', 'hanle', $hDesktop[0])
    EndIf
    Return $Result
EndFunc   ;==>_IsWorkstationLocked

Func _Message()
    MsgBox(0, '', 'Hot key has been pressed!')
EndFunc   ;==>_Message
Link to comment
Share on other sites

Well, a few stress-tests later and I'm back. It seems that, even with 4 cores running at 100% usage (I ran 4 copies of 'Stess Prime 2004', as well as 'CPU Stability Test'), the keyboard hook stayed strong, so for me at least, it looks like I'll just need to use KaFu's 'EVENT_OPEN_DESKTOP_CHANGED' callback and possibly the WTS Session notification messages to unhook and rehook the keyboard.

Yashied, your method is fine but the logon UI can be exited within less than a second (hitting the {Esc} key can do this), and even lower polling intervals might still miss it.. I'd prefer to rely on some sort of consistent callback.

Actually.. in re-testing KaFu's code - I get two callbacks. Even though it may report incorrectly that the workstation is 'locked', its still is a good indicator that the keyboard hook should be removed from the chain temporarily. On return from a workstation-locked screen and from a logon UI screen, I get the second callback and an 'unlocked' value, which would mean re-enabling the keyboard hook. In that way, I would only need to use KaFu's method. I'm just curious how the heck he stumbled onto that message being sent!

*edit: added 'CPU Stability Test' and link

Edited by Ascend4nt
Link to comment
Share on other sites

I'm still convinced that the logon window is not the only reason why the hook vanishes from time to time... how about this one? Maybe there's an even more unlikely Hotkey combo that can be used :)?

#include <HotKey.au3>

Global Const $VK_ESCAPE = 0x1B
_HotKeyAssign($VK_ESCAPE, '_Exit') ; F12

Global Const $VK_F12 = 0x7B
Global $i_HotKey_Check_Timer = TimerInit()
_HotKeyAssign(BitOR($CK_SHIFT, $CK_CONTROL, $CK_ALT, $CK_WIN, $VK_F12), '_HotKey_Activity_Check_Dummy', BitOR($HK_FLAG_NOBLOCKHOTKEY, $HK_FLAG_DEFAULT))
AdlibRegister('_HotKey_Activity_Check_Dummy_Timer', 1000)

While 1
    Sleep(1000)
WEnd

Func _Exit()
    Exit
EndFunc   ;==>_Exit

Func _HotKey_Activity_Check_Dummy()
    $i_HotKey_Check_Timer = TimerInit()
EndFunc   ;==>_HotKey_Activity_Check_Dummy
Func _HotKey_Activity_Check_Dummy_Timer()
    Send("#^!+{F12}")
    ConsoleWrite(TimerDiff($i_HotKey_Check_Timer) & @CRLF)
    If TimerDiff($i_HotKey_Check_Timer) > 2000 Then
        _HotKeyDisable()
        _HotKeyEnable()
    EndIf
EndFunc   ;==>_HotKey_Activity_Check_Dummy_Timer

Edit: Hmmm, the send() seems to steal the keyboard focus (sys-menus vanish on trigger). Is there another way of triggering the hotkey without interfering with normal operations?

Edited by KaFu
Link to comment
Share on other sites

Link to comment
Share on other sites

I do not understand what you mean by this code.

A dummy hotkey that resets a global timer is triggered every second by adlibregister. If the timer times-out the hotkey is dead and needs to be re-set.

Anyway, combine Hook and Send() is not a good idea.

Is there another method to trigger the hook to check if it is alive? Maybe via sendmessage?

http://social.msdn.microsoft.com/Forums/en/windowsgeneraldevelopmentissues/thread/56093d14-c1bc-4d0a-a915-57fef0695191

http://stackoverflow.com/questions/2655278/what-can-cause-windows-to-unhook-a-low-level-global-keyboard-hook

http://stackoverflow.com/questions/3891750/did-ms-change-something-about-keyboard-hooks-in-windows-vista-or-7

Edit: Or, to be on the safe side, just add this?

AdlibRegister("_Reenable_HotKeys", 2000)
Func _Reenable_HotKeys()
    _HotKeyDisable()
    _HotKeyEnable()
EndFunc   ;==>_Reenable_HotKeys
Edited by KaFu
Link to comment
Share on other sites

Changelog v1.2 > v1.3

General

  • fixes a stuck command key (ctrl, alt, win, shift) and another “save settings” issue
Source and Executable are available at

http://www.funk.eu

If something does not work as expected... I'm happy about any feedback :)...

Link to comment
Share on other sites

  • 1 month later...
Link to comment
Share on other sites

  • 2 months later...

Hi KaFu,

fantastic - exactly what i was looking for ..

;)

... and with commandline support. So i can use it for the different kids-game requirements.

Thanks fo sharing and greetings from Hamburg to Hamburg :huh2:

Dizzy

Link to comment
Share on other sites

Ich habe Google übersetzen zu verstehen, was du gesagt hast .. Also hier ist ein herzliches Wort des Dankes von Rupert, WV, USA ;)

Ordinarily it works exactly differently. The germans try to translate everything ;)

Thanks! from Hamburg :)

WV = West Virginia ?

Edited by Dizzy
Link to comment
Share on other sites

  • 3 months later...

I have a 24" 120hz gaming monitor plugged into my graficard i also have my TV plugged in aswell.

Problem:

Both monitors shows that it can run 120hz even tho its not possible for my TV to show more than 60hz in 1920x1080 :graduated:.

Suggestion:

Add a cross to kill the one monitor so its only one minitor again, and a enable button ;).

Link to comment
Share on other sites

Sorry, but HRC is just meant to work with a single monitor. The problem in your case is that the supported resolutions are extracted for the primary one only and of course might not work for the secondary. I've looked into multi-monitor support, but currently I'm not planning to implement it (never enough spare time ;) ). Extracting multiple supported resolutions, setting primary and secondary monitors, desktop placement, extended or cloned display, X x Y possible set-ups, just to complex to shot it from the hip :)....

Anyhow, some people reported that HRC is crashing for them. I've dug into that part and found an occurrence of the DEVMODE structure not initialized correctly (which made me wonder why it worked for some in the first place :graduated: )... so if HRC crashed for you in the past, here's the source of the most recent Beta, please test and report back.

HRC_v1_4_2.zip

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...