2010年09月17日

第0x08回 画像の表示〜グラフィカルなアプリケーションのために〜

昨今のPCはスペックが上がり、OSやアプリケーションも
よりグラフィカルに、より美しい見た目になってきました。
よって、これからアプリケーションを作成する上で、
「見た目」も重要なファクターとなります。

その「見た目」を構成する上で重要になるのが、画像の表示です。
今回から数回に分けて、Windows上で画像を表示する方法を考えていきたいと思います。

Windowsで画像を表示する方法として、次の方法が考えられます。
@Win32APIの関数を使い、表示する(DDBとDIBなど)
ADirectXを使用し、表示する(DirectGraphicsなど)
BOpenGLを使用し、表示する
このブログでは、@Aを中心に解説していきたいと思っています。
Bは、完全に3Dとしての方法になってしまうので、省略します。

今回は、まず@のWin32APIを用いた方法を解説していきたいと思います。
Win32APIを用いてビットマップを表示するとき、
2つのタイプに描画の方法が分かれます。
ひとつはDDBで、もうひとつがDIBです。

DDB(DeviceDependentBitmap)とは、デバイス依存ビットマップと呼ばれ、
現在表示しているデバイス(=デバイスコンテキスト、HDC)に依存する形で
ビットマップを描画するものです。
DIB(DeviceIndependentBitmap)とは、デバイス独立ビットマップと呼ばれ、
DDBに対し、デバイスに依存することなくビットマップを描画します。

それぞれのメリット、デメリットは次のようになります。
〔DDB〕
 ●メリット
  現在のデバイスと親和性が高く(Compatible)、
  多くの場合、ビデオメモリ上に作成されるので
  描画が非常に高速
 ●デメリット
  CPUから遠い、ビデオメモリ上にビットマップが
  置かれるため、自前でのピクセル演算が低速(画像解析など)

〔DIB〕
 ●メリット
  メインメモリ上に確保されるため、
  自前でのピクセル演算が高速
 ●デメリット
  メインメモリ上に確保されるため、
  描画が低速


今回は、@のDDBの方法を使い画像を表示してみます。
早速サンプルを見てみましょう。


(サンプル:プロジェクトファイル)
sample008.zip

(サンプル:ソースファイルのみ)
WinMain008.cpp


まず、ビットマップのファイルをロードします。
その際に、ハンドル越しに操作を行うため、
ビットマップハンドルの変数を宣言します。
// ビットマップハンドル
HANDLE g_hBitmap = NULL;

ビットマップファイルをロードするには、下の関数を使用します。
// ビットマップをロードする
g_hBitmap = LoadImage( NULL , TEXT( "bitmap.bmp" ) , IMAGE_BITMAP , 0 , 0 , LR_LOADFROMFILE | LR_DEFAULTSIZE );
この関数は、第6引数のフラグによって、ロード元をファイルにすることができ、
フラグによっては、実行ファイルに関連付けられているリソースをロードすることもできます。

ハンドルは、その使用を終えた時点で解放してあげる必要があります。
// ビットマップを解放する
CloseHandle( g_hBitmap );

ここまでで、ビットマップのロードが完了しました。
ここからは、実際にデバイスコンテキストにビットマップを割り当て
画面のデバイスコンテキストに転送することにより表示を行います。

まず、画面と互換性のあるデバイスコンテキストを作成し、
そのデバイスコンテキストに先ほどロードしたビットマップを関連付けます。
// 互換デバイスコンテキスト作成
hMemDC = CreateCompatibleDC( hDC );
// 互換デバイスコンテキストにビットマップを設定する
SelectObject( hMemDC , g_hBitmap );

デバイスコンテキスト間の転送は下の関数を使います。
// ビットマップを表示
BitBlt( hDC , 0 , 0 , 800 , 600 , hMemDC , 0 , 0 , SRCCOPY );
BitBltの引数は次のとおりになります。
第1引数:転送先デバイスコンテキスト
第2引数:転送先左上座標X
第3引数:転送先左上座標Y
第4引数:転送サイズ幅
第5引数:転送サイズ高さ
第6引数:転送元デバイスコンテキスト
第7引数:転送元左上座標X
第8引数:転送元左上座標Y
第9引数:転送方法


以上で、DDBによるビットマップを表示することができました。
次回は、ビットマップのファイル構造について触れてみたいと思います。
タグ:ふわ猫

2010年06月04日

閑話休題その3 変数の名前、どのように名づけますか?

プログラムを書いているときに、いつも悩むことがあります。
それは変数の名前です。
ひと目でどのような変数なのかわかり、短く、センスよく名づけたいものです。
int a = 0;
スコープがものすごく限定的で、for文などの一時変数でない限り
上記のような変数は論外です。
これがクラスのメンバ変数などだったら、そのプログラムの保守は
限りなく大変なものになることが予想されます。

変数の名前は、特にプログラムの動作に影響しないので、
各プログラマによって様々な手法があると思います。
前置きとして、ここに書かれていることはあくまで私が変数をつけるときの考えであり、
これが正しいというわけではありません。


私が変数の名前を決めるときに、根底として頭によぎるのは次のことです。
●クラスのメンバ変数(フィールド)
●グローバル変数
  長さにこだわらず、なるべく意味の通る名前にする

●ローカル変数
  できるだけ短い名前にし、できれば意味の通る名前にする

外部に公開する場合や、他のクラスと連携するような、多岐にスコープが渡る場合、
長さを気にせず、とにかく意味がわかるような名前にします。
こうすることで、ドキュメントを見ずとも、雰囲気でプログラムがわかるようになります。
グローバル変数の場合には、嫌味なほど長くてもかまわないと思っています。
そもそもグローバル変数は悪であり(と私は思っています)、多用を防ぐためでもあります。

逆に、ローカル変数など、スコープが限定される場合、特に長さにはこだわらず、
むしろ短くすることを良しとします。
これは外部に出す必要がなく、その関数内で完結するため、意味を持たせることにあまり意味を持たないからです。
ローカル変数を長くすることは、必然としてプログラムが長くなってしまい冗長となってしまいます。


大文字や小文字の取り扱いにも、色々な方法があります。
代表的なものは次のとおりです。
●アンダースコア記法
  単語ごとに区切り文字としてアンダースコアを用いるものです。
  (例)is_action_state
  
●ラクダ記法
  単語ごとにその先頭文字を大文字にするものです。
  (例)IsActionState
  
●ラクダ記法(第1単語小文字)
  ラクダ記法の第1単語を小文字にするものです。
  主に動詞が小文字になります。
  (例)isActionState
これらは、特にどれが良いというものはないので、個人的な好みになります。
但し、プログラムに一貫してその記法を突き通すというのが原則です。

また、関数の名前と記法を変えることにより、
それが関数であるか変数であるかを判断しやすくなる方法があります。
例えば、関数名にラクダ記法(第1単語小文字)を用い、
変数に、アンダースコア記法を用いるといった感じです。


