mieki256's diary



2024/01/17(水) [n年前の日記]

#1 [prog] C/C++で設定ダイアログを表示してみたいその3

C/C++ と OpenGL を使ってWindows用のスクリーンセーバを作りたい。そのためには設定ダイアログを表示して、設定値の読み書きしないといけない。環境は Windows10 x64 22H2 + MnGW (gcc 6.3.0)。

_昨日の実験 では、リソースファイル(resource.rc)内のダイアログ設定部分に trackbar (トラックバー/スライダー/TRACKBAR_CLASS/"msctls_trackbar32") を記述しても、MinGW の windres 2.28 では syntax error になってしまってリソースをコンパイルすることができなかったので、諦めて EDITTEXT に置き換えて誤魔化していたけれど。

  CONTROL "Wait",   IDC_WAITVALUE, "msctls_trackbar32", TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP, 96,24,180,15
  CONTROL "Speed",  IDC_SPEED,     "msctls_trackbar32", TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP, 96,48,180,15
  CONTROL "Number", IDC_NUMBER,    "msctls_trackbar32", TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP, 96,72,180,15

↓

  EDITTEXT IDC_WAITVALUE, 96, 24, 100, 16, ES_AUTOHSCROLL
  EDITTEXT IDC_SPEED,     96, 48, 100, 16, ES_AUTOHSCROLL
  EDITTEXT IDC_NUMBER,    96, 72, 100, 16, ES_AUTOHSCROLL

その後色々試していたら、trackbar/sliderを記述してもエラーにならない書き方が分かった。

原因と対策 :

エラーの原因は、trackbar を記述する際、スタイルとして指定していた TBS_TOOLTIPS が未定義になっていたせいだった。

TBS_TOOLTIPS は commctrl.h の中で定義されているので、本来なら resource.rc の最初のほうで、「#include <commctrl.h>」 を書いておけばそれだけで TBS_TOOLTIPS も使えるようになるはずだけど。commctrl.h を眺めてみたところ、以下のような記述になっていた。
#if (_WIN32_IE >= 0x0300)
#define TBS_TOOLTIPS 0x0100
#define TBTS_TOP 0
#define TBTS_LEFT 1
#define TBTS_BOTTOM 2
#define TBTS_RIGHT 3
#endif

「_WIN32_IE」が 0x0300 以上の値になってないと、TBS_TOOLTIPS は未定義になってしまう…。

そんなわけで、resource.rc の最初のほうで、「_WIN32_IE」を勝手に定義して以下のように書いておけば、一応コンパイルできると分かった。「_WIN32_IE」って何なのか分かってないけど…。いや、名前からしてIE関係の何かなのだろうけど…。
/* trackbar enable */
/* trackbarを使うなら以下も記述しないと TBS_TOOLTIPS が未定義になり Syntax error が出る。 */
#define _WIN32_IE   0x0300
#include <commctrl.h>

#include <windows.h>

#include "resource.h"

// ...

ちなみに、何故かは分からんけど、windows.h を include する前に、commctrl.h を include しておかないといけない模様。順番が逆だと反映されなかった。

ソース :

そんなわけで、MinGW を使ってビルドする場合でも、trackbar を使った設定ダイアログの表示ができるようになった。サンプルソースは以下。

_resource.h
#ifndef RESOURCE_H_
#define RESOURCE_H_

#define IDC_MYICON                      2
#define IDD_DIALOGTEST01_DIALOG         102
#define IDR_MAINFRAME                   128
#define IDD_DIALOG1                     129
#define IDC_FPSDISPLAY                  2003
#define IDC_RESET                       2004
#define IDC_WAITVALUE                   2005
#define IDC_SPEED                       2006
#define IDC_NUMBER                      2007
#define IDC_STATUS1                     2008
#define IDC_STATIC                      -1

#endif

_resource.rc
/* resource */

/* trackbar enable */
/* trackbarを使うなら以下も記述しないと TBS_TOOLTIPS が未定義になり Syntax error が出る。 */
#define _WIN32_IE   0x0300
#include <commctrl.h>

