엑셀 VBA 키보드입력 자동인식 모듈 :: KeyPressAPI 총정리

엑셀 VBA 게임을 제작하기 위해서 반드시 알아야 할 KeyPressAPI 모듈 사용법과 마리오 이동을 위한 예제 코드를 살펴봅니다.

홈페이지 » 엑셀 VBA 키보드입력 자동인식 모듈 :: KeyPressAPI 총정리

엑셀로 키보드 입력 인식받기 (KeyPressAPI 모듈) :: 엑셀 VBA 강의 3-5

실습파일 무료E-Book 강의 PPT 파일 완성파일
첨부파일에 이상이 생겼을 경우, 1:1 문의하기로 연락주시면 신속히 해결해드리겠습니다. 

1. User32.dll 라이브러리 사용하기

User32.dll 라이브러리는 마이크로소프트의 운영체제인 윈도우에 기본적으로 설치되는 라이브러리 파일입니다. 라이브러리 파일은 ‘단독’으로 실행될 수 없는 파일이며 주로 다른 프로그램에서 해당 라이브러리 파일을 참조하여 동작하게 됩니다.

그 중 User32.dll 라이브러리는 실행중인 윈도우의 GUI (Graphic User Interface)와 밀접한 연관이 있는 라이브러리로서, 실행중인 프로그램과의 소통(예, 키보드/마우스 클릭 이벤트, 알림창 출력 등)을 주로 합니다. 기본적으로 윈도우 시스템에 큰 영향을 끼치지 않는 안전한 라이브러리이며 윈도우 운영체제에서 프로그램을 제작하기 위해서는 반드시 숙지해야 할 라이브러리입니다.

[링크] 마이크로소프트 User32.dll 라이브러리 설명 바로가기
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/

이번 강의에서는 크게 아래의 3가지 함수를 사용합니다.

FindWindowA 함수
User32 FindWindowA 함수
윈도우에서 실행중인 프로그램 중 하나를 선택하여 Top Level에서 실행되는 정보를 받아옵니다.
PeekMessageA 함수
User32 PeekMesageA 함수
FindWindowA 함수를 통해 받아온 윈도우(프로그램)에서 입력된 메시지를 받아옵니다.
PostMessageA 함수
User32 PostMesageA 함수
PeekMessage 함수와는 반대로, 선택된 윈도우에 원하는 키 값을 입력합니다.

2. 64비트 VBA 호환성 문제 해결

User32.dll 라이브러리를 사용할 경우 64비트 엑셀 VBA와의 호환성 문제를 고려해야 합니다. User32.dll에는 수많은 종류의 함수가 있습니다. 각 함수마다 사용되는 인수가 모두 다르기 때문에, “이렇게 하면 모든 호환성 문제가 해결된다”라는 명확한 정답은 없습니다.
마이크로소프트에서 제시하는 호환성문제 해결방안는 크게 2가지입니다.

  • User32.dll 라이브러리를 참조하여 함수를 선언할 경우, PtrSafe를 사용하여 Private Declare PtrSafe Function 으로 선언합니다.
    VBA 64bit PtrSafe Function
  • Long 데이터 타입 대신, 64비트에서도 호환 가능한 LongPtr 데이터타입을 사용합니다.
    VBA 64bit LongPtr 데이터타입

하지만 앞서 말씀드린 바와 같이, 함수마다 사용되는 인수는 각각 다르기 때문에 상황에 따라 LongPtr 데이터타입을 적용할 필요가 있습니다.

3. KeyPressAPI 클래스 모듈 작성

엑셀에서 키보드 입력을 인식받기위해 새로운 클래스모듈 개체를 생성합니다. 이번 강의에서는 64bit 의 VBA를 기준으로 작성하였습니다. 삽입에가서 클래스 모듈을 생성한 뒤, 속성창에서 클래스모듈의 이름을 KeyPressAPI로 변경해줍니다.

VAB 클래스모듈 이름변경
모듈에서 키보드 F4키를 누르면 속성창이 활성화됩니다.

우선 첫번째로 아래 코드를 복사하여 붙여넣기 해줍니다. (타입과 변수, 사용자 지정함수를 선언합니다.)
자세한 내용은 영상강의를 참고해주세요.