さて、記法のひとつとしてハンガリアン記法というものがあります。
これは、接頭文字、もしくは接尾文字に特殊な意味を持たせるというものです。
実は、Win32APIもこのハンガリアン記法に則って記述されています。

具体的には、接頭文字に変数の型を表現するというものです。
次のようなものがあります。
型     文字    例     
BOOLbまたはfbFlag
BYTEbybyCount
LONGnnNumber
DWORDdwdwSize
floatffTax
doubleddPrice
ポインタpまたはlppPointer
ハンドルhhWnd
こうすることにより、その変数がどのような型であるのか、瞬時で判断できます。

これはあくまで一例であり、変数の型を接頭文字にすることが
すなわちハンガリアン記法ではありません。
重要なのは、接頭文字に特殊な意味を持たせるというところにあります。

この部分を強調したのは、この記法にはある変数の定義が変更されたときに、
リアルの型と、記述上での型が不一致になりうる可能性があるデメリットがあるからです。
実際にマイクロソフトでは、後発の.NETFrameforkでは、この記法が廃止されています。

あくまで、型を示すものに使用するのではなく、特殊な状況が生まれる場合に使用することに
そのメリットが生まれます。
例えば、その変数がメンバであるのかローカルであるのか、ポインタであるのか、配列であるのかなど、
また、その変数が税金なのか、貯金金額なのか、利率なのかなど、
誤用しそうなところに、接頭文字を持たせることでその効果が発揮されます。
タグ:ふわ猫

2009年09月24日

第0x07回 データの保存〜ファイルアクセス〜

C言語の標準関数に定義されているファイルアクセスですが、
Win32APIでも、関数が定義されています。
どちらの関数を使っても、結局同一のWindowsネイティブの関数が呼び出されるので、
結果は同じですが、Win32APIの関数では、細かいオプション指定ができます。
まず、ざっとファイルアクセスに関する関数を見ていきましょう。


●ファイルオープン
HANDLE CreateFile(
LPCTSTR lpFileName, // ファイル名
DWORD dwDesiredAccess, // アクセスモード
DWORD dwShareMode, // 共有モード
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // セキュリティ記述子
DWORD dwCreationDisposition, // 作成方法
DWORD dwFlagsAndAttributes, // ファイル属性
HANDLE hTemplateFile // テンプレートファイルのハンドル
);

戻り値:ファイルのハンドル
(失敗時には、INVALID_HANDLE_VALUEが返る)
この関数は、ファイルのハンドルをオープンする関数です。
ファイルをロードする、ファイルを作成するなど、全ての処理は、
まずこの関数を呼び出し、ファイルハンドルをオープンしなければなりません。

dwDesiredAccessでは、オープンされるハンドルを用いて、どのようなアクセスをするか指定します。
アクセスモードとして、次の値が定義されています。
0             : デバイス属性のみアクセス
GENERIC_READ : 読み取りアクセス
GENERIC_WRITE : 書き込みアクセス

dwShareModeでは、オープンするファイルの共有方法を指定します。
この値を指定しない場合には、このファイルはロックされ、他のアプリケーションからアクセスできなくなります。
共有方法として、次の値が定義されています。
FILE_SHARE_READ   : ファイルの読み取りを許可する
FILE_SHARE_WRITE : ファイルの書き込みを許可する
FILE_SHARE_DELETE : ファイルの削除を許可する(Windows NT/2000以降)

dwCreationDispositionでは、ファイルの作成方法を指定します。
ファイルの作成方法として、次の値が定義されています。
CREATE_NEW     : 新しいファイルを作成します。指定したファイルが既に存在する場合、この関数は失敗します。
CREATE_ALWAYS : 新しいファイルを作成します。指定したファイルが既に存在する場合、上書きされます。
OPEN_EXISTING : ファイルを開きます。指定したファイルが存在しない場合、この関数は失敗します。
OPEN_ALWAYS : ファイルを開きます。指定したファイルが存在しない場合、CREATE_NEWと仮定し、新たに作成します。

dwFlagsAndAttributesでは、ファイルの属性を指定します。
属性として、次の値が定義されています。
FILE_ATTRIBUTE_ARCHIVE    : アーカイブ属性
FILE_ATTRIBUTE_ENCRYPTED : ファイル暗号化属性
FILE_ATTRIBUTE_HIDDEN : 隠しファイル属性
FILE_ATTRIBUTE_NORMAL : 通常ファイル属性
FILE_ATTRIBUTE_READONLY : 読み取り専用属性


●ファイルクローズ
BOOL CloseHandle( 
HANDLE hObject // オブジェクトのハンドル
);

戻り値:(成功時)0以外の値が返る (失敗時)0が返る
CreateFile()関数でオープンしたハンドルの使用が終了した時点で、
この関数を使い、ハンドルをクローズしなければなりません。
クローズしなかったハンドルは、リソースリークとして残り、そのハンドルの処理は未定義となります。


●データ読み取り
BOOL ReadFile(
HANDLE hFile, // ファイルのハンドル
LPVOID lpBuffer, // データバッファ
DWORD nNumberOfBytesToRead, // 読み取り対象のバイト数
LPDWORD lpNumberOfBytesRead, // 読み取ったバイト数
LPOVERLAPPED lpOverlapped // オーバーラップ構造体のバッファ
);

戻り値:(成功時)0以外の値が返る (失敗時)0が返る

ファイルハンドルを使用し、ファイルのデータを読み取ります。
lpBufferは、受け取るデータのバッファになります。
nNumberOfBytesToReadは、何バイトデータを読み取るかを指定します。
lpNumberOfBytesReadは、読み取ったデータのサイズが返されます。
nNumberOfBytesToReadより小さい値の場合、何らかのエラーが発生した可能性があります。


●データ書き込み
BOOL WriteFile(
HANDLE hFile, // ファイルのハンドル
LPCVOID lpBuffer, // データバッファ
DWORD nNumberOfBytesToWrite, // 書き込み対象のバイト数
LPDWORD lpNumberOfBytesWritten, // 書き込んだバイト数
LPOVERLAPPED lpOverlapped // オーバーラップ構造体のバッファ
);

戻り値:(成功時)0以外の値が返る (失敗時)0が返る

ファイルハンドルを使用し、ファイルにデータを書き込みます。
lpBufferは、書き込むデータのバッファになります。
以降は、基本的に読み取りと同じように指定します。


●ファイルポインタ移動
DWORD SetFilePointer(
HANDLE hFile, // ファイルハンドル
LONG lDistance, // 下位オフセット
PLONG pDistanceHigh, // 上位オフセットの変数
DWORD dwMoveMethod // 移動開始点
);

戻り値:成功時には、新しいファイルポインタの位置を示す
符号なし数値の下位32ビットが返ります。
(失敗時には、INVALID_SET_FILE_POINTERが返ります)
ファイルの読み書きするポイントを移動します。
dwMoveMethodは、移動開始点をします。
値は、次のように定義されています。
FILE_BEGIN   : ファイルの先頭を移動開始点にします
FILE_CURRENT : 現在のファイルポイントを移動開始点にします
FILE_END : ファイルの終端を移動開始点にします