#include <windows.h>

#include "resource.h"

#if 0
#define TBS_AUTOTICKS   1
#define TBS_TOOLTIPS 0x0100
#endif

/* Dialog */
IDD_DIALOG1 DIALOG DISCARDABLE  0, 0, 290, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Setting"
 FONT 11, "Segoe UI"
BEGIN
  DEFPUSHBUTTON "OK",      IDOK,      172, 96, 50, 14
  PUSHBUTTON    "Cancel",  IDCANCEL,  228, 96, 50, 14
  CTEXT "ssstars GL setting", IDC_STATIC, 7, 7, 275, 8
  LTEXT "Wait (5 - 200 msec)", IDC_STATIC, 12, 24, 72, 8
  LTEXT "Speed (100 - 4000)", IDC_STATIC, 12, 48, 72, 8
  LTEXT "Number (10 - 4000)", IDC_STATIC, 12, 72, 72, 8
  AUTOCHECKBOX  "FPS display", IDC_FPSDISPLAY, 12, 96, 53, 10
  PUSHBUTTON    "Reset",   IDC_RESET, 76,  96, 50, 14
  CONTROL "", IDC_WAITVALUE, "msctls_trackbar32", TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP, 96, 24, 180, 15
  CONTROL "", IDC_SPEED,     "msctls_trackbar32", TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP, 96, 48, 180, 15
  CONTROL "", IDC_NUMBER,    "msctls_trackbar32", TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP, 96, 72, 180, 15
END

_02_dialogtest_tb.c
#include <tchar.h>
#include <wchar.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

// trackbar を使う時は commctrl.h の include が必要
// comctl32 もリンクする。(-lcomcil32)
#include <commctrl.h>

// リソースファイルを使う時は include する
#include "resource.h"

// メインウインドウ上に設置するボタンのID
#define IDC_BUTTON_OPENDLG 110

// global work グローバル変数
HINSTANCE hInst;

// static WCHAR txt[1024]; // message work
static char txt[1024]; // message work

// global paramater work
static int waitValue = 15;
static int speedValue = 1000;
static int numberValue = 1000;
static int fps_display = 1;

// ========================================
// prototype プロトタイプ宣言
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

// ========================================
// entry point エントリーポイント。Win32 GUIアプリは最初にここが呼ばれる
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
  TCHAR szWindowClass[] = TEXT("DIALOGTEST01"); // main window class name メインウインドウクラス名
  TCHAR szTitle[] = TEXT("DialogTest01");       // title bar text タイトルバーに表示するテキスト
  WNDCLASSEX wc;
  MSG msg;

  // set window class properties (struct)
  // ウインドウクラス構造体の設定
  {
    wc.cbSize = sizeof(wc);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc; // ウインドウプロシージャを指定
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szWindowClass;
    wc.hIcon = NULL; // 今回はアイコンを指定しない
    wc.hIconSm = NULL;
    // wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
    // wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    if (RegisterClassEx(&wc) == 0)
      return FALSE;
  }

  // create window ウインドウを作成
  {
    hInst = hInstance;

    HWND hWnd = CreateWindow(
        szWindowClass,
        szTitle, // タイトルバーのテキストを指定
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, // x, y
        512, 288,                     // width, height
        NULL, NULL, hInstance, NULL);

    if (hWnd == NULL)
      return FALSE;

    // display window ウインドウを表示
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
  }

  // main message loop メッセージループ
  {
    BOOL bRet;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
      if (bRet == -1)
      {
        break;
      }
      else
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
  }

  return (int)msg.wParam;
}

