Specifying a Window Procedure for child Windows - c++

I would like to know if its possible to specify a WndProc for a Child Window created by CreateWindowEx.
I have created a Window Class, the Main Window, the Window Procedure and a Message Loop already. The code works and I decided to keep it out for the clarity of my question.
This is my Window Proc, so far:
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// Creation of the Win32 Window
case WM_CREATE:
// Add an Edit Field
CreateWindowEx(
WS_EX_CLIENTEDGE,
"EDIT",
"",
WS_CHILD | WS_VISIBLE,
5, 5, 200, 24,
hwnd,
(HMENU)100,
g_Instance, // Comming from WinMain
NULL
);
return DefWindowProc(hwnd, uMsg, lParam, wParam);
case WM_KEYDOWN:
// Track key presses on the edit field
std::cout << "The key with the code " << wParam << " was pressed." << std::endl;
return 0;
case WM_PAINT:
// Some painting code...
return DefWindowProc(hwnd, uMsg, lParam, wParam);
default:
return DefWindowProc(hwnd, uMsg, lParam, wParam);
}
}
I expected key presses on the child Edit Field that I created to throw a WM_KEYDOWN message, but they dont! The keys just get added to the Edit Field in my Window but do not cause a WM_KEYDOWN message.
It seems that the created Edit Window does not use my WndProc. How can I change that?

Your WndProc don't get WM_KEYDOWN messages because, if the user is typing inside the edit control, it means that it has the focus (not your window), so they are sent to the edit control window proc, not yours. However, the edit control window proc will send notifications to your WndProc (his parent window proc).
So, if you only want to react to the user changing the content of your child edit control, you don't need another window procedure. Your current WndProc will receive EN_CHANGE notification code through a WM_COMMAND message.
See https://msdn.microsoft.com/en-us/library/windows/desktop/bb761676(v=vs.85).aspx
If you really want to catch WM_KEYDOWN messages, you need to subclass the edit control, like this:
OldWndProc = (WNDPROC)SetWindowLongPtr (hButton, GWLP_WNDPROC, (LONG_PTR)NewWndProc);
You also need to define a new windows procedure (the NewWndProc), that should handle WM_KEYDOWN message (and any other message you want to handle). You also need to call OldWndProc as you would call DefWndProc in a standard WndProc, unless you want to prevent the edit control to do its normal processing.
For details on subclassing, see https://msdn.microsoft.com/en-us/library/windows/desktop/bb773183(v=vs.85).aspx
Edit
Responding to OP comment here.
If your window is a dialog box, you should be notified of enter key, in your WndProc:
case WM_COMMAND:
if(wParam == IDOFDEFBUTTON || wParam == IDOK) ...
See https://support2.microsoft.com/Default.aspx?scid=kb;en-us;Q102589
To be honest, I never took the time to understand what a dialog box really is. But if I recall correctly, you can get your window to get theses special notifications by calling IsDialogMessage in your message pump:
if(!IsDialogMessage(hWnd,&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
For interesting information about IsDialogMessage, see http://blogs.msdn.com/b/oldnewthing/archive/2012/04/16/10293933.aspx
If this doesn't give you enough control, you probably have to subclass the edit control.

Your call to CreateWindowsEx creates a new window with "EDIT" wnd class having its own Window procedure. You need a new WndProc and set it to the newly created window (whose handle is returned by CreateWindowEx) via SetClassLong function

Related

Is there a solution for this case?

This is a Windows Desktop Application project created by Visual Studio.
I have a Dialog resource created from the Resource View that has a Static Text.
I'm using this dialog in order to show errors to the user:
DialogBox(hInst, MAKEINTRESOURCE(IDD_MY_MESSAGE_BOX), hWnd, MyMessageBoxProc);
The reason I'm using a DialogBox is that I need it to stop the code execution, because the next line of code will close the application I mean the user should be aware of the error message before application exits. I know a way to change the Static Text:
HWND myMessageBox = CreateDialog(hInst, MAKEINTRESOURCE(IDD_MY_MESSAGE_BOX), nullptr, MyMessageBoxProc);
HWND staticText = GetDlgItem(myMessageBox, IDC_STATIC);
SetWindowText(staticText, L"Text changed.");
But that approach doesn't stop code execution.
Since it's a Windows Desktop Application project I cannot create MFC classes and try the following approach:
// Find the Static Text.
// If called from within MyMessageBox class.
CWnd *staticText = GetDlgItem(IDC_STATIC);
staticText->SetWindowText("Text changed.");
// If called from elsewhere.
MyMessageBox myMessageBox;
CWnd *staticText = myMessageBox.GetDlgItem(IDC_STATIC);
staticText->SetWindowText("Text changed.");
So what would be a workaround in order to change the Static Text using a DialogBox without the need of MFC classes or even another approach that allows me to change the Static Text and still stop code execution like a DialogBox.
Just change the text in your window procedure (MyMessageBoxProc) by handling WM_INITDIALOG message. If you wish to supply the text to the dialog, then create it using DialogBoxParam instead, which is then accessible via the lParam parameter.
e.g.
INT_PTR MyMessageBoxProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_INITDIALOG) {
HWND hCtrl = GetDlgItem(hWnd, IDC_STATIC);
SetWindowText(hCtrl, reinterpret_cast<LPCTSTR>(lParam));
}
return FALSE;
}
The creation would be something like:
LPCTSTR text = _T("Text changed.");
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_MY_MESSAGE_BOX), hWnd, MyMessageBoxProc,
reinterpret_cast<LPARAM>(text));
Note that there is a standard message box that ships with windows, which you may want to use instead of writing your own. That's available via the function MessageBox