さて、これらを踏まえて、前回までの環境設定の保存先をファイルに変更してみます。
サンプルは次のとおりになります。


(サンプル)
sample007.zip


以前までの処理を変更し、CConfigクラスに環境設定の処理を実装しました。
メンバ変数として、環境設定の内容を保持し、クラスの生成時、破棄時に、
それぞれ、ファイルのロード、セーブを行っています。
ファイルに保存することにより、環境設定の内容がアプリケーションを終了した状態でも
保持されることになります。
タグ:ふわ猫

2009年07月16日

第0x06回 お手軽ウィンドウ〜ダイアログ〜

Windowsでは、全てウィンドウによって成り立っていることは、
これまでにお話したとおりです。
しかし、各アプリケーションでウィンドウを出し、
中に色々描画をし、処理をするということを毎回書くのは大変です。

ほとんどのアプリケーションは、
ボタンを出し、ユーザにテキストなどを入力させ、処理をする。
こんな流れになっていると思います。
Windowsでは、これら定型のインターフェースをコモンコントロールとして
用意されています。
これらコモンコントロールが集合したウィンドウのことを、ダイアログと呼びます。

それでは早速、ダイアログを作成していきましょう。


(サンプル)
sample006.zip


まず、前回同様、プロジェクトにリソースを追加します。
今回作成するのはダイアログなので、「Dialog」を選択します。

win006_001.png

すると下のようにダイアログのフォームが出てきます。
初期状態では、「OK」ボタンと「キャンセル」ボタンのみが配置されています。

win006_002.png

ここで、ダイアログのプロパティをいくつか変更します。
Center   : ダイアログを中央に配置するか(ここでは「True」を指定)
Visible : 初期状態でダイアログを表示するか(ここでは「True」を指定)
Caption : タイトルバー文字列 (ここでは「オプション」を指定)

次にコントロールを追加します。
まず下のように「Static Text」を選択し、ダイアログの適当な場所に配置してください。
スタティックとは、文字列や画像などをダイアログに配置するコントロールです。
表示する文字列は「Caption」で変更することができます。

ちなみに、「Caption」とはウィンドウのタイトルのことなのですが、
コントロールでは、表示する文字列などに置き換わります。

win006_003.png

同様に、下のようにコントロールをいくつか配置します。
今回は、「Check Box」「Edit Control」「Group Box」を使用します。
見てわかるように、その他にも様々なコントロールが用意されているので、
アプリケーションの動作に応じて、適宜配置していきます。

win006_004.png

配置が終了したら、左上のボタンを押し、ダイアログの概観を確認します。
これで、インターフェースの実装が終わりました。
このリソースを元に、実際にプログラムに組み込んでいきます。


ところで、ダイアログにはモーダルダイアログモードレスダイアログ
2種類が存在します。
モーダルダイアログとは、ダイアログが表示されている間、他の操作ができなくなる
ダイアログのことです。
例えば、ファイルを選択するダイアログなどが、これにあたります。
モードレスダイアログでは、その反対で、ダイアログが表示されている間でも、
他の操作ができます。

今回は、オプションの設定ダイアログという想定で作るので、
設定中に、他の操作をされるのは困るので、モーダルダイアログで表示します。
モーダルダイアログを表示するには、次のようにします。
DialogBox( g_hInstance , (LPCWSTR)IDD_DIALOG1 , hWnd , DialogProc );
関数の引数は次のようになります。
第1引数:アプリケーションのインスタンスを指定
第2引数:表示するダイアログのリソース名を指定
第3引数:表示するダイアログの親ウィンドウを指定
第4引数:コールバック関数を指定

コールバック関数は次のように宣言します。
BOOL CALLBACK DialogProc( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam );
基本的には、ウィンドウのコールバック関数と同様ですが、
戻り値がBOOLなことに注意してください。

DialogBox()関数でダイアログを呼び出し、
コールバック関数にダイアログに対するメッセージを処理するのが
基本的な流れになります。

今回は次のメッセージを処理します。
WM_INITDIALOG:ダイアログの初期化時に呼ばれるメッセージ
WM_COMMAND :メニュー(ボタン)が選択されたときのメッセージ
WM_INITDIALOGは、DialogBox()関数を呼び出したときに即時に呼ばれるメッセージです。
このメッセージに応答して、ダイアログの初期化を行います。
ここで、ダイアログの各アイテムの状態を設定したりします。

ダイアログに配置されているコントロールを操作するためには、
ハンドルを取得しなければなりません。
// ダイアログアイテムのハンドルを取得する
HWND hEdit = GetDlgItem( hWnd , IDC_EDIT1 );
HWND hCheck = GetDlgItem( hWnd , IDC_CHECK1 );
GetDlgItem関数を呼び出すことにより、コントロールのハンドルが取得できます。
コントロールの所有権はダイアログが持っているので、
このハンドルをReleaseする必要はありません。

エディットボックスの中に初期文字列を指定するには、
Captionの内容を変更する必要があります。
// エディットボックスにテキストを設定する
SetWindowText( hEdit , g_szName );
SetWindowText()関数は、ウィンドウのタイトルを変更する関数です。
この関数は、エディットボックスに限らず
「Static Text」や通常のウィンドウのタイトルを変更するのに使用します。

逆にエディットボックスの中の文字列を取得するには、
Captionの内容を取得する形になります。
// エディットボックスからテキストを取得する
GetWindowText( hEdit , g_szName , sizeof( g_szName ) );

同様にチェックボックスもハンドルを使い、
操作をするわけですが、実はチェックボックスに対する操作は、
Win32APIの関数としては用意されていません。
ほとんどのダイアログのコントロールに対する処理は、
関数を用いず、直接メッセージをコントロールに対して送ることによって対話します。

// チェックボックスを設定する
if( g_bCheckSound )
SendMessage( hCheck , BM_SETCHECK , (WPARAM)BST_CHECKED , 0 );
else
SendMessage( hCheck , BM_SETCHECK , (WPARAM)BST_UNCHECKED , 0 );
// チェックボックスを取得する
if( BST_CHECKED == SendMessage( hCheck , BM_GETCHECK , 0 , 0 ) )
g_bCheckSound = true;
else
g_bCheckSound = false;
SendMessage()関数は、メッセージを送る関数です。
第1引数:メッセージを送るウィンドウのハンドル
第2引数:送るメッセージ
第3引数:メッセージに関連するWPARAM
第4引数:メッセージに関連するLPARAM

チェックボックスには、次の2つのメッセージが用意されています。
BM_SETCHECK  チェックボックスを設定するメッセージ
 WPARAM:設定する値(BST_CHECKEDならチェック、BST_UNCHECKEDならチェック解除)
 LPARAM:特に使用しない

