2012年6月29日金曜日

コンソールアプリケーションの作成

コンソールアプリケーションとは文字ベースのソフトの事です。
↓こんな感じのウインドウが出せます。


VC++でこの画面を作るのは簡単であり、C++を学ぶための入り口として適しています。必要なソースコードが少ないので、何がC++の最小要素なのかを理解できます。本やWebで見られる参考資料もだいたいこのモードで通用する形で書かれています。
ここではVC++2010Expressでのコンソールアプリケーションの作り方を説明しますが、仮に他のバージョンでもVC++ならだいたいこんな感じでしょう。

プロジェクト(ソリューション)の作成

VC++でどの様なアプリケーションを作る場合でも、まずはじめにやらないといけないのがプロジェクト(ソリューション)の作成です。
 プロジェクトはアプリケーションを作るために必要なファイルを管理します。ソリューションは複数のプロジェクトをまとめて管理できるフォルダの様な存在です。
上の写真はBulletというフリーの物理演算用のライブラリのソリューションの例です。一つのソリューションに複数のプロジェクトが存在して機能群(部門・部品みたいな感じ)を構成しています。

今回は一つのソリューションに一つのプロジェクトだけを作ります。

まずVC++を起動させたら左上のメニューから
ファイル→新規作成→プロジェクト・・・を選択します


次に左の枠からWin32を選択し、中央の枠からWin32コンソールアプリケーションを選びます。(左の枠がどうあれ、中央の枠の中でWin32コンソールアプリケーションが選べればOKです。)
そして下の枠にプロジェクトの名前を入れてOKボタンを押します。プロジェクトの名前がこれから作るアプリケーションの名前になると思って下さい。右下の「ソリューションディレクトリを作成」のチェックは、はずしてもOKです。


次に表示される画面で、左の枠から「アプリケーションの設定」を選択し、追加のオプションを「空のプロジェクト」にチェックします。こうしないとWindowsお勧めのお約束の雛形みたいなファイルが作られてしまい、何がC++コンソールプログラミングにおける最小要素かが分かり辛くなります。

さて、これでプロジェクトフォルダが出来ました。これはプログラムを作ってく上でファイルの保管場所になります。

ソースコードの追加


次にC++のソースコードを書くためのファイルを作成します。
左側のソリューションエクスプローラーから
ソースファイルを右クリックして→追加→新しい項目
を選択します。



左側から「コード」を選び、中央の枠から「C++ソースファイル(.cpp)」を選び、下の枠に名前を付けてから右下の追加ボタンを押します。(左側の枠がどうあれ、作るファイルがC++ファイルなら大丈夫です。)
付ける名前は全角でも半角でもOKな様ですが、念の為に半角にしておいた方がいい気がします・・・と助言するのは古い話でしょうか?



これで今名前を付けたC++のソースファイルが画面左のソリューションエクスプローラのソースファイルフォルダの中に入っている事が確認できると思います。

このファイルに

#include <stdio.h>
 int main()
 {
printf("こんにちわ世界!");

getchar();
 }
と書いてビルドして実行すると冒頭で挙げた画面が出るでしょう。

ビルドは[F7]キーを押すかメニューから「ソリューションのビルド」を選ぶと出来ます。この時ソースコードに誤りがあるとエラーが表示されます。エラーを訂正してからもう一度ビルドしましょう。 エラが-なければ[F5]キーを押すかメニューから「デバッグ」→「デバッグの開始」でプログラムを実行出来ます。
実行中のプログラムは、普通に黒いウインドウを閉じるかVC++上でShift+F5を押すかメニューの「デバッグ」→「デバッグの停止」を選択します。

ソースコード中に何かのミスで全角のスペースとかが混じってしまうと
「'0x3000': この文字を識別子で使用することはできません」
という様なエラーが表示されます。
その場合、全角スペースという見えない敵を探すハメになります。

ソースコードの説明


#include <stdio.h>
<stdio.h>は 標準Cライブラリの一つです。CやC++が使える環境なら、標準Cライブラリもだいたい使えるみたいです。標準Cライブラリに関しては様々なサイトで解説されています。例えばここ
プログラム内でprintf()やgetchar()という関数を使っているため、
#include <stdio.h>が必要です。

 int main()
 {
  ここに処理内容を既述
}
CやC++ではmain()という名前の関数からプログラムが開始する事になっています。


printf("こんにちわ世界!");
画面に文字を出力します。引数の設定次第で色々な書き方が出来ます。
詳細は例えばこのサイトとか。

getchar();
キーボードからの入力を得る関数です。ここではユーザーの入力を待つ意味で置いています。これがないとプログラムが一瞬で終了します。



2012年6月26日火曜日

プログラミングの世界で言う「環境」とは

プログラミングの世界で「環境」と言う言葉がよく使われます。例えば開発環境とか実行環境という風な使われ方をします。
開発する環境はソフトウェア開発に関わる要素です。例えばコンパイラ、ライブラリです。
実行環境は実行するソフトウェアの動作に関わる要素です。例えばパソコンや周辺機器やドライバ、OSやランタイムライブラリ等です。

情報の流れ

プログラマーが書いたソースコードは次の様な情報の旅の開始地点だと言えます。

(開発環境)
プログラマーがC++等のプログラミング言語でソースコードを作る
コンパイラにコンパイルされて機械語のオブジェクトファイルになる
リンカによってリンクされて実行ファイル.exe(いわゆるソフトウェアとかアプリケーションと呼ばれる状態)になる

(実行環境)
ユーザーがソフトを実行してプログラムはダイナミックリンクライブラリ(DLL)やランタイムライブラリ(CRT)と呼ばれる物と連携する。OSから見るとアプリケーションインスタンスやプロセスと呼ばれる状態になる
OSを構成するDLLやAPI等のソフトモジュールが機械語の命令を解釈、処理
ハードウェア(CPU、メモリ、ハードディスク、ビデオカード、ディスプレイ、周辺機器等)が信号を受け取ってさらに外部に信号を出したりして相互作用

この様にプログラミングには複雑多様な環境が関わって来ます。プログラミング言語という公的な憲法を掲げたところで実際に施策を施しているOSがそれを無視したり解釈を変える可能性がありますし、さらにOSが出した命令をパソコンや周辺機器やドライバが正しく実行するとは限りません。環境中の様々な要素が己の勢力を拡大するために、あるいは時代の流れや具体的な現状や実情に対応するために、あるいは単なるミスやエラーによって意図した情報は姿を変えます。

とは言え、メジャーなプログラミング言語で書いたソースコードは、比較的環境に捕らわれ難い存在だと言えます。ソースコードとういのは規約みたいなものなので、時代やプラットフォームが変わってもソースコードをその実行環境に合うように修正したり翻訳できるコンパイラを作ればソースコードが利用できるといえるからです。

プログラミング言語はある程度大まかな規約であり、抽象的な存在でもあります。そこから
ソースコード→オブジェクトファイル→OS,ライブラリ、実行環境、ドライバ、→ハード
という様な具合で具体的な存在になっていきます。
具体的になるほど実行環境そのものになっていくので、環境に影響を受けやすくなると言えます。

ソースコードの中にも環境に依存し易い具体的な部分と、環境に影響を受けにくい抽象的な部分が存在します。
というかプログラマーが意図的に抽象的な部分と具体的な部分を分離したソースコードを書くことで、移植性を保ったり保守管理しやすいソースコードにする事ができます。


2012年6月23日土曜日

intからワイド文字列(wchar_t)への変換

C++の標準ライブラリを使うやり方は、整数、少数等の型の種類にとらわれずに簡単です。

ここではCっぽいやり方のうちの一つを書いておきます。

まず
#include < stdlib.h >あるいは
#include < wchar.h >
とインクルードを済ませて(多分 < windws.h >でインクルードしてます。)
↓こんな感じにします。
int number = 100;//変換したい数字
wchar_t wStr[12];//変換後の文字列
_itow_s(number, wStr, 10);//変換用関数
//使用例(デバッグ窓に出力)
OutputDebugString(wStr);

変換の主役は
errno_t _itow_s(
  int value, //変換したいint
  wchar_t *buffer, //変換後の文字列を格納するポインタ
  int radix //何進数か?
);
という関数です。名前の意味はint to wchar_t(int型からwchar_t型へ)です。末尾の_sは昔あった文字列の範囲をチェックしない関数 _itow と置き換えるために新しく使った関数だという事を意味しています。

最後の引数radixは進数の基数です。例えばソースコード上のリテラルで10進数を入れたとしても、ここを16にすれば16進数表記に直した上での文字列が代入されます。

符号付きintの範囲は-2,147,483,648~2,147,483,647なのでマイナスの場合は11文字+終端文字=12の配列が必要になる可能性があります。ここではwcharという、charの倍のメモリが必要な分けですが、確保する配列の単位もwcharだから元々2倍なので普通に必要文字数+1の配列を確保すれば良い事になります。

