さてさて、前回 で「『Hello World!巡り』-コンソール編 」をC++、C#共に終了しましたので、今回からデスクトップテンプレートを使って旅を続けていきたいと思います。
先ずはC++から。
選択したのは「Windows デスクトップウィザード 」というやつです。「次へ」ボタンを押して進むと、
はて?
成程。「Windows Desktopで使えるプログラムだから、ウィンドウアプリのみならず、コマンドプロンプト(いわゆる「DOS窓」) で使えるコンソールアプリや.DLL、.LIBも含む」という意味ね。(注 )
注 :だったら「コンソールアプリを作る入口がダブっているということ?それとも違いがあるの? 」という突っ込みができそうです。最初にVisual Studioを使った5年前と全く違うのは私にはCopilotというAIのお供(友?)がいる こと。彼に聞くと、次の通りとのこと。(矢張りここにもユーザーの知らない黒子達がいっぱいいました。)
とはいえ、私は「デスクトップアプリケーション(.exe) (Win32 API) 」を選択して進みます。
【全体イメージ】
小さくて見難いですが、今度はソースファイル(HelloWorld3.cpp )にびっしりコードが書き込まれているのがわかるでしょう。また一見でそれがWindows SDK (注 )ベースのコードであることがわかります。
注 :Windows SDK (Windows S oftware D evelopment K it )とはC/C++言語でWin32 APIを使ってプログラミングをするためのヘッダーファイル等が一式セットになったものです。MFC(Microsoft Foundation Class) 等を使っていないのでWindowsがウィンドウプログラムをどのように動かしているのか等、基礎的な構造や仕組みを学べます。至極端折って言えば、ウィンドウプログラムはマウスクリックやキー入力、ウィンドウのサイズ変更等という「 入力イベント 」の 割り込み に対し、対応する「 関数(メソッド) 」を都度処理するプログラムであり、プログラムの起動も"WinMain"というエントリーポイント関数になります。
コンソールプログラムと大きく異なるのは関連ファイルの多さです。↑のイメージの左上にある「ソリューションエクスプローラー 」の拡大版を載せます。
御覧のようにヘッダーファイルが4本、リソースファイルとしてリソーススクリプト(.rc)とアイコンファイルが二つあります。(「外部依存関係」はWin32 APIがズラズラと並び膨大です。)
(1)framework.h
HelloWorld3のプログラム(ウィンドウズプログラミング) 用のSKDとC++ファイブラリーのヘッダーファイルがまとめられていました。
// header.h : 標準のシステム インクルード ファイルのインクルード ファイル、
// またはプロジェクト専用のインクルード ファイル
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Windows ヘッダーからほとんど使用されていない部分を除外する
// Windows ヘッダー ファイル
#include <windows.h>
// C ランタイム ヘッダー ファイル
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
(2)HelloWorld3.h
単に以下(3)のファイルを読み込む為だけのものでした。(複雑なもプログラムではもっと他のヘッダー等が追加されるのかもしれません。)
#pragma once
#include "resource.h"
(3)Resource.h
これはWin32リソースにID番号を設定するものです。(BCCSkeltonでは"Res(プロジェクト名).h"となる奴)
尚、プリプロセッサー(if~endif )についてはインデントがされていないので、色で対応を示しておきます。
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ で生成されたインクルード ファイルです。
// 次で使用: HelloWorld3.rc
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_HELLOWORLD3_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_HELLOWORLD3 107
#define IDI_SMALL 108
#define IDC_HELLOWORLD3 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
// 新しいオブジェクトの次の既定値
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 130
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
(4)targetver.h
良く分かりませんが、Windows SDKの「(利用できるもののうち、最も新しい)最適化されたバージョンを選択する」為のヘッダーファイルのようです。
#pragma once
// SDKDDKVer.h をインクルードすると、利用できる最も高いレベルの Windows プラットフォームが定義されます。
// 以前の Windows プラットフォーム用にアプリケーションをビルドする場合は、WinSDKVer.h をインクルードし、
// サポートしたいプラットフォームに _WIN32_WINNT マクロを設定してから SDKDDKVer.h をインクルードします。
#include <SDKDDKVer.h>
(5)HelloWorld3.ico、small.ico
二つとも同じデザインの自動生成されたアイコンファイルで次の(6)HelloWOrld.rc から参照されます。
(6)HelloWorld3.rc
自動生成されたリソーススクリプトファイルです。
//Microsoft Visual C++ で生成されたリソース スクリプトです。
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE リソースから生成されました。
//
#ifndef APSTUDIO_INVOKED
#include "targetver.h"
#endif
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
LANGUAGE 17, 1
/////////////////////////////////////////////////////////////////////////////
//
// アイコン
//
// アプリケーション アイコンをすべてのシステム上で維持するために、最も小さい
// ID 値のアイコンが最初に配置されます。
IDI_HELLOWORLD3 ICON "HelloWorld3.ico"
IDI_SMALL ICON "small.ico"
/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//
IDC_HELLOWORLD3 MENU
BEGIN
POPUP "ファイル(&F)"
BEGIN
MENUITEM "終了する(&X)", IDM_EXIT
END
POPUP "ヘルプ(&H)"
BEGIN
MENUITEM "バージョン情報(&A)...", IDM_ABOUT
END
END
/////////////////////////////////////////////////////////////////////////////
//
// アクセラレータ
//
IDC_HELLOWORLD3 ACCELERATORS
BEGIN
"?", IDM_ABOUT, ASCII, ALT
"/", IDM_ABOUT, ASCII, ALT
END
/////////////////////////////////////////////////////////////////////////////
//
// ダイアログ
//
IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "バージョン情報 HelloWorld3"
FONT 9, "MS UI Gothic"
BEGIN
ICON IDI_HELLOWORLD3,IDC_STATIC,14,14,21,20
LTEXT "HelloWorld3, バージョン 1.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX
LTEXT "Copyright (c) 2025",IDC_STATIC,42,26,114,8
DEFPUSHBUTTON "OK",IDOK,113,41,50,14,WS_GROUP
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_ABOUTBOX, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 163
TOPMARGIN, 7
BOTTOMMARGIN, 55
END
END
#endif // APSTUDIO_INVOKED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#ifndef APSTUDIO_INVOKED\r\n"
"#include ""targetver.h""\r\n"
"#endif\r\n"
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n"
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// 文字列テーブル
//
STRINGTABLE
BEGIN
IDC_HELLOWORLD3 "HELLOWORLD3"
IDS_APP_TITLE "HelloWorld3"
END
#endif
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE リソースから生成されました。
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
(7)HelloWorld3.cpp
そして最後に自動生成された(ユーザー用の) プログラム「本体」です。ウィンドウにグラフィックで文字出力を行う ために、WM_PAINTイベント割り込みでクライアントエリアの中央に文字列を書いて います。
// HelloWorld3.cpp : アプリケーションのエントリ ポイントを定義します。
//
#include "framework.h"
#include "HelloWorld3.h"
#define MAX_LOADSTRING 100
// グローバル変数:
HINSTANCE hInst; // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING]; // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING]; // メイン ウィンドウ クラス名
// このコード モジュールに含まれる関数の宣言を転送します:(解説:「転送」が意味不明ですが、所謂「プロトタイプ宣言」です。)
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
//解説:ウィンドウプログラムのエントリ^ポイント関数です。'w'がついているのはユニコード対応であることを示しています。
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: ここにコードを挿入してください。
// グローバル文字列を初期化する
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_HELLOWORLD3, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); //解説:これはこのウィンドウをOSに登録しています。
// アプリケーション初期化の実行:( 解説:ウィンドウの生成と表示を行っています。)
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
//解説:アクセラレーションテーブルを読み込んでいます。
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HELLOWORLD3));
MSG msg;
// メイン メッセージ ループ:(解説:このループが続く限り、ウィンドウが開いています。メッセージWM_CLOSEが呼ばれるとウィンドウを閉じ、続いてWM_DESTROYでウィンドウが廃棄されます。)
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 関数: MyRegisterClass()
//
// 目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOWORLD3));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_HELLOWORLD3);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 関数: InitInstance(HINSTANCE, int)
//
// 目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します
//
// コメント:
//
// この関数で、グローバル変数でインスタンス ハンドルを保存し、
// メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // グローバル変数にインスタンス ハンドルを格納する
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 関数: WndProc(HWND, UINT, WPARAM, LPARAM)
// 解説:所謂ウィンドウコールバック関数で↑にあったメッセージループと対になっています。
// 目的: メイン ウィンドウのメッセージを処理します。
//
// WM_COMMAND - アプリケーション メニューの処理
// WM_PAINT - メイン ウィンドウを描画する
// WM_DESTROY - 中止メッセージを表示して戻る
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 選択されたメニューの解析:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: HDC を使用する描画コードをここに追加してください...
//解説:文字列の 描画部分 だけ は私が書きました。Win32APIでよく使われる文字列描画関数は
//BOOL TextOut(HDC hdc, int x座標, int y座標, LPC(W)STR 文字列ポインター, int 文字列長);
//ですが、
//int DrawText(HDC hdc, LPC(W)TSTR 文字列ポインター, int 文字列長, LPRECT 文字列描画RECT構造体へのポインター, UINT 書式設定);
//も使いやすいと思われます。
TCHAR str[] = TEXT("Hello World!");
RECT rec;
GetClientRect(hWnd, &rec);
DrawText(hdc, str, -1, &rec, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// バージョン情報ボックスのメッセージ ハンドラーです。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
いかがだったでしょうか?Windows SDKを使ってプログラミングすると、C/C++文法とWin32 APIだけを使ったプログラミングになるため、何をしているのかが分かりやすい反面、細かいところまでコーディングしなければならないので、コード量が増えることがお判りいただけたかと存じます。 その意味で大部分が自動生成されるVisual Studioはありがたいのですが、
「どこに何を書くの?」
ということがわからない段階では結構ハードルが高いのではないか、と愚考します。
いずれにせよ、これをビルドして実行すると、こういう成果物が現れます。(自動生成リソースはアイコン、メニューとバージョンダイアログです。)
次はC#でやってみましょう。