2022年9月に初めて64bit機を購入しましたが、その後立ち上がりで異常に時間をとるようになり()、立ち上がらなくなるまでになったので、昨年のブラックフライデーで新PCに買い替えをしたことは既に書きました。今回はその総括として、現状と(現時点の)今後の展望について備忘的に書いておきましょう。

:内臓Wifiレシーバーのハード不具合から、初期化、起動、正常動作確認までの時間がかかったのではないかと思われます。現在は内臓レシーバーを無効にして、アリババで買った158円の5G Wifi/Blue tooth 2 way レシーバーを使用していますが、起動時間が正常化されました。

 

1.現状

(1)新PC

↑の写真の子亀(上にあるの)がIntel Core-i9 13900HK、32GBメモリー、1TB SSDの新PC(ちっちゃっ!)で、現在極めてスムースに(また心なしか前よりも速く)動いております。(昨年暮れに中国製のミニPCを買ったので、セキュリティに結構気を使いましたが、杞憂に終わりホッとしました。製品自体は極めて優秀で、満足しています。)

新PCは基本的に旧PCのファイル、設定をすべて承継しており、家庭における仕事(文書作成、財務会計処理や税務等の計算業務etc)、エンターテインメント(インターネットや旧メディアの音楽、ビデオ鑑賞)、ご存じの通りの自家製開発環境()でのプログラミング(現在はVisual Studioの探検でお休み中)に使用しています。

:Embarcadero C++ではBCCForm and BCCSkelton(Unicode用ECCSkelton)+BCCMaker/BatchGood、.NET FrameworkのC#コンパイラー(csc.exe)ではMSCompAss(これはBCCSkeltonのアプリ)を使っています。

 

(2)旧PC

↑の写真の親亀(下にあるの)がAMD Ryzen 7 5800H、16GBメモリー、500GB SSDの旧PC(当時は第4世代の32bit Core-i7のタワーPCだったので、このコンパクトさに吃驚!)で、クリーンインストール後、↑で書いたようにWifiレシーバーを交換し、これもスムースに動いております。(では、何のために買い替えたのか?と突っ込まないでくださいね。)

旧PCはクリーンインストール後、最新のVisual Studio Community(C#とC++)をダウンロードして導入し、現在Copilot君にご指導いただきながら格闘している最中です。

 

(3)共用環境

新PCはディスプレーにDisplay Port接続し、旧PCはHDMIポートからディスプレーのDVI(Degital Visual Interface-)ポートに接続しており、ディスプレー側から入力切替が可能です。また、キーボードはマウス一体型で、1USBポートで無線接続できるため、USBスイッチャー(↑の写真でCDCカメラの左、新PCの上に乗っているもの)を購入して新PC、旧PCを切り替えて使用しています。

:DVIは画像データだけなので、音声は出力できません。音を聞く必要がある場合、もう使わなくなった昔のiPhoneのイヤホンを使っています。

 

2.今後の展望

(1)普段使い(家庭仕事)

現状のまま新PCにお願いしようかと思っています。データバックアップ体制も1T HDで従来通りで行っております。

 

(2)エンタメ

現状のまま新PCにお願いしようかと思っています。iTuneとiPhoneの連携、Amazon Primeの映画やドラマも旧機同様(以上?)に問題なく楽しめております。

 

(3)開発(趣味のプログラミング)

これは...プログラミング自体が今年72歳になる私にはだんだんつらくなってきましたので、考え直そうかと思っています。これからは(というか、これまでも、なんですが)自分の好きな環境とスタイルで(技術的な旧さにとらわれず)好き勝手なプログラミングをしてゆきたいと思っています。→これの意味するところは【Visual Studio探検記】の総括で改めて書こうと思っています。(ただ、旧機をその後どうするか、も考えなくては、ですね。)

 

いずれにしても

 

2台続けて7万円弱で買った中国製 mini PCの評価として

 

Those machines' high performance, speed and very low prices,  as well as their space saving feature, well warrant "Absolute Value for money", to say the least.

 

前回は単なるプレーンウィンドウを作っただけだったので、今回はWin32リソースとしてメニューとダイアログをつけてみましょう。なお、前回リソースについて書いたことを再掲します。

【再掲】

5.リソースコンパイラー(解説:このプログラムではリソースファイルがありませんが、序に解説しておきます。)
→Borlandでは許容されていたEXSTYLEがMicrosoftのrc.exeでは通らないのでエラーになった。また、BorlandのリソースコンパイラーではメインのC++プログラムのインクルードファイルが暗黙に適用されていたが、Microsoftのrc.exeでは別途独立して#include <windows.h>や#include <commctrl.h>を明示的に指定する必要がある。また、指定された場合、本体プログラムで#pragma comment(lib, "comctl32.lib")とするなど、リンクパスを設定する手当てを講じないとリンカーエラーに発展する。

 

今回のプログラム(ECCSkelton02.cpp)も複雑なことは一切をさせず、単にメニュー付きのSDIウィンドウとバージョンダイアログを表示させるだけです。また今回はリソースファイル(ECCSample02.rc)、ID定義ファイル(ResECCSample02.h)も紹介します。

 

【ECCSkelton02.rc】

//-----------------------------------------
//             BCCForm Ver 2.41
//    An Easy Resource Editor for BCC
//  Copyright (c) February 2002 by ysama
//-----------------------------------------

#include <windows.h>    //解説:前にも書きましたが、Microsoftのrc.exeにはこれが必要です。
#include <commctrl.h>    //解説:前にも書きましたが、Microsoftのrc.exeにはこれが必要です。

#include    "ResCSDITest.h"

//----------------------------------
// ダイアログ (IDD_VERSION)
//----------------------------------

IDD_VERSION DIALOG DISCARDABLE 0, 0, 160, 40
//EXSTYLE WS_EX_DLGMODALFRAME    //Microsoftのリソースコンパイラーではこの書き方はできない
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_MODALFRAME | DS_3DLOOK | DS_CENTER
CAPTION L"バージョン情報"
FONT 9, L"Times New Roman"
{
 CONTROL IDI_ICON, 0, L"STATIC", WS_CHILD | WS_VISIBLE | SS_SUNKEN | SS_ICON, 12, 8, 24, 24
 CONTROL L"ECCSkelton\nVersion 1.0", 0, L"STATIC", SS_CENTER | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 42, 8, 80, 24
 CONTROL L"OK", IDOK, L"BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 14, 20, 12
}

//-------------------------
// メニュー(IDM_MENU)
//-------------------------

IDM_MENU MENU DISCARDABLE
{
    POPUP L"ファイル(&F)"
    {
        MENUITEM L"新規(&N)", IDM_NEW    //解説:使いません。
        MENUITEM L"データを開く(&O)", IDM_OPEN    //解説:使いません。
        MENUITEM L"データの保存(&S)", IDM_SAVE    //解説:使いません。
        MENUITEM L"名前をつけて保存(&A)", IDM_SAVEAS    //解説:使いません。
        MENUITEM SEPARATOR
        MENUITEM L"終了(&X)", IDM_EXIT
    }
    POPUP L"編集(&E)"
    {
        MENUITEM L"切り取り(&T)", IDM_CUT    //解説:使いません。
        MENUITEM L"コピー(&C)", IDM_COPY    //解説:使いません。
        MENUITEM L"貼り付け(&P)", IDM_PASTE    //解説:使いません。
    }
    POPUP L"表示(&V)"
    {
        MENUITEM L"標準(&S)", IDM_NORM    //解説:使いません。
    }
    POPUP L"ヘルプ(&H)"
    {
        MENUITEM L"バージョン情報(&V)", IDM_VERSION
    }
}

 

//--------------------------
// イメージ(IDI_ICON)
//--------------------------

IDI_ICON    ICON    DISCARDABLE    "C:\\Users\\...<略>...\\Cpp.ico"
 

【ResECCSkelton02.h】

//-----------------------------------------
//             BCCForm Ver 2.41
//   Header File for Resource Script File
//   Copyright (c) February 2002 by ysama
//-----------------------------------------
//---------------------
//  ダイアログリソース
//---------------------
// ダイアログ IDD_VERSION


//---------------------
//  メニューリソース
//---------------------
// メニュー IDM_MENU

#define    IDM_NEW                100    //解説:使いません。
#define    IDM_OPEN            101    //解説:使いません。
#define    IDM_SAVE            102    //解説:使いません。
#define    IDM_SAVEAS            103    //解説:使いません。
#define    IDM_EXIT            104
#define    IDM_CUT                105    //解説:使いません。
#define    IDM_COPY            106    //解説:使いません。
#define    IDM_PASTE            107    //解説:使いません。
#define    IDM_NORM            108    //解説:使いません。
#define    IDM_VERSION            109

//---------------------
//  イメージリソース
//---------------------

#define    IDI_ICON            200

//---------------------
//  ストリングテーブル
//---------------------

//--------------------
//  アクセラレーター
//--------------------

//------------------
//  ヴァージョン情報
//------------------

【ECCSkelton02.cpp】

//---------------------
// プロジェクト.h 部分
//---------------------
/////////////////////////
// インクルードファイル
/////////////////////////

#include    "ECCSkelton.h"
#include    "ResCSDITest.h"

#pragma comment(lib, "comctl32.lib")    //解説:前にも書きましたが、Microsoftのrc.exeにはこれが必要です。

/////////////////////////
// 旧BCCSkeltonにおける
// ".h"ファイル部分
/////////////////////////
//CMyWndクラスをCSDIから派生させ、OnCommandを定義する

class CMyWnd : public CSDI
{
public:
    CMyWnd(WCHAR* UName) : CSDI(UName) {};
    CMDTABLE    //CMyWndクラスのOnCommand()関数宣言
    bool OnCreate(WPARAM, LPARAM);
    bool OnClose(WPARAM, LPARAM);
    bool OnExit();
    bool OnVersion();
};

//CMyWndインスタンスをメインウィンドウとして作成する
CMyWnd MainWnd((LPWSTR)L"ECCSkeltonTest");

//CMyWndクラスのOnCommand()関数定義
BEGIN_CMDTABLE(CMyWnd)
    ON(IDM_EXIT, OnExit())
    ON(IDM_VERSION, OnVersion())
END_CMDTABLE

//VERSIONDLGクラスをCDLGから派生させ、OnCommandを定義する
class VERSIONDLG : public CDLG
{
public:
    CMDTABLE    //VERSIONDLGのOnCommand()関数宣言
    bool OnIdok();
};

//VERSIONDLGインスタンスをversiondlgダイアログとして作成する
VERSIONDLG versiondlg;

//VERSIONDLGクラスのOnCommand()関数定義
BEGIN_CMDTABLE(VERSIONDLG)
    ON(IDOK, OnIdok())
END_CMDTABLE

///////////////
//外部変数宣言
///////////////
//CMNDLGクラスインスタンス

CMNDLG g_CmnDlg;    //解説:使いません。

//CSBARクラスインスタンス
CSBAR g_SBar;    //解説:使いません。

//EDITコントロール用CCTRLクラスインスタンス
CCTRL g_Edit;    //解説:使いません。

//-------------------------
// プロジェクトProc.h 部分
//-------------------------
//CMyWndのOnCreate関数を定義する

bool CMyWnd::OnCreate(WPARAM wParam, LPARAM lParam) {

    MessageBoxW(m_hWnd, L"OnCreate関数にいます", L"WM_CREATE", MB_YESNO | MB_ICONINFORMATION);
    return TRUE;
}

//CMyWndのOnClose関数を定義する
bool CMyWnd::OnClose(WPARAM wParam, LPARAM lParam) {

    if(MessageBoxW(m_hWnd, L"終了してもよいですか", L"終了確認",
        MB_YESNO | MB_ICONQUESTION) == IDYES)
        return TRUE;
    else
        return FALSE;
}

//CMyWndのOnExit関数を定義する
bool CMyWnd::OnExit() {

    SendMsg(WM_CLOSE, 0, 0);
    return TRUE;
}

//CMyWndのOnVersion関数を定義する
bool CMyWnd::OnVersion() {

    versiondlg.DoModal(m_hWnd, L"IDD_VERSION", GetInstance());    //解説:モーダルダイアログを表示します。
    return TRUE;
}

///////////////////////////////
//ユーザーダイアログの関数定義
///////////////////////////////
//VERSIONDLGのOnIdok関数を定義する

bool VERSIONDLG::OnIdok() {

    EndModal(TRUE);    //解説:「はい」ボタンを押された時の終了処理です。
    return TRUE;
}

//-----------------------
// プロジェクト.cpp 部分
//-----------------------
////////////////
// WinMain関数
////////////////

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPWSTR lpCmdLine, int nCmdShow) {

    //CSDIインスタンスの初期化
    if(!MainWnd.Init((LPWSTR)L"ECCSkeltonTest", hInstance, (LPWSTR)L"IDM_MENU", (LPWSTR)MAKEINTRESOURCEW(IDI_ICON)))    //解説:2025/12/16キャスト追加(
        return 1;
    //SDIウィンドウの作成
    if(!MainWnd.Create((LPWSTR)L"CSDITest"))    //解説:2025/12/16キャスト追加(
        return 1;
    //メッセージループに入る
    return MainWnd.Loop();
}