その他、MSのデータ変換のページ

2012年6月21日木曜日

ヘッダファイルのインクルード

バージョンについて
ここではDirect3D11のチュートリアルを例に取ります。Direct3D10の場合は11の部分を10に置き換え、Direct3D10.1の場合は10_1に置き換えます。ただしD3DXに関してはD3D10.1で使うのもD3D10XでOKです。


Direct3D11のTutorial01.cppは次のファイルのインクルードから始まっています。
#include <windows.h>
#include < d3d11.h >
#include < d3dx11.h >
#include "resource.h"

各ファイルの意味は次の通りです。

<windows.h>

ウインドウズの基本的なプログラミングに必要な機能を使えるようにします。仮にDirect3Dを使わないとしてもウインドウを出す以上は必要なヘッダです。ウインドウの作成のほか、プログラミングを補助する機能が色々入った他の重要なファイルを内部でインクルードしています。下で説明するd3d11.hの中でもこれがインクルードされています。そのためここでインクルードしなくてもコンパイルできます。

< d3d11.h >  

Direct3D11の機能を使うために必要なヘッダです。なおd3d11(D3D11)とはDirect3D11の略称であり、ライブラリや関数の名前などにもこの略称が使われています。

< d3dx11.h >

Direct3DX11(略称d3dx11)というライブラリを使うためのヘッダファイルです。一文字違いでややこしいですがd3d11を補助するライブラリです。D3Dのプログラム作る場合は大抵D3DXも必要になるものですが、Tutorial01はウインドウを青く塗りつぶすだけなので実は不要です。

#include "resource.h"

メニュー、アイコン、ボタン等のウインドウズのGUIリソースを読み込むためのファイルです。VC++でプロジェクトを作ると勝手に生成されたりする奴です。Direct3Dにもリソースという用語や概念がありますが、それとは無関係です。
-----細かい話------
チュートリアルではこのタイプのリソースはアイコンしか使わないのでこのインクルード文を消して代わりに

#define IDI_TUTORIAL1 107
と書いて、プロジェクトのフォルダにあるTutorial01.rcをメモ帳で開いてそ次の2行
#include "resource.h"
IDI_TUTORIAL1 ICON "directx.ico"
だけを残して残りを全部消去してもビルドできると思います。
この時、ついでに directx.icoの部分を自分で作成したアイコンファイル名に書き換えて自分のアイコンファイルをプロジェクトのフォルダに入れておけばそのアイコンが実行ファイルに組み込まれるはずです。
ちなみにリソースやアイコンについてはWin32APIプログラミングにおけるInitWindowのページにも書きました。

なお、InitWindow()のウインドウクラス構造体でアイコンに関わる2つのメンバ( wcex.hIconとwcex.hIconSm)をNULLにしておけばシステムデフォルトのアイコンが表示されますが、この場合もresource.hをインクルードする必要はありません。
------------------

関数や定数や型の名前について

さて、Direct3Dによるプログラミングにはd3dというライブラリ以外にもd3dxとwindows.hが必要だという事が分かりました。これは逆に言えば、ソースコード中に登場する関数や型の名前は、これら3つの機能群のうちのどこかで定義されている可能性が高い訳です。
D3Dで使われる名前は頭にD3Dが、D3DXで使われる名前は頭にD3DXが付いていて、windows.hで使われる名前はそれらのいずれも付いないので所属を推察するのは容易です。
例)
D3D11_VIEWPORT → D3Dに所属
ID3D11Device        → D3Dに所属
D3DX11CompileFromFile() →D3DXに所属
CreateWindow() → windows.hに所属
WNDCLASSEX  →  windows.hに所属
HWND        →  windows.hに所属

  要点

一般的なwindowsプログラミングに必要な機能はwindows.hにまとまっています。tutorial 01はウインドウを出して一色に塗りつぶすだけの単純なプログラムなので本来Direct3Dの機能は不要ですが、今後のチュートリアルで Direct3D11の機能を使った描画を行うために、ビデオカードやウインドウの関連付け、及びDirect3D11の初期化処理の目的でd3d11.hをインクルードしていると言えます。

2012年6月19日火曜日

WndProc(ウインドウプロシージャ)

最後の関数WndProcはウインドウプロシージャと呼ばれるものです。いわゆるイベントドリブンを記述するための部分です。
Windowsのシステムは作られたウインドウに対し、事ある毎にメッセージを発行します。例えばウインドウを動かしたり最小化したり最大化したりマウスをクリックしたりキーボードを押した時などに、それを知らせるメッセージがウインドウプロシージャに届けられます。その中にはシステムによって自動的に届けられるメッセージもあれば、wWinMain()関数内で書いたPeekMessage()やGetMessage()でプログラマが自らメッセージを捕まえてDispatchMessage( &msg );によってウインドウプロシージャへ送っているものもあります。
いずれにしても これにより、ウインドウプロシージャの中でプログラマは作ったウインドウに対して何が起きたらどういう動作をさせるのかという事を記述しておく事が出来ます。

メッセージの種類は多岐に渡りますがそれを全部処理する必要はありません。各ウインドウが必要に応じて処理を記述すれば良い訳です。処理を記述しなかったメッセージは DefWindowProc()関数によってシステム内部でデフォルトの処理がなされます。
またプログラマが任意でメッセージを発行する様なコードを書く事もできます。これにより一つのウインドウが別のウインドウと通信を取る事が出来ます。

ウインドウプロシージャは複数のウインドウやボタンコントロールから共通して利用され得ますし、逆に複数のウインドウプロシージャを作ってウインドウ毎の処理を別に記述する事もあり得ます。チュートリアルにおいてはウインドウを一つだけ作り、ウインドウプロシージャも一つだけ作っています。


LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )

第1引数hWndはウインドウハンドルです。これを使えばウインドウプロシージャが複数のウインドウで共有されている場合でも、どのウインドウに対して送られて来たメッセージかを識別できます。前項、InitWindow()の説明の中でウインドウの作成に使った CreateWindow() の戻り値がウインドウハンドルです。

第2引数messageはメッセージの種類を表す番号です。
3番目と4番目の引数はメッセージの詳細を表す数値であり、2番目の引数messageの内容によってその意味が異なります。

ウインドウプロシージャに送られて来たメッセージはチュートリアルにある様にスイッチ文を使って処理するのが一般的な様です。


    switch( message )
    {
        case WM_PAINT:
            hdc = BeginPaint( hWnd, &ps );
            EndPaint( hWnd, &ps );
            break;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            break;

        default:
            return DefWindowProc( hWnd, message, wParam, lParam );
    }

2番目の引数messageは番号だと言いましたが、どの番号がどのメッセージなのか分かり易くするためにWinUser.hファイル(windows.hから読み込まれる)の中でその数字に対して名前を与えてあります。例えば
#define WM_CREATE 0x0001
#define WM_DESTROY 0x0002
#define WM_MOVE 0x0003
#define WM_SIZE 0x0005
・・・という感じです。

チュートリアル1は空のウインドウを出すだけなので処理しているメッセージはWM_PAINTとWM_DESTROYの2種類だけです。

WM_PAINT
ウインドウの描画が必要な際に送られて来るメッセージです。まずウインドウを作って画面に表示した時に描画が必要ですし、それ以外にも最小化して再び復元した時、ウインドウのサイズを変えた時などにシステムが「再描画してはいかがか?」という意図でメッセージを送ってくれます。一つの画面を描画、表示するには莫大なピクセルの色情報を保持する必要があります。そのため基本的にWindowsはアプリケーションが描画したデータを保存しません。その代わり、再描画が必要になる度にメッセージを送って来る訳です。

ちなみに静止画の場合はBeginPaintとEndPaintの間に描画処理を記述すれば良いのですが、今後のチュートリアルでは動的なモデルを表示する予定なので今後もここには何も書かないでしょう。しかしチュートリアル1から7までのプログラムは、実行中にウインドウのサイズを変えると一瞬画面がチラ付くのではないでしょうか?WM_PAINTが来た時にRender()を呼んでやればそのチラ付きはなくなるはずです。

WM_DESTROY
ユーザーがウインドウを閉じる等してウインドウが破壊された時に呼ばれます。その下の

PostQuitMessage( 0 ); がアプリケーションの終了を伝えます。具体的には WM_QUIT メッセージを発行する事になり、これによってwWinMainエントリーポイント内に書いたwhile( WM_QUIT != msg.message )が満たされる事になります。

