WM_GETDLGCODE無限ループ:2

d:id:kuboon:20090901
なんだかいろんな人がいろんなことを言っているが、
http://www.tech-archive.net/Archive/InetSDK/microsoft.public.inetsdk.programming.webbrowser_ctl/2006-06/msg00038.html
に載っていたコードが実装しやすそうだったので入れてみたら直った。

一度直ればこっちのもんで、コード修正と実行を繰り返し、最終的に落ち着いたのは以下のコード。

LRESULT BrowserDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	if( (message==WM_SYSCOMMAND && wParam==SC_MINIMIZE) //最小化ボタン
	||  (message==WM_ACTIVATE && wParam==WA_INACTIVE)   //フォーカスアウト
	||  (message==WM_SHOWWINDOW && wParam==FALSE)		//不可視化
	){
		::SetFocus(NULL);
	}
	return CDialog::WindowProc(message, wParam, lParam);
}

WM_GETDLGCODE無限ループ

IWebBrowserを乗っけたダイアログ上でJavaAppletを開き、アプレット上のUIをいじっているとメインスレッドがフリーズ。
なんだろうと思ってブレークをかけてみると、ダイアログの子要素に対してWM_GETDLGCODEがものすごい勢いで投げまくられている状態。

で、調査した。

WM_GETDLGCODEのなんたるか:
http://msdn.microsoft.com/ja-jp/library/ms645425
http://support.microsoft.com/kb/83302/en

タブコントロールを使っている場合に同じ状況に陥るケースが頻繁に報告されている。僕には関係ない。次。

http://blog.goo.ne.jp/satomi_takeo/e/b058ef705da6ca88ec87db660878c789
あたりっぽいんだが、リンク先サイトが死んでいて状況を追えず。次。

http://www.guyswithtowels.com/articles/2002-08-15-1600.html#ModelessDialogs
具体的なコードも出ていて助かる。が、メッセージポンプはMFC側にあるので、この通りに実装するの難しそう。

R/Wロックをテーブルで管理する

あるファイルシステムがロック機構を有していないので、RDB上でロック状態のみを管理することを考える。
超教科書的に考えると、以下の2テーブルを定義すればOK. *は主キー。

Read Lock Table

  • pk*
  • path
  • userId

Write Lock Table

  • path*
  • userId

Read Lockは複数人が可能でWrite Lockは一人が可能、ということで、まあ、こうなる。

しかし、2つもテーブルを作るのがダサいので、以下のようにする。

Lock Table

  • path*
  • lockCount*
  • userId

pathとlockCountで複合主キーとする。
lockCount=-1をwrite lock、1〜をread lockと定義する。

これで、まあ要件は満たせる。

MSXMLで文字列をインポートする方法

loadXMLを使うと encoding が期待通り処理されない。
以下のようにする。

	VARIANT var;
	{
		SAFEARRAYBOUND	rgb={strlen(xml),0}; //末尾にNULL文字つけちゃだめ
		SAFEARRAY* psa = SafeArrayCreate(VT_UI1,1, &rgb);

		if(!psa){
			assert(false);
			return false;
		}
		char*	buf;
		SafeArrayAccessData(psa,(void**)&buf);	// 作成したSAFEARRAYのポインタ取得及びロック
		memcpy(buf,xml,strlen(xml)); // 末尾のNULL文字分のバッファは無い!
		SafeArrayUnaccessData(psa);		// アンロック

		var.vt=VT_ARRAY|VT_UI1;
		var.parray=psa;
	}
	bool ret=manifest->load(var);
	::SafeArrayDestroy(var.parray);
	if(!ret){
		::OutputDebugString(manifest->GetparseError()->Getreason());
		::OutputDebugString(manifest->GetparseError()->GetsrcText());
		return false;
	}

末尾にNULLが付いているとパースエラーになるのが落とし穴。

WM_GETDLGCODE無限ループ

IWebBrowserを乗っけたダイアログ上でTextFieldを含んだJavaAppletを開き、そのあたりをいじっているとメインスレッドがフリーズ。
なんだろうと思ってブレークをかけてみると、ダイアログの子要素に対してWM_GETDLGCODEがものすごい勢いで投げまくられている状態。

で、調査した。

WM_GETDLGCODEのなんたるか:
http://msdn.microsoft.com/ja-jp/library/ms645425
http://support.microsoft.com/kb/83302/en

タブコントロールを使っている場合に同じ状況に陥るケースが頻繁に報告されている。僕には関係ない。次。

http://blog.goo.ne.jp/satomi_takeo/e/b058ef705da6ca88ec87db660878c789
あたりっぽいんだが、リンク先サイトが死んでいて状況を追えず。次。

http://www.guyswithtowels.com/articles/2002-08-15-1600.html#ModelessDialogs
具体的なコードも出ていて助かる。が、メッセージポンプはMFC側にあるので、この通りに実装するの難しそう。

JobObject の壁

複数のプロセスをまとめて管理する仕組みとしてJobObjectというものがある。
http://msdn.microsoft.com/en-us/library/ms684161(VS.85).aspx
POSIX準拠の仕組みらしく、古くはapacheのようなヤツがいくつかのプロセスを立ち上げて堅牢性を向上させていたり、最近はGoogle Chromeなどでも使われている。

SetInformationJobObject のリファレンスを見ると、
http://msdn.microsoft.com/en-us/library/ms686216(VS.85).aspx
JobObjectに対して様々な制約をかけることができることが分かる。
例えば、総プロセッサ時間、総メモリ消費量のようなものだ。
その他に、JOB_OBJECT_UILIMIT_DISPLAYSETTINGS, JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS のようなものもあり、ジョブ配下のプロセスがシステム全体に影響を及ぼすことがないようにする仕組みと考えられる。

VISTAではすべてのプロセスは Explorer のジョブグループに属してしまうようだ。(しかもProcessExplorerでも見えない?)
CREATE_BREAKAWAY_FROM_JOB を使って新規プロセスを作ると、そこでは AssignProcessToJobObject が成功する。

UACのせいだとかいろんなことを言っている人がいるが、たぶん間違い。
http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/71c9599e-a3d5-4b79-bfc1-1800565c5b8a
だって REATE_BREAKAWAY_FROM_JOB だけで出来たもん。

で、どこにも書いてないけどほかにも様々な壁があるっぽい。

  • SHAppBarMessage が利かない。ウィンドウハンドルが別扱いになるからかな?
  • CHtmlView を利用したブラウザが、PowerPoint等を開くとき、powerpnt.exe はDCOM経由で立ち上がるためにJobObjectに加わらない。で、たぶんそのウィンドウがブラウザから制御できないため、MFC内でクラッシュする。致命的。
  • RegisterWindowMessage が別になる
  • Jobの外でコピーしたデータをジョブの中へペースト出来ない。逆はできるっぽい。