Catching WM_SETTEXT messages in window with predefined class name

M

Matej Rizman

We have a third party application that uses WM_SETTEXT windows messages for
interprocess communication. This application sends WM_SETTEXT messages to a
window with predefined class name ("NotificationForm", for example). I would
like to implement a managed component that would listen to these messages
and raise corresponding events. I am using Visual Studio 2003 on Windows XP
(or Windows 2000).

First I had a problem with creating a window that would have this predefined
class name. Because I could not force NativeWindow to accept
"NotificationForm" as a class name (property ClassName of class
CreateParams) I decided to handle this problem directly with Windows API
functions. So I have created the code that is attached at the end of this
message. However, this code works fine for minute or two and after that .NET
raises an exception. Exceptions are random - they appear at different
WM_SETTEXT messages and in different parts of code (most common part being
exception System.NullReferenceException at call to function GetMessage). So
I assume that umanaged code overwrites some part of memory that it should
not....

Interestingly, the exception is not raised if I remove part that handles
WM_SETTEXT messages (case WM_SETTEXT...).

I would very appreciate if anybody could provide any hint on how to resolve
this problem.
Thank you,
Matej


using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;
public class MyWindow : MarshalByRefObject
{
static public void Main ()
{
Console.WriteLine ("Starting...");
// Register class
Console.WriteLine("Registering class...");
RegisterMyClass();
// creating windows
Console.WriteLine("Creating window...");
IntPtr hwnd;
hwnd = CreateWindowExA(
(uint)0, // dwExStyle
"NotificationForm", // lpClassName
"asd", // lpWindowName
(uint)WS_VISIBLE, // dwStyle
0,0,0,0, //x, y, nWidth, nHeight
//(IntPtr)HWND_MESSAGE, // hWndParent
(IntPtr)0, // hwndParent
(IntPtr)0, // hMenu
(IntPtr)0,
//GetModuleHandle(null), // hInstance
(IntPtr)0 // lpParam
);
if (hwnd == (IntPtr)0)
throw new Win32Exception();
Console.WriteLine("Running...");
MSG msg = new MSG();
while (GetMessageA(ref msg, 0, 0, 0) != 0)
{
TranslateMessage(ref msg);
DispatchMessageA(ref msg);
}
}
static protected int RegisterMyClass()
{
WNDCLASS wc = new WNDCLASS();
int res;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = (IntPtr)0;
wc.hCursor = (IntPtr)0;
wc.hInstance = (IntPtr)0;
wc.lpfnWndProc = new WndProc(MyWndProc);;
//wc.hInstance = (IntPtr)0;
//wc.hInstance = GetModuleHandle(null);
wc.hInstance = (IntPtr)0;
wc.lpszMenuName = "";
wc.lpszClassName = "NotificationForm";
res = RegisterClass(ref wc);
if (res == 0)
throw new Win32Exception();
return res;
}
static private IntPtr MyWndProc(
IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
String s = "";
switch(msg)
{
case WM_SETTEXT:
s = Marshal.PtrToStringAnsi(lParam);
Console.WriteLine("WM_SETTEXT: " + s);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProcA(hWnd, msg, wParam, lParam);
}
public const uint WS_VISIBLE = 0x10000000;
public const uint WM_SETTEXT = 0x000c;
public const uint WM_DESTROY = 0x0002;
public delegate IntPtr WndProc (IntPtr hwnd, uint msg, IntPtr wParam, IntPtr
lParam);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi, EntryPoint = "RegisterClassA", SetLastError = true)]
public static extern int RegisterClass(ref WNDCLASS wndClass);
[DllImport("kernel32", SetLastError = true)]
public extern static IntPtr GetModuleHandle(string modName);
[DllImport ("user32.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr CreateWindowExA (
uint dwExStyle, string lpClassName,
string lpWindowName, uint dwStyle,
int x, int y, int nWidth, int nHeight,
IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance,
//ref object lpParam);
IntPtr lpParam);
[DllImport ("user32.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public extern static IntPtr DefWindowProcA(IntPtr hWnd, uint Msg, IntPtr
wParam, IntPtr lParam);
[DllImport ("user32.dll", CallingConvention =
CallingConvention.StdCall,CharSet = CharSet.Ansi, SetLastError = true)]
public static extern int GetMessageA (ref MSG msg, int hwnd, int msgFrom,
int msgTo);
[DllImport ("user32.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern int TranslateMessage (ref MSG msg);
[DllImport ("user32.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern int DispatchMessageA (ref MSG msg);
[DllImport ("user32.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public extern static void PostQuitMessage (int nExitCode);
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public uint message;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public POINT pt;
}
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public long x;
public long y;
}
[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASS
{
public int style;
public WndProc lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
}
}
 
M

Mattias Sjögren

Matej,
However, this code works fine for minute or two and after that .NET
raises an exception.

I'm guessing that's because your WndProc delegate has been garbage
collected and the native function pointer becomes invaild.

Why don't you use a NativeWindow class instead, and assign to it the
window handle you get from CreateWindowEx?



Mattias
 
A

Andreas Håkansson

Matej,

Try it like Mattias suggested. All you need to do it to create a class
which
derives from the NativeWindow class and override the WndProc method
like this.

public delegate void NotifyWindowMessageDelegate(ref Message m);

class NotifyWindow : NativeWindow
{
public event NotifyWindowMessageDelegate OnMessage;

public NotifyWindow(IntPtr windowHandle)
{
this.AssignHandle(windowHandle);
}

protected override void WndProc(ref Message m)
{
if( this.OnMessage != null )
this.OnMessage(ref m);
base.WndProc (ref m);
}
}

Once you this class you use take the returned from your CreateWindowEx
call and pass it to a NotifyWindow object and then provide an event callback
like this.

NotifyWindow msgWindow = new NotifyWindow(yourHandle);
msgWindow.OnMessage +=
new NotifyWindowMessageDelegate(msgWindow_OnMessage);

private void msgWindow_OnMessage(ref Message m)
{
uint msgId = (uint)m.LParam;
switch(msgId)
{
case WM_SETTEXT:
// Do your stuff
break;
}
}

Hope this helps,

//Andreas
 
M

Matej Rizman

Thank you very much to both of you! This solution works very fine (and
stable ;)

Best regards,
Matej



Andreas Håkansson said:
Matej,

Try it like Mattias suggested. All you need to do it to create a class
which
derives from the NativeWindow class and override the WndProc method
like this.

public delegate void NotifyWindowMessageDelegate(ref Message m);

class NotifyWindow : NativeWindow
{
public event NotifyWindowMessageDelegate OnMessage;

public NotifyWindow(IntPtr windowHandle)
{
this.AssignHandle(windowHandle);
}

protected override void WndProc(ref Message m)
{
if( this.OnMessage != null )
this.OnMessage(ref m);
base.WndProc (ref m);
}
}

Once you this class you use take the returned from your CreateWindowEx
call and pass it to a NotifyWindow object and then provide an event callback
like this.

NotifyWindow msgWindow = new NotifyWindow(yourHandle);
msgWindow.OnMessage +=
new NotifyWindowMessageDelegate(msgWindow_OnMessage);

private void msgWindow_OnMessage(ref Message m)
{
uint msgId = (uint)m.LParam;
switch(msgId)
{
case WM_SETTEXT:
// Do your stuff
break;
}
}

Hope this helps,

//Andreas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top