CREATESTRUCT.lpCreateParams

CREATESTRUCT.lpCreateParamsは以前も使ったけど、ウィンドウをC++クラス"CWindow"(名前はなんでもいいけど)として定義し、ウィンドウハンドルとこのCWindowオブジェクトポインタが1対1に対応した形になるようにするために使うのが真骨頂かと思う。

CREATESTRUCTA (winuser.h) - Win32 apps | Microsoft Docs

Window.hで、

CWindowオブジェクトはウィンドウハンドルm_hWndを持つ。
staticのm_mapWindowMapも重要。
ウィンドウ作成メンバ関数Create。
そして、個々のオブジェクトごとにウィンドウメッセージ処理するDynamicWindowProc。
Window.cppは、

CWindow::Createの中のCreateWindowの最後の引数にthisを渡してる。
つまりCWindowオブジェクト自身のポインタが、CREATESTRUCT.lpCreateParamsに格納されることになる。
ParentWindow.hは、

CParentWindowは、CWindowの派生で、CChildWindowのポインタm_pChildWindowを持っていて、あとはDynamicWindowProcのオーバーライド。
ParentWindow.cppで、

子ウィンドウを作成し、ポインタはm_pChildWindowに格納。
ChildWindow.hは、

こちらのCChildWindowもCWindow派生。
ChildWindow.cppは、

こちらは特に中で子ウィンドウなどを作成はしない。
最後にCREATESTRUCT.cppで、

親ウィンドウCParentWindowの作成。
そして、CChildWindowもCParentWindowもウィンドウクラス"CREATESTRUCT"を指定しているので、WindowProcがウィンドウプロシージャになり、最初はここにウィンドウメッセージ処理が来る。
WM_CREATEで、lpCreateStruct->lpCreateParamsにオブジェクトポインタが入ってるので、ウィンドウハンドルをキー、ポインタを値としてCWindow::m_mapWindowMapに登録する。
WM_CREATEでない場合は、ハンドルを指定して、ポインタを取り出す。
ポインタが無ければDefWindowProcだが、ポインタがあればそのポインタが指すオブジェクトのDynamicWindowProcに処理を任せることになる。
こういう形で、最初のウィンドウプロシージャを統一しながらも、それぞれのウィンドウごとに具体的な処理をするウィンドウプロシージャを分けている。

最初に親ウィンドウを作る部分
最初に親ウィンドウを作る部分

最初に親ウィンドウを作る部分。

最初はこっちに来る
最初はこっちに来る

最初はこっちに来る。

WM_CREATEが来た時はこっちに
WM_CREATEが来た時はこっちに

WM_CREATEが来た時はこっちに。

そのあとはそれぞれのDynamicWindowProcの方に
そのあとはそれぞれのDynamicWindowProcの方に

そのあとはそれぞれのDynamicWindowProcの方に。

まずは親ウィンドウなのでこっち
まずは親ウィンドウなのでこっち

まずは親ウィンドウなのでこっち。

今度は子ウィンドウのWM_CREATEかな
今度は子ウィンドウのWM_CREATEかな

今度は子ウィンドウのWM_CREATEかな。

個々のDynamicWindowProcに
個々のDynamicWindowProcに

個々のDynamicWindowProcに。

今度は子ウィンドウのココに来る
今度は子ウィンドウのココに来る

今度は子ウィンドウのココに来る。

WM_CREATE以外の個別の処理
WM_CREATE以外の個別の処理

WM_CREATE以外の個別の処理。

ここにきて
ここにきて

ここにきて、

ここにくる
ここにくる

ここにくる。

ブレークポイントを無効にすると
ブレークポイントを無効にすると

ブレークポイントを無効にすると、

こんな感じでウィンドウが表示される
こんな感じでウィンドウが表示される

こんな感じでウィンドウが表示される。
閉じると、

親ウィンドウのココに来る。
親ウィンドウのココに来る。

親ウィンドウのココに来る。

子ウィンドウオブジェクトも破棄してる
子ウィンドウオブジェクトも破棄してる

_tWinMainの終盤でメッセージループ抜けたらCParentWindowオブジェクトを破棄しているが、そのデストラクタで子ウィンドウCChildWindowオブジェクトも破棄してる。

Sample/winapi/CREATESTRUCT/lpCreateParams/src/CREATESTRUCT at master · bg1bgst333/Sample · GitHub

CallWindowProc

これまでは、独自にウィンドウプロシージャを定義し、そのウィンドウプロシージャを持つウィンドウクラスを独自に登録し、そのウィンドウを作成し、別のウィンドウプロシージャに差し替えを行った。
さて、スタティックコントロールや、エディットボックスの場合は、既定のウィンドウクラスがあるわけで、そのウィンドウクラスのウィンドウプロシージャを差し替えることも出来るだろうか?
結論としては出来る。
ただし、普通のウィンドウのように差し替えただけでは正しく動作しない場合がある。
CallWindowProc.cppで、

// ヘッダファイルのインクルード
// 既定のヘッダファイル
#include <windows.h>	// 標準WindowsAPI
#include <tchar.h>		// TCHAR型
#include <commctrl.h>	// コモンコントロール

// 関数のプロトタイプ宣言
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);	// ウィンドウメッセージに対して独自の処理をするように定義したコールバック関数WindowProc.
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);	// エディットボックスの新しいウィンドウプロシージャEditProc.

// グローバルなウィンドウプロシージャ関数ポインタ
WNDPROC DefEditProc;	// エディットボックスのデフォルトウィンドウプロシージャ関数ポインタDefEditProc.

// _tWinMain関数の定義
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd){

	// 変数の宣言
	HWND hWnd;			// CreateWindowで作成したウィンドウのウィンドウハンドルを格納するHWND型変数hWnd.
	MSG msg;			// ウィンドウメッセージ情報を格納するMSG構造体型変数msg.
	WNDCLASS wc;		// ウィンドウクラス情報をもつWNDCLASS構造体型変数wc.

	// ウィンドウクラスの設定
	wc.lpszClassName = _T("CallWindowProc");				// ウィンドウクラス名は"CallWindowProc".
	wc.style = CS_HREDRAW | CS_VREDRAW;						// スタイルはCS_HREDRAW | CS_VREDRAW.
	wc.lpfnWndProc = WindowProc;							// ウィンドウプロシージャは独自の処理を定義したWindowProc.
	wc.hInstance = hInstance;								// インスタンスハンドルは_tWinMainの引数.
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);				// アイコンはアプリケーション既定のもの.
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);				// カーソルは矢印.
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// 背景は白ブラシ.
	wc.lpszMenuName = NULL;									// なし
	wc.cbClsExtra = 0;										// 0でいい.
	wc.cbWndExtra = 0;										// 0でいい.

	// ウィンドウクラスの登録
	if (!RegisterClass(&wc)){	// RegisterClassでウィンドウクラスを登録し, 0が返ったらエラー.

		// エラー処理
		MessageBox(NULL, _T("RegisterClass Failure!"), _T("CallWindowProc"), MB_OK | MB_ICONHAND);	// MessageBoxで"RegisterClass Failure!"とエラーメッセージを表示.
		return -1;	// 異常終了(1)

	}

	// ウィンドウの作成
	hWnd = CreateWindow(_T("CallWindowProc"), _T("CallWindowProc"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);	// CreateWindowで, 上で登録した"CallWindowProc"ウィンドウクラスのウィンドウを作成.
	if (hWnd == NULL){	// ウィンドウの作成に失敗したとき.

		// エラー処理
		MessageBox(NULL, _T("CreateWindow Failure!"), _T("CallWindowProc"), MB_OK | MB_ICONHAND);	// MessageBoxで"CreateWindow Failure!"とエラーメッセージを表示.
		return -2;	// 異常終了(2)

	}

	// ウィンドウの表示
	ShowWindow(hWnd, SW_SHOW);	// ShowWindowでSW_SHOWを指定してウィンドウの表示.

	// メッセージループ
	while (GetMessage(&msg, NULL, 0, 0) > 0){	// GetMessageでメッセージを取得, 戻り値が0より大きい間はループし続ける.

		// ウィンドウメッセージ処理
		TranslateMessage(&msg);	// TranslateMessageで仮想キーメッセージを文字メッセージへ変換.
		DispatchMessage(&msg);	// DispatchMessageで受け取ったメッセージをウィンドウプロシージャ(この場合は独自に定義したWindowProc)に送出.

	}

	// プログラムの終了
	return (int)msg.wParam;	// 終了コード(msg.wParam)を戻り値として返す.

}

エディットボックス用に新たに定義したEditProcを用意。
また、エディットボックス既定のウィンドウプロシージャを持っておくポインタDefEditProcも用意。
WindowProcは、

