C++ ⇒ VBA 書いて覚えるための初心者自己中記事

C++ ⇒ VBA 勉強の履歴を付けるというかノート代わりに使ってます

C++ Windows プログラミング③ 書いて覚えるための初心者自己中記事

前回Windowsデベロッパーセンターの初心者用ページについていけなくなった件。

とりあえず、ぼんやりと理解できたから先に進む。

あ、前回わからなかったのを貼ってから進む。

 

#ifndef UNICODE
#define UNICODE
#endif 
 
#include <windows.h>
 
template <class DERIVED_TYPE>//ウィンドウに関することをまとめたクラステンプレート/抽象クラスなので継承して使う
class BaseWindow
{
public:
	static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)//staticなメンバのウィンドウプロシージャ DispatchMessageはここにメッセージを渡す
	{
		DERIVED_TYPE *pThis = NULL;
 
		if (uMsg == WM_NCCREATE)
		{
			CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
			pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
			SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);//作成したウィンドウにこのクラス自体が関連付けられる?
 
			pThis->m_hwnd = hwnd;//メンバ変数m_hwndに作成したウィンドウのハンドルを登録
		}
		else
		{
			pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);//ウィンドウ作成済みのメッセージも場合は関連付けられた自身を呼び出す。(メンバ関数が使えるということ?)
		}
		if (pThis)
		{
			return pThis->HandleMessage(uMsg, wParam, lParam);//メッセージごとの処理はHandleMessageで行う
		}
		else
		{
			return DefWindowProc(hwnd, uMsg, wParam, lParam);
		}
	}
 
	BaseWindow() : m_hwnd(NULL) { }//コンストラクタ/メンバ変数m_hwndの初期化
 
	BOOL Create(//ウィンドウの作成を行うメンバ関数/m_hwndにハンドルが入ったかどうかをboolで返す
		PCWSTR lpWindowName,
		DWORD dwStyle,
		DWORD dwExStyle = 0,
		int x = CW_USEDEFAULT,
		int y = CW_USEDEFAULT,
		int nWidth = CW_USEDEFAULT,
		int nHeight = CW_USEDEFAULT,
		HWND hWndParent = 0,
		HMENU hMenu = 0
	)
	{
		WNDCLASS wc = { 0 };
 
		wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
		wc.hInstance = GetModuleHandle(NULL);
		wc.lpszClassName = ClassName();
 
		RegisterClass(&wc);
 
		m_hwnd = CreateWindowEx(
			dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
			nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
		);
 
		return (m_hwnd ? TRUE : FALSE);
	}
 
	HWND Window() const { return m_hwnd; }//m_hwndに入っているウィンドウハンドルを取得するメンバ関数
 
protected:
 
	virtual PCWSTR  ClassName() const = 0;//派生クラスで指定するための純粋仮想関数
	virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;//派生クラスで指定するための純粋仮想関数
 
	HWND m_hwnd;//作成したウィンドウのハンドルを保持するメンバ変数
};
 
class MainWindow : public BaseWindow<MainWindow>//派生クラスでごとにウィンドウを作れる
{
public:
	PCWSTR  ClassName() const { return L"Sample Window Class"; }//ウィンドウクラスの名前の設定
	LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);//ウィンドウプロシージャに来たメッセージはここに送られてくるのでこっちで処理する
};
 
 
 
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
	MainWindow win;//抽象クラステンプレート?というのか? の派生クラスを用意して実体化
 
	if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))//メンバ関数Createでウィンドウ作成
	{
		return 0;
	}
 
	ShowWindow(win.Window(), nCmdShow);//ShowWindow関数の第一引数に必要なウィンドウハンドルはメンバ関数Windowで取得できる
 
	// メッセージ ループを実行する
 
	MSG msg = {};
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
 
	return 0;
 
 
}
 
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)//ウィンドウプロシージャから渡されたメッセージの処理はここで行う
{
	switch (uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
 
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(m_hwnd, &ps);
		FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
		EndPaint(m_hwnd, &ps);
	}
	return 0;
 
	default:
		return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
	}
	return TRUE;
}
 

 

 よし、Windowsデベロッパーセンターの初心者ページ、解説文でこれは単純って書かなくてもいいじゃん・・。難しいよ・・。

 

 

つぎ、WindowsプログラムにおけるCOMの使用

 COM(component object model)

全く、知らない。

再利用可能なソフトウェアコンポーネントを作成するための仕様。???

  • グラフィックス (Direct2D)
  • テキスト (DirectWrite)
  • Windows Shell
  • リボン コントロール
  • UI アニメーション

 これらはCOMを基盤としている。いや、これらも知らない。汗

 

えっと、目的はプログラム内でCOMベースのAPIを呼び出すこと、らしい。

