C++ WINAPI: How to use SendMessage/PostMessage WM_KEYDOWN lparam - winapi

As an amateur to anything lower than VB/VBS (C++ WINAPI is a nightmare for someone of my experience) I have no idea how to go about constructing the long lParam for a simple KEYDOWN message in C++ and have probably spent more time looking for a decent explanation than is worth, would someone be able to describe exactly how to go about this?
It seems that almost every response to this question in other forums has been "why" or "use SendInput instead", without actually answering the question; I'm guessing that it's far too elementary for people to explain. Anyway here's the description from msdn, would very much appreciate a decent explanation or link to one, and a worked example if possible:
The repeat count, scan code,
extended-key flag, context code,
previous key-state flag, and
transition-state flag, as shown...
0-15 The repeat count for the current
message. The value is the number of
times the keystroke is autorepeated as
a result of the user holding down the
key. If the keystroke is held long
enough, multiple messages are sent.
However, the repeat count is not
cumulative. 16-23 The scan code. The
value depends on the OEM.
24 Indicates whether the key is an
extended key, such as the right-hand
ALT and CTRL keys that appear on an
enhanced 101- or 102-key keyboard. The
value is 1 if it is an extended key;
otherwise, it is 0. 25-28 Reserved; do
not use
29 The context code. The value is 1 if
the ALT key is down while the key is
pressed; it is 0 if the WM_SYSKEYDOWN
message is posted to the active window
because no window has the keyboard
focus.
30 The previous key state. The value
is 1 if the key is down before the
message is sent, or it is 0 if the key
is up.
31 The transition state. The value is
always 0 for a WM_SYSKEYDOWN message.

From the explanation of lParam parameter of WM_KEYDOWN message and Keystroke Message Flags chapter, I would write the following code to deal with the value of lParam:
iRepeatCount := LOWORD(lParam);
iScanCode := HIWORD(lParam) and $FF;
iExtendedKey := (HIWORD(lParam) and KF_EXTENDED) shr 8;
iContextCode := (HIWORD(lParam) and KF_ALTDOWN) shr 13;
iPreviousState := (HIWORD(lParam) and KF_REPEAT) shr 14;
iKeyDown := (HIWORD(lParam) and KF_UP) shr 15;
Although it is Delphi implementation, I'm sure you can understand such a simple code. :-)
OK, for your convenience, I google for shr instruction equivalent in VB, and I get this:
Private Function SHL(ByVal inVal As Long, ByVal inShift As Byte) As Long
SHL = inVal * (2 ^ inShift) ' Bit shift left
End Function
Private Function SHR(ByVal inVal As Long, ByVal inShift As Byte) As Long
SHR = inVal \ (2 ^ inShift) ' Bit shift right
End Function
To mimic the LOWORD and HIWORD in VB, see How to Mimic HIWORD, LOWORD, HIBYTE, LOBYTE C Macros in VB.

http://msdn.microsoft.com/en-us/library/ms646280(VS.85).aspx
The lparam is a 32 bit value; each bit controls a different aspect of the WM_KEYDOWN message.
To correctly fill out the lparam, you might try using spy++ to see what real WM_KEYDOWN messages look like.

Related

EnumWindows strange behaviour

I am trying to use MFC with visual Studio 2012 on Windows 8.
I have the following code:
BOOL CALLBACK EWP(HWND hwnd, LPARAM lParam)
{
int txtlen = GetWindowTextLengthW(hwnd);
std::wstring s;
s.reserve(txtlen + 1);
GetWindowText(hwnd, const_cast<wchar_t*>(s.c_str()), txtlen);
return TRUE;
}
EnumWindows(EWP, 0);
What happens is that the very first string comes out as "Task Switchin" and the rest come out as "". I get about 330 of these strings. I have tried without using that weird string method too with just char buff[300], same story.
Can someone please tell me whats going on?
Your last argument to GetWindowText() is off-by-one. From the MSDN article description of that argument:
Specifies the maximum number of characters to copy to the buffer, including the NULL character. If the text exceeds this limit, it is truncated.
Pass txtlen+1 to fix.

How to redirect mouse wheel message to other windows?

