2004年07月04日

背景が透明なウィンドウの作り方(2/2)

環境
Visual C++.NET 2003
ライブラリ
MFC 7.1
OS
Win2000以降

以下のような背景が透明なウィンドウの作り方の続きです。前回はこちら

20040613_transparent.png

このウィンドウは、2枚のウィンドウを重ねて使います。 1枚目は、マウスメッセージを受信して処理する透明なウィンドウで、 2枚目は、文字などを表示する描画用のウィンドウです。

ウィンドウの設定より先に、Win2000以降でしか使えないAPIを使うことになるので、 定数を適切な値に設定しておきます。

どこかに以下のような定数定義がありましたら、

#define _WIN32_WINNT 0x0400

以下のように変更しておきましょう。

#define _WIN32_WINNT 0x0500

さて、肝心のウィンドウの設定ですが、 1枚目のウィンドウCXxxFrame(CFrameWnd派生クラス)のPreCreateWindowはこんな感じです。

BOOL CXxxFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;

	cs.x = 100;
	cs.y = 100;
	cs.cx = 200;
	cs.cy = 200;
	cs.style = WS_VISIBLE | WS_POPUP;
	cs.lpszClass = AfxRegisterWndClass(0);
	cs.dwExStyle = WS_EX_LAYERED;
	cs.hMenu = NULL;
	return TRUE;
}

サイズなど細かい点はおいておきまして、注目するのはウィンドウスタイルです。 拡張スタイルとしてWS_EX_LAYEREDを指定しています。

1枚目のウィンドウのOnCreateはこんな感じです。

int CXxxFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	SetLayeredWindowAttributes(0,1,LWA_ALPHA);
	m_pFrame = static_cast(RUNTIME_CLASS(CYyyFrame)->CreateObject());
	CRect rectWindow;
	GetWindowRect(&rectWindow);
	m_pFrame->CreateEx(0, NULL, "YYY", 0, rectWindow, this, 0);
	return 0;
}

ポイントは2点。SetLayeredWindowAttributesでウィンドウのα値を1にしているところと、 CYyyFrameを生成しているところです。 なお、m_pFrameというのはCFrameWnd*型のメンバ変数です。

ウィンドウのα値を1にすると、ウィンドウはほとんど透明になります。 α値を0にすると完全に透明になりますが、残念ながら完全に透明にしてしまうと ウィンドウを操作できなくなってしまいます。そのためここではα値を1に留めておきます。

CYyyFrameというのは、文字などを表示する描画用のウィンドウです。 ちょうどCXxxFrameと同じ場所に表示することで、1つのウィンドウのように見せているわけです。

CYyyFrameのウィンドウスタイルは、CYyyFrameのPreCreateWindowで指定します。

BOOL CYyyFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if (!CFrameWnd::PreCreateWindow(cs)) {
		return FALSE;
	}
	cs.style = WS_VISIBLE | WS_POPUP;
	cs.dwExStyle = WS_EX_LAYERED | WS_EX_TRANSPARENT;
	cs.lpszClass = AfxRegisterWndClass(0);

	return TRUE;
}

ポイントは1点。ウィンドウの拡張スタイルをWS_EX_LAYERED | WS_EX_TRANSPARENTとしているところです。
この拡張スタイルの組み合わせの意味は、Microsoftのレイヤードウィンドウ解説ページに書いてあります。

レイヤード ウィンドウが WS_EX_TRANSPARENT 拡張ウィンドウ スタイルを持つ場合、レイヤード ウィンドウの形状は無視され、マウス イベントはレイヤード ウィンドウの下の他のウィンドウに渡されます。
というわけで、このウィンドウに対するマウスイベントは、このウィンドウの下にあるウィンドウ、すなわちCXxxFrameに渡されます。このため、CYyyFrameは描画だけに専念できるわけです。
でもこれキーボードイベントはどうなるんでしょうね。 キーボードイベントは試してないので分かりません。
しかし不可思議なのはWS_EX_TRANSPARENTスタイル。このスタイルは、マウスイベントを無視することを意味するウィンドウスタイルではありません。でもレイヤードウィンドウのときだけ特殊な効果を発揮するようです。うーん不思議。

不可思議な点は残りましたが、とりあえずこれで透明ウィンドウを実現することができました。ひとまずめでたしめでたしと言う事で。

投稿者 MASATO : 2004年07月04日 10:30 | トラックバック
コメント

ソースファイルは2つ用意しました。どちらもVisual C++.NET 2003用です。

http://www.sutosoft.com/room/archives/products/heim107src.lzh
本サイトで公開しているHeimdallrというソフトのソースファイルです。
Heimdallrの透明スキンは、本記事の方法で実現されていますので、Heimdallrのソースで確認できます。
こちらソースの透明ウィンドウは様々な機能を持っていますが、ソース全体の量が多く、また(様々なライブラリを用意しなければならないため)ビルド困難です。よって肝心な場所を探すのはちょっと大変です。

http://www.sutosoft.com/room/archives/products/transparent.lzh
本記事のスクリーンショットを撮る為に作った簡単なプログラムのソースファイルです。
ほとんど機能の無い透明ウィンドウですが、ソース全体の量は少なく、ビルドも容易です。

お好きな方をどうぞ。

Posted by: MASATO : 2005年02月21日 20:12

背景が透明なウィンドウの作り方の全ソースファイルをご提供頂けないでしょうか? 宜しくお願いします。2005/2/21

Posted by: 山口 : 2005年02月21日 17:11
コメントする









名前、アドレスを登録しますか?