I'm doing maintenance on a legacy MFC application. When the user selects Help → Help from the main menu along the top of the app, I need my custom OnHelp() to be called. From my research, I've learned that MFC normally intercepts this command automatically and brings up your help file itself. But you can override this command and intercept this message yourself. I have this in my message map:
BEGIN_MESSAGE_MAP(MyApp, MyBaseApp)
//{{AFX_MSG_MAP(MyApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_HELP, OnHelp)
//}}AFX_MSG_MAP
// Standard file based document commands
END_MESSAGE_MAP()
The "About" OnAppAbout() gets called, but not my OnHelp() (MFC still intercepts it and brings up the help by itself). My implementation is pretty straightforward:
void MyApp::OnHelp()
{
// This never gets called
MessageBox( NULL, "HtmlHelp: MyApp", "Hey", MB_OK );
CString csHelpFile;
csHelpFile.Format( "%s/MyHelp.chm", MyDoc::GetHelpPath() );
::HtmlHelp(
NULL,
csHelpFile,
HELP_WM_HELP,
NULL );
}
I know it's not getting called because my MessageBox never appears (I can't use Visual Studio to debug this; message boxes only). I also tried to wedge it into the CMDIFrameWnd, with the message map and similar implementation, with no success either (a different item from the Help menu item is implemented here and works fine). Any idea what I need to do to hook into my own custom help function?
You need to add ON_WM_HELPINFO() into your CMainFrame's message map. Define afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo); in your header file for the Main Frame, and implement it in the cpp file:
BOOL CMainFrame::OnHelpInfo(HELPINFO* pHelpInfo)
{
#ifdef _DEBUG
MessageBox( NULL, "HtmlHelp: MyApp", "Hey", MB_OK );
#endif
CString csHelpFile;
csHelpFile.Format( "%s/MyHelp.chm", MyDoc::GetHelpPath() );
::HtmlHelp(
NULL,
csHelpFile,
HELP_WM_HELP,
NULL );
return CFrameWnd::OnHelpInfo(pHelpInfo);
}
Related
How can I know that for a particular ComboBox which Dialog Style is being used? Is there any Win32 API which can give me that information?
I am using CDialog for a few ComboBox, CDialogEx for some, and an in-house Dialog class, let's say Ctl3dDialogEx, for others. GetClassName() will return the Class name of the ComboBox (if I am passing a ComboBox Handler) which can be "CComboBox".
Is there any Win32 API where I will pass the ComboBox Handler and it will return back to me the Dialog class name, for eg : "CDialog", "CDialogEx", "Ctl3dDialogEx", etc?
Below code will help to understand maybe:
void ComboBox_DoSomeManipulation( HWND hldg , int n )
{
/*"hldg" is the handler of the Current ComBobox */
LPTSTR lpClassName;
int nMaxCount = 256;
/*This will return "CComboBox" as value in lpClassName */
GetClassName(hldg , lpClassName , _count_of(nMaxCount));
/*Is there any WIN API like above which can give */
/* Dialog class information like CDialog, CDialogEx */
/* which the ComboBox is using ? */
}
If your combo-box can somehow get hold of a genuine pointer to its parent window, then you can use dynamic_cast<CDialogEx*>(pParent) to see if it's CDialogEx (returns nullptr if not). You will need several separate checks, starting from the most derived class! So, if your Ctl3dDialogEx is derived from CDialogEx, then:
. . .
CWnd *pParent = pCombo->GetParent(); // This, as is, is not valid code!
if (dynamic_cast<Ctl3dDialogEx*>(pParent) != nullptr) {
// It's a Ctl3dDialogEx parent
}
else if (dynamic_cast<CDialogEx*>(pParent) != nullptr) {
// It's a CDialogEx
}
else { // Assuming no other options …
// It's a CDialog
}
I would recommend making an accessible (static?) copy of the parent window's this pointer during initialisation, if you can. But there are other ways …
For example, assuming you have control over the definition of ComboBox_DoSomeManipulation and when it's called, change the first argument from an HWND to a CWnd* and, when you call it, use this rather than this->m_hwnd. (But this depends on the structure of your code!)
There is no Windows API help since all those dialogs will be subclassing the Windows DIALOG class. If this is all in process, and you are using the same MFC instance, you might be able to do this:
CWnd* pWnd = CWnd::FromHandlePermanent(hdlg);
if (pWnd != NULL)
{
if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialog))
{
}
else if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialogEx))
{
}
else if (pWnd->GetRuntimeClass() == RUNTIME_CLASS(CDialogxyz))
{
}
}
Back in the old days, MS compilers used with MFC didn't play well with dynamic_cast<>, so generally, when using MFC, I don't use it. I probably should have more trust in it, but I was stuck using Visual C++ 6 until 2008, so I am probably a little jaded. The more "standard" "MFC way" is to use the MFC macros...
Another possible ways is something like:
if (CDialogxyz* pDlgxyz = DYNAMIC_DOWNCAST(CDialogxyz, pWnd))
{
}
else if (CDialogEx* pDlgEx = DYNAMIC_DOWNCAST(CDialogEx, pWnd))
{
}
else if (CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog, pWnd))
{
}
We have an application that misbehaves when the user changes the date format in his Windows region settings. (Yes, the solution would be to fix the misbehaviour--- not the point here.)
Changing the settings braodcasts a WM_SETTINGCHANGE message to all applications. We're trying to avoid our problem by ignoring this message and keep the date format as it was (for now, as a hotfix).
Whatever I do, the TMonthCalendar component still changes its format, as well as TDateTimePicker which uses TMonthCalendar as its popup.
I tried (all with the same result):
Overriding TForm::WndProc()
void __fastcall TfrmMainWindow::WndProc( TMessage & msg )
{
if( msg.Msg == WM_SETTINGCHANGE ) return; // ignore
TForm::WndProc( msg );
}
Setting a hook with Application->HookMainWindow( hookWindowsMessage );
Setting TApplication.UpdateFormatSettings = false;
Setting Application->OnEvent = onApplicationEvent; to catch all events... but unfortunately this specific case falls under the "OnMessage only receives messages that are posted to the message queue, not those sent directly with the Windows API SendMessage function." rule.
It would seem that overriding WndProc() is almost a good idea, only that it only affects that one window, not the application as a whole. I thought that's what Application->HookMainWindow() was for, but apparently not.
Anyone any idea how to solve or circumvent this problem?
As Cody Gray states in the comment above, the problem is not the message handling but the TMonthCalendar component which uses the Win32 API calendar and thus works outside the application context.
However, for all other components (ie. TDateTimePicker's line edit) and internal global variables (like ShortDateFormat etc.) it is possible to ignore the format change as follows:
class TfrmMainWindow : public TForm
{
public:
__fastcall TfrmMainWindow( TComponent * Owner )
: TForm( Owner )
{
Application->HookMainWindow( hookWindowsMessage );
}
__fastcall ~TfrmMainWindow()
{
Application->UnhookMainWindow( hookWindowsMessage );
}
private:
bool __fastcall hookWindowsMessage( TMessage & msg )
{
if( msg.Msg == WM_SETTINGCHANGE )
{
String sLParam( (char *)msg.LParam );
// Check for Region Settings change (not Desktop background etc.)
if( sLParam.LowerCase() == "intl" )
{
return true; // ignore message
}
}
return false; // handle message as usual
}
};
Sources:
"Trapping Messages Sent to an Application"
"TMonthCalendar displays dates according to the system locale (ignoring the BiDiMode setting)."
"The Win32 API provides a control used to select dates on a colorful calendar. The dates used and the way they display are based on the Regional Settings of the Control Panel."
The MS documentation (and others) "clearly" states:
... Because the normal OnOk and OnCancel member functions of a CDialog
object would call EndDialog, make sure your modeless dialog box does
not call those functions and instead overrides
Since CDialog::OnOk effectively calls CDialog::EndDialog, and that method looks like:
void CDialog::EndDialog(int nResult)
{
ASSERT(::IsWindow(m_hWnd));
if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))
EndModalLoop(nResult);
::EndDialog(m_hWnd, nResult);
}
we can also check the docs for ::EndDialog which again "clearly" state:
Dialog boxes created by the DialogBox, DialogBoxParam,
DialogBoxIndirect, and DialogBoxIndirectParam functions must be
destroyed using the EndDialog function. An application calls EndDialog
from within the dialog box procedure; the function must not be used
for any other purpose.
Yet, I have a CDialog derived class that has it's default behavior wrt. OnOKand seemingly everything is working when I use it non-modal / modeless.
That is:
* When I close the (modeless) dialog, it is closed/removed from view.
* The application doesn't show any memory leaks. (MFC debug build)
So what? Do I need to prevent EndDialog and call DestroyWindow myself or not?
Note: I know what the docs and "the web" says. It's just that I haven't yet found why I need to do it differently, and this one class should be usable for modeless and modal mode, so not having to do anything different might be handy.
The MSDN Docs for CDialog::OnOK clearly states
If you implement the OK button in a modeless dialog box, you must
override the OnOK method and call DestroyWindow inside it. Do not call
the base-class method, because it calls EndDialog which makes the
dialog box invisible but does not destroy it
So you would need to override CDialog::OnOK and call DestroyWindow() inside -- here's a modified example from MSDN:
class CDlg : public CDialog
{
...
BOOL m_bModal;
...
}
CDlg::CDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDlg::IDD, pParent)
{
...
m_bModal = FALSE;
...
}
INT_PTR CDlg::DoModal()
{ m_bModal = TRUE;
const INT_PTR rval = CDialog::DoModal();
m_bModal = FALSE;
return rval;
}
void CDlg::OnOK()
{
if (!UpdateData(TRUE))
{
TRACE(_T("UpdateData failed during dialog termination\n"));
// The UpdateData routine will set focus to correct item
return;
}
if (m_bModal)
EndDialog(IDOK);
else
DestroyWindow();
}
void CDlg::OnCancel()
{
if (m_bModal)
EndDialog(IDCANCEL);
else
DestroyWindow();
}
I am writing a framework using VC2010, and mixed MFC/Win32. I have a number of control types, which all register their own window classes, and some of them use the WindowProc of one of the standard window class - similar to the following example.
void Button::OnInitClass(WNDCLASS &wndClass)
{
Object::OnInitClass(wndClass);
if(!GetClassInfo(NULL, WC_BUTTON, &wndClass)) {
throw std::exception("Failed getting window class info.");
}
wndClass.lpszClassName = ButtonBase::GetWndClassName();
}
bool Button::Create(CWnd* parent)
{
if(!WndClassInitialized) {
static VSCriticalSection cs;
cs.Enter();
if(!WndClassInitialized) {
WNDCLASS wndClass;
memset(&wndClass, 0, sizeof(WNDCLASS));
OnInitClass(wndClass);
assert(wndClass.lpszClassName);
if(!RegisterClass(&wndClass)) {
throw std::exception("Error registering window class");
}
WndClassInitialized = true;
}
cs.Leave();
}
DWORD style = WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON | BS_MULTILINE;
DWORD exStyle = WS_EX_LEFT | WS_EX_LTRREADING;
Created = CWnd::CreateEx(exStyle, ButtonBase::GetWndClassName(), "", style , _attribAnchor.Rect, parent, 0) != FALSE;
SetFont(_font);
return Created;
}
It all worked for a long time, but now I am getting a heap corruption now. However, if I use the WC_BUTTON class directly in the CreateEx call I don’t get the heap corruption.
I am in doubt of whether this way of registering a window class where I use the WindowProc from WC_BUTTON is safe. Is it?
If it is safe, then I of cause need to look for the course for the heap corruption elsewhere. But if not, is there a safe way to do what I try to do? (Namely registering an existing class with a new name - which I do in order to be able to recognize the controls from their window class name, given an arbitrary window handle)
I should mention that the heap corruption occurs in a string allocation, and all parameters seem right – so the problem must be somewhere prior to that.
Thanks in advance,
Martin
I'm afraid mixing MFC/Win32 make it worse. If your main framework/SDK is MFC then why not following MFC's rules and style? Why use ::RegisterClass instead of AfxRegisterClass (a lot simpler). Why register all different classes for mere UI controls?
Usual MFC programs only register new window class for popup or top-level windows (by AfxRegisterClass). MFC CWnd provides basic WndProc for that purpose. If you want a custom design button control you could use window subclassing mechanism.
I've been developing with QT for around a week now and am pleased to say that I'm picking it up really fast. I'm an intermediate C++ programmer but picking up some parts of QT is proving to be challenging. I need to process key press events from the QPlainTextEdit when the user presses enter and I presume that the solution will involve sub classing the widget. Can any of you smart guys give me a potential implementable solution?
To really understand Qt and event handling there are two key areas of the documentation you should read. The first is the overview on The Event System and the second is a very important bit which is a cleverly hidden link on that page for QCoreApplication::notify. They should really move that to the main page of the Event System documentation as it really makes things quite clear (to me at least).
If you only need to handle some messages sent to the control - like the key-presses - there is no need to subclass it. You can alternatively use the event filtering mechanism. Here is a simple example:
Provide virtual eventFilter method in one of your QObject-based classes (e.g. the window form class).
bool MyWindow::eventFilter(QObject *watched, QEvent *event)
{
if(watched == ui->myTargetControl)
{
if(event->type() == QKeyEvent::KeyPress)
{
QKeyEvent * ke = static_cast<QKeyEvent*>(event);
if(ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)
{
// [...]
return true; // do not process this event further
}
}
return false; // process this event further
}
else
{
// pass the event on to the parent class
return QMainWindow::eventFilter(watched, event);
}
}
Install your class as the event filter for the target control. Form constructor is usually a good place for this code. In the following snippet this refers to the instance of class in which you implemented the eventFilter method.
ui->myTargetControl->installEventFilter(this);
i would try subclassing QPlainTextEdit and reimplementing QWidget::keyPressEvent:
void YourTextEdit::keyPressEvent ( QKeyEvent * event )
{
if( event->key() == Qt::Key_Return )
{
// optional: if the QPlainTextEdit should do its normal action
// even when the return button is pressed, uncomment the following line
// QPlainTextEdit::keyPressEvent( event )
/* do your stuff here */
event->accept();
}
else
QPlainTextEdit::keyPressEvent( event )
}
please try :
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter){
//do something
}
in your keyPressEvent() function.