intercept WM_NCPAINT

What is the best way to intercept a WM_NCPAINT message going to a window. I want to find out when Non client area are going to be drawn so I can draw my own and stop the target process from getting the WM_NCPAINT. Should I hook DLL functions or use something in the message pump? and how do I tell if target is a full window, text box or a button given that I have the HWND.
LucidityAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

MadshiCommented:
Do we talk about a window in your own application or do we talk about a window that belongs to a different application or do we even talk about system wide stuff?
0
LucidityAuthor Commented:
yes, system wide. I'm sorry
0
MadshiCommented:
Then I think the best way would be to use SetWindowsHookEx with either WH_CALLWNDPROC or WH_GETMESSAGE (you will have to try which one works better). You must put the callback function in a dll.

Regards, Madshi.
0
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

NickRepinCommented:
Madshi, why do you not use the "Answer" button?
0
StarHuangleCommented:
Madshi,because what Madshi said is just
a comment.
0
MadshiCommented:
Hi Nick...  :-)

Well, I can't give Lucidity any C++ sources, you know, I'm working with Delphi. So I'm not sure if my comments help him enough to earn those 300 points. If Lucidity is satisfied with my comments, he can accept them as the answer...
0
LucidityAuthor Commented:
yes, source would be nice :-) too many people just cut and paste from MSDN. If that actually helped me I would never have to post here.
0
NickRepinCommented:
The problem with NCPAINT message is that it is *sent*, but not *posted*.
To intercept sent messages, there are two hooks: CALLWNDPROC and CALLWNDPROCRET. Both of them do not allow to modify the message. In other words, it seems, there is no way to prevent a window from receiving this message.

As I understood, you want to paint non-client area yourself. I suggest you to leave this idea alone. I don't think that the result will be acceptable.

Nevertheless,
if you cannot prevent NCPAINT, you can only paint a window after it is handled this message. So you have to use the CALLWNDPROCRET hook. The code is below.

There are no such things as "full window" or "text box".

There are some predefined class like "button", "static", "edit".
To get the info about window class, you have to use GetClassInfoEx() and then compare the class name with the names of the predefined classes. It's useless for non-standard (user-defined) window classes. Most programs use their own window classes. So also you have to determine the window [extended] style by GetWindowLong( GWL_STYLE/GWL_EXSTYLE)

//----------
// Main.cpp
//----------
#include <windows.h>
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>

bool setHook(DWORD threadId);
bool removeHook();

void main(int argc,char* argv[])
{
   if(setHook(0)) {
      cout<<"Press Enter to quit"<<endl;
      _getch();
      cout<<removeHook()<<endl;
   }  
}

//----------
// Hook.dll
//----------

// Cannot use WH_GETMESSAGE, because WM_NCPAINT is *sent*, but not *posted*.

#include <windows.h>

#pragma data_seg(".shared")
HHOOK hHook=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:.shared,RWS")

HINSTANCE hDll;

void onNCPaint(CWPRETSTRUCT& msg)
{
  // Do whatever you want.
}


LRESULT CALLBACK hookProc(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPRETSTRUCT* msg=(CWPRETSTRUCT*)(lParam);
      if(msg->message==WM_NCPAINT) {
         onNCPaint(*msg);
      }
   }
   return CallNextHookEx(hHook,code,wParam,lParam);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID)
{
   if(fdwReason==DLL_PROCESS_ATTACH)
      hDll=hinstDLL;
   return TRUE;
}

_declspec(dllexport) bool setHook(DWORD threadId)
{
   if(hHook==0)
      hHook=SetWindowsHookEx(WH_CALLWNDPROCRET,hookProc,hDll,threadId);
   return hHook!=0;
}

_declspec(dllexport) bool removeHook()
{
   bool r=true;
   if(hHook) {
      r=UnhookWindowsHookEx(hHook);
      hHook=0;
   }
   return r;
}
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
MadshiCommented:
Hehe...  :-)  Nick, I've an idea. Why not doing the following:

(1) Use your code, but with CALLWNDPROC instead.
(2) When receiving a NCPAINT message, *subclass* the receiving window (by calling SetWindowLong(GWL_WNDPROC, ...).
(3) In the wndProc of the subclassed window, handle the NCPAINT message, do your own drawing and don't give the message to the original wndProc.
(4) At the end of the subclassed wndProc call SetWindowLong(GWL_WNDPROC, ...) again to restore the old wndProc.

This should work funny, but it may slow down the performance a bit...

Regards, Madshi.
0
NickRepinCommented:
It's really a good idea. But I'm not sure that Windows will use the new proc address.
Lucidity have to try.
0
LucidityAuthor Commented:
well. I didn't get it to work, did you? It did appear to catch any messages.
0
NickRepinCommented:
I tested this code before I post it here. It works fine.

I copiled it from the command-line

cl /LD hook.cpp user32.lib
cl main.cpp hook.lib user32.lib
0
NickRepinCommented:
Once you press Enter, the hook will be uninstalled.
0
NickRepinCommented:
Madshi is right. It's possible to intercept the sent message by subclassing in the hook proc.
Here is the code.

Madshi, 200 points to you from me.

//----------
// Main.cpp
//----------

//cl %1 %2 %3 %4 /LD hook.cpp user32.lib
//cl main.cpp hook.lib user32.lib

#include <windows.h>
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>

bool setHook(DWORD threadId);
void removeHook();

void main(int argc,char* argv[])
{
   if(setHook(0)) {
      cout<<"Press Enter to quit"<<endl;
      _getch();
   }  
   removeHook();
}

//----------
// Hook.dll
//----------

#include <windows.h>

#pragma data_seg(".shared")
HHOOK hHook1=0;
HHOOK hHook2=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:.shared,RWS")

HINSTANCE hDll;

LPCSTR prop="TestPropName";

LRESULT CALLBACK wndProc(HWND hwnd,UINT uMsg,WPARAM wP,LPARAM lP)
{
   WNDPROC oldp=(WNDPROC)GetProp(hwnd,prop);
   if(uMsg==WM_NCPAINT) return 0;
   return CallWindowProc(oldp,hwnd,uMsg,wP,lP);
}

void onNCPaint1(CWPSTRUCT& msg)
{
   WNDPROC oldp=(WNDPROC) GetWindowLong(msg.hwnd,GWL_WNDPROC);
   if(SetProp(msg.hwnd,prop,HANDLE(oldp)))
      SetWindowLong(msg.hwnd,GWL_WNDPROC,LONG(wndProc));
}

void onNCPaint2(CWPRETSTRUCT& msg)
{
   WNDPROC oldp=(WNDPROC)GetProp(msg.hwnd,prop);
   if(oldp) {
      if(SetWindowLong(msg.hwnd,GWL_WNDPROC,LONG(oldp)))
         RemoveProp(msg.hwnd,prop);
   }
   // Here you can paint the non-client area yourself.
}


LRESULT CALLBACK hookProc1(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPSTRUCT* msg=(CWPSTRUCT*)(lParam);
      if(msg->message==WM_NCPAINT) {
         onNCPaint1(*msg);
      }
   }
   return CallNextHookEx(hHook1,code,wParam,lParam);
}

LRESULT CALLBACK hookProc2(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPRETSTRUCT* msg=(CWPRETSTRUCT*)(lParam);
      if(msg->message==WM_NCPAINT) {
         onNCPaint2(*msg);
      }
   }
   return CallNextHookEx(hHook2,code,wParam,lParam);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID)
{
   if(fdwReason==DLL_PROCESS_ATTACH)
      hDll=hinstDLL;
   return TRUE;
}

_declspec(dllexport) bool setHook(DWORD threadId)
{
   if(hHook1==0)
      hHook1=SetWindowsHookEx(WH_CALLWNDPROC,hookProc1,hDll,threadId);
   if(hHook2==0)
      hHook2=SetWindowsHookEx(WH_CALLWNDPROCRET,hookProc2,hDll,threadId);
   return hHook1 && hHook2;
}

_declspec(dllexport) void removeHook()
{
   if(hHook1) UnhookWindowsHookEx(hHook1);
   if(hHook2) UnhookWindowsHookEx(hHook2);
}
0
NickRepinCommented:
Madshi, which topic area do you prefer?
Delphi?
0
LucidityAuthor Commented:
is this the begining of the hook.cpp file?

//----------
// Hook.dll
//----------

I am trying to compile it now and I get  fatal error C1034 : windows.h: no include path set. Probably just cuz I am getting confused :)
0
LucidityAuthor Commented:
is this the begining of the hook.cpp file?

//----------
// Hook.dll
//----------

I am trying to compile it now and I get  fatal error C1034 : windows.h: no include path set. Probably just cuz I am getting confused :)
0
LucidityAuthor Commented:
OK, I got it to work.. and it works well. Ocasionally the minimize,maximize and close will not show up but show up later when you click where the title bar was or is supposed to be. Is there a way to make it more consistant, either so that the buttons are always drawn or not.

I've also noticed that sometimes the bar and frame will get drawn in. I don't actually know why yet. any ideas.. I'll keep increasing the points as long as people are still contributing, then make sure I open some more token questions for the others.
0
NickRepinCommented:
Code marked //hook.dll must reside in a separate .cpp file and must be compiled as dll.

To allow the default NCPAINT action (buttons will always be drawn), use the code in my original answer, not in the comment below the answer.

The code in the comment shows how to prevent passing WM_NCPAINT to the target window.

The title bar (including buttons) can be drawn in response on WM_NCACTIVATE and WM_SETTEXT messages. Try to intercept these messages also, especially WM_NCACTIVATE (add additional if() in the hookProc).

0
MadshiCommented:
Hi Nick, yes, my prefered topic area is Delphi programming, because there I can post my code. Here in the Windows forum, I can only say how to do it. Most people here don't understand Delphi sources...
On the other hand the questions here are often more interesting, because they're generally a bit more low level than the questions in the Delphi forum.
0
RadlerCommented:
listening...
0
LucidityAuthor Commented:
Well, I have been playing with this code and its looking promising. I have added a GetClassName call to the message intercepter and I get some interesting results from that, I can let scrollbars and edit controls maintain their frames while all other windows get the frame ripped.

Is there a way to find out if a window is of CMainFrame(?) or CDialog origin, those are the only windows I want to redraw myself.

Oh, and any time you right click on the process in the system tray the window get title bar gets drawn most times. I don't understand that..

0
NickRepinCommented:
<<right click on the process in the system tray >>
WM_SYSCOMMAND can also affect repainting.
As I said, it's hard to get the reliable result in your case.

Most dialogs has WC_DIALOG window class.

If under CMainFrame you mean the top-level window,
then you can check the following:
1) Window has no parent (GetParent()==0)
2) Window style is WS_POPUP or WS_OVERLAPPED and not WS_CHILD
3) Window has WS_CAPTION style



0
NickRepinCommented:
If your CMainFrame has min/max buttons or the system menu, check WS_MINIMIZEBOX/WS_MAXIMIZEBOX/WS_SYSMENU styles.
0
LucidityAuthor Commented:
OK, I guess I've gotten enough.. it works good except for its not quite consistant. but it is a start
0
NickRepinCommented:
Thanks for points.
I don't think it will be consistent. It seems Windows doesn't expect that somebody will handle WM_NCPAINT globally.
0
NickRepinCommented:
Because Windows performs non-client painting "behind" WM_NCPAINT.
0
LucidityAuthor Commented:
there must be a way to catch the other way it is drawing as well. I will try to figure it out or post another q.
0
NickRepinCommented:
Yes, you have to do it yourself. Use spy++ to check messages.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.