C++ による Windows プログラミングの学習 1-3 ウィンドウ プロシージャを記述する

MSDN ライブラリ>Windows 開発>C++ による Windows プログラミングの学習>モジュール 1. 初めての Windows プログラム>ウィンドウ プロシージャを記述する
https://msdn.microsoft.com/ja-jp/library/ff381408(v=vs.85).aspx

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

ウィンドウプロシージャには戻り値LRESULT と4つの引数があります。
hwnd:ウィンドウへのハンドル
uMsg:メッセージ コード。たとえば WM_SIZE メッセージはウィンドウのサイズが変更されたことを示す。
wParam,lParam:メッセージに関する追加情報,ポインターと同じサイズを持つを格納します。正確な意味はメッセージ コードごとに異なる。
LRESULT:プログラムが Windows に返す整数値、特定のメッセージに対するプログラムの応答。正確な意味はメッセージ コードごとに異なる。

wParam/lParamはポインターと同じサイズ(32bit OR 64bit)を持つ整数値で
通常、構造体のアドレスまたは数値です。
数値の場合、合計32bit以下の複数の値が格納されている場合がありその場合は
bit演算で各値を取り出す必要があります。
またあらかじめ値を取り出すためのマクロが用意されている場合があります

ウィンドウのサイズを変更したときに発生するメッセージWM_SIZE はLParamが16bit値二つでマクロそれぞれLOWORD()、HIWORD()、で取り出すことが出来ます。
全ての処理をウィンドウプロシージャに書くのはわかりにくいため必要なデータを取り出したら別の関数に渡すようにします。

メッセージを特に処理しない場合は既定の動作としDefWindowProc()関数を呼び出します。

LRESULT CALLBACK Form::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_SIZE:
	{
		int width = LOWORD(lParam);  // 下位ワードを取得するマクロ
		int height = HIWORD(lParam); // 上位ワードを取得するマクロ

									 // メッセージに応答:
		Form::OnSize(hwnd, (UINT)wParam, width, height);
		return 0;
	};
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void Form::OnSize(HWND hwnd, unsigned int flag, int width, int height)
{
	// ウィンドウサイズ変更時の処理
}

現状メッセージを処理するための関数がFormクラスがstatic関数にになっています。
どうにかして通常のメンバ関数にしたいと思います、
最初はstd::mapなどを用いて、ウィンドウハンドルとオブジェクトを関連付けようと思ったのですが、
専用のAPIがある様です、これについては後で解説します。

カテゴリー: C++, WinAPI | タグ: , , , , | コメントをどうぞ

C++ による Windows プログラミングの学習 1-2 ウィンドウメッセージ

MSDN ライブラリ>Windows 開発>C++ による Windows プログラミングの学習>モジュール 1. 初めての Windows プログラム>ウィンドウメッセージ
https://msdn.microsoft.com/ja-jp/library/ff381405(v=vs.85).aspx

GetMessage()
https://msdn.microsoft.com/ja-jp/library/cc364699.aspx

TranslateMessage()
https://msdn.microsoft.com/ja-jp/library/cc364841.aspx

DispatchMessage()
https://msdn.microsoft.com/ja-jp/library/cc410766.aspx

// メッセージ ループを実行する
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
	TranslateMessage(&msg);
	DispatchMessage(&msg);
}

GUI アプリケーションはユーザーとオペレーティング システムによるさまざまなイベントに応答しなければなりません。

  • ユーザーによるイベント:
  • マウス クリック、キー操作、タッチスクリーン ジェスチャ,etc…

  • オペレーティング システムによるイベント:
  • プログラムの “外部” から行われ、プログラムの動作に影響を与えるすべてのイベント。
    ハードウェアデバイスの接続、スリープ、休止状態、etc…

Windows ではオペレーティング システムは、メッセージを用いてアプリケーションとの対話を行います。
ウィンドウを作成するスレッドごとに,メッセージ用のキューが作成されます。
このキューが、そのスレッドで作成されるすべてのウィンドウに向けられるメッセージを保持します。

GetMessage()メソッドで呼び出したスレッドのキューからメッセージを取り出し
DispathMessage()メソッドは、そのメッセージを処理するため、
適切なウィンドウのウィンドウプロシージャを呼び出します。
TransMessage()メソッドはキーボード入力に関連付けられており、これがキー操作 (キーを押す、キーを離す) を文字に変換します。DispatchMessage の直前に呼び出すということだけ覚えておけば、この関数の実際の処理を理解する必要はありません。