スイッチ文の最後に
return DefWindowProc( hWnd, message, wParam, lParam );
とあります。これは我々がウインドウプロシージャ内で記述しなかったメッセージに対する一般的な処理をさせるためのものです。私は詳しく知りませんが、こういう仕組みが必要だという事は、実は我々がよく見るウインドウプロシージャの姿はWindowsが持つ膨大なメッセージ処理機構の氷山の一角に過ぎないのかも知れません・・・。その膨大なメッセージに対して、我々は必要なものだけをすくい取ってデフォルトの処理に対するオーバーロードを行なっているに過ぎないと考える事も出来ます。

さて、せっっかくなのでここで比較的良く使いそうなキーボやマウスのイベントを処理するためのコード例を書いておきます。


LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
 PAINTSTRUCT ps;
 HDC hdc;

 switch( message )
 {
 case WM_KEYDOWN:        // キーが押されたとき
  if( wParam == VK_RETURN )
  {
   MessageBox( hWnd, L"Enter" ,L"pressMessage",MB_OK );
  }
  if(wParam == VK_SHIFT)
  {
   MessageBox( hWnd, L"Shift",L"pressMessage",MB_OK );
  }
  if(wParam == VK_CONTROL)
  {
   MessageBox( hWnd, L"Ctrl",L"pressMessage",MB_OK );
  }
  if(wParam == VK_ESCAPE)
  {
   if( IDYES ==MessageBox( hWnd, L"アプリケーションを終了しますか?",L"エスケープキーが押されました",MB_YESNO ) )
    DestroyWindow(hWnd);
  }
  if(wParam == VK_SPACE)
  {
   MessageBox( hWnd, L"space",L"pressMessage",MB_OK );
  }
  if(wParam == VK_LEFT)
  {
   MessageBox( hWnd, L"←",L"pressMessage",MB_OK );
  }
  if(wParam == VK_UP)
  {
   MessageBox( hWnd, L"↑",L"pressMessage",MB_OK );
  }
  if(wParam == VK_RIGHT)
  {
   MessageBox( hWnd, L"→",L"pressMessage",MB_OK );
  }
  if(wParam == VK_DOWN)
  {
   MessageBox( hWnd, L"↓",L"pressMessage",MB_OK );
  }
  if(wParam == VK_F1)
  {
   MessageBox( hWnd, L"F1",L"pressMessage",MB_OK );
  }
  if(wParam =='A')
  {
   MessageBox( hWnd, L"a",L"pressMessage",MB_OK );
  }
  if(wParam =='D')
  {
   MessageBox( hWnd, L"d",L"pressMessage",MB_OK );
  }
  if(wParam =='W')
  {
   MessageBox( hWnd, L"w",L"pressMessage",MB_OK );
  }
  if(wParam =='S')
  {
   MessageBox( hWnd, L"s",L"pressMessage",MB_OK );
  }
  if(wParam =='1')
  {
   MessageBox( hWnd, L"1",L"pressMessage",MB_OK );
  }
  break;

  // マウスイベント
  //ダブルクリックを検知するにはウインドウスタイルでCS_DBLCLIKSのフラグを入れている必要あり。
  //例)wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
 case WM_LBUTTONDOWN:
  MessageBox( hWnd, L"マウス左ダウン",L"pressMessage",MB_OK );
  break;
 case WM_LBUTTONUP:
  MessageBox( hWnd, L"マウス左アップ",L"pressMessage",MB_OK );
  break;
 case WM_LBUTTONDBLCLK:
  MessageBox( hWnd, L"マウス左ダブルクリック",L"pressMessage",MB_OK );
  break;

 case WM_RBUTTONDOWN:
  MessageBox( hWnd, L"マウス右ダウン",L"pressMessage",MB_OK );
  break;
 case WM_RBUTTONUP:
  MessageBox( hWnd, L"マウス右アップ",L"pressMessage",MB_OK );
  break;
 case WM_RBUTTONDBLCLK:
  MessageBox( hWnd, L"マウス右ダブルクリック",L"pressMessage",MB_OK );
  break;

 case WM_MBUTTONDOWN:
  MessageBox( hWnd, L"マウス中ダウン",L"pressMessage",MB_OK );
  break;
 case WM_MBUTTONUP:
  MessageBox( hWnd, L"マウス中アップ",L"pressMessage",MB_OK );
  break;
 case WM_MBUTTONDBLCLK:
  MessageBox( hWnd, L"マウス中ダブルクリック",L"pressMessage",MB_OK );
  break;

 case WM_PAINT:
  hdc = BeginPaint( hWnd, &ps );
  EndPaint( hWnd, &ps );
  break;

 case WM_DESTROY:
  PostQuitMessage( 0 );
  break;

 default:
  return DefWindowProc( hWnd, message, wParam, lParam );
 }

return 0;
}



ちなみにMessageBox関数はメッセージを気軽に出していけるwindowsの関数です。(ただしこういったウインドウズのGUIは一般的にフルスクリーンモードとの相性が悪いです。)
コメントにも記しましたがダブルクリックのメッセージを受け取るにはウインドウクラスの登録の時に、構造体のスタイルメンバにCS_DBLCLKSのフラグを混ぜている必要があります。
各イベントが発生するとどのキーやマウスのメッセージが発生したかをメッセージボックスで教えるので、動作確認が出来る様になっています。しかしマウスをクリックした時点でメッセージボックスにフォーカスが移動してしまうので素早くEnterキーを押してからもう一度画面をクリックしないとダブルクリックの確認は出来ない仕様です。(マウスアップの確認は押しっぱなしが効きます。)

キーボードのメッセージを受け取った時は ウインドウプロシージャ第3引数wParamの値を使って具体的にどのキーが押されたのかを調べています。その値もWinUser.hで文字としての体裁が与えられています。ここで紹介したもの以外も「ウィンドウズ 仮想キー」とかでググれば出てくると思います。


ちなみにウインドウズのアプリケーションはデフォルトでAlt+F4で終了させられますが、上のコードではエスケープキー(Esc)でアプリケーションを終了させられるようにしてみました。

なお、ここで書いているDestroyWindow()はウインドウを閉じるための関数です。これによって上でも説明したWM_DESROYメッセージが発行されます。じゃぁ、DestroyWindow()を呼ばないでここで直接 WM_DESTROYメッセージが処理している様に PostQuitMessage( 0 );でアプリケーションを終了したらどうでしょうか?・・・DestroyWindow()がやっている処理はWM_DESTROYメッセージの発行以外にもあるのでそういう事はしない方がいいです。

2012年6月18日月曜日

InitWindow (ウインドウの作成)

InitWinodw()の中でウインドウの作成をしています。つまりこの関数の中にウインドウの作成に不可欠な処理が書かれています。他の2つの関数は、関数自体がエントリーポイントとウインドウプロシージャという独立した不可欠なモジュールでしたが、このInitWindowは関数自体はユーザーの定義次第であって、例えば中身さえ丸々wWinMain()の中に書いてしまえばInitWindowという関数自体は不要となります。

InitWindow関数の始めに//Register class とコメントがあります。
「クラスを登録しろ」という意味です。
 クラスといってもC++のクラスを作れという意味ではありません。しかし概念的には鋳型を作るという意味で似ています。
Windowsでウインドウを作る時にはまずウインドウクラスというものを作る事でウインドウの性質を決定します。 ウインドウズではボタンの様なコントロールもウインドウと呼ばれる部品です。ですからウインドウクラスの設定次第ではボタンを作る事も可能です。しかしボタンなどはwindowsAPIに存在するあらかじめ定義されたウインドウクラスを使えばより簡単に作る事ができます。

ここでは普通のウインドウを作ります。
ウインドウクラスを作るには WNDCLASSEX という構造体にどの様なウインドウを作りたいか設定をして、その構造体をRegisterClassEx()関数でシステム側へ登録すればOKです。WNDCLASSEX構造体の中にウインドウクラスを表す名前もあるので、一旦ウインドウクラスを登録すれば後からウインドウを作る時にその名前によってウインドウクラスを指定出来ます。一つのウインドウクラスから複数のウインドウを作る事も出来ます。

さて、ウインドウクラスを作るための情報、WNDCLASSEX構造体のメンバを解説します。

UINT  cbSiz

この構造体のバイトサイズをsizeof(WNDCLASSEX)でセットします。

UINT style 

Window Class Stylesというものをビットフラグで指定します。


 設定次第で性質が違うウインドウを作れるっぽいです。日本語の説明としてはパッとググった感じではここ(の下の方にある解説)がナイスな印象でした。

チュートリアルでは
CS_HREDRAW | CS_VREDRAW ;
となっています。その意味は

CS_HREDRAW
ウインドウの移動やサイズ調節時にクライアントエリアの幅が変われば全体を再描画する。

CS_VREDRAW
ウインドウの移動やサイズ調節時にクライアントエリアの高さが変われば 全体を再描画する。

・・・となっています。普通のウインドウを作る場合はだいたいこれでいいらしいです。

