位置とサイズを記憶するHTA2007年05月07日 01時20分20秒

アプリっぽくなる

以前HTAコネタで起動時に位置とサイズを設定するTIPS(てほどでもないが)を紹介したが、それにちょっとひねりを加えて、自動的に位置とサイズを記憶、次回自動時にそれを再現するサンプルを書いてみる。なんとなく、デスクトップアプリっぽいしょ?

なお、サンプルでは、矩形情報の読み込み・保存にjson.orgのJSONライブラリを利用しているが、単純なシリアライズ・デシリアライズなので自前でメソッドを用意できるなら特にjson.jsは必要ない。

処理のタイミング

まず、前のエントリでも書いたが、ウィンドウの位置とサイズを設定するタイミングは、HTA:APPLICATIONタグの出現前にしないと気になるちらつきが発生する。なので、json.jsをロード(または同等の機能の定義・ロード)してからhta:applicationが出現するまでに

  • 保存したウィンドウ矩形情報のロード
  • ロードした矩形情報を元にリサイズと位置の確定
の処理をする必要がある。

また、最終的な矩形情報の保存のタイミングだが、後述の通りdocument.bodyのプロパティを参照するので、window.onbeforeunloadにする。onunloadではdocument.bodyへアクセスできないためエラーになる。

ウィンドウの位置とサイズの取得方法

一番面倒なのは、ウィンドウの位置とサイズの正確な取得方法だ。

まず位置情報。IEにはwindow.screenLeft/screenTopというプロパティがあるが、これはウィンドウボーダーとタイトルバーのサイズを除いた情報しか取得できない。

また、サイズについてはdocument.bodyのoffsetWidth/offsetHeightが取得可能なもっとも外側のサイズのようで、これまたタイトルバーやらとの差がある。

で、どうするかというと

起動直後に位置とサイズを調整するのだからその情報を元に、document.bodyのレンダリングがすんだ時点で差分を取得しておく。で、保存時はその差分を加えた情報を保存する。
という、ややベタな方法をとることにする。 具体的には、window.onload時に
// offsetRectはleft/top/width/heightの
//   プロパティに、測定サイズと実サイズの差を格納する。
// initialRectはwindow.resizeTo/moveToに使用した値を
//   格納している。
with( offsetRect ) {
	left = initialRect.left - window.screenLeft;
	top = initialRect.top - window.screenTop;
	width = initialRect.width - document.body.offsetWidth;
	height = initialRect.height - document.body.offsetHeight;
}
としてオフセット情報を取得し、window.onbeforeunload時に
// savePrefは指定オブジェクトのJSONデータを保存する関数。
// window.screenLeft/Topとdocument.body.offsetWidht/Heightに
//   offsetRectの補正値を加える。
savePref( {
	left : window.screenLeft + offsetRect.left,
	top : window.screenTop + offsetRect.top,
	width : document.body.offsetWidth + offsetRect.width,
	height: document.body.offsetHeight + offsetRect.height
} );
の要領で補正した矩形情報を保存する。

サンプルコード

実際に位置とサイズを自動保存するHTAのサンプルコードは以下のとおり。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<title>テスト</title>
<script src="json.js"></script>
<script>
// 最後に保存した矩形情報をロードし、矩形オブジェクトとして返す
function loadPref() {
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	var stream;
	try {
		// 起動ディレクトリの「preference.json」に決めうち
		stream = fso.OpenTextFile( "preference.json", 1 );
		return stream.ReadAll().parseJSON();
	} catch(e) {
		with( { w : 320, h : 240 } ) {
			return {
				width : w,
				height : h,
				left : parseInt( ( screen.width - w ) / 2 ),
				top : parseInt( ( screen.height - h ) / 2 )
			}
		}
	} finally {
		if( stream ) stream.Close();
	}
}

function savePref(obj) {
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	var stream;
	try {
		// 起動ディレクトリの「preference.json」に決めうち
		stream = fso.CreateTextFile( "preference.json" );
		stream.Write( obj.toJSONString() );
	} finally {
		if( stream ) stream.Close();
	}
}

// 設定ロード直後の実ウィンドウサイズ
var initialRect = loadPref();
// 実サイズとdocument.bodyの矩形とのオフセット
var offsetRect = {
	left : 0,
	top : 0,
	width : 0,
	height : 0
};