// WindowProc関数の定義
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){	// ウィンドウメッセージに対して独自の処理をするように定義したウィンドウプロシージャ.

	// ウィンドウメッセージに対する処理.
	switch (uMsg){	// switch-casa文でuMsgの値ごとに処理を振り分ける.

		// ウィンドウの作成が開始された時.
		case WM_CREATE:		// ウィンドウの作成が開始された時.(uMsgがWM_CREATEの時.)

			// WM_CREATEブロック
			{

				// 変数の宣言と初期化.
				LPCREATESTRUCT lpCreateStruct;	// CREATESTRUCT構造体ポインタlpCreateStruct.
				HWND hEdit = NULL;	// ウィンドウハンドルhEditをNULLで初期化.

				// エディットコントロールの作成.
				lpCreateStruct = (LPCREATESTRUCT)lParam;	// lParamをLPCREATESTRUCTにキャストし, lpCreateStructに格納.
				hEdit = CreateWindow(WC_EDIT, _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 100, 100, 300, 300, hwnd, (HMENU)WM_APP + 1, lpCreateStruct->hInstance, NULL);	// CreateWindowでエディットボックスを作成し, 戻り値のウィンドウハンドルをhEditに格納.
				DefEditProc = (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);	// エディットボックスのウィンドウプロシージャDefEditProcを取得.
				SetWindowLong(hEdit, GWL_WNDPROC, (LONG)EditProc);	// EditProcをセット.

			}

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

		// ウィンドウが破棄された時.
		case WM_DESTROY:	// ウィンドウが破棄された時.(uMsgがWM_DESTROYの時.)

			// WM_DESTROYブロック
			{

				// 終了メッセージの送信.
				PostQuitMessage(0);	// PostQuitMessageで終了コードを0としてWM_QUITメッセージを送信.(するとメッセージループのGetMessageの戻り値が0になるので, メッセージループから抜ける.)

			}

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

		// 上記以外の時.
		default:	// 上記以外の値の時の既定処理.

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

	}

	// あとは既定の処理に任せる.
	return DefWindowProc(hwnd, uMsg, wParam, lParam);	// 戻り値も含めDefWindowProcに既定の処理を任せる.

}

WM_CREATEでエディットボックスを作成。
そして、もともとのウィンドウプロシージャのポインタはDefEditProcに格納しておき、EditProcを新たにセット。
EditProcは、

// EditProc関数の定義
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){	// エディットボックスの新しいウィンドウプロシージャEditProc.

	// ウィンドウメッセージに対する処理.
	switch (uMsg){	// switch-casa文でuMsgの値ごとに処理を振り分ける.

		// マウスの左ボタンが押された時.
		case WM_LBUTTONDOWN:	// マウスの左ボタンが押された時.(uMsgがWM_LBUTTONDOWNの時.)

			// WM_LBUTTONDOWNブロック
			{

				// "WM_LBUTTONDOWN"を表示.
				MessageBox(NULL, _T("WM_LBUTTONDOWN"), _T("CallWindowProc"), MB_OK);	// MessageBoxで"WM_LBUTTONDOWN"と表示.

			}

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

		// 上記以外の時.
		default:	// 上記以外の値の時の既定処理.

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

	}

	// あとは既定の処理に任せる.
	return DefWindowProc(hwnd, uMsg, wParam, lParam);	// 戻り値も含めDefWindowProcに既定の処理を任せる.

}

マウス左クリックでメッセージボックスを出すようにしている。

起動時
起動時

起動時。

左クリックには対応している
左クリックには対応している

左クリックには対応している。
ただ、文字列など入力には対応できてない。
そこで、

CallWindowProcA function (winuser.h) - Win32 apps | Microsoft Docs

CallWindowProcを使って、もともとのエディットボックスのウィンドウプロシージャDefEditProcを呼び出す。