前回も書きましたが、「要すれば文字列ポインター変数(WCHAR*、LPWSTR)に文字列定数(const WCHAR*、LPCWSTR)を代入すると即エラーになります。」なので、キャストによる型変換を追加しています。

 

前回でECCSkeltonライブラリーは修正したので、今度はこれらプロジェクトのファイルだけ手直しし、ビルドできました。

 

 

さて、今回はC++で過去のプログラム遺産を利用できるか否か確認し、何とか問題(という敵をやっつけ)克服ができましたので、

 

 

ということでよろしいかと。では次回は再度C#に戻り、(Windows 11付属の).NET Framework 4.8MSCompAssで)作成した過去のC#プログラムも再利用可能か否かテストしてみたいと思います。

 

乞ご期待!

 

冒険の旅は何とか初戦を勝利し、次の冒険に進みます。

 

最初にVisual Studioのテンプレートの概要を【Hello World!巡り】で確認し、次にコンソールプログラムの実行速度を見ましたので、今度は我がC++用ウィンドウライブラリー(今回はUnicode版のECCSkelton)がVisual Studioで動くか否か確かめます。

 

ECCSkeltonというのは、このパッケージ"BCCForm_and_BCCSkelton"→"ECCSkelton"フォールダー以下に入っていますので、これをVisual Studioが使用する"source"フォールダーの中に入れておきましょう。

 

そしてVisual Studioを起動し、C++の「空のプロジェクト」をECCSkelton01という名前にします。そしてそのソースファイルに"ECCSkelton01.cpp"(および↓参照)というソースを追加します。

:これもどのようにしてプロジェクトに加えるのか最初は分からず、試行錯誤しましたが、最終的にCopilot君に訊いて、「プロジェクトECCSkelton01を選択、右クリックして出現するポップアップメニューから『追加』→『既存の項目』でファイル選択ダイアログから選ぶ」のだそうです。まぁ、慣れるまでは結構何をどうしたらよいのか良く分からないですよね。理屈じゃないんだから。

 

【ECCSkelton01.cpp】

/* BCCSkeltonで使っているnon-Unicordのchar*ベースのエントリーポイント
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPSTR lpCmdLine, int nCmdShow) {

は、TCHAR環境(MSで主流の二刀流環境)ではこうなります
    #define        UNICODE        //ユニコードにするにはこれが必要です
    int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPTSTR lpCmdLine, int nCmdShow) {

しかし、VC++では通るこれは、BCC102のリンク時に"WinMainが見当たらない"
エラーを出します。
その為、BCC32cのヘルプに"-t"オプションを付けて調べたら、-tUというユニ
コードオプションがあり、対応するエントリーポイントは以下の通りでした。
*/


#pragma comment(lib, "Comctl32.lib")    //解説:2025/12/15リンカーエラーのために追加

