첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
0.03MB

▶ HookType.cs

namespace TestProject
{
    /// <summary>
    /// 후킹 타입
    /// </summary>
    public enum HookType : int
    {
        /// <summary>
        /// WH_JOURNALRECORD
        /// </summary>
        WH_JOURNALRECORD = 0,

        /// <summary>
        /// WH_JOURNALPLAYBACK
        /// </summary>
        WH_JOURNALPLAYBACK = 1,

        /// <summary>
        /// WH_KEYBOARD
        /// </summary>
        WH_KEYBOARD = 2,

        /// <summary>
        /// WH_GETMESSAGE
        /// </summary>
        WH_GETMESSAGE = 3,

        /// <summary>
        /// WH_CALLWNDPROC
        /// </summary>
        WH_CALLWNDPROC = 4,

        /// <summary>
        /// WH_CBT
        /// </summary>
        WH_CBT = 5,

        /// <summary>
        /// WH_SYSMSGFILTER
        /// </summary>
        WH_SYSMSGFILTER = 6,

        /// <summary>
        /// WH_MOUSE
        /// </summary>
        WH_MOUSE = 7,

        /// <summary>
        /// WH_HARDWARE
        /// </summary>
        WH_HARDWARE = 8,

        /// <summary>
        /// WH_DEBUG
        /// </summary>
        WH_DEBUG = 9,

        /// <summary>
        /// WH_SHELL
        /// </summary>
        WH_SHELL = 10,

        /// <summary>
        /// WH_FOREGROUNDIDLE
        /// </summary>
        WH_FOREGROUNDIDLE = 11,

        /// <summary>
        /// WH_CALLWNDPROCRET
        /// </summary>
        WH_CALLWNDPROCRET = 12,

        /// <summary>
        /// WH_KEYBOARD_LL
        /// </summary>
        WH_KEYBOARD_LL = 13,

        /// <summary>
        /// WH_MOUSE_LL
        /// </summary>
        WH_MOUSE_LL = 14
    }
}

 

728x90

 

▶ KeyboardHookStructureFlag.cs

namespace TestProject
{
    /// <summary>
    /// 키보드 후킹 구조체 플래그
    /// </summary>
    public enum KeyboardHookStructureFlag
    {
        /// <summary>
        /// LLKHF_EXTENDED
        /// </summary>
        LLKHF_EXTENDED = 0x01,

        /// <summary>
        /// LLKHF_INJECTED
        /// </summary>
        LLKHF_INJECTED = 0x10,

        /// <summary>
        /// LLKHF_ALTDOWN
        /// </summary>
        LLKHF_ALTDOWN = 0x20,

        /// <summary>
        /// LLKHF_UP
        /// </summary>
        LLKHF_UP = 0x80
    }
}

 

300x250

 

▶ MouseMessage.cs

namespace TestProject
{
    /// <summary>
    /// 마우스 메시지
    /// </summary>
    public enum MouseMessage
    {
        /// <summary>
        /// WM_LBUTTONDOWN
        /// </summary>
        WM_LBUTTONDOWN = 0x0201,

        /// <summary>
        /// WM_LBUTTONUP
        /// </summary>
        WM_LBUTTONUP = 0x0202,

        /// <summary>
        /// WM_MOUSEMOVE
        /// </summary>
        WM_MOUSEMOVE = 0x0200,

        /// <summary>
        /// WM_MOUSEWHEEL
        /// </summary>
        WM_MOUSEWHEEL = 0x020A,

        /// <summary>
        /// WM_RBUTTONDOWN
        /// </summary>
        WM_RBUTTONDOWN = 0x0204,

        /// <summary>
        /// WM_RBUTTONUP
        /// </summary>
        WM_RBUTTONUP = 0x0205,

        /// <summary>
        /// WM_NCLBUTTONDOWN
        /// </summary>
        WM_NCLBUTTONDOWN = 0x00A
    }
}

 

▶ POINT.cs

using System.Runtime.InteropServices;

namespace TestProject
{
    /// <summary>
    /// 포인트
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// X
        /// </summary>
        public int X;

        /// <summary>
        /// Y
        /// </summary>
        public int Y;

        #endregion
    }
}

 

▶ KEYBOARDHOOKSTRUCT.cs

using System;
using System.Runtime.InteropServices;

namespace TestProject
{
    /// <summary>
    /// 키보드 후킹 구조체
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public class KEYBOARDHOOKSTRUCT
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 가상 키 코드
        /// </summary>
        public uint VirtualKeyCode;

        /// <summary>
        /// 스캔 코드
        /// </summary>
        public uint ScanCode;

        /// <summary>
        /// 키보드 후킹 구조체 플래그
        /// </summary>
        public KeyboardHookStructureFlag Flag;

        /// <summary>
        /// 시간
        /// </summary>
        public uint Time;

        /// <summary>
        /// 부가 정보 핸들
        /// </summary>
        public UIntPtr ExtraInformationHandle;

        #endregion
    }
}

 

▶ MOUSEHOOKSTRUCT.cs

using System;
using System.Runtime.InteropServices;

namespace TestProject
{
    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEHOOKSTRUCT
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 위치
        /// </summary>
        public POINT Location;

        /// <summary>
        /// 윈도우 핸들
        /// </summary>
        public IntPtr WindowHandle;

        /// <summary>
        /// 히트 테스트 코드
        /// </summary>
        public uint HitTestCode;

        /// <summary>
        /// 부가 정보 핸들
        /// </summary>
        public IntPtr ExtraInformationHandle;

        #endregion
    }
}

 

▶ KeyData.cs

namespace TestProject
{
    /// <summary>
    /// 키 데이터
    /// </summary>
    public class KeyData
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 디폴트 키 - DefaultKey

        /// <summary>
        /// 디폴트 키
        /// </summary>
        public string DefaultKey;

        #endregion
        #region SHIFT 키 - ShiftKey

        /// <summary>
        /// SHIFT 키
        /// </summary>
        public string ShiftKey;

        #endregion
        #region 한글 키 - HangulKey

        /// <summary>
        /// 한글 키
        /// </summary>
        public string HangulKey;

        #endregion
        #region 한글 SHIFT 키 - HangulShiftKey

        /// <summary>
        /// 한글 SHIFT 키
        /// </summary>
        public string HangulShiftKey;

        #endregion
    }
}

 

▶ WIN32Helper.cs

using System;
using System.Runtime.InteropServices;

namespace TestProject
{
    /// <summary>
    /// WIN32 헬퍼
    /// </summary>
    public class WIN32Helper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Import
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 윈도우 후킹 설정하기 (확장) - SetWindowsHookEx(hookID, hookProc, moduleHandle, threadID)

        /// <summary>
        /// 윈도우 후킹 설정하기 (확장)
        /// </summary>
        /// <param name="hookID">후킹 ID</param>
        /// <param name="hookProc">후킹 프로시저</param>
        /// <param name="moduleHandle">모듈 핸들</param>
        /// <param name="threadID">스레드 ID</param>
        /// <returns>후킹 핸들</returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(int hookID, HookProc hookProc, IntPtr moduleHandle, uint threadID);