// EditProc関数の定義
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){	// エディットボックスの新しいウィンドウプロシージャEditProc.

	// ウィンドウメッセージに対する処理.
	switch (uMsg){	// switch-casa文でuMsgの値ごとに処理を振り分ける.

		// マウスの左ボタンが押された時.
		case WM_LBUTTONDOWN:	// マウスの左ボタンが押された時.(uMsgがWM_LBUTTONDOWNの時.)

			// WM_LBUTTONDOWNブロック
			{

				// "WM_LBUTTONDOWN"を表示.
				MessageBox(NULL, _T("WM_LBUTTONDOWN"), _T("CallWindowProc"), MB_OK);	// MessageBoxで"WM_LBUTTONDOWN"と表示.

			}

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

		// 上記以外の時.
		default:	// 上記以外の値の時の既定処理.

			// 既定の処理へ向かう.
			break;	// breakで抜けて, 既定の処理(DefWindowProc)へ向かう.

	}

	// あとは既定の処理に任せる.
	//return DefWindowProc(hwnd, uMsg, wParam, lParam);	// 戻り値も含めDefWindowProcに既定の処理を任せる.
	return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);	// CallWindowProcを通してDefEditProcに任せる.

}

こうすると、

さっきは反映されてなかったWS_BORDERが反映された
さっきは反映されてなかったWS_BORDERが反映された

さっきは反映されてなかったWS_BORDERが反映された。

クリックには対応してる
クリックには対応してる

クリックには対応してる。

キャレットも出ている。でも入力が反映されない。
キャレットも出ている。でも入力が反映されない。

キャレットも出ている。でも入力が反映されない。

MessageBoxが余計かな。

あー、入力出来た。
あー、入力出来た。

あー、入力出来た。

ここには来てるから、新たに定義したウィンドウプロシージャには来てるんだけどね。
ここには来てるから、新たに定義したウィンドウプロシージャには来てるんだけどね。

ここには来てるから、新たに定義したウィンドウプロシージャには来てるんだけどね。
あとは、ウィンドウメッセージに対する処理を書けばいいのだが、これだけでは都度都度上手くいかない場合(CallWindowProcで既定プロシージャを呼ぶ場合、DefWindowProcを呼ぶ場合、0などその場で値を返す場合など。)があるので、それはメッセージごとにいずれ・・・。

Sample/winapi/CallWindowProc/CallWindowProc/src/CallWindowProc at master · bg1bgst333/Sample · GitHub

SetWindowLong

SetWindowLongで、ウィンドウの様々なLong値をセットできる。
GWL_WNDPROCで、新たなウィンドウプロシージャをセットして差し替える。

SetWindowLongA function (winuser.h) - Win32 apps | Microsoft Docs

SetWindowLong.cppで、WinMainは、

親ウィンドウと子ウィンドウで、2つのウィンドウクラスを登録。
親は白背景でウィンドウプロシージャがWindowProc。
子は淡灰色背景でウィンドウプロシージャがChildWindowProc。
WindowProcは、

WM_CREATEで子ウィンドウ作成。(スタティックコントロールってコメントは間違い。)
あと、マウスの左クリックで子ウィンドウのウィンドウプロシージャをSetWindowLongでThirdWindowProcに差し替え。
ChildWindowProcは、

子ウィンドウの領域を左クリックで"ChildWindowProc#Clicked!"がメッセージボックスで表示される。
ThirdWindowProcは、

子ウィンドウ領域を左クリックで"ThirdWindowProc#Clicked!"をメッセージボックス表示。

起動時
起動時

起動時。
グレーの領域が子ウィンドウ。

グレーの領域をクリックするとこれが出る
グレーの領域をクリックするとこれが出る

グレーの領域をクリックするとこれが出る。

白い親ウィンドウ領域をクリックするとこれが出て差し替えが発生
白い親ウィンドウ領域をクリックするとこれが出て差し替えが発生

白い親ウィンドウ領域をクリックするとこれが出て差し替えが発生。

その後、またグレーをクリックするとこうなる
その後、またグレーをクリックするとこうなる

その後、またグレーをクリックするとこうなる。
ChildWindowProcがThirdWindowProcに差し替えられてるのがわかる。

Sample/winapi/SetWindowLong/SetWindowLong/src/SetWindowLong at master · bg1bgst333/Sample · GitHub

CREATESTRUCT.hwndParent

CREATESTRUCT.hwndParentを使えば、わざわざ親ウィンドウハンドルをlpCreateParams使って渡してもらわなくても、取得できる。

CREATESTRUCTA (winuser.h) - Win32 apps | Microsoft Docs

CREATESTRUCT.cppで、

親ウィンドウハンドルを表示。
その後のCreateWindowの最後の引数はNULLに戻してる。

こっちでlpCreateStruct->hwndParentで親ウィンドウハンドルを表示してる。

WindowProcではこうなる
WindowProcではこうなる

WindowProcではこうなる。