GetMessage()はキューにメッセージがなかった場合、メッセージが追加されるまで待機してから
そのメッセージを取り出します、
またメッセージが WM_QUIT の時0を返しwhileループを抜けます
それメッセージの値がそれ以外の時0以外の値を返し、ループが続きます。

カテゴリー: C++, WinAPI | タグ: , , , , | コメントをどうぞ

C++ による Windows プログラミングの学習 1-1 ウィンドウを作成する

参考資料:
MSDN ライブラリ>Windows 開発>C++ による Windows プログラミングの学習>モジュール 1. 初めての Windows プログラム>ウィンドウを作成する
https://msdn.microsoft.com/ja-jp/library/ff381397(v=vs.85).aspx

RegisterClass
https://msdn.microsoft.com/ja-jp/library/cc410975.aspx

RegisterClassEx
https://msdn.microsoft.com/ja-jp/library/cc410996.aspx

WNDCLASSEX
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
  // ウィンドウ クラスを登録する
    const wchar_t CLASS_NAME[]  = L"Sample Window Class";
    
    WNDCLASS wc = { };
    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

ウィンドウクラス(WNDCLASS)を作成してRegisterClass()に渡しています。
“ウィンドウ クラス” は、複数のウィンドウが共通して実行する一連の動作を定義するもので、C++のクラス(class Window)とは別物です。

RegisterClass/WNDCLASSはRegisterClassEx/WNDCLASSEXを使ったほうが良いらしいのでそちらを使います。
それによりWNDCLASSEXの初期化部分に以下の分の追加が必要になります。

wc.cbSize = sizeof(WNDCLASSEXW);

Application(シングルトン)クラスを作って、
エントリポイントの引数であるhInstance,nCmdShowを渡し、
RegisterClassExもApplicationクラスメンバから呼び出すようにしてみました。
hInstance: インスタンスへのハンドル” または “モジュールへのハンドル” と呼ばれます。
 オペレーティング システムは、この値を使用して、メモリに読み込まれた実行可能ファイル (EXE) を特定します。
 このインスタンス ハンドルは、アイコンやビットマップを読み込むなど、Windows の特定の機能に必要です。
nCmdShow: メイン アプリケーション ウィンドウの最小化、最大化、通常表示を指定するためのフラグです。

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
	Application::InitializeInstance(hInstance,nCmdShow);
	
	// ウィンドウ クラスを登録する
	const wchar_t CLASS_NAME[] = L"Sample Window Class";
	WNDCLASSEXW wc = {};
	wc.cbSize = sizeof(WNDCLASSEXW);
	wc.lpfnWndProc = WindowProc;
	wc.lpszClassName = CLASS_NAME;

	Application::Instance().RegisterWindowClass(wc);

	// ウィンドウを作成する
	HWND hwnd = CreateWindowEx(
		0,                              // オプションのウィンドウ スタイル
		CLASS_NAME,                     // ウィンドウ クラス
		L"Learn to Program Windows",    // ウィンドウ テキスト
		WS_OVERLAPPEDWINDOW,            // ウィンドウ スタイル
										// サイズと位置
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL,       // 親ウィンドウ    
		NULL,       // メニュー
		Application::Instance().getHandle(),  // インスタンス ハンドル
		NULL        // 追加のアプリケーション データ
		);

	if (hwnd == NULL)
	{
		return 0;
	}

	ShowWindow(hwnd, Application::Instance().getCmdShow());

	// メッセージ ループを実行する
	MSG msg = {};
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	Application::FinalizeInstance();
	return 0;
}
//Application.h
class Application
{
private:
	static Application* spAppInstance_;
public:

	static void InitializeInstance(HINSTANCE hInstance,int nCmdShow);
	static void FinalizeInstance();
	static Application& Instance();

private:
	HINSTANCE hInstance_;
	int nCmdShow_;
public:
	Application(HINSTANCE hInstance, int nCmdShow);
	~Application();

	void RegisterWindowClass(WNDCLASSEXW& exw);
	HINSTANCE getHandle()const;
	int getCmdShow() const;

};
//Application.cpp
//staticメンバのインスタンス作成
Application* Application::spAppInstance_;

void Application::InitializeInstance(HINSTANCE hInstance,int nCmdShow)
{
	FinalizeInstance();
	Application::spAppInstance_ = new Application(hInstance,nCmdShow);
}