/////////////////////////
// インクルードファイル
/////////////////////////

#include    "ECCSkelton.h"

////////////////
// WinMain関数
////////////////

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPWSTR lpCmdLine, int nCmdShow) {

    CSDI MainWnd(((WCHAR*) L"ECCSkelton_Test");                //解説:2025/12/15キャスト追加
    //CSDIインスタンスの初期化
    if (!MainWnd.Init(((WCHAR*) L"PlainWindow", hInstance))    //解説:2025/12/15キャスト追加
        return 1;
    //SDIウィンドウの作成
    if (!MainWnd.Create((WCHAR*) L"PlainWindow"))            //解説:2025/12/15キャスト追加
        return 1;
    //メッセージループに入る
    return MainWnd.Loop();
}

 

そして「ソリューションをビルド」すると、出るわ出るわ200超のエラーが発生しました。エラーは↑のプログラムだけではなく、ECCSkeltonの多数のヘッダーファイル(*.h)のコードに及んでいます。その概要は以下の通りです。

 

1.Embarcadero C++では問題にならなかったが、Visual StudioでLC_CTYPE、LC_ALL、setlocaleが未定義になった。
→//ロケールマクロを使用する(2025/12/14追加)#include    <locale.h>を追加した。

2.GWL_HINSTANCEが未定義になった。(解説:Win32 API関数の64bit化対応です。)
→64bit化に伴い、GetWindowsLong関数とそれにかかわる定数が使われなくなった。GetWindowsLongPtr()関数とGWLP_HINSTANCE //2025/12/14変更に置き換えた。(64bit化)
 
3.型チェックがより厳格になった(解説:エラーの90%以上がこれでした。要すれば文字列ポインター変数(WCHAR*、LPWSTR)文字列定数(const WCHAR*、LPCWSTR)を代入すると即エラーになります。正しいんだけどね。)
→Embarcadero C++ではエラーにならなかった「(戻り値型がWCHAR*の場合の)return L'0';」がエラーとなったので、キャスト(WCHAR*)を追加した。またLPWSTRとLPCWSTRの型の違いが多数引っ掛かり多くのキャストが必要になった。(LPWSTR変数から代入する可能性のあるLPWSTRは、定数を代入する際にすべて引っかかるし、その逆もしかりです。)

4.全角文字やエンコーディング
→Borlandでは無視されていた「全角空白文字」や、(知らない間にUTF-8に変えられて)がエラーになる。前者は検索で調べて置換し、後者はエンコーディングを変更するために、「ファイル」-「名前を付けて保存」-「上書き保存」-「エンコーディング付きで保存」でShift-JISを選択して保存する。

5.リソースコンパイラー(解説:このプログラムではリソースファイルがありませんが、序に解説しておきます。)
→Borlandでは許容されていたEXSTYLEがMicrosoftのrc.exeでは通らないのでエラーになった。また、BorlandのリソースコンパイラーではメインのC++プログラムのインクルードファイルが暗黙に適用されていたが、Microsoftのrc.exeでは別途独立して#include <windows.h>や#include <commctrl.h>を明示的に指定する必要がある。また、指定された場合、本体プログラムで#pragma comment(lib, "comctl32.lib")とするなど、リンクパスを設定する手当てを講じないとリンカーエラーに発展する。

 

まぁ、内容は分かりますし、エディターの置換機能で一括置換もできるので、一気に修正しました。(↑の「解説:」参照。)

 

その結果、

 

 

きちんと動くようになりました。次回はリソースを使う簡単なプログラムをやってみましょう。

 

 

皆さま

 

あけましておめでとうございます。

 

本年もよろしくお願いします。(私もとうとう本年で6回目の年男となります。)

 

さて、

 

新年第一発目は、昨年暮れからこの方矢鱈きな臭い今日この頃(注1)、「人間的な、余りに人間的な」問題である標題「チキンゲーム(Chicken game)」(注2)を(心理学的興味もあり)取り上げてみようかと思います。

注1:既に長引いている戦火が二つ、大国による威嚇と攻撃(またはその警告)が二つ。前者も大国による弱国に対する攻撃であり、他国の国政的枠組みによる抑止効果がないことが後者にも共通していると考えられます。

注2:私が「チキンゲーム(Chicken game)」という言葉を知ることとなった1955年のこの映画。ジェィムス・ディーンの白の丸首インナー赤のスイングトップブルージーンが子供心に大いに魅力的でしたね。原題を"Rebel Without a Cause"というのですが、私の場合は"Revel Without a Cause"でしょうか?(注の注

注の注:Revel-大いに楽しむ、酒盛りする、飲み浮かれる、Rebel-反対する,反抗する,反逆する

 

日本においても昨年暮れの総理発言に起因する様々な論議がありますが、それは根底にこの問題のある世界、即ち合理性とは異なる、感情(負ける恐怖-競争心による対抗と臆病と言われなくない自尊心-自分を臆病者という相手への敵意)が支配する世界があるように思われます。

 

この問題は典型的な「ゼロサムゲーム」(ゲームの「場」自体は不変で、当事者一方がゲインすれば、他方はルーズする)であり、このような局面はビジネスでも多いため、これとか、あれとか、ビジネスコンサルタントの方がマトリックス分析(ペイオフマトリックス)を使っています。例えばの方はチキンゲームを「二競争者」 x「『突っ込む=強気』『避ける=弱気』の二択」から、このように表現しています。

 

画像

 

このマトリクスを見るとチキンゲームが必ずしも「ゼロサム」ゲームではないことがわかります。共に「避ける=弱気」となった場合は「現状を維持できる(0 ゼロ)」という結果でゲインもルーズもありませんが、共に「突っ込む=強気」はマイナス(損失)のみの結果となるからです。(しかし、「共謀」という手段無しに二競争者が「避ける=弱気」行動を選択することは稀であり、「対話(コミュニケーション)」と第三者仲介の必然性が理解されます。)これが国家間の争いとなると更に問題が深刻で、一旦「突っ込む=強気」となれば、「戦費(コスト)」と「被害(ロス)」が生じ、時間経過に連れて増大し、それに見合う「補償(ゲイン)」がなければ「避ける=弱気」にならないからです。

 

だからどーだっていうの?

 

と怒られそうですが、私の言いたいのは、

 

あなたも(知ってか知らずかはあるでしょうが)当事者(競争者)になっているのよ

 

ということだけです。チキンゲームを当事者として突き進むのか、(秘密裏に)リスク分析を行い、他の結末シナリオと手段(alternative finalization scenario and means)を求めるのか、私は後者を望んでやみません。

 

さて、前回旅に出て初めての戦いを始める」、というところまで来ました。

 

では、

 

戦いの様子を見てみましょう。

 

1.Intel Core-i9の新PCで動くEmbarcadero C++ コンパイラー(32bit)

 

2.Intel Core-i9の新PCで動くWindows付属の.NET Framework 4.8のC#コンパイラー

 

3.AMD Ryze 7の旧PC上のVisual Studio 2026の最新バージョンのC++(Debug)

 

4.AMD Ryze 7の旧PC上のVisual Studio 2026の最新バージョンのC++(Release)

 

5.AMD Ryze 7の旧PC上のVisual Studio 2026の最新バージョンのC#(Debug)

 

6.AMD Ryze 7の旧PC上のVisual Studio 2026の最新バージョンのC#(Release)

 

いかがでしょうか?

 

「そんなこと言われてもわかんねーよ、ですか?

 

それでは一覧にしたものをお見せしましょう。

 

ウ~ン、評価に困りますねぇ...

(元々このテストは与えられたソート配列如何でタイムが結構変わるので...)

 

(1)先ず、32bitのC++とC#の比較は前にも書きましたが、C#の最適化の所為か、思ったよりも速く、実際ネイティブC++コンパイラーよりも速い処理が目立ちます。(Embarcadero C++、.NET Framework 4.8CSC共にDebugコードを入れていないのでRelease相当と考えてください。)しかしすでにといっても、Windows 11では64bit環境なので、エミュレーター処理となり、すでに

64bitネイティブコード」の点を理解しないといけませんね。(WOW64エミュレーター下で32bitプログラムを実行しても、体感的には変わらないとか、必ずしも遅くなるだけではないとのことですが、まぁ、「動くだけでめっけもの」なんでしょう。)

 

(2)Visual Studioの64bitコンパイラーでこれらに相当するのがVisual StudioのC++とC#の"(Release)"ですが、ハード的にはIntel Core-i9の新PCの方が、AMD Ryzen 7の旧PCより1~2割がた勝っているのですが、それでもソート処理、総時間ともに大幅なタイムアップを果たしています。ご本家のOSと最適化された開発環境だからでしょう。

 

(3)また64bitのVisual Studio版のC++とC#でも"(Release)"でC#がソート処理で遜色がないし、総時間では逆転している点にも注目されます。

 

(4)Debug版では相当にDebug用コードが挿入されている模様で、Release版と比べると大幅に処理速度が増加していることが分かりますね。それもC#はより著しいです。

 

と、いうことで、

 

少なくとも開発された時点の年月の経過や技術的な進歩(特に64bit化)等は着実に良い仕事をしている(ポルシェじゃないですが「最新の開発環境が最良の開発環境」)、といえるのではないでしょうか?

 

ず、初(少なくとも、敗退はしていません)

 

といえるでしょう。

 

ん?

 

もう12月30日か。では、本稿にて今年(2025年)の仕事納めとさせていただきます。

 

皆さま、よいお年をお迎えください。

 

ps. 実は「最新の開発環境が最良の開発環境」に関して、実にdisappointingな事実を経験しました。それはまた来年。

 

前回までで、「旅立ちの支度」として一応C++C#のコンソールプログラムとウィンドウプログラム(C#はWinFormsとWPF)"Hello World!"を表示するだけのプロジェクトを作り、比較しながらVisual Studioがどのように動くのかの概観を見てまいりました。

 

今回は、旅へ出発するとすぐに経験する「戦い」の一つとして、

 

(1)前にやったこれ【ソート】まずは比較コード(C++)の紹介)とこれ【無駄話】"Comparison_Sort"をC#に移植)を使い、

:尚、このプログラムには誤りがありました。C#TimeSpanMilliSecondsプロパティを使っていますが、これは通算の時間ではなく、MilliSecond単位の時間のみを返すようで、値が正しくなかったようです。MilliSecondTotalMilliSecond修正したものを以下(↓)に再掲載します。

 

(2)Intel Core-i9の新PCで動くEmbarcadero C++ コンパイラー(32bit)とWindows付属の.NET Framework 4.8のC#コンパイラーを使って上記(1)のプログラムを走らせた結果を、

 

(3)AMD Ryze 7の旧PC上のVisual Studio 2026の最新バージョンのC++とC#(共に64bit)で走らせた結果と比較してみよう、

 

という子供じみた競争をやってみようと思います。

 

さて、どんな結果になるのやら?

 

【Comparison_Sort.cpp】

///////////////////////
// C++ Sort Comparison
///////////////////////

#include    <stdio.h>        //C言語の最小限のシステムヘッダー
#include    <conio.h>        //getch()を使う為
#include    <iostream>        //C++言語の入出力システムヘッダー
#include    <chrono>        //C++の時間計測用ライブラリー

//定数定義
#define        MAX 10000        //取り敢えず10,000がお勧め

using namespace std;

////////////////////////
// 関数プロトタイプ宣言
////////////////////////
// Comparison

void comparison(int);
// Bubble Sort
void bubblesort(int*, int);
// Heap Sort
void heapsort(int*, int);
void exchange(int, int*, int);

// Quick Sort
void quicksort(int*, int*);
// Shell Sort
void shellsort(int*, int);
// Print result
void print_result(int);
// Comparison function
int compare(const void*, const void*);

////////////////////
// 共通テスト用配列
////////////////////

static int original[MAX];
static double results[4][MAX];


/////////////
// main関数
/////////////

int main(int argc, char** argv) {

    chrono::system_clock::time_point begining = chrono::system_clock::now();
    for(int i = 0; i < MAX; i++) {
        comparison(i);
    }
    for(int i = 0; i < 4; i++) {
        print_result(i);
    }
    chrono::system_clock::time_point ending = chrono::system_clock::now();
     double t = static_cast<double>(chrono::duration_cast<chrono::seconds>(ending - begining).count());
    printf("Total time consumed for the above processing is: %lf[s]\r\n", t);
    cout << endl << "Push CR key ..." << endl;
    getchar();
    return 0;
}


///////////////
// Comparison
///////////////

void comparison(int n) {

    //テスト用の配列の作成
    srand((unsigned)time(NULL));    //現在時刻を元に種を生成
    static int test_array[MAX];        //テスト用ローカル変数
    for(int i = 0; i < MAX; i++) {
        test_array[i] =             
//originalを保存し、テストはこれで行う
        original[i] = rand();        //0から32,767の整数で初期化する
    }
  
 //時間計測の準備
    chrono::system_clock::time_point start, end;
    
//ソート比較
    start = chrono::system_clock::now();
    bubblesort(test_array, MAX);
    end = chrono::system_clock::now();
    results[0][n] =  static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);

    for(int i = 0; i < MAX; i++)
        test_array[i] = original[i];
    start = chrono::system_clock::now();
    heapsort(test_array, MAX);
    end = chrono::system_clock::now();
    results[1][n] =  static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);

    for(int i = 0; i < MAX; i++)
        test_array[i] = original[i];
    start = chrono::system_clock::now();
    quicksort(test_array, &test_array[MAX - 1]);
    end = chrono::system_clock::now();
    results[2][n] =  static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);

    for(int i = 0; i < MAX; i++)
        test_array[i] = original[i];
    start = chrono::system_clock::now();
    shellsort(test_array, MAX);
    end = chrono::system_clock::now();
    results[3][n] =  static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
}