        #endregion
        #region 윈도우 후킹 해제하기 (확장) - UnhookWindowsHookEx(hookHandle)

        /// <summary>
        /// 윈도우 후킹 해제하기 (확장)
        /// </summary>
        /// <param name="hookHandle">후킹 핸들</param>
        /// <returns>처리 결과</returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hookHandle);

        #endregion
        #region 다음 후킹 호출하기 (확장) - CallNextHookEx(hookHandle, code, wordParameter, longParameter)

        /// <summary>
        /// 다음 후킹 호출하기 (확장)
        /// </summary>
        /// <param name="hookHandle">후킹 핸들</param>
        /// <param name="code">코드</param>
        /// <param name="wordParameter">WORD 매개 변수</param>
        /// <param name="longParameter">LONG 매개 변수</param>
        /// <returns>처리 결과</returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CallNextHookEx(IntPtr hookHandle, int code, IntPtr wordParameter, IntPtr longParameter);

        #endregion
        #region 모듈 핸들 구하기 - GetModuleHandle(moduleName)

        /// <summary>
        /// 모듈 핸들 구하기
        /// </summary>
        /// <param name="moduleName">모듈명</param>
        /// <returns>모듈 핸들</returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string moduleName);

        #endregion
        #region 현재 스레드 ID 구하기 - GetCurrentThreadId()

        /// <summary>
        /// 현재 스레드 ID 구하기
        /// </summary>
        /// <returns>현재 스레드 ID</returns>
        [DllImport("kernel32.dll")]
        public static extern uint GetCurrentThreadId();

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Delegate
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 후킹 프로시저 대리자 - HookProc(code, wordParameter, longParameter)

        /// <summary>
        /// 후킹 프로시저 대리자
        /// </summary>
        /// <param name="code">코드</param>
        /// <param name="wordParameter">WORD 매개 변수</param>
        /// <param name="longParameter">LONG 매개 변수</param>
        /// <returns>처리 결과</returns>
        public delegate IntPtr HookProc(int code, IntPtr wordParameter, IntPtr longParameter);

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// HC_ACTION
        /// </summary>
        public const int HC_ACTION = 0;

        #endregion
    }
}

 

▶ VisualElementHelper.cs

using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 비주얼 엘리먼트 헬퍼
    /// </summary>
    public static class VisualElementHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 부모 구하기 - GetParent(child)

        /// <summary>
        /// 부모 구하기
        /// </summary>
        /// <param name="child">자식</param>
        /// <returns>부모</returns>
        public static DependencyObject GetParent(this DependencyObject child)
        {
            if(child == null)
            {
                return null;
            }

            ContentElement childContentElement = child as ContentElement;

            if(childContentElement != null)
            {
                DependencyObject parent = ContentOperations.GetParent(childContentElement);

                if(parent != null)
                {
                    return parent;
                }

                FrameworkContentElement childFrameworkContentElement = childContentElement as FrameworkContentElement;

                return childFrameworkContentElement != null ? childFrameworkContentElement.Parent : null;
            }

            FrameworkElement childFrameworkElement = child as FrameworkElement;

            if(childFrameworkElement != null)
            {
                DependencyObject parent = childFrameworkElement.Parent;

                if(parent != null)
                {
                    return parent;
                }
            }

            return VisualTreeHelper.GetParent(child);
        }

        #endregion
        #region 부모 찾기 시도하기 - TryFindParent<TParent>(child)

        /// <summary>
        /// 부모 찾기 시도하기
        /// </summary>
        /// <typeparam name="TParent">부모 타입</typeparam>
        /// <param name="child">자식</param>
        /// <returns>부모</returns>
        public static TParent TryFindParent<TParent>(this DependencyObject child) where TParent : DependencyObject
        {
            DependencyObject parentObject = GetParent(child);

            if(parentObject == null)
            {
                return null;
            }

            TParent parent = parentObject as TParent;

            if(parent != null)
            {
                return parent;
            }
            else
            {
                return TryFindParent<TParent>(parentObject);
            }
        }

        #endregion
        #region 자식 열거 가능형 구하기 - GetChildEnumerable(parent)

        /// <summary>
        /// 자식 열거 가능형 구하기
        /// </summary>
        /// <param name="parent">부모</param>
        /// <returns>자식 열거 가능형</returns>
        public static IEnumerable<DependencyObject> GetChildEnumerable(this DependencyObject parent)
        {
            if(parent == null)
            {
                yield break;
            }

            if(parent is ContentElement || parent is FrameworkElement)
            {
                foreach(object childObject in LogicalTreeHelper.GetChildren(parent))
                {
                    DependencyObject child = childObject as DependencyObject;

                    if(child != null)
                    {
                        yield return (DependencyObject)childObject;
                    }
                }
            }
            else
            {
                int count = VisualTreeHelper.GetChildrenCount(parent);

                for(int i = 0; i < count; i++)
                {
                    yield return VisualTreeHelper.GetChild(parent, i);
                }
            }
        }

        #endregion
        #region 자식 찾기 - FindChildren<TChild>(parent)

        /// <summary>
        /// 자식 찾기
        /// </summary>
        /// <typeparam name="TChild">자식 타입</typeparam>
        /// <param name="parent">부모</param>
        /// <returns>자식 열거 가능형</returns>
        public static IEnumerable<TChild> FindChildren<TChild>(this DependencyObject parent) where TChild : DependencyObject
        {
            if(parent != null)
            {
                IEnumerable<DependencyObject> childEnumerable = GetChildEnumerable(parent);

                foreach(DependencyObject child in childEnumerable)
                {
                    if(child != null && child is TChild)
                    {
                        yield return (TChild)child;
                    }

                    foreach(TChild descendant in FindChildren<TChild>(child))
                    {
                        yield return descendant;
                    }
                }
            }
        }

        #endregion
        #region 포인트에서 찾기 시도하기 - TryFindFromPoint<T>(referenceElement, point)

        /// <summary>
        /// 포인트에서 찾기 시도하기
        /// </summary>
        /// <typeparam name="TElement">엘리먼트 타입</typeparam>
        /// <param name="referenceElement">참조 엘리먼트</param>
        /// <param name="point">포인트</param>
        /// <returns>엘리먼트</returns>
        public static TElement TryFindFromPoint<TElement>(UIElement referenceElement, Point point) where TElement : DependencyObject
        {
            DependencyObject element = referenceElement.InputHitTest(point) as DependencyObject;

            if(element == null)
            {
                return null;
            }
            else if(element is TElement)
            {
                return (TElement)element;
            }
            else
            {
                return TryFindParent<TElement>(element);
            }
        }

        #endregion
    }
}

 

▶ HookingHelper.cs

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;

namespace TestProject
{
    /// <summary>
    /// 후킹 헬퍼
    /// </summary>
    public class HookingHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Delegate
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 키 클릭시 핸들러 - KeyClickHandler(keyCode)

