Aeroで残像が出る – プログラミング – Home

通知
すべてクリア

Aeroで残像が出る

固定ページ 1 / 2

亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

Windows7、VS2008です。

http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+201302/13020003.txt
での質問と同じように、
モードレスダイアログで発生している問題を再現させるためのサンプルです
(MFCダイアログベースのプロジェクトをデフォルトのまま新規作成)。

void CTestDlg::OnBnClickedOk()
{
// ユーザがいったん非表示にし、再度表示させる操作
ShowWindow(SW_HIDE);
SetTimer(1, 1000, NULL);
}

void CTestDlg::OnTimer(UINT_PTR nIDEvent)
{
KillTimer(nIDEvent);
ShowWindow(SW_SHOW);
}

void CTestDlg::OnPaint()
{
// このSleep()は残像をわかりやすくするため
Sleep(500);

CString str;
str.Format(_T(%d), m_counter++);
CPaintDC(this).TextOut(0, 0, str);
}

このダイアログを起動すると、画面左上に「0」と表示されます。

その後「OK」と押すといったん非表示になり、1秒後に再表示され、
そのときは「1」と表示されますが、Windows7のAeroが有効になっていると、
ダイアログが再表示された直後に前回の「0」が一瞬が見えます
(その後すぐに消え、0.5秒後に「1」が表示される)。

隠れていたウィンドウを表示するときに
前回の描画内容が表示されることは通常はないはずです。

モードレスダイアログの非表示中にも、
表示するべき内容が変化することが当然あるのですが、
Aeroがなにか余計(?)なことをやっているのでしょうか。

これを防ぐ方法はなにか考えられませんでしょうか。


引用解決済
トピックタグ
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

AeroではGlass表示等を行うために、ウインドウの表示を
キャッシュしているようです。これが一瞬表示されるのでは
ないでしょうか。

やってみるべき事はあまりなくて、

1.ShowWindow(SW_SHOW); の直後に、
InvalidateRect(NULL,TRUE)と
UpdateWindow() をしてみる。

2.ShowWindow(SW_SHOW); の直後に、
 RedarwWindow()をRDW_UPDATENOW付きで実行する。

ぐらいですかね、テストのときSleep(500)をコメントするのを
お忘れなく。

(蛇足)
このような問題は、パフォーマンスオプション→視覚効果で
アニメーションなどを有効にしている場合も発生するようです。
こちらの問題は、アニメーション終了時にWM_NCPAINTが送付されない
様にコードされることが原因のようで、本件とは関係ないかもしれません。


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

あちゃ、言い忘れ。orz.
RedarwWindow()の場合の対象ウインドウは
 1.デスクトップ
 2.対象DLG
 3.上記の両方
を、試してみてください。


返信引用
亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

仲澤@失業者さん、ありがとうございます。

Invalidate()やRedrawWindow()をいろいろなパターンで呼んでみましたが、
結果は同じでした。やはり残像が出てしまいます。

> AeroではGlass表示等を行うために、ウインドウの表示を
> キャッシュしているようです。これが一瞬表示されるのでは
> ないでしょうか。

まさにこれが原因のような残像問題なのですが、
「キャッシュをクリア」のような仕組みはさすがにないですよね。

非表示になったウィンドウが、再表示時には別の表示状態になっていることがある、
ということがAeroでは想定されていないということになるのでしょうか。


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 20年前
投稿: 22
 

CS_SAVEBITS を外してみるとかどうでしょうか。


返信引用
亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

forty-fiveさん、ありがとうございます。

OnInitDialog()に以下のような処理を入れたところ、
Spy++で表示されるクラススタイルからは「CS_SAVEBITS」は消えたのですが、
この状態でもやはり同じように残像が出てしまいました。

ULONG_PTR cs = GetClassLongPtr(m_hWnd, GCL_STYLE);
SetClassLongPtr(m_hWnd, GCL_STYLE, cs & ~CS_SAVEBITS);


