How to handle wm_print message in a CWnd (using MFC)? - winapi

Is there a way to handle wm_print message in a CWnd (using MFC) ?. I'm trying to intercept this message and prevent printing of a child control. Adding ON_WM_PRINT() to my message map throws compilation errors.

MFC only defines ON_WM_XXXX() macros for the commonest messages, but there is also a general ON_MESSAGE() macro to allow you to handle other cases. Add
ON_MESSAGE(WM_PRINT, OnPrint)
to your message map, and then declare and implement a member function
afx_msg LRESULT OnPrint(WPARAM, LPARAM);
The device context is passed in the WPARAM, so you need something like this in your implementation:
LRESULT MyWindowClass::OnPrint(WPARAM wp, LPARAM)
{
CDC* dc = CDC::FromHandle((HDC)wp);
// Do custom logic here ...
// Only call Default() if you want the default processing for this message too ...
return Default();
}

Related

SetWindowsHookEx(WH_JOURNALRECORD, ..) sometimes hang the system

I'm trying upgrade an old application to work on Windows 7 and I'm having problems with the "Macro recording" functionality, which is using Journal Hooks. I have followed every step necessary to make this work on Windows 7, that is, setting the uiAccess=true, signing the exe and running it from the Program Files directory.
It usually works, but sometimes, for no apparent reason, it seems like the SetWindowsHookEx function is waiting for something and hang the whole system in a weird way : no input is send to any application. The only way I can get out of this hang is by doing ctrl-alt-del, which force uninstall the hook.
I've replicated the problem with a simple application. Using the default generated Win32 template of Visual Studio, I've modified the About dialog callback to register and unregister the hook :
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
{
// added this
recordHook = ::SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordCallback, hInst, 0);
return (INT_PTR)TRUE;
}
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
// and this
UnhookWindowsHookEx(recordHook);
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
My hook callback function is simply
LRESULT CALLBACK JournalRecordCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
And, as with the larger application, sometimes, the SetWindowsHookEx call hang.
Does anybody has experienced this? I thought that maybe installing the hook inside the MessageLoop was causing the hang and I tried to move it to another thread but still got the hang.
Am I doing something wrong?
Thanks
JournalRecordProc callback function:
An application that has installed a JournalRecordProc hook procedure should watch for the VK_CANCEL virtual key code (which is implemented as the CTRL+BREAK key combination on most keyboards). This virtual key code should be interpreted by the application as a signal that the user wishes to stop journal recording. The application should respond by ending the recording sequence and removing the JournalRecordProc hook procedure. Removal is important. It prevents a journaling application from locking up the system by hanging inside a hook procedure.

(MFC) How can a parent class receive a control's messages if the control is a private member?