        /// <summary>
        /// 키 클릭시 핸들러
        /// </summary>
        /// <param name="keyCode">키 코드</param>
        public delegate void KeyClickHandler(uint keyCode);

        #endregion
        #region 마우스 클릭시 핸들러 - MouseClickHandler(point, message)

        /// <summary>
        /// 마우스 클릭시 핸들러
        /// </summary>
        /// <param name="point">포인트</param>
        /// <param name="message">메시지</param>
        public delegate void MouseClickHandler(POINT point, MouseMessage message);

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 키 클릭시 이벤트 - KeyClick

        /// <summary>
        /// 키 클릭시 이벤트
        /// </summary>
        public static event KeyClickHandler KeyClick;

        #endregion
        #region 마우스 클릭시 이벤트 - MouseClick

        /// <summary>
        /// 마우스 클릭시 이벤트
        /// </summary>
        public static event MouseClickHandler MouseClick;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 핸들
        /// </summary>
        private static IntPtr _handle = IntPtr.Zero;

        /// <summary>
        /// 모듈 핸들
        /// </summary>
        private static IntPtr _moduleHandle = IntPtr.Zero;

        /// <summary>
        /// 키보드 후킹 핸들
        /// </summary>
        private static IntPtr _keyboardHookHandle = IntPtr.Zero;

        /// <summary>
        /// 마우스 후킹 핸들
        /// </summary>
        private static IntPtr _mouseHookHandle = IntPtr.Zero;

        /// <summary>
        /// 키보드 프로시저
        /// </summary>
        private static WIN32Helper.HookProc _keyboardProc = new WIN32Helper.HookProc(KeyboardProc);

        /// <summary>
        /// 마우스 프로시저
        /// </summary>
        private static WIN32Helper.HookProc _mouseProc = new WIN32Helper.HookProc(MouseProc);

        /// <summary>
        /// 마우스 후킹 구조체
        /// </summary>
        private static MOUSEHOOKSTRUCT _mouseHookStructure;

        /// <summary>
        /// 이전 윈도우 핸들
        /// </summary>
        private static IntPtr _previousWindowHandle = IntPtr.Zero;

        /// <summary>
        /// 이전 포커스 핸들
        /// </summary>
        private static IntPtr _previusFocusHandle = IntPtr.Zero;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 실행 여부 - IsRun

        /// <summary>
        /// 실행 여부
        /// </summary>
        public static bool IsRun { get; private set; }

        #endregion
        #region 전역 사용 여부 - UseGlobal

        /// <summary>
        /// 전역 사용 여부
        /// </summary>
        public static bool UseGlobal { get; set; }

        #endregion
        #region 후킹 영역 - HookArea

        /// <summary>
        /// 후킹 영역
        /// </summary>
        public static Rect HookArea { get; set; }

        #endregion
        #region 후킹 엘리먼트 - HookElement