BM_GETCHECK  チェックボックスの状態を取得するメッセージ
 WPARAM:特に使用しない
 LPARAM:特に使用しない
 戻り値として、現在の状態が返ってくる

さて、通常のダイアログでは、「OK」ボタンが押されると状態が保存され、
「キャンセル」ボタンが押されると、状態が保存されないという作りになっています。
WM_COMMANDの中で、その処理を追加し、「OK」ボタンが押された場合のみ、
状態を保存するという作りにします。

そして、ダイアログを終了させるためには、次の関数を呼びます。(モーダルダイアログの場合)
// ダイアログを終了する
EndDialog( hWnd , 1 );
第2引数で指定した値は、そのままDialogBox()関数の戻り値となります。
ダイアログボックスの呼び出し元で、状態によって処理を分岐させる場合に
用いることになります。


今回は、ダイアログで設定した内容をグローバル変数に保存しました。
次回では、この設定の内容をファイルに保存するように改造してみます。
タグ:ふわ猫

2009年07月09日

閑話休題その2 64ビットCPU対応プログラミング

現在主流になっているCPUは、32/64bit両方に対応しているものが
多いと思います。
今後OSで、64bitCPU対応のものが主流になれば、自ずと32bitCPUは
廃れていくことになるでしょう。
Windowsのコンシューマ版でも、XPより64bit版が登場し、
WindowsVistaやWindows7でも32bit版、64bit版両方のバージョンが
発売されています。
今後、64bitCPUへの対応が避けては通れないものになるでしょう。
ここでは、64bitCPUに対応する上での注意点を挙げていきたいと思います。

そもそも32bitや64bitとは何を指しているのでしょうか?
これを説明するためには、CPUがどういった構造になっているのか
理解する必要があります。

CPUとは、Central Processing Unit(中央演算処理装置)の略で、
PCで発生する演算の一手を担います。

cpu_image.jpg

PCは、全てバイナリの命令群によって制御されています。
バイナリの命令は全てメモリに配置され、逐次的に実行しています。
バイナリにはそれぞれ番号に意味があり、インテルCPUでは
例えば加算を意味するADDは、0x04と定義され、
その後に加算する値が続きます。
上の図の例では、メモリの最初のアドレスで、0x04ときているので、
CPUは加算命令と判断します。
次の0x54をみて、実際に0x54を加算します。

このとき、CPUはレジスタという一時領域に計算結果を保存し
次の計算へとつなげます。
32bitCPUは、このレジスタのサイズが32bitで、
64bitCPUは、レジスタのサイズが64bitになります。

また、現在実行している命令の場所もレジスタに保存されます。
32bitCPUの時に、メモリが3GBちょいしか積めないという
問題の発端はここからきています。

これは、一度に(1命令で)計算できる幅が32bitに
限定されているということです。
仮に32bitのCPUで64bit同士の値の計算を行う場合、
まず、下位桁を計算し、次に上位桁を計算するといった
ことを行い、結果を出します。
64bitの方が高速と言われる理由は、この部分で、
64bit同士の計算が1命令で済んでしまうわけです。

ここまでは、アセンブラや機械語の話で、
C言語などの高級言語に置き換えると次のような影響をうけます。
32bitから64bitに移行した場合の、影響です。
@int型やlong型が32bitから64bitに拡張される(場合がある)
Aポインタが64bitに拡張される(場合がある)
B0xFFFFFFFFが[-1]にならない
Cアライメントが4バイトから8バイトに拡張される
DWin32APIなどの関数の一部で32bit対応のものと64bit対応のものを変更する
Eアセンブラのレジスタ、命令を書き換える

厳密にC言語の仕様を踏襲している人なら、
@などは問題にならないかと思いますが、
案外軽視しがちなのが、A〜Cあたりだと思います。
Aなどは、ポインタをintに代入するプログラムなどが
動かなくなる可能性があります。

ちなみにDは具体的には次の関数に影響がでます。
@SetWindowLong() → SetWindowLongPtr()
AGetWindowLong() → GetWindowLongPtr()


以上のことを考慮にいれてプログラムする必要があります。
タグ:ふわ猫

2009年06月30日

第0x05回 Windowsの資源〜リソース〜

ウィンドウも出し、グラフィカルな操作も行い、
やっとWindowsのプログラムらしくなってきました。
今回は、前回に出てきたペンやブラシなど、Windowsに定義されているリソースについて
踏み込んでいきたいと思います。

Windowsは、ユーザからの操作や情報の表示をグラフィカルに行っています。
このようなユーザインターフェースのことをGUI(Graphical User Interface)といいます。
Windowsでは、マウスやウィンドウ、アイコンなどを用いて、それを実現しています。
ユーザに対して、直感的でわかりやすい操作を提供している反面、
プログラマにとっては、実際の機能以外に
これらインターフェース部分の実装まで考えなければならず、頭の痛い問題となります。

しかし今までプログラムしてきたように、
Windowsでは極力意識せずにプログラムできるようになっています。
ウィンドウやメニューなど、ほとんどのアプリケーションが使用するものに関しては、
既に用意されています。
このWindows固有のオブジェクトのことをリソースと呼びます。
リソースはメモリ上に確保され、その他のメモリ領域に確保されているデータとは
別物として管理されています。(※1)

ここでは、今までに作ってきたものに、メニュー・アイコン・カーソルを追加してみます。
これら全てはリソースとして扱われています。
では、早速サンプルを見てみましょう。


(プロジェクトファイル)
sample005.zip


VisualStudioでは、リソースを統合環境上で追加することができます。
リソースを追加するには、作成中のプロジェクトを右クリックし、
メニューより「追加」→「リソース」を選択します。

win_005_001.png

追加するリソースを選択する画面が出てくるので、
メニューを選択し、「新規作成」を選択します。

win_005_002.png

メニューを作成する画面が出てきますので、適宜メニューを追加していきます。
今回は、「ファイル(&F)」と「オプション(&O)」の2つのメニューで、
さらに「ファイル(&F)」が選択された場合に、「終了(&X)」が出てくるようにします。
メニュー名の横についている(&F)とは、メニューに対するショートカットキーで、
Alt+指定のキーでそのメニューが選択されるようになります。
Windowsのアプリケーションのほぼ全てに、そのようなショートカットがついていますので、
ここでもその作法に則っています。

win_005_003.png

各メニューを追加するときに、同時にプロパティも設定します。
いくつか説明します。
ID   :メニューを識別するためのID
Caption:メニューのタイトル
Enabled:メニューの初期有効性
Grayed :メニューの初期選択可能状態
Popup :ポップアップメニューかどうか
     Trueの場合、このメニューの下にさらにメニューがくる

「オプション(&O)」と「終了(&X)」は選択するメニューですので、
PopupをFalseにします。
「ファイル(&F)」は、それ自身を選択することはなく、下に子メニューを持つので
PopupをTrueにします。

