views:

47

answers:

3

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.

+2  A: 

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 silico
After correcting the code, it still acts the same way. The second dialog is still unresponsive. I modified it's properties to include being a "System Modal" but it still remains unresponsive after being called second. Anything else I should check that may be wrong with it? OffTopic: The book I learned about dialogs from used the LRESULT and the (DLGPROC) cast. I guess bad programming conventions really are used everywhere.
Justin Sterling
What book was this? You really should not need to fight with the compiler to pass in a dialog procedure to CreateDialog() or DialogBox() without warnings or errors. So many people get this wrong that the operating system will actually try to fix it for you, but it's best to get it right the first time around. MSDN makes it clear what function signature these procedures have to be. Also, having an ampersand to take the address of a function is standard C++. Not having the ampersand is not.
In silico
A: 

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.

Chris Becke
A: 

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.

In silico