        /// <summary>
        /// 후킹 엘리먼트
        /// </summary>
        public static UIElement HookElement { get; set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 시작하기 - Start()

        /// <summary>
        /// 시작하기
        /// </summary>
        public static void Start()
        {
            if(!IsRun)
            {
                uint threadID = WIN32Helper.GetCurrentThreadId();

                using(Process process = Process.GetCurrentProcess())
                {
                    using(ProcessModule processModule = process.MainModule)
                    {
                        _handle = process.MainWindowHandle;

                        _moduleHandle = WIN32Helper.GetModuleHandle(processModule.ModuleName);

                        _keyboardHookHandle = WIN32Helper.SetWindowsHookEx
                        (
                            (int)HookType.WH_KEYBOARD_LL,
                            _keyboardProc,
                            _moduleHandle,
                            0
                        );

                        _mouseHookHandle = WIN32Helper.SetWindowsHookEx
                        (
                            (int)HookType.WH_MOUSE_LL,
                            _mouseProc,
                            _moduleHandle,
                            0
                        );

                        IsRun = true;
                    }
                }
            }
        }

        #endregion
        #region 중단하기 - Stop()

        /// <summary>
        /// 중단하기
        /// </summary>
        public static void Stop()
        {
            if(IsRun)
            {
                WIN32Helper.UnhookWindowsHookEx(_keyboardHookHandle);
                WIN32Helper.UnhookWindowsHookEx(_mouseHookHandle   );

                IsRun = false;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 후킹 영역 여부 구하기 - IsHookingArea()

        /// <summary>
        /// 후킹 영역 여부 구하기
        /// </summary>
        /// <returns>후킹 영역 여부</returns>
        private static bool IsHookingArea()
        {
            if(HookElement != null && !HookArea.IsEmpty)
            {
                Point point = HookElement.PointFromScreen
                (
                    new Point
                    (
                        _mouseHookStructure.Location.X,
                        _mouseHookStructure.Location.Y)
                );

                bool contains = HookArea.Contains(point);

                return contains;
            }

            return true;
        }

        #endregion

        #region 키보드 프로시저 처리하기 - KeyboardProc(code, wordParameter, longParameter)

        /// <summary>
        /// 키보드 프로시저 처리하기
        /// </summary>
        /// <param name="code">코드</param>
        /// <param name="wordParameter">WORD 매개 변수</param>
        /// <param name="longParameter">LONG 매개 변수</param>
        /// <returns>처리 결과</returns>
        private static IntPtr KeyboardProc(int code, IntPtr wordParameter, IntPtr longParameter)
        {
            if(code == WIN32Helper.HC_ACTION)
            {
                uint wordParameterValue = (uint)wordParameter;
                long longParamererValue = (long)longParameter;

                if(wordParameterValue == 256)
                {
                    KEYBOARDHOOKSTRUCT keyboardHookStructure = (KEYBOARDHOOKSTRUCT)Marshal.PtrToStructure
                    (
                        longParameter,
                        typeof(KEYBOARDHOOKSTRUCT)
                    );

                    KeyClick?.Invoke(keyboardHookStructure.VirtualKeyCode);
                }

                if((wordParameterValue == 229 && longParamererValue == -2147483647) || (wordParameterValue == 229 && longParamererValue == -2147483648))
                {
                    if(IsHookingArea())
                    {
                        return (IntPtr)1;
                    }
                }
            }

            return WIN32Helper.CallNextHookEx(_keyboardHookHandle, code, wordParameter, longParameter);
        }

        #endregion
        #region 마우스 프로시저 처리하기 - MouseProc(code, wordParameter, longParameter)

        /// <summary>
        /// 마우스 프로시저 처리하기
        /// </summary>
        /// <param name="code">코드</param>
        /// <param name="wordParameter">WORD 매개 변수</param>
        /// <param name="longParameter">LONG 매개 변수</param>
        /// <returns>처리 결과</returns>
        private static IntPtr MouseProc(int code, IntPtr wordParameter, IntPtr longParameter)
        {
            if(code >= 0)
            {
                _mouseHookStructure = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure
                (
                    longParameter,
                    typeof(MOUSEHOOKSTRUCT)
                );

                MouseMessage mouseMessage = (MouseMessage)wordParameter;

                if(UseGlobal)
                {
                    if(mouseMessage == MouseMessage.WM_LBUTTONDOWN || mouseMessage == MouseMessage.WM_LBUTTONUP)
                    {
                        MouseClick?.Invoke(_mouseHookStructure.Location, mouseMessage);

                        if(mouseMessage == MouseMessage.WM_LBUTTONDOWN && IsHookingArea())
                        {
                            return (IntPtr)1;
                        }
                    }
                }
            }

            return WIN32Helper.CallNextHookEx(_mouseHookHandle, code, wordParameter, longParameter);
        }

        #endregion
    }
}

 

▶ Simulator.cs

using WindowsInput;

namespace TestProject
{
    /// <summary>
    /// 시뮬레이터
    /// </summary>
    internal class Simulator
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Internal

        #region 입력 시뮬레이터 - Input

        /// <summary>
        /// 입력 시뮬레이터
        /// </summary>
        internal static InputSimulator Input { get; private set; }

        #endregion
        #region 키보드 시뮬레이터 - Keyboard

        /// <summary>
        /// 키보드 시뮬레이터
        /// </summary>
        internal static KeyboardSimulator Keyboard { get; private set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - Simulator()

        /// <summary>
        /// 생성자
        /// </summary>
        static Simulator()
        {
            Input    = new InputSimulator();
            Keyboard = new KeyboardSimulator(Input);
        }

        #endregion
    }
}

 

▶ KeyButton.cs

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

using WindowsInput.Native;

namespace TestProject
{
    /// <summary>
    /// 키 버튼
    /// </summary>
    public class KeyButton : Button
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 키 코드 속성 - KeyCodeProperty

        /// <summary>
        /// 키 코드 속성
        /// </summary>
        public static readonly DependencyProperty KeyCodeProperty = DependencyProperty.Register
        (
            "KeyCode",
            typeof(VirtualKeyCode),
            typeof(KeyButton)
        );

        #endregion
        #region PRESS 여부 속성 - IsPressedProperty

        /// <summary>
        /// PRESS 여부 속성
        /// </summary>
        public static readonly new DependencyProperty IsPressedProperty = DependencyProperty.Register
        (
            "IsPressed",
            typeof(bool),
            typeof(KeyButton)
        );

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 키 데이터 딕셔너리
        /// </summary>
        private static Dictionary<VirtualKeyCode, KeyData> _keyDataDictionary;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region PRESS 여부 - IsPressed

        /// <summary>
        /// PRESS 여부
        /// </summary>
        public new bool IsPressed
        {
            get
            {
                return (bool)GetValue(IsPressedProperty);
            }
            set
            {
                SetValue(IsPressedProperty, value);
            }
        }

        #endregion
        #region 키 코드 - KeyCode

        /// <summary>
        /// 키 코드
        /// </summary>
        public VirtualKeyCode KeyCode
        {
            get
            {
                return (VirtualKeyCode)GetValue(KeyCodeProperty);
            }
            set
            {
                SetValue(KeyCodeProperty, value);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - KeyButton()

        /// <summary>
        /// 생성자
        /// </summary>
        static KeyButton()
        {
            SetKeyDataDictionary();
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - KeyButton()

        /// <summary>
        /// 생성자
        /// </summary>
        public KeyButton()
        {
            Focusable = false;
            IsTabStop = false;
            ClickMode = ClickMode.Press;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 키 데이터 딕셔너리 설정하기 - SetKeyDataDictionary()

        /// <summary>
        /// 키 데이터 딕셔너리 설정하기
        /// </summary>
        private static void SetKeyDataDictionary()
        {
            _keyDataDictionary = new Dictionary<VirtualKeyCode, KeyData>();

            _keyDataDictionary.Add(VirtualKeyCode.VK_1, new KeyData { DefaultKey = "1", ShiftKey = "!" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_2, new KeyData { DefaultKey = "2", ShiftKey = "@" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_3, new KeyData { DefaultKey = "3", ShiftKey = "#" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_4, new KeyData { DefaultKey = "4", ShiftKey = "$" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_5, new KeyData { DefaultKey = "5", ShiftKey = "%" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_6, new KeyData { DefaultKey = "6", ShiftKey = "^" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_7, new KeyData { DefaultKey = "7", ShiftKey = "&" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_8, new KeyData { DefaultKey = "8", ShiftKey = "*" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_9, new KeyData { DefaultKey = "9", ShiftKey = "(" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_0, new KeyData { DefaultKey = "0", ShiftKey = ")" });

            _keyDataDictionary.Add(VirtualKeyCode.VK_A, new KeyData { DefaultKey = "a", HangulKey = "ㅁ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_B, new KeyData { DefaultKey = "b", HangulKey = "ㅠ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_C, new KeyData { DefaultKey = "c", HangulKey = "ㅊ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_D, new KeyData { DefaultKey = "d", HangulKey = "ㅇ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_E, new KeyData { DefaultKey = "e", HangulKey = "ㄷ", HangulShiftKey = "ㄸ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_F, new KeyData { DefaultKey = "f", HangulKey = "ㄹ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_G, new KeyData { DefaultKey = "g", HangulKey = "ㅎ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_H, new KeyData { DefaultKey = "h", HangulKey = "ㅗ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_I, new KeyData { DefaultKey = "i", HangulKey = "ㅑ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_J, new KeyData { DefaultKey = "j", HangulKey = "ㅓ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_K, new KeyData { DefaultKey = "k", HangulKey = "ㅏ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_L, new KeyData { DefaultKey = "l", HangulKey = "ㅣ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_M, new KeyData { DefaultKey = "m", HangulKey = "ㅡ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_N, new KeyData { DefaultKey = "n", HangulKey = "ㅜ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_O, new KeyData { DefaultKey = "o", HangulKey = "ㅐ", HangulShiftKey = "ㅒ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_P, new KeyData { DefaultKey = "p", HangulKey = "ㅔ", HangulShiftKey = "ㅖ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_Q, new KeyData { DefaultKey = "q", HangulKey = "ㅂ", HangulShiftKey = "ㅃ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_R, new KeyData { DefaultKey = "r", HangulKey = "ㄱ", HangulShiftKey = "ㄲ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_S, new KeyData { DefaultKey = "s", HangulKey = "ㄴ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_T, new KeyData { DefaultKey = "t", HangulKey = "ㅅ", HangulShiftKey = "ㅆ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_U, new KeyData { DefaultKey = "u", HangulKey = "ㅕ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_V, new KeyData { DefaultKey = "v", HangulKey = "ㅍ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_W, new KeyData { DefaultKey = "w", HangulKey = "ㅈ", HangulShiftKey = "ㅉ" });
            _keyDataDictionary.Add(VirtualKeyCode.VK_X, new KeyData { DefaultKey = "x", HangulKey = "ㅌ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_Y, new KeyData { DefaultKey = "y", HangulKey = "ㅛ"                     });
            _keyDataDictionary.Add(VirtualKeyCode.VK_Z, new KeyData { DefaultKey = "z", HangulKey = "ㅋ"                     });

            _keyDataDictionary.Add(VirtualKeyCode.OEM_3     , new KeyData { DefaultKey = "`"        , ShiftKey = "~"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_MINUS , new KeyData { DefaultKey = "-"        , ShiftKey = "_"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_PLUS  , new KeyData { DefaultKey = "="        , ShiftKey = "+"  });
            _keyDataDictionary.Add(VirtualKeyCode.BACK      , new KeyData { DefaultKey = "Backspace"                  });
            _keyDataDictionary.Add(VirtualKeyCode.TAB       , new KeyData { DefaultKey = "Tab"                        });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_4     , new KeyData { DefaultKey = "["        , ShiftKey = "{"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_6     , new KeyData { DefaultKey = "]"        , ShiftKey = "}"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_5     , new KeyData { DefaultKey = "₩"       , ShiftKey = "|"  });
            _keyDataDictionary.Add(VirtualKeyCode.CAPITAL   , new KeyData { DefaultKey = "Caps Lock"                  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_1     , new KeyData { DefaultKey = ";"        , ShiftKey = ":"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_7     , new KeyData { DefaultKey = "'"        , ShiftKey = "″" });
            _keyDataDictionary.Add(VirtualKeyCode.RETURN    , new KeyData { DefaultKey = "Enter"                      });
            _keyDataDictionary.Add(VirtualKeyCode.SHIFT     , new KeyData { DefaultKey = "Shift"                      });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_COMMA , new KeyData { DefaultKey = ","        , ShiftKey = "<"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_PERIOD, new KeyData { DefaultKey = "."        , ShiftKey = ">"  });
            _keyDataDictionary.Add(VirtualKeyCode.OEM_2     , new KeyData { DefaultKey = "/"        , ShiftKey = "?"  });
            _keyDataDictionary.Add(VirtualKeyCode.HANGUL    , new KeyData { DefaultKey = "한/영"                      });
            _keyDataDictionary.Add(VirtualKeyCode.SPACE     , new KeyData { DefaultKey = "icodebroker"                });

            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD0, new KeyData { DefaultKey = "0" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD1, new KeyData { DefaultKey = "1" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD2, new KeyData { DefaultKey = "2" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD3, new KeyData { DefaultKey = "3" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD4, new KeyData { DefaultKey = "4" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD5, new KeyData { DefaultKey = "5" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD6, new KeyData { DefaultKey = "6" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD7, new KeyData { DefaultKey = "7" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD8, new KeyData { DefaultKey = "8" });
            _keyDataDictionary.Add(VirtualKeyCode.NUMPAD9, new KeyData { DefaultKey = "9" });
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 키 업데이트하기 - UpdateKey(shift, capsLock, hangul)

        /// <summary>
        /// 키 업데이트하기
        /// </summary>
        /// <param name="shift">SHIFT 키 눌림 여부</param>
        /// <param name="capsLock">CAPS LOCK 키 눌림 여부</param>
        /// <param name="hangul">한글 키 눌림 여부</param>
        public void UpdateKey(bool shift, bool capsLock, bool hangul)
        {
            if(!_keyDataDictionary.ContainsKey(KeyCode))
            {
                return;
            }

            KeyData keyData = _keyDataDictionary[KeyCode];

            string key = keyData.DefaultKey;

            if(KeyCode >= VirtualKeyCode.VK_A && KeyCode <= VirtualKeyCode.VK_Z)
            {
                if(hangul)
                {
                    key = keyData.HangulKey;

                    if
                    (
                        shift &&
                        (
                            KeyCode == VirtualKeyCode.VK_Q ||
                            KeyCode == VirtualKeyCode.VK_W ||
                            KeyCode == VirtualKeyCode.VK_E ||
                            KeyCode == VirtualKeyCode.VK_R ||
                            KeyCode == VirtualKeyCode.VK_T ||
                            KeyCode == VirtualKeyCode.VK_O ||
                            KeyCode == VirtualKeyCode.VK_P
                        )
                    )
                    {
                        key = keyData.HangulShiftKey;
                    }
                }
                else if(shift && !capsLock)
                {
                    key = key.ToUpper();
                }
                else if(capsLock && (KeyCode >= VirtualKeyCode.VK_A && KeyCode <= VirtualKeyCode.VK_Z))
                {
                    key = key.ToUpper();

                    if(shift && capsLock)
                    {
                        key = key.ToLower();
                    }
                }
            }
            else
            {
                if(!hangul)  
                {
                    key = keyData.DefaultKey;

                    if(shift && !capsLock)
                    {
                        key = string.IsNullOrWhiteSpace(keyData.ShiftKey) ? key : keyData.ShiftKey;
                    }
                    else if(shift && capsLock)
                    {
                        key = string.IsNullOrWhiteSpace(keyData.ShiftKey) ? key : keyData.ShiftKey;
                    }
                }
                else if(hangul)
                {
                    if(shift && !capsLock)
                    {
                        key = string.IsNullOrWhiteSpace(keyData.ShiftKey) ? key : keyData.ShiftKey;
                    }
                    else if(shift && capsLock)
                    {
                        key = string.IsNullOrWhiteSpace(keyData.ShiftKey) ? key : keyData.ShiftKey;
                    }
                }

            }

            Content = key;
        }

        #endregion
    }
}

 

▶ VirtualKeyboard.cs

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

using WindowsInput.Native;

namespace TestProject
{
    /// <summary>
    /// 가상 키보드
    /// </summary>
    public class VirtualKeyboard : UserControl
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// ROUTED EVENT
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 가상 키 DOWN 이벤트 - VirtualKeyDownEvent

        /// <summary>
        /// 가상 키 DOWN 이벤트
        /// </summary>
        public static readonly RoutedEvent VirtualKeyDownEvent = EventManager.RegisterRoutedEvent
        (
            "VirtualKeyDown",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(VirtualKeyboard)
        );

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 표시 여부 속성 - IsShowProperty

        /// <summary>
        /// 표시 여부 속성
        /// </summary>
        public static readonly DependencyProperty IsShowProperty = DependencyProperty.Register
        (
            "IsShow",
            typeof(bool),
            typeof(VirtualKeyboard),
            new PropertyMetadata(true, IsShowPropertyChangedCallback)
        );

        #endregion
        #region 전역 사용 여부 속성 - UseGlobalProperty

        /// <summary>
        /// 전역 사용 여부 속성
        /// </summary>
        public static readonly DependencyProperty UseGlobalProperty = DependencyProperty.Register
        (
            "UseGlobal",
            typeof(bool),
            typeof(VirtualKeyboard),
            new PropertyMetadata(true, UseGlobalPropertyChangedCallback)
        );

        #endregion
        #region 후킹 이용 가능 여부 속성 - IsEnableHookProperty

        /// <summary>
        /// 후킹 이용 가능 여부 속성
        /// </summary>
        public static readonly DependencyProperty IsEnableHookProperty = DependencyProperty.Register
        (
            "IsEnableHook",
            typeof(bool),
            typeof(VirtualKeyboard),
            new PropertyMetadata(false, IsEnableHookPropertyChangedCallback)
        );

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 키 눌림 여부
        /// </summary>
        private bool keyPressed;

        /// <summary>
        /// 이전 키 버튼
        /// </summary>
        private KeyButton previusKeyButton;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 가상 키 DOWN 이벤트 - VirtualKeyDown

        /// <summary>
        /// 가상 키 DOWN 이벤트
        /// </summary>
        public event RoutedEventHandler VirtualKeyDown
        {
            add
            {
                AddHandler(VirtualKeyDownEvent, value);
            }
            remove
            {
                RemoveHandler(VirtualKeyDownEvent, value);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 표시 여부 - IsShow

        /// <summary>
        /// 표시 여부
        /// </summary>
        public bool IsShow
        {
            get
            {
                return (bool)GetValue(IsShowProperty);
            }
            set
            {
                SetValue(IsShowProperty, value);
            }
        }

        #endregion
        #region 전역 사용 여부 - UseGlobal

        /// <summary>
        /// 전역 사용 여부
        /// </summary>
        public bool UseGlobal
        {
            get
            {
                return (bool)GetValue(UseGlobalProperty);
            }
            set
            {
                SetValue(UseGlobalProperty, value);
            }
        }

        #endregion
        #region 후킹 이용 가능 여부 - IsEnableHook

        /// <summary>
        /// 후킹 이용 가능 여부
        /// </summary>
        public bool IsEnableHook
        {
            get
            {
                return (bool)GetValue(IsEnableHookProperty);
            }
            set
            {
                SetValue(IsEnableHookProperty, value);
            }
        }

        #endregion

        #region SHIFT 눌림 여부 - IsPressedShift

        /// <summary>
        /// SHIFT 눌림 여부
        /// </summary>
        public bool IsPressedShift { get; private set; }

        #endregion
        #region 한글 눌림 여부 - IsPressedHangul

        /// <summary>
        /// 한글 눌림 여부
        /// </summary>
        public bool IsPressedHangul { get; private set; }

        #endregion
        #region CAPS LOCK 눌림 여부 - IsPressedCapsLock

        /// <summary>
        /// CAPS LOCK 눌림 여부
        /// </summary>
        public bool IsPressedCapsLock { get; private set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 표시 여부 속성 변경시 콜백 처리하기 - IsShowPropertyChangedCallback(d, e)

        /// <summary>
        /// 표시 여부 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="d">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void IsShowPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            VirtualKeyboard keyboard = d as VirtualKeyboard;

            bool isShow = (bool)e.NewValue;

            keyboard.ChangeShow(isShow);
        }

        #endregion
        #region 전역 사용 여부 속성 변경시 콜백 처리하기 - UseGlobalPropertyChangedCallback(d, e)

        /// <summary>
        /// 전역 사용 여부 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="d">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void UseGlobalPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            HookingHelper.UseGlobal = (bool)e.NewValue;
        }

        #endregion
        #region 후킹 이용 가능 여부 속성 변경시 콜백 처리하기 - IsEnableHookPropertyChangedCallback(d, e)

        /// <summary>
        /// 후킹 이용 가능 여부 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="d">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void IsEnableHookPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            VirtualKeyboard virtualKeyboard = d as VirtualKeyboard;

            if((bool)e.NewValue)
            {
                virtualKeyboard.UpdateHookData();

                HookingHelper.UseGlobal = virtualKeyboard.UseGlobal;

                HookingHelper.MouseClick += virtualKeyboard.Hook_MouseClick;
                HookingHelper.KeyClick   += virtualKeyboard.Hook_KeyClick;

                HookingHelper.Start();
            }
            else
            {
                HookingHelper.MouseClick -= virtualKeyboard.Hook_MouseClick;
                HookingHelper.KeyClick   -= virtualKeyboard.Hook_KeyClick;

                HookingHelper.Stop();
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - VirtualKeyboard()

        /// <summary>
        /// 생성자
        /// </summary>
        public VirtualKeyboard()
        {
            Loaded           += UserControl_Loaded;
            Unloaded         += UserControl_Unloaded;
            IsVisibleChanged += UserControl_IsVisibleChanged;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 키 업데이트하기 - UpdateKeys(panel)

        /// <summary>
        /// 키 업데이트하기
        /// </summary>
        /// <param name="panel">패널</param>
        protected void UpdateKeys(Panel panel)
        {
            foreach(UIElement child in panel.Children)
            {
                if(child is Panel)
                {
                    Panel content = child as Panel;

                    UpdateKeys(content);
                }
                else if(child is KeyButton)
                {
                    KeyButton keyButton = child as KeyButton;

                    keyButton.UpdateKey(IsPressedShift, IsPressedCapsLock, IsPressedHangul);
                }
            }
        }

        #endregion
        #region 키 업데이트하기 - UpdateKeys()

        /// <summary>
        /// 키 업데이트하기
        /// </summary>
        protected void UpdateKeys()
        {
            Panel content = Content as Panel;

            UpdateKeys(content);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 사용자 컨트롤 로드시 처리하기 - UserControl_Loaded(sender, e)

        /// <summary>
        /// 사용자 컨트롤 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if(DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            }

            RenderTransform = new TranslateTransform();

            AddHandler(KeyButton.ClickEvent, (RoutedEventHandler)KeyButton_Click);

            if(IsEnableHook)
            {
                UpdateHookData();

                HookingHelper.KeyClick += Hook_KeyClick;

                HookingHelper.UseGlobal = UseGlobal;

                HookingHelper.Start();
            }

            Application.Current.MainWindow.Closed += (sender2, e2) => HookingHelper.Stop();
        }

        #endregion
        #region 사용자 컨트롤 언로드시 처리하기 - UserControl_Unloaded(sender, e)

        /// <summary>
        /// 사용자 컨트롤 언로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            HookingHelper.MouseClick -= Hook_MouseClick;
            HookingHelper.KeyClick   -= Hook_KeyClick;
        }

        #endregion
        #region 사용자 컨트롤 표시 여부 변경시 처리하기 - UserControl_IsVisibleChanged(sender, e)

        /// <summary>
        /// 사용자 컨트롤 표시 여부 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            IsPressedHangul = true;

            if((bool)e.NewValue)
            {
                if(IsEnableHook)
                {
                    IsPressedHangul   = !Simulator.Input.InputDeviceState.IsTogglingKeyInEffect(VirtualKeyCode.HANGUL );
                    IsPressedCapsLock =  Simulator.Input.InputDeviceState.IsTogglingKeyInEffect(VirtualKeyCode.CAPITAL);

                    UpdateHookData();
                }

                UpdateKeys();
            }
        }

        #endregion

        #region 후킹 마우스 클릭시 처리하기 - Hook_MouseClick(point, message)

        /// <summary>
        /// 후킹 마우스 클릭시 처리하기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <param name="message">메시지</param>
        private void Hook_MouseClick(POINT point, MouseMessage message)
        {
            Point screenPoint = PointFromScreen(new Point(point.X, point.Y));

            KeyButton keyButton = VisualElementHelper.TryFindFromPoint<KeyButton>(this, screenPoint);

            if(keyButton == null)
            {
                return;
            }

            VirtualKeyCode keyCode = keyButton.KeyCode;

            if(message == MouseMessage.WM_LBUTTONDOWN)
            {
                this.previusKeyButton = keyButton;

                keyButton.IsPressed = true;

                if(keyButton.ClickMode == ClickMode.Press)
                {
                    keyButton.RaiseEvent(new RoutedEventArgs(KeyButton.ClickEvent));
                }
            }
            else if(message == MouseMessage.WM_LBUTTONUP)
            {
                if(keyCode != VirtualKeyCode.CAPITAL && keyCode != VirtualKeyCode.SHIFT)
                {
                    if(this.previusKeyButton != null)
                    {
                        this.previusKeyButton.IsPressed = false;

                        this.previusKeyButton = null;
                    }

                    keyButton.IsPressed = false;
                }

                if(keyButton.ClickMode == ClickMode.Release)
                {
                    keyButton.RaiseEvent(new RoutedEventArgs(KeyButton.ClickEvent));
                }
            }
        }

        #endregion
        #region 후킹 키 클릭시 처리하기 - Hook_KeyClick(keyCode)

        /// <summary>
        /// 후킹 키 클릭시 처리하기
        /// </summary>
        /// <param name="keyCode">키 코드</param>
        private void Hook_KeyClick(uint keyCode)
        {
            if(this.keyPressed)
            {
                return;
            }

            switch((VirtualKeyCode)keyCode)
            {
                case VirtualKeyCode.HANGUL :

                    IsPressedHangul = !Simulator.Input.InputDeviceState.IsTogglingKeyInEffect(VirtualKeyCode.HANGUL);

                    break;

                case VirtualKeyCode.CAPITAL :

                    IsPressedCapsLock = !Simulator.Input.InputDeviceState.IsTogglingKeyInEffect(VirtualKeyCode.CAPITAL);

                    break;

                case VirtualKeyCode.LSHIFT :
                case VirtualKeyCode.RSHIFT :

                    IsPressedShift = !Simulator.Input.InputDeviceState.IsTogglingKeyInEffect(VirtualKeyCode.SHIFT);

                    break;
            }

            if(IsShow || Visibility == Visibility.Visible)
            {
                UpdateKeys();
            }
        }

        #endregion

        #region 키 버튼 클릭시 처리하기 - KeyButton_Click(sender, e)

        /// <summary>
        /// 키 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void KeyButton_Click(object sender, RoutedEventArgs e)
        {
            KeyButton keyButton = e.OriginalSource as KeyButton;

            if(keyButton == null || (IsEnableHook && keyButton.IsStylusCaptured))
            {
                return;
            }

            this.keyPressed = true;

            if(keyButton.KeyCode == VirtualKeyCode.CAPITAL)
            {
                IsPressedCapsLock = !IsPressedCapsLock;

                Simulator.Keyboard.KeyPress(keyButton.KeyCode);
            }
            else if(keyButton.KeyCode == VirtualKeyCode.SHIFT)
            {
                IsPressedShift = !IsPressedShift;

                if(IsPressedShift)
                {
                    Simulator.Keyboard.KeyDown(keyButton.KeyCode);
                }
                else
                {
                    Simulator.Keyboard.KeyUp(keyButton.KeyCode);
                }
            }
            else if(keyButton.KeyCode == VirtualKeyCode.HANGUL)
            {
                IsPressedHangul = !IsPressedHangul;

                Simulator.Keyboard.KeyPress(keyButton.KeyCode);
            }
            else
            {
                if(keyButton.KeyCode == VirtualKeyCode.RETURN)
                {
                    if(Keyboard.FocusedElement is TextBox)
                    {
                        TextBox textBox = Keyboard.FocusedElement as TextBox;

                        BindingExpression expression = textBox.GetBindingExpression(TextBox.TextProperty);

                        expression?.UpdateSource();
                    }
                }

                if(IsPressedShift)
                {
                    IsPressedShift = false;

                    Simulator.Keyboard.KeyPress(keyButton.KeyCode);

                    Simulator.Keyboard.KeyUp(VirtualKeyCode.SHIFT);
                }
                else
                {
                    Simulator.Keyboard.KeyPress(keyButton.KeyCode);
                }
            }

            UpdateKeys();

            RaiseEvent(new RoutedEventArgs(VirtualKeyDownEvent, keyButton.KeyCode));

            this.keyPressed = false;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 후킹 데이터 업데이트하기 - UpdateHookData()

        /// <summary>
        /// 후킹 데이터 업데이트하기
        /// </summary>
        private void UpdateHookData()
        {
            FrameworkElement content = Content as FrameworkElement;

            if(content == null)
            {
                return;
            }

            Rect area = VisualTreeHelper.GetDescendantBounds(content);

            if(!area.IsEmpty)
            {
                HookingHelper.HookArea    = area;
                HookingHelper.HookElement = this;
            }
        }

        #endregion
        #region 표시 여부 변경하기 - ChangeShow(isShow)

        /// <summary>
        /// 표시 여부 변경하기
        /// </summary>
        /// <param name="isShow">표시 여부</param>
        private void ChangeShow(bool isShow)
        {
            Vector offset = VisualTreeHelper.GetOffset(this);

            Storyboard storyboard = new Storyboard();

            double to = isShow ? 0 : ActualHeight + offset.Y;

            DoubleAnimation doubleAnimation = new DoubleAnimation(to, new Duration(TimeSpan.FromSeconds(0.35)));

            doubleAnimation.EasingFunction = new QuinticEase { EasingMode = EasingMode.EaseInOut };

            storyboard.Children.Add(doubleAnimation);

            Storyboard.SetTargetProperty
            (
                storyboard,
                new PropertyPath
                (
                    "(0).(1)",
                    new DependencyProperty[] { UIElement.RenderTransformProperty, TranslateTransform.YProperty }
                )
            );

            BeginStoryboard(storyboard, HandoffBehavior.SnapshotAndReplace);
        }

        #endregion
    }
}

 

▶ VirtualKeyboardControl.xaml

<local:VirtualKeyboard x:Class="TestProject.VirtualKeyboardControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject">
    <local:VirtualKeyboard.Resources>
        <Style TargetType="{x:Type local:KeyButton}">
            <Setter Property="Margin"                     Value="1"         />
            <Setter Property="BorderThickness"            Value="1"         />
            <Setter Property="Background"                 Value="RoyalBlue" />
            <Setter Property="Padding"                    Value="1"         />
            <Setter Property="Foreground"                 Value="White"     />
            <Setter Property="FontSize"                   Value="22"        />
            <Setter Property="HorizontalContentAlignment" Value="Center"    />
            <Setter Property="VerticalContentAlignment"   Value="Center"    />
            <Setter Property="RenderTransform">
                <Setter.Value>
                    <RotateTransform Angle="0.00000000001" />
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:KeyButton}">
                        <Border Name="border"
                            Margin="1"
                            CornerRadius="3"
                            Padding="1 0"
                            Background="{TemplateBinding Background}"
                            SnapsToDevicePixels="True">
                            <ContentPresenter Name="contentPresenter"
                                Margin="{TemplateBinding Padding}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                ContentStringFormat="{TemplateBinding ContentStringFormat}"
                                Content="{TemplateBinding Content}"
                                Focusable="False"
                                RecognizesAccessKey="True"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="border" Property="BorderBrush" Value="#f1685e" />
                                <Setter TargetName="border" Property="Background"  Value="#f1685e" />
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter TargetName="border" Property="BorderBrush" Value="#f1685e" />
                                <Setter TargetName="border" Property="Background"  Value="#f1685e" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </local:VirtualKeyboard.Resources>
    <Grid Background="White">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>
            <local:KeyButton KeyCode="OEM_3"     Grid.Column="0"  />
            <local:KeyButton KeyCode="VK_1"      Grid.Column="1"  />
            <local:KeyButton KeyCode="VK_2"      Grid.Column="2"  />
            <local:KeyButton KeyCode="VK_3"      Grid.Column="3"  />
            <local:KeyButton KeyCode="VK_4"      Grid.Column="4"  />
            <local:KeyButton KeyCode="VK_5"      Grid.Column="5"  />
            <local:KeyButton KeyCode="VK_6"      Grid.Column="6"  />
            <local:KeyButton KeyCode="VK_7"      Grid.Column="7"  />
            <local:KeyButton KeyCode="VK_8"      Grid.Column="8"  />
            <local:KeyButton KeyCode="VK_9"      Grid.Column="9"  />
            <local:KeyButton KeyCode="VK_0"      Grid.Column="10" />
            <local:KeyButton KeyCode="OEM_MINUS" Grid.Column="11" />
            <local:KeyButton KeyCode="OEM_PLUS"  Grid.Column="12" />
            <local:KeyButton KeyCode="BACK"      Grid.Column="13" FontSize="18" />
        </Grid>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions >
                <ColumnDefinition Width="1.8*" />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition Width="1.8*" />
            </Grid.ColumnDefinitions>
            <local:KeyButton KeyCode="TAB"   Grid.Column="0" FontSize="18" />
            <local:KeyButton KeyCode="VK_Q"  Grid.Column="1"  />
            <local:KeyButton KeyCode="VK_W"  Grid.Column="2"  />
            <local:KeyButton KeyCode="VK_E"  Grid.Column="3"  />
            <local:KeyButton KeyCode="VK_R"  Grid.Column="4"  />
            <local:KeyButton KeyCode="VK_T"  Grid.Column="5"  />
            <local:KeyButton KeyCode="VK_Y"  Grid.Column="6"  />
            <local:KeyButton KeyCode="VK_U"  Grid.Column="7"  />
            <local:KeyButton KeyCode="VK_I"  Grid.Column="8"  />
            <local:KeyButton KeyCode="VK_O"  Grid.Column="9"  />
            <local:KeyButton KeyCode="VK_P"  Grid.Column="10" />
            <local:KeyButton KeyCode="OEM_4" Grid.Column="11" />
            <local:KeyButton KeyCode="OEM_6" Grid.Column="12" />
            <local:KeyButton KeyCode="OEM_5" Grid.Column="13" />
        </Grid>
        <Grid Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="2*" />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition Width="2*" />
            </Grid.ColumnDefinitions>
            <local:KeyButton KeyCode="CAPITAL" Grid.Column="0" FontSize="18" />
            <local:KeyButton KeyCode="VK_A"    Grid.Column="1"  />
            <local:KeyButton KeyCode="VK_S"    Grid.Column="2"  />
            <local:KeyButton KeyCode="VK_D"    Grid.Column="3"  />
            <local:KeyButton KeyCode="VK_F"    Grid.Column="4"  />
            <local:KeyButton KeyCode="VK_G"    Grid.Column="5"  />
            <local:KeyButton KeyCode="VK_H"    Grid.Column="6"  />
            <local:KeyButton KeyCode="VK_J"    Grid.Column="7"  />
            <local:KeyButton KeyCode="VK_K"    Grid.Column="8"  />
            <local:KeyButton KeyCode="VK_L"    Grid.Column="9"  />
            <local:KeyButton KeyCode="OEM_1"   Grid.Column="10" />
            <local:KeyButton KeyCode="OEM_7"   Grid.Column="11" />
            <local:KeyButton KeyCode="RETURN"  Grid.Column="12" FontSize="18" />
        </Grid>
        <Grid Grid.Row="3">
            <Grid.ColumnDefinitions >
                <ColumnDefinition Width="2.2*" />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition Width="2.2*" />
            </Grid.ColumnDefinitions>
            <local:KeyButton KeyCode="SHIFT"      Grid.Column="0" FontSize="18" />
            <local:KeyButton KeyCode="VK_Z"       Grid.Column="1"  />
            <local:KeyButton KeyCode="VK_X"       Grid.Column="2"  />
            <local:KeyButton KeyCode="VK_C"       Grid.Column="3"  />
            <local:KeyButton KeyCode="VK_V"       Grid.Column="4"  />
            <local:KeyButton KeyCode="VK_B"       Grid.Column="5"  />
            <local:KeyButton KeyCode="VK_N"       Grid.Column="6"  />
            <local:KeyButton KeyCode="VK_M"       Grid.Column="7"  />
            <local:KeyButton KeyCode="OEM_COMMA"  Grid.Column="8"  />
            <local:KeyButton KeyCode="OEM_PERIOD" Grid.Column="9"  />
            <local:KeyButton KeyCode="OEM_2"      Grid.Column="10" />
            <local:KeyButton KeyCode="SHIFT"      Grid.Column="11" FontSize="18" />
        </Grid>
        <Grid Grid.Row="4">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="6*" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <local:KeyButton KeyCode="SPACE"   Grid.Column="0" FontSize="18" />
            <local:KeyButton KeyCode="HANGEUL" Grid.Column="1" FontSize="18" />
        </Grid>
    </Grid>
</local:VirtualKeyboard>

 

▶ VirtualKeyboardControl.xaml.CS

namespace TestProject
{
    /// <summary>
    /// 가상 키보드 컨트롤
    /// </summary>
    public partial class VirtualKeyboardControl : VirtualKeyboard
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - VirtualKeyboardControl()

        /// <summary>
        /// 생성자
        /// </summary>
        public VirtualKeyboardControl()
        {
            InitializeComponent();

            IsEnableHook = false;
        }

        #endregion
    }
}

 

▶ MainWindow.xaml

<Window x:Class="TestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject"
    Width="800"
    Height="600"
    Title="가상 키보드 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"    />
        </Grid.RowDefinitions>
        <TextBox Name="inputTextBox" Grid.Row="0"
            Height="200"
            InputMethod.PreferredImeState="On"
            InputMethod.PreferredImeConversionMode="Native" />
        <local:VirtualKeyboardControl Grid.Row="1"
            Margin="0 10 0 0"
            IsShow="True" />
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System.Windows;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            this.inputTextBox.Focus();           
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요