내용추가 [2018년 11월 17일] :: 엑셀 2010 이전 32비트 버전 엑셀을 사용하여 오류가 발생할 경우, 본 포스트 마지막부분에 첨부로 작성해드린 명령문을 사용하세요. 

'########################################################################################################################
'# 해당 명령문에 대한 저작권은 오빠두엑셀(https://www.oppadu.com)에 있습니다.                                                #
'# 모든 정보는 Cretive Commns License에 의해 저작권을 보호받습니다.                                                     #
'# 영리를 목적으로 하지 않는 사용 및 공유는 허용됩니다. 반드시 저작자, 오빠두엑셀(https://www.oppadu.com)을 명시해야합니다.  #
'# This VBA Code is protected by Creative Commons License.                                                              #
'# All information can be posted, uploaded, shared at online for NON-commercial use only.                               #
'# The Author, '오빠두엑셀(https://www.oppadu.com)' have to be mentioned when you post this code.                            #
'########################################################################################################################
 
Option Explicit
 
'// 마우스 포인트 위치 (x,y)를 받아옵니다.
Private Type POINTAPI
    x As Long
    y As Long
End Type
 
'// TOP LEVEL WINDOW 의 키정보, 마우스위치정보를 받아올 메세지 Type을 설정합니다. (윈도우 Default 값)
Private Type msgKeyPress
    hwnd As LongPtr
    Message As LongPtr
    wParam As LongPtr
    lParam As LongPtr
    time As Long
    pt As POINTAPI
End Type
 
'// TOP LEVEL 윈도우 정보를 받습니다.
'// 더 자세한 내용은 아래 링크를 참고하세요
'// For more details, please refer to below links at MSDN
'// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindowa
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, _
     ByVal lpWindowName As String) As LongPtr
 
'// 실행중인 Thread에서 키입력을 받기위해 대기합니다.
'// 더 자세한 내용은 아래 링크를 참고하세요
'// For more details, please refer to below links at MSDN
'// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-waitmessage
Private Declare PtrSafe Function WaitMessage Lib "user32" () As LongPtr
 
'// 입력된 키정보를 받습니다.
'// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-peekmessagea
Private Declare PtrSafe Function PeekMessage Lib "user32" Alias "PeekMessageA" _
    (ByRef lpMsg As msgKeyPress, ByVal hwnd As LongPtr, _
     ByVal wMsgFilterMin As LongPtr, _
     ByVal wMsgFilterMax As LongPtr, _
     ByVal wRemoveMsg As LongPtr) As LongPtr
 
'// 키 값을 TOP LEVEL WINDOW에 Return 합니다.
'// https://msdn.microsoft.com/en-us/library/ms910658.aspx
Private Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" _
    (ByVal hwnd As LongPtr, _
     ByVal wMsg As LongPtr, _
     ByVal wParam As LongPtr, _
     lParam As Any) As LongPtr
 
'// 받아온 키 정보를 처리가능한 데이터로 변환합니다.
'// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-translatemessage
Private Declare PtrSafe Function TranslateMessage Lib "user32" _
    (ByRef lpMsg As msgKeyPress) As LongPtr
 
Private Const wmKeyDown As LongPtr = &H100
Private Const PM_REMOVE  As LongPtr = &H1
Private Const wmChar    As LongPtr = &H102
Private blnExit As Boolean
 
'// 키입력 이벤트가 일어났을때 발생시킵니다.
Public Event KeyPressed(ByVal KeyAscii As LongPtr, _
     ByVal KeyCode As LongPtr, _
     ByVal Target As Range, _
     ByRef Cancel As Boolean)

두번째로 클래스모듈의 2개의 프로시져 명령문을 작성합니다. 마찬가지로 복사해서 생성한 클래스모듈에 붙여넣기 해주세요. (*위에서 붙여넣기 한 명령문 아래로 이어서 붙여넣기 해주어야 합니다.)