////////////////
// Bubble Sort
////////////////
/*
    n個の整数配列dataを総て順に比較、交換し、配列の最後から大きい順に並べる
    通称「馬鹿ソート」
*/

void bubblesort(int* data, int n) {

    int i, j, temp;

    for(i = n - 1; i > 0; i--) {        
//配列の末尾から先頭へ
        for(j = 0; j < i; j++) {        //配列の先頭から末尾へ
            if(data[j] > data[j + 1]) {    //次の配列要素の方が小さければ交換する
                temp = data[j];
                data[j] = data[j + 1];
                data[j + 1] = temp;
            }
        }                              
 //その結果iに最大の要素が置かれる
    }
}


//////////////
// Heap Sort
//////////////
/*
ヒープソートは次のようなヒープ(二分木)の「親-子」関係を用いる
        Parent(0)
          /\
  ChildL(1)  ChildR(2)

この二分木を用いて配列を表現すると以下のようになる(数字は添字)
           0
         / \
        1     2
      / \ / \
     3    4 5    6
   / \
  7     8 ...
*/


void exchange(int top, int* data, int n) {

    int temp = data[top];                        
//開始時の二分木の親の値をtempに退避
    //子のある二分木の左の子供を指定し、(交換があればやり直す為)末尾迄子孫を辿る
    for(int i = top * 2 + 1; i < n; top = i, i = top * 2 + 1) {
        if(i < n - 1 && data[i] < data[i + 1])    
//右の子供が左の子供より大きければ
            i++;                                //右の子供を指定する
        if(temp >= data[i])                        //開始時の親と大きい方の子供を比較
            break;                                //親が大きければループを抜ける(topは変化しない)
        data[top] = data[i];                    //二分木の親に大きい子の値を代入する
    }                                            //大きい子(data[i])はループのi = topで
    data[top] = temp;                            //data[top]となり、退避した親の値を代入
}

void heapsort(int* data, int n) {
    int top, i;

    for(top = n / 2 - 1; top >= 0; top--)        
//最初に「子がある最後の二分木」から
        exchange(top, data, n);                    //「親が最大値を持つ」ように頂点まで継続する
    for(i = n - 1; i > 0; i--) {                //次に配列の最後と頂点(最大値)の値を交換し
        int temp;
        temp = data[i];
        data[i] = data[0];
        data[0] = temp;
        exchange(0, data, i);
    }                                          
 //配列の最後を切り離し、その前の要素から頂点まで継続する
}