// ========================================
// main window procedure
// メインウインドウプロシージャ。メインウインドウ上で何かが起きるとここが呼ばれる
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  // static WCHAR txt[1024];

  switch (message)
  {
  case WM_CREATE:
    // create main window. initialize
    // メインウインドウ作成時にここに来る。初期化処理をする

    // create "Open Dialog" button ボタンを1つ設置する
    CreateWindow(
        "BUTTON", "Open Dialog", // クラス名、ボタン上に表示するテキスト
        WS_CHILD | WS_VISIBLE,   // スタイル
        20, 20, 100, 24,         // x, y, width, height
        hWnd,
        (HMENU)IDC_BUTTON_OPENDLG, // ボタンに紐づけるID
        hInst,
        NULL);
    break;

  case WM_COMMAND:
  {
    // application menu process

    int wmId = LOWORD(wParam);
    switch (wmId)
    {
    case IDC_BUTTON_OPENDLG:
    {
      // push "Open Dialog" button
      // "Open Dialog" ボタンが押されるとここに来る

      // Open dialog リソースファイル内で記述されてるダイアログを開く
      int ret;
      ret = DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc);

      // モーダルダイアログを開いているので、OK か Cancel が押されるまで処理は返ってこない

      // dialog results ダイアログが返してきた値で処理を分ける
      switch (ret)
      {
      case IDOK:
        // push "OK" button OKボタンが押された
        strcpy(txt, "IDOK");
        // wcscpy_s(txt, TEXT("IDOK"));
        break;

      case IDCANCEL:
        // push "Cancel" button Cancelボタンが押された
        strcpy(txt, "IDCANCEL");
        // wcscpy_s(txt, TEXT("IDCANCEL"));
        break;
      }
      InvalidateRect(hWnd, NULL, TRUE);
    }
    break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
    }
  }
  break;

  case WM_PAINT:
  {
    // draw main window メインウインドウ描画処理

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);

    // draw message "IDOK" or "IDCANCEL" のメッセージを表示
    // TextOut(hdc, 20, 70, txt, lstrlen(txt));
    TextOut(hdc, 20, 70, txt, strlen(txt));

    // draw paramater value ダイアログ上で設定した値を表示
    {
      char s[256];

      sprintf(s, "wait=%d", waitValue);
      TextOut(hdc, 20, 90, s, strlen(s));

      sprintf(s, "speed=%d", speedValue);
      TextOut(hdc, 20, 110, s, strlen(s));

      sprintf(s, "number=%d", numberValue);
      TextOut(hdc, 20, 130, s, strlen(s));

      sprintf(s, "fps_display=%d", fps_display);
      TextOut(hdc, 20, 150, s, strlen(s));
    }

    EndPaint(hWnd, &ps);
  }
  break;

  case WM_DESTROY:
    // destroy window ウインドウ破棄時の処理
    PostQuitMessage(0);
    break;

  default:
    return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

// set value to config dialog
// 設定ダイアログ上の各値を設定する
void SetvalueToDialog(HWND hDlg, int waitValue, int speedValue, int numberValue, int fps_display)
{
  // set trackbar ("msctls_trackbar32")
  // trackbarの範囲や値を設定
  {
    HWND cHwnd;

    cHwnd = GetDlgItem(hDlg, IDC_WAITVALUE);                    // get hundle
    SendMessage(cHwnd, TBM_SETRANGE, TRUE, MAKELPARAM(5, 200)); // set range
    SendMessage(cHwnd, TBM_SETTICFREQ, 5, 0);                   // set tick
    SendMessage(cHwnd, TBM_SETPOS, TRUE, (LPARAM)waitValue);    // set position

    cHwnd = GetDlgItem(hDlg, IDC_SPEED);
    SendMessage(cHwnd, TBM_SETRANGE, TRUE, MAKELPARAM(100, 4000));
    SendMessage(cHwnd, TBM_SETTICFREQ, 100, 0);
    SendMessage(cHwnd, TBM_SETPOS, TRUE, (LPARAM)speedValue);

    cHwnd = GetDlgItem(hDlg, IDC_NUMBER);
    SendMessage(cHwnd, TBM_SETRANGE, TRUE, MAKELPARAM(10, 4000));
    SendMessage(cHwnd, TBM_SETTICFREQ, 100, 0);
    SendMessage(cHwnd, TBM_SETPOS, TRUE, (LPARAM)numberValue);
  }

  // set checkbox
  // CHECKBOXの状態を初期化する
  // GetDlgItem() でコントロールのハンドルを取得できる
  // SendMessage() でコントロールの状態を変更できる
  // BM_SETCHECK で状態の変更を指示
  {
    HWND cHwnd;
    cHwnd = GetDlgItem(hDlg, IDC_FPSDISPLAY);
    SendMessage(cHwnd, BM_SETCHECK, ((fps_display == 0) ? BST_UNCHECKED : BST_CHECKED), 0);
  }
}