MFC CToolBar update mechanism bug?

In Visual Studio, MFC CToolBar buttons are updated by ON_UPDATE_COMMAND_UI which is sent during idle state. I suppose this mechanism is buggy if I change enable/disable state of the button in ON_UPDATE_COMMAND_UI handler.
Specifically:
Suppose the button is now in enabled state. At a certain time, the code 'wants' the button to be disabled (and of-course should not be clicked by user), but the button will be really disabled at next idle period, as in following figure:
In red area in the figure, the code state, in my opinion, is unstable and the developer must handle this unstable state by checking the button state manually. I have no idea if I have missed something or have some misunderstanding of this mechanism?
PS: The procedure for Menu is OK. It always calls ON_UPDATE_COMMAND_UI handler and check the button state before ON_COMMAND handler.
My question is how to make the ON_UPDATE_COMMAND_UI handler be called before ON_COMMAND handler just like Menus do?
After debugging and tracing, I finally found a possible solution. Key codes are listed here to help others with the same problem. Override OnCommand as follows:
BOOL CMainDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
// Disable WM_COMMAND reflection for ToolBar control
// makes the ON_UPDATE_COMMAND_UI handler be called
// right before ON_COMMAND handler.
if ((HWND)lParam == m_wndToolBar.GetSafeHwnd())
lParam = NULL;
return CDialog::OnCommand(wParam, lParam);
}
The side effect is WM_COMMAND reflection is disabled for ToolBar, but it would be OK in most cases.
As I just ran into this so I thought I'd add my solution. I have a button to paste records into a database and so it is clear for the client, I only wanted the button enabled if there is valid data on the clip board. Here is what it looks like:
My App in the back and notepad++ in front with records selected. When I 'ctrl C' the text in notepad++ the 'I' on my tool bar becomes active even though my app is idle. My app is part of the clipboard chain and gets notified. This is the WM_DRAWCLIPBOARD handler.
LRESULT CMainFrame::OnDrawClipboard( WPARAM wparam, LPARAM lparam )
{
if( hWndClipboardChain )
::SendMessage( hWndClipboardChain, WM_DRAWCLIPBOARD, wparam, lparam );
if( wparam )
PostMessage( ID_CLIPBOARD_HASCHANGED, 0, 0 );
return TRUE;
}
From there I post to my app not getting in the way of the WM_DRAWCLIPBOARD message, and there:
LRESULT CMainFrame::OnCheckClipboard( WPARAM wparam, LPARAM lparam )
{
std::string data( GetClipboardStr( ) );
std::string::size_type end_cnt= data.find( "\r\n" );
if( end_cnt == std::string::npos )
bClipboardHasValidRecords= false;
else
{
auto header_end= data.begin( ) + end_cnt;
csv_vect_t header;
split( header, str_it_range_t( data.begin( ), header_end ), boost::is_any_of("\t") );
bClipboardHasValidRecords= header.size( ) == RARECORD_SIZE;
}
return TRUE;
}
The main thread of my app is waken up by the messages and the 'I' will turn on and off without making the app an active window. And it just happens without any extra code.

Opening a dialog box

I don't quite understand how this works. So I've made my dialog box.. or boxes. And I don't know how to make them appear in my code. Right now I'm trying to just get them to pop up right when I start my program so I can get a basic understanding of how this works.
switch (message)
{
case WM_CREATE:
HINSTANCE hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
CreateDialog(hInstance, "Whatever", hwnd, ABOUT_DIALOG);
That gives me an error in CreateDialog saying a parameter of type int is incompatible with DLGPROC. I'm assuming that I need to declare my dialog box somewhere?
And If I had a button on my very first start up window, how would I know that the user pressed the button? I'm going to once again assume and say that I need to catch it somewhere in the WM_COMMAND command?
The final parameter, the thing that you pass ABOUT_DIALOG to, needs to be a DLGPROC. That is a function of this form:
INT_PTR CALLBACK DialogProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
The compiler is telling you that ABOUT_DIALOG is not a function of that form. In fact the compiler tells you that ABOUT_DIALOG is an int which is definitely not the right thing!
To get it up and running with a default do-nothing dialog procedure implement it like this:
INT_PTR CALLBACK DialogProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
return FALSE;
}
The documentation says this:
Typically, the dialog box procedure should return TRUE if it processed the message, and FALSE if it did not. If the dialog box procedure returns FALSE, the dialog manager performs the default dialog operation in response to the message.
So by returning FALSE we are asking for default processing.
Once you have the dialog up and running, you can then fill out the dialog procedure with any functionality that you need.