//////////////
// Quick Sort
//////////////
//検索範囲(startとend)で、基準値(key)を定め、それよりも多いものと少ないものを分割し、更にkeyの前後で同じ処理を再帰呼び出しする。

void quicksort(int* start, int* end) {

    if(start < end) {                            
//開始位置が終了位置以上で終了(最後の再帰呼び出し参照)
        int *left, *right, key, *kp;            //順に右進行位置、左進行位置、「分水嶺」値、その位置
        //右端乃至左端に到達し、breakで抜けるまでループする
        for(left = start, right = end, key = *right;;) {
            while(key >= *left)
                left++;                          
 //keyを超える値迄右に進む
            if(left < right)                    //rightの位置迄は
                *right = *left;                    //leftの値をrightの位置に書き込む
            else {                                //leftがright位置を過ぎたなら
                kp = right;                        //keyの位置をrightの位置としてループを抜ける
                break;
            }
            while(key <= *right)
                right--;                      
 //key未満の値迄右に進む
            if(left < right)                    //leftの位置迄は
                *left = *right;                    //rightの値をleftの位置に書き込む
            else {                                //rightがleft位置を過ぎたなら
                kp = left;                        //keyの位置をleftの位置としてループを抜ける
                break;
            }
        }
        *kp = key;                              
 //keyポインターの位置にkeyの値を代入する
        quicksort(start, kp - 1);                //開始位置からkeyポインター迄で再帰呼び出しする
        quicksort(kp + 1, end);                    //keyポインターから終了位置迄で再帰呼び出しする
    }
}


//////////////
// Shell Sort
//////////////
//シェルソートは配列要素を一定の距離を置いたグループに分けて比較、交換を行いソートする方法

void shellsort(int *v, int n) {

    int gap, i, j, temp;    
//gapは比較、交換する要素間の距離

    for(gap = n / 2; gap > 0; gap /= 2) {    //初期的な距離は配列サイズの1/2とする
        for(i = gap; i < n; i++) {            //距離は徐々に狭まってゆく
            for(j = i - gap; j >= 0 && v[j] > v[j + gap]; j -= gap) {
              
 //v[j]とv[j + gap]を交換する
                temp = v[j];
                v[j] = v[j + gap];
                v[j + gap] = temp;
            }
        }
    }
}


/////////////////
// Print results
/////////////////

void print_result(int n) {

    //double results[n][MAX]
    char* title[4] = {">>> Bubble Sort <<<", ">>> Heap Sort <<<", ">>> Quick Sort <<<", ">>> Shell Sort <<<"};
    double max = 0.0, min = (double)MAX, med = 0, ave = 0;
    for(int i = 0; i < MAX; i++) {
        if(results[n][i] > max)    
//n番目のソートの結果の最大値を求める
            max = results[n][i];
        if(results[n][i] < min)  
 //n番目のソートの結果の最小値を求める
            min = results[n][i];
        ave += results[n][i];    
//n番目のソートの結果を合計する
    }
    qsort(results[n], MAX, sizeof(double), compare);
    med = results[n][MAX % 2 ? MAX / 2 : MAX / 2 - 1];
    cout << title[n] << endl;
    cout <<"最大値  :" << max << endl;
    cout <<"最小値  :" << min << endl;
    cout <<"平均値  :" << ave / MAX << endl;
    cout <<"中央値  :" << med << endl;
    cout <<"総所要時間:" << ave << endl << endl;
}


///////////////////////////////////////
//Comparison function
//戻り値    負-elem1がelem2より小さい
//            0 -elem1がelem2と等しい
//            正-elem1がelem2より大きい
///////////////////////////////////////

int compare(const void *elem1, const void *elem2) {

    return (int)(*(double*)elem1 - *(double*)elem2);
}

 

【Comparison_Sort.cs】

///////////////////////
// C# Sort Comparison
///////////////////////

using System;
using System.Diagnostics;        
//Stopwatchを使う為

namespace Comparison_Sort
{
    class comparison_sort
    {

        ///////////
        //定数定義
        ///////////

        const int MAX = 10000;        //取り敢えず10,000がお勧め
        ////////////////////
        // 共通テスト用配列
        ////////////////////

        int[] original = new int[MAX];
        double[,] results = new double[4, MAX];


        ///////////////
        //Mainメソッド
        ///////////////

        public static void Main()
        {
            comparison_sort cs = new comparison_sort();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < MAX; i++) {
                cs.comparison(i);
            }
            for(int i = 0; i < 4; i++) {
                cs.print_result(i);
            }
            sw.Stop();
            TimeSpan ts = sw.Elapsed;  
//時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.TotalMillisecondsはその位のみ(通算はTotalが必要)
            string elapsed = String.Format("Total time(s) consumed for the above processing is: {0:0.000000}", ts.TotalSeconds);
            Console.WriteLine(elapsed);
            Console.WriteLine("Push CR key ..." + Environment.NewLine);
            Console.Read();        
//コンソールを閉じない為
        }

        ///////////////
        // Comparison
        ///////////////

        void comparison(int n)
        {
            
//乱数の初期化
            Random rand = new Random((int) DateTime.Now.Ticks & 0x0000FFFF);
            
//テスト用の配列の作成
            //解説:C#では引数に配列を渡す場合、参照渡しになる-https://water2litter.net/rye/post/c_array_ref/
            int[] test_array = new int[MAX];    //テスト用ローカル変数
            for(int i = 0; i < MAX; i++)
            {
                test_array[i] =               
 //originalを保存し、テストはこれで行う
                original[i] = rand.Next();        //0から32,767の整数で初期化する
            }
            
//時間計測の準備
            Stopwatch sw = new Stopwatch();
          
 //ソート比較
            sw.Start();
            bubblesort(test_array);
            sw.Stop();
            TimeSpan ts = stw.Elapsed;
 //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.TotalMillisecondsはその位のみ(通算はTotalが必要)
            results[0, n] = (double)ts.TotalMilliseconds;

            for (int i = 0; i < MAX; i++)
                test_array[i] = original[i];
            sw.Reset();
            sw.Start();
            heapsort(test_array);
            sw.Stop();
            TimeSpan ts = stw.Elapsed;  
//時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.TotalMillisecondsはその位のみ(通算はTotalが必要)
            results[1, n] = (double)ts.TotalMilliseconds;

            for (int i = 0; i < MAX; i++)
                test_array[i] = original[i];
            sw.Reset();
            sw.Start();
            quicksort(test_array, 0, MAX - 1);    
//配列、開始添字、終了添字
            sw.Stop();
            TimeSpan ts = stw.Elapsed;  
//時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.TotalMillisecondsはその位のみ(通算はTotalが必要)
            results[2, n] = (double)ts.TotalMilliseconds;

            for (int i = 0; i < MAX; i++)
                test_array[i] = original[i];
            sw.Reset();
            sw.Start();
            shellsort(test_array);
            sw.Stop();
            TimeSpan ts = stw.Elapsed;
 //時間ts.Hours, 分ts.Minutes, 秒ts.Seconds, ミリ秒ts.TotalMillisecondsはその位のみ(通算はTotalが必要)
            results[3, n] = (double)ts.TotalMilliseconds;
        }


        ////////////////
        // Bubble Sort
        ////////////////
        /*
            n個の整数配列dataを総て順に比較、交換し、配列の最後から大きい順に並べる
            通称「馬鹿ソート」
        */

        void bubblesort(int[] data)
        {
            int i, j, temp, n = data.Length;

            for(i = n - 1; i > 0; i--) {        
//配列の末尾から先頭へ
                for(j = 0; j < i; j++) {        //配列の先頭から末尾へ
                    if(data[j] > data[j + 1]) {    //次の配列要素の方が小さければ交換する
                        temp = data[j];
                        data[j] = data[j + 1];
                        data[j + 1] = temp;
                    }
                }                                
//その結果iに最大の要素が置かれる
            }
        }


