NGMsoftware

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

    학습


    C# C# .NET 매크로 프로그램 만들기. (비활성 마우스 이펙트 보기)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 요즘 너무 기능 추가만 하다보니 코딩이 지겹기도 하고, 점점 텐션이 덜어지는 느낌이 들어서 새로운 걸 만들어보고 있습니다. 딱히 새롭다기 보다는 기존 엔지엠 매크로 6을 업그레이드 한다는 느낌으로 여러가지 GUI 관련 기능을 추가하고 있어요. 아무래도 단순 반복 작업을 하는 것보다는 GUI를 개선할 방법을 고민하고, 기능을 구현하는게 더 재미있긴 합니다.

     

    매크로 프로그램은 활성 모드와 비활성 모드로 나뉩니다. 활성 모드는 하드웨어 방식과 소프트웨어 방식 2가지가 있는데요. 하드웨어 방식은 또다시 3가지로 나뉩니다. 엔지엠 매크로에서는 디디와 인터셉션 그리고, 시리얼 통신을 제공하고 있습니다. 비활성 모드는 윈도우 API를 이용하는 SendMessage와 PostMessage입니다. 둘다 로직은 동일하지만 동기 모드와 비동기 모드로 나뉩니다. 비활성 모드는 일부 프로그램에서 정상 동작하지 않습니다. 해당 API를 막거나 소프트웨어 신호를 감지해서 입력을 막기 때문입니다. 그래서, 대부분은 하드웨어 모드로 사용하고 있습니다.

     

    우선, 엔지엠 매크로에서 이펙트를 보기 위한 옵션을 추가 해줍니다. 옵션은 에디터와 플레이어가 구분되어 있기 때문에 에디터용 사용자 컨트롤을 추가 했습니다.

    Vo2bjiv.png

     

     

    사용자 컨트롤의 디자인은 아래와 같습니다. 나중에 항목이 하나 더 추가될 수 있습니다.

    2vH3rwq.png

     

     

    컨트롤은 하나의 기능만 제공하고 있습니다. 속성을 하나 열어주면 될거 같네요.

    using Ai.Common.Client.Interface;
    
    namespace Ai.Common.Client.OptionItem
    {
        public partial class EditorMouseEffectItem : UserControl, IMouseEffectItem
        {
            public bool UseMouseEffect { get { return this.chkMouseEffect.Checked; } set { this.chkMouseEffect.Checked = value; } }
    
            public EditorMouseEffectItem()
            {
                InitializeComponent();
    
                pnlHelper.BackgroundImage = (Image)Ai.ResourceManager.ImageResource.Instance.GetObject("question");
                pnlHelper.BackgroundImageLayout = ImageLayout.Center;
            }
        }
    }

     

     

    옵션 뷰 또는 팝업을 표시할 컨테이너에 행을 하나 추가 했습니다. 이 행에 위에서 만든 사용자 컨트롤(UserControl)을 추가해야 합니다.

    x0iJ27b.png

     

     

    컨테이너에서 접근할 수 있도록 추가한 컨트롤의 속성을 한번 더 열어줍니다.

    public bool UseMouseEffect
    {
        get
        {
            return ((Interface.IMouseEffectItem)this.tableLayoutPanel.Controls.Find("effect", true).First()).UseMouseEffect;
        }
        set
        {
            ((Interface.IMouseEffectItem)this.tableLayoutPanel.Controls.Find("effect", true).First()).UseMouseEffect = value;
        }
    }

     

    위 속성을 구현하는 부분은 생성자에서 처리합니다. 지금까지 추가했던 모든 옵션 컨트롤이 추가되어 있습니다. 플레이어도 에디터만큼 옵션들을 추가해야 하지만, 현재는 2개만 만들어 놓았습니다. 나중에 플레이어를 개발할 때 모두 추가해야 하는데요. 지금은... 단순 테스트만 해보고, 넘어갔어요. 일단 기본 기능을 구현하는게 더 급하니까요.

    public Option(Ai.Interface.IClient client)
    {
        _client = client;
        _optionModel = Ai.Api.ModelHelper.Load<Ai.Model.Client.OptionModel>(Path.Combine(System.Windows.Forms.Application.StartupPath, Ai.Definition.ConfigDirectory, Ai.Definition.OptionFileName, Ai.Definition.ConfigFileExtension));
        InitializeComponent();
    
        if (client is Ai.Interface.IEditor)
        {
            this.tableLayoutPanel.Controls.Add(new OptionItem.EditorTitleItem(_client) { Name = "title", Dock = DockStyle.Fill }, 0, 0);
            this.tableLayoutPanel.Controls.Add(new OptionItem.EditorSetDirectoryItem() { Name = "directory", Dock = DockStyle.Fill }, 0, 1);
            this.tableLayoutPanel.Controls.Add(new OptionItem.EditorScaleItem() { Name = "scale", Dock = DockStyle.Fill }, 0, 2);
            this.tableLayoutPanel.Controls.Add(new OptionItem.EditorCaptureModeItem() { Name = "captureMode", Dock = DockStyle.Fill }, 0, 3);
            this.tableLayoutPanel.Controls.Add(new OptionItem.EditorSyncItem() { Name = "sync", Dock = DockStyle.Fill }, 0, 4);
            this.tableLayoutPanel.Controls.Add(new OptionItem.EditorMouseEffectItem() { Name = "effect", Dock = DockStyle.Fill }, 0, 5);
        }
        else if (client is Ai.Interface.IPlayer)
        {
            this.tableLayoutPanel.Controls.Add(new OptionItem.PlayerScaleItem() { Name = "scale", Dock = DockStyle.Fill }, 0, 1);
            this.tableLayoutPanel.Controls.Add(new OptionItem.PlayerCaptureModeItem() { Name = "captureMode", Dock = DockStyle.Fill }, 0, 2);
        }
    }

     

    MouseEffectForm을 엔진의 클라이언트에 추가 해줍니다. MouseEffectForm은 이름처럼 마우스 클릭 위치에 이펙트를 보여주기 위한 GUI입니다.

    JdRzJVI.png

     

     

    타이머를 이용해서 동그라미를 그려줍니다.

    public MouseEffectForm()
    {
        _timeCheck = 0;
        _timeLimit = 250;
        _timer = new System.Windows.Forms.Timer { Interval = 25, Enabled = false };
        _timer.Tick += OnTimerTicked;
        _pen1 = new Pen(Color.Orange, 4);
        _pen2 = new Pen(Color.Blue, 3);
        _rect1 = new Rectangle(1, 1, 27, 27);
        _rect2 = new Rectangle(10, 10, 10, 10);
    
        FormBorderStyle = FormBorderStyle.None;
        BackColor = Color.White;
        TransparencyKey = Color.White;
        TopLevel = true;
        TopMost = true;
        VisibleChanged += Effector_VisibleChanged;
        Paint += Effector_Paint;
    }

     

    폼의 그래픽 개체로 동그라미를 그려줍니다.

    private void DrawEllipseRectangle(PaintEventArgs e)
    {
        int alpha = 255;
        alpha -= _timeCheck > 255 ? 255 : _timeCheck;
        _pen2.Color = Color.FromArgb(alpha, _pen2.Color);
        _pen1.Color = Color.FromArgb(alpha, _pen1.Color);
        e.Graphics.DrawEllipse(_pen2, _rect2);
        e.Graphics.DrawEllipse(_pen1, _rect1);
    }

     

    참고로, 폼을 실행할 때 사용자에게 표시하지 않아야 합니다. 그래서, 폼 인스턴스를 생성하면서 Taskbar에서 숨겨야 합니다. 그리고, Manager는 초기화 지연 인스턴스이기 때문에 실행되기 전에는 핸들이 만들어지지 않습니다. 따라서, 메니저의 생성자에서 폼을 인스턴스화 하더라도 핸들이 만들어지지 않았을 가능성이 있습니다. 실제로 테스트를 해보면 핸들이 만들어지기 전에 스크립트가 먼저 실행되어 에러가 발생합니다.

    MouseEffector = new Client.MouseEffectForm();
    MouseEffector.ShowInTaskbar = false;
    
    if (!this.MouseEffector.IsHandleCreated)
        Ai.Api.ApiHelper.CreateHandle(this.MouseEffector.Handle);

     

    이와 같은 시나리오에서 특정 폼의 핸들을 강제로 만들려면 아래와 같이 CreateHandle 메소드를 하나 만들어서 호출해주면 됩니다.

    public static void CreateHandle(IntPtr handle)
    {
        int exstyle = Ai.Api.Core.User32.GetWindowLong(handle, (int)Options.WindowPropertyChangeCommand.GWL_EXSTYLE);
        exstyle |= (int)Options.ExtendedWindowStyles.WS_EX_TRANSPARENT;
        Ai.Api.Core.User32.SetWindowLong(handle, (int)Options.WindowPropertyChangeCommand.GWL_EXSTYLE, exstyle);
    }

     

    이제 마지막으로 마우스 클릭 액션 아래에 아래와 같이 옵션을 읽어서 마우스 이펙트를 표시할지 여부를 판단해야 합니다. 그리고, 마우스 이펙트를 표시한다면 활성 모드인지 비활성 모드인지에 따라서 좌표를 보정해줘야 합니다.

    if (player.Manager.UseMouseEffect)
    {
        System.Drawing.Point effectPoint = position;
    
        if ((inputType == Definition.DeviceInputType.PostMessage || inputType == Definition.DeviceInputType.SendMessage) &&
            controlHandle.HasValue && controlHandle.Value != IntPtr.Zero)
            effectPoint = ActiveMouseLocation(controlHandle.Value, position);
        
        player.Manager.ShowMouseEffect(effectPoint);
    }

     

    이제 테스트를 해볼까요? 아래 유튜브 동영상을 참고해서 녹스 앱플레이어를 비활성으로 클릭하고, 마우스 이펙트 표시 여부도 확인 해보세요. 추후에는 ADB에 연결해서 마우스 이펙트가 정상적으로 표시되는지도 테스트 해봐야 합니다. 지금은 활성과 비활성만 테스트 했는데요. ADB는 좌표계가 달라서 추가적인 코딩이 필요합니다.

     

     

    이 글이 도움이 되셨다면~ 커피 한잔이라도 후원 부탁드립니다^^

    개발자에게 후원하기

    MGtdv7r.png

     

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

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

    감사합니다~

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

    댓글목록

    등록된 댓글이 없습니다.