Let's say my main class has a private member that is a class derived from a CTreeView control. How can I handle the messages from this tree view control on the main class itself?
This is similar to the MDI base application that Visual Studios builds for you, where you have two dockable tree view controls named CClassView and CFileView and each has a private member that's derived from CTreeView.
Can I pass the message from the child member control CViewTree to my CFileView class with like this?
void CViewTree::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)pNMHDR);
}
This code throws an exception, but if this does work, how would I handle the TVN_SELCHANGED message in the parent class?
Edit:
So I've tried the following suggestions but haven't had much luck with either one.
//First try, in the parent .h file:
afx_msg BOOL OnSelChange(NMHDR *pNMHDR, LRESULT *pResult);
//In the .cpp file:
ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelChange)
//and
BOOL ParentClass::OnSelChange(NMHDR *pNMHDR, LRESULT *pResult)
{
AfxMessageBox(L"in handler");
Return TRUE;
}
Second try:
//in the parent .h file:
afx_msg void OnSelChange(NMHDR *pNMHDR, LRESULT *pResult);
//In the .cpp file:
ON_NOTIFY(TVN_SELCHANGED, AFX_IDW_PANE_FIRST, OnSelChange)
//and
void ParentClass::OnSelChange(NMHDR *pNMHDR, LRESULT *pResult)
{
AfxMessageBox(L"in handler");
}
Not sure why you want to do this, you have less code reusability as you have a tight coupling between the view and the parent. If you want to reuse the selection logic, you can extract it out into a separate class like the DRAWCLI sample does.
TVN_SELCHANGED is already sent to the parent. However MFC's message reflection routes the notification to the child window's message map when ON_NOTIFY_REFLECT is present in the child.
If you want the parent to have a say in the message processing as well, you can change ON_NOTIFY_REFLECT to ON_NOTIFY_REFLECT_EX and return FALSE in the reflected message handler.
You will get a WM_NOTIFY at the parent so the way you handle the notification is to add a ON_NOTIFY macro to the parent of the tree view like you normally do for a tree control on a dialog. The view's id is likely AFX_IDW_PANE_FIRST if you haven't specified one.
Sheng was able to figure out my problem, which looking back now was quite trivial. Maybe this will help others who might have the same question.
In the MDI w/ visual studio style program that I generated from Visual Studio 2010, the CFileView has a child member instance of CViewTree. CViewTree was derived from CTreeCtrl.
By default, MFC is already passing the messages up the child-to-parent chain. The answer is determining the control ID to get notification messages from in your parent class.
So, first things first, we need to know the ID of the tree control. In the OnCreate method of CFileView, you can see this code:
if (!m_wndFileView.Create(dwViewStyle, rectDummy, this, 4))
MSDN has the following for the Create method:
virtual BOOL Create(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID
);
In my example, the id is 4. Now in the parent (CFileView in this case), just create your ON_NOTIFY macro as such:
BEGIN_MESSAGE_MAP(CFileView, CDockablePane) //precreated for you
ON_NOTIFY(TVN_SELCHANGED, 4, OnSelChanged) //you create this
END_MESSAGE_MAP() //precreated for you
I had to type the line above by hand because the class wizard or message property for the parent didn't have a =TVN_SELCHANGED message. Next, make sure your handler method OnSelChanged is declared in the CFileView.h file as:
afx_msg void OnSelChanged(NMHDR *pNMHDR, LRESULT *pResult);
Now I'm able to handle the TVN_SELCHANGED message like this (back in the CFileView.cpp):
void CFileView::OnSelChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
HTREEITEM item = m_wndFileView.GetSelectedItem();
AfxMessageBox(m_wndFileView.GetItemText(item));
}
In the described case if you want to notify parent CFileView from control CViewTree with WM_NOTIFY TVN_SELCHANGED message, you should do it in virtual OnNotify function, not using message map. If OnNotify doesn't met correct handler, message would go to parent CMainFrame, and there you can use message map.
BOOL CFileView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if (nmHdr->idFrom != 4)
return CDockablePane::OnNotify(wParam, lParam, pResult);
if (nmHdr->code == TVN_SELCHANGED)
{
OnItemsSelChanged((NMHDR*)lParam, pResult);
return TRUE;
}
return FALSE;
}

Why would Windows hooks not receive certain messages?

