Reputation: 743
I have the following code for an Access form. It appears as if the SendKeys
is messing with my NumLock key by toggling it on and off as I open and close the form.
For perfectly valid reasons which I don't want to get into, I really do not want to completely hide the ribbon (I want the pull down menus still accessible) so the DoCmd.ShowToolbar
command is not my preferred way of doing it.
Does anyone have any suggestions as to how I can modify the code below to accomplish what I want using the SendKeys
command?
Using Access 2007 so the command
CommandBars.ExecuteMso "MinimizeRibbon"
is not available to me.
By the way, database will be distributed so solution must be contained within database.
Private Sub Form_Close()
' Unhide navigation pane
DoCmd.NavigateTo "acNavigationCategoryObjectType"
DoCmd.Maximize
' Maximize the ribbon
RibbonState = (CommandBars("Ribbon").Controls(1).Height < 75)
Select Case RibbonState
Case True
SendKeys "^{F1}", True
Case False
'Do nothing, already maximized
End Select
End Sub
Private Sub Form_Load()
' Hide navigation pane
DoCmd.NavigateTo "acNavigationCategoryObjectType"
DoCmd.Minimize
Debug.Print Application.CommandBars.Item("Ribbon").Height
' Minimize ribbon
RibbonState = (CommandBars("Ribbon").Controls(1).Height < 100)
Select Case RibbonState
Case True
'Do nothing, already minimized
Case False
SendKeys "^{F1}", False
End Select
End Sub
Upvotes: 25
Views: 67714
Reputation: 3257
I agree with BTjacker that it's likely best to just use keybd_event
from the start instead of SendKeys
. However, if you're not interested in that, I hope the code below helps - it works for me on Windows 10 (x64).
The answer above from SendETHToThisAddress is among many similar posts that did not work for me. After searching around, this post on MrExcel finally worked for me so I trimmed out some unnecessary operating system checks (as it's unlikely to find a user on Windows 95 or 98).
The original snippet is quite similar to this older SO answer and the example on this Microsoft help page. I'm also aware that my result is similar to this SO answer and Storax's but only the former worked for me.
Option Explicit
Private Const KEYEVENTF_EXTENDEDKEY = &H1
Private Const KEYEVENTF_KEYUP = &H2
Private Const VK_NUMLOCK = &H90
Private Const SCANCODE_NUMLOCK = &H45
#If VBA7 Then
Private Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As LongPtr)
Private Declare PtrSafe Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
#Else
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Declare Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
#End If
Public Sub NumlockTOGGLE()
If CheckButtonOnOff(VK_NUMLOCK) Then
Debug.Print "Numlock is currently on. Toggling OFF."
NumlockOFF
Else
Debug.Print "Numlock is currently off. Toggling ON."
NumlockON
End If
End Sub
Public Sub NumlockON()
SetButtonOnOff VK_NUMLOCK, True 'force key state ON
End Sub
Public Sub NumlockOFF()
SetButtonOnOff VK_NUMLOCK, False 'force key state OFF
End Sub
Private Function CheckButtonOnOff(ByVal virtualKey As Variant)
CheckButtonOnOff = False
Dim keyboardBuffer(255) As Byte
GetKeyboardState keyboardBuffer(0) 'get state of all keys
If keyboardBuffer(virtualKey) And 1 Then 'state of param key is ON
CheckButtonOnOff = True
End If
End Function
Private Sub SetButtonOnOff(ByVal virtualKey As Variant, ByVal buttonStateDesired As Boolean)
Dim buttonState As Boolean
buttonState = CheckButtonOnOff(virtualKey) 'get state of only param key
If buttonState <> buttonStateDesired Then
keybd_event virtualKey, SCANCODE_NUMLOCK, KEYEVENTF_EXTENDEDKEY Or 0, 0 'key press
keybd_event virtualKey, SCANCODE_NUMLOCK, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'key release
End If
End Sub
Upvotes: 0
Reputation: 1
Use: Set SheetShell = CreateObject("WScript.Shell") SheetShell.SendKeys "~", True
'for sending the "Enter" key for example.
This approach works consistently.
Using the Application.SendKeys "~", True approach will toggle the the Numlock On and Off if you run the the script multiple times (for example)
Upvotes: 0
Reputation: 1
I randomly tried this command and my problem solved try it and let me know if it work my first command was this and always numlock turned off
SendKeys "{F12}", True
I replace it with this and problem solved SendKeys "{F12},{NUMLOCK}", True
Upvotes: 0
Reputation: 1
I know this is old, but I've been annoyed by this bug for years and it still happens a decade later. I just found an easy workaround. I don't really know how it works, but it does and I want others who find this thread to try it out, so here it goes:
Forget about checking NumLock status and the API and everything else. Just use your sendkeys command as you intended and add the following after the last sendkeys:
DoEvents
Sendkeys "^", True
I found that the PtrSafe GetKeyState function only returned the status correctly when the key was actually pressed, and not when the state was changed as a result of the VBA bug. DoEvents makes the bug go off, and then you send another key (I chose control because it had no effect on my application at all) and that triggers another instance of the bug, effectively undoing the first one.
During tests I noticed that CapsLock is also affected by this bug and it gets restored as well when forcing the bug once again.
I am aware that this is not a technical solution, but it's a very practical one that has consistently worked and effectively solved the problem for me. I'm sure others will find an explanation, and I will be very happy if at least one person finds this helpful.
Upvotes: 0
Reputation: 570
Posting an adaptation of SendETHToThisAddress's
answer which worked for me, as their answer did not work (fully.). . .
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Const NumlockKey = 144
Public Function Reset_NumLock() As Boolean
Reset_NumLock = KeyState(NumlockKey)
If (Reset_NumLock = False) Then 'Checking for True gave incorrect results
''''''''''''''''''''''''''''''''''''''
' Modified This - removed MsgBox too '
''''''''''''''''''''''''''''''''''''''
With CreateObject("WScript.Shell")
.SendKeys "{NUMLOCK}"
End With
Else
Debug.Print "Num Lock was on! WOOT!"
End If
End Function
Private Function KeyState(lKey As Long) As Boolean
KeyState = CBool(GetKeyState(lKey))
End Function
In my instance of Windows, the
KeyState
had to beFalse
in order to accurately depict whether the Numlock key was off. Additionally, if I usedApplication.SendKeys
, it NEVER saw the state change correctly and would always alternate the status of my numlock vs keep it on as what appears to be the goal of their function (meaning - if I turned it on, this would turn it off. If I turned it off, this would turn it on. . .).Sending 2x
{Numlock}
also created a problem where it never saw the numlock turned ON, even though it was! (I believe this was due to it being in a specific state/condition when first attempt was made - still - I did not care for this.)Additionally, their answer does not specify whether they are using
Application.SendKeys
or another adaptation of it, so I find it rather ambiguous, and prefer to unequivocally state what kind of adaptation I'm using. With the modified code below - I had 100% success rate with 200 tries (give or take, rapid fire, toggling the numlock key roughly every other interval). . .That said - the only way to prevent the
GetKeyState
function to properly see my Numlock status was to NOT useExcel's SendKeys
but instead useCreateObject("WScript.Shell")
method.Otherwise, the core of this answer is very close to what
SendETHToThisAddress
proposed, as at the core, I ultimately wanted a way to ensure that Numlock was always on.
Upvotes: 0
Reputation: 83
64bit VBA version
Private Declare PtrSafe Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Const VK_NUMLOCK = &H90
If GetKeyState(VK_NUMLOCK) = 0 Then
SendKeys "{NUMLOCK}", True
End If
You could also check the return value from GetKeyState() before using SendKeys() and restore it by either executing SendKeys "{NUMLOCK}" or not when finished.
Upvotes: 1
Reputation: 21
in my case application.senkeys method was creating this problem. so I used
with shell
.sendkeys "{}"
End with
Instead of
with shell
Application.sendkeys ("{}")
End with
Upvotes: 1
Reputation: 71
The SendKeys() function that is built-in VBA has really a side effect that causes NumLock to be deactivated. But you can use a workaround and call another implementation of the same function that is a part of WSCRIPT component (a part of Windows operating system). The following sample code shows, how a reference to this component can be made and then its method called:
Set WshShell = CreateObject("WScript.Shell")
WshShell.SendKeys "^g", True
This way, you get the same functionality (calling Ctrl-G keyboard shortcut in the example), but there is no issue with NumLock in this case.
Upvotes: 7
Reputation: 121
I had similar issue and I found solution on some vba forum. Instead of buggy Sendkeys you can simulate kyes like this.
Option Explicit
'//WIN32API Declare
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
'//WIN32API Constant
Public Const KEYEVENTF_EXTENDEDKEY = &H1
Public Const KEYEVENTF_KEYUP = &H2
Public Const VK_CONTROL = &H11
Public Const VK_SHIFT = &H10
Public Const VK_F6 = &H75
Public Function PreviousTab()
keybd_event VK_CONTROL, 0, 0, 0
keybd_event VK_SHIFT, 0, 0, 0
keybd_event VK_F6, 0, 0, 0
keybd_event VK_F6, 0, KEYEVENTF_KEYUP, 0
keybd_event VK_SHIFT, 0, KEYEVENTF_KEYUP, 0
keybd_event VK_CONTROL, 0, KEYEVENTF_KEYUP, 0
End Function
Other keys can be found here vba forum This "previousTab" function just send Control+Shift+F6 key.
Upvotes: 9
Reputation: 3744
When you do a final sendKeys command in your code, adding in {NUMLOCK} to the statement may do the trick, as noted by RodB and iceBird76. But this is not a good coding practice, and here is why: if anything is different from one time to the next when you run the macro, it may or may not work. I know this because I was experiencing a similar issue myself. When I would do a sendKeys command at the end of my program, sometimes the Num Lock would stay on, but other times it would stay off, just depending on certain variables in my spreadsheet (regardless of whether or not I included {NUMLOCK} in my last SendKeys statement).
I won't get into the details of my own variables, but the point is that to build a program/macro that will keep your Num Lock on consistently, you need to FIRST TEST TO SEE IF THE NUM LOCK IS ON OR OFF, then execute code based upon the present condition.
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Const kNumlock = 144
Public Function NumLock() As Boolean
NumLock = KeyState(kNumlock)
If (NumLock = True) Then
MsgBox ("Num lock was off. Will turn back on now...")
SendKeys "{NUMLOCK}", True
Else: MsgBox ("Num Lock stayed on")
End If
End Function
Private Function KeyState(lKey As Long) As Boolean
KeyState = CBool(GetKeyState(lKey))
End Function
Sub myMainMethod()
'do a function here that includes .SendKeys
Call NumLock
End Sub
This sample program will give you a confirmation message as to whether the Num Lock is on or off, and turn it on if it is off.
Upvotes: 7
Reputation: 762
It's a bug in Microsoft VBA. But there is a workaround.
Use F8 to run through the macro and find where it turns it off. It's usually after a SendKeys
.
Then add an
Sendkeys "{NUMLOCK}", True
after the line to reverse the effect.
If you can't find it, just add it at the end and when it finishes, it will go back. Hopefully, if you add it during the show/hide process, it will work.
Upvotes: 22
Reputation: 41
This line caused my problem:
Application.SendKeys "%s"
SOLVED by changing to this:
Application.SendKeys "{NUMLOCK}%s"
There's no difference between adding {NUMLOCK}
at the beginning or end of the string.
Upvotes: 4
Reputation: 107
This is caused by :
Sendkeys "any key", False
Instead of False
as second parameter, use True
.
Upvotes: 9
Reputation: 1
SendKeys "^{HOME}", True
was turning off the num lock so I just repeated the command and it turns it back on again:
SendKeys "^{HOME}", True
SendKeys "^{HOME}", True
Upvotes: 0
Reputation: 51
Right after your SendKeys statement add these 2 lines:
DoEvents
SendKeys "{NUMLOCK}{NUMLOCK}"
Upvotes: 5