void Application::FinalizeInstance()
{
	//インスタンスが作成済みだったら削除する
	if (Application::spAppInstance_)
	{
		delete Application::spAppInstance_;
		Application::spAppInstance_ = NULL;
	}
}

Application & Application::Instance()
{
	return *Application::spAppInstance_;
}

Application::Application(HINSTANCE hInstance, int nCmdShow)
{
	this->hInstance_ = hInstance;
	this->nCmdShow_ = nCmdShow;
}


Application::~Application()
{
}

void Application::RegisterWindowClass(WNDCLASSEXW& exw)
{
	exw.hInstance = this->hInstance_;
	::RegisterClassExW(&exw);
}

HINSTANCE Application::getHandle()const
{
	return this->hInstance_;
}

int Application::getCmdShow()const
{
	return this->nCmdShow_;
}

ウィンドウクラスの作成~ウィンドウの作成、表示をラップします。
ボタン等もウィンドウハンドルを持つウィンドウらしいのでWindowクラスじゃなくてFormクラスにしてみました。
とりあえず、WindowProc関数もFormクラスのstaticメンバにしておきます。

class Form
{
public:
	static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
	HWND hwnd_;
public:
	Form();
	virtual ~Form();

	bool Show();
};
Form::Form()
{
	// ウィンドウ クラスを登録する
	const wchar_t CLASS_NAME[] = L"Sample Window Class";
	WNDCLASSEXW wc = {};
	wc.cbSize = sizeof(WNDCLASSEXW);
	wc.lpfnWndProc = Form::WindowProc;
	wc.lpszClassName = CLASS_NAME;

	Application::Instance().RegisterWindowClass(wc);

	// ウィンドウを作成する
	this->hwnd_ = CreateWindowEx(
		0,                              // オプションのウィンドウ スタイル
		CLASS_NAME,                     // ウィンドウ クラス
		L"Learn to Program Windows",    // ウィンドウ テキスト
		WS_OVERLAPPEDWINDOW,            // ウィンドウ スタイル
										// サイズと位置
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL,       // 親ウィンドウ    
		NULL,       // メニュー
		Application::Instance().getHandle(),  // インスタンス ハンドル
		NULL        // 追加のアプリケーション データ
		);
}

Form::~Form(){}

bool Form::Show()
{
	BOOL b = ShowWindow(hwnd_, Application::Instance().getCmdShow());
	return (b != 0);
}

LRESULT CALLBACK Form::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
		EndPaint(hwnd, &ps);
	}
	return 0;

	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
	Application::InitializeInstance(hInstance,nCmdShow);
	
	Form form;
	form.Show();	

	// メッセージ ループを実行する
	MSG msg = {};
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	Application::FinalizeInstance();
	return 0;
}

最後にメッセージループもApplicationクラスに移動してRun()メソッドとしました(ソースコード略)

このままでは何もできないので次は
ウィンドウメッセージによって、
ユーザーやオペレーティング システムによるさまざまなイベントに応答します

カテゴリー: C++, WinAPI | タグ: , , , , | コメントをどうぞ

C++ による Windows プログラミングの学習 1-0 初めての Windows プログラム

参考資料:
MSDN ライブラリ>Windows 開発>C++ による Windows プログラミングの学習>モジュール 1. 初めての Windows プログラム
https://msdn.microsoft.com/ja-jp/library/ff381409(v=vs.85).aspx

まず、新しいプロジェクトを作成します。
※各操作はVisual Studio 2015での物

ファイル>新規作成>プロジェクト>Visual C++>Win32プロジェクト
でプロジェクトを作成
ウィザードが表示されるのでアプリケーションの種類で
Windows アプリケーションを選択して完了

個人的に、使いまわせそうなラッパーはライブラリ化したいので
もう一つスタティックリンクライブラリを作成しておきます。

ファイル>追加>新しいプロジェクト>Visual C++>Win32プロジェクト
でプロジェクトを追加し、今度は、アプリケーションの種類でスタティックライブラリを選択して完了します。

アプリケーションプロジェクトから、ライブラリプロジェクトを参照するように設定します。

ソリューションエクスプローラーでアプリケーションプロジェクトで右クリック
>追加>参照>参照の追加ウィンドウが表示されるので
作成したスタティックライブラリプロジェクトをチェックしてOKします。