個人的に注目しているのはCS_DBLCLKSとうフラグです。これを入れておけばウインドウをダブルクリックした時にウインドウプロシージャにメッセージが通知されます。するとウインドウプロシージャのスイッチの中で

case WM_LBUTTONDBLCLK;
 マウス左ボタン ダブルクリック時の処理

case WM_RBUTTONDBLCLK:
マウス右ボタン ダブルクリック時の処理を既述

と書くことが出来ます。ダブルクリックが必要なアプリケーションを作る人は重宝するでしょう。

WNDPROC  lpfnWndPro

ウインドウプロシージャへのポインタです。 ウインドウプロシージャとは最後に説明する関数WindProc()の事です。チュートリアルでは
wcex.lpfnWndProc = WndProc;
となっている部分です。関数の名前を書く事でその関数のポインタを指定しています。もし別の名前のウインドウプロシージャを使う場合はその名前を書く必要があります。

int  sExtra

ウインドウクラス構造体の末尾に余分なメモリを確保させる場合、そのバイト数を指定します。そんなの不要という人は0を入れて下さい。余分なメモリを確保させた場合、プログラマが目的に応じてその領域を使う事が出来ます。その際に使用する関数は Set(あるいはGet)ClassWord(あるいはLong)とかです。

int  cbWndExtra 

作成した個々のウインドウインスタンスの末尾にも余分なメモリを確保させせる事が出来ます。使わない場合は0を入れておきます。使う時はバイト数を指定します。確保したメモリを使う関数は Set(あるいはGet)WindowLong とかです。

HINSTANCE  hInstance

エントリーポイント(最初の関数 wWinMain)で渡されたインスタンスハンドル(hInstance)を指定します。このハンドルを元にシステムはどのアプリケーションやDLLのインスタンスがどのウインドウクラスやウインドウを作ったのかを知る事が出来ます。

HICON  hIcon

アプリケーションのアイコンを設定します。アイコンは実行ファイルの絵は勿論、ウインドウの左上やタスクバーにも表示されている奴です。
アイコンはwindowsプログラミングにおいてリソースとして扱われます。(Direct3Dでもリソースという用語がありますが、ここで述べるリソースとは違うものです。)一般的なウィンドウズプログラミングにおけるリソースは実行ファイルに組み込まれるデータのことです。アイコンの他にもメニューの情報など、GUI系のデータがリソースとして扱われたりします。アイコンの様な画像ファイル(つまりデータ)でも、実行ファイル.exeに組み込んでしまう事でプログラムを一つのファイルとして結実させる事が出来る訳です。
チュートリアルではこのメンバは
wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 );
となっています。これでDirectXのアイコンが表示される仕組みなっています。

これを自分で描いた絵に差し替える手順は、次の様になります。
1 アイコン画像の用意
アイコンの画像の拡張子はicoです。このフォーマットの画像を作るにはそれが可能なドット絵ツールなどの画像編集系ソフトを使えば良いでしょう。私はEDGEを使いました。作った画像は拡張子が.icoとなるアイコン形式として保存します。
保存場所は VC++のプロジェクトファイルやらソースファイルが置かれているフォルダです。(設定次第なのかな?) DirectXサンプルブラウザにチュートリアル1を作らせた場合は「Tutorial01」という名前のフォルダの中にアイコンを置けば良いです。(ちなみに元からあるdirectx.icoというアイコンファイルもそこにあると思います。)

2 リソーススクリプト(リソース定義ファイル)の改造
1でアイコンを保存した場所に拡張子が.rcとなっているファイルがあると思います。チュートリアルではTutorial01.rcという名前のファイルです。これがリソーススクリプトです。これをメモ帳で開いて下さい。その中に

/////////////////////////////////////////
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.

IDI_TUTORIAL1 ICON "directx.ico"

//////////////////////////////////////
という箇所があると思います。
directx.icoの部分を自分で作ったアイコン名に差し替えてから保存します。

これでプロジェクトを改めてビルドすると、自分で作ったアイコンが使われているはずです。

ところでチュートリアルのヘッダに
#include "resource.h"
というものがあります。
このヘッダファイルの中にもリソースのIDとかが定義されていて私はよく分からんのですがVC++はこのヘッダファイルとリソーススクリプトの2つを使ってリソースを実行ファイルに組み込んでいるっぽいですね。

HCURSOR  hCursor

カーソルのリソースを指定します。 チュートリアルでは
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
となっています。これでウインドウズで標準的に使用されている定義済みのカーソル (矢印) を利用できます。

HBRUSH  hbrBackground

ウインドウの背景色を指定します。GDIのブラシを使うかシステムカラーから指定します。チュートリアルでは
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
となっています。これによってシステムカラーの中のウインドウ色を利用しています。
チュートリアルでは背景もDirect3Dで描画するので気にする必要がないでしょう。

LPCTSTR  lpszMenuName

アプリケーションに搭載させるメニューのリーソスを表す名前を指定します。 チュートリアルではメニューを使わないのでNULLを指定しています。

LPCTSTR  lpszClassName

作るクラスの名前をプログラマが任意で命名出来る文字列です。あとで説明するCreateWindow関数を使ってウインドウを作成する時、ここで登録した名前が必要になります。
一つのプロセスで何度も同じ名前のクラスを登録する事はできませんが、システムが元から定義しているボタンなどのクラス(いわゆるシステムクラス)と同じ名前を付けても構いません。その場合、システムクラスはオーバーライドされ、あなたが定義したクラスが優先的に使われる事になります。勿論、その場合でも他のアプリケーションにおいては通常のシステムクラスを使う事は阻害されません。

HICON  hIconSm

小さいアイコン用のリソースを指定します。(アイコンの大きさは、使われ方によって2種類あるのでプログラマも2種類の大きさのアイコン画像を用意する必要があります。 上で説明したhIconメンバは実は、大きいサイズのアイコンの設定だったと考えられます。 )これをNULLにしとくとシステムが勝手に大きいアイコン画像を利用したりして小さいアイコンを作るっぽいです。
ただし上のhIcomメンバで説明した様な手順でちゃんとしたアイコンファイルを一つ作っておけば、ここのメンバでも同じアイコンを指定してやればOKです。つまりhIconメンバと同じ値にしておけば良いです。ちゃんとしたアイコンファイルの中には各サイズに対応する画像が収められているからです。

--------------------

ふぅ・・・・・

構造体の情報を全部埋めたら
RegisterClassEx関数によって実際にシステムに対してクラスを登録します。 チュートリアルには
 if( !RegisterClassEx( &wcex ) )
    return E_FAIL;
とあります。万が一エラーが戻った場合はプログラムを終了させる流れとなります。

CreateWindow関数でウインドウの作成


登録したウインドウクラスの情報を元にいよいよ実際のウインドウの作成に入ります。
同じ名前のウインドウクラスは一つのプリケーションで一つしか作れません。しかし実際に作るウインドウはその一つのクラスを鋳型として複数作る事が出来ます。これはC++で言うクラスの型とそこから作るインスタンス(オブジェクト)の関係と似ています。 もちろん性質の違うウインドウを作りたい時は別の名前の、つまり別のウインドウクラスも作る必要がありますが、似た性質のウインドウ(例えば同じ様なボタンを並べる時は)、一つのウインドウクラスを複数回使い回す事になます。(・・・まぁチュートリアルでは一つのウインドウしか作りませんが。)
ウインドウを作成する時は CreateWindow()関数を使い、この引数の中で事前に登録した中で使いたいウインドウクラスの名前を指定します。その他の引数でウインドウをどこに表示させるか等のウインドウ個別の設定を付与します。