'########################################################################################################################
'# 해당 명령문에 대한 저작권은 오빠두엑셀(https://www.oppadu.com)에 있습니다.                                                #
'# 모든 정보는 Cretive Commns License에 의해 저작권을 보호받습니다.                                                     #
'# 영리를 목적으로 하지 않는 사용 및 공유는 허용됩니다. 반드시 저작자, 오빠두엑셀(https://www.oppadu.com)을 명시해야합니다.  #
'# This VBA Code is protected by Creative Commons License.                                                              #
'# All information can be posted, uploaded, shared at online for NON-commercial use only.                               #
'# The Author, '오빠두엑셀(https://www.oppadu.com)' have to be mentioned when you post this code.                            #
'########################################################################################################################
Public Sub StartKeyPress()
    Dim msgMessage As msgKeyPress
    Dim blnCancel As Boolean
    Dim iMessage As LongPtr
    Dim iKeyCode As LongPtr
    Dim lXLhwnd As LongPtr
 
    On Error GoTo errHandler
    Application.EnableCancelKey = xlErrorHandler
 
    '// blnExit이 TRUE 가 되면 키입력인식을 종료합니다.
    blnExit = False
 
    '// 현재 실행중인 TOP LEVEL WINDOW (프로그램 창)을 인식하여 longptr (64비트 호환)로 받아옵니다.
    lXLhwnd = FindWindow("XLMAIN", Application.Caption)
 
    Do
        '// 실행중인 Thread에서 키입력을 받기위해 대기합니다.
        WaitMessage
 
        '// 오류 방지를 위해 blnExit를 Cross-Check 합니다.
        If blnExit Then Exit Do
 
        '// 키 입력을 받아옵니다.
        '// PeekMessage 값이 존재하는지(<>0) Cross-Check 합니다.
        If PeekMessage(msgMessage, lXLhwnd, wmKeyDown, wmKeyDown, PM_REMOVE) Then
            'Store the virtual key code for later use.
            iMessage = msgMessage.Message
            iKeyCode = msgMessage.wParam   '// <- 입력된 키보드 Input입니다.
 
            '받아온 msgKeyPress(=Virtual Message)를 실제 처리가능한 정보로 변환합니다.
            TranslateMessage msgMessage
            PeekMessage msgMessage, lXLhwnd, wmChar, wmChar, PM_REMOVE
 
            '// 오류 방지를 위해 blnCancel를 Cross-Check 합니다.
            blnCancel = False
 
            'KeyPressed 이벤트를 실행합니다.
            RaiseEvent KeyPressed(msgMessage.wParam, iKeyCode, Selection, blnCancel)
 
            'TOP LEVEL WINDOW에 입력값 Return합니다.
            If Not blnCancel Then
                PostMessage lXLhwnd, iMessage, iKeyCode, 0
            End If
        End If
errHandler:
        'StopKeyPress까지 DoEvents로 Loop합니다.
        DoEvents
    Loop Until blnExit
End Sub
 
Public Sub StopKeyPress()
    'Set this boolean flag to exit the above loop.
    blnExit = True
End Sub

4. 원하는 시트에 KeyPressAPI 클래스 모듈 적용하기

이제 원하는 시트에 키보드 입력이 들어왔을 때, 방금 작성한 KeyPressAPI 클래스 모듈을 사용하여 시트에 입력된 키보드의 정보를 받아보도록 하겠습니다.

이번 강의에서는 예제파일의 “Sheet1”에 키보드 입력이 들어왔을시, 해당 키의 Ascii(아스키)코드를 받아서 메시지박스로 출력하는 명령문을 작성하였습니다. VBA 편집창에서 Sheet1 을 더블클릭 하신 뒤, 아래 명령문을 복사하여 붙여넣기합니다.

'########################################################################################################################
'# 해당 명령문에 대한 저작권은 오빠두엑셀(https://www.oppadu.com)에 있습니다.                                                #
'# 모든 정보는 Cretive Commns License에 의해 저작권을 보호받습니다.                                                     #
'# 영리를 목적으로 하지 않는 사용 및 공유는 허용됩니다. 반드시 저작자, 오빠두엑셀(https://www.oppadu.com)을 명시해야합니다.  #
'# This VBA Code is protected by Creative Commons License.                                                              #
'# All information can be posted, uploaded, shared at online for NON-commercial use only.                               #
'# The Author, '오빠두엑셀(https://www.oppadu.com)' have to be mentioned when you post this code.                            #
'########################################################################################################################
Option Explicit
 
Dim WithEvents KeyPressWatcher As KeyPressAPI
 
Sub Start_KeyPress()
 
If KeyPressWatcher Is Nothing Then
    Set KeyPressWatcher = New KeyPressAPI
End If
 
KeyPressWatcher.StartKeyPress
 
End Sub
 
Sub End_KeyPress()
 
If KeyPressWatcher Is Nothing Then Exit Sub
KeyPressWatcher.StopKeyPress
 
End Sub
 
Private Sub KeyPressWatcher_KeyPressed(ByVal KeyAscii As LongPtr, ByVal KeyCode As LongPtr, ByVal Target As Range, Cancel As Boolean)
 
MsgBox "[[알림]]" & vbNewLine & _
        "현재 입력한 키는 " & Chr(CLng(KeyAscii)) & " 입니다." & vbNewLine & _
        "키의 아스키코드는 " & CLng(KeyAscii) & " 입니다."
 
End Sub

5. 해당시트에 매크로 실행을 위한 버튼 생성

이제 모든 모듈과 명령문 작성이 끝났습니다. VBA 편집창을 닫고 엑셀 시트로 돌아옵니다. Sheet1을 보시면 2개의 버튼이 있습니다. 각 버튼을 클릭한 뒤 [매크로지정]에 들어갑니다. [키입력받기]버튼에는 Start_KeyPress 명령문을, [키입력중단]버튼에는 End_KeyPress 명령문을 입혀줍니다.

VBA 매크로 지정하기 GIF

이제 모든 단계가 완료되었습니다. [키입력받기]버튼을 클릭한 뒤 아무 키나 입력하시면 실행중인 엑셀 프로그램에서 입력된 키보드정보를 받아와 아래처럼 메시지박스를 출력합니다.

6. 32비트용 KeyPressAPI 클래스모듈

만약 이전버전의 32비트 엑셀을 사용중일 경우 클래스모듈 함수설정 과정에서 오류가 발생할 수 있습니다. 그럴 경우 클래스모듈의 함수/변수 설정부분을 아래 적어드린 명령문으로 수정하여 사용하세요.

'########################################################################################################################
'# 해당 명령문에 대한 저작권은 오빠두엑셀(https://www.oppadu.com)에 있습니다.                                                #
'# 모든 정보는 Cretive Commns License에 의해 저작권을 보호받습니다.                                                     #
'# 영리를 목적으로 하지 않는 사용 및 공유는 허용됩니다. 반드시 저작자, 오빠두엑셀(https://www.oppadu.com)을 명시해야합니다.  #
'# This VBA Code is protected by Creative Commons License.                                                              #
'# All information can be posted, uploaded, shared at online for NON-commercial use only.                               #
'# The Author, '오빠두엑셀(https://www.oppadu.com)' have to be mentioned when you post this code.                            #
'########################################################################################################################
 
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)
5 1 투표
게시글평점
guest
4 댓글
Inline Feedbacks
모든 댓글 보기
이석호
이석호
2020년 8월 19일 8:51 오후
게시글평점 :
     

요즘 마리오 게임을 한창 배우고 있는 직장인 입니다. 영상 언제나 감사합니다. 질문 하나 드리고 싶어서요 rivate Sub KeyPressWatcher_KeyPressed(ByVal KeyAscii As LongPtr, ByVal KeyCode As LongPtr, ByVal Target As Range, cancle… 더보기 »

김진국
김진국
2021년 7월 22일 9:58 오후
답글 남기기  오빠두엑셀

안녕하세요. 정말 많은 도움을 받고 있습니다. 저도 질문을 드리고 싶어요. 앞에 분과 같은 질문에 대한 추가 질문입니다. 뭐랄까.. 이제까지 알고 있는 함수의 연결고리가 없는데 RaiseEvent KeyPressed로 KeyPressWatcher_KeyPressed 함수가 실행된다는게 이해가… 더보기 »

4
0
여러분의 생각을 댓글로 남겨주세요.x