NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C# .NET 매크로 프로그램 만들기. (마우스 후킹 이벤트 1부)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 오늘은 마우스 후킹에 대해서 알아보겠습니다. 엔지엠 매크로 6에는 마우스 다운과 마우스 업 이벤트가 존재하는데요. 엔지엠 매크로 7은 여기에서 더 확장된 다양한 기능들을 제공할 수 있도록 할거예요. 그래서, 몇몇 추가된 부분들이 있고 기존의 윈도우 후킹 방식과 다르게 이벤트 핸들러를 디바이스에 연결해서 신호를 받도록 변경했습니다.

     

    엠지엠 매크로를 새롭게 다시 만들면서 이벤트에 관련된 액션은 필요가 없을줄 알았습니다. 그래서, 가능하면 비동기로 처리할 수 있는 그룹 액션을 사용하려고 했습니다. 그런데, 외부에서 받아와야 하는 이벤트는 비동기로 순간적인 신호를 캐치할 방법이 없더라고요. 이런 시나리오가 생각보다 많아서 비동기 그룹 액션의 한계 때문에 이벤트 핸들러를 할당할 수 있는 새로운 로직이 필요하게 되었습니다. 이벤트 액션은 사용하지 않을줄 알았는데 어쩔 수 없이 기존 이벤트 로직과 유사하게 만들어야 할거 같네요.

     

    이벤트는 마우스와 키보드 2개를 미리 추가해두었습니다.

    F4UhCFI.png

     

     

    카테고리만 다를뿐 클래스 이름은 동일합니다. 프로젝트의 논리 구조를 따라서 네임스페이스가 만들어지기 때문에 이름이 같아도 문제 없습니다.

    aVTEpsx.png

     

     

    글로벌 인터페이스가 하나 필요하게 되었습니다. 이벤트 핸들러를 일단 연결하면 어느 시점에 해제할 수 있어야 하고, 매크로가 중지 또는 완료될 때 할당된 이벤트 핸들러들을 전부 제거해줘야 합니다. 이벤트는 구조적으로 구독이기 때문에 여러개의 구독이 포함될 수 있습니다. 보통, 델리게이트(델리깃, Delegate: 대리자)를 이미 구현하고 있는 핸들러를 기본 사용합니다.

    namespace Ai.Interface
    {
        public interface IEvent : IDisposable
        {
            string? ID { get; }
    
            string? EventID { get; }
        }
    }

     

    IEvent 인터페이스는 이벤트를 식별할 수 있는 아이디가 필수로 제공되어야 하고, 이벤트가 발생했을 때 이동할 EventID도 존재해야 합니다. 그리고, IDisposable을 구현해야 합니다. 대부분의 이벤트 핸들러는 사용자가 명시적으로 메모리에서 해제할 수 있도록 해야 합니다. 이벤트의 경우 추가 연산자와 삭제 연산자를 이용합니다.

     

    인터페이스를 구현하는 코드입니다.

    [LocalizedCategory("Action")]
    [LocalizedDisplayName("GotoID")]
    [LocalizedDescription("GotoID")]
    [Browsable(true)]
    [DefaultValue(null)]
    [TypeConverter(typeof(TypeConverter.ActionIdConverter))]
    public string? EventID { get; set; }
    
    [LocalizedCategory("Action")]
    [LocalizedDisplayName("MouseState")]
    [LocalizedDescription("MouseState")]
    [Browsable(true)]
    [DefaultValue(null)]
    public Ai.Definition.MouseState[]? MouseState { get; set; }

     

    마우스 이벤트를 감지하기 위한 옵션은 아래와 같습니다.

    public enum MouseState
    {
        Unknown = 0,
        LeftDown = 1,
        LeftUp = 2,
        LeftClick = 3,
        RightDown = 4,
        RightUp = 5,
        RightClick = 6,
        MiddleDown = 7,
        MiddleUp = 8,
        MiddleClick = 9,
        X1Down = 10,
        X1Up = 11,
        X1Click = 12,
        X2Down = 13,
        X2Up = 14,
        X2Click = 15,
        Move = 16,
        Wheel = 17
    }

     

    기존 엔지엠 6 버전은 마우스 다운과 업만 존재했는데요. 새로운 버전에서는 더 다양한 옵션을 제공합니다. 그리고, 여러개를 같이 체크할 수 있습니다.

     

    매크로를 실행했을 때 효율을 위해서 사용자가 선택한 그룹에 속한 이벤트 핸들러만 연결하도록 했습니다. 

    public override string? Execute(Ai.Interface.IPlayer player)
    {
        _player = player;
        player.Events.Add(this);
    
        if (_mouseHook == null)
            _mouseHook = new MouseHookerHandler();
    
        if (MouseState.Contains(Definition.MouseState.LeftDown) || MouseState.Contains(Definition.MouseState.RightDown) || MouseState.Contains(Definition.MouseState.MiddleDown))
            _mouseHook.MousePressed += Hook_MousePressed;
    
        if (MouseState.Contains(Definition.MouseState.LeftUp) || MouseState.Contains(Definition.MouseState.RightUp) || MouseState.Contains(Definition.MouseState.MiddleUp))
            _mouseHook.MouseReleased += _mouseHook_MouseReleased;
    
        if (MouseState.Contains(Definition.MouseState.LeftClick) || MouseState.Contains(Definition.MouseState.RightClick) || MouseState.Contains(Definition.MouseState.MiddleClick))
            _mouseHook.MouseClicked += _mouseHook_MouseClicked;
    
        if (MouseState.Contains(Definition.MouseState.Move))
            _mouseHook.MouseMoved += _mouseHook_MouseMoved;
    
        if (MouseState.Contains(Definition.MouseState.Wheel))
            _mouseHook.MouseWheel += _mouseHook_MouseWheel;
    
        return null;
    }

     

    이벤트가 발생하면 처리하는 로직을 만들어야 하는데요. 대부분의 코드가 동일합니다.

    private void Hook_MousePressed(object? sender, MouseHookEventArgs e)
    {
        if (SuppressEvent)
            e.SuppressEvent = true;
    
        switch (e.Data.Button)
        {
            case SharpHook.Native.MouseButton.Button1:
                if (MouseState.Contains(Definition.MouseState.LeftDown))
                    _player.SetForceGotoId(EventID);
                break;
            case SharpHook.Native.MouseButton.Button2:
                if (MouseState.Contains(Definition.MouseState.RightDown))
                    _player.SetForceGotoId(EventID);
                break;
            case SharpHook.Native.MouseButton.Button3:
                if (MouseState.Contains(Definition.MouseState.MiddleDown))
                    _player.SetForceGotoId(EventID);
                break;
            case SharpHook.Native.MouseButton.Button4:
                if (MouseState.Contains(Definition.MouseState.X1Down))
                    _player.SetForceGotoId(EventID);
                break;
            case SharpHook.Native.MouseButton.Button5:
                if (MouseState.Contains(Definition.MouseState.X2Down))
                    _player.SetForceGotoId(EventID);
                break;
        }
    
        if (SuppressEvent)
            e.SuppressEvent = false;
    }

     

    이제 완성된 코드를 테스트 해볼건데요. 테스트용 스크립트는 아래와 같은 구조입니다.

    b2C6Oh6.png

     

     

    이벤트를 가장 위에 등록해놓고, 체크할 마우스 상태를 하나 추가합니다. 그리고, 지연 b와 액션이동으로 스크립트가 완료되지 않도록 무한 반복시킵니다. 스크립트를 실행하고, 마우스 상태를 등록한 키(마우스 왼쪽 또는 오른쪽등등...)를 누르면 이벤트가 발생하고, 이벤트가 발생했을 때 이동할 아이디로 루틴이 이동하는지 확인합니다. 이벤트가 발생했을 때 이동할 루틴은 스크립트의 마지막 그룹 c입니다.

     

    테스트에 대한 내용은 아래 동영상을 참고하세요.

     

     

    이번에는 마우스의 휠을 동작했을 때 이벤트가 작동하도록 해볼께요.

     

     

    마지막 테스트로 특정 영역 안에서만 이벤트가 동작되는지도 테스트 해봅시다.

     

     

    마우스와 키보드 후킹 관련된 이벤트를 어디에 사용할지는 모르겠지만, 잘 사용하면 업무에 도움이 될수도 있을듯 합니다. 다음에는 이벤트 범위도 설정해보도록 할께요. 그리고, 키보드 관련된 내용도 같이 다루면 좋겠네요. 다음 내용도 많이 기대해주세요^^

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.