さて、チュートリアルのコードには
// Create window
RECT rc = { 0, 0, 680, 480 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
g_hWnd = CreateWindow(
L"TutorialWindowClass", // ウインドウクラス名
L"Direct3D 11 Tutorial 1: Direct3D 11 Basics", // ウインドウタイトル
WS_OVERLAPPEDWINDOW, // ウインドウスタイル
CW_USEDEFAULT, // X座標の初期値
CW_USEDEFAULT, // Y座標の初期値
rc.right - rc.left, // 幅の初期値
rc.bottom - rc.top, // 高さの初期値
NULL, // 親ウインドウのハンドル
NULL, // ウインドウメニューのハンドル
hInstance, // インスタンスハンドル
NULL ); // 作成パラメータ
if( !g_hWnd )
return E_FAIL;

ShowWindow( g_hWnd, nCmdShow ); //ウインドウを表示する

とあります。
コメントは私が付与したもんです。
説明はMSDNのCreateWindow()の解説にありますが、ここでも解説を加えます。

ウインドウクラス名
どのウインドウクラスに基づくウインドウを作るか指定します。
上で解説した RegisterClassEx()関数で登録した WNDCLASSEX構造体中のlpszClassNameと同じ名前を入れればOKです。

ウインドウタイトル
ウインドウ上部に表示される文字列を入れます。システムクラスのボタンなどを作る場合は、そのキャプションになります。

ウインドウスタイル
たかがウインドウの作成一つに設定の中にさらに設定項目がぎっしり・・・。まぁそれだけ選択の自由があるという事でしょうが・・・。
ともかく普通にタイトルがあるメインウインドウみたいなのを作りたい場合はWS_OVERLAPPEDWINDOWを指定しておけばOKです。このページ(外部リンク)にウインドウスタイルの一通りの解説があります。MSDNのページもありますね。ちなみに世の中にはCreateWindowEX関数というものもあって、こちらはさらに様々なウインドウスタイルを指定できます。ところで、ボタンコントール等、システムウインドウクラスに基づいてウインドウを作る場合、この項目はそのコントロールに特有のものを指定するためにも使われます。

X、Y座標の初期値
ウインドウの座標をウインドウの左上位置で指定します。この例の様にオーバーラップウインドウを作る場合はCW_USEDEFAULTを入れておくとシステムがデフォルトの位置を選んでくれるらしいです。・・・しかしシステムのデフォルトの位置って何処っすか?まぁよく使われているっぽいのでこれを指定しておけば悪い様にはならないでしょう。逆に安易にこのぐらいでいいのでは?と思って
(x = 200, y = 200)の座標を指定した場合、仮にユーザーが使っているモニターが200ドット未満しかない様な極小モニターだったら範囲外になる訳です。また、マルチモニター環境だとまたどういう値が正しいのか簡単には分からなかったりします(私が不勉強なんですが)。

幅、高さの初期値
ウインドウの幅と高さを指定します。
AdjustWindowRect()関数でクライアントエリアを求めています。チュートリアルにおいてはビデオカードに描画させたいピクセルサイズが幅640高さ480の長方形みたいですね。この値をそのままここの引数にセットするとウインドウ全体の大きさがそれになってしまい、実際に描画できるクライアントエリア(つまりウインドウのタイトルバーや枠などを取り除いた部分)はこれよりも若干小さい範囲となってしまいます。そこでこのAdjustWindowRect()関数を使って一旦、左上端が(0, 0)と仮定した場合のクライアント座標を求め、そこから幅と高さを計算しています。
なお、 ウィンドウの作成にCreateWindowEx 関数を使って拡張ウィンドウスタイルを指定する場合はAdjustWindowRectEx 関数を使ってクライアント座標を計算するらしいです。

親ウインドウのハンドル
アプリケーションでただ一つのウインドウを作りたい場合、NULLを指定すればOKです。
ところでCreateWindow()関数の戻り値は作ったウインドウのハンドル型となります。つまり新たに子ウインドウを作りたい場合は、この引数で親ウインドウを作った時の戻り値を指定してやれば良いのです。そうすることで親ウインドウのウインドウプロシージャに対してボタン(子ウインドウ)を押した時のメッセージなどが渡されます。

ウインドウメニューのハンドル
ウインドウに与えるメニューリソース ( よくアプリケーションの上部に並んでいるコマンド一覧 )へのハンドルを指定します。
ウインドウクラスを登録した時も構造体の中にメニューの項目がありました。ここで新たに付与した場合はこちらが優先になります。チュートリアルではメニューを使わないのでNULLにしてあります。

インスタンスハンドル

アプリケーションのインスタンスハンドルを入れます。インスタンスハンドルはwWinMain関数等のエントリーポイントでもらっている奴です。ちなみにMSDNに
Windows 95/98:ウィンドウに関連付けられたモジュールのインスタンスハンドルを指定します。

Windows NT/2000:このパラメータは無視されます。

とありましたので最近のOSでも意味は持たないのかも知れませんね。

作成パラメータ
ウインドウ固有の情報を指すポインタとかを入れておけます。チュートリアルでは使わないのでNULLです。


CreateWindow関数を呼んでエラーが戻って来なければウインドウ作成は完了です。しかしこのままではウインドウを作っただけで表示はしていないので

ShowWindow( g_hWnd, nCmdShow );
によってウインドウを表示しています。

2012年6月10日日曜日

wWinMain(エントリーポイント)


int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )

プログラムの入り口という意味でエントリーポイントとも呼ばれる関数です。普通のCのプログラムではmain()に相当します。
wWinMain()という名前の頭に付くwは、「はっは、ちょ、うぇ WinMain()関数とか」という意味ではなく、3つ目の引数 LPWSTR lpCmdLineをユニコードで受け取るという意味のwです。つまりワイド文字に由来します。 ワイド文字の解説はこのサイトが分かり易いかも知れません。 この空白ウインドウを出すだけのプログラムの様にコマンドラインを処理しないプログラムでは意味のない話です。だからwをはずして三番目の引数の型をPSTRに変えても動作します。

ところでLPWSTRとかHRESULTって何なの?
・・・と思う方もいるかも知れません。これらはC++を知っていても見慣れない単語でしょう。windowsのプログラミングではこのような独自定義の型が他にもありますが、つまるところ、これらは型です。int型だとかchar型とかと同じように型です。これらは大体複雑なクラス型ではなく、単なるデータ型なので深い意味はありません。もっと知りたい方はMSDNの
Windows におけるコーディング規約
とか 文字列を使用する
を一読しておくと良いかも知れません。

wWinMain内の引数について
ここから抜粋ーーーーーーーー
hInstance: "インスタンスへのハンドル" または "モジュールへのハンドル" と呼ばれます。オペレーティング システムは、この値を使用して、メモリに読み込まれた実行可能ファイル (EXE) を特定します。このインスタンス ハンドルは、アイコンやビットマップを読み込むなど、Windows の特定の機能に必要です。
hPrevInstance: 16 ビット Windows で使われていましたが、現在の値は常にゼロなので意味を持ちません。
lpCmdLine: コマンドライン引数を Unicode 文字列として格納します。
nCmdShow: メイン アプリケーション ウィンドウの最小化、最大化、通常表示を指定するためのフラグです。
ーーーーーーーーーーーーーーーーーー
チュートリアルでは最初と最後の引数(hInstanceとnCmdShow)だけはウインドウ表示のために
InitWindow( hInstance, nCmdShow )関数の中に送っています。
中の2つの引数は使いませんが何も書かないとコンパイラがそれで大丈夫かと警告をしてくるのでその警告を出さないためにwWinMain関数の中で
hPrevInstance;
lpCmdLine;
と、何も作用がなくても書くだけ書いておくというのが一つの手です。しかしそれはそれで意味が不明なコードとなるので、それを割けるためにこれまた何もしないマクロで包んでやっているのがチュートリアルのコードです。
UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );

何処からも参照されていない、使われていない値という意味です。

if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
  return 0;

この部分は、
1・まず関数InitWindow()を実行してウインドウを作成する。
2・その戻り値がエラーを知らせる番号だった場合はreturn 0によって関数を、ひいてはプログラム自体をここで終了させてしまう。

という意味です。
無事にウインドウが作成できた場合はプログラムはその下のメッセージループという部分に行きます。

// Main message loop
MSG msg = {0};
while( WM_QUIT != msg.message )
{
   if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }
}

これはWindowsからのサインを受け取ってウインドウプロシージャと呼ばれるシステムに渡すための部分です。 (最後にある関数WndProc()がウインドウプロシージャです。)
まずPeekMessage()という関数でメッセージをシステムから受け取っています。よくあるWinodowsのプログラムではイベント主導でプロセスが進むため、PeekMessage()関数の代わりにGetMessage()を使います。Direct3Dのプログラムでは能動的な描画処理を書くためにPeekMessage()を使ったりします。ここをもしGetMessage()に変えてしまうとメッセージが来るまで待機してしまうために、描画などの一定時間ごとに行わせたい処理を実行できません。チュートリアルではメッセージ来ない場合は
else
{
   Render();
}
によって描画処理をするコードになっています。

メッセージを受け取った時に行っている
TranslateMessage()は文字メッセージの処理に関わるらしいです。
DispatchMessage()はそのアプリケーションで定義したウインドウプロシージャに対してメッセージの送出が行われます。この一行を削除するとウインドウは表示されたとしても、まったく操作を受け付けないプログラムになってしまいます。

ところでこのメッセージループと呼ばれる部分は文字通りwhileによってループ文となっていますよね。PeekMessage()によってmsg構造体にメッセージを取得した時に終了情報( WM_QUIT )がない限りはこのコードを繰り返す事になります。
ちなみにこのWM_QUITという終了情報を送るのはウインドウプシージャー内の
PostQuitMessage( 0 );
という関数です。
つまり一般的なプログラムの終了手順は次の様なものとなります。
1・ユーザーがウインドウを閉じる
2・ウインドウプロシージャにcase WM_DESTROYなメッセージが送られて来る。
3・ PostQuitMessage( 0 );によって wWinMain()内の while(WM_QUIT != msg.message)の条件が満たされてメッセージループを抜けて return ( int )msg.wParam; までたどり着く。

