NGMsoftware

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

    학습


    C# C# .NET Core 매크로 프로그램 만들기. (마우스와 키보드)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 매크로 프로그램을 만들면서 가장 중요한 요소가 뭘까요? 많은 기능들이 떠오를텐데요. 가장 기본이 되는 마우스 입력과 키보드 입력일겁니다. 이와 더불어서 이미지 서치까지 포함하면 업무 자동화 매크로 프로그램을 거의 완성했다고 볼 수 있습니다. 다양한 함수들을 제공함으로써 편의성을 개선해야겠지만요. 아무튼, 이번 시간에는 앞서 만든 GUI 프로그램에 마우스와 키보드를 추가하고, 동작하는것까지 테스트 해볼께요.

     

    아래와 같이 마우스와 키보드 액션을 추가 했습니다.

    fEUms9O.png

     

     

    Action 안에 키보드와 마우스를 네임스페이스로 분리하고, 각각 BaseModel이라는 추상 클래스를 만들었습니다. 공통 속성은 여기에 정의하고, 강제해야 할 메소드와 오버라이딩 메소드를 미리 만들어 주도록 합니다. 마우스는 모든 동작에 마우스 좌표가 필요합니다. 따라서, Mouse.BaseModel 추상 클래스에 속성을 하나 추가합니다. 마우스 좌표를 사용자가 쉽게 추가할 수 있도록 TypeEditor.MouseTrackingEditor 특성도 붙여줍니다.

    ※ MouseTrackingEditor는 마우스 좌표를 추적할 수 있는 컨트롤입니다.

    namespace Ai.Model.Action.Mouse
    {
        [Serializable]
        public abstract class BaseModel : ActionModel
        {
            [Category("Action")]
            [LocalizedDisplayName("Coordinate")]
            [LocalizedDescription("Coordinate")]
            [Browsable(true)]
            [DefaultValue(typeof(Point), "0, 0")]
            [Editor(typeof(TypeEditor.MouseTrackingEditor), typeof(UITypeEditor))]
            public Point Coordinate { get; set; }

     

    최상위 추상 모델인 ActionModel의 추상 메소드를 재정의 합니다. 아직은 뭔가 내용이 없어서 빈 메소드로 두었습니다.

            public override void Excute()
            {
    
            }

     

    마우스 클릭을 구현하는 ClickModel에서 추상 메소드인 Excute를 구현합니다. 마우스 버튼 속성은 구현 클래스들이 모두 만들어야 합니다. 마우스 클릭, 마우스 다운, 마우스 업은 마우스 버튼에 따라서 동작해야 하는데요. 마우스 휠의 경우에는 버튼이 필요 없기 때문에 베이스 클래스로 올릴수가 없었습니다. 물론, 구현 클래스에서 메소드를 숨길수도 있지만, 유지보수가 어려워지는 측면이 있습니다. 그래서, 번거롭더라도 구현 클래스에서 속성을 만드는 방향으로 진행합니다.

    ※ 기존 NGM 6에서 수많은 속성들을 베이스에 올려놓았습니다. 상속 받은 구현 클래스에서 숨겨야 하는 속성이 누락되는 경우들이 빈번하게 발생했습니다.

    ※ 이런 실수를 반복하지 않기 위해서 구현 클래스에서 필요한 속성을 만드는게 디자인적으로 좋습니다.

    using System.ComponentModel;
    
    namespace Ai.Model.Action.Mouse
    {
        [Serializable]
        public class ClickModel : BaseModel
        {
            [Category("Action")]
            [LocalizedDisplayName("MouseButton")]
            [LocalizedDescription("MouseButton")]
            [Browsable(true)]
            [DefaultValue(typeof(Api.MouseKeyboardManager.MouseSimulator.MouseButton), "Left")]
            public Api.MouseKeyboardManager.MouseSimulator.MouseButton MouseButton { get; set; } = Api.MouseKeyboardManager.MouseSimulator.MouseButton.Left;
    
            public override void Excute()
            {
                Ai.Common.Mouse.Click(this.Coordinate, this.MouseButton, this.InputType, this.UseRelative);
            }
        }
    }

     

    이번에는 키보드를 만들어 볼까요? 키보드도 키보드 쓰기, 키보드 다운과 업이 있습니다. 키보드 프레스도 있군요. 간단한 테스트라서 키보드 쓰기를 먼저 만들고, 나머지 액션들은 하나씩 추가하는 방향으로 가야겠습니다. 모든 기능을 미리 만들어놓고, 테스트하면 누락되는 것들이 꼭~ 하나씩 생기더라고요^^

     

    키보드의 추상 클래스인 베이스 모델입니다. InputType이 하나 있는데요. 윈도우즈 환경에서 디바이스(Device: 장치)에 메세지를 보내는 방법은 3가지가 있습니다. 첫번째는 윈도우 메세지를 사용하는 방식이고, 두번째는 소프트웨어 방식입니다. 마지막 세번째는 하드웨어 신호입니다. 엔지엠 매크로에서 인풋 타입은 아래와 같습니다.

    • Windows: 윈도우 명령
    • Event: 소프트웨어 신호
    • Input: 하드웨어 신호
    using System.ComponentModel;
    
    namespace Ai.Model.Action.Keyboard
    {
        [Serializable]
        public abstract class BaseModel : ActionModel
        {
            [Category("Action")]
            [LocalizedDisplayName("InputType")]
            [LocalizedDescription("InputType")]
            [Browsable(true)]
            [DefaultValue(typeof(Ai.Device.InputType), "Windows")]
            public Ai.Device.InputType InputType { get; set; } = Device.InputType.Windows;
    
            public override void Excute()
            {
    
            }
        }
    }

     

    키보드 구현 모델은 아래와 같이 코딩했습니다. 사용자가 선택한 프로그램에 텍스트를 쓰기 위한 Text 속성이 하나 있습니다. InputType에 따라 어떤 방식을 사용할지 결정합니다. 나중에 Text가 null이거나 empty인 경우에 어떻게 처리할지를 추가적으로 구현해야 합니다. 아마도~ 필수 입력값이 누락되었다고 사용자에게 알려줘야겠죠?

    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Drawing.Design;
    
    namespace Ai.Model.Action.Keyboard
    {
        [Serializable]
        public class WriteModel : BaseModel
        {
            [Category("Action")]
            [LocalizedDisplayName("Text")]
            [LocalizedDescription("Text")]
            [Browsable(true)]
            [DefaultValue(null)]
            [Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
            public string? Text { get; set; }
    
            public override void Excute()
            {
                if (Text != null)
                    Ai.Common.Keyboard.Write(Text, InputType);
            }
        }
    }

     

    기존 엔지엠 6에서 필수 입력값 처리를 세분화하지 않아서 자동화 스크립트를 제작하다보면 혼란이 오는 경우들이 있습니다. 엔지엠 7은 Information, Warning, Error와 같이 3단계로 메세지를 알려줄 수 있도록 만들 예정입니다. 그리고, 아웃풋에 메세지 형식에 따라 색상도 설정하면 좋을듯 합니다.

     

    프로그래밍을 하다보면 기존 설계와 다르게 구현할 때 다양한 아이디어들이 떠오르게 됩니다. 이런것들을 문서화하지 않으면 자꾸 까먹게 되더라고요. 그래서, 프로그램 제작에 대한 내용을 정리하고 나중에 쭉~ 읽어보면서 추가해야 할 내용들을 다시 한번 상기시키고 리펙토링을 거쳐서 완성해야 합니다. 개발자들이 싫어하는 작업중에 하나가 리펙토링인데요. 시스템이 커질수록 어쩔 수 없는 일들이 발생합니다. 귀찮더라도 해야할 일들이죠^^;

     

    기본적인 코딩은 완료 했습니다. 테스트를 위해 메모장을 하나 실행하세요. 그리고, 에디터에서 아래와 같이 클릭과 쓰기 액션을 추가해줍니다.

    Alo3Ogz.png

     

     

    마우스 클릭 좌표를 메모장으로 설정하고, 쓰기 할 텍스트도 입력하세요.

    T86GRX5.png

     

     

    스크립트를 실행 해볼까요? 아래와 같이 메모장에 텍스트가 입력되는걸 확인할 수 있습니다.

     

     

    이번에는 입력 방식을 Event 또는 Input으로 변경하고, 다시 실행 해볼께요. Windows와 비교해보면 좋을듯 합니다.

     

     

    에디터에서 텍스트를 쓸 때 아래와 같이 입력했습니다.

    안녕하세요. 엔지엠소프트웨어입니다.
    http://ngmsoftware.com
    

     

    메모장에 텍스트가 입력될 때는 아래와 같이 줄바꿈이 한번 더 들어갑니다.

    TigXDjk.png

     

     

    윈도우의 API와 메모장의 텍스트 캐릭터인 캐리지 리턴을 해석하는 방식이 서로 달라서 발생하는 문제입니다. 따라서, 캐리지 리턴을 리눅스 방식으로 처리할 수 있는 속성을 하나 만들면 좋겠네요. 윈도우 방식과 리눅스 방식이 다르기 때문에 사용자에게 선택권을 주는게 좋을듯 합니다. 윈도우 프로그램이라도 윈도우 방식을 사용하는 프로그램과 리눅스 방식을 사용하는 프로그램이 있거든요.

     

    다음에는 이미지 서치 또는 이미지 매치를 만들어 보겠습니다. 그동안 간단한 것들만 만들다가 복잡한 내용을 진행해야 하니 벌써부터 걱정이 앞서는군요. 글 내용이 상당히 많이 길어질거 같거든요. 그래서, 이미지 서치와 이미지 매치는 글을 2회에 걸쳐서 작성할께요.

     

    개발자에게 후원하기

    MGtdv7r.png

     

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

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

    감사합니다~

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

    댓글목록

    등록된 댓글이 없습니다.