        //////////////
        // Heap Sort
        //////////////
        /*
        ヒープソートは次のようなヒープ(二分木)の「親-子」関係を用いる
                Parent(0)
                  /\
          ChildL(1)  ChildR(2)

        この二分木を用いて配列を表現すると以下のようになる(数字は添字)
                   0
                 / \
                1     2
              / \ / \
             3    4 5    6
           / \
          7     8 ...
        */


        void exchange(int top, int[] data, int n)
        {
            int temp = data[top];                      
 //開始時の二分木の親の値をtempに退避
            //子のある二分木の左の子供を指定し、(交換があればやり直す為)末尾迄子孫を辿る
            for(int i = top * 2 + 1; i < n; top = i, i = top * 2 + 1) {
                if(i < n - 1 && data[i] < data[i + 1])  
 //右の子供が左の子供より大きければ
                    i++;                                //右の子供を指定する
                if(temp >= data[i])                        //開始時の親と大きい方の子供を比較
                    break;                                //親が大きければループを抜ける(topは変化しない)
                data[top] = data[i];                    //二分木の親に大きい子の値を代入する
            }                                            //大きい子(data[i])はループのi = topで
            data[top] = temp;                            //data[top]となり、退避した親の値を代入
        }

        void heapsort(int[] data)
        {
            int top, i, n = data.Length;
            for(top = n / 2 - 1; top >= 0; top--)      
 //最初に「子がある最後の二分木」から
                exchange(top, data, n);                    //「親が最大値を持つ」ように頂点まで継続する
            for(i = n - 1; i > 0; i--) {                //次に配列の最後と頂点(最大値)の値を交換し
                int temp;
                temp = data[i];
                data[i] = data[0];
                data[0] = temp;
                exchange(0, data, i);
            }                                            
//配列の最後を切り離し、その前の要素から頂点まで継続する
        }

        //////////////
        // Quick Sort
        //////////////
        //検索範囲(startとend)で、基準値(key)を定め、それよりも多いものと少ないものを分割し、更にkeyの前後で同じ処理を再帰呼び出しする。

        void quicksort(int[] data, int start, int end)    //配列、開始添字、終了添字
        {
            if(start < end)                                
//開始位置が終了位置以上で終了(最後の再帰呼び出し参照)
            {
                int left, right, key, kp;              
 //順に右進行添字、左進行添字、「分水嶺」値、その添字
                //右端乃至左端に到達し、breakで抜けるまでループする
                for(left = start, right = end, key = data[right];;) {
                    while(key >= data[left])
                    {
                        left++;                          
 //keyを超える値迄右に進む
                        if(left == right)                //配列添字が範囲を超えないよう追加
                            break;
                    }
                    if(left < right)                  
 //rightの位置迄は
                        data[right] = data[left];        //leftの値をrightの位置に書き込む
                    else                                //leftがright位置を過ぎたなら
                    {
                        kp = right;                        
//keyの位置をrightの位置としてループを抜ける
                        break;
                    }
                    while(key <= data[right])
                    {
                        right--;                        
//key未満の値迄右に進む
                        if(right == 0)                    //配列添字が範囲を超えないよう追加
                            break;
                    }
                    if(left < right)                  
 //leftの位置迄は
                        data[left] = data[right];        //rightの値をleftの位置に書き込む
                    else                                //rightがleft位置を過ぎたなら
                    {
                        kp = left;                      
 //keyの位置をleftの位置としてループを抜ける
                        break;
                    }
                }
                data[kp] = key;                            
//keyポインターの位置にkeyの値を代入する
                quicksort(data, start, kp - 1);            //開始位置からkeyポインター迄で再帰呼び出しする
                quicksort(data, kp + 1, end);            //keyポインターから終了位置迄で再帰呼び出しする
            }
        }


        //////////////
        // Shell Sort
        //////////////
        //シェルソートは配列要素を一定の距離を置いたグループに分けて比較、交換を行いソートする方法

        void shellsort(int[] data)
        {
            int gap, i, j, temp, n = data.Length;        
//gapは比較、交換する要素間の距離
            for(gap = n / 2; gap > 0; gap /= 2) {    //初期的な距離は配列サイズの1/2とする
                for(i = gap; i < n; i++) {            //距離は徐々に狭まってゆく
                    for(j = i - gap; j >= 0 && data[j] > data[j + gap]; j -= gap) {
                      
 //v[j]とv[j + gap]を交換する
                        temp = data[j];
                        data[j] = data[j + gap];
                        data[j + gap] = temp;
                    }
                }
            }
        }


        /////////////////
        // Print results
        /////////////////

        void print_result(int n)
        {
            double[] array = new double[MAX];  
 //n行の配列のコピー用
            string[] title = new string[4] {">>> Bubble Sort <<<", ">>> Heap Sort <<<", ">>> Quick Sort <<<", ">>> Shell Sort <<<"};
            double max = 0.0, min = (double)MAX, med = 0, ave = 0;
            for(int i = 0; i < MAX; i++)
            {
                if(results[n, i] > max)            
//n番目のソートの結果の最大値を求める
                    max = results[n, i];    
                if(results[n, i] < min)            
//n番目のソートの結果の最小値を求める
                    min = results[n, i];
                ave += results[n, i];          
 //n番目のソートの結果を合計する
                array[i] = results[n, i];        //n番目の配列をコピーする
            }
            Array.Sort(array);                    
//コピー配列をソートする 
            med = array[(MAX % 2 != 0) ? MAX / 2 : MAX / 2 - 1];
            Console.WriteLine(title[n]);
            Console.WriteLine("最大値  :{0}", max);
            Console.WriteLine("最小値  :{0}", min);
            Console.WriteLine("平均値  :{0}",  ave / MAX);
            Console.WriteLine("中央値  :{0}", med);
            Console.WriteLine("総所要時間:{0}", ave);
            Console.WriteLine();
        }
    }
}

 

前回C#のウィンドウプログラム(最近は「アプリ」、か)WinFormsで作ってみましたが、今回はWPFWindows Presentation Foundation )にトライ(「HelloWorld5」)したいと思います。(私はXAMLは書けませんが...テヘッ)

 

【全体イメージ】

 

何かWinFormsよりもスッキリしていると思いませんか?(実は後でバラしますが、ずるしています。)

先ずはProjectを概観できる「ソリューションエクスプローラー」を見てみましょう。

 

 

WinFormsでは11あった「依存関係」-「アナライザー」が8に減少し(フレームワークは.NETCoreで同じですね)Form1.csProgram.csが消滅し、代わりにApp.xamlMainWindow.xamlにとって代わられています。前者にはApp.xaml.csAssemblyInfo.csがあり、後者にはMainWindow.xaml.cs、その下にMainWindowがあり、(同じ畑から生えた下に抜かれる『下剋上』ではなく)会社生え抜きの管理者csが、社外中途採用のマネージャーxamlにとって代わられた」という現代的既視感を覚えます。

 

実際、WPF導入から(それが目的なんですが)プログラム(アプリ)の構成が、「UI(User Interface)とロジックを分離するためのMVVM (Model-View-ViewModel) 」()という、保守性や拡張性を向上させるためのデザインパターンになりました。

MVVMは次の要素で構成されます。

Model: アプリケーションのデータやロジックを管理します。UIに依存せず、データ処理や外部APIとのやり取りを担当します。

View:XAMLファイルでレイアウトやUIデザインを定義し、データバインディング(注の注)を通じてViewModelと連携します。

ViewModel: ViewModelの橋渡し役で、データバインディングやコマンドを通じて、両者を結びつけます。

注の注:「データバインディング」はUIによるデータ変更がリアルタイムに反映される仕組みで、例えば、XAMLで「<TextBox Text="{Binding TextProperty}" />」と書き、ViewModelINotifyPropertyChangedインターフェースを実装することで、プロパティとTextBoxのデータがバインドされ、変更時に通知が送られて反映されるようになります。

 

では、エディターで開かれるファイルを見てみましょう。

 