2012年6月6日水曜日

基本的な学習方法は・・・

このブログでも私が気が向いた部分に関しては解説をしますが、わからない事はどんどんインターネットの検索で調べると良いかと思います。(・・・身も蓋もないですが。)

また、プログラミングの学習は自分で色々実験する事が非常に有効です。ゆっくり地道にコツコツと色んな要素を検証していく姿勢が大事だと思います。

DirectXを学習する時はチュートリアルを含むサンプル群が役に立ちます。サンプルプログラムは
DirectX SDKをインストールした時に出来るDirectX Sample Browserというプログラムから実行出来ます。

Sample Browserの左上にShowというリストボックスがあります。Direct3D10のサンプルだけを見たい場合はDirect3D10を選んで下さい。Direct3D11を勉強する場合もチュートリアル以外はDirect3D10のサンプルが基礎的な内容として役立つと思います。
試してみたいサンプルがあれば64(32)-bit Executable等のボタンから実行する事が出来ます。また をInstall Projectというボタンを押せばVC++のプロジェクトが作成されます。


情報源

Microsoft公式のMSDNのDirect3Dの情報は主にここにあります。これはDirectXSDKと同じ場所からダウンロードできるヘルプの内容と同じなのですが、個人的にはWebページの方が自分の使い慣れたブラウザで読めるのがナイスな気がします。
ただしDirectXの日本語Helpは2種類あります。
一つは「Windows DirectX グラフィック ドキュメント 日本語版 」で、上のリンクで紹介したMSDNの情報と内容が同じです。
もう一つは「DirectX ドキュメント 日本語版」 で、DirectX オーディオなどのグラフィクス以外の説明の他、Direct3Dに関わる説明もあります。例えばCOMやDXUT、D3D10のチュートリアルとD3D11のサンプル等です。こちらのHelpもネット上のどっかにもあったと思います。

また、Direct3D11はDirect3D10と基本的な構造が似ている、というかほぼ同じで追加要素がある程度だと言った方が良いかも知れません。そのせいかDirect3D11のHelpドキュメントやサンプルプログラムはDirect3D10にはない応用的な機能を中心に説明されています。Direct3D10で出来る事は大体同じようにDirect3D11でも出来るのでDirect3D11を使う場合もDirect3D10の解説を参考にすると良いでしょう。というか、最初はほとんどDirect3D10を中心に勉強すると良いかも知れません。とくにパイプラインステージの説明は重要です。

しかしながら・・・ヘルプの内容は理解し難いと思います。
理由は…
・元々、3Dプログラミングのアーキテクチャーが複雑で、量が多い。
・説明の仕方に問題がある。
・英語のHelpを日本語に訳した時点でさらに分かり難くなる。
・ヘルプでは解説されない前提となる必要知識が多い。

…等だと思います。
そこでこのサイトではプログラミング自体が初級レベルだという人でもDirect3Dのプログラミングに食い込んでいけるような解説をする予定です。

外部サイト

Direct3D10や9、C++のクラス設計、ゲーム制作に何かと役立ちそうなサイトにGameつくろー! というものがあります。
他にも探せば色々あるでしょう。

開発環境の構築とか

時代が進み、OSが変わり、VC++が変わり…段々と情報が古くなっていくものですが

2016年現在、Windows10で
Visual Studio Community 2015
(VC++で言うならVC++2015なのだろうか)
を使っていて、
とりあえず新しいプロジェクトのサンプルから
チュートリアルプロジェクトの生成と実行ができました。





何かこれからはライブラリなどが他のWindows開発用ライブラリと同じ
フォルダに入っているから
Visual StudioとかWindowsSDKが入ってる環境なら
これまでの様にプロジェクトが参照するフォルダ設定とかを
しなくても良くなったとかなのでは?







以下は昔の時代の記事です。


VisualC++
このブログで使用する統合開発環境はVisualC++2010express(以下VC++)です。
これはMicrosoftが無料で提供しています。
統合開発環境というのはソフトウェア開発を統合的に担うツールの事です。C++のソースコードはメモ帳で作成する事も不可能ではないですが、VC++の様なツールを使った方が色々な支援を受けられて便利です。また、作成したコードをコンパイルしたり、ライブラリをリンクして実行ファイルを作るのもVC++がやってくれます。
現時点でVC++は下のリンクからダウンロードできます。
http://www.microsoft.com/japan/msdn/vstudio/express/

DirectX
DirectX SDKはDirectXを用いたプログラムの開発に必要なSDK(ソフトウェア開発キット)です。DirectXを用いないWindowsプログラミングのやり方もあり、そういったプログラムは上の統合開発環境をダウンロードするだけで良いのですが、DirectXを用いたプログラムを作る場合はそのSDKもダウンロードしてインストールする必要があります。これもMicrosoftが無料で提供しています。
「DirectX SDK」等をキーワードにググれば見つかると思います。
このブログではDirectX SDK 英語版 (June 2010) を使用しています。
SDKのバージョンがアップしてもDirect3D10等の最新ではないバージョンのライブラリも含まれています。だからどのバージョンのDirect3Dを使うにしても、その時点で最新のSDKを入れとけば良いと思います。

VC++とDirectX SDKのインストールは各自の責任においてやって下さい。

VC++express2010の基本的な解説はVisual C++ 2010 Express プログラミング入門 ( 外部へのリンク )とかを参考にするといいかも知れません。
私のブログのコンソールアプリケーションの作成 でも似た様な解説があります。

私は当初、「ツール」メニューから「設定」→「上級者用の設定」というのを知らずに
「最近のVC++はなくなった機能も結構あるな・・・。製品版と差別化を図っているのかな?」とか思ってしまいました。
(;^▽^)
このサイトでも説明の便宜上、上級者用の設定を使います。



リンクするライブラリの指定

基本的な学習方法は…でも触れていますが、DirectXSDKインストール時に付いてくるサンプルブラウザから様々なサンプルプログラムを含むVC++プロジェクトを立ち上げる事が出来ます。そのプロジェクトは当然ディレクトリの設定が済んでいてすぐにビルドできます。また、同じサンプルを何度でも生成できますし、その際にソリューションやプロジェクトの名前を自分で決める事も出来ます。
つまり、これをアプリケーションの雛形の生成装置として利用できます。これが最も即効性のあるDirect3dアプリケーションの作成方法です。

一方、自分で作ったプロジェクトに対してDirect3Dを利用できるように設定する場合はどうするのか?以下ではその方法を述べます。

以下では例として Direct3D11 (d3d11.libとd3dx11.lib)を使える様に設定します。もしDirect3D10を使いたいならd3d11.libの部分をd3d10.libに、d3dx11.d3dx10.libに置き換えて読んで下さい。
もしDirect3D10.1を使いたいならd3d10.libではなくd3d10_1.libと置きましょう。


まずはこの2つのライブラリを使用することをVC++に知らせます。

  プロパティを出す
それにはまず、プロジェクトのプロパティを表示させる必要があります。プロパティではビルドに関する様々な設定を行うことが出来ます。プロパティの出し方は対象とするプロジェクトが認識されている時にはAlt+F7や、メニューの「プロジェクト」→「プロパティ」を選択すれば出せます。
対象となるプロジェクトがVC++に認識されていない様なら、メニューの「表示」からソリューションエクスプローラを表示させ、その中から対象となるプロジェクト(写真中ではTutorial01) を右クリックしてプロパティを選択します。


プロパティのウインドウは下の画像の様な感じです。
これを開いたら、左側の表示域の
「構成プロパティ」→「リンカー」→「入力」を選択します。
次に右側の表示域の
追加の依存ファイルに
d3d11.lib;d3dx11d.lib;を加えます。
これでライブラリがリンクされるようになります。

なお、リリースビルドする時には
d3dx11d.libではなく、
d3dx11.libを使うと良いでしょう。
末尾にdが付くのはデバッグ用のバージョンだからです。

リリースビルド用にライブラリを登録するには
現状ではプロパティ画面左上のボタンが「アクティブ(Debug)」になっていると思うので、「Release」に変えて、今度は
追加の依存ファイルに
d3d11.lib;d3dx11.lib;を加えます。


VC++ディレクトリの設定

