I know this is an old question, but I recently needed similar functionality, and the answer provided had some limitations that I had to deal with how it handled (or did not handle) Del, Backspace, function keys, etc.
The fix is โโto return the original message instead of the translated one.
The use of the class module with events has also changed, since it works fine in Excel 2010, and I did not want to copy the same code to multiple sheets:
Class module
Option Explicit Private Type POINTAPI x As Long y As Long End Type Private Type MSG hwnd As Long Message As Long wParam As Long lParam As Long time As Long pt As POINTAPI End Type Private Declare Function WaitMessage Lib "user32" () As Long Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" _ (ByRef lpMsg As MSG, ByVal hwnd As Long, _ ByVal wMsgFilterMin As Long, _ ByVal wMsgFilterMax As Long, _ ByVal wRemoveMsg As Long) As Long Private Declare Function TranslateMessage Lib "user32" _ (ByRef lpMsg As MSG) As Long Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _ (ByVal hwnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, _ lParam As Any) As Long Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Const WM_KEYDOWN As Long = &H100 Private Const PM_REMOVE As Long = &H1 Private Const WM_CHAR As Long = &H102 Private bExitLoop As Boolean Public Event KeyPressed (ByVal KeyAscii As Integer, _ ByVal KeyCode As Integer, _ ByVal Target As Range, _ ByRef Cancel As Boolean) Public Sub StartKeyPressInit() Dim msgMessage As MSG Dim bCancel As Boolean Dim iMessage As Integer Dim iKeyCode As Integer Dim lXLhwnd As Long On Error GoTo errHandler Application.EnableCancelKey = xlErrorHandler 'Initialize this boolean flag. bExitLoop = False 'Get the app hwnd. lXLhwnd = FindWindow("XLMAIN", Application.Caption) Do WaitMessage 'Exit the loop if we were aborted If bExitLoop Then Exit Do 'Check for a key press and remove it from the msg queue. If PeekMessage(msgMessage, lXLhwnd, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) Then 'Store the virtual key code for later use. iMessage = msgMessage.Message iKeyCode = msgMessage.wParam 'Translate the virtual key code into a char msg. TranslateMessage msgMessage PeekMessage msgMessage, lXLhwnd, WM_CHAR, WM_CHAR, PM_REMOVE bCancel = False RaiseEvent KeyPressed(msgMessage.wParam, iKeyCode, Selection, bCancel) 'If not handled, post back to the window using the original values If Not bCancel Then PostMessage lXLhwnd, iMessage, iKeyCode, 0 End If End If errHandler: 'Allow the processing of other msgs. DoEvents Loop Until bExitLoop End Sub Public Sub StopKeyPressWatch() 'Set this boolean flag to exit the above loop. bExitLoop = True End Sub
Using
Option Explicit Dim WithEvents CKeyWatcher As KeyPressApi Private Sub Worksheet_Activate() If CKeyWatcher Is Nothing Then Set CKeyWatcher = New KeyPressApi End If CKeyWatcher.StartKeyPressInit End Sub Private Sub Worksheet_Deactivate() CKeyWatcher.StopKeyPressWatch End Sub '\\This example illustrates how to catch worksheet '\\Key strokes in order to prevent entering numeric '\\characters in the Range "A1:D10" . Private Sub CKeyWatcher_KeyPressed(ByVal KeyAscii As Integer, _ ByVal KeyCode As Integer, _ ByVal Target As Range, _ Cancel As Boolean) Const MSG As String = _ "Numeric Characters are not allowed in" & _ vbNewLine & "the Range: """ Const TITLE As String = "Invalid Entry !" If Not Intersect(Target, Range("A1:D10")) Is Nothing Then If Chr(KeyAscii) Like "[0-9]" Then MsgBox MSG & Range("A1:D10").Address(False, False) _ & """ .", vbCritical, TITLE Cancel = True End If End If End Sub