(1)App.xaml

XAMLでプログラム(アプリ)の基本定義がなされているようです。

 

(2)App.xaml.cs

これはC#でApplication クラスから派生させたAppクラスを作っただけです。尚、今まで書いてきたWinFormsベースのプログラムではウィンドウのプラットフォームが"using System.Windows.Forms"であったのに対し、WPFでは"using System.Windows"になっている点に注意してください。(

:また、.NET6以降のWinFormsプロジェクトで、プロジェクトファイルに"<ImplicitUsings>enable</ImplicitUsings>"が書かれることにより、プログラムファイルに"using <なんちゃら>"の記述が省略されることにも注意してください。前回のWinformsプログラムには書かれていないです。)

 

(3)Assembly.info.cs

このファイルはコメント()を読むと基本的に実行ファイル(Assembly)に含まれるリソースの所在とIDを記述しているようです。

:英文コメント部分です。

ResourceDictionaryLocation.None,            //where theme specific resource dictionaries are located
                                                                                    //(used if a resource is not found in the page,
                                                                                    // or application resource dictionaries)

ResourceDictionaryLocation.SourceAssembly   //where the generic resource dictionary is located
                                                                                                 //(used if a resource is not found in the page,
                                                                                                 // app, or any theme specific resource dictionaries)

 

(4)MainWindow.xaml

これはXAMLで記述された(UIとなる)ウィンドウそのものです。ここで

白状

しなければならないのですが、WPFでは「ウィンドウに文字列を(Win32 APIを使って)描画する」という概念はなく、「文字列を表示するオブジェクトをウィンドウに配置する」()という形になるため、私も(.csファイルではなく)このMainWindow.xamlファイルに、次(↓)の文で(ウィンドウのGridの中に)文字列をデータとして持つTextBlockオブジェクトを作成するコード(これが私のAnother sky、もとい、今回のプログラミングコード)」を書き込んで、それを配置しています。(従って、今までのプログラムと全く同じとは言えませんね。)