さて、上記の設定により、ビルド時にDirect3D11のライブラリをリンクする設定に出来ましたが、これだけではVC++はどこにそのライブラリがあるのかが分かりません。また、ヘッダファイルの場所も分かりません。これらを登録するのがVC++ディレクトリという項目です。これも先程と同様、プロパティの画面から行えます。左側の
「構成プロパティ」→「VC++ディレクトリ」を選択し、右側の
インクルードディレクトリ
$(DXSDK_DIR)Include;
ライブラリディレクトリ
$(DXSDK_DIR)Lib\x86;
を加えます。
$(DXSDK_DIR)は環境変数という奴で、これを書くだけで自分のPCのDirectX SDKがインストールされたフォルダを指定してくれるらしいです。
なお、ライブラリディレクトリはx86の部分をx64に変えて使う場合もあるかも知れません。
これは多分、64ビット環境向けのアプリケーションをビルドする場合に使うのだと思うのですが、私はそこんとこ詳しく知りません



VC++ディレクトリの設定を汎用的な設定にする

今まで行った設定はそのプロジェクトのみに通用する設定であり、新しくプロジェクトを作った時にはその都度同様の設定が必要です。しかしVC++ディレクトリの設定は単なるリンクの探し場所ですから、これぐらいはVC++のデフォルトの設定にした方が便利に感じます。その方法としては次の様なものがあります。

今立ち上げているプロジェクトは何であっても良いのでメニューの
「表示」→「プロパティーマネージャー」を開きます。
そこでフォルダを開き、
「Microsoft.Cpp.Win32.user」をダブルクリックします。
これで出てくるプロパティに対して上で説明したように
インクルードディレクトリ
$(DXSDK_DIR)Include
ライブラリディレクトリ
$(DXSDK_DIR)Lib\x86
を指定します。

後はOKでプロパティページを閉じた後、 プロパティマネージャーのMicrosoft.Cpp.Win32.user を右クリックして保存すれば完了です。

2012年6月5日火曜日

DirectXやDirect3Dとは何か

DirectXはWindowsの開発メーカーであるMicrosoftがWindowsにおけるソフトウェア開発の手段として用意している技術の一つです。 この中でグラフィックを担当するのはDirect3Dというライブラリです。
詳しくはWikipadiaの解説をどうぞ。

名前に「3D」と付いていますが、2Dグラフィックが扱えない訳ではありません。とは言えちょこちょこっと2DCGとして利用したい場合にDirect3Dを勉強するのは大変かも知れません。そういう場合はSiv3D,OpenSiv3DやDXライブラリ等、何か他のライブラリを使うといいかも知れません。しかしDirect3Dを使えば目的に合った2D描画用のクラスやライブラリを作る事も可能です。

Direct3Dでプログラミングをする意味とは?

このサイトではwin32APIとDirect3Dを使ったプログラミングをC++で書く例を見ていきますが、これはWindowsのグラフィクスプログラミングにおいては自由度が最も高いやり方の一つだと思います。

Direct3Dはwindows用のビデオカードの根源的な機能に効率よくアクセスできるようにする目的で開発されたAPIなので、MS公式、非公式含めて様々なライブラリがDirect3Dの上に構築されていたりします。つまりグラフィックに限定するなら、最終的な成果物としてDirect3D以外のライブラリで出来てDirect3Dに出来ない事はほとんどないという事になります。

しかしWindows以外のOSでも使える移植性や
細かい設定を気にしないで使える簡便性を持った
ライブラリやツールが増えてきた今日、
Direct3Dをそのまま使う事の意義は薄れている気がします。

はじめに

大した知識もないくせに
自分用のメモを
あたかも初心者向けの標準Direct3D講座
みたいに書いていくという身勝手な試みでした。

・・・間違った内容の記事もあるかも知れないので注意してください。

Win32APIプログラミングの部分

DirectXによる描画を行わず、ただ単に空白のウインドウを出すだけならばDirectX関係の一切のコードは不要です。
ここではウインドウズの基本的なコードが何なのかを示すため、チュートリアル1からDirectXにかかわる部分を除いてみました。また、一部改行したりコメントを付けました。

#include <windows.h>
#include "resource.h"
//---------------------------------------
// Global Variables
//----------------------------------------
HWND g_hWnd = NULL;

//-----------------------------------------
// Forward declarations
//------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow );
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

//------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing
// loop. Idle time is used to render the scene.
//------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );

if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;



// Main message loop
MSG msg = {0};
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

}

return ( int )msg.wParam;
}


//--------------------------------------------
// Register class and create window
//------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Register class
WNDCLASSEX wcex;
wcex.cbSize = sizeof( WNDCLASSEX );//この構造体のサイズ
wcex.style = CS_HREDRAW | CS_VREDRAW;//ウインドウスタイル
wcex.lpfnWndProc = WndProc;//ウインドウプロシージャ
wcex.cbClsExtra = 0; //ウインドウクラスに付加したいメモリ
wcex.cbWndExtra = 0;//ウインドウに付加したいメモリ
wcex.hInstance = hInstance;//インスタンスハンドル
wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 );//アイコン
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );//カーソル
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );//背景色
wcex.lpszMenuName = NULL;//メニュー
wcex.lpszClassName = L"TutorialWindowClass";//ウインドウクラスの名前
wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_TUTORIAL1 );//アイコン(小)
if( !RegisterClassEx( &wcex ) )
return E_FAIL;

// Create window
RECT rc = { 0, 0, 680, 480 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
g_hWnd = CreateWindow(
L"TutorialWindowClass", // ウインドウクラス名
L"Direct3D 11 Tutorial 1: Direct3D 11 Basics", // ウインドウタイトル
WS_OVERLAPPEDWINDOW, // ウインドウスタイル
CW_USEDEFAULT, // X座標の初期値
CW_USEDEFAULT, // Y座標の初期値
rc.right - rc.left, // 幅の初期値
rc.bottom - rc.top, // 高さの初期値
NULL, // 親ウインドウのハンドル
NULL, // ウインドウメニューのハンドル
hInstance, // インスタンスハンドル
NULL ); // 作成パラメータ
if( !g_hWnd )
return E_FAIL;

ShowWindow( g_hWnd, nCmdShow ); //ウインドウを表示する

return S_OK;
}


//--------------------------------------------
// Called every time the application receives a message
//--------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;

switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;

case WM_DESTROY:
PostQuitMessage( 0 );
break;

default:
return DefWindowProc( hWnd, message, wParam, lParam );
}

return 0;
}


これを実行すると空白のウインドウが現れると思います。
これはいわゆるwin32APIによるプログラミングの部分なので詳しく知りたい場合はwin32APIwindowsプログラミングウインドウズAPIWindowsAPI等でググれば良いでしょう。MS公式の概要の説明もあります。

当サイトでも簡単に説明する・・・つもりが一連のチュートリアルに無関係な細かい部分まで書いてしまいました。まずはじめに大まかな話をするとコードは大きく3つの関数に分かれています。wWinMain()InitWindow()WndProc()の3つです。プログラムは最初にwWinMain()から始まります。その中で2つ目の関数InitWindow()が呼ばれます。この2つ目の関数InitWindow()の中でウインドウが作られます。最後の関数WndProc()はコード上の何処からも呼ばれていませんが、ウインドウをいじった時やマウス、キーが入力された時など、いわゆるイベント発生時にシステム側から自動的に呼ばれます。そこでアプリケーションにどのような動作をさせたいかを記述する事が出来ます。

以下のリンクでこれら3つの関数の中身を説明しようと思います。しかし3Dプログラミングとは関係ない内容が多いので、とりあえず普通のウインドウが出てくれればそれで良いよ・・・という人は読み飛ばすのもありでしょう。

wWinMain(エントリーポイント)

プログラムの入り口となる関数であり、内部でInitWindow()を呼び出してウインドウを構築し、その後メッセージループに入ります。

InitWindow(ウインドウの作成)

ウインドウクラスを登録してからウインドウを作ります。つまりWNDCLASSEX構造体をRegisterClassEx( &wcex )で登録してCreateWindow関数を呼びます。各パラメータの解説をしています。アイコンの差し替え方にも触れます。・・・普通のウインドウが作れれば良いという人にとってはかなり冗長な内容かも知れません。

WindProc(ウインドウプロシージャ)

ウインドウプロシージャの説明です。キーボードやマウスの処理、エスケープキーによるプログラムの終了の方法についても書いてみました。

2012年6月4日月曜日

プログラミング等の自分用メモ

自分用メモのページです

 

 シェルコマンドなど

vim

 

わりとプログラミングの初心者でもDirect3Dを使ってちょっとした
3Dアプリケーションが作れるような手引きを書く予定でしたが頓挫しました。
windows7時代に書いた記事です。

はじめに
DirectXやDirect3Dとは何か?
開発環境の構築
Direct3Dのバージョンと動作環境
基本的な学習方法は・・・
ヘッダファイルのインクルード
COMインターフェースの基礎知識

   リンク

MSDN:DirectX
技術記事
windows(ストア)アプリ全般

 Direct3D11

チュートリアル1~7日本語訳

 Direct3D10.1 

概要と導入



  リンク
Direct3D10のグラフィック

Direct3D 10 に関してよく寄せられる質問


 Direct2D

初期化と単純な描画コード(サンプル)
初期化と単純な描画コード(解説)
リソースの解放忘れをチェックする

DirectWriteを使った文字列の描画
大まかな概念
DrawText()を使った文字描画
DrawTextLayout()を使った文字描画

 リンク
Direct2Dについて
Direct2D の基本的な利用方法
Direct2Dの紹介
 ・Direct2Dによる描画

Direct2D入門

プログラミングガイド
 /Direct2DAPIの概要
  /API リファレンス
   /ID2D1RenderTarget
アプリケーションのパフォーマンス向上


DirectWriteの概要
___/プログラミングガイド 

____/チュートリアル
IDWriteTextFormat
IDWriteTextLayout

 

 VC++

デバッグモードとブレークポイント
デバッグウインドウへ出力する
ASSERTで警告ダイアログを出す
メモリリークを検出する
ソリューションにプロジェクトを束ねる
ライブラリのリンクをソースファイル内で指定する
関数や目的の編集箇所を探す
その他ショートカット等
ファイル入出力

 リンク
C/C++ ビルドのリファレンス
MessageBox()
VC++データ型

Windows

Win32APIプログラミングの部分
時間の測定

リンク
C++ による Windows プログラミングの学習
Windows ストア アプリの API リファレンス

 C++

C++のご案内
コンパイルとリンク
プログラミングにおける「環境」とは?
コンソールアプリケーションの作成
分割コンパイルの仕組みとか
メモリの量と情報量
型のサイズとsizeof
型はメモリと演算を規定する
ポインタ、アドレス、リファレンス(渡し)
newによるメモリの確保
数値から文字列への変換(ワイド文字)
charの数字をintへ
ビット演算によるフラグ管理
範囲for



 リンク
C言語編
C++編(言語解説)
C++編(標準ライブラリ)
ロベールのC++教室
C/C++FAQ
C/C++ リファレンス
printf
マルチバイト文字列とワイド文字列
std::vectorによる配列の動的確保
演算子とかビット演算の基礎

   未分類

intからwchar_t(ワイド文字)へ

コンパイルとリンク

コンパイル

機械が実行できる機械語の命令を人間が読み易い言葉にほぼ一対一で直して記述していくアセンブラという言語があります。
C++や、その他多くのプログラミング言語は、アセンブラよりはもっとおおまかにどのような文を書いたら機械にどのような処理をさせるかその規約を定めたものだと言えます。
アセンブラの場合は機械へ与える命令の数だけ文を書かないといけません。しかしその他のプログラミング言語はよくやる処理を簡単な文で記述できるようにしてあるので、ひとつの文を書けば、大抵、複数の機械語が生産されます。
どのようなプログラミング言語を使うにしろ、最終的にはそれが機械語の命令に翻訳される必要があります。


人間が読めるC++の例
int x, y, z;
x = 2;
y = 3;
z = x * y;

機械が読める機械語のコードの例
10001001
00111100
01000000
・・・実は適当に書きましたが、純粋な機械語は0と1の配列になっているので、普通の人は解読できません。



プログラミング言語のコードから機械語を生産(翻訳)する事をコンパイルと言います。また、それを行う装置をコンパイラと呼びます。
C++のどういう文をコンパイルするとどういう機械語に翻訳されるかはコンパイラを作る人やメーカーの裁量に任されますが、少なくともC++が規定、想定する処理が行えるような機械語の命令に翻訳されない限りはC++のコンパイラとしては不備があると見なされます。(ただし、あえて特殊な使い方が出来るようにコンパイラの提供者が独自の仕様を盛り込むことはあります。)

コンパイルを行う機能はVC++(Visual C++ とかVisual Studio)等のプログラミングの開発環境に備わっています。

なお、コンパイルが終わって0と1の2進数で表現されているバイナリファイルのコードをオブジェクトコードと言い、そのファイルをオブジェクトファイルと言います。
一方、C++の様な人間が読める状態のテキストファイルのプログラムコードはソースコードと言い、そのファイルをソースファイルと呼びます。


リンク

プログラムの作成はコンパイルで終わりではありません。大抵のプログラムは独立した存在ではなく、他の多くのプログラムから機能を得て成立しています。自分でプログラムを複数のファイルに分けて書くこともありますし、CやC++の標準ライブラリの様にライブラリと呼ばれるものを使う事もあります。

プログラマーは自分が書くC++のソースコードの冒頭に

#include "ヘッダファイルの名前"
という様な記述をします。これでファイルをインクルード(内包)出来ます。
例えばウインドウズの一般的な機能を使いたいなら
#include <windows.h>
を書きます。
#includeという既述は、その先に書いたファイルの内容をこの場所に持って来るという役割を持ちます。この例で言えば
#include <windows.h>
の一行を消して替りにwindows.hファイルの中身をこの場所に丸々コピーしても同じ事です。

多くのファイルからインクルードされる事を想定して作られたファイルには.hの拡張子を付けてその意図を示してあります。この.hが付いたファイルはヘッダファイルと呼ばれます。
ヘッダファイルの中には関数の宣言が書かれているため、ライブラリのユーザーはその関数を自分のプログラムで呼び出せるのです。

コンパイルが済んだ複数のオブジェクトファイルを連結させる作業をリンクといいます。 コンパイルをするのはコンパイラの役割でしたが、リンクをするシステムはリンカと呼びます。コンパイルやリンクを経て最終的に出来上がるファイルは実行ファイルと呼ばれます。我々が良く目にする一般的なアプリケーション、つまりアイコンの絵があって拡張子が.exeになっている奴が実行ファイルです。ご存知の様にこれをダブルクリックするとプログラムが開始します。

ソースファイルからコンパイルとリンクを経て実行ファイルを作る事を纏めてビルドと呼び、設定さえ済んでいればVC++等の統合開発環境からボタン一つで行えます。VC++ではF7キーでビルド、F5キーはビルした上にさらに出来たプログラムの実行まで行えます。

↓プログラミングの大まかな手順はこんな感じになります。

ちなみにライブラリは静的なライブラリと動的なライブラリ(ダイナミックリンクライブラリ)の2種類があり、動的な方は実行ファイルが実行されるまで実際の連結は行いません。
ここの説明がわかり易かったので引用しておきます。
-----------------
ライブラリには大きく2種類存在します。1つは「スタティックライブラリ」。これはプログラムに組み込んで最終的に実行ファイル形式内にソースを固定してしまうライブラリです。実行ファイルを作成してしまったら変更が出来ないので「スタティック(静的)」と名付けられています。拡張子はlibです。もう1つは「ダイナミックリンクライブラリ」。これは実行ファイルの中にソースを含めず、実行中にライブラリを展開してその実行バイナリコードを使用できるライブラリです。実行ファイルを作成した後もライブラリ自体の変更が可能で、それにより実行ファイルの機能を変えたり拡張できたりするので「ダイナミック(動的)」と名付けられています。拡張子はdllです。

どちらのライブラリにも一長一短があります。libの方はプログラムソースとして完全に固定してしまうので、完全に動くプログラムを保証します。しかし、作成された実行ファイルは変更ができないので、バージョンアップをするときには実行ファイルごとコンパイルしなおさなければなりません。dllの方は実行ファイルが外部ソースとしてアクセスするので、dllを丸ごと取り替えてしまえば異なる動作をさせることが可能になります。これはソフトのバージョンアップやパッチ修正を容易にします。しかし、dllによるアクセスは一般に手続きが多く、またdllが無い場合や壊れている場合に実行ファイルの動作保証ができなくなります。また、実行ファイルは完全にdllを意識した書き方になります。

-----------------

コンパイラ先生

今時のコンパイラやIDE(統合開発環境)は、コンパイル時にプログラマーが書いたソースコードに間違や不備があると、エラーを報告したり警告をしてくれます。そして具体的にソースコードのどこが誤りだったかを指摘してくれるでしょう。コンパイラにしてみれは、機械が実行できる正しい機械語のコードを作成するという使命がある訳です。それなのにプログラマーが変なコードを書くと、それをどの様に機械語に訳せば良いかが分からないので、エラーを出して書き直しを求めて来るという訳です。
VC++ではエラーや警告の文をダブルクリックするとソースコード上の問題の箇所に飛ぶ事が出来ます。
また、エラーとまで言えなくても、文法上曖昧な箇所や、解釈は可能だけど、そういう書き方は普通しないよね・・・という場所を警告してくれます。
こういったコンパイラーが出す報告は初めは読みにくかったりするのですが、多くの症例をこなす内に、理解できるようになるかと思います。
コンパイラはソースコードの翻訳者であると共に、我々にC++を教える文法の先生でもある気がします。

・・・最近はコンパイルしなくてもエディタ上に誤った文法を書いた時点で教えてくれたりもするので尚更便利になりました。