// get value from confgi dialog
// 設定ダイアログ上の各値を取得する
void GetValueFromDialog(HWND hDlg)
{
  // get trackbar value
  // trackbar の値を取得してグローバル変数に記録
  // GetDlgItemText() でEDITTEXTの内容(文字列)を取得
  waitValue = SendMessage(GetDlgItem(hDlg, IDC_WAITVALUE), TBM_GETPOS, 0, 0);
  speedValue = SendMessage(GetDlgItem(hDlg, IDC_SPEED), TBM_GETPOS, 0, 0);
  numberValue = SendMessage(GetDlgItem(hDlg, IDC_NUMBER), TBM_GETPOS, 0, 0);

  // get checkbox status
  // CHECKBOXの状態をグローバル変数に記録
  // IsDlgButtonChecked() で CHECKBOX の状態を取得できる
  fps_display = (IsDlgButtonChecked(hDlg, IDC_FPSDISPLAY) == BST_CHECKED) ? 1 : 0;
}

// ========================================
// dialog procedure
// ダイアログプロシージャ。ダイアログ上でボタン等が押された際に呼ばれる
INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
  case WM_INITDIALOG:
  {
    // initialize dialog
    // ダイアログ初期化時の処理。GUI部品の状態を設定

    InitCommonControls(); // trackbarを使う時はどこかで一度呼んでおくらしい

    // 設定ダイアログ上の値を設定
    SetvalueToDialog(hDlg, waitValue, speedValue, numberValue, fps_display);
  }
    return TRUE;

  case WM_COMMAND:
    switch (LOWORD(wParam))
    {
    case IDOK:
      // push "OK" button "OK"ボタンが押されたらここに来る
      GetValueFromDialog(hDlg); // 設定ダイアログ上の値を取得してグローバル変数に記録
      EndDialog(hDlg, IDOK);    // ダイアログを終了
      return TRUE;

    case IDCANCEL:
      // push "Cancel" button
      EndDialog(hDlg, IDCANCEL); // ダイアログを終了
      return TRUE;

    case IDC_RESET:
    {
      // push "Reset" button "Reset"ボタンが押された

      // change status text
      // ダイアログ上のLABELのテキストを変更
      SetWindowText(GetDlgItem(hDlg, IDC_STATUS1), TEXT("Reset"));

      // ダイアログ上の値をデフォルト値に変更
      SetvalueToDialog(hDlg, 15, 1000, 1000, 1);
    }
      return TRUE;
    }
    break;
  }
  return FALSE;
}

_Makefile
PROGRAM=02_dialogtest_tb.exe

SRC=$(PROGRAM:.exe=.c)
OBJS=$(SRC:.c=.o) resource.o

.PHONY: all
all: $(PROGRAM)

$(PROGRAM): $(OBJS) Makefile
    gcc $(OBJS) -lcomctl32 -mwindows -o $@

%.o: %.c
    gcc -O3 -o $@ -c $<

resource.o: resource.rc Makefile
    windres $< -o $@

.PHONY: cleanall clean

cleanall: clean
    rm -f $(PROGRAM)

.PHONY: clean
clean:
    rm -f $(OBJS)

make と打てば 02_dialogtest_tb.exe をビルドできる。実行結果は以下。


以上です。

過去ログ表示

Prev - 2024/01 - Next
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project