WindowProc2はこうなる
WindowProc2はこうなる

WindowProc2はこうなる。
同じ値になってる。

Sample/winapi/CREATESTRUCT/hwndParent/src/CREATESTRUCT at master · bg1bgst333/Sample · GitHub

GWL_WNDPROC

GetWindowLongにGWL_WNDPROCを指定すると、指定したウィンドウハンドルのウィンドウのウィンドウプロシージャを取得できる。

GetWindowLongA function (winuser.h) - Win32 apps | Microsoft Docs

GWL_WNDPROC.cppで、

WindowProcのアドレスを表示。

今度はWindowProc2のアドレスを表示。

WindowProcの中で、親ウィンドウと子ウィンドウのウィンドウプロシージャアドレスを表示。

こっちのWindowProc2も、親ウィンドウと子ウィンドウのウィンドウプロシージャアドレスを表示。
なのだが、WindowProc2からは親ウィンドウハンドルは取得できないので(ほんとはできるんだけど)、WindowProcの時のCreateWindowの最後の引数でhwndを指定している。それが、WindowProc2からはlpCreateStruct->lpCreateParamsで取得することが出来る。

まず_tWinMainのWindowProc
まず_tWinMainのWindowProc

まず_tWinMainのWindowProc。

今度は_tWinMainのWindowProc2
今度は_tWinMainのWindowProc2

今度は_tWinMainのWindowProc2。

WindowProc内から自分自身
WindowProc内から自分自身

WindowProc内から自分自身。

WindowProc2内からWindowProc
WindowProc2内からWindowProc

WindowProc2内からWindowProc。

WindowProc2内から自分自身
WindowProc2内から自分自身

WindowProc2内から自分自身。

WindowProc内からWindowProc2
WindowProc内からWindowProc2

WindowProc内からWindowProc2。

ウィンドウ自体は変わらない
ウィンドウ自体は変わらない

ウィンドウ自体は変わらない。

Sample/winapi/GetWindowLong/GWL_WNDPROC/src/GWL_WNDPROC at master · bg1bgst333/Sample · GitHub

CREATESTRUCT.hInstance

実は、WM_CREATE時に限っては、GetWindowLongとGWL_HINSTANCEを使わなくても、CREATESTRUCT.hInstanceという形でアプリケーションインスタンスハンドルを取得できる。

CREATESTRUCTA (winuser.h) - Win32 apps | Microsoft Docs

CREATESTRUCT.cppで、

lParamをLPCREATESTRUCT型のlpCreateStructにキャストして、lpCreateStruct->hInstanceを文字列変換し表示。

まずは_tWinMain
まずは_tWinMain

まずは_tWinMain。

こちらはWindowProc内のWM_CREATEのlpCreateStruct-&gt;hInstance。
こちらはWindowProc内のWM_CREATEのlpCreateStruct->hInstance。

こちらはWindowProc内のWM_CREATEのlpCreateStruct->hInstance。

ウィンドウ表示は変わらない
ウィンドウ表示は変わらない

ウィンドウ表示は変わらない。

Sample/winapi/CREATESTRUCT/hInstance/src/CREATESTRUCT at master · bg1bgst333/Sample · GitHub

GWL_HINSTANCE

アプリケーションインスタンスハンドルhInstanceは_tWinMainの引数として与えられるが、ウィンドウプロシージャのWindowProcとか任意の別の関数から取得したい場合は、GetWindowLongにGWL_HINSTANCEを指定する。

GetWindowLongA function (winuser.h) - Win32 apps | Microsoft Docs

GWL_HINSTANCE.cppで、

_tWinMainのhInstanceを表示。

このhInstanceはCreateWindowに使う。

WindowProcの中で子ウィンドウを作る場合は、GetWindowLongにGWL_HINSTANCEでhInstanceを取得して、それをCreateWindowに渡す。
こっちでもhInstanceを表示する。

_tWinMainではこうなる
_tWinMainではこうなる

_tWinMainではこうなる。

WindowProc内で取得したhInstanceはこう
WindowProc内で取得したhInstanceはこう

WindowProc内で取得したhInstanceはこう。

黒いボーダーの枠が子ウィンドウ
黒いボーダーの枠が子ウィンドウ

黒いボーダーの枠が子ウィンドウ。

Sample/winapi/GetWindowLong/GWL_HINSTANCE/src/GWL_HINSTANCE at master · bg1bgst333/Sample · GitHub