これで、メニューの準備ができました。
同様にアイコン、カーソルも追加します。

これら作成したリソースは「sample005.rc」ファイルとして保存されます。
このファイルをテキストエディタで開くと、実際にはどのように定義されているか
大まかにわかると思います。
これらの定義についてはここでは省略します。


では作成したリソースをプログラムに組み込んでいきましょう。
前準備として、先ほどのリソースと同時に作成された「resource.h」を
インクルードします。
#include "resource.h"

このヘッダでは、リソースを識別するためのIDが定義されています。
このIDを元に、プログラムでリソースを指定します。

ウィンドウにリソースを追加するには、
ウィンドウを定義するウィンドウクラスの指定を変えてあげる必要があります。
wcl.hIcon   = LoadIcon( hInstance , (LPCWSTR)IDI_ICON1 );    // ウィンドウのアイコン
wcl.hCursor = LoadCursor( hInstance , (LPCWSTR)IDC_CURSOR1 );// ウィンドウのカーソル

以前のプログラムまで、Windowsに用意されていたアイコンとカーソルを
今回作成したものに差し替えます。
リソースはリンク時に実行ファイルに含められるので、
ここでは、上記関数の第1引数で、リソースの定義されている場所、
すなわち自分自身のインスタンスを指定します。
第2引数で、resource.hに定義されているIDを文字列として渡してあげます。

さらにメニューを追加します。
wcl.lpszMenuName = (LPCWSTR)IDR_MENU1;

これで全ての準備が整いました。

メニューが選択された場合、メッセージとして飛んできますので、
メッセージハンドラにメニューに対する処理を追加します。
case WM_COMMAND:
// メニュー処理
switch( LOWORD( wParam ) )
{
case ID_MENU_EXIT: // 「終了(&X)」
// アプリケーションを終了させる
PostQuitMessage( 0 );
break;
case ID_MENU_OPTION: // 「オプション(&O)」
// とりあえずの処理
MessageBox( hWnd , TEXT("メニューのオプションが選択された") , TEXT("メニュー") , MB_OK | MB_ICONINFORMATION );
return 1;
}
break;

メニューが選択されると、WM_COMMANDメッセージが飛んできます。
このメッセージは、wParamの下位16ビットに選択された
メニューのIDが渡されますので、その値を元に条件分岐させます。

次回では、さらに「オプション(&O)」の処理として
設定画面が出せるよう、ダイアログについて触れたいと思います。

(※1)
Windows 95/98/MEなどのWin9x系では、
作成できるリソースは、4MBまでという制限があります。
Windows 2000/XP/VistaなどのWinNT系では
そのような制限がないため、開発でNTを用いてプログラムする上で
この点に注意しないと、9x系で動かなくなってしまいます。

タグ:ふわ猫

2009年06月24日

第0x04回 オセロ製作の第一歩

前回までの内容で、ウィンドウに描画するところまで解説しました。
せっかくのゲームプログラミングの解説なのに、
Win32APIの解説だけでは眠くなってしまいますので、
ここまでのまとめとして、オセロの骨組みを作成してみたいと思います。

まずは、オセロ盤、駒を表示し、盤をクリックすると駒が置かれるという
シンプルなものを作ってみましょう。
それでは、早速サンプルです。


(ソースコードのみ)
WinMain.h
WinMain.cpp
Board.h
Board.cpp

(プロジェクトファイル)
sample004.zip

class Board

オセロの盤はBoardというクラスに処理をまとめました。
これにより、ゲームに関することはこの部分を修正していくという流れに
なります。
クラスの構造を説明します。
// WM_PAINTメッセージハンドラ
void OnPaint( HWND hWnd );
// WM_LBUTTONDOWNメッセージハンドラ
void OnLButtonDown( HWND hWnd , int x , int y );
// WM_RBUTTONDOWNメッセージハンドラ
void OnRButtonDown( HWND hWnd , int x , int y );

これら3つのメンバ関数は、メッセージを処理するための関数です。
これは、メッセージのコールバック関数より呼ばれ、
引数として、そのメッセージに関連することを渡しています。
このように、メッセージ処理を委譲し、処理させる関数を
メッセージハンドラと呼びます。
今後、オセロゲームに関連するメッセージ処理は、
このメッセージハンドラに記述します。
// 置かれている石の状態
STONE_KIND m_nStone[ MASS_NUM_X ][ MASS_NUM_Y ];

これは、置かれている石の状態を示すフラグです。
2次元配列で、各マスに対応しています。
配列の中身は次の値をとります。
// 石の状態
enum STONE_KIND
{
None = 0 , // 置かれていない
Black = 1 , // 黒
White = 2 , // 白(※1)
};

Noneは、石が置かれていない状態、
BlackとWhiteは、それぞれ石が置かれている状態になります。
石を追加する場合や変更する場合、この配列のフラグを
置き換えるだけにし、描画のタイミングで石の表示状態を
一気に更新するようにします。

実際の描画処理はBoard::OnPaint()関数に記述されています。
このように、描画処理を一箇所にまとめておくと、
プログラムの見通しがよくなり、修正が簡易になります。

Board::OnLButtonDown()関数の中に入っている
以下の関数に注目してください。
InvalidateRect( hWnd , NULL , FALSE );

これは明示的にWM_PAINTメッセージを呼び出す関数です。
第1引数は、メッセージを送るウィンドウハンドルを指定します。
第2引数は、再描画する範囲をRECT型(矩形)で指定します。
NULLを指定する場合には、全体が範囲になります。
第3引数は、背景を再描画するかをTRUEもしくはFALSEで
指定します。
ウィンドウの背景描画には、ウィンドウを作成したときに指定した
次のブラシが使用されます。
wcl.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );

今回は盤を塗りつぶすため、無駄な処理を省くためにFALSEを
指定しています。

以上で、オセロのベースの部分ができました。
今後、このベースを発展させて、ゲームを作成していきたいと
思います。


(※1)
enumの最後の,(カンマ)は本来必要ないのですが、
ここではあえて含めています。
これはC言語の仕様として合法です。
こうすることにより、今後値が追加されたとしても
カンマを忘れずにすみます。

タグ:ふわ猫

2009年06月15日

閑話休題その1 国際対応プログラミング〜UNICODE〜

2009年現在、UNICODEというものが徐々に浸透しつつあります。
WindowsでもWindows2000から完全にUNICODEに対応し、
WindowsVistaやWindows7で、標準的な位置づけになっています。
これからプログラミングを行う上で、UNICODEを考慮することは
必須条件であり、特に日本語というコンピュータで扱いにくい言語を
取り扱うためには、避けられないものです。


それでは、文字列がどのように処理されているのか
見ていくことにしましょう。

C言語では文字列を次のように取り扱います。

char str[256] = "ABC日本語もオッケー";


