성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] How can I tell whether two programs...
[정성태] The case of the fail-fast crashes c...
[정성태] Creating Docker multi-arch images f...
[정성태] BinaryFormatter removed from .NET 9...
[정성태] Extending the Windows Shell Progres...
[우광현] 와..... 범위를 잡았으니 클라이언트가 해당 범위를 확인해본다...
[정성태] 딱히, 그것 이상으로 더 설명할 내용이 없습니다. 동적 포...
[정성태] If Windows 3.11 required a 32-bit p...
[정성태] What is a hard error, and what make...
[괴물신인] 질문작성자인데 이 글을 이제봤네요 ㄷㄷ 이 글처럼 타입별로 인...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>RawInput을 이용한 키보드/마우스 입력 모니터링</h1> <p> 지난 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows - Raw Input의 Top level collection 의미 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11612'>http://www.sysnet.pe.kr/2/0/11612</a> </pre> <br /> Raw Input에 대한 이야기를 했는데요, 키에 대한 블록킹 기능은 없지만 모니터링을 하는 용도로는 잘 맞는 것 같습니다. 실제로 전역 후킹으로 인한 DLL 잠금 등의 문제가 없어 개발도 편리하고 C#에서도 구현하는 것이 매우 쉽습니다.<br /> <br /> 검색해 보면, 이미 대부분의 코드를 구현한 분이 있습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# Get Mouse handle (GetRawInputDeviceInfo) ; <a target='tab' href='https://stackoverflow.com/questions/14584280/c-sharp-get-mouse-handle-getrawinputdeviceinfo'>https://stackoverflow.com/questions/14584280/c-sharp-get-mouse-handle-getrawinputdeviceinfo</a> </pre> <a name='add_msg_filter'></a> <br /> 실제로 다음과 같이 코딩해 봤습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Windows.Forms; namespace WindowsFormsApp1 { // <a target='tab' href='https://stackoverflow.com/questions/14584280/c-sharp-get-mouse-handle-getrawinputdeviceinfo'>https://stackoverflow.com/questions/14584280/c-sharp-get-mouse-handle-getrawinputdeviceinfo</a> public partial class Form1 : Form, IMessageFilter { public Form1() { InitializeComponent(); Application.AddMessageFilter(this); } public bool PreFilterMessage(ref Message m) { if (m.Msg == Win32Interop.WM_INPUT) { RawInput raw = Win32Interop.GetDeviceID(m); string source = (m.WParam.ToInt32() == Win32Interop.RIM_INPUT) ? "this" : "others"; switch (raw.Header.Type) { case RawInputType.Keyboard: System.Diagnostics.Debug.WriteLine( $"[{source}] [keyboard] Device ID : {raw.Header.Device}, VKey : {raw.Keyboard.VKey}, Code : {raw.Keyboard.MakeCode} Msg: {raw.Keyboard.Message}"); break; case RawInputType.Mouse: System.Diagnostics.Debug.WriteLine( $"[{source}] [mouse] Device ID : {raw.Header.Device}, X : {raw.Mouse.LastX}, Y : {raw.Mouse.LastY}"); break; } } return false; } protected override void OnFormClosing(FormClosingEventArgs e) { Application.RemoveMessageFilter(this); base.OnFormClosing(e); } private unsafe void Form1_Load(object sender, EventArgs e) { RAWINPUTDEVICE [] Rid = new RAWINPUTDEVICE[2]; Rid[0].UsagePage = HIDUsagePage.Generic; Rid[0].Usage = HIDUsage.Mouse; Rid[0].Flags = RawInputDeviceFlags.InputSink; Rid[0].WindowHandle = this.Handle; Rid[1].UsagePage = HIDUsagePage.Generic; Rid[1].Usage = HIDUsage.Keyboard; Rid[1].Flags = RawInputDeviceFlags.InputSink; Rid[1].WindowHandle = this.Handle; if (Win32Interop.RegisterRawInputDevices(Rid, 2, sizeof(RAWINPUTDEVICE)) == false) { MessageBox.Show("Failed to register"); } } } } </pre> <br /> 등록 부분을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Rid[0].UsagePage = HIDUsagePage.Generic; Rid[0].Usage = HIDUsage.Mouse; Rid[0].Flags = RawInputDeviceFlags.InputSink; Rid[0].WindowHandle = this.Handle; Rid[1].UsagePage = HIDUsagePage.Generic; Rid[1].Usage = HIDUsage.Keyboard; Rid[1].Flags = RawInputDeviceFlags.InputSink; Rid[1].WindowHandle = this.Handle; </pre> <br /> 키보드와 마우스에 대한 입력을 모니터링하는 걸로 했고, Flags에 InputSink 옵션을 줬는데 이 옵션이 없으면 모니터링하는 프로그램이 전경(foreground) 윈도우로 선택되지 않으면 이벤트를 받지 못합니다. 즉, 다른 프로그램에 입력 포커스가 간 경우에도 모니터링을 원한다면 InputSink 옵션을 주는 것인데, 이 경우 시스템에게 어떤 윈도우에게 메시지를 전송할지 WindowHandle 속성에 Message Loop를 실행하고 있는 윈도우의 핸들을 설정해 주는 것으로 알려줄 수 있습니다.<br /> <br /> 등록이 정상적으로 되면, Window의 메시지 루프로 WM_INPUT 메시지를 통해 전달이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > if (m.Msg == Win32Interop.<span style='color: blue; font-weight: bold'>WM_INPUT</span>) { RawInput raw = Win32Interop.GetDeviceID(m); string source = (m.WParam.ToInt32() == Win32Interop.RIM_INPUT) ? "this" : "others"; switch (raw.Header.Type) { case RawInputType.Keyboard: System.Diagnostics.Debug.WriteLine( $"[{source}] [keyboard] Device ID : {raw.Header.Device}, VKey : {raw.Keyboard.VKey}, Code : {raw.Keyboard.MakeCode} Msg: {raw.Keyboard.Message}"); break; case RawInputType.Mouse: System.Diagnostics.Debug.WriteLine( $"[{source}] [mouse] Device ID : {raw.Header.Device}, X : {raw.Mouse.LastX}, Y : {raw.Mouse.LastY}"); break; } } </pre> <br /> 따라서 WM_INPUT 이벤트가 들어온 경우, 위와 같이 키보드/마우스 유형에 따라 적절하게 정보를 구하면 됩니다. 해보면, keydown/keyup, mouse down/move/up의 모든 입력을 받아오는 것을 확인할 수 있습니다.<br /> <br /> 참고로, UAC 권한에 의해 일반 사용자 권한으로 위의 프로그램을 실행한 경우 관리자 권한으로 실행한 프로그램의 입력은 감지할 수 없습니다. 당연히, 관리자 권한의 프로그램에 대한 입력도 받고 싶다면 자신의 프로그램도 관리자 권한으로 실행해야 합니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1319&boardid=331301885'>첨부 파일은 이 글의 프로젝트를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5750
(왼쪽의 숫자를 입력해야 합니다.)