I'm developing a Word addin for MS Word on Windows, and this addin has as 'advanced task pane' showing and docking on the left side of the Word document window (it's treeview(outline) showing a list of Word documents for fast editing multiple documents in a project).
My question is, the Word document window responds to mouse wheel message only when it's focused, but I want to be able to scroll the document with mouse wheel whenever the mouse cursor is hovering on it even the Word document window doesn't have a input focus.
Any hints in this particular case? Thank you!
Not quite sure it will work, but I'd try the following:
Implement a global low-level mouse hook using the SetWindowsHookEx function.
In the hook procedure, which should be called on mouse wheel scroll events, check if the window under mouse cursor is the Word document window. If so, set a shared flag indicating the needed scroll action.
Don't send WM_VSCROLL directly from the hook procedure! This procedure has to be really fast and simple.
Now, in your add-in's message loop check the flag and if it is set, send WM_VSCROLL to the Word document window.
Perhaps you could make use of the SetCapture(hWnd) Windows API function. This will cause all mouse events to go to your hWnd instead of whatever hWnd might normally expect to receive them. If you capture when the mouse enters the Word document window, and ReleaseCapture() when the mouse leaves or Word gains focus, it should work alright.
Disclaimer: I've used mouse capturing in C# before, but I've never done it in C++. I don't know if it behaves exactly the same way.
Try the following , this might help you.
1) Handle WM_MOUSEHOVER message.
2) In the handler , use SendMessage, with WM_VSCROLL as the message parameter .
Using Spy++ I saw that the window that gets the messages is of the class _Wwg (At least 2003 is) and it is responding to the WM_MOUSEWHEEL message. So you would send that window a WM_MOUSEWHELL message when you want it to scroll.
I've got the C++ code snipped below from a comment in https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
And I used it (and variations on it) successfully.
The user who wrote it claims it was inspired by a ms recommendation on a Windows Vista best practices guide, to forward the mouse wheel event to whatever window is hovered by the mouse cursor. The virtue of his/her implementation is that it's completely unintrusive, you just drop it and it set the hooks, referencing your main thread. It avoids forwarding the event to windows belonging to other processes, but perhaps that could actually be a good thing.
namespace {
LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam) {
//"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
//without further processing and should return the value returned by CallNextHookEx"
if (nCode >= 0) {
MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);
if (msgInfo.message == WM_MOUSEWHEEL ||
msgInfo.message == WM_MOUSEHWHEEL) {
POINT pt = {};
pt.x = ((int)(short)LOWORD(msgInfo.lParam)); //yes, there's also msgInfo.pt, but let's not take chances
pt.y = ((int)(short)HIWORD(msgInfo.lParam)); //
//visible child window directly under cursor; attention: not necessarily from our process!
//http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
if (HWND hWin = ::WindowFromPoint(pt))
if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr) {
DWORD winProcessId = 0;
::GetWindowThreadProcessId(//no-fail!
hWin, //_In_ HWND hWnd,
&winProcessId); //_Out_opt_ LPDWORD lpdwProcessId
if (winProcessId == ::GetCurrentProcessId()) //no-fail!
msgInfo.hwnd = hWin; //it would be a bug to set handle from another process here
}
}
}
return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}
struct Dummy {
Dummy() {
hHook = ::SetWindowsHookEx(WH_GETMESSAGE, //__in int idHook,
mouseInputHook, //__in HOOKPROC lpfn,
nullptr, //__in HINSTANCE hMod,
::GetCurrentThreadId()); //__in DWORD dwThreadId
assert(hHook);
}
~Dummy() {
if (hHook)
::UnhookWindowsHookEx(hHook);
}
private:
HHOOK hHook;
} dummy;
}

How to emulate WM_KEYDOWN, WM_KEY* from a WM_INPUT handler registered with RIDEV_NOLEGACY?