COMはバイナリ標準、言語標準ではない。??

COMはアプリケーションとソフトウェアコンポーネント間のバイナリインターフェイスを定義する。

ん~、ソフトウェアコンポーネントがよくわかってないから調べよう。

 

~~~~~~~~~~~~~

 

うん、なんだ、再利用可能なプログラムがあってそれらを使うためのインターフェイスがCOMなのか?

進んでみよう。

  • オブジェクトの実装とインターフェイスを分離する
  • オブジェクトの有効期間を管理する
  • オブジェクトの機能を実行時に特定する

 これらを勉強するらしい。

大丈夫かな、ついていける気がしない。

 

COM インターフェイスとは

オブジェクトがサポート可能な一連のメソッドを、実装に関する宣言なしに定義するもの。

インターフェイスによって

メソッドを呼び出すコード

メソッドを実装するコード

の間に明確な境界を設定できる。

???

これを呼び出し元を実装から切り離す、というらしい。

・・・全く分からない。

 

メソッドを呼び出すコードがアプリケーション側で

メソッドを実装するコードがオブジェクト側ということ?

 

C++で一番近いのは抽象クラスらしい。

// 以下は実際の COM ではない

// Pseudo-C++:

interface IDrawable
{
    void Draw();
};

 

説明のためのやつ。

一部のグラフィックスライブラリのオブジェクトセットが描画可能ということを示している。ということらしい。

IDrawableインターフェイスは、すべての描画可能オブジェクトがサポートすべき操作を定義している。

上記の例ではIDrawableインターフェイスがDrawという操作を定義している。

全てのインターフェイスは抽象的。なので実体は作れない。

代わりにグラフィックスライブラリがIDrawableインターフェイスを実装するオブジェクトを提供する。

 

C++では共通の抽象型基本クラスを継承する形で行う。

 

class Shape : public IDrawable
{
public:
    virtual void Draw();	// Draw を上書きし、実装を提供する
};

class Bitmap : public IDrawable
{
public:
    virtual void Draw();	// Draw を上書きし、実装を提供する
};

 

これらを使う場合は直接ポインターを作るのではなく

IDrawable *pDrawable = CreateTriangleShape();

if (pDrawable)
{
    pDrawable->Draw();
}

 

  IDrawableポインター経由でつかう。

 

おっと、

: このトピックで例として使われているコードは概念を示すためのものであり、実際に記述することを目的としていません。

 まじか。

 

ねむい・・。

つぎは、やっと実際の話だ。

COMライブラリを初期化する

 

HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

 

 CoInitializeEX関数でCOMライブラリを初期化する。第一引数は予約済みで常にNULL。

第二引数はプログラムが使用するスレッドモデル。

フラグ 説明
COINIT_APARTMENTTHREADED アパートメント スレッド

COINIT_MULTITHREADED

マルチスレッド

 どちらかを選択する。

  • 各 COM オブジェクトには単一のスレッドからアクセスし、複数のスレッド間で COM インターフェイス ポインターを共有しない。
  • スレッドがメッセージ ループを使用する

 ↑ これらの場合はアパートメントスレッド

ウィンドウを作成するスレッドはアパートメント

その他のスレッドはマルチスレッドが一般的らしい。

 

上記で説明したフラグに加えて、dwCoInit パラメーターに COINIT_DISABLE_OLE1DDE フラグを設定する方法も推奨されます。このフラグを設定することで、現在は使用されなくなった OLE (Object Linking and Embedding) 1.0 テクノロジに伴うオーバーヘッドの一部を回避できます。

 

 なんの事かわからんけど、やっといたほうがいいっぽい。

 

戻り値の型はHRESULT

エラーコード・成功コードを格納する。

 COMを初期化したスレッドが終わるときには初期化解除を行う

CoUninitialize();

 

つぎ。

 COMのエラーコード。

さっきのあれか、HRESULTに格納されるやつ。

HRESULT型は32ビット整数。

最上位ビットが使用されて

0が成功

1が失敗となる。

数値の範囲は下のようになる。

  • 成功コード: 0x0–0x7FFFFFFF
  • 失敗コード: 0x80000000–0xFFFFFFFF

ん、どう見るんだ?

あ、「-」 は 「~」 か。

 

COMメソッドにはHRESULT を返さないものがある。

 

HRESULTの最上位ビットを確認するためマクロがWindows SDKヘッダーに用意されている。

SUCCEEDEマクロ 成功の時にtrue

FAILDEマクロ 失敗の時にtrue

if (SUCCEEDED(hr)) {
	// 関数が成功する
}else {
	// エラーを処理する
}

 

 

長くなってきたから、いったん止める。

ここまで。