C# 에서 Window Message 전달
C# 에서 Window Message 를 전달 하고 받을 수 있도록 구현해 보았다. 기본적으로 다음 메소드를 “protected override void WndProc(ref Message wMessage)” override 하여 구현해소 사용 하면 된다.
COPYDATASTRUCT 구조체를 이용하여 WM_COPYDATA Message 로 전달 하는 방법을 이용하여 메시지를 전달해 보자
다음 코드는 Win32API 의 SendMessag 와 COPYDATASTRUCT 구조체를 정의 해놓은 Class 이다.
- Win32API 정의
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace MessageSender { public class Win32API { public const Int32 WM_COPYDATA = 0x004A; public struct COPYDATASTRUCT { public IntPtr dwData; public UInt32 cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, ref Win32API.COPYDATASTRUCT lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPStr)] string lParam); [DllImport("user32.dll", EntryPoint = "SendMessageW")] public static extern IntPtr SendMessageW(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] public static extern IntPtr SendMessage(HandleRef hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr FindWindow(string strClassName, string strWindowName); } } |
다음 코드는 ProcessName 을 통해 Process Handle 값을 얻어 해당 Process 에 Window Message 를 전달 하는 코드이다.
- SendMessage
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Diagnostics; using System.Runtime.InteropServices; namespace MessageSender { public partial class Form1 : Form { Win32API.COPYDATASTRUCT data; public Form1() { InitializeComponent(); } private void btnSendMessage_Click(object sender, EventArgs e) { String message = this.txtMessage.Text; String processName = this.txtProcessName.Text; data = new Win32API.COPYDATASTRUCT(); data.dwData = (IntPtr)(1024 + 604); data.cbData = (uint)message.Length * sizeof(char); data.lpData = message; IntPtr handle = Win32API.FindWindow(null, processName); if (handle.ToInt32() > 0) Win32API.SendMessage(handle, Win32API.WM_COPYDATA, IntPtr.Zero, ref data); } } } |
메시지 전달에서 핵심은 해당 Process의 Handle 을 얻어오는 것이며 해당 Handle 을 이용하여 Message 를 전달 하는 것이다. FindWindow 는 user32.dll 에 API 를 사용한 것이다. C++ 에서 CWnd::CreateEx () 생성시 lpszWindoName 값으로 들어간 WindowName 통해 찾아 준다.
virtual BOOL CreateEx( DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL ); virtual BOOL CreateEx( DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, LPVOID lpParam = NULL ); |
하지만 FindWindow 로 찾지 못하는 경우가 있다. 그럴 때는 Process[] proesss = Process.GetProcessesByName(processName); 와 같은 방식을 Process 를 얻어온 후 “proesss[0].MainWindowHandle” 로 사용 할 수 있다. 이와 같은 방식으로 못 가져 오는 경우에는 user32.dll 의 FindWindow 를 이용하여 하였더니 잘 되었다.
IntPtr handle = Win32API.FindWindow(null, processName); if (handle.ToInt32() > 0) Win32API.SendMessage(handle, Win32API.WM_COPYDATA, IntPtr.Zero, ref data); |
다음은 Window Message 를 전달 받는 방법을 구현해 보도록 하자. WndProc 는 C++ 에서도 볼 수 있는 익숙한 메소드(함수)이다.
- WmdProc()
protected override void WndProc(ref Message wMessage) { switch (wMessage.Msg) { case Win32API.WM_COPYDATA: Win32API.COPYDATASTRUCT lParam1 = (Win32API.COPYDATASTRUCT)Marshal.PtrToStructure(wMessage.LParam, typeof(Win32API.COPYDATASTRUCT)); Win32API.COPYDATASTRUCT lParam2 = new Win32API.COPYDATASTRUCT(); lParam2 = (Win32API.COPYDATASTRUCT)wMessage.GetLParam(lParam2.GetType()); MessageBox.Show("WM_COPYDATA : " + lParam1.lpData + "/ " + lParam2.lpData); break; default: break; } base.WndProc(ref wMessage); } |
매우 심플하다. WndProc() 를 override 하여 구현 하면 되는 것이다. 그런데 여기서 lpParam 값을 꺼내 오는 방법이 관건이였다. lpParam 은 해당 데이터(객체)의 포인터 주소 값이 넘어오게 된다. 하지만 C# 에서는 포인터라 없다. 그렇기 때문에 포인터 값을 통해 데이터(객체)를 얻어오기 위해서는 다음과 같이 하면 된다.
Win32API.COPYDATASTRUCT 는 Win32API Class 에 정의 해놓은 “COPYDATASTRUCT” 구조체이다. Marshal.PtrToStructure() 를 이용하면 쉽게 데이터를 꺼내 올 수있다. “PtrToStructure” 는 포인터 주소 값을 통해 구조체를 가져온다. 여기서 넘겨준 구조체는 Win32API.COPYDATASTRUCT 였기 때문에 해당 구조체로 형변환 해주면 되는 것이다.
Marshal.PtrToStructure() 은 모든 상황에서 포인터 주소 값만 있으면 구조체로 변환해서 값을 가져와 준다.
그리고 System.Windows.Forms.Message 에서는 GetLParam() 메소드를 제공해 주고 있다. 리턴 값은 object 이기 때문에 어떠한 객체든 가지고와 해당 객체의 타입으로 형변환 하면 된다.
[출처] C# 에서 Window Message 전달.|작성자 jackylim