I added a notification icon to my dialog based application, and it received WM_LBUTTONDBLCLK when the icon is double clicked on, but it is not receiving WM_CONTEXTMENU when the icon is right clicked or when the icon is highlighted with the keyboard and the context menu key is pressed. I based my usage of the notification icon on the example in the Windows 7.1 SDK Samples. So, I have no idea where I'm going wrong or why this isn't working.
Note: If I change WM_CONTEXTMENU to WM_RBUTTONUP, it receives the event, but the cursor coordinates are wrong.
/******************************************************************************/
/* Menu Resource */
/******************************************************************************/
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDR_TRAYMENU MENU
{
POPUP ""
{
MENUITEM "&Show Status Window", IDM__SHOW_STATUS_WINDOW
MENUITEM "&About", IDM__ABOUT
MENUITEM SEPARATOR
MENUITEM "&Exit", IDM__EXIT
}
}
/******************************************************************************/
/* WinMain() */
/******************************************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// ... code unrelated to icon
// Enable Visual Styles
InitCommonControls();
// create the main dialog
if( NULL == (hWnd=CreateDialog(hInstance,MAKEINTRESOURCE(IDD_MAINDLG),NULL,(DLGPROC)WndProc)) )
{
MessageBox( NULL, "Error creating the main dialog!", NULL, MB_OK | MB_ICONERROR );
return -1;
}
// ... code unrelated to icon
MSG msg;
while( GetMessage(&msg,NULL,0,0) && IsWindow(hWnd) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/******************************************************************************/
/* WndProc() */
/******************************************************************************/
BOOL CALLBACK WndProc(HWND hWndDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
{
// ... code unrelated to icon
hIcon = (HICON)LoadImage( GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_DDCMP), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE );
// Setup the system tray icon
memset( &nid, 0, sizeof(NOTIFYICONDATA) );
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWndDlg;
nid.uID = 0xDDC;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP;
nid.uCallbackMessage = WM_APP + 0xDDC;
nid.hIcon = hIcon;
strcpy( nid.szTip, "DDCMP Driver" );
Shell_NotifyIcon( NIM_ADD, &nid );
// ... code unrelated to icon
return true;
} break;
case WM_APP + 0xDDC:
{
switch( LOWORD(lParam) )
{
case WM_CONTEXTMENU:
{
MessageBox( hWndDlg, "This message box never shows up.", NULL, MB_OK | MB_SYSTEMMODAL );
HMENU hMenu = LoadMenu(GetModuleHandle(NULL),MAKEINTRESOURCE(IDR_TRAYMENU));
if( hMenu )
{
HMENU hSubMenu = GetSubMenu(hMenu,0);
if( hSubMenu )
{
SetForegroundWindow( hWndDlg );
POINT pt = { LOWORD(wParam), HIWORD(wParam) };
UINT uFlags = TPM_RIGHTBUTTON;
if( 0 != GetSystemMetrics(SM_MENUDROPALIGNMENT) )
uFlags |= TPM_RIGHTALIGN;
else
uFlags |= TPM_LEFTALIGN;
TrackPopupMenuEx( hSubMenu, uFlags, pt.x, pt.y, hWndDlg, NULL );
}
DestroyMenu( hMenu );
}
} break;
case WM_LBUTTONDBLCLK:
if( IsWindowVisible(hWndDlg) )
ShowWindow( hWnd, SW_HIDE );
else
ShowWindow( hWnd, SW_SHOW );
break;
}
return true;
} break;
case WM_CLOSE:
ShowWindow( hWndDlg, SW_HIDE );
break;
case WM_DESTROY:
case WM_QUIT:
{
Shell_NotifyIcon( NIM_DELETE, &nid );
// ... code unrelated to icon
return true;
} break;
}
return false;
}
This is the WndProc from the Windows 7.1 SDK Sample
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND s_hwndFlyout = NULL;
static BOOL s_fCanShowFlyout = TRUE;
switch (message)
{
case WM_CREATE:
// add the notification icon
if (!AddNotificationIcon(hwnd))
{
MessageBox(hwnd,
L"Please read the ReadMe.txt file for troubleshooting",
L"Error adding icon", MB_OK);
return -1;
}
break;
case WM_COMMAND:
{
int const wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_LOWINK:
ShowLowInkBalloon();
break;
case IDM_NOINK:
ShowNoInkBalloon();
break;
case IDM_PRINTJOB:
ShowPrintJobBalloon();
break;
case IDM_OPTIONS:
// placeholder for an options dialog
MessageBox(hwnd, L"Display the options dialog here.", L"Options", MB_OK);
break;
case IDM_EXIT:
DestroyWindow(hwnd);
break;
case IDM_FLYOUT:
s_hwndFlyout = ShowFlyout(hwnd);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
break;
case WMAPP_NOTIFYCALLBACK:
switch (LOWORD(lParam))
{
case NIN_SELECT:
// for NOTIFYICON_VERSION_4 clients, NIN_SELECT is prerable to listening to mouse clicks and key presses
// directly.
if (IsWindowVisible(s_hwndFlyout))
{
HideFlyout(hwnd, s_hwndFlyout);
s_hwndFlyout = NULL;
s_fCanShowFlyout = FALSE;
}
else if (s_fCanShowFlyout)
{
s_hwndFlyout = ShowFlyout(hwnd);
}
break;
case NIN_BALLOONTIMEOUT:
RestoreTooltip();
break;
case NIN_BALLOONUSERCLICK:
RestoreTooltip();
// placeholder for the user clicking on the balloon.
MessageBox(hwnd, L"The user clicked on the balloon.", L"User click", MB_OK);
break;
//
//
// As you can very plainly see, the Windows SDK Sample ONLY used WM_CONTEXTMNEU
//
//
case WM_CONTEXTMENU:
{
POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
ShowContextMenu(hwnd, pt);
}
break;
}
break;
case WMAPP_HIDEFLYOUT:
HideFlyout(hwnd, s_hwndFlyout);
s_hwndFlyout = NULL;
s_fCanShowFlyout = FALSE;
break;
case WM_TIMER:
if (wParam == HIDEFLYOUT_TIMER_ID)
{
// please see the comment in HideFlyout() for an explanation of this code.
KillTimer(hwnd, HIDEFLYOUT_TIMER_ID);
s_fCanShowFlyout = TRUE;
}
break;
case WM_DESTROY:
DeleteNotificationIcon();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
I think you should try changing the uVersion member of the NOTIFYICONDATA structure to NOTIFYICON_VERSION_4, the documentation states that this members value will tell how the uCallbackMessage parameters will be interpreted when passed on to your callback function.
You can also have a look at this: Basic use of Shell_NotifyIcon in Win32
I did a bit of research and the following should work for you:
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWndDlg;
nid.uID = 0xDDC;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP;
nid.uCallbackMessage = WM_APP + 0xDDC;
nid.hIcon = hIcon;
nid.uVersion = NOTIFYICON_VERSION_4;
strcpy(nid.szTip, "DDCMP Driver");
Shell_NotifyIcon(NIM_ADD, &nid);
Shell_NotifyIcon(NIM_SETVERSION, &nid);
NIM_SETVERSION (MSDN):
Shell32.dll version 5.0 and later only. Instructs the notification
area to behave according to the version number specified in the
uVersion member of the structure pointed to by lpdata. The version
number specifies which members are recognized.
The notify icon has changed behaviour over the years. For reasons of compatibility with pre-existing code, you must opt-in to the new behaviour. If you don't opt-in then you don't get sent WM_CONTEXTMENU messages. Instead you have to respond to WM_RBUTTONUP. Even if you invoke the context menu from the keyboard, the system still sends WM_RBUTTONUP. You have to obtain the cursor position, in order to know where to show the menu, by calling GetCursorPos.
You can opt in to the new behaviour (and WM_CONTEXTMENU) as described in the documentation, by calling Shell_NotifyIcon passing NIM_SETVERSION after the NIM_ADD call. Presumably the SDK sample you are looking at does this somewhere. My guess is that is what is missing from your code.
The key extract from the documentation is in the remarks section:
As of Windows 2000 (Shell32.dll version 5.0), Shell_NotifyIcon mouse and keyboard events are handled differently than in earlier Shell versions found on Microsoft Windows NT 4.0, Windows 95, and Windows 98. The differences include the following:
If a user selects a notify icon's shortcut menu with the keyboard, the Shell now sends the associated application a WM_CONTEXTMENU message. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages.
If a user selects a notify icon with the keyboard and activates it with the SPACEBAR or ENTER key, the version 5.0 Shell sends the associated application an NIN_KEYSELECT notification. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages.
If a user selects a notify icon with the mouse and activates it with the ENTER key, the Shell now sends the associated application an NIN_SELECT notification. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages.
Related
I am creating a window with the following 2 aims both of which are requird:
when person clicks outside of the window, the window is destroyed.
when a person clicks a button inside the window, we process the WM_COMMAND message, and then the window is destroyed.
To achieve both these goals, I looked and found there is a WM_KILLFOCUS message which is sent when the window looses focus.
I wrote destroy window code in the WM_KILLFOCUS handler, but then the button click message box does not come. I searched and found the destroy event is not serialized and hence it could be that it is being sent before the window button click could be caught. I have hence changed the code like below, where it also sends a WM_COMMNAD to kill the window.
case WM_KILLFOCUS:
SendMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);// hope it serializes message
break;
case WM_COMMAND:
switch (wp)
{
case KILLTHEWIN:
DestroyWindow(hwnd);
break;
case BUTTON_CLICKED:
default:
MessageBox(NULL, L"Hurray", L"Hurray MSG reveid", MB_OK);
break;
}
I see that when I click the button inside the window, the window is destroyed and the MessageBox "Hurray" is not received.
How can I ensure that the messages are processed before destroying the window?
Update 1: Minimal code as asked. Now handling the WM_ACTIVATE message instead for handling WM_KILLFOCUS . Both the two aims are written above:
#include <windows.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <windowsx.h>
#include <map>
#include <commctrl.h>
#define KILLTHEWIN 10
#define BUTTON_PRESS 11
LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow)
{
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"mywindowsclass";
wc.lpfnWndProc = windowprocedure;
// before creating window for a class we need to register that class
if (!RegisterClassW(&wc))
{
return -1; // registration failed
}
HWND hWnd = CreateWindowW(L"mywindowsclass", L"My window", WS_POPUP | WS_VISIBLE/*| WS_EX_TOOLWINDOW*/ /* check for no task bar*/,
100, 100, 170, 500, NULL, NULL, NULL, NULL);
MSG msg{ 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
// translate the messahe
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// main window message handling
LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
DWORD dwStyleOfStaticText = 0;
HWND hBmp2 = NULL;
WORD reason = 0;
switch (msg)
{
case WM_CREATE:
dwStyleOfStaticText = SS_NOTIFY | SS_LEFT | WS_CHILD | WS_VISIBLE | WS_TABSTOP /*| WS_BORDER */;
hBmp2 = CreateWindowW(L"Button", L"Button1", WS_VISIBLE | WS_CHILD,
5, 5 + 4 * 15, 100, 50,
hwnd, (HMENU)BUTTON_PRESS, NULL, NULL);
SetWindowLong(hwnd, GWL_STYLE, 0);
break;
case WM_ACTIVATE:
reason = LOWORD(wp);
if (reason == WA_ACTIVE)
{
//Activated by some method other than a mouse click (for example, by a call to the SetActiveWindow function or by use of the keyboard interface to select the window).
}
else if (reason == WA_CLICKACTIVE)
{
// activated by mouse click
}
else if (reason == WA_INACTIVE)
{
PostMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);
}
break;
case WM_COMMAND: // when ever a menu is clicked
// WM_COMMAND has been received for which control handle and then kill it
switch (wp)
{
case KILLTHEWIN:
DestroyWindow(hwnd);
break;
case BUTTON_PRESS:
MessageBoxW(hwnd, L"button pressed", L"button pressed caption ", MB_OK);
break;
}
PostMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wp, lp);// case other as default window procedure
}
return (LRESULT)0;
}
The WM_ACTIVATE was handled above instead of the kill focus .
The two aims written above are really important . Still on pressing the button the MessageBox does not appear and window is killed .
I put it in debugger
one time the breakpoint hit the button message box but it still did not came . The other time the breakpoint hit the WM_ACTIVATE deactivate reason first .
Running my program it runs and due to me having a menu with EXIT to Destroy the window it runs and immediately exits the window. Unsure how to fix my issue here on compiling the program to have it not run the WindowProcedure function and passing the argument EXITMENU resulting in the Window being destroyed.
*.CPP
#include <windows.h>
#define HELPMENU 1
#define HIGHSCROREMENU 2
#define EXITMENU 3
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {
WNDCLASS wc = { 0 }; // WNDCLASSW is a structure
LPCWSTR title = L"Window"; // Long Pointer Constant Wide (UTF-16) String
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // Background
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_HAND); // Sets Cursor
wc.hInstance = hInst; // Instance of window
wc.lpszClassName = L"windowClass"; // Class name
wc.lpfnWndProc = WindowProcedure; // Pointer to the function // Controller of window handle
if (!RegisterClassW(&wc)) { // Registers the window class
return -1;
}
// | binary combination value, posX, posY, Width, Height
// Creates the window
CreateWindow(wc.lpszClassName, title, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_BORDER, 100, 100, 800, 600, NULL, NULL, NULL, NULL);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL) > 0) { // Keeps the window running
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/* Event Paths */
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_CREATE: // On window creation
AddControls(hWnd);
AddMenu(hWnd);
break;
case WM_LBUTTONDOWN: // Left Mouse button
break;
case WM_DESTROY: // Makes GetMessage Function return false, closing the window
PostQuitMessage(0);
return 0;
case EXITMENU:
DestroyWindow(hWnd); // This part of the code shouldn't run on creation
break;
default:
return DefWindowProc(hWnd, msg, wp, lp);
}
}
/* Creates menu */
void AddMenu(HWND hWnd) {
hMenu = CreateMenu(); // Creates menu object
// AppendMenu(Menu Instance, Usage Type, Argument, String info);
AppendMenu(hMenu, MF_STRING, HELPMENU, L"Help - F1");
AppendMenu(hMenu, MF_STRING, HIGHSCROREMENU, L"Highscores - F2"); // Menu Created
AppendMenu(hMenu, MF_STRING, EXITMENU, L"Exit - ESC");
// SetMenu(Window Handle , Menu Instance);
SetMenu(hWnd, hMenu); // Sets menu for window //
}
You are not handling the menu commands correctly in your WindowProcedure().
You have defined EXITMENU as 3, which is the same value as the WM_MOVE message. So, in your switch, you are destroying your window as soon as it receives a WM_MOVE message during window creation.
You need to instead handle the menu commands via the WM_COMMAND message, per the documentation:
About Menus: Messages Used With Menus
When the user chooses a command item from a menu, the system sends a WM_COMMAND message to the window procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.
Try this instead:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_COMMAND:
switch (wp) {
case HELPMENU: {
...
return 0;
}
case HIGHSCROREMENU: {
...
return 0;
}
case EXITMENU: {
DestroyWindow(hWnd);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
UPDATE: That being said, consider having your EXITMENU handler use SendMessage(WM_CLOSE) instead of DestroyWindow(). If your app maintains data that should be saved when the app is closed by the user, you can add a WM_CLOSE handler to perform that action regardless of how the window is being closed (your exit menu, X close button, Alt-F4, etc). DefWindowProc() destroys a window when processing WM_CLOSE.
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_CLOSE: {
if (data has been modified) {
prompt user to save data...
if (cancelled) {
return 0;
}
if (should save) {
save data ...
}
}
break;
}
case WM_COMMAND:
switch (wp) {
...
case EXITMENU: {
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
I have a working windows API application with the CALLBACK proc function as follows:
INT_PTR CALLBACK myTestCallBack(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_INITDIALOG:
HWND myRichEditHWND = GetDlgItem (hwndDlg, IDC_TEST);
// set some text length limit
SendMessage (myRichEditHWND, EM_LIMITTEXT, (WPARAM)MAX_STRING_LEN - 1, 0);
// we need key and mouse events, see WM_NOTIFY
SendMessage (myRichEditHWND, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS);
// misc options
SendMessage (myRichEditHWND, EM_SETOPTIONS, ECOOP_OR, ECO_AUTOWORDSELECTION | ECO_SELECTIONBAR);
SendMessage (myRichEditHWND, EM_SETZOOM, 64, 52);
return TRUE;
case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDOK:
case IDCANCEL:
PostQuitMessage(0);
EndDialog(hwndDlg, 0);
return TRUE;
}
break;
case WM_NOTIFY:
{
MessageBox(hwndDlg, L"Testing Dialog", L"Test", S_OK);
MSGFILTER* msgf = (MSGFILTER*)lParam;
if (msgf && msgf->nmhdr.hwndFrom == myRichEditHWND)
spellchecker_process (myRichEditHWND, msgf);
return FALSE;
}
default:
break;
}
return FALSE;
}
I am trying to integrate this functionality into an MFC application and I have carried out the following steps:
in the OnInitialUpdate() function i have:
CWnd* pwndBody = GetDlgItem( IDC_EDIT_OUTBOUND_BODY );
ASSERT( pwndBody );
// Send messages to the Control, AA-20/01/2016
// set some text length limit
// richeditcrtl = (HWND)GetDlgItem( IDC_EDIT_OUTBOUND_BODY );
pwndBody->PostMessage(EM_LIMITTEXT, (WPARAM)MAX_STRING_LEN - 1, 0);
// we need key and mouse events, see WM_NOTIFY
pwndBody->PostMessage(EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS);
// misc options
pwndBody->PostMessage(EM_SETOPTIONS, ECOOP_OR, ECO_AUTOWORDSELECTION | ECO_SELECTIONBAR);
pwndBody->PostMessage(EM_SETZOOM, 64, 52);
I declared a handler:
afx_msg void OnSpellingError( NMHDR * pNotifyStruct, LRESULT * result );
defined the handler:
void CAMCMVOutboundEmailView::OnSpellingError(NMHDR* pNMHDR, LRESULT* Result)
{
MSGFILTER* msgf = (MSGFILTER*)pResult;
spellchecker_process ((HWND)pwndBody, msgf);
}
I provided a binding in the message map:
ON_NOTIFY(NM_RCLICK, IDC_EDIT_OUTBOUND_BODY, OnSpellingError);
because I want to call the spellchecker_process() function when WM_NOTIFY message is received just like it is in the WIN API CALLBACK function above.
I am new to MFC, but I know several things things are wrong and the obvious ones are:
What should be the first parameter of the MFC ON_NOTIFY function? (it shouldn't be NM_RCLICK)
How do I get the LPARAM parameter inside the message handler (OnSpellingError) as I need to cast it to the MSGFILTER?
In summary, I am just trying to implement the WIN API function in my MFC Application. Thanks
Newbie to Winapi.Learnt the basics of winapi and tips of creating child window in firebreath plugin.
Searched tutorials,got answers.But don't know What Am i Doing wrong here? Following is my code>>My main objective is to create a textbox functioning like that in MSWord on aparent plugin(windowed firebreath plugin) window handle whereby I can edit,modify the text as well as move and resize the textbox.Also, I am not able to fire many events like WM_LBUTTONDOWN and more on the child window.
int WINAPI childwindow::LoadForm()
{
HWND pluginWnd=win->getHWND();
HINSTANCE pluginInstance = (HINSTANCE)GetWindowLong(pluginWnd, GWL_HINSTANCE);
HWND demoWnd = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"),TEXT("text to be written"),WS_CHILD|WS_VISIBLE|
ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL|WS_BORDER|WS_VSCROLL|WS_HSCROLL, 10, 10, 300, 100, pluginWnd, (HMENU)IDC_MAIN_EDIT, pluginInstance , NULL);
//SUBCLASSING=============REFERENCE>>(http://cboard.cprogramming.com/windows-programming/148771-subclassing-edit-box.html)
PreviousSubclassedEditProc=(WNDPROC)GetWindowLong(demoWnd, GWL_WNDPROC);//WNDPROC PreviousSubclassedEditProc;
SetWindowLong(demoWnd, GWL_WNDPROC,(LONG_PTR)&childwindow::CustomWinProc);
SetWindowText(demoWnd,_T("LALALA"));
//======================
ShowWindow(demoWnd, SW_SHOWNORMAL);
UpdateWindow(demoWnd);
return EXIT_SUCCESS;
}
LRESULT CALLBACK childwindow::CustomWinProc( HWND chWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
HWND textBoxInput,button,tempHandle=0;
HINSTANCE hInstance = (HINSTANCE)GetWindowLong(chWnd, GWL_HINSTANCE);
switch(uMsg)
{
case WM_CREATE:
{
return 0;
break;
}
case WM_MOUSEACTIVATE:
{
SetFocus(chWnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN:
{
dragWindow = true;
tempHandle=SetCapture(chWnd);
break;
}
case WM_LBUTTONUP:
{
ReleaseCapture();
tempHandle=NULL;
dragWindow = false;
break;
}
case WM_MOUSEMOVE:
{
if(dragWindow==true)
{
RECT mainWindowRect;
POINT pos;
int windowWidth, windowHeight;
pos.x = (int)(short) LOWORD(lParam);
pos.y = (int)(short) HIWORD(lParam);
GetWindowRect(chWnd,&mainWindowRect);
windowHeight = mainWindowRect.bottom - mainWindowRect.top;
windowWidth = mainWindowRect.right - mainWindowRect.left;
ClientToScreen(chWnd, &pos);
SetWindowPos(chWnd, NULL, pos.x, pos.y, windowWidth,windowHeight, SWP_NOZORDER);
ShowWindow(chWnd,SW_SHOWNORMAL);
}
break;
}
case WM_KILLFOCUS:
{
LPCWSTR buffer[1024];
SendMessage(chWnd,
WM_GETTEXT,
sizeof(buffer)/sizeof(buffer[0]),
reinterpret_cast<LPARAM>(buffer));
break;
}
default:
{
return DefWindowProc(chWnd,uMsg,wParam,lParam);
}
}
return TRUE;
}
The above child window is being made on a parent windowed firebreath plugin handle.
I also want to get the text from the child textbox window.but that's not possible till I get this right.I saw one google grouptutorials and this which has helped me a lot to get the basics.But the above problem is bugging me from last day.
Any takers for this question?
You can't RegisterClass for "EDIT" and hope that your class will magically behave like an "Edit". You have two solutions:
First one: drop your Class Registration Code and use "Edit" (or WC_EDIT) as Class Name in CreateWindowEx.
Second One: use GetClassInfo to query the Windows "Edit" class informations, and use the Window Procedure from the class (lpfnWndProc) in place of DefWindowProc.
If you choose the fisrt solution, you can provide your own Window Procedure with subclassing:
As for moving/resizing a Child Window, provided that you have the hWnd, use either SetWindowPos or MoveWindow.
After creating the initial dialog box that works perfectly fine, I create another dialog box when the Join Game button is pressed. The dialog box is created and show successfully, however I am unable to type in the edit box or even press or exit the dialog. Does anyone understand how to fix this or why it happens? I made sure the dialog box itself was not the problem by creating and displaying it from the main loop in the application. It worked fine when I created it that way. So why does it error when being created from another dialog?
My code is below.
This code is for the DLGPROC function that each dialog uses.
#define WIN32_LEAN_AND_MEAN
#include "Windows.h"
#include ".\Controllers\Menu\MenuSystem.h"
#include ".\Controllers\Game Controller\GameManager.h"
#include ".\Controllers\Network\Network.h"
#include "resource.h"
#include "main.h"
using namespace std;
extern GameManager g;
extern bool men;
NET_Socket server;
extern HWND d;
HWND joinDlg;
char ip[64];
void JoinMenu(){
joinDlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_GETADDRESSINFO), NULL, (DLGPROC)GameJoinDialogPrompt);
SetFocus(joinDlg);
// ShowWindow(joinDlg, SW_SHOW);
ShowWindow(d, SW_HIDE);
}
LRESULT CALLBACK GameJoinDialogPrompt(HWND Dialogwindow, UINT Message, WPARAM wParam, LPARAM lParam){
switch(Message){
case WM_COMMAND:{
switch(LOWORD(wParam)){
case IDCONNECT:{
GetDlgItemText(joinDlg, IDC_IP, ip, 63);
if(server.ConnectToServer(ip, 7890, NET_UDP) == NET_INVALID_SOCKET){
LogString("Failed to connect to server! IP: %s", ip);
MessageBox(NULL, "Failed to connect!", "Error", MB_OK);
ShowWindow(joinDlg, SW_SHOW);
break;
}
}
LogString("Connected!");
break;
case IDCANCEL:
ShowWindow(d, SW_SHOW);
ShowWindow(joinDlg, SW_HIDE);
break;
}
break;
}
case WM_CLOSE:
PostQuitMessage(0);
break;
}
return 0;
}
LRESULT CALLBACK GameMainDialogPrompt(HWND Dialogwindow, UINT Message, WPARAM wParam, LPARAM lParam){
switch(Message){
case WM_PAINT:{
PAINTSTRUCT ps;
RECT rect;
HDC hdc = GetDC(Dialogwindow);
hdc = BeginPaint(Dialogwindow, &ps);
GetClientRect (Dialogwindow, &rect);
FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 0, 0)));
EndPaint(Dialogwindow, &ps);
break;
}
case WM_COMMAND:{
switch(LOWORD(wParam)){
case IDC_HOST:
if(!NET_Initialize()){
break;
}
if(server.CreateServer(7890, NET_UDP) != 0){
MessageBox(NULL, "Failed to create server.", "Error!", MB_OK);
PostQuitMessage(0);
return -1;
}
ShowWindow(d, SW_HIDE);
break;
case IDC_JOIN:{
JoinMenu();
}
break;
case IDC_EXIT:
PostQuitMessage(0);
break;
default:
break;
}
break;
}
return 0;
}
}
I call the first dialog using the below code
void EnterMenu(){
// joinDlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_GETADDRESSINFO), g_hWnd, (DLGPROC)GameJoinDialogPrompt);//
d = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_SELECTMENU), g_hWnd, (DLGPROC)GameMainDialogPrompt);
}
The dialog boxes are not DISABLED by default, and they are visible by default. Everything is set to be active on creation and no code deactivates the items on the dialog or the dialog itself.
First, make sure you write the correct signature for the dialog procedures:
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam);
(See http://msdn.microsoft.com/en-us/library/ms645469(v=VS.85).aspx)
Your dialog procedures should therefore look like this:
INT_PTR CALLBACK GameJoinDialogPrompt(HWND Dialogwindow, UINT Message,
WPARAM wParam, LPARAM lParam)
{ /* ... */ }
INT_PTR CALLBACK GameMainDialogPrompt(HWND Dialogwindow, UINT Message,
WPARAM wParam, LPARAM lParam)
{ /* ... */ }
Then you should be able to do this without warnings or errors:
void EnterMenu()
{
d = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_SELECTMENU),
g_hWnd, &GameMainDialogPrompt);
// Note the ampersand. Also note that no cast is needed. You should
// not need to use a cast to pass in the address of the function.
}
See http://blogs.msdn.com/oldnewthing/archive/2004/01/15/58973.aspx for why it's extremely important to get the function signature right.
That being said, your joinDlg should be a modal dialog box, since it is requesting information from the user:
void JoinMenu()
{
// DialogBox() creates a modal dialog box. It "blocks" its owner until
// it closes. On the other hand, CreateDialog() creates a non-modal
// dialog box.
joinDlg = DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_GETADDRESSINFO),
d, &GameJoinDialogPrompt);
// Again, note the ampersand and the lack of a cast when passing in
// the address of the dialog procedure. Also, the main dialog box is
// serving as the owner of this dialog box.
}
Also note that dialog box procedures are different from windows procedures in that they return either TRUE or FALSE: TRUE if your dialog procedure processed the message, FALSE otherwise. (There are some "weird" messages that violate this rule, but you're not handling for those messages)
So your dialog procedures should look something like this:
INT_PTR CALLBACK GameMainDialogPrompt(HWND Dialogwindow, UINT Message,
WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_PAINT:
/* Do painting */
return TRUE; // We handled the paint message
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_HOST:
/* Do command */
return TRUE; // We handled this particular command.
case IDC_JOIN:
/* Do command */
return TRUE; // We handled this particular command.
case IDC_EXIT:
/* Do command */
return TRUE; // We handled this particular command.
}
return FALSE; // The command wasn't handled.
}
return FALSE; // The message wasn't handled.
}
Dialog procedures do not call DefWindowProc() nor return 0!
In addiiton to the other excellent post you are also doing silly things like:
if(server.CreateServer(7890, NET_UDP) != 0){
MessageBox(NULL, "Failed to create server.", "Error!", MB_OK);
PostQuitMessage(0);
in a WM_COMMAND handler. This is a horrible piece of code as it stalls the dialogs modal loop without disabling it, or popping up the message box.
If you call a modal window from a different window (or dialogs) message proc you MUST disable the stalled window. Practically speaking, pass the windows HWND to the MessageBox call.
If all else fails, start fresh with this:
In resource.h:
#define IDD_DIALOG1 101
#define IDD_DIALOG2 102
#define ID_OPEN 1001
#define ID_MESSAGE 1002
In a resource file:
#include <winres.h>
#include "resource.h"
IDD_DIALOG1 DIALOGEX 0, 0, 300, 200
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER |
WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Main Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Open Secondary Dialog", ID_OPEN, 73 ,49, 133, 64
END
IDD_DIALOG2 DIALOGEX 0, 0, 200, 150
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER |
WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Secondary Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Message Box", ID_MESSAGE, 50, 49, 88, 50
END
In a source file:
#include <windows.h>
#include "resource.h"
INT_PTR CALLBACK SecondaryDialogProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_MESSAGE:
// Show a message box. Note that we're passing in our own HWND into
// the function, so we "block" this dialog box until the user
// dismisses this message box.
::MessageBox(hwnd, "Hello World!", "Greetings", MB_OK);
return TRUE;
}
return FALSE;
case WM_CLOSE:
// Because this is a modal dialog box (we used ::DialogBox()), we
// use ::EndDialog() instead of ::DestroyWindow() to destroy this
// dialog box.
::EndDialog(hwnd, 0);
return TRUE;
}
return FALSE;
}
INT_PTR CALLBACK MainDialogProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_OPEN:
// Open a modal dialog box. This will block the main dialog box
// until the secondary dialog box is closed.
::DialogBox(::GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG2), hwnd, &SecondaryDialogProc);
return TRUE;
}
return FALSE;
case WM_CLOSE:
// We close this dialog box with ::DestroyWindow(). This causes the
// WM_DESTROY message to be sent.
::DestroyWindow(hwnd);
return TRUE;
case WM_DESTROY:
// Since the main dialog box is being destroyed, we quit
// the application.
::PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
{
// Open a non-modal dialog box using ::CreateDialog().
HWND mainDlg = ::CreateDialog(::GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1), NULL, &MainDialogProc);
// The first ::ShowWindow() call should use nShowCmd.
::ShowWindow(mainDlg, nShowCmd);
MSG msg;
while (::GetMessage(&msg, NULL, 0, 0) > 0)
{
// So our main dialog behaves properly.
if(!::IsDialogMessage(mainDlg, &msg))
{
::TranslateMessage( & msg );
::DispatchMessage( & msg );
}
}
return msg.wParam;
}
Here, this is just the bare bones code to open up a main dialog, with a button to open another dialog. See if this works first, then add your business logic.