As part of our quest to improve our editing macros, we inevitably need a way to streamline reassigning keyboard shortcuts for our growing list of macros. Adding new keyboard shortcuts is a little messy in Word VBA, so this function tackles part of the problem by determining a unique keycode for any keyboard shortcut combination.
Thanks for your interest
This content is part of a paid plan.
Get Keycode Function
Assigning keyboard shortcuts to macros or commands is called “keybinding.” Adding a new keybinding is clumsy in Word VBA (see previous introductory article), but we can simplify the process with a few of our own macros and functions.
Depending on how we structure our simplified keyboard shortcut assignments, it’s useful to create a few functions to break the process down into smaller bite-sized chunks … which brings us to a long detour with this workhorse function. Regardless of how the assignments are set up, we will need to determine the unique “keycode” for whatever key combination is specified.
Fortunately, creating this initial function accomplishes the bulk of the work necessary to streamline and automate our own keyboard shortcut assignments. We’ll follow up with several additional articles including a few more functions to further streamline the process. In the last article, we’ll assemble the pieces to create our own keybinding macros.
This article is part of a series of mostly member articles to automatically assign our own keyboard shortcuts in VBA. Each article covers different aspects of the small project.
- Simpler approach to get a keycode for any key combination (lacks error checks)
- More concise but programmy version to get a keycode for any key combination (includes error checks; this article)
- A function that validates whether isolated keys are allowed for a tentative keyboard shortcut
- Another macro assembles the pieces to automatically assign or reset keyboard shortcuts for macros or Word commands
While lengthy, one advantage of the current pair of functions is they are clear. If you prefer a more concise but more programmy version, see the improved member article (introductions overlap). We further add some logical error checks to catch potential problems earlier.
Modifier keys
What are modifier keys?
You’ve tapped them thousands of times and probably not called them that.
Modifier keys allow us to change the action associated with another main key. The catch is the application must be set up to interpret the alternative action for any keyboard shortcut. Word installs out of the box with a default set of keyboard shortcuts, but we can also assign our own macros to custom keyboard shortcuts through VBA.
There are additional considerations for some keys. For example, the Shift key usually needs to be combined with at least one other modifier key because it has pre-defined actions based on the other key pressed (capitalization or accessing alternative characters). We’ll deal with these special cases in a follow-up article. Moreover, some key combinations are simply not recognized by Word’s custom keybinding methods, and the specifics vary between Word for Windows or Mac.
Windows
In Word for Windows, the available modifier keys are Control, Alt, and Shift.
No Windows command key in Word
We cannot use the Windows Command key as a modifier key in Word (please change this Microsoft). There are some workarounds to use it at the operation system level using PowerToys or AutoHotkey apps, but both are beyond the scope of this article.
Mac
Macs have four (really five) modifier keys including Command, Control, Option, and Shift. For this function, we can use any three because a standard VBA function we use later to "build" a keycode for a given keyboard shortcut only allows up to three modifier keys.
All four modifier keys?
See the improved member article if you want a workaround to allow automatically assign all four modifier keys to a keyboard shortcut. Otherwise, we can still manually assign such a keyboard shortcut with all four modifier keys by using the Tools → Customize keyboard … menu option (see the introduction of this previous article for Mac or this video for Windows).
No Function (Globe) key use
At the OS level, Macs also have the “Function” (Globe) key, which is different from the “Function Keys” (how’s that for clarity) along the top of most keyboards, but it can’t be used for keyboard shortcuts in Word.
What about no modifier keys?
What keyboard shortcuts work without any modifier keys?
We can assign keyboard shortcuts directly to function keys, arrow keys, Insert, Delete, Page Up and Page Down, Home, End, and probably a few others without using any modifier keys. However, all but the function keys have pre-defined fundamental actions to varying degrees in Word. The function keys are also assigned the specific actions but nothing as fundamental to word processing as the others.
I override the function keys with some of my own macros because I don’t use most of Word’s default assignments, but I leave the others alone unless I am binding it with at least one modifier key.
Function skeleton
An important task for assigning keyboard shortcuts in Word VBA involves determining the unique keycode for a given combination of shortcut keys. A function skeleton to accomplish this is:
We’ll end up creating two versions for Windows and Mac since the steps are different enough to be a problem.
An underscore character _ just continues the current line through the next as far as the VBA editor is concerned. Using it just allows a human to read it more easily.
Parameters
The function requires three arguments from the user.
I like to precede plain text String variables with an “s” or “S” to identify the variable type. A capital first letter implies the variable value will not change inside the function. VBA does not care nor enforce either “rule” as long as we don’t try to use one of its reserved words.
Modifier keys text string
The first parameter requires a plain text string telling the macro what the modifier keys are for the keyboard shortcut. We imagine something like “Control+Shift”. We’ll call this variable sModifierKeys.
We convert any alphabetic characters in sModifierKeys to lowercase inside the function, but any changes to String arguments in VBA can leak back outside the function. The ByVal keyword tells VBA not to change the argument value given by the user.
The “main key” such as “A” or “9” does not need to be included in the sModifierKeys text. As far as this function is concerned, plain text values of “Control+Shift” and “Control+Shift+A” will give the same keycode function result provided the next parameter is specified correctly. Any “+A” part of the keyboard shortcut text will be ignored by the steps that process the sModifierKeys variable. An upcoming article will allow us to interpret a keyboard shortcut assignment in this more convenient form “Control+Shift+A”.
Main key
The second parameter requires a standard Word key constant from the WdKey enumeration. The table includes all text characters as well as most special keys like Home or Delete (certain keys like arrows are omitted for some reason). For example, we could specify an “A” key by providing the function a value of wdKeyA.
Advantages of enumerations
Enumerations are just constant lists with names that are easier to read and remember, but we can also treat enumerations as special numeric data types for a variable. As done here, the MainKey variable is given a WdKey data type. In VBA, using an enumeration as a data type is more for readability and an organizational convenience (some languages constraint assigned values).
Macro name
We also need the plain text name of the macro to run which we’ll store in the variable SMacro. We can assign keyboard shortcuts to macros but not functions. An awkward system exists in VBA to provide a single string parameter in a keybinding assignment, but I wouldn’t use it unless it was forced on me.
Return value
The function returns a Long value because a specific KeyCode property we’ll cover in a later article requires a unique identifier with a Long number type. A Long variable type is essentially a bigger Integer in VBA.
Interpret the modifier keys
We need to tell VBA the specific key combination for the keyboard shortcut. Every possible key combination has a unique value which we use to identify the keyboard shortcut assigned to our macro or command. Fortunately, VBA also provides a BuildKeyCode(…) function to make generating the keycode easier, but a companion member article provides a slight generalization with a few advantages.
We need to determine the various modifier keys for the shortcut from the sModifierKeys text the user provided, bit it gets a little messy.
Define keycode number variable
We start by defining a numeric variable myKeyCode which will temporarily store the aforementioned unique key combination number.
We’ll eventually replace this variable with the function name to assign the return value, but it provides a working variable for our assignments until then.
What about Control?
Let’s consider a single modifier key to get started. How can we detect whether the user specified a “Control” modifier key?
Use lowercase characters
To avoid any comparison issues, we convert the text string to lowercase using the LCase(…) function.
LCase(…) does what its abbreviated name implies. It converts all alphabetic characters in the text or string variable to lowercase without affecting existing uppercase text. Any non-alphabetic characters are ignored. This step just simplifies the logic as we move forward because text searches are generally case sensitive in VBA.
Using Like to search text (review)
We’ve previously used a search operator called Like. Using it to search text looks something like:
Don’t get hung up on Like being called an “operator”. For example, a plus + sign is also an operator that adds numbers and spits out another number as the result. Like applies a search pattern to some text and gives us a True or False (Boolean) values based on whether it found a match.
In the statement above, SomeText is the plain text to search which is usually given as a string variable storing the desired text. The SearchPattern is a specific pattern of characters to match in SomeText, but it also allows for a few special wildcard characters. For example, an asterisk * tells the Like search to allow any (as many as needed) or no characters in that position.
Another wildcard character includes a question mark “?”. It’s like an asterisk, except is only looks for a single or no character in its search pattern position. Also, a hashtag “#” looks for a single digit number 0 through 9 at its location in the search pattern.
Looking for Control
We already converted the entire text of the sModifierKeys variable to lowercase, so we want to know whether the word “control” is anywhere inside it.
Our search pattern will be something like “control”, but this isn’t sufficient for our search since nothing else is present before or after the word. More specifically, this pattern would require the searched text to begin and end with the word “control”, so this pattern looks only for the explicit text “control” while not allowing any other characters in the string. That’s not very useful for general searches.
We need to allow for other possible characters on either side using asterisks. The revised search pattern looks like “*control*”. The asterisks tell Like to allow no or any number of characters before or after the word control which better matches the intuitive expectation when searching for a word in the search text.
Our revised search operation on the sModifierKeys variable now looks like:
This results in a True or False value depending on whether the search matched the text control anywhere in the sModifierKeys variable.
Conditional statement
Since Like returns a True or False value, we can set up a conditional If statement using it to do something when we detect the word control in our modifier key text variable.
What about the other modifier keys?
Here’s where it gets a little messy. We have two more modifier keys to consider (three in Word for Mac), and they can occur in any combination or order.
Ughhh.
But hey, we’re still having fun, and our final keyboard shortcut assignment macros will be nice, so don’t quit on me.
There are several ways to track which modifier keys were given. For this presentation, we’ll use an abbreviated text key to decide which modifier keys to apply to the keyboard shortcut assignment. This variation is clean and clear even if long but see the member article for a more concise generalization if you’re interested.
Windows
We’ll build a custom string to indicate which combination of modifier keys was given. Then we'll use it to build the respective keycode. Our constructed possibilities include:
We also need to ensure any order variations such as “Control+Alt+Shift” or “Alt+Shift+Control” yield the same unique key combination. The Select Case statement used below would allow us to include each variation without much effort, but we have full control over the abbreviated keys text, so it’s tidier to construct it in a specific order.
Mac
For Mac systems, we can use a similar pattern except we’ll use “D” for “Command” since “C” is already taken for Control.
The BuildKeyCode function we'll use below cannot accept all four Mac modifier keys at the same time, but they can still be set up manually through the Tools → Customize keyboard … menu option. The member article version handles this special case.
Key combination issues
Not every combination will work.
Some combinations are claimed by the operating system (such as Control+Alt+Delete in Windows), Windows and Mac implement punctuation and some other special keys differently, so they aren’t detected properly as a valid keyboard shortcut within Word. Also, some keys like the Shift key have a specific use in a Word processor.
Separation character does not matter
We haven’t worried about whether the modifier keys are given as “Control+Alt” or “Control-Alt” or any other variation. The above search technique with Like ignores the separation characters which is handy.
Overriding existing keyboard shortcuts
We can override any Word keyboard shortcuts and even some keyboard shortcuts defined at the OS level, but there is no general rule. Mac systems tend to be a little pickier about overriding system shortcuts. We need to test any unusual combination to see if it works.
An alternative key combination approach
The above approach is clear with the abbreviated keys text, but another variation creates a little subcode number:
This process constructs a binary number represented as a decimal. With this numbering scheme, each modifier key combination corresponds to a unique number from 0 to 15 in Word for Mac or 0 to 7 in Windows. I think this approach is cleaner, but it’s a little more programmy if you’re not used to thinking like this, so I went with the more direct variation using the abbreviated keys text for this article.
Construct the abbreviated key combination
As an intermediate step, we need to build and store the abbreviated modifier key text.
Setup key code text variable
Declare a plain text string variable named sKeyCode and set it equal to an empty string “”.
An empty string implies no modifier keys were detected in the sModifierKeys text. It’s actually the default value for a new String variable, but I like to be clear.
How do we construct the abbreviated keycode text?
We’ll start from the empty string sKeyCode and append a “C” to it if we matched control in the modifier keys text.
Adding strings together looks natural since we just use a plus + sign like we do with numbers. This is called concatenation with strings, and it just jams them together into a new, longer string. We then store the result back in the original variable sKeyCode using the equals = sign.
Additions like this are very common across many programming tasks, so other languages have a more convenient += shorthand symbol to do the same thing (meaning add and immediately store the result back in the same variable), but VBA doesn’t include it [sad face].
If the key text was not matched, then the sKeyCode variable remains unchanged for the next test.
Command conditional statement
In Word for Mac, we start by verifying whether Command was specified in the modifier key text. Following the earlier search pattern, we have:
Since the conditional statement is short, we’re using the more compact form on one line.
Control conditional statement
Both Mac and Windows have a Control key, so we verify whether it was given in the modifier key text. Following the earlier search pattern, we have:
Of course, on a Mac this is the second check in our sequence.
Alt or Option conditional statement
For the Alt key, we use:
On a Mac, we instead reference the Option key:
We perform this match after the control check to ensure the order we’re building our abbreviated modifier key text.
Shift conditional statement
Both OSs use a Shift key, and we check for it last:
Order of modifier key words does not matter
In the sModifierKeys variable, the key names can be given in any order, but the internal order (to our function) for the sKeyCode text is important because our upcoming keys selector expects the keys to be represented in that order to limit the already lengthy list of choices. Fortunately, we control the order in which we construct our abbreviated key combination.
Regardless of the modifier key word order in sModifierKeys, we check each key condition independently in our desired order to produce the exact abbreviated list we’ll use below.
Other key text variations
We could allow other key representations such as “ctrl” or even a relatively standard symbol for Control of “^”. Also the alt key is often represented by “%”, and shift is usually a “+”. Mac modifier keys have standard symbols as well. In the interest of clarity, I used a simpler version for this article requiring full words for each modifier key.
Build the numeric key code
We’ve determined the modifier keys, and we're also given the MainKey as a function argument. Now we have all the information we need to build the unique number for the intended keyboard shortcut combination.
Select Case statement review
Since there are so many modifier key combinations to choose between them, we’ll use a Select Case statement which is a generalization of an If statement. We’ve covered it before, but the statement looks like:
It begins with Select Case … and ends with End Select.
SomeVariable is the variable we’ll be testing, and it can be most standard data types like a string in this function or any of the standard number types. Each case begins with Case and a comparison value like:
We can include as many specific cases as desired. There are more general Cases allowing numeric or string ranges, but we don’t need them for this macro.
Once the Select Case statement finds a match, the steps just below the Case are run. Once a match is found, no other cases are tested. The Case Else steps are run only if no other matches are found, but including it is optional.
Select Case statement
Our list of possible cases was given earlier. We’ll be comparing our constructed sKeyCode abbreviated key string to each item in the list of cases. Plain text string matches using an equals = sign must be exact, so “CAS” is different than “CA” or “AS”.
Windows
The Windows modifier keys select statement looks like:
Mac
The Mac version is even longer with twice as many cases, but it is still clear.
Error message
We can create a descriptive error message to notify the user when something goes wrong. The standard VBA function MsgBox opens a popup dialog:
The MsgBox function works on both Windows and Mac systems, and it takes a plain text message in double quotes or a string variable as a required argument. Sometimes it’s clearer to assign the message text to a separate string variable, but it’s just a preference.
The more details we put in the error message (within reason), the more helpful it will be if an error does occur. Since I included three different bits of information in the above message, I used a paragraph mark character vbCr to separate the lines in the pop-up message.
Use such messages judiciously (no less but no more than what is needed) in your macros since they can become annoying if you put them everywhere. I usually reserve them for error messages about significant problems or genuine warnings something like “You’re about to delete a whole heading … continue?”
Invalid value
If we encounter a problem, we need to specify an invalid value for the keycode. Since all possible keycodes are positive, we can use a negative value to indicate an error.
We assign this in the Case Else because it is run when no other matches are found.
Build the keycode
In each case, we need to run the BuildKeyCode(…) function using the identified modifier keys along with the given MainKey.
The BuildKeyCode function returns a unique numeric value for the keyboard shortcut combination. We’ll use the value in an upcoming article where we assign the keyboard shortcut to the macro. Here, we store the result in the previous variable myKeyCode, but the function below will use the variable name to assign the return value.
We provide the BuildKeyCode(…) function with each key value separated by commas. The key values come from the Word WdKey enumeration table. For example, if our modifier key text from sModifierKeys argument was specified as “Control+Shift” with a MainKey argument of wdKeyA (the “A” key constant), we would run the BuildKeyCode function like:
We insert similar commands into each Case above with appropriate changes for the various modifier key combinations. This is easy to read, but it’s not the most concise, so see the corresponding member article if you’re interested in that.
Windows
The Windows modifier keys select statement looks like:
Mac
The Mac version is even longer with twice as many cases, but it is still relatively clean and easy to read.
Return function keycode value
This function returns the keycode based on all the above steps. We’ve stored it in the myKeyCode variable, so we can just return this value by setting the function name equal to it.
We can just skip the intermediate numeric assignment to myKeyCode and assign the BuildKeyCode function values directly to the function name GetKeyCode. This saves a step at the end of the function, but it’s just a preference.
Final GetKeyCode functions
Putting the steps together, the function to get a keycode based on a keyboard shortcut text string and a main key is:
Windows
We used the function name for every BuildKeyCode assignment rather than myKeyCode to save a step at the end of the function.
Mac
The Mac version is longer. Unfortunately, we need to use a slightly different function name GetKeyCodeMac because VBA doesn’t allow repeated function names (called “function overloading”) if you happen to write macros in both Word for Windows or Mac.
This function is clean but long and awkward. It covers most cases, so if you’re happy with it stop here.
If you prefer to be a little more concise or more thorough, we dig a little deeper in the next article where we allow all four Mac modifier keys and add some additional error checks. For example, I prefer the function to check whether a special key can be used by itself. Also, using just a Shift modifier key could be problematic since Shift has a clearly defined action for most keys in Word.