char型は1バイトの整数です。
これを1文字として、その配列で基本的には表現されています。
ANSI(ASCII)(※1)では、'A'〜'Z'をそれぞれ0x41〜0x5A、
'a'〜'z'を0x61〜0x7Aに割り当てています。(ShiftJISでも同様です)
全角文字や特殊文字などは、2バイトを使用して表現されています。
ShiftJIS(※2)では、1バイト目に0x81〜0x0x9Fと0xE0〜0xEFまで使われ、
2バイト目に0x40〜0xFC(0x7F除く)までが割り当てられています。

はじめ、コンピュータ発祥の英語圏などではアルファベット26文字と
数字やいくつかの記号しかなく、1バイト(256種類)を
1つの文字として割り当てられていたのですが、
のちに日本語などを表現する上で、漢字など膨大にある文字を1バイトで
表現する手段がなく、2バイトに拡張していったという経緯があります。

上記str配列には1バイト文字と2バイト文字の混在状態になっています。
この混在している状態というのが厄介で、例えば

str[4] = 'D'; // 文字化けする


このようにしてしまうと、たちまち文字化けしてしまいます。
アメリカ製のプログラムで、日本語が使用できないのは、
このあたりの考慮がされていないことが、原因のひとつです。

このようなタイプの文字コードの代表は次のとおりです。

ANSI(ASCII)アメリカ圏で古くから使われている
ShiftJIS  日本語圏のWindowsで主に使われている
EUC    日本語圏のUNIX(Linux)で主に使われている
その他、国ごとに文字コードが存在する




脱線しますが、文字化けの原因としてもうひとつに
WindowsとUNIX(Linux)間での改行文字の持ち方の違いがあります。

UNIX(Linux)
"1行目です\n2行目です"

Windows
"1行目です\r\n2行目です"


Windowsでは、\rすなわち、キャリッジリターンが含まれます。
UNIX(Linux)では、キャリッジリターンは不要です。
この違いにより、WindowsのファイルをLinuxなどに転送した場合
文字化けすることがあります。


さて、国ごとに文字コードが違い、取り扱い方も違うので
複数の言語に対応することが困難になったことから
策定されたのがUNICODEという規格です。
基本的な考え方として、全ての文字を2バイト(※3)として扱い、
文字に使用できるコードを多くしたという点です。

このようなタイプの文字コードの代表は次のとおりです。

UTF-8   LinuxやMac OS Xなどの標準文字コード
UTF-16 Windowsなどで多く使われている



C言語で表現すると次のようになります。

wchar_t str[256] = L"ABC日本語もオッケー";


wchar_t型は2バイト整数です。
この2バイト整数の配列として文字列を表現しています。
これにより、さきほどの問題が解決されることになります。

str[4] = L'D'; // 文字化けしない


ちなみにLは2バイト文字列を指定するためのプレフィックスです。

また、C標準関数なども、2バイト文字列に対応するものを
使用しなくてはなりません。
例えば、次のようになります。

(非UNICODE)
char str[256] = "文字列の表示です";
printf( "%s" , str );

(UNICODE)
wchar_t str[256] = L"文字列の表示です";
wprintf( L"%s" , str );



プログラムを行う上で、以上のことを考慮すれば、
国際的に対応したプログラムを作成することが容易となります。
変数の持ち方や、関数の変更など、
既存のプログラムを修正するのは非常に困難なため、
新たに作成するプログラムは、はじめからこれらのことを
考慮したほうが、スムーズに保守することができます。



(※1)
ANSI(American National Standards Institute)
米国規格協会
アメリカにおける工業分野の標準規格

(※2)
JIS(Japanese Industrial Standards)
日本工業規格
日本における工業分野の標準規格

ANSIもJISもその国の標準規格で、
国際的な規格としてはISOなどがある。

(※3)
しかし、それでも足りず、Unicode 2.0以降では
さらなる拡張が行われている。



(参考資料)
C言語による最新アルゴリズム事典(奥村晴彦著)
技術評論社

タグ:ふわ猫

2009年06月04日

第0x03回 初めてのグラフィックプログラム

前回までで、ウィンドウを表示することができました。
実際にウィンドウを表示したら次の段階として、
ウィンドウの中に画像やテキストなどを描画するという
要求が発生します。
今回は、それらの基本的な描画について
説明していきたいと思います。

Windowsでは、テキストや画像をする際に、
ウィンドウに直接要求を出さず、一段階クッションをおき、
抽象化を行っています。
この抽象化の部分を『デバイスコンテキスト』と呼んでいます。
デバイスコンテキストを用いることで、
ウィンドウへの表示はもちろんのこと、
印刷などの作業もデバイステキストを用いて行うので、
共通化させることができるなどのメリットがあります。

描画処理 → デバイスコンテキスト →ウィンドウ
→印刷



それでは早速サンプルプログラムを見ていくことにしましょう。

sample003.cpp(ソースコードのみ)
sample003.zip(VS.NET 2008プロジェクト)


順を追って重要なポイント見ていきましょう。

case WM_PAINT:


WM_PAINTメッセージとは、ウィンドウが再描画されるべきタイミングで
呼ばれるものです。
具体的には、次のような状況でWM_PAINTメッセージが発生します。

・ウィンドウのサイズが変更された
・ウィンドウが最小化から最大化など状態が変化した
・ウィンドウで隠されていた部分が表示された

一般的にはこのWM_PAINTメッセージ処理に
ウィンドウの描画処理を記述します。

ウィンドウに描画するためには、まずウィンドウの
デバイスコンテキストを取得する必要があります。
取得する方法として、次の2種類があります。

hdc = BeginPaint( hWnd , &ps );
// ... 描画処理 ...
EndPaint( hWnd , &ps );



hdc = GetDC( hWnd );
// ... 描画処理 ...
ReleaseDC( hWnd , hdc );


上と下は共にウィンドウのデバイスコンテキストを取得する関数です。
BeginPaint()もしくはGetDC()の戻り値としてHDCが返されます。
HDCは、デバイスコンテキストのハンドルになります。
WM_PAINT内では、GetDC()関数を使うことはできないので、
BeginPaint()関数でデバイスコンテキストを取得します。
取得したデバイスコンテキストは、使用後、解放する必要があります。

デバイスコンテキストが取得できたら、それを使い実際に
描画をしていきます。
線を描画する関数は次のとおりです。

MoveToEx( hdc , 150 , 200 , NULL );
LineTo( hdc , 400 , 450 );


描画を行う関数は、基本的にデバイスコンテキストを引数として
渡します。
まず、MoveToEx()関数で現在のカレントポジションを変更します。
第2引数、第3引数で、移動するカレントポジションを指定します。
続くLineTo()関数で、実際に線を描画します。
LineTo()関数は、現在のカレントポジションから
第2引数、第3引数で指定した座標まで線を引きます。
その後、カレントポジションは指定の場所まで移動します。

四角形や、円なども同様にデバイスコンテキストを用いて
描画することができます。
これらの図形を描画する関数を
『GDI(Graphics Device Interface)』と呼びます。
また、WindowsXPで新しく登場したGDI+というものもあり、
それを使うことにより高度な表現もできるようになりました。