How do I use Dialog resources in Win32?

Without resources I can create my UI with a complex array of CreateWindow() and CreateWindowEx(), and WndProc() to process my events.
I noticed if I right-click in the resource view and click "add resource", I can draw a dialog box with all the controls. This would save me a huge amount of time if I could draw the interface like I normally do with C#.
After I've drawn the interface with the resource editor, how do I then create the window from code? Can someone provide a very simple example with a button, and show how to handle a WM_COMMAND event on that button please?
Also, is this generally how people create the GUI? Is there any loss in flexible to do this way? Even in C# I often have to supplement designer-generated UI with my own code-generated UI, but the majority of the time I'm quite happy to use designer.
After creating the dialog in the resource editor, call CreateDialog(modeless dialog;you need to dispatch the messages manually just like when you use CreateWindow) or DialogBox(modal dialog; the function does not return until you close the dialog. it does the dispatching for you) to make the dialog show up. Just like you pass in the window proc to RegisterClass, you pass the dialog proc to those functions for the dialog call back. An example of DialogProc looks likes this:
BOOL DialogProc( HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam ){
switch( iMessage ){
case WM_COMMAND:
switch( LOWORD( wParam ) ){
case BTOK:
MessageBox( hDlg, "Hello, World!", NULL, NULL );
return TRUE;
break;
}
break;
}
return FALSE;
}
This is a basic way of creating a dialog. More sophisticated method would normally involve OOP, usually wrapping each resource( button, window, etc) as a C++ object or using MFC.
If you have placed your button or any control on some dialog, that control is already in created state. For handling the messages of these child controls on this dialog , you have to override OnCommand Method in the class which is implementing your dialog.
For Example:
//CDialog_ControlDlg is my Dialog class derived from CDialog
//IDC_BUTTON_SAMPLE is the ID of the button which was palced on the dialog in the resource Editor..
BOOL CDialog_ControlDlg::OnCommand(WPARAM wParam,LPARAM lparam){
int iNotiFicationMsg=HIWORD(wParam);//This is thenotification Msg from the child control
int iCommandId=LOWORD(wParam);//And Control ID of the Child control which caused that Msg
BOOL result=FALSE;
switch(iCommandId){
case IDC_BUTTON_SAMPLE:
if(iNotiFicationMsg==BN_CLICKED)
{
//Your Code for handling this type of Msg for this control..
}
break;
default:
{
//Specific Code;
}
return result;
}
}

SetWindowLongPtr doesnt work properly

I want to subclass RichEdit in my program (here is c++ code: http://dumpz.org/46182/). _native_log is a hwnd of richedit. At first all works fine and LogWindow::wndProc callback called normal, but if i set some text in RichEdit or click on them LogWindow::wndProc stops work (there no any further calls of it). Is there any thoughts what's i do wrong?
void LogWindow::replaceNativeLog(HWND native_log_handle) {
_native_log = native_log_handle;
SendMessage(_native_log, EM_GETOLEINTERFACE, 0, (LPARAM) &_rich_edit_ole);
_old_wnd_proc = (WNDPROC) SetWindowLongPtr(_native_log, GWLP_WNDPROC, (LONG) &wndProc);
}
LRESULT LogWindow::wndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case EM_STREAMIN:
break;
case WM_SETTEXT:
break;
};
return CallWindowProc(_old_wnd_proc, _native_log, Msg, wParam, lParam);
}
Starting with Common Controls version 6 the procedure of subclassing windows has been revised to eliminate the issues with previous versions. In particular it is no longer a problem if a control is subclassed more than once.
A comparison between subclassing pre-v6 Common Controls and the v6 way of doing things can be found at "Subclassing Controls". Instead of calling SetWindowLongPtr to replace the window procedure there is SetWindowSubclass which in addition to replacing the window procedure does all the internal bookkeeping. A consequence of the redesign is that you do not have to store a pointer to the previous window procedure either; if you need to call into the original window procedure there is DefSubclassProc at your disposal.
This of course will only help if all competing clients trying to subclass the a control all agree on using the v6 style subclassing.
Finally, I found the problem. I actually develop a plugin for Miranda IM, and there was another function trying to subclass richedit i want. So there is a kind of conflict between my and that functions. Thanks all for trying to help.