:これにより、高度の表現能力を持つことが可能となる一方、Win32 APIでプログラミングをしてきた人は面食らうことも考えられ、分かりやすく利用できる配慮もされているようです。(ご参考

    <Grid Margin="40,30,40,30">
        <
TextBlock HorizontalAlignment="Center" Height="65" TextWrapping="Wrap" VerticalAlignment="Center" Width="540" FontSize="48" TextAlignment="Center"><Run Language="ja-jp" Text="Hello World!"/></TextBlock>
    </
Grid>

 

(5)MainWindow.xaml.cs(尚、MainWindowファイルをクリックするとこれが表示される)

これ以上に簡単なプログラムはありませんね。MainWindowクラスインスタンスを作成するだけです。コンストラクターにUIを設定する"InitializeComponent()"メソッドが入っています。

 

これをビルドし、実行してみましょう。

 

 

当たり前ですが、ウィンドウサイズを変更しても真ん中に表示されます。(というよりも、真ん中にオブジェクトを配置しています。)

 

さて、これで一応Visual Studioの小手試しとして実施した「Hello World!巡り」は完了、ということで、次回からは別の切り口でVisual Studioを探検してみます。

 

【寓話1】

4月からとある地方都市の私立中高一貫学校に入学したA君は、しかし、駅から学校までの通学路で公立好高校の生徒に囲まれるようになり、ある時「このままではいけない」と護身用のナイフを購入することを考えます。しかし、店で聞くとスイッチブレードやダガーナイフは銃刀法で禁止(注1)されているため、「正当な理由(注2)があれば」所持できる折り畳みナイフを購入しました。(注3)

注1:銃砲刀剣類所持等取締法第22条「(刃体の長さが6センチメートルをこえる刃物について)何人も、業務その他正当な理由による場合を除いては、これを携帯してはならない。」(秋葉原無差別殺人事件の発生により)2009年1月5日「刃渡り5.5cm以上15cm未満の柄を付けて用いる左右均整の形状をした諸刃の鋼質性の刃物で先端部が著しく鋭いもの」は銃刀法により禁止となる。

注2:「防犯のため、護身用」というのは正当な理由にはなりません。

注3:軽犯罪法第1条2号「正当な理由がなくて刃物、鉄棒その他人の生命を害し、又は人の身体に重大な害を加えるのに使用されるような器具を隠して携帯していた者は、拘留又は科料に処する。」

 

A君はその後公立高校の生徒が近づいてきた時にフォールディングナイフの刃を出し、威嚇するようになり、

(1)ある日「ざけんじゃねーぞ」と、それでも暴行を加えようとする高校生が怖くなり、刺してしまいました。

(2)高校生の通報により軽犯罪法違反を理由に補導されました。

 

【寓話2】

半グレのB君は体が小さく、喧嘩も弱いのでいつも小突かれ、パシリにされてきました。見返してやりたい一心で手製爆弾を調べ、ネットや窃盗により手製銃()を作成して、示威のため周囲の者にに見せるようになりました。ある日、B君のチームが敵対するチームとこじれ、衝突することになり、チームのヘッドから「B、お前そいつで気合い入れて先頭を仕切ってくれ」ということで肩をたたかれました。

:銃砲刀剣類所持等取締法、爆発物取締がっそく、火薬類取締法、刑法(第117条 激発物破裂罪)等違反

 

チーム衝突の日、先頭を切ったのは格好がよかったのですが、

(1)相手も「撃てるものなら撃ってみろよっ!」と突っ込んできたので思わず怖くなり引き金を引いたところ、相手3名を殺傷しましたが、重心が破裂し、自分の左腕も切断することになりました。

(2)相手半数は逃げたものの、残りが突っ込んできたので手製銃を捨てて逃げ出しました。その後、自分のチームは雲散霧消、ヘッドに焼きを入れられたものの、暴力に懲りて普通の勤め人として生きています。

 

【寓話3】

「絶対的兵器」と核抑止論の起源:

軍事戦略家のバーナード・ブロディは1946年に発表した論考で、核兵器が持つ圧倒的な破壊力に着目しました。彼は核兵器を「軍事的に絶対に使えない兵器」という意味で「絶対的兵器」と称し、その唯一の役割は抑止に限定されるべきだと主張しました。これは、従来の戦争における「勝利」とは異なる、核兵器による報復の脅威によって相手国の攻撃意図を思いとどまらせるという「核抑止論」の先駆的な概念の提唱となりました。

 

日本政府は、自ら核兵器を保有しない「非核三原則」を国是としつつも、安全保障政策においては米国の核兵器による抑止力(拡大抑止)を肯定し、その傘下に入っているという立場をとっています。

  • 非核三原則の堅持: 日本は唯一の戦争被爆国として、「核兵器を持たず、作らず、持ち込ませず」という非核三原則を堅持しています。これは法律ではなく政府の政策的原則ですが、国是として尊重されています。
  • 米国の拡大抑止への依存: 日本の安全は、自衛隊の防衛力に加え、日米安全保障条約に基づく米国の「拡大抑止」(米国の核戦力による抑止効果を日本に及ぼす政策)によって確保されている、というのが政府の一貫した見解です。
  • 核兵器禁止条約への対応: 日本は核兵器廃絶という目標を共有しつつも、核兵器禁止条約には署名・批准していません。その理由として、同条約が安全保障上の観点を踏まえておらず、核保有国が参加していないため、日本の安全保障政策(米国の拡大抑止への依存)と整合しないことが挙げられています。 

したがって、日本政府は自国による核保有は否定していますが、現在の国際安全保障環境において、他国の核兵器による抑止力(特に米国の拡大抑止)は必要不可欠な安全保障の基盤として肯定していると言えます。 

(以上いずれもGoogle-AI による概要)

 

【ご参考】

核抑止1

核抑止2

戦略核

戦術核

大気圏核実験による放射性降下物の影響(環境省)

厚労省による「原子爆弾の投下 に伴う放射性降下物の拡散状況 等に関する調査等一式」 強化策ならびに進捗状況

壊滅的な - 反核医師の会

核実験が及ぼす影響 | 5-3-2-4 放射性降下物の広がり(広島平和記念資料館)

放射性物質の大気から土壌への移行(農業・食品産業技術総合研究機構)

台湾有事から米中の核戦争に発展するケースで放射性降下物 ...

事故後30年記者が見たチェルノブイリ(ゴーストタウンになった「プリピャチ」)

チェルノブイリ原発の外部シェルターが損傷、放射能漏れ防止できず IAEA(2025/12/07)

避難地域復興課 - 福島県ホームページ

 

前回の「【Visual Studio探検記】「Hello World!巡り」-ウィンドウズ編(C#)総則」でC#のウィンドウズプログラミングには色々とプラットフォームが分かれているので、一応それらの概観を確認しました。

 

今回は旧い(Win95~のGDIとWinXP~のGDI+)グラフィックを使う、Win32 APIに近いWinFormsで「HelloWorld4」を作ってみましょう。

 

【全体イメージ】

 

これは既に作ったHelloWorld4の画面ですが、ソリューションエクスプローラーを見ると大分当事者(ファイル等)が多いので、ズームしてみます。

 

 

依存関係」はこのアプリケーションがよって立つプラットフォームのシステムやDLLなどが表示されます。(これをユーザーがいじることはまずありません。)尚、「依存関係」-「フレームワーク」を見ると、このプログラムは.NETCore上で走るようです。(「アナライザー」がコード自動生成やファイル管理、「フレームワーク」がプラットフォームなのかな、という感じを受けますね。)

 

Form1.cs」というのがこのプロジェクトの中核なのですが、「Form1(Form1()はコンストラクター、OnPaint()はイベント処理メソッドですね)と「Form1.Designers.cs(「してその実体は」UI設定処理で、リソーススクリプトForm1.resxもその手下のようです)からなります。

 

Program.cs」はエントリーポイントのMain()メソッドを持ち、コンソールプログラムではユーザープログラムの中核だったのですが、どうなってしまったのでしょうか?

 

では、エディターで開かれるファイルを見てみましょう。

 

(1) Form1.cs(ユーザーが処理を書き込む「プログラム本体」部分)

 

 

今回私が書き込んだ部分は緑色のコメント部分です。内容はC++のときと同じく、WM_PAINTメッセージの際のシステムの処理(OnPaintメソッド処理-"base.OnPaint(e)")の後に、Form1ウィンドウのクライアントエリア(ウィンドウの内枠部分)を求め、StringFormatクラスインスタンス sf を(画面の中央に表示をするよう)初期化し、文字列とフォントを設定してGraphicsクラスのDrawStringメソッドで"Hello World!"と描画します。

 

(2) Form1.cs [デザイン](これはForm1というクラスのUIをWYSWYGで設計し、XAMLで記述する部分)

 

 

コントロールやメニュー等は何も使わないのでこのままです。以下(↓)のXAMLファイルでも設定はすべてシステムの既定値です。

 

(3) Form1.Designers.cs(これはForm1クラスの一部で、Form1.resxというリソースの取り込みを含めてUIを規定する部分)

 

 

最初にSystem.ComponentModel.IContainerのインスタンスcomponents(実際はポインター)を初期化し、Form1のDisposeメソッドをオーバーライド()して(リソースがある場合<components != null>の)components.Dispose()メソッドを追加してから、componentsを生成(new)、Form1の初期設定を行っています。

:C++からの用語で、(異なる引数や戻り値の)同じ名前の関数やメソッドを作れるfeature(特長?)です。

 

<Form1.resx - ご参考(これがXAMLというマークアップ言語です)>

<?xml version="1.0" encoding="utf-8"?>
<root>
  <!-- 
    Microsoft ResX Schema 
        Version 2.0
    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.
    
    Example:
    
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
                
    There are any number of "resheader" rows that contain simple 
    name/value pairs.
    
    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.
    
    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:
    
    Note - application/x-microsoft.net.object.binary.base64 is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.
    
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.

    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>

 

(4) Program.cs(これがForm1のエントリーポイント<Main()>で、初期化と実行を行う部分)

 

 

お馴染みの"[STAThread] static void Main()"()というエントリーポイントメソッドがあり、アプリケーションの初期化と実行を行います。

: シングルスレッド アパートメント (Single-Threaded Apartment) 

 

いかがだったでしょうか?私的にはまだまだC++で書いたWIndowsプログラム(HellowWorld3)との相関が理解し得る範囲ではありますが、Visual Studio側で多くの処理を裏方の黒子がやっているので、「ユーザーは自分がやることが少なくなっている反面、(自動生成コードと自動処理が多く)プログラム全体で何がどうなっているのかがわからない」というジレンマが生じる、とも言えそうです。

 

いずれにしても、

 

これをビルドして実行すると、

 

 

こんな風になります。尚、「OnPaintイベント処理時点のクライアントエリアの中央に文字列を書き込むので、ウィンドウサイズを変更すると真ん中にならない」プログラムとなってしまいました。これを常に真ん中に持ってくるには「クライアント領域サイズをクラスメンバーとし、OnPaintメソッドにある描画位置処理を、ReSizeイベントOnSizeChanged(EventArgs e)メソッドをオーバーライドしたメソッドへ移動」してください。(面倒くさくてやりませんでした。テヘッ)

        .

        .

        .

        .

わかったよ、分かったから。

【変更版ーVisual Studioにやらせると厄介なので、変更は手作業で行いました。】

namespace HelloWorld4
{
    public partial class Form1 : Form
    {

        //解説:クラスメンバー変数(フィールド)
        int width = 0;
        int height = 0;

        //解説:コンストラクター
        public Form1()
        {
            InitializeComponent();
            width = this.ClientSize.Width;
            height = this.ClientSize.Height;
        }


        //解説:Paintイベントで描画します。
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            //解説:クライアントエリアの幅と高さを取得
            RectangleF rec = new RectangleF(0, 0, width, height);
            //解説:StringFormatインスタンスの設定
            StringFormat sf = new StringFormat
            {
                Alignment = StringAlignment.Center,    
// 水平方向中央
                LineAlignment = StringAlignment.Center  // 垂直方向中央
            };
 
          //解説:表示文字列
            string str = "Hello World!";
            //解説:描画用フォントとブラシの作成(usingを使い、Dispose()メソッドを省略)
            using (Font font = new Font("Meiryo", 20, FontStyle.Bold))
            using (Brush brush = new SolidBrush(Color.DarkBlue))
            {
               
//解説:文字列を描画する
                e.Graphics.DrawString(str, font, brush, rec, sf);
            }
        }


        //解説:SizeChangedイベントで描画します。
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);

            //解説:クライアントエリアの幅と高さを取得
            width = this.ClientSize.Width;
            height = this.ClientSize.Height;
            this.Refresh();    
//解説:強制的にクライアント領域を無効化、再描画します。(Win32API のinvalidaterect(hWnd, NULL, false);相当)
        }
    }
}

 

防衛費を前倒しでGDPの2%とするそうな。

 

主義や価値観を別とし、(米国大統領は同世代の商売人だから)過去の「(日米貿易不均衡是正策の一環として)付き合いで、断れずに使いもしないものを買わざるを得なかった」だけであれば、単に「無駄な歳出」だけで済むのですが、第三番目の「カネ」がついて、第二番目の「モノ」を(実際に"when in need"に本気で使うつもりで)購入したならば、どうしてもそれを(技術的、整備的、実践的に)使う「ヒト」がいなければどうにもなりません。これはあらゆる

 

マネージメント

 

の世界に共通する原理です。

 

人が要る

 

とうのは、単に「員数を合わせる」という意味では不十分で、教育、訓練双方が不可欠です。特に現在の状況下では深刻な

 

Achilles Tendon

 

となります。そんな赤子でもわかることを現政権や国民が分からないはずがない。にも拘わらず、

 

誰もが他人事のようなそぶりで、

誰も、何も言わない、沈黙の不気味さ

 

それが気になります。

 

死にかけの私のためではなく、(まだ使えそうな)子供たち、(逃げられない)孫たちのことを思うと。