テキストの描画を行う関数は次のとおりです。

TextOut( hdc , 100 , 400 , szBuffer , lstrlen( szBuffer ) );


第2引数、第3引数で表示する座標を指定し、
第4引数で文字列、第5引数で文字列のサイズを指定します。
この関数は、改行することができませんので、
改行が必要な場合、適切に配置する必要があります。


これらの図形描画を行う際に、色や種類を指定することも
できます。
線の形状を指定するものを『ペン』、
塗りつぶしの形状を指定するものを『ブラシ』と呼びます。
これらは、『リソース』と呼ばれているもので、
リソースは不要になった時に必ず解放を行わなければなりません。

ペンの作成は次のとおりです。

hPen = CreatePen( PS_SOLID , 2 , RGB(0,0,255) );
// ... ペンの使用 ...
DeleteObject( hPen );


CreatePen()関数で、ペンの作成を行います。
第1引数でペンのタイプ、第2引数で太さ、第3引数で色を指定します。
戻り値で、作成されたペンのハンドルが返ってきます。
不要になった場合、DeleteObject()関数で解放を行います。

作成されたペンを実際に使用するためには、
デバイスコンテキストに関連付けを行わなければなりません。

hOldPen	= (HPEN)SelectObject( hdc , hPen );


SelectObject()関数で、デバイスコンテキストに関連付けを行います。
この関数は少し特殊で、ペンやブラシなどのリソース全てを
この関数で行います。関数は、引数で渡された変数のタイプにより
判別することになります。
戻り値としてHGDIOBJが返されますが、これは入力された引数に
応じてキャストして使用することになります。
なお、戻り値として以前に関連付けられていたペンが返されます。
元に戻す場合には、このハンドルを使いSelectObject()で
戻すことになります。
関連付けられた状態で、DeleteObject()などで解放してしまうと
予想できない状態になってしまいますので、
DeleteObject()をする前に古いリソースに戻してから
解放するのが行儀のよいプログラムと言えます。
ペンを関連付けられたことにより、
これ以降、線描画などで新たなペンを使用して
描画するようになります。


以上で、基本的な描画ができるようになりました。
次回は、メニューやアイコン、カーソルなどについて
解説したいと思います。
タグ:ふわ猫

2009年05月26日

第0x02回 対話をするWindows・メッセージ

前回では、ウィンドウの表示を行いました。
しかし、一部説明していない部分がありました。
それが『メッセージ』です。

Windowsからアプリケーションに対するアクションは、
全てメッセージで行われます。
アプリケーションでは、そのメッセージを受け取り
実際の処理を決定します。
Windowsから通知されるメッセージは膨大で、

「ウィンドウがドラックされた」
「ウィンドウがクリックされた」
「キーが入力された」
「ファイルがドロップされた」
「スクリーンセイバーを起動しようとしている」
「シャットダウンに入る」
など、ウィンドウに関することからシステムに関することまで、
様々な事象がメッセージによって通知されます。
例えば、「スクリーンセイバーを起動しようとしている」という
メッセージを受け取った場合に、アプリケーションでそれを阻止する
といったことも可能になります。

また、アプリケーションからメッセージを発行することも可能です。
これにより、他のウィンドウの動作を制御したり、
直接Windowsに対して処理を促すことができます。


メッセージは、その膨大な量があるため
全てをリアルタイムに処理することが困難です。
そのため、各アプリケーションは、メッセージキューと言われる
メッセージを一時的に保存しておく場所が提供されています。
Windowsは、メッセージキューにメッセージを送ります。
アプリケーション側では、定期的にメッセージキューの中身を参照し、
処理に余裕がある場合に、メッセージを処理していきます。

この処理は、前回のサンプルの中にあった以下の部分がそれにあたります。

MSG stMsg;
while( GetMessage( &stMsg , NULL , 0 , 0 ) != 0 )
{
TranslateMessage( &stMsg );
DispatchMessage( &stMsg );
}


GetMessage()関数で、メッセージキューからメッセージを取り出し、
同時にそのメッセージをキューから削除します。
TranslateMessage()関数で、メッセージを適切に変換し、
DispatchMessage()関数で、メッセージをアプリケーション側で
処理します。

このとき、この中で直接メッセージ処理を書かず、
一度Windowsに処理を渡し、コールバックしてもらうというのが
ミソです。
Windowsでは、1つのアプリケーションで、複数のウィンドウを
持つことが可能です。
DispatchMessage()関数は、メッセージの内容によって
どのウィンドウに送られたものなのか判断し、
適切な場所へコールバックします。
これにより、プログラマは複数のウィンドウを
素直に管理することが可能になります。

コールバック関数は次のフォーマットで定義します。

LRESULT CALLBACK WndProc( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
// 処理を行う
return 0;
}

ここではWndProc()というコールバック関数を定義しています。
ちなみに関数の名前は任意のものです。
引数について説明します。

第1引数
どのウィンドウからメッセージが送られたのかを示す
ウィンドウハンドルです。
例えば、クリックされたときの応答として、
Windowを非表示にする、などといったときにこのHWNDを使用します。

第2引数
メッセージの種類が入ります。
メッセージは実際には4バイトの符号なし整数値で、
switch文などで分岐させ、メッセージ処理を行います。
メッセージの一例として次のものがあります。
WM_LBUTTONDOWN : マウスの左ボタンが押し下げられたWM_LBUTTONUP : マウスの左ボタンが押し上げられたWM_RBUTTONDOWN : マウスの右ボタンが押し下げられたWM_RBUTTONUP : マウスの右ボタンが押し上げられた
WM_KEYDOWN : キーボタンが押し下げられた
WM_KEYUP : キーボタンが押し上げられた
WM_ACTIVATE : ウィンドウのアクティブ状態が変更されたことを通知
WM_PAINT : ウィンドウの内容を再描画するよう通知

第3引数
メッセージの内容を補足するパラメータが渡されます。
この内容はメッセージによって異なります。
例えばWM_KEYDOWNでは、入力されたキーの種類が入ります。

第4引数
メッセージの内容を補足するパラメータが渡されます。
この内容はメッセージによって異なります。
例えばWM_LBUTTONDOWNでは、マウスの座標が入ります。

ここまでが、Windowsのプログラムを行う上での作法になります。
これらはどのプログラムにも何かしらの形ででてきますので、
定型文として覚えてしまってもよいと思います。

試しに、前回のサンプルにマウスをクリックしたときのメッセージ処理を
追加してみましょう。
次のようになります。

sample002.cpp(ソースコードのみ)
sample002.zip(VS.NET 2008プロジェクト)



MessageBox( hWnd , szMessage , TEXT("確認") , MB_OK | MB_ICONINFORMATION );