最初から書かれているコードを削除して
MSDNからサンプルコードを貼り付けます

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
	// ウィンドウ クラスを登録する
	const wchar_t CLASS_NAME[] = L"Sample Window Class";
	WNDCLASS wc = {};
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.lpszClassName = CLASS_NAME;

	RegisterClass(&wc);

	// ウィンドウを作成する
	HWND hwnd = CreateWindowEx(
		0,                              // オプションのウィンドウ スタイル
		CLASS_NAME,                     // ウィンドウ クラス
		L"Learn to Program Windows",    // ウィンドウ テキスト
		WS_OVERLAPPEDWINDOW,            // ウィンドウ スタイル
										// サイズと位置
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL,       // 親ウィンドウ    
		NULL,       // メニュー
		hInstance,  // インスタンス ハンドル
		NULL        // 追加のアプリケーション データ
		);

	if (hwnd == NULL)
	{
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);

	// メッセージ ループを実行する
	MSG msg = {};
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
		EndPaint(hwnd, &ps);
	}
	return 0;

	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

とりあえず実行結果

カテゴリー: C++, WinAPI | タグ: , , , , | コメントをどうぞ

そろそろWindowsAPIを始めようと思う。

C#は大体抑えたのでそろそろWinAPIやCOMの勉強を始めようとおもう、

必要な参考書は大体揃えてあるのでどこから始めようかなやんだんだけど
WIN_20150829_22_15_10_Pro

MSDNにWinAPIのチュートリアルがあったので、とりあえず、
ここから始めようと思う

MSDNライブラリ>Windows開発>C++ による Windows プログラミングの学習
https://msdn.microsoft.com/ja-jp/library/ff381399(v=vs.85).aspx

そのままやるのもつまらないので、
適当にラップしたりテスト書いたりしながらやる(予定)

カテゴリー: C++, WinAPI | タグ: , , , , | コメントをどうぞ

ユニットテスト メモ

テストの目的:ソースコードを保護すること

テストではDBやファイルシステムにアクセスしないこと
目的:テストの実行時間を短くして、少しでも修正をしたら気軽にテストを実行できるようにするため。
→DBアクセス、を最低限にし、実行時間を短く保つのであれば、DBに接続してもよい
→DBからデータを取得する際の絞り込み条件などを変更する場合は、実際にDBに接続しないと保護しながらの変更は難しい。

Mock酔い対策
戻り値が別の関数(Mock)の返す値と等しいテスト
→みればわかるので省略する

条件を満たしたときMockが呼び出されてその戻り値を返す。
→条件を満たさないとき別の値を返すテストがあれば省略、

引数に渡した値が別の関数に渡されるテスト
→省略

引数に渡したオブジェクトのあるプロパティの値が別の関数に渡されるテスト
→省略

引数に渡したコレクションのデータの内条件を満たすものが別の関数に渡されるテスト
→多分必要

フラグを見て対応した関数を呼び出すテスト
→フラグ自体も関数の引数にして、関数内で分岐するように修正して呼び出すテストは省略
→呼び出される関数の方ででテストする。

カテゴリー: ユニットテスト | タグ: , , , , , , , , , , , | コメントをどうぞ

RepositoryパターンでDBに接続せずににEntity Framework (Linq to Entity)によるデータの取得をテストする。

下記のRepositoryBaseから派生してRepositoryクラスを作成
_tableプロパティからLinqをかけば、
通常はLinq to Entityでクエリ構築、検索が行われ
SetTableData()に普通の配列などを渡せば、Linq to Objectで検索が行われるので
データベースに入ってる想定のデータを渡しててやれば、
実際にはデータベースに接続せずにRepositoryのテストを行うことが出来る。
また、DBに入っている値を入力としてテストが出来るので
Repositoryの検索関数をMockにして、検索関数が返す値を入力として扱う場合より広範囲を保護することが出来る。

    public class RepositoryBase<TContext,TEntity>
        where TContext:DbContext,new()
        where TEntity : class
    {
        DbContext context;

        protected IEnumerable<TEntity> _tabledata;
        protected IQueryable<TEntity> _table { get { return _tabledata.AsQueryable(); } }
        protected IDbSet<TEntity> _dbset { get { return _tabledata as IDbSet<TEntity>; } }

        public void SetTableData(IEnumerable<TEntity> data)
        {
            _tabledata = data;
        }

        protected RepositoryBase()
        {
            context = new TContext();
            SetTableData(context.Set<TEntity>());
        
        }
    }
カテゴリー: ユニットテスト, C#, C#-その他 | タグ: , , , , , | コメントをどうぞ