with( { rect : initialRect } ) {
	window.moveTo( rect.left, rect.top );
	window.resizeTo( rect.width, rect.height );
}
</script>
<hta:application innerBorder="no" scroll="no"></hta:application>
</head>
<body>
</body>
<script>
window.onload = function() {
	// document.bodyのレンダリングが完了したのでoffsetRectを算出
	with( offsetRect ) {
		left = initialRect.left - window.screenLeft;
		top = initialRect.top - window.screenTop;
		width = initialRect.width - document.body.offsetWidth;
		height = initialRect.height - document.body.offsetHeight;
	}
}
window.onbeforeunload = function() {
	// offsetRectを加えた矩形情報を保存
	savePref( {
		left : window.screenLeft + offsetRect.left,
		top : window.screenTop + offsetRect.top,
		width : document.body.offsetWidth + offsetRect.width,
		height: document.body.offsetHeight + offsetRect.height
	} );
}
</script>
</html>
まあ、こんな機能なくてもいいんだけど、ちょっとデスクトップアプリ気分に浸れるので。

JSDBのhttp取得・おかわり2007年05月07日 13時14分13秒

しつこいなぁ > 自分

いいかげん誰も興味がないだろうが、JSDBのhttp取得をさらに調べた。

Keep-Aliveかい!?

ローカルホストに環境変数を返すasp設置して試したら、リクエストヘッダに「Connection」の指定がなかった。

ということは、Connection: keep-alive扱いですか。そうですか。

んじゃ、Connection: closeを送りつけたらどうなるかを試そうと思ったが、Streamコンストラクタにhttp://渡した場合はリクエストヘッダのカスタマイズができないぽい。リファレンス確認したら

To simply open a socket to the server and send your own headers, use a = new Stream('net://server.com:80/'); a.writeln('POST /file.cgi/ HTTP/1.1\r\n').
とか書いてあるので以下のようなコードを実行。
js>var stream = new Stream("net://localhost:80")
js>stream.writeln( "GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n" )
52
js>stream.canRead
true
js>writeln( stream.read() )
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Mon, 07 May 2007 04:25:46 GMT
X-Powered-By: ASP.NET
Connection: close
Content-Length: 11519
Content-Type: text/html
Set-Cookie: myCounter=1; path=/
Set-Cookie: ASPSESSIONIDASBRTBDS=PJEHLAFDDOAGKOAIHFNJHMNI; path=/
Cache-control: private
:
(略)
返ってくるじゃん!!

結局

以前のエントリ(これこれ)でハングするだなんだぶつぶついっていたのは、HTTPキープアライブのタイムアウト待ち状態だったということのようだ。実際、IISの設定でHTTPキープアライブのタイムアウトを10秒程度にしたら、待ち時間がそのくらいで制御が帰ってきた。うあ~、このくらいちゃんと調べろよ > 自分

まあ、このことから、「Stream#read()で一括読み込みしようとしたら、長い長いタイムアウト待ちになる可能性がある」ということになるので、webのリソースを単純に引っ張ってくるにはこの方法は向かないってことになる。

さらに恥ずかしいことに(こればっか)、最初のエントリで行単位読み込みのループがうまくいかなかったのはStream#eofを頼りにしていたからと判明。Stream#canReadはちゃんと意図した動作をするので、

while( stream.canRead ) { writeln( stream.readln() ); }
で、最初のリクエストに対する応答を全部読み込み終わった時点でループを脱出する。

まとめ

  • JSDBのStreamをhttpで初期化するとHTTPキープアライブアプリケーションとして動作する
  • リクエストヘッダをカスタマイズするにはnet://で初期化して、いちいちリクエストをwriteln()してやる必要がある
  • 行単位のループ処理をする場合はcanReadプロパティをみるべし
  • Stream#read()なんて不精をするとタイムアウト待ちに巻き込まれる可能性があるので注意
といった感じだろうか。はぁ。

JSDB日本語リファレンス2007年05月07日 13時47分06秒

JSDBがらみでちょろちょろ検索かけてたら、シンテック株式会社というCAD製品なんかやっている企業さんに行き着いた。

自社製品のオンラインマニュアルに、JSDBリファレンスの日本語訳版も加えておられる模様。

Cosmos Referenceへ飛ぶと、「分類」フレームに「JSDBライブラリー」のリンクがある。

ざっと見たところ、手持ちのリファレンス(4/27版に付属)のものと若干内容が異なる部分があるみたいなので、バージョンが違うものかもしれないが、dara-jみたいに「英語読むのマンドクセェ」ってな方には助けになるかも。

栃東関引退2007年05月07日 15時18分44秒

大関栃東の公式ブログ - ファンの皆さまへ

ぐあ、やはり引退かぁ。わかっちゃいたけどショックだなぁ。

「相撲」カテゴリ作成したので追加(2007.5.27)