🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA SendKeys in Excel — Syntax, Key Codes, and Why It Runs Out of Order

|

VBA SendKeys in Excel — Syntax, Key Codes, and Why It Runs Out of Order

TL;DR — SendKeys does not press keys. It drops keystrokes into a queue, and the window that has focus when they are read receives them. Two consequences explain nearly every complaint about it. Keys sent to Excel are read only when your macro yields, at the end or at DoEvents, so the lines after SendKeys run first. And keys sent to another program go to whatever window has focus when they arrive, which may not be the one you meant. Characters such as + ^ % ~ ( ) are codes, so a literal 50% must be written 50{%}. Use SendKeys only as the last line of a macro, to leave Excel's own interface in a state for the user. For everything else there is an object-model call that does the job directly.

Sub EditNoteCell()
    Range("D2").Select
    SendKeys "{F2}"                 ' last line: the user lands in edit mode in D2
End Sub

This is the third article in a cluster on macros that Excel runs for you: from a shortcut key, from the clock with OnTime, and here, from keystrokes you fake. The cluster idea is that none of these calls anything directly: each one puts a request in a queue, and the request is handled when Excel is idle and ready. For SendKeys, the request is a keystroke, and the queue is the keyboard input of whichever window is in front.

What you'll learn

  • The mental model: keystrokes in a queue, delivered to the focused window
  • Syntax, key codes, and how to send literal special characters
  • Why SendKeys seems to run out of order
  • Focus, the wrong window, and why it cannot run unattended
  • The Num Lock side effect
  • The few jobs SendKeys is right for, and what to use instead

The mental model: keystrokes in a queue

When you press a key, Windows puts it in the input queue of the window that has focus, and that program reads it when it gets around to it. SendKeys writes into that same queue, as if the keys had been typed.

So SendKeys decides what is typed, but not when it is read or who reads it:

  • When: the receiving program reads its queue when it is free. If the receiver is Excel, Excel is busy running your macro, so it reads the keys after your macro yields.
  • Who: the keys go to the window with focus at the moment they are read, not the window you had in mind when you wrote the line.

Keep those two questions in mind and the strange behaviour becomes predictable.

Syntax and key codes

There are two forms, with the same arguments:

SendKeys "^c"                       ' VBA statement
Application.SendKeys "^c"           ' Excel's method
SendKeys "~", True                  ' Wait:=True, wait until the keys are processed

Most keys are written as themselves: SendKeys "abc" types abc. Modifiers and named keys use codes:

Code Key
^ Ctrl, as in ^s for Ctrl+S
+ Shift, as in +{F10} for Shift+F10
% Alt, as in %{DOWN} for Alt+Down
~ or {ENTER} Enter
{TAB}, {ESC}, {BS}, {DEL} Tab, Escape, Backspace, Delete
{UP}, {DOWN}, {LEFT}, {RIGHT} arrow keys
{HOME}, {END}, {PGUP}, {PGDN} navigation
{F1} to {F16} function keys
{LEFT 3} a key repeated 3 times
+(abc) Shift held down while a, b and c are pressed

There is no code for the Windows key or for Print Screen, and SendKeys cannot click the mouse.

Literal special characters must be braced

Because + ^ % ~ ( ) are codes, they cannot be typed as text. This sends 50 and then presses Alt:

SendKeys "Discount 50%"             ' wrong: % means Alt
SendKeys "Discount 50{%}"           ' right: braces make it literal

The characters to brace are + ^ % ~ ( ) { } [ ]. If the text comes from a cell or a user, escape it in a function instead of by hand:

Function EscapeKeys(ByVal s As String) As String
    Dim i As Long, ch As String, result As String
    For i = 1 To Len(s)
        ch = Mid$(s, i, 1)
        If InStr("+^%~(){}[]", ch) > 0 Then
            result = result & "{" & ch & "}"
        Else
            result = result & ch
        End If
    Next i
    EscapeKeys = result
End Function

Why SendKeys seems to run out of order

This is the most common question: the code after SendKeys runs before the keys do. A typical example:

Sub CopyWithKeys()
    Range("A1:C10").Select
    SendKeys "^c"                   ' queued, not yet read
    Worksheets("Report").Paste      ' runs now: the clipboard is still empty
End Sub

SendKeys put Ctrl+C in Excel's queue and returned at once. Excel will read the queue when it is free, and it is not free: it is running CopyWithKeys. So Paste runs first, and the copy happens after the macro ends. Wait:=True does not help here, because Excel cannot process keys aimed at itself while your code is still running. A DoEvents after the SendKeys sometimes lets the keys through, and sometimes not, which is worse than never.

