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#-その他 | タグ: , , , , , | コメントをどうぞ

はじめてのユニットテスト /テスト苦悩開発

今参加中ASP.NET MVCのプロジェクト
成果物としてユニットテストが必要で、
本格的にユニットテストを書く初めてのプロジェクトなので。
勉強会や参考書などで聞きかじった
「テスト駆動開発(TDD)」とか「テストファースト」、「保護してリファクタリング」などを試してみようと
と張り切っていたのだが、
既存のテストコードをみると
ControllerのテストでControllerから普通にWorkerService,Model,Repositoryと順に呼び出してDBにアクセスしていて
WorkerServiceのテストでもModelのテストでも同じ様にそれ以下のクラスを順番に呼び出してDBにアクセスし,同じようなテストを何度も繰り返していた

自分も最初はそれに合わせてユニットテストの様なものを書いていたのだが、
全然単体テストではないし、それに
何かの本で単体テストではDBやファイルシステムにアクセスしてはいけないと読んだ気がする。
このままでは駄目だと思い ちゃんとテストを書くことにした

しかし、初めてのユニットテストなのでどう書けばいいのかよくわからない
そこで、とりあえず テスト対象の関数以外全てをMockにして,
適切なMock関数を適切な引数で呼び出していることを保証するテストを書いてみることにした。

作るのは、ドロップダウンでテーブルを選択し、
そのテーブルとその他の関連テーブルからEntity Frameworkでデータを取得し、
それを元に計算、最終的にFileStreamResultとして返すという物なのだが、

ControllerからWorkerService,Modelまではあまり問題はなかったように思う
引数を元にテーブルに対応した計算関数のMockを呼び出し、Mockの返した値ををStream作成関数のMockに渡してしてその戻り値にファイル名を付けてFileStreamResultとして返すといった形だが
一度形がきまってしまえば修正する必要がほとんどない

しかし、Modelや、Repositoryに入ると問題が出てきた
リファクタリングをしようと思い、ソースコードを少しでも変更すると
Mock生成部分を修正する必要が出てくるのだ
引数を増やせば引数が足りないとコンパイルエラーがでるし
呼び出す関数を変えればMockが適切に呼ばれていないというエラーが出る
修正には新しく呼ぶ方の関数のMockを用意して、それを呼び出していることをチェックする必要がある。
「保護してリファクタリング」をするはずが、少しでも修正しようとするとテストコードを直す必要があるのではリファクタリングのしようがない。
これではDBにアクセスしていて実行に時間がかかるし、単体テストではないがきちんと保護されていてリファクタリングの出来る既存コードの方がまだましである。

次に、Mockを呼び出したこと自体のテストはせずに、Mockが想定通りの呼び出し方で呼び出された時に最終的に返されるであろう値でテストを行うようにしてみた、Mock関数の返すはずの値を別の関数が返してもテストは通るが、その別の関数のMockが必要なことには変わりはなく、あまり効果はなかった

どうすればいいのかわからずに悩んでいたのだが、
EnittyFrameworkの生成するDbContextの派生クラスの持つテーブルに対応したDBSetプロパティにRepositoryで直接アクセスせずに、
一度IEnumerableのプロパティに保存してAsQueryable()でIQueryableに変換してLinqにつなぐようにすれば、そのIEnumerableプロパティに普通の配列やListをわたすことで、
Repositoryの検索関数のテストをDBに接続せずに行うことができることに気付いた、
(※AsQueryableを忘れるとそのテーブルの全データを取得してからLinq to Objectで検索してしまうので注意)

この方法を利用すればDBにアクセスせずにDBにこういう値が入っているときにModelがこういう値を返すというテストが可能になり、途中で呼び出す関数などにも一切制限はかからない、
DBにアクセスせずに「保護してリファクタリング」が可能になる!
と思ったのだが、冷静に考えるとModelのテストでRepositoryにアクセスしてしまっている。
DBにアクセスしていないことを除いては最初の状態と同じな気がしてきた……

そうして、また苦悩している時に或る言葉をふと思い出した
「TDDのテストと品質保証のテストは違う」
もしかしてこれか?、これでいいのか?
この基礎中の基礎をわかっていなかったことがすべての苦悩の原因なのか?

いろいろ調べてみた結果、
多少の誤解はありそうなものの、基本的にはその考えは正しそうであるという結論に達した。


[動画で解説]和田卓人の“テスト駆動開発”講座
第8回 テスト駆動開発の「サイクル」――まず受け入れテストで土台を作る

まずDBやファイルシステムにアクセスしてしまっても、
複数のクラスや関数を横断してしまいそうでもいいので、
保護、リファクタリングのできるテストを書けばいいのだ
それから中で呼び出す処理や関数を実装し、
必要であればMockを使った単体テストも用意すればよい。
最初からあったテストコードはそのような物は存在していないのに、
一度 納品・受領されているらしいのでおそらく必須ではないだろう。
保護可能テストさえ通っていればリファクタリング中はMockによる単体テストはコメントアウトしても
大丈夫なはずだ。

とりあえずこれでテスト苦悩開発から解放されそうな気がする。

カテゴリー: ユニットテスト, C#, C#-その他 | タグ: , , , , , , , , , | コメントをどうぞ