Subclassing an unmanaged WndProc

S

Sean Setter

I am trying my hand at subclassing the WndProc of an unmanaged form in
order to trap its window messages, and do my own processing. Using
the code below, it will successfully subclass my own WndProc, but
anytime use a handle for any form or control outside of my own app,
SetWindowLongPtr fails. FWIW, I am testing this on 64bit Windows 7.

private void button1_Click(object sender, EventArgs e)
{
//enumerate thru Windows collection
//Windows wndList = new Windows(null, "FTCChat");
//foreach (Window wnd in wndList)
//{
// Console.WriteLine("hWnd[{0}] Class[{1}] Title[{2}]",
(int)wnd.hWnd, wnd.Class, wnd.Title);
//}
string targetTitle = "Untitled - Notepad";
IntPtr targethWnd = FindWindow(targetTitle);
IntPtr target = FindWindowEx(targethWnd, IntPtr.Zero,
"Edit", IntPtr.Zero);
SubclassHWnd(target);
SubclassHWnd(this.console.Handle);
}


// Win32 API needed
// This static method is required because legacy OSes do not
support
// SetWindowLongPtr
private static IntPtr SetWindowLongPtr(IntPtr hWnd, int
nIndex, IntPtr dwNewLong)
{
if (IntPtr.Size == 8)
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
else
return new IntPtr(SetWindowLong32(hWnd, nIndex,
dwNewLong.ToInt32()));
}

[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(IntPtr hWnd, int
nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd,
int nIndex, IntPtr dwNewLong);
[DllImport("user32")]
private static extern IntPtr CallWindowProc(IntPtr
lpPrevWndFunc, IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle,
IntPtr childAfter, string className, IntPtr windowTitle);

// from winuser.h:
private const int GWL_WNDPROC = -4;
private const int WM_LBUTTONDOWN = 0x0201;

// program variables
private IntPtr oldWndProc = IntPtr.Zero;
private Win32WndProc newWndProc = null;

// this is the new wndproc, just show a messagebox on left
button down:
private IntPtr MyWndProc(IntPtr hWnd, int Msg, int wParam, int
lParam)
{
switch (Msg)
{
case WM_LBUTTONDOWN:
MessageBox.Show("Clicked");
return IntPtr.Zero;

default:
break;
}

return CallWindowProc(oldWndProc, hWnd, Msg, wParam,
lParam);
}

// A delegate that matches Win32 WNDPROC:
private delegate IntPtr Win32WndProc(IntPtr hWnd, int Msg, int
wParam, int lParam);

void SubclassHWnd(IntPtr hWnd)
{
// create a delegate for the new wndproc
newWndProc = new Win32WndProc(MyWndProc);
// subclass
oldWndProc = SetWindowLongPtr(hWnd, GWL_WNDPROC,
Marshal.GetFunctionPointerForDelegate(newWndProc));
int err = Marshal.GetLastWin32Error();
Console.WriteLine("[err = {0}] oldWndProc = {1}", err,
oldWndProc);
}

public static IntPtr FindWindow(string name)
{
Process[] procs = Process.GetProcesses();
foreach (Process proc in procs)
{
if (proc.MainWindowTitle == name)
return proc.MainWindowHandle;
}
return IntPtr.Zero;
}
 
S

Sean Setter

Point of order: there's no such thing as "an unmanaged form", in this
context.  The word "form" implies an object from the System.Windows.Forms
namespace, which is by definition always managed.

IMHO, "unmanaged window" is a better term.


Can you be specific about "outside my own app"?  From your code example,
you appear to be trying to subclass a window class in a different
process.  I'm not sure I'd expect your example to work except for windows
within your own process.  I haven't bothered to try it, but I think your
code would not work even if you were writing a unmanaged Win32
application, if you are targeting a window in a different process.  The
function pointer you get from the delegate is valid only within your own
process.

Pete

Point taken on your clarifications, I would still consider myself
pretty new to the language and managed code in general.

Yes, my intention is to target the window in a different process that
has no interaction with the CLR.

The function pointer being valid for only my process sounds like it
would have the same effect as I was seeing. How would I go about this
another way?
 
S

Sean Setter

[...]
The function pointer being valid for only my process sounds like it
would have the same effect as I was seeing.  How would I go about this
another way?

Unfortunately, a) it's been too long for me to know the details off the  
top of my head, and b) this newsgroup isn't really an appropriate place  
for the discussion.  But, basically: if I recall correctly, you'll wantto  
install a window hook, to watch for the messages you're interested in.  
You can look in the regular Win32 unmanaged API documentation for more  
details (under "User" stuff, where window support is).

Pete

Ok, it looks like you are right. I found an example on codeproject
that looks like it will get me where I want to go using hooks.

Thanks for the help.
 

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