ではWCEditor プロジェクトを構成する各ファイルを見てゆきます。
【WCEditor.cpp】
//////////////////////////////////////////
// WCEditor.cpp
//Copyright (c) 05/01/2022 by BCCSkelton
//////////////////////////////////////////
#include "WCEditor.h"
#include "WCEditorProc.h"
////////////////
// WinMain関数
////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
//2重起動防止
if(!WCEditor.IsOnlyOne()) {
HWND hWnd = FindWindow("MainWnd", "WCEditor");
if(IsIconic(hWnd))
ShowWindow(hWnd, SW_RESTORE);
SetForegroundWindow(hWnd);
return 0L;
}
//ウィンドウ登録 - Init(ClassName, hInstance, WndProc, "IDM_MAIN",
// (以下省略可)MAKEINTRESOURCE(IDI_ICON), IDC_ARROW, Brush)
WCEditor.Init("MainWnd", hInstance, SDIPROC, "IDM_MAIN", MAKEINTRESOURCE(IDI_ICON));
//ウィンドウ作成と表示-Create(WindowTitle, (以下省略可)Style,
// ExStyle, hParent, hMenu, x, y, w, h)
if(!WCEditor.Create("WCEditor"))
return 0L;
//メッセージループに入る
return WCEditor.Loop();
}
//(解説:基本的にSkeltonWizardが生成したSDIウインドウ用のコードのままです。Windowsのワイド文字(WCHAR == wchar_t、UTF-16ユニコード)を調べてから知ったのですが、Win2000からエントリーポイントは"wmain"や、"wWinMain"になり、引数がUTF-16になったとのことです。何故かエントリーポイント関数は、他のpostfix型Win32API関数と違い、prefix型になっていますね。勿論ベースのBCCSkeltonの様に、"main"、"WinMain"でcharベースの引数も使えます。)
【WCEditor.h】
//////////////////////////////////////////
// WCEditor.h
// Copyright (c) 05/01/2022 by BCCSkelton
//////////////////////////////////////////
//BCCSkeltonのヘッダー-これに必要なヘッダーが入っている
#include "BCCSkelton.h"
//リソースIDのヘッダー
#include "ResWCEditor.h"
/*
//ワイド文字を使用する
#include <wchar.h>
//ワイド文字対応StrStrW使用の為
#include <shlwapi.h>
//ワイド文字対応CWNDWクラス
#include "CWNDW.h"
//ワイド文字対応CDLGWクラス
#include "CDLGW.h"
//ワイド文字対応CFONTWクラス
#include "CFONTW.h"
//ワイド文字対応CCTRLWクラス
#include "CCTRLW.h"
//ワイド文字対応CSTRWクラス
#include "CSTRW.h"
//ワイド文字対応CMNDLGWクラス
#include "CMNDLGW.h"
//ワイド文字対応CSBARWクラス
#include "CSBARW.h"
*/
//ワイド文字を使用する
#include "BCCSkeltonWplus.h"
//(解説:コメント/*~*/で囲ったのは、開発時のコードです。その後ワイド文字対応クラスの「アドオン化(BCCSkelotonWplus)」に伴い、"BCCSkelotonWplus.h"にまとめました。)
/////////////////////////////////////////////////////////////////////
//CMyWndクラスをCSDIクラスから派生させ、メッセージ用の関数を宣言する
/////////////////////////////////////////////////////////////////////
class CMyWnd : public CSDI
{
public: //以下はコールバック関数マクロと関連している
//2重起動防止用のMutex用ID名称
CMyWnd(char* UName) : CSDI(UName) {}
//メンバー変数
bool m_16OR8 = TRUE; //UTF-16(TRUE)かUTF-8(FALSE)か
bool m_BOM = TRUE; //BOM付きか否か
int m_POS = 0; //変換型式(UTF-16 - 0、BOM付UTF-8 - 1、BOM無UTF-8 - 2)
//(解説:CSTRWの出力形式に合わせてメンバー変数フラグを持たせました。)
//メニュー項目、ダイアログコントロール関連
bool OnNew();
bool OnOpen();
bool OnSave();
bool OnSaveas();
bool OnExit();
bool OnCut();
bool OnCopy();
bool OnPaste();
bool OnUtf16();
bool OnUtf8();
bool OnUtf8n();
bool OnVersion();
//(解説:全てメニューアイテムです。)
//ウィンドウメッセージ関連
bool OnCreate(WPARAM, LPARAM);
bool OnSize(WPARAM, LPARAM);
bool OnClose(WPARAM, LPARAM);
//ユーザー定義関数
void ChangeCheck(int);
//(解説:メニューにチェックを付ける関数です。)
};
////////////////////////////////////////////////////////////////////////
//派生させたCMyWndクラスのインスタンスとコールバック関数(マクロ)の作成
//主ウィンドウはダイアログと違い、コールバック関数は一つしか作れない
////////////////////////////////////////////////////////////////////////
CMyWnd WCEditor("WCEditor"); //ウィンドウクラスインスタンスの生成
BEGIN_SDIMSG(WCEditor) //ダイアログと違い、コールバック関数名を特定しない
//メニュー項目、ダイアログコントロール関連
ON_COMMAND(WCEditor, IDM_NEW, OnNew())
ON_COMMAND(WCEditor, IDM_OPEN, OnOpen())
ON_COMMAND(WCEditor, IDM_SAVE, OnSave())
ON_COMMAND(WCEditor, IDM_SAVEAS, OnSaveas())
ON_COMMAND(WCEditor, IDM_EXIT, OnExit())
ON_COMMAND(WCEditor, IDM_CUT, OnCut())
ON_COMMAND(WCEditor, IDM_COPY, OnCopy())
ON_COMMAND(WCEditor, IDM_PASTE, OnPaste())
ON_COMMAND(WCEditor, IDM_UTF16, OnUtf16())
ON_COMMAND(WCEditor, IDM_UTF8, OnUtf8())
ON_COMMAND(WCEditor, IDM_UTF8N, OnUtf8n())
ON_COMMAND(WCEditor, IDM_VERSION, OnVersion())
//ウィンドウメッセージ関連
ON_CREATE(WCEditor)
ON_SIZE(WCEditor)
ON_CLOSE(WCEditor)
END_WNDMSG
//////////////////////
//UTF Types Constants
//////////////////////
const WCHAR *g_UTFTypes[3] = {L"UTF-16(BOM付)", L"UTF-8(BOM付)", L"UTF-8(BOM無)"};
//(解説:メニューアイテム番号の0、1、2が表示用のワイド文字列に対応するように設定しています。)
///////////////////////
//ステータスバーの作成
///////////////////////
CSBARW SBar;
////////////////////////
//EDIT BOX用コントロール
////////////////////////
CCTRLW g_Edit;
////////////////////////
//コモンダイアログの作成
////////////////////////
CMNDLGW g_Cmndlg;
///////////////////
//ファイル関係変数
///////////////////
CSTRW g_FileName;
CSTRW g_File;
//(解説:今回開発したワイド文字対応クラスを使っています。)
///////////////////////////////////////////
// CDLGクラスからVERSIONDLGクラスを派生
// 複数の同一ダイアログ変数とダイアログも作
// れるが、一つのダイアログに一つの派生ダイ
// アログクラスを作成するのが基本
///////////////////////////////////////////
class VERSIONDLG : public CDLG {
public:
bool OnInit(WPARAM, LPARAM);
bool OnIdok();
};
////////////////////////////////////////////////////////////////////////////
// VERSIONDLGクラスダイアログ変数の生成とそのコールバック関数(マクロ)を定義
// 複数同一クラスのダイアログを作成することを予期してコールバック関数を明記
////////////////////////////////////////////////////////////////////////////
VERSIONDLG versiondlg;
BEGIN_MODALDLGMSG(versiondlgProc, versiondlg) //第1引数がコールバック関数の名前
ON_COMMAND(versiondlg, IDOK, OnIdok())
END_DLGMSG
【WCEditorProc.h】
//////////////////////////////////////////
// WCEditorProc.h
// Copyright (c) 05/01/2022 by BCCSkelton
//////////////////////////////////////////
/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//ウィンドウメッセージ関数
/////////////////////////////////
bool CMyWnd::OnCreate(WPARAM wParam, LPARAM lParam) {
//コモンコントロールの初期化
InitCommonControls();
//(解説:以下ステータスバーはワイド文字対応型ですので、L付文字列を使っています。)
//ステータスバー登録-Init(hWnd, hIinstance, ID, (以下省略可)Style)
SBar.Init(m_hWnd, m_hInstance, STATUSBAR);
//ステータスバー区画設定
int sec[3] = {100, 200, -1};
SBar.SetSection(3, sec);
//ステータスバー文字列設定
SBar.SetText(0, L"WCEditor Ver 1.0");
SBar.SetText(1, g_UTFTypes[0]);
SBar.SetText(2, L"新規ファイル");
//EDITコントロールの作成(登録、作成、)
g_Edit.Init(L"EDIT", m_hInstance);
g_Edit.Create(L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL |
ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_LEFT,
WS_EX_CLIENTEDGE, m_hWnd, IDC_EDIT, 0, 0, 0, 0);
SendItemMsg(IDC_EDIT, EM_SETLIMITTEXT, 1048576, 0); //1M bytes
//(解説:CCTRLWクラスでエディットボックスを作りますが、これもワイド文字対応です。)
return TRUE;
}
//(解説:以下も同様にL付文字列や、W付きワイド文字対応関数を使っています。)
bool CMyWnd::OnSize(WPARAM wParam, LPARAM lParam) {
//ステータスバー自動調整
SBar.AutoSize();
//EDITコントロール位置調整
RECT rec;
GetWindowRect(SBar.GetHandle(), &rec);
MoveWindow(GetDlgItem(m_hWnd, IDC_EDIT), 0, 0, LOWORD(lParam), HIWORD(lParam) - rec.bottom + rec.top, TRUE);
return TRUE;
}
bool CMyWnd::OnClose(WPARAM wParam, LPARAM lParam) {
if(MessageBoxW(m_hWnd, L"終了しますか", L"終了確認",
MB_YESNO | MB_ICONINFORMATION) == IDYES)
//(解説:ワイド文字対応のW仕様ですね。)
//処理をするとDestroyWindow、PostQuitMessageが呼ばれる
return TRUE;
else
//そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
return FALSE;
}
/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CMyWnd::OnNew() {
//IDC_EDITに文字列があれば保存するか確認するする
if(SendItemMsg(IDC_EDIT, WM_GETTEXTLENGTH, 0, 0)) {
if(MessageBoxW(m_hWnd, L"現在のテキストを保存しますか?", L"確認", MB_YESNO | MB_ICONQUESTION) == IDYES)
OnSave();
}
//IDC_EDITを初期化
SendItemMsg(IDC_EDIT, WM_SETTEXT, 0, (LPARAM)"");
//ステータスバーに新規であることを表示
SBar.SetText(2, L"新規ファイル");
g_FileName = L"";
return TRUE;
}
bool CMyWnd::OnOpen() {
//開くファイルを指定する
WCHAR* fn = g_Cmndlg.GetFileName(m_hWnd, L"テキストファイル(*.txt)\0*.txt\0全てのファイル\0*.*\0\0", TRUE);
//ファイルパス、名をg_FileNameに保存する
g_FileName = fn;
//g_Fileにファイルを読み込む
g_File.FromFile(g_FileName.ToChar());
//(解説:これがワイド文字対応ファイルの読み込みです。ASCII版と全く変わらないコーディングです。)
//IDC_EDITにファイルを表示
SendDlgItemMessageW(m_hWnd, IDC_EDIT, WM_SETTEXT, 0, (LPARAM)g_File.ToChar());
//ステータスバーにファイルパス名を表示
SBar.SetText(2, g_FileName.ToChar());
return TRUE;
}
bool CMyWnd::OnSave() {
if(!*g_FileName.ToChar()) //新規ファイルならOnSaveasへ移行
OnSaveas();
else {
//WCHAR配列を文字列長分確保してIDC_EDITの文字列を取得する
DWORD size = SendItemMsg(IDC_EDIT, WM_GETTEXTLENGTH, 0, 0) + 1;
WCHAR* ptr = new WCHAR[size];
//IDC_EDITのデータをg_Fileへ保存
SendDlgItemMessageW(m_hWnd, IDC_EDIT, WM_GETTEXT, lstrlenW(ptr) + 1, (LPARAM)ptr);
//データをg_Fileに保存してWCHAR配列は破棄する
g_File = ptr;
delete [] ptr;
//(解説:以下がフラグに従って、3様のUTF型式とBOMの有無でファイルを保存するところです。)
//UTF区分、BOMの有無に従って保存
if(m_16OR8) //UTF-16
g_File.ToFile(g_FileName.ToChar());
else { //UTF-8
if(m_BOM) //BOM付
g_File.ToUTF8File(g_FileName.ToChar(), TRUE);
else //BOM無
g_File.ToUTF8File(g_FileName.ToChar(), FALSE);
}
}
return TRUE;
}
bool CMyWnd::OnSaveas() {
//保存するファイルパス名を指定する
WCHAR* fn = g_Cmndlg.GetFileName(m_hWnd, L"テキストファイル(*.txt)\0*.txt\0全てのファイル\0*.*\0\0", FALSE);
g_FileName = fn;
//WCHAR配列を文字列長分確保してIDC_EDITの文字列を取得する
DWORD size = SendItemMsg(IDC_EDIT, WM_GETTEXTLENGTH, 0, 0) + 1;
WCHAR* ptr = new WCHAR[size];
//IDC_EDITのデータをg_Fileへ保存
SendDlgItemMessageW(m_hWnd, IDC_EDIT, WM_GETTEXT, lstrlenW(ptr) + 1, (LPARAM)ptr);
//データをg_Fileに保存してWCHAR配列は破棄する
g_File = ptr;
delete [] ptr;
//(解説:ここも同様に、3様のUTF型式とBOMの有無でファイルを保存するところです。)
//UTF区分、BOMの有無に従って保存
if(m_16OR8) //UTF-16
g_File.ToFile(g_FileName.ToChar());
else { //UTF-8
if(m_BOM) //BOM付
g_File.ToUTF8File(g_FileName.ToChar(), TRUE);
else //BOM無
g_File.ToUTF8File(g_FileName.ToChar(), FALSE);
}
//ステータスバーにファイルパス名を表示
SBar.SetText(2, g_FileName.ToChar());
return TRUE;
}
bool CMyWnd::OnExit() {
SendMsg(WM_CLOSE, 0, 0); //終了処理
return TRUE;
}
bool CMyWnd::OnCut() {
SendItemMsg(IDC_EDIT, WM_CUT, 0, 0); //Cut処理
return TRUE;
}
bool CMyWnd::OnCopy() {
SendItemMsg(IDC_EDIT, WM_COPY, 0, 0); //Copy処理
return TRUE;
}
bool CMyWnd::OnPaste() {
SendItemMsg(IDC_EDIT, WM_PASTE, 0, 0); //Paste処理
return TRUE;
}
//(解説:以下がメニューアイテムによるフラグの設定です。)
bool CMyWnd::OnUtf16() {
m_16OR8 = TRUE; //UTF-16(TRUE)
m_BOM = TRUE; //BOM付
ChangeCheck(0); //メニューアイテムにチェックを入れる
SBar.SetText(1, g_UTFTypes[0]);
return TRUE;
}
bool CMyWnd::OnUtf8() {
m_16OR8 = FALSE; //UTF-8(FALSE)
m_BOM = TRUE; //BOM付
ChangeCheck(1); //メニューアイテムにチェックを入れる
SBar.SetText(1, g_UTFTypes[1]);
return TRUE;
}
bool CMyWnd::OnUtf8n() {
m_16OR8 = FALSE; //UTF-8(FALSE)
m_BOM = FALSE; //BOM無
ChangeCheck(2); //メニューアイテムにチェックを入れる
SBar.SetText(1, g_UTFTypes[2]);
return TRUE;
}
bool CMyWnd::OnVersion() {
//IDD_VERSIONダイアログを呼び出す
versiondlg.DoModal(m_hWnd, "IDD_VERSION", versiondlgProc, m_hInstance);
return TRUE;
}
///////////////////////////////////
//ユーザー定義関数
//メニューアイテムチェック切替
///////////////////////////////////
void CMyWnd::ChangeCheck(int pos) {
HMENU hMenu = GetSubMenu(GetMenu(m_hWnd), 2); //表示メニューハンドルの取得
//メニューアイテム
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
//既にチェックされているアイテムのチェックを外す
mii.fState = MFS_UNCHECKED;
SetMenuItemInfo(hMenu, m_POS, TRUE, &mii);
//指定アイテムをチェックする
mii.fState = MFS_CHECKED;
SetMenuItemInfo(hMenu, pos, TRUE, &mii);
//チェックアイテムの記録
m_POS = pos;
}
//(解説:3様のUTF型式とBOMの有無に基づいてメニューアイテムにチェックを入れます。)
///////////////////////////////
//ユーザーダイアログの関数定義
//コントロール関数
///////////////////////////////
bool VERSIONDLG::OnInit(WPARAM wParam, LPARAM lParam) {
WCHAR* str = L"WCEditor Version 1.0\nCopyright 2022 by Ysama";
SendDlgItemMessageW(m_hWnd, IDC_LABEL, WM_SETTEXT, 0, (LPARAM)str);
return TRUE;
}
//(解説:バージョンダイアログの文字をワイド文字にしています。)
bool VERSIONDLG::OnIdok() {
EndModal(TRUE);
return TRUE;
}
まぁ、こんなところでしょうか?
このようにUTF-16のワイド文字対応しても、ユーザーに得るところが乏しく、単にプログラムが大きくなり(理屈上)処理負荷が大きくなるだけ、という感が残ります。(そこらへんがUTF-8が好まれる所以ですね。)
↑のプログラムでもCWNDWやCDLGWは使っておらず、「完全なワイド文字対応」ではないですが、それは「データ文字列を扱っていないのに、(例えばCCTRLクラスの"EDIT"のような)何故アルファベットのウィンドウクラス名を1バイト文字列から2バイト文字にしなくてはならないのか?」という疑念が生じ、必然性に疑問が付いたからです。それが「アドオン化」にしようと思った理由でもあります。
今回のワイド文字化対応はAlbum開発中に遭遇した「バグ探しと(1バイト文字では対応できない)バグ退治ツールの為」という大義があった為、その目的に必要なワイド文字対応クラスやワイド文字対応プログラムをBCCSkeltonでが書けることを示しただけであり、今後はやはり(ワイド文字を使う必然性が無く、可能なら)ASCII版に戻ってコンパクトに行きたいですね。