Microsoft does not recommend DirectInput for keyboard and mouse input. As such, I've written an input manager class that uses SetWindowsHookEx to hook into WndProc and GetMsg. I believe the hooks are set appropriately, though they look to be the cause of various issues.
Neither my WndProc nor GetMsg hooks receive any of the messages that the actual WndProc is receiving. My input manager never receives the WM_INPUT, WM_BUTTON, WM_MOUSEWHEEL, and WM_KEY* messages that it needs.
What gives?
Partial header:
namespace InputManager
{
class CInputManager
{
HWND m_Window;
HHOOK m_WndProcHook;
HHOOK m_GetMessageHook;
static LRESULT CALLBACK WindowsProcedureHookProcedure(int Code, WPARAM WParameter, LPARAM LParameter);
static LRESULT CALLBACK GetMessageHookProcedure(int Code, WPARAM WParameter, LPARAM LParameter);
static LRESULT CALLBACK MessageHandler(HWND Window, UINT Message, WPARAM wParameter, LPARAM lParameter);
};
}
Partial source:
namespace InputManager
{
bool CInputManager::Initialize(HWND Window)
{
m_Window = Window;
// Hook into the sent messages of the target window to intercept input messages.
m_WndProcHook = SetWindowsHookEx(WH_CALLWNDPROC, &(WindowsProcedureHookProcedure), NULL, GetCurrentThreadId());
// Hook into the posted messages of the target window to intercept input messages.
m_GetMessageHook = SetWindowsHookEx(WH_GETMESSAGE, &(GetMessageHookProcedure), NULL, GetCurrentThreadId());
// Register mouse device for raw input.
RAWINPUTDEVICE RawInputDevice;
RawInputDevice.usUsagePage = HID_USAGE_PAGE_GENERIC;
RawInputDevice.usUsage = HID_USAGE_GENERIC_MOUSE;
RawInputDevice.dwFlags = RIDEV_INPUTSINK;
RawInputDevice.hwndTarget = m_Window;
return RegisterRawInputDevices(&(RawInputDevice), 1, sizeof(RawInputDevice));
}
void CInputManager::Shutdown()
{
// Unhook from the posted messages of the target window.
UnhookWindowsHookEx(m_GetMessageHook);
// Unhook from the sent messages of the target window.
UnhookWindowsHookEx(m_WndProcHook);
}
LRESULT CALLBACK CInputManager::WindowsProcedureHookProcedure(int nCode, WPARAM wParameter, LPARAM lParameter)
{
if(nCode == HC_ACTION)
{
// Forward to message handler.
CWPSTRUCT* Message = reinterpret_cast<CWPSTRUCT*>(lParameter);
MessageHandler(Message->hwnd, Message->message, Message->wParam, Message->lParam);
}
return CallNextHookEx(NULL, nCode, wParameter, lParameter);
}
LRESULT CALLBACK CInputManager::GetMessageHookProcedure(int nCode, WPARAM wParameter, LPARAM lParameter)
{
if(nCode == HC_ACTION)
{
// Forward to message handler.
CWPSTRUCT* Message = reinterpret_cast<CWPSTRUCT*>(lParameter);
MessageHandler(Message->hwnd, Message->message, Message->wParam, Message->lParam);
}
return CallNextHookEx(NULL, nCode, wParameter, lParameter);
}
}
I don't include the code for the message handler as it consists of 149 lines, most of which are the switches for the message types. The message values received in the WndProc are not the same as the ones in my callbacks.
I can't seem to add a comment under your original question which is where I would prefer to put this but:
From what it looks like you are trying to do, wouldn't a WH_KEYBOARD and WH_MOUSE hook be more appropriate?
I'm quite late to the party here, but I just spent so many hours figuring out the same problem, and hopefully someone else will find this useful.
My empirical conclusion is that DispatchMessage does not trigger WH_CALLWNDPROC hooks. In other words, messages that are posted in the thread's message queue and go through the message loop (GetMessage -> DispatchMessage) will not be caught by WH_CALLWNDPROC. It only catches messages sent directly to the window procedure with SendMessage etc.. And when you look at the documentation, that's kinda what it says:
An application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function before calling the window procedure to process a message sent to the thread.
And of course the opposite is true for WH_GETMESSAGE hook. It will catch posted messages but not sent messages. To get all messages you either have to use both hooks, or use subclassing to hook the window procedure directly:
WNDPROC realProc;
LRESULT CALLBACK hookProc(HWND h, UINT msg, WPARAM wp, LPARAM lp)
{
return CallWindowProc(realProc, h, msg, wp, lp);
}
...
realProc = (WNDPROC)SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)hookProc);
Also, the reason the OP's GetMessage hook wasn't working is probably because lParameter should be casted to MSG* and not CWPSTRUCT*.
I once had a similar problem. Im not quite shure what it was (i think it was consumed somewhere in PreTranslateMessage but i'm not shure) but i know how i spotted it:
I created one of those vanishing messages myself and debugged its way thru MFC. If i remeber correctly, i just returned somewhere the wrong BOOLEAN. However, this approach might give you the actual clue.

How to respond to a PropertySheet's OK or Apply button after the pages have processed it?

The PropertySheet API lets you define a PropSheetProc that can (on Windows XP and above) receive messages when the OK or Apply button is pressed. This lets you do processing when one of these buttons is clicked.
However, this handler is called before the individual property pages receive the PSN_APPLY notification through their respective dialog procedures. I want to do my processing after these notifications have been processed, preferably without ugly hacks. How do I do this?
Background: I'm storing my configuration in a single struct, and the individual pages each modify parts of this struct when they are applied. Then, after these values have been written, I want to apply the settings from the struct all at once, instead of re-applying all of them from each property page.
I ended up using the PropSheetProc callback to capture the property sheet's window handle, then hook up a subclass window procedure, like this:
// This is not officially defined, but the whole world uses it.
#define ID_APPLY_NOW 0x3021
WNDPROC origWinProc;
LRESULT CALLBACK MyWinProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
// Call the original window procedure.
LRESULT retVal = CallWindowProc(origWinProc, hwndDlg, msg, wParam, lParam);
// Now, and only now, do our own stuff.
switch (msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case ID_APPLY_NOW:
applyConfig();
saveConfig();
break;
}
break;
}
// Return the original winproc's result.
return retVal;
}
int CALLBACK myPropSheetProc(HWND hwndDlg, UINT msg, LPARAM lParam) {
switch (msg) {
case PSCB_INITIALIZED:
// Override the property sheet's window procedure with our own.
origWinProc = (WNDPROC)SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)&MyWinProc);
break;
}
return 0;
}
(Yes, I use switch statements even if there is only one case to consider. I'm strange like that.)
couldn't you just intercept the WM_COMMAND's BL_CLICKED message for ids IDOK, IDAPPLY and IDCANCEL?
Are you using MFC? or ATL?
Though I can't think of a time I wouldn't be able to intercept the BM_CLICKED...
Edit: Hmm never used property pages through that function... Just had a read through it. Have you set the pfnCallback function and the PSH_USECALLBACK flag set?
Each page also has a dialog proc. Can you not intercept the ok, cancel and apply from there as well? I think the other plan seems to fit more, though.
You don't NEED to apply the struct from inside any of the dialog handlers. So, if its a modal Property Sheet, you could do something simple like:
// Display the property sheet.
PropertySheet(&theSheet);
// The peroperty sheets PSN_APPLY notification sets this bool to true in the struct.
if(theStruct.fApply)
ApplyTheStruct(&theStruct);
Alternatively, I don't understand why, given that the 'struct' is just collecting the information entered on the pages, you dont change the struct as the user interacts with the page. Ignore the per-page PSN_APPLY messages and apply the struct when PropSheetProc is called. OR discard it totally if the notification is PSN_CANCEL.

MFC - execute code right after dialog is shown (.NET equivalent of Form.Shown)

I'm doing some small changes to C++ MFC project. I'm .NET developer so Windows programming is new to me.
I need to launch some method right after CDialog is completely shown (painted) for the first time, but only once.
How can I do this? In .NET I would handle Form.Shown event.
Do I need to handle some message? Which?
Do I need to override some CDialog method?
Or is there no easy way? I'm thinking of handling WM_ACTIVATE and then using a flag to ensure I call another method only once.
Found the answer here: Waiting until the dialog box is displayed before doing something
Short story:
INT_PTR CALLBACK
DlgProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg) {
case WM_INITDIALOG:
return TRUE;
case WM_WINDOWPOSCHANGED:
if ((((WINDOWPOS*)lParam)->flags & SWP_SHOWWINDOW) &&
!g_fShown) {
g_fShown = TRUE;
PostMessage(hwnd, WM_APP, 0, 0);
}
break;
case WM_APP:
MessageBox(hwnd,
IsWindowVisible(hwnd) ? TEXT("Visible")
: TEXT("Not Visible"),
TEXT("Title"), MB_OK);
break;
case WM_CLOSE:
EndDialog(hwnd, 0);
break;
}
return FALSE;
}
If you're using MFC like I am you'll need to map WM_WINDOWPOSCHANGED and then use ON_MESSAGE to map WM_APP. See this CodeProject article for more details.
For reference you don't need to override DlgProc to intercept WM_WINDOWPOSCHANGED.
ON_WM_WINDOWPOSCHANGED()
ON_MESSAGE(MyCDialog::MY_USER_MSG, OnDialogShown)
void MyCDialog::OnWindowPosChanged(WINDOWPOS *wndpos)
{
__super::OnWindowPosChanged(wndpos);
if (!mDialogShown && (wndpos->flags & SWP_SHOWWINDOW)) {
PostMessage(MY_USER_MSG);
mDialogShown = true;
}
}
LRESULT MyCDialog::OnDialogShown(WPARAM, LPARAM)
{
...
}
You can implement the handling inline instead of posting another message if appropriate.
Another approach I've used a number of times with great success is to use a timer. Set it for 10m0s. It'll only fire after the dialog is shown.
Hell put the code in OnPaint() and thow a bool m_fullyInitilized in your class.
I like the timer too.. Though I usually go with 100ms. I also move all my initilization code out of the oninit, in these cases.. Just to protect against too much processing in there.

Resources