The rule that follows: keys sent to Excel belong on the last line of the macro. Anything that must happen after them should not depend on them. And here the right answer is not to fix the timing at all, but to use the object model, which runs exactly when the line runs:

Range("A1:C10").Copy Destination:=Worksheets("Report").Range("A1")

The copy and paste guide covers the full set of copy methods.

Focus decides who receives the keys

When you send keys to another program, the second question takes over: who has focus when the keys are read?

Shell "notepad.exe", vbNormalFocus
Application.Wait Now + TimeSerial(0, 0, 1)   ' hope Notepad is ready by then
SendKeys "Monthly total: 1250~"

If Notepad is slow to open, the keys arrive in Excel instead and type into a cell. If a notification, an email or a chat window takes focus during that second, the text goes there. On a locked screen or a remote session that is minimised, the keys go nowhere. And Windows blocks keys from a normal program to a program running as administrator, so SendKeys into an elevated window silently does nothing.

That makes the rule for unattended work absolute: SendKeys cannot run when nobody is watching. A macro started by OnTime or Task Scheduler that uses SendKeys will, sooner or later, type into the wrong place. To control another program, use its object model through CreateObject, or start it with arguments through Shell.

The Num Lock side effect

A long-known side effect: on many Windows machines, the VBA SendKeys statement switches Num Lock off, and sometimes affects Caps Lock. Users notice that their number pad stopped working after running your macro.

Code that sends {NUMLOCK} to switch it back only toggles it again, so it is right on some machines and wrong on others. Application.SendKeys is reported to cause it less often than the statement, but neither is reliable. The real fix is the same as for everything else on this page: send fewer keys. A macro that only uses SendKeys once, as its last line, rarely gets noticed for this.

The judgment call: a last resort, for Excel's own interface

SendKeys is fit for one kind of job: leaving Excel's own interface in a state for the user, as the last line of a macro. Putting a cell in edit mode with {F2}, or opening a filter drop-down with %{DOWN}, are reasonable, because the user is there and nothing in your code depends on the result.

For everything else, there is a direct call that runs when the line runs, on any keyboard layout, with any window in front:

What people send keys for Use instead
^c, ^v to copy and paste Range.Copy, PasteSpecial
^s to save ThisWorkbook.Save
~ to answer an Excel prompt Application.DisplayAlerts = False
% key sequences for a ribbon command Application.CommandBars.ExecuteMso "PasteValues"
opening a built-in dialog Application.Dialogs(xlDialogPrint).Show
typing into another program its object model via CreateObject

Shortcut sequences such as Alt, H, V, V also depend on the Office language: the ribbon letters differ between English, German and Spanish Excel, so a SendKeys sequence written on one is wrong on the other. The calls in the right column do not have that problem. The DisplayAlerts guide shows how to handle Excel's own prompts without pressing any keys.

How ExcelMaster helps

SendKeys code usually works on the machine it was written on, on the day it was written. It breaks on a different keyboard layout, a slower PC, a different Office language, or the first time a notification steals focus.

ExcelMaster reads the macro, works out what each SendKeys line is trying to do, and replaces it with the object-model call that does the same job directly, so the macro no longer depends on timing, focus or keyboard layout.

Frequently asked questions

How do I send the Enter key with SendKeys?

Use SendKeys "~" or SendKeys "{ENTER}". The tilde is the main Enter key; {ENTER} is the Enter key on the number pad, and both work in most programs.

Why does SendKeys not work in my macro?

Usually it works, but later than you expect: keys sent to Excel are read after your macro yields, so the lines after SendKeys run first. Keys sent to another program go to whichever window has focus when they are read. Put SendKeys on the last line, or replace it with an object-model call.

Why does SendKeys turn off Num Lock?

It is a long-known side effect of the VBA SendKeys statement on Windows. Sending {NUMLOCK} back only toggles it and is not reliable. Use SendKeys as little as possible.

What is the difference between SendKeys and Application.SendKeys?

SendKeys is the VBA statement and Application.SendKeys is Excel's method. They take the same key codes and the same Wait argument. Both write into the input queue of the window with focus.

How do I send a percent sign or plus sign with SendKeys?

Put it in braces: {%}, {+}, {^}, {~}, {(} and {)}. Without braces these characters are read as Alt, Shift, Ctrl, Enter and grouping codes.

Tested in

Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-10-03.

Related guides: VBA Shortcut Key · VBA OnTime · VBA Copy Paste · VBA DisplayAlerts · VBA DoEvents · VBA Shell · VBA CreateObject · VBA Wait