DS_CONTROL | WS_CHILD 组合导致死循环

DS_CONTROL | WS_CHILD combination causes infinite loop

你好。

我正在尝试使用 WinAPI 和 C 创建一个 "dialog in dialog" 示例。这个示例包含一个子对话框autocheckbox,以及一个主对话框,其中包含一个静态黑色矩形,它是子对话框的父级和一个在消息框中显示具有复选框状态的文本的按钮。

当我设置标志时 DS_CONTROL | WS_CHILD 对于子对话框,每当我尝试更改复选框状态时,应用程序都会进入无限循环,我必须强制关闭它。当我删除 DS_CONTROL 标志时,可以按预期工作,但我无法使用 Tab 键在控件之间循环。

我该怎么做才能使其按预期使用DS_CONTROL flag?

这是我的 main.c 文件的内容:

#include <windows.h>

#pragma comment (lib, "user32")


HINSTANCE hInst;
BOOL isChecked;
const unsigned char checkedStr[] = "Checkbox is checked";
const unsigned char notCheckedStr[] = "Checkbox is not checked";


BOOL CALLBACK ChildDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    case WM_COMMAND:
      switch (LOWORD(wParam))
      {
        case 21:
          isChecked = IsDlgButtonChecked(hwndDlg, 21);
          return TRUE;
      }

      return FALSE;
  }

  return FALSE;
}


BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    case WM_INITDIALOG:
    {
      HWND hContainer, hChilddDlg;

      hContainer = GetDlgItem(hwndDlg, 11);
      hChilddDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(20), hContainer, ChildDlgProc, 0);
      ShowWindow(hChilddDlg, SW_SHOW);

      return TRUE;
    }

    case WM_COMMAND:
      switch (LOWORD(wParam))
      {
        case 12:
        {
          const unsigned char *ptr;

          if (isChecked)
          {
            ptr = checkedStr;
          }
          else
          {
            ptr = notCheckedStr;
          }

          MessageBox(hwndDlg, ptr, TEXT("Checkbox status"), MB_OK | MB_ICONINFORMATION);

          return TRUE;
        }
      }

      return FALSE;

    case WM_CLOSE:
      EndDialog(hwndDlg, 0);
      return TRUE;
  }


  return FALSE;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
  hInst = hInstance;
  isChecked = FALSE;

  return DialogBoxParam(hInstance, MAKEINTRESOURCE(10), NULL, DialogProc, 0);
}

这是我的 rsrc.rc 文件的内容:

#include <windows.h>

10 DIALOGEX 0, 0, 130, 47
STYLE DS_CENTER | DS_SETFONT | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU
CAPTION "Checkbox status"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 9, "Segoe UI"
{
  CONTROL "", 11, STATIC, SS_BLACKRECT | WS_CHILD | WS_VISIBLE, 5, 5, 120, 20
  CONTROL "&Status", 12, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 75, 30, 50, 12
}


20 DIALOGEX 0, 0, 120, 20
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 9, "Segoe UI"
{
  CONTROL "Checkbox", 21, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 5, 50, 10
}

我在 Visual C++ 命令提示符下编译它:cl /c main.c && rc rsrc.rc && link /SUBSYSTEM:WINDOWS /OUT:test.exe main.obj rsrc.res

在此先感谢您的帮助。

在NSIS源码中找到解决方法。问题是我将子对话框作为 blackrect 的子项,所以它不在主对话框的事件循环中,导致挂起。为了解决这个问题,我不得不将它作为主对话框的子项并将其移动到 blackrect 上。

这是主对话框中 WM_INITDIALOG 案例的更新代码过程:

// ...
case WM_INITDIALOG:
{
  HWND hChilddDlg;

  hChilddDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(20), hwndDlg, ChildDlgProc, 0);

  if (hChilddDlg)
  {
    RECT rect;

    GetWindowRect(GetDlgItem(hwndDlg, 11), &rect);
    ScreenToClient(hwndDlg, (LPPOINT)&rect);
    SetWindowPos(hChilddDlg, 0, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
    ShowWindow(hChilddDlg, SW_SHOWNA);
  }

  return TRUE;
}
// ...

有了这个,我就可以在控件之间使用 DS_CONTROL 标志和制表符循环。