これは、メッセージボックスを表示する関数です。
第1引数は、メッセージボックスの親ウィンドウハンドルを指定します。
第2引数は、メッセージボックスの本文を指定します。
第3引数は、メッセージボックスのタイトルを指定します。
第4引数は、メッセージボックスのタイプを指定します。

サンプルでは、WM_LBUTTONDOWNメッセージの処理を追加しています。
これにより、ウィンドウをクリックしたことにより
メッセージボックスを表示するプログラムが記述できました。


次回はウィンドウに線などを描画するプログラムについて
解説したいと思います。
タグ:ふわ猫

2009年05月15日

第0x01回 "Window"sの基本はWindowから

Windowsでは、たくさんのウィンドウを表示し様々な処理を行います。
どのアプリケーションでも例外なく(※1)、ウィンドウを表示します。

そこで、まずはウィンドウを表示するところから、始めていく事にしましょう。

Windowsでは、ウィンドウの表示を行うために、『Win32API』と呼ばれる
ライブラリを用います。

Win32APIとは、プログラムからWindowsに対する要求を一手に担ってくれる
橋渡し的な存在で、これによりプログラマはWindowsの中身を知ることなく
プログラミングを行うことができます。

Win32APIでは、ウィンドウ表示の他に、メニューや画像などの描画、印刷、
マルチスレッドやプロセス生成といったことまで行うことができます。

(※1)
例外はあります。タスクに常駐するアプリケーションや、
そもそもウィンドウを持たないアプリケーションです。
実装にもよりますが、大抵はウィンドウをHide(隠す)ことにより、
ウィンドウがないアプリケーションを実現しています。

では、早速ウィンドウを表示するサンプルプログラムです。

sample001.cpp(ソースコードのみ)
sample001.zip(VS.NET 2008プロジェクト)


重要となる部分を説明していきます。


#include <windows.h>


Windowsのプログラムを作成するにあたり、windows.hは必須となるヘッダファイルです。
このヘッダの中に、Windowsプログラムに必要な定義が行われています。


int WINAPI WinMain( HINSTANCE   hInstance ,
HINSTANCE hPrevInstance ,
LPSTR lpCmdLine ,
int nCmdShow )


これは、C言語のコンソールプログラムにおけるmain()にあたる、プログラムのエントリーポイントになります。

第1引数でアプリケーションのインスタンスが渡されます。
これはアプリケーションをユニークに指定するために必要で、時折Win32API側でアプリケーションを特定するために必要とされます。

第4引数でウィンドウの表示設定が渡されます。
この表示設定とは、Windowsの実行ファイルのプロパティに設定されている、実行するときに「最大化で起動する」「最小化で起動する」などの設定が渡されます。
この値を用いて、要求どおりにShowWindow()するのが、Windowsでの行儀よいプログラムと言えます。



if( !RegisterClassEx( &wcl ) )
return -1; // 失敗


この部分でウィンドウクラスを登録します。
ウィンドウクラスとは、ウィンドウの概観を決めたテンプレートのようなもので、
後のウィンドウ作成時に、このテンプレートを指定することにより、ウィンドウを作成することができます。
ウィンドウクラスは使いまわしすることができるので、同じ概観のウィンドウを複数表示したい場合、
同じウィンドウクラスを指定することで同様のウィンドウを作成することもできます。



hWnd = CreateWindow(APP_NAME ,
APP_NAME ,
WS_OVERLAPPEDWINDOW ,
0 , 0 ,
200 , 150 ,
NULL ,
NULL ,
hInstance ,
0 );


この関数は、実際にウィンドウの作成を行います。
第3引数のスタイルには次のようなものがあります。

WS_CAPTION  	タイトルバーを持つウィンドウを作成します。
WS_MAXIMIZEBOX 最大化ボタンを持つウィンドウを作成します。
WS_MINIMIZEBOX 最小化ボタンを持つウィンドウを作成します。
WS_SYSMENU タイトルバーの中にコントロールメニューを持つウィンドウを作成します。必ずWS_CAPTIONスタイルを付けて指定します。
WS_HSCROLL 水平スクロールバーを持つウィンドウを作成します。
WS_VSCROLL 垂直スクロールバーを持つウィンドウを作成します。


複数指定するには、 WS_MINIMIZEBOX | WS_SYSMENU などと、or演算子を使ってつなげます。

CreateWindow()の戻り値として、HWNDが返ってきます。
これはウィンドウハンドルで、HWNDの実体は4バイトの整数値です。
Win32APIでは、時折ハンドルを返し、Windows内部をいじることなく操作させることがあります。
ハンドルとは、車などのハンドル、操作するものという意味合いです。
ポインタと似たようなイメージです。


ShowWindow( hWnd , nCmdShow );


ウィンドウは、作成しただけでは表示されません。
実際に表示を行うためには、上記の関数を呼び出し、ウィンドウの表示状態を変更しなければなりません。
ウィンドウを最大化、最小化などの状態にするのも、このShowWindow()を使用します。


この一連の操作を行うことに晴れてウィンドウを表示できるようになりました。
後半のメッセージ処理については、次回に説明したいと思います。

ちなみに、これらのWin32APIの関数仕様については、マイクロソフト社の公開しているMSDNで確認することができます。
細かい仕様などについて、調べたい場合に利用することをお勧めします。

MSDNホーム
http://msdn.microsoft.com/ja-jp/default.aspx
タグ:ふわ猫

第0x00回 ゲームプログラミングへの誘い

プログラムに興味があり、少しゲームに興味がある方なら
一度はゲームプログラミングに興味を持ったことがあると思います。
テトリスやオセロなどや、テレビで遊ぶようなアクションやシューティングなど、
ゲームには、様々なジャンルがあり、そのどれもが魅力的です。

今回は、そのゲームを身近な環境であるWindowsを使って作成し、
解説していこうと思います。
ゲームプログラミングは、見た目では想像できないくらい
様々なロジックを使って実装されています。

Windowsに関する内部的なことを知りたくて、
その取っ掛かりとしてゲームプログラミングから入ってみたい、
そんな方に読んで頂ければ幸いです。


さて、ゲームをプログラミングする上で必要な要素がいくつかあります。
色々ありますが、一般的には次のようなものがあります。

・画像を表示する
・サウンドを再生する
・キー入力を判定する
・ネットワークで通信を行う

ネットワークに関しては必要のないゲームもありますが、
大抵のゲームはこれらの処理を行い、それを作り上げていきます。

ここでは、オセロを題材として作成しながら、
これらの要素について説明していきたいと思います。


まず、プログラムを作成する前準備として、
プログラムを記述するための統合環境が必要になります。

マイクロソフト社で、学習用の統合環境がフリーで
ダウンロードできるようになっていますので、
Windows向けC++の統合環境がない場合には、
以下のものを準備してください。

Visual Studio Express Editions
http://www.microsoft.com/japan/msdn/vstudio/Express/

準備ができましたら、さっそくプログラミングを始めていきましょう。
タグ:ふわ猫
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。