[🖥️Project: HoP] #4 Windows Low-Level Hook과 전역 입력 감지

2025. 12. 11. 00:07·도토리도굴단/Project: HoP 개발일지

🚀 오늘의 작업 요약

  • 전역 감지: Windows Low-Level Hook을 이용해 백그라운드에서도 키보드/마우스 입력을 감지.
  • 스레드 안전성: OS 스레드와 Unity 메인 스레드 간의 충돌 방지를 위한 이벤트 큐 시스템 구축.
  • 상호작용: 글로벌 핫키(Ctrl+Shift+H) 및 햄초 스택 자동 증가 로직 연동.

햄초 위젯의 핵심 기믹은 사용자가 열심히 일(타이핑/클릭)하는 만큼 스택이 쌓이는 것이다. 이를 위해 앱이 비활성 상태일 때도 입력을 감지할 수 있는 Global Input Hook 시스템을 구현했다.


🛠 구현 상세

오늘은 Unity의 샌드박스를 벗어나 Windows 운영체제 레벨의 이벤트를 다루는 작업을 진행했다.

1. Global Input Hook 시스템 (GlobalInputHook.cs)

User32.dll의 SetWindowsHookEx를 사용하여 WH_KEYBOARD_LL(13과 WH_MOUSE_LL(14) 훅을 설치했다.

  • 구현 내용:
    • 백그라운드 감지: 앱이 포커스를 잃어도 OS 차원에서 넘어오는 입력 메시지를 가로챈다.
    • 입력 필터링: 키를 꾹 누르고 있을 때 발생하는 반복 입력(Repeat)을 HashSet으로 필터링하여 스택이 비정상적으로 쌓이는 것을 방지했다.
    • 이벤트 큐(Event Queue): 훅 프로시저에서 들어온 데이터를 즉시 처리하지 않고 큐에 담은 뒤, Unity의 Update()에서 소비하는 구조로 설계했다.

 

2. 기능 연동 및 핫키

감지된 입력을 실제 게임 로직과 연결했다.

  • 햄초 스택: 유효한 키 입력이나 클릭 발생 시 HamchoWidget.AddStack()을 호출하여 애니메이션 트리거 조건을 채운다.
  • 글로벌 핫키: Ctrl + Shift + H 조합을 감지하여, 어떤 작업을 하던 중이라도 위젯을 즉시 켜고 끌 수 있게 만들었다.
  •  

 

🔥 트러블슈팅: 스레드, 메모리, 그리고 셧다운

Windows API를 직접 다루다 보니 예상치 못한 크래시와 씨름해야 했다.

1. Unity API Thread Violation

문제 상황: 훅 프로시저(콜백) 내에서 Debug.Log나 HamchoWidget 함수를 호출하자마자 Unity가 강제 종료(Crash)되었다.

원인: Windows Hook 콜백은 OS가 관리하는 별도의 스레드(혹은 인터럽트 컨텍스트)에서 실행되는데, Unity API는 오직 메인 스레드에서만 접근 가능하다.

해결: '생산자-소비자 패턴'을 적용했다.

 
// 1. Hook 스레드 (OS): 데이터만 큐에 넣고 즉시 리턴
private static IntPtr HookCallback(...) {
    inputQueue.Enqueue(new InputEvent(...));
    return CallNextHookEx(...);
}

// 2. Unity 메인 스레드: 쌓인 데이터를 안전하게 처리
void Update() {
    while (inputQueue.TryDequeue(out var data)) {
        ProcessInput(data); // 여기서 Unity API 호출
    }
}

 

2. "CallbackOnCollectedDelegate" 오류

 

문제 상황: 실행 후 몇 초 뒤 혹은 GC가 돌 때마다 ExecutionEngineException이 발생하며 셧다운 되었다.

원인: C#의 델리게이트(Delegate)를 네이티브 API(SetWindowsHookEx)에 넘겨주었는데, C# 가비지 컬렉터가 "이거 안 쓰는 변수네?" 하고 치워버려서 발생한 문제다. 네이티브 쪽에서는 이미 사라진 메모 주소를 호출하려 하니 터지는 것.

해결: 델리게이트를 클래스 멤버 변수(static)로 선언하여 GC가 수거해가지 못하도록 참조를 유지했다.

// GC 방지용 정적 참조 유지
private static LowLevelKeyboardProc _keyboardProc;

void Start() {
    _keyboardProc = HookCallback; // 참조 저장
    SetWindowsHookEx(..., _keyboardProc, ...);
}

 

3. 앱 종료 후 마우스 먹통 현상

문제 상황: 에디터에서 플레이를 중지했는데 마우스가 움직이지 않았다.

원인: UnhookWindowsHookEx를 호출하지 않고 앱이 꺼지면, OS는 여전히 죽어버린 훅 체인을 통과하려고 시도하다가 타임아웃이 걸린다.

해결: OnDestroy뿐만 아니라 OnApplicationQuit에서도 이중으로 체크하여 반드시 훅을 해제하도록 안전장치를 걸었다.


🔮 다음 계획

기반 시스템은 거의 완성되었다. 가장 걱정되는 점프 차례다.

  • 스택 소모하여 점프 구현

'도토리도굴단 > Project: HoP 개발일지' 카테고리의 다른 글

[🖥️Project: HoP] #5 무한 점프 시스템  (0) 2025.12.13
[🖥️Project: HoP] #3 애니메이션 시스템과 O(1) 최적화  (0) 2025.12.08
[🖥️Project: HoP] #2 위치 저장과 멀티 모니터  (0) 2025.12.08
[🖥️Project: HoP] #1 투명 윈도우와 위젯 시스템 구축  (0) 2025.12.04
[👾Project: PokeIdle] #1 투명 창 만들기  (0) 2025.08.19
'도토리도굴단/Project: HoP 개발일지' 카테고리의 다른 글
  • [🖥️Project: HoP] #5 무한 점프 시스템
  • [🖥️Project: HoP] #3 애니메이션 시스템과 O(1) 최적화
  • [🖥️Project: HoP] #2 위치 저장과 멀티 모니터
  • [🖥️Project: HoP] #1 투명 윈도우와 위젯 시스템 구축
happy124219
happy124219
happylaboratory 님의 블로그 입니다.
  • happy124219
    윤아 실험실
    happy124219
  • 전체
    오늘
    어제
    • 분류 전체보기 (116)
      • 공부방 (103)
        • Unity UI (2)
        • C# 알고리즘 (3)
        • C# 에러발생 로그 (7)
        • 내일배움캠프 Unity 9기 TIL (91)
      • 도토리도굴단 (9)
        • Project: HSR 개발일지 (3)
        • Project: HoP 개발일지 (6)
      • 개인 프로젝트 (1)
        • Project: BloomFolk (1)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    유니티트랙후기
    내일배움캠프후기
    유니티
    Unity게임개발
    스파르타내일배움캠프
    도망공부일지
    도망개발일지
    게임개발
    c#문법
    스파르타내일배움캠프TIL
    내일배움캠프
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
happy124219
[🖥️Project: HoP] #4 Windows Low-Level Hook과 전역 입력 감지
상단으로

티스토리툴바