返信引用
仲澤@失業者
(@uncle_kei)
Prominent Member
結合: 5年前
投稿: 828
 

やはり、だめそうですね。
あきらめたほうが良いかもしれません。

どうしてもなんとかしたい場合の話になりますが、

1.ShowWindow( SW_SHOW)から最初のWM_PAINT受領までの全てのメッセージを
 SPY++でログします。

2.ログに現れたメッセージの内、キャッシュが表示される時のメッセージ
 を判別します。一つ一つブレークする方法しかないでしょう。

3.そのタイミング、又はそのメッセージ後の合理的なタイミングで
 クライアントエリアの再描画を行う。

ことで、回避できるかもしれません。
WM_SHOWWINDOWか、その前後のメッセージではないかと予測できます。


返信引用
亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

ありがとうございます。
Spy++でいろいろメッセージを追って再描画を入れてみたのですが、
やはり残像は消えないようです。

ちなみになんですが、「同じWindows7+Aeroだけど発生しないよ」
というかたは、やはりいらっしゃいませんでしょうか?

ビデオカードの問題も考えられるかなとも思っていたのですが。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 23年前
投稿: 1235
 

レジストリーの設定で無効にできるみたいです。
参考にどうぞ
http://pc-zero.jp/technic/disable_aeropreview.html
http://www.atmarkit.co.jp/fwin2k/win2ktips/1297hovertime/hovertime.html


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 23年前
投稿: 1235
 

こんなものも
ここまで変えると戻すのが大変そうですね。
http://radhia.blog23.fc2.com/blog-entry-1659.html


返信引用
亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

ありがとうございます。
ただ、提示していただいたものは、今回の問題とは関係ないと思われます。
プレビュー表示ではなく、ダイアログの再表示時に残像が出る問題です。


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 20年前
投稿: 22
 

私も色々試してみました。

void CTestDlg::OnBnClickedOk()
{
ShowWindow(SW_MINIMIZE);
ShowWindow(SW_HIDE);
SetTimer(1, 1000, NULL);
}

void CTestDlg::OnTimer(UINT_PTR nIDEvent)
{
KillTimer(nIDEvent);
ShowWindow(SW_RESTORE);
SetActiveWindow();
SetForegroundWindow();
}

これでどうでしょうか。


返信引用
亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

forty-fiveさん、ありがとうございます。

再表示される際の文字の残像は出なくなりました。
ただ、文字の代わりにダイアログ全体が一瞬黒く表示され、
その直後にダイアログが正しく描画されるようです。

ShowWindow(SW_MINIMIZE)によって、直後にAeroがキャッシュする画像が
真っ黒になったということかもしれません。

もしこの問題がAero環境で必ず発生するもので、
プログラム側で防ぐ方法がないということになれば、
提示していただいた方法を元にいろいろなパターンを試してみて、
一番気にならなそうな再表示方法を探ってみたいと思います。


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 20年前
投稿: 22
 

リージョンを使って消えたように見せかける方法はどうでしょうか。
レイヤードウィンドウでも同じことが出来ると思います。

void CTestDlg::OnBnClickedOk()
{
SetWindowRgn(::CreateRectRgn(0, 0, 0, 0), false);
SetTimer(1, 1000, NULL);
}

void CTestDlg::OnTimer(UINT_PTR nIDEvent)
{
KillTimer(nIDEvent);
SetWindowRgn(NULL, true);
}


返信引用
亀山
 亀山
(@亀山)
ゲスト
結合: 18年前
投稿: 133
Topic starter  

forty-fiveさん、引き続きありがとうございます。

残像が出るケースは格段に減りました
(自分の環境ではたまに同じような残像が出ることがあるようですが)。

この方法だと、内部的にはダイアログは表示状態のままなので、
IsWindowVisible()などで判定しているところを改める必要が出てきますが、
有力な回避方法の一つとして検討させていただきます。


返信引用
固定ページ 1 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました