ATL/WTLのメモ書き

ホーム   プログラム  

情報が役に立ったら、ぜひ、をクリックして、ポイントを贈ってください。

ATL/WTL関連で参考にしたサイトのリンク
  サイト   概要
 Roy.'s Home Page  WTLの講座があります
 The So-Software Studio  コントロールの使い方が分かり易く解説されています。
 Virtual Truth Lab  ATLのいくつかのクラスの使い方が解説されています。

ATL/WTL
  話題更新日時   概要
 インストール   2004/03/25 WTL7.1のインストール
 デバッグ用関数   2004/03/25 ATLTRACE2,_ATL_DEBUG_INTERFACES,_ATL_DEBUG_QI
 ダイアログを作る   2004/03/25 ダイアログを作る
 Editコントロール   2004/03/25 Editコントロールから文字列を取得、出力する
 Static Text   2004/04/01 Static Textに出力する
 コンボボックス   2004/04/20 コンボボックスを使う
 2つのダイアログ   2004/04/15 モーダルダイアログと、モードレスダイアログの違い
 ダイアログの追加   2004/04/15 アプリケーションにダイアログを加える方法
 コモンダイアログ   2004/03/25 コモンダイアログ(ファイルを開く)
 Windowsの文字と文字列に関して   2004/03/31 Windowsで扱う文字と文字列の定義
 文字列関数   2004/04/26 Windowsの文字や文字列関数
 STL、標準Cについて   2004/04/22 デバッグ時には問題ないのに、リリース時にはエラー
 COMのメモリの管理   2004/04/16 [in],[out],[in,out]の意味
 CComPtr   2004/04/14 CComPtrを使う
 便利な使い方   2004/04/01 便利な方法

DirectShow
  話題更新日時   概要
 GraphEdit   2004/04/09 DirectShowを使ったアプリケーションのデバッグに便利
 DirectShow 基底クラス   2004/04/22 基底クラスを使うには
 AM_MEDIA_TYPE   2004/05/09 AM_MEDIA_TYPEを使うときの注意

WTL7.1のインストール

 このページの記述は、個人的なメモ書きのため、正しいとは限りません。
Visual Studio .NET 2003を使って、ATL/WTLを使います。

次のホームページを参考にしています。
Roy.'s Home Page
The So-Software Studio

zen
ダイアログを作る

プロジェクトの名前と、フォルダの位置を指定する。
Visual C++プロジェクトから、ATL/WTL Application Wizard を選択
Application Typeで、Dialog Basedを選択し、Generate .CPP Filesにチェック。(チェックしないと、ヘッダファイル(*.h)に記述されてしまう)

ビルドし、「デバッグなしで開始」を実行するとダイアログが表示される。(遅いマシンでは、最初は起動が遅いので注意)

リソースビューを表示し、IDD_MAINDLGのデザインを表示する。
(インストールのときに、日本語への対応を行っておいたために、ここで、リソースを日本語に変更する必要はなかった。)

・「OK」ボタンを「はい」に変える場合
「OK」ボタンをクリックして、プロパティを表示し、Captionに「はい」と入力すると、OKボタンが、はいボタンに変わります。

・新しくボタンを配置するには、左にあるツールボックスから、Buttonを選択して配置します。このとき、プロパティでIDを分かりやすい名前に変更しておきます。後で変更するのは、少々、面倒です。

・イベントを記述する
 Buttonをダブルクリックすると、クリック時の処理を記述するソースが表示されます。
ここで、例えば、
ATLTRACE2(_T("OK"));
を記述すると、実行時に、ボタンをクリックすると、"OK"が表示されます。

 他のイベントを記述するには、Buttonのプロパティを表示し、イベントコントロールを表示させます。目的のイベントの逆三角のアイコンをクリックすると、イベントを追加できます。ここから、ソースを表示させたり、コードを削除することもできます。ただし、削除はコメントになるだけです。
zen
Editコントロールから文字列を取得、出力する

 Editコントロール1の文字列を取得し、Editコントロール2へ出力します。

	CEdit cEditTest1,cEditTest2;
	TCHAR a[256];
	cEditTest1.Attach(GetDlgItem(IDC_EDIT1));
	cEditTest2.Attach(GetDlgItem(IDC_EDIT2));

	cEditTest1.GetWindowText(a,256);
	cEditTest2.SetWindowText(a);

 CStringを使えないかと思いましたが、GetWindowTextのキャストが、うまくいきません。調査中。