I have a system with two HID keyboards (actually, one's a barcode scanner.)
I registered for raw input with RIDEV_NOLEGACY to block the system from creating WM_KEY* messages for the barcode scanner, which tediously also blocks the messages from the other keyboard.
My goal is to keep the WM_* messages for any keybaord device that isn't the barcode scanner.
Basically, I need to either:
Create the WM_* messages myself, and post them to my hwnd from the wndproc that received the wm_input
or
Predict the WM_* messgaes the system will generate, and ignore them if they came from the barcode scanner.
I created a working implementation of 2, which works great on XP but fails to block anything on Windows 7. (In fact, on win7 it seems like i'm only reciving WM_INPUTs even without the RIDEV_NOLEGACY flag)
I'm now trying method 1, which is arguably 'more correct', but I can't seems to find a way to do this completely correctly.
My environment is Python 2.6 using PyQt. I'm sending messages directly to a window created by PyQt, and i've hooked into it's wndproc with a win32 event filter.
class wm_lparam(Structure):
_fields_ = [("repeat_count", c_short),
("scancode", c_byte),
("extended_key", c_int, 1),
("reserved", c_int, 4),
("context_code", c_int, 1),
("prev_state", c_int, 1),
("transition_state", c_int, 1),
]
assert sizeof(wm_lparam) == 8, sizeof(wm_lparam)
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105
ALL_WM_KEYDOWN = (WM_KEYDOWN, WM_SYSKEYDOWN)
ALL_WM_KEYUP = (WM_KEYUP, WM_SYSKEYUP)
VK_SHIFT = 0x10
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1
#These values are filled in by my WM_INPUT handler and the RAWINPUT struct
#staticmethod
def _synthesize_wm_legacy(hwnd, wm, vk, scancode, modifider_keys=None):
kbState_old = (c_byte*255)()
kbState = (c_byte*255)()
def keydown(vk):
return bool(user32.GetAsyncKeyState(vk) & 0x8000)
kbState[VK_SHIFT] = 0x80 if keydown(VK_SHIFT) else 0
kbState[VK_LSHIFT] = 0x80 if keydown(VK_SHIFT) else 0
user32.GetKeyboardState(kbState_old)
user32.SetKeyboardState(kbState)
lParam = wm_lparam()
lp = c_uint.from_address(ctypes.addressof(lParam))
lParam.repeat_count = 0
lParam.scancode = scancode
lParam.extended_key = 0
if wm in ALL_WM_KEYDOWN:
lParam.context_code = 0
lParam.prev_state = 0
lParam.transition_state = 0
if wm in ALL_WM_KEYUP:
lParam.repeat_count = 0
lParam.context_code = 0
lParam.prev_state = 1
lParam.transition_state = 1
lp = lp.value
if wm in ALL_WM_KEYUP: #Seems ctypes doesn't like my struct definition.
lp |= 1 << 30
lp |= 1 << 31
log.debug("Posting %s %s %s %08x\n%s"%(hwnd, wm_str(wm), vk, lp, lParam.dump_s()))
user32.SendMessageA(hwnd, wm, vk, lp)
user32.SetKeyboardState(kbState_old)
This code works, but certain things (like holding the shift key, etc) fail. Also very strange is that when using SendMessage, the letters I type are in uppercase, but switchign to PostMessage makes them lowercase. I can probably solve this via Get/SetKeyState, but I was hoping somebody could give me some answers.
In addition, I'm posting these messages back onto PyQt's queue, but the application fails to process them until a real event is sytem generated. That is, If I type a sentence into a text box, nothing shows up until I then move my mouse over the window. The messages seem queued until a real event happens. Any suggestions?
Clarification:
This is a window in my own process, created by PyQt. I have gotten it's hwnd, and hooked the raw input notification up to it. In the window procedure for WM_INPUT on this hwnd, I want to sendmessage to my own hwnd to duplicate the 'legacy' WM_KEY* messages that I previously disabled to filter them. Again, this all happens in my own process, in my own thread.
Update:
Shift state detection simply doesn't work. No matter what, I am getting all capital keys. Any advice?
I was unable to solve this in pure Win32, and I've gotten only a half solution since i'm using PyQt. In case anyone is interested though, here's the code i'm using for that portion:
class BarcodeQtEventFiler(QtCore.QObject):
def __init__(self, parent, *args):
self.log = logging.getLogger(__name__ + '.keyevent')
self.app = parent
self.input_to_surpress = list()
super(BarcodeQtEventFiler, self).__init__(parent, *args)
def ignoreKey(self, which):
"""On WM_INPUT from the device, call this with the reported VKey"""
self.input_to_surpress.append(which)
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.KeyPress:
if self.input_to_surpress:
if event.nativeVirtualKey() in self.input_to_surpress:
z = None
#This loop eats the suppression buffer until the VK pressed is found. Fixes Dupes for WM key up/down, etc.
while z != event.nativeVirtualKey():
z = self.input_to_surpress.pop(0)
self.log.debug("Ate key press %s (%s)", event.key(), event.text())
return True
else:
self.log.debug("Future surpressed input: %s", self.input_to_surpress)
self.log.debug("Allowing key press %s (%s)", event.key(), event.text())
return False
This is not fixable as-is, you cannot control the keyboard state. The receiving app will use GetKeyState() to check if the Shift, Ctrl or Alt key is down. SetKeyState() doesn't work, it only changes the keyboard state of your process, not the process that gets the messages.
Use SendInput() instead. A window in the target process must have the focus.

ToAscii/ToUnicode in a keyboard hook destroys dead keys

It seems that if you call ToAscii() or ToUnicode() while in a global WH_KEYBOARD_LL hook, and a dead-key is pressed, it will be 'destroyed'.
For example, say you've configured your input language in Windows as Spanish, and you want to type an accented letter á in a program. Normally, you'd press the single-quote key (the dead key), then the letter "a", and then on the screen an accented á would be displayed, as expected.
But this doesn't work if you call ToAscii() or ToUnicode() in a low-level keyboard hook function. It seems that the dead key is destroyed, and so no accented letter á shows up on screen. Removing a call to the above functions resolves the issue... but unfortunately, I need to be able to call those functions.
I Googled for a while, and while a lot of people seemed to have this issue, no good solution was provided.
Any help would be much appreciated!
EDIT: I'm calling ToAscii() to convert the virtual-key code and scan code received in my LowLevelKeyboardProc hook function into the resulting character that will be displayed on screen for the user.
I tried MapVirtualKey(kbHookData->vkCode, 2), but this isn't as "complete" a function as ToAscii(); for example, if you press Shift + 2, you'll get '2', not '#' (or whatever Shift + 2 will produce for the user's keyboard layout/language).
ToAscii() is perfect... until a dead-key is pressed.
EDIT2: Here's the hook function, with irrelevant info removed:
LRESULT CALLBACK keyboard_LL_hook_func(int code, WPARAM wParam, LPARAM lParam) {
LPKBDLLHOOKSTRUCT kbHookData = (LPKBDLLHOOKSTRUCT)lParam;
BYTE keyboard_state[256];
if (code < 0) {
return CallNextHookEx(keyHook, code, wParam, lParam);
}
WORD wCharacter = 0;
GetKeyboardState(&keyboard_state);
int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
keyboard_state, &wCharacter, 0);
/* If ta == -1, a dead-key was pressed. The dead-key will be "destroyed"
* and you'll no longer be able to create any accented characters. Remove
* the call to ToAscii() above, and you can then create accented characters. */
return CallNextHookEx(keyHook, code, wParam, lParam);
}
Quite an old thread. Unfortunately it didn't contain the answer I was looking for and none of the answers seemed to work properly. I finally solved the problem by checking the MSB of the MapVirtualKey function, before calling ToUnicode / ToAscii. Seems to be working like a charm:
if(!(MapVirtualKey(kbHookData->vkCode, MAPVK_VK_TO_CHAR)>>(sizeof(UINT)*8-1) & 1)) {
ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
keyboard_state, &wCharacter, 0);
}
Quoting MSDN on the return value of MapVirtualKey, if MAPVK_VK_TO_CHAR is used:
[...] Dead keys (diacritics) are indicated by setting the top bit of the return value. [...]
stop using ToAscii() and use ToUncode()
remember that ToUnicode may return you nothing on dead keys - this is why they are called dead keys.
Any key will have a scancode or a virtual key code but not necessary a character.
You shouldn't combine the buttons with characters - assuming that any key/button has a text representation (Unicode) is wrong.
So:
for input text use the characters reported by Windows
for checking button pressed (ex. games) use scancodes or virtual keys (probably virtual keys are better).
for keyboard shortcuts use virtual key codes.
Call 'ToAscii' function twice for a correct processing of dead-key, like in:
int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
keyboard_state, &wCharacter, 0);
int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
keyboard_state, &wCharacter, 0);
If (ta == -1)
...
Calling the ToAscii or ToUnicode twice is the answer.
I found this and converted it for Delphi, and it works!
cnt:=ToUnicode(VirtualKey, KeyStroke, KeyState, chars, 2, 0);
cnt:=ToUnicode(VirtualKey, KeyStroke, KeyState, chars, 2, 0); //yes call it twice
I encountered this issue while creating a key logger in C# and none of the above answers worked for me.
After a deep blog searching, I stumbled across this keyboard listener which handles dead keys perfectly.
Here is a full code which covers dead keys and shortcut keys using ALT + NUMPAD, basically a full implementation of a TextField input handling:
[DllImport("user32.dll")]
public static extern int ToUnicode(uint virtualKeyCode, uint scanCode, byte[] keyboardState, [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder receivingBuffer, int bufferSize, uint flags);
private StringBuilder _pressCharBuffer = new StringBuilder(256);
private byte[] _pressCharKeyboardState = new byte[256];
public bool PreFilterMessage(ref Message m)
{
var handled = false;
if (m.Msg == 0x0100 || m.Msg == 0x0102)
{
bool isShiftPressed = (ModifierKeys & Keys.Shift) != 0;
bool isControlPressed = (ModifierKeys & Keys.Control) != 0;
bool isAltPressed = (ModifierKeys & Keys.Alt) != 0;
bool isAltGrPressed = (ModifierKeys & Keys.RMenu) != 0;
for (int i = 0; i < 256; i++)
_pressCharKeyboardState[i] = 0;
if (isShiftPressed)
_pressCharKeyboardState[(int)Keys.ShiftKey] = 0xff;
if (isAltGrPressed)
{
_pressCharKeyboardState[(int)Keys.ControlKey] = 0xff;
_pressCharKeyboardState[(int)Keys.Menu] = 0xff;
}
if (Control.IsKeyLocked(Keys.CapsLock))
_pressCharKeyboardState[(int)Keys.CapsLock] = 0xff;
Char chr = (Char)0;
int ret = ToUnicode((uint)m.WParam.ToInt32(), 0, _pressCharKeyboardState, _pressCharBuffer, 256, 0);
if (ret == 0)
chr = Char.ConvertFromUtf32(m.WParam.ToInt32())[0];
if (ret == -1)
ToUnicode((uint)m.WParam.ToInt32(), 0, _pressCharKeyboardState, _pressCharBuffer, 256, 0);
else if (_pressCharBuffer.Length > 0)
chr = _pressCharBuffer[0];
if (m.Msg == 0x0102 && Char.IsWhiteSpace(chr))
chr = (Char)0;
if (ret >= 0 && chr > 0)
{
//DO YOUR STUFF using either "chr" as special key (UP, DOWN, etc..)
//either _pressCharBuffer.ToString()(can contain more than one character if dead key was pressed before)
//and don't forget to set the "handled" to true, so nobody else can use the message afterwards
}
}
return handled;
}
It is known that ToUnicode() and its older counterpart ToAscii() can change keyboard state of the current thread and thus mess with dead keys and ALT+NUMPAD keystrokes:
As ToUnicodeEx translates the virtual-key code, it also changes the
state of the kernel-mode keyboard buffer. This state-change affects
dead keys, ligatures, alt+numpad key entry, and so on. It might also
cause undesired side-effects if used in conjunction with
TranslateMessage (which also changes the state of the kernel-mode
keyboard buffer).
To avoid that you can do your ToUnicode() call in a separate thread (it will have a separate keyboard state) or use a special flag in wFlags param that is documented in ToUnicode() docs:
If bit 2 is set, keyboard state is not changed (Windows 10, version
1607 and newer)
Or you can prepare sc->char mapping table beforehand and update it on language change event.
I think it should work with ToAscii() too but better not use this old ANSI codepage-dependant method. Use ToUnicode() API instead that can even return ligatures and UTF-16 surrogate pairs - if keyboard layout have them. Some do.
See Asynchronous input vs synchronous input, a quick introduction
for the reason behind this.
I copy the vkCode in a queue and do the conversion from another thread
#HOOKPROC
def keyHookKFunc(code,wParam,lParam):
global gkeyQueue
gkeyQueue.append((code,wParam,kbd.vkCode))
return windll.user32.CallNextHookEx(0,code,wParam,lParam)
This has the advantage of not delaying key processing by the os
This works for me
byte[] keyState = new byte[256];
//Remove this if using
//GetKeyboardState(keyState);
//Add only the Keys you want
keysDown[(int)Keys.ShiftKey] = 0x80; // SHIFT down
keysDown[(int)Keys.Menu] = 0x80; // ALT down
keysDown[(int)Keys.ControlKey] = 0x80; // CONTROL down
//ToAscii should work fine
if (ToAscii(myKeyboardStruct.VirtualKeyCode, myKeyboardStruct.ScanCode, keyState, inBuffer, myKeyboardStruct.Flags) == 1)
{
//do something
}

Programmatically prevent Windows screensaver from starting

Is there a recommended way to prevent the Windows screensaver from starting? The closest thing I've found is this article, but what I would really like to do is just tell Windows that the computer isn't idle rather than fooling with the currently set screensaver values.
For testing, I set the screensaver to 1 minute and required a password.
I tried capturing SC_SCREENSAVE and returning -1 in VB .Net. As commented, it works when there is no screensaver password but fails if the screensaver password is active. (I tried it in Windows XP). I also put this into a Timer's tick event, every 1000 milliseconds:
Static dir As Integer = 4
Cursor.Position = Cursor.Position + New Size(dir, dir)
dir = -dir
It doesn't work. The cursor jiggles back and forth and after 1 minute the screensaver flashes on for a short instance and then turns off. The screensaver turns on for only a moment, not long enough to require a password. But still, the flash is ugly.
Then I tried using user32.dll's SetCursorPos and GetCursorPos. You can look them up at pinvoke. Same result as above.
Then I peeked at the code of "JiggleMouse" mentioned elsewhere in this question. JiggleMouse uses SendInput. SendInput works! No flash of the screensaver. I put a call to SendInput inside of a Timer that triggers every 50 seconds (just less than the minimum screensaver timeout of 60 seconds). It's sufficient to move the mouse by a delta of 0,0, no real movement. That does work. The code to put in the Tick event:
Dim i(0) As INPUT
i(0).dwType = INPUT.InputType.INPUT_MOUSE
i(0).mkhi = New MOUSEKEYBDHARDWAREINPUT
i(0).mkhi.mi = New MOUSEINPUT
i(0).mkhi.mi.dx = 0
i(0).mkhi.mi.dy = 0
i(0).mkhi.mi.mouseData = 0
i(0).mkhi.mi.dwFlags = MOUSEINPUT.MouseEventFlags.MOUSEEVENTF_MOVE
i(0).mkhi.mi.time = 0
i(0).mkhi.mi.dwExtraInfo = IntPtr.Zero
SendInput(1, i(0), Marshal.SizeOf(i(0)))
This comes from pinvoke.com:
Public Declare Function SendInput Lib "user32" (ByVal nInputs As Integer, ByRef pInputs As INPUT, ByVal cbSize As Integer) As Integer
Public Structure INPUT
Enum InputType As Integer
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2
End Enum
Dim dwType As InputType
Dim mkhi As MOUSEKEYBDHARDWAREINPUT
End Structure
Public Structure MOUSEINPUT
Enum MouseEventFlags As Integer
MOUSEEVENTF_MOVE = &H1
MOUSEEVENTF_LEFTDOWN = &H2
MOUSEEVENTF_LEFTUP = &H4
MOUSEEVENTF_RIGHTDOWN = &H8
MOUSEEVENTF_RIGHTUP = &H10
MOUSEEVENTF_MIDDLEDOWN = &H20
MOUSEEVENTF_MIDDLEUP = &H40
MOUSEEVENTF_XDOWN = &H80
MOUSEEVENTF_XUP = &H100
MOUSEEVENTF_WHEEL = &H800
MOUSEEVENTF_VIRTUALDESK = &H4000
MOUSEEVENTF_ABSOLUTE = &H8000
End Enum
Dim dx As Integer
Dim dy As Integer
Dim mouseData As Integer
Dim dwFlags As MouseEventFlags
Dim time As Integer
Dim dwExtraInfo As IntPtr
End Structure
Public Structure KEYBDINPUT
Public wVk As Short
Public wScan As Short
Public dwFlags As Integer
Public time As Integer
Public dwExtraInfo As IntPtr
End Structure
Public Structure HARDWAREINPUT
Public uMsg As Integer
Public wParamL As Short
Public wParamH As Short
End Structure
Const KEYEVENTF_EXTENDEDKEY As UInt32 = &H1
Const KEYEVENTF_KEYUP As UInt32 = &H2
Const KEYEVENTF_UNICODE As UInt32 = &H4
Const KEYEVENTF_SCANCODE As UInt32 = &H8
Const XBUTTON1 As UInt32 = &H1
Const XBUTTON2 As UInt32 = &H2
<StructLayout(LayoutKind.Explicit)> Public Structure MOUSEKEYBDHARDWAREINPUT
<FieldOffset(0)> Public mi As MOUSEINPUT
<FieldOffset(0)> Public ki As KEYBDINPUT
<FieldOffset(0)> Public hi As HARDWAREINPUT
End Structure
Subtle. The official way to tell Windows that the system is not idle is SetThreadExecutionState. This resets the idle timer, (or turns it off, if you pass ES_CONTINUOUS ). However, even though SetThreadExecutionState resets the idle timer, it does not stop the screensaver!
SystemParametersInfo
Specifically, the SPI_SETSCREENSAVEACTIVE parameter.
Does this not work? I was surprised that I did not see it here. Note that SetThreadExecutionState will not affect the screen saver at all, just the sleeping of the display.
I use Mouse Jiggler to reset the idle state. This gets around a Group Policy that tends to start my screensaver (and lock the machine) at inopportune times: when I'm reading a long document, studying a complex chunk of code, or talking/listening/not-constantly-typing during a meeting.
As it can be slightly annoying to have the mouse jump 1px diagonally every second, I intend to use AutoHotKey to write a script that does basically the same thing, but only after a configured keyboard/mouse idle timeout, and maybe use the Shift key (or Scroll Lock) instead of a mouse move.
From MSDN:
Windows does not start the screen saver if any of the following conditions exist:
The active application is not a Windows-based application.
A CBT window is present.
The active application receives the WM_SYSCOMMAND message with the wParam parameter set to the SC_SCREENSAVE value, but it does not pass the message to the DefWindowProc function.
There's a caveat though:
Windows Vista and later: If password protection is enabled by policy, the screen saver is started regardless of what an application does with the SC_SCREENSAVE notification.
That seems to apply even if you use the SetThreadExecutionState with ES_CONTINUOUS.
So, if it weren't for the caveat, your choices would be:
SetThreadExecutionState with ES_CONTINUOUS (as described in other answers).
Put up a computer-based training window (which requires hooks).
Don't let the WM_SYSCOMMAND with SC_SCREENSAVE be passed onto DefWindowProc. (Assuming you care only when your application is the active application.)
Install a dongle that simulates mouse jiggle.
The last option is nice in that it works even with the password protection policy.
In Windows 7+, use the Power Management API's PowerSetRequest() with PowerRequestDisplayRequired
https://msdn.microsoft.com/en-us/library/windows/desktop/dd405534(v=vs.85).aspx
In previous versions of windows, intercept the WM_SYSCOMMAND - SC_SCREENSAVE message as detailed in Eddie Parker's answer.
This blog post details what you need to do in C++.
The actual code snippet from the website:
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SYSCOMMAND:
{
switch (wParam)
{
case SC_SCREENSAVE:
return 0;
case SC_MONITORPOWER:
return 0;
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
Can't believe no one has pointed out the easy and obvious solution:
#include <windows.h>
void main()
{
while(1){
INPUT input;
input.type = INPUT_MOUSE;
input.mi.dx = 1;
input.mi.dy = 1;
input.mi.mouseData = 0;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
input.mi.time = 0;
input.mi.dwExtraInfo = 0;
SendInput( 1, &input, sizeof(input) );
sleep(60000);
}
}
As Adrian McCarthy mentioned from MSDN that :
If password protection is enabled by policy, the screen saver is started regardless of what an application does with the SC_SCREENSAVE notification.
So catch the event from WM_SYSCOMMAND using UINT SC_SCREENSAVE and discarded it by returning 0 or by creating a fake mouse move ("mouse_event(MOUSEEVENTF_MOVE, 0, 1, 0, 0)") will not work properly if the user enabled password-protected screen saver option.
Use SetThreadExecutionState winAPI to tell the operating system that the thread is in use, even if the user is not interacting with the computer. These will prevent to appear screen saver and stop the machine from being suspended automatically.
There are series of flags to specify a new state for the current thread:
ES_AWAYMODE_REQUIRED (0x00000040) : Enables away mode.
ES_DISPLAY_REQUIRED (0x00000002) : Forces the display to be on by
resetting the display idle timer.
ES_SYSTEM_REQUIRED (0x00000001) : Forces the system to be in the
working state by resetting the system idle timer.
ES_CONTINUOUS (0x80000000) : Informs the system that the state being
set should remain in effect until the next call that uses
ES_CONTINUOUS and one of the other state flags are cleared.
As it's a winAPI, you can call this directly in win32 or mfc application
//To stop/start screen saver and monitor power off event
void SetKeepScreenOn(BOOL isKeepScreenOn)
{
if (isKeepScreenOn == TRUE)
{
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED /*| ES_AWAYMODE_REQUIRED*/);
}
else
{
SetThreadExecutionState(ES_CONTINUOUS);
}
}
If someone wants to use this in C#, must have to PInvoke this :
[DllImport("kernel32.dll", CharSet = CharSet.Auto,SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
User-Defined Types:
[FlagsAttribute]
public enum EXECUTION_STATE :uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001
}
Here below is the calling procedure:
void SetKeepScreenOn(bool isKeepScreenOn)
{
if (isKeepScreenOn == true)
{
//You can combine several flags and specify multiple behaviors with a single call
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED /*| EXECUTION_STATE.ES_AWAYMODE_REQUIRED*/);
}
else
{
//To reset or allow those event again you have to call this API with only ES_CONTINUOUS
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}
}
According to MSDN this API is safe also to use.
The system maintains a count of applications that have called SetThreadExecutionState. The system tracks each thread that calls SetThreadExecutionState and adjusts the counter accordingly. If this counter reaches zero and there has not been any user input, the system enters sleep.
If the Application crashed before resetting flag, the System will adjust and will reset automatically.
You can use SystemParametersInfo
to get the SCREENSAVETIMEOUT and then immediately set the timeout back to the same value. Do this periodically on a timer for as long as you want to prevent the screensaver from going on.
This has the effect of resetting the current countdown timer without actually changing the system setting.
You probably also want to call SetThreadExecutionState to affect the power as other answers mention.
Just reset the timeout counter with
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, nil, SPIF_SENDWININICHANGE);
From JD Design Freeware - Flipss.exe (download 12kb) is a command line utility that will set SPI_SETSCREENSAVEACTIVE for you.
"FlipSS.exe -h" to see the current state.
"FlipSS.exe /on" to set the screensaver on.
"FlipSS.exe /off" to set the screensaver off.
AutoHotkey can set SystemParametersInfo(SPI_SETSCREENSAVEACTIVE) with a 1-liner DllCall in script to easily accomplish this with a .ahk script.
AutoHotkey code to disable Screensaver:
DllCall("SystemParametersInfo", Int, 17, Int, 0, UInt, NULL, Int, 2)
AutoHotkey code to enable screensaver:
DllCall("SystemParametersInfo", Int, 17, Int, 1, UInt, NULL, Int, 2)
Reference Forum Threads:
F13Key - Toggling Screen Saver with SystemParametersInfo
SKAN - How to Disable Screen Saver Temporarily
I realize this is an old thread, but I'm faced with this issue for the first time (work machine is totally locked down, as far as changing super short sleep time, screensaver, etc. - I can't even change my desktop background). I've looked around at solutions, some seemingly way overcomplicated and some...not so much.
Some of my colleagues are using Caffeine. But that is surely some kind of spyware, etc., as it refuses to run if there is not an open internet connection.
So I found this (and modified it slightly), which is exactly what Caffeine does (except Caffeine does it every 59 seconds), without all the...at best, bloatware.
In PowerShell, execute the following 2 command lines:
$WShell = New-Object -Com "Wscript.Shell"
while(1) {$WShell.SendKeys("{F15}"); sleep 200}
Or you can make it a one-liner if you like:
while(1) {(New-Object -Com "Wscript.Shell").SendKeys("{F15}"); sleep 200}
(the latter of which seems like it would leak memory, but it does not seem to at all)
Once you run either of those, your screen will NOT lock, until you do ctrl-c, or close the Powershell window (in the latter version only, it seems, the ctrl-c may not happen until the sleep interval elapses).
Note that there is no F15 key, at least on any keyboard I've ever seen (but it's a legit windows keystroke), so there are no side effects. Now, if you your IT dept. is exceptionally paranoid, they may flag an F15 keystroke (mine is super paranoid, but they haven't noticed anything for months). If so, use something like scroll-lock instead.
Both of these 100% work on my win10 machine. Simple is good!

Resources