zen
Static Textに出力する

 Static Textに出力します。

	CStatic cLabel;
	
	cLabel.Attach(GetDlgItem(IDC_STATIC));
	cLabel.SetWindowText(_T("abc"));

 Static Textに、”abc”が表示されます。
zen
コンボボックス

 コンボボックスにリストを追加します。

	CComboBox cmbProperty;
	cmbProperty.Attach(GetDlgItem(IDC_CMB_DEVICE));

	cmbProperty.ResetContent();	 // リストを削除する。
	cmbProperty.AddString(_T("フィルタ設定"));  // リストに追加する
	cmbProperty.SetCurSel(0) // 指定の項目を表示する

 ユーザーの入力に関する操作

	int no = cmbProperty.GetCurSel(); // 選択された項目の番号を得る

 デフォルトでは、リストに項目を追加すると、並べ替えられてしまうことに注意します。並べ替えを止めるには、プロパティで、SortをFalseにします。
zen
デバッグ用関数


zen
コモンダイアログ(ファイルを開く)

#include "atldlgs.h"

//ファイルを開く
CFileDialog dlg(TRUE, _T("txt"), NULL, OFN_HIDEREADONLY | OFN_CREATEPROMPT,
  _T("テキスト ファイル (*.txt)\0*.txt\0すべてのファイル (*.*)\0*.*\0\0"));
if(dlg.DoModal() == IDOK){
	ATLTRACE2(_T("ファイル名:%s\nパス:%s"), dlg.m_szFileTitle, dlg.m_szFileName);
}

//名前を付けて保存
CFileDialog dlg(FALSE, _T("txt"), NULL, OFN_HIDEREADONLY | OFN_CREATEPROMPT,
  _T("テキスト ファイル (*.txt)\0*.txt\0すべてのファイル (*.*)\0*.*\0\0"));
if(dlg.DoModal() == IDOK){
	ATLTRACE2(_T("ファイル名:%s\nパス:%s"), dlg.m_szFileTitle, dlg.m_szFileName);
}

複数の拡張子を表示するには、;で区切ります。

CFileDialogのよく使うメンバ
名前 意味
TCHAR m_szFileTitle ファイル名のみ
TCHAR m_szFileName パス付きのファイル名

zen
Windowsの文字と文字列に関して

 Windowsで扱う文字
意味 標準C++
TCHAR UNICODEが定義されている場合 WCHAR
それ以外 CHAR
wchar_t
char
WCHAR 16-bit Unicode wchar_t
CHAR 8-bit ANSI char

 Windowsで扱う文字列
意味 標準C++
LPTSTR UNICODEが定義されている場合 LPWSTR
それ以外 LPSTR
wchar_t*
char*
LPWSTR 16-bit Unicode 文字列の最後に\0のある文字列 wchar_t*
LPSTR 8-bit 文字列の最後に\0のある文字列 char*
LPCTSTR UNICODEが定義されている場合 LPCWSTR
それ以外 LPCSTR
const wchar_t*
const char*
LPCWSTR 16-bit Unicode 文字列の最後に\0のある文字列 const wchar_t*
LPCSTR 8-bit 文字列の最後に\0のある文字列 const char*
BSTR 文字列内に、NULL文字を含むことのできる文字列
OLECHAR FAR*,LPWSTRなどの別名
 他の文字列からの変換には、CComBSTRクラスを使う。
 逆の変換には、COLE2Tなどを使う。
(wchat_t*)

文字定数
マクロ 意味 文字 標準C++
_T('x') UNICODEが定義されているかどうかで、動作が異なる
つまり、環境の違いを吸収してくれる
ワイド文字(16-bit Unicode) L'x'
8-bit 文字 'x'

文字列定数
マクロ 意味 文字 標準C++
_T("x") UNICODEが定義されているかどうかで、動作が異なる
つまり、環境の違いを吸収してくれる
ワイド文字列(16-bit Unicode) L"x"
8-bit 文字列 "x"

zen
文字列関数

 printf系
Win32 API C (拡張) C
TCHAR wsprintf _sntprintf _stprintf
WCHAR wsprintfW _snwprintf swprintf
CHAR wsprintfA _snprintf sprintf

例:

char s[] = "computer";
char buffer[200];
int Used;

Used = _snprintf(&buffer[0], sizeof(buffer), "String: %s\n", s);	// &buffer[0] は、buffer でも良い
buffer[sizeof(buffer) - 1] = 0;	// バッファサイズより、文字列が大きくなった場合への対応。
				// 常にバッファの最後には、0を設定しておく。



zen
STL、標準Cについて

 デバッグでは問題なくコンパイルできるのに、Release ビルド時に、_main が未解決であるというリンカ エラーが発生することがあります。Cランタイム (CRT)を使ったことが原因です。標準のCや、STL関数を使った部分を確認します。
 例えば、printfやstd::vectorを使っていると、この問題が発生します。他にも
・文字列比較ルーチン
・メモリ割り当てルーチン
・コンストラクタがあるグローバル オブジェクト
・C++ 例外処理 (/GX)
を使った関数を利用すると発生します。

原因となっている部分を見つけるには、プロジェクトのプロパティを開き、[リンク] タブをクリックして、[入力] をクリックします。[特定のライブラリの無視] ボックスに「LIBCMT.LIB」と入力します。ここでビルドを実行します。解決できない外部参照の一覧が表示されます。使用している CRT ルーチンが、この一覧に含まれています。スタートアップ コードが必要と思われるルーチンを探します。

CRTをシステムコールで置き換える例(ATL)
std::vector -> CAtlArrayなど。

zen
CComPtr

 ATL では、CComPtr および CComQIPtr を使って、COM インターフェイス ポインタを管理します。どちらのクラスとも、AddRef および Release を呼び出して参照のカウントを自動的に実行します。オーバーロード演算子がポインタ演算を処理します。さらに、CComQIPtr は QueryInterface を介したインターフェイスの自動問い合わせもサポートします。

 CComPtrを使わない場合
	ICreateDevEnum *pDevEnum = NULL;

	CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
		IID_ICreateDevEnum, (void **)&pDevEnum);
	pDevEnum->Release();
 CComPtrを使う場合
	CComPtr <ICreateDevEnum> pDevEnum; // =NULLは不要。

	CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
		IID_ICreateDevEnum, (void **)&pDevEnum);
 ソースを調べると、ベースクラスで、ポインタが初期化(NULL)されているので、NULLは不要です。Release()も不要です。
 注意が必要なのは、ポインタと違い、使いまわしてはいけないようです。例えば、次のような使い方はできませんでした。
	ULONG cFetched;
	CComPtr <IEnumMoniker> pClassEnum;	// こちらは問題なし
	CComPtr <IMoniker> pIMoniker; // 下の使い方で問題あり

	while(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK) // 2度目のループでエラー
	{
	}
 この場合、ポインタを使い、次のようにします。
	ULONG cFetched;
	CComPtr <IEnumMoniker> pClassEnum;	// こちらは問題なし
	IMoniker *pIMoniker; 			// ポインタを使う

	while(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)
	{
		pMoniker->Release();
	}
 CComPtrを使うなら、次のようにするようです。あまり、CComPtrの利点を感じません。
	ULONG cFetched;
	CComPtr <IEnumMoniker> pClassEnum;	
	CComPtr <IMoniker> pIMoniker; 

	while(pMoniker.Release(), pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)
	{
	}
 参考までに、”,”は、文をグループ化する書き方です。意味的には、以下と同等です。
	func()
	{
		pMoniker.Release();
		return pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK;
	}
	while(func())
 次のやり方でも、良いかもしれません。
	ULONG cFetched;
	CComPtr <IEnumMoniker> pClassEnum;	
	CComPtr <IMoniker> pIMoniker; 

	while(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)
	{
		pMoniker.Release();
	}
zen
便利な使い方

WTLのヘッダファイルをプロジェクトに追加する
WTLのヘッダファイルをプロジェクトに追加しておくと、クラスビューでWTLに関する情報が得られて便利です。
ソリューション エクスプローラで、Header Filesに、WTLフォルダを作成し、その中にWTLのincludeディレクトリにあるヘッダファイルを追加します。

グリッドの設定
メニュー
 書式−>ガイドの設定
グリッドを設定できます。

プリコンパイルヘッダを使う
WTLやSDKのヘッダファイルのように、変更しないようなファイルは、stdafx.hに含めておくと、コンパイルが速くなり便利です。

ブラウザ情報を使う
 デバッグのときには、ブラウザ情報があると参照している部分を調べやすくなります。
プロパティで、「全般」−>「ブラウザ情報のビルド」を「はい」にします。「C/C++」−>「ブラウザ情報を作成する」を作成にする。
使い方は、メニューの「編集」−>「検索と置換」−>「シンボルの検索」で、検索し、検索結果のリストをクリックすると、参照している場所を表示させることができます。


zen
GraphEdit

DirectShowを使ったアプリケーションを作る場合、以下の関数を記述しておくと、GraphEdit(graphedt.exe)を使って、グラフを見ることができ、便利です。
DWORD dwRegister;	

HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) 
{
    IMoniker * pMoniker;
    IRunningObjectTable *pROT;
    if (FAILED(GetRunningObjectTable(0, &pROT))) {
        return E_FAIL;
    }
    WCHAR wsz[256];
    wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId());
    HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
    if (SUCCEEDED(hr)) {
        hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
        pMoniker->Release();
    }
    pROT->Release();
    return hr;
}

void RemoveFromRot(DWORD pdwRegister)
{
    IRunningObjectTable *pROT;
    if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
        pROT->Revoke(pdwRegister);
        pROT->Release();
    }
}

 使うときは、フィルタグラフ作成後に、AddToRotを実行し、Releaseの前に、RemoveFromRotを実行するようにプログラムを記述します。
 アプリケーションの実行中に、GraphEditを実行し「File」->「Connect to Remote Graph」を選択すると、アプリケーションのグラフの状態を知ることができます。GraphEditを終了させてから、アプリケーションを終了させます。
    IGraphBuilder * g_pGraph = NULL;

    CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
                           IID_IGraphBuilder, (void **) &g_pGraph);
    AddToRot(g_pGraph, &dwRegister);
...
    RemoveFromRot(dwRegister);
    g_pGraph->Release();

zen
DirectShow 基底クラス

 DirectShowのアプリケーションを作っているとき、マニュアルに関数の説明があるのに、その関数が使えないことがあります。ライブラリをビルドした後、自分のアプリケーションに組み込む必要があります。具体的には、incudeにパスを通し、次に、libのある場所にパスを通すか、libを直接指定します。
 以下は、マニュアルからの引用です。

 基底クラスを使うには、以下のディレクトリにある基底クラス ライブラリをビルド、リンクしなければならない : (SDK root)\Samples\Multimedia\DirectShow\BaseClasses

注 SDK の以前のバージョンではバイナリファイルとしてライブラリが含まれていた。MicrosoftR DirectXR 8.0 では、基底クラスを使おうとするなら、このライブラリを自身でビルドしなければならない。

基底クラスのプロジェクト ワークスペースは BaseClasses.dsw と名づけられている。このプロジェクトは 2 つのバージョンのライブラリをビルドする、リテール バージョン Strmbase.lib と デバッグ バージョン Strmbasd.lib である。自分のプロジェクト内で、ヘッダー ファイル Streams.h をインクルードすること。リテール ビルドかデバッグ ビルド化によって、プロジェクトに Strmbase.lib か Strmbsd.lib をリンクすること。デバッグ ビルドでは、ヘッダー ファイルをインクルードする前にプリプロセッサ シンボル DEBUG を定義すること。

次の表は必要なファイルを要約する。

ワークスペース BaseClasses.dsw
リテール ライブラリ Strmbase.lib
デバッグ ライブラリ Strmbasd.lib
ヘッダー ファイル Streams.h


注意) 私が使ってみたところ、ATLのヘッダと定義が重複し、エラーが発生しました。結局、必要な関数を抜き出して使っています。
zen
AM_MEDIA_TYPE

 以下の記述は、個人的なメモ書き程度のものです。現在、調査中のため、正しいとは限りません。

 AM_MEDIA_TYPEを使うとき、この構造体には、ポインタが含まれているため、注意が必要です。メンバのpbFormatがポインタであるため、この変数に記憶領域を割り当てる関数を使った後は、解放が必要になると思います。
 サンプルのamcapを見ると、AM_MEDIA_TYPE*型で定義した変数は、IEnumMediaTypesのNext()関数を使った後や、GetFormat()を使った後で、DeleteMediaType()を使っているのですが、一部では使っていないこともあり、正しい使い方は不明です。おそらく、DeleteMediaType()を使うのが正しいのでしょう。
 ドキュメントでは、AM_MEDIA_TYPE型の変数に、GetConnectedMediaType()を使って値を入れる例がありますが、バッファメモリを解放するようにとの注意があります。しかし、実際に解放するコードは省略されているのか見つかりません。おそらく、FreeMediaType()を使って、pbFormatを解放するのが正しいと思われます。
 基本的に、AM_MEDIA_TYPE構造体に、関数を使って値を入れたなら、解放する必要があるようです。

 AM_MEDIA_TYPE型の変数を使って、SetMediaType()を呼び出した場合は、解放する必要はありません。
zen