2006年6月 8日 (木)

windowsサービスのapacheでtracを動かす

以下の環境変数の設定がきちんとされてるかチェック

  • PYTHONHOME
  • SVN_HOME
  • DIFFUTILS_HOME
  • APR_ICONV_PATH
  • PATH

make_obcallback: could not import mod_python.apache
とか出ることがある。

| | コメント (0)

2006年6月 4日 (日)

Pythonのスレッド管理

1 スレッド状態 (thread state) とグローバルインタプリタロック (global interpreter lock)
の日本語も英語も理解しづらかったので今日理解したことを俺語でメモ

■初めに

Pythonのマルチスレッド実装は擬似マルチスレッド。ネイティブコードレベルでシングルであるかマルチであるかにはかかわらず。
Pythonレベルではひとつのスレッド同期用フラグ(グローバルインタプリタロック)があって、このロックを取得できたスレッドだけがコードを実行できる。
このロックはバイトコード100個分とブロックする可能性のある操作の前を目安に自動的に解放されて、次のスレッドが獲得できるようになっている。
つまり、Pythonで管理されてるスレッド群がOSレベルで本当に同時に実行されることはない。(んだとおもう)

んで、Pythonの中だけで完結してるときはこんなことを知らなくてもいいんだけど、Cで拡張しようとするとこの仕組みに参加しなければならない。

■グローバルインタプリタロックの初期化

まず初めに必要なのがグローバルインタプリタロックの初期化。

void PyEval_InitThreads()

Pythonインタプリタが走り始める最初のメインスレッドがこれを実行しなきゃいけない。
これを実行するとグローバルインタプリタロックが初期化されて、メインスレッドがその所有者となる。

ちなみにここで出てくる話はPython/C双方ともに一つのスレッドしか使わないのであればまったく関係ない。PyEval_InitThreadsも含め、以降に出てくるAPIは全て関係がない。
が、pythonの中でスレッドが作られているのならCのコードがPythonを起動したメインスレッドからのコールバックで動く場合でも以降のAPIは必要となる。
が、その場合はPyEval_InitThreadsは必要ない。Pythonの中で初めて新しいスレッドが作られるときに自動的にPyEval_InitThreadsされるからである。

PyEval_InitThreadsが必要となるのはPythonコード内で初めて新しいスレッドが作られるのよりも前に以降のAPIを使いたいときとなる。
スレッドのロックに関するたぶん全てのAPIがグローバルインタプリタロックを必要として、それを初期化するのがPyEval_InitThreadsなので。

スレッドのロックAPIを使う人は全てのAPIより前でPyEval_InitThreads()を呼んどいたほうがいいだろう。

■PythonにアクセスするCのスレッドの初期化・解放

次に必要なのがPythonAPIを使うスレッドの初期化と解放。
先に書いたとおり、CのスレッドがPythonAPIを使うときにはPythonのスレッド管理の仕組みに自身のスレッドを参加させなければならない。
CのスレッドをPythonのスレッド管理の仕組みに参加させるには、スレッド状態オブジェクトを作ってスレッドに関連付ける必要がある。

このスレッド状態オブジェクトを扱うときの流れはPythonのドキュメントにあるサンプルコードを引用すると以下のようになる。

PyThreadState *tstate;
PyObject *result;
/* interp is your reference to an interpreter object. */
tstate = PyThreadState_New(interp);
PyEval_AcquireThread(tstate);
/* Perform Python actions here.  */
result = CallSomeFunction();
/* evaluate result */
/* Release the thread. No Python API allowed beyond this point. */
PyEval_ReleaseThread(tstate);
/* You can either delete the thread state, or save it
until you need it the next time. */
PyThreadState_Delete(tstate);

スレッド状態オブジェクトはPyThreadState_Newでつくる。

PyThreadState* PyThreadState_New( PyInterpreterState *interp )

指定したインタプリタオブジェクトに属する新たなスレッド状態オブジェクトを生成します。インタプリタロックを保持しておく必要はありませんが、この関数を次々に呼び出す必要がある場合には保持しておいたほうがよいでしょう。

PyThreadState_Newの引数には先のインタプリタ状態オブジェクトのインスタンスへのポインタが必要となる。 このポインタを取得するには以下の方法がある。

  • PyThreadState::interpから取得する。

    PyThreadStateを取得するAPIにはPyThreadState_Get()などがある。

    PyThreadState* PyThreadState_Get()

    現在のスレッド状態を返します。インタプリタロックを保持していなければなりません。現在のスレッド状態が NULLなら、(呼び出し側が NULLチェックをしなくてすむように) この関数は致命的エラーを起こすようになっています。

    つまり、Pythonで管理されているスレッドでしか使えない。

  • PyInterpreterState_New()で作る。
    PyInterpreterState* PyInterpreterState_New()

    新しいインタプリタ状態オブジェクトを生成します。インタプリタロックを保持しておく必要はありませんが、この関数を次々に呼び出す必要がある場合には保持しておいたほうがよいでしょう。

    void PyInterpreterState_Clear(PyInterpreterState *interp)

    インタプリタ状態オブジェクト内の全ての情報をリセットします。インタプリタロックを保持していなければなりません。

    void PyInterpreterState_Delete(PyInterpreterState *interp)

    インタプリタ状態オブジェクトを破壊します。インタプリタロックを保持しておく必要はありません。インタプリタ状態はPyInterpreterState_Clear() であらかじめリセットしておかなければなりません。

PyThreadState_Getを使うならインタプリタロックを持っている必要がある。インタプリタロックの取得と解放には次のAPIを使う。

void PyEval_AcquireLock()

グローバルインタプリタロックを獲得します。ロックは前もって作成されていなければなりません。この関数を呼び出したスレッドがすでにロックを獲得している場合、デッドロックに陥ります。この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

void PyEval_ReleaseLock()

グローバルインタプリタロックを解放します。ロックは前もって作成されていなければなりません。この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

多分上のAPIはスレッドがPython管理下にないと使えない。
また、通常PythonAPIを使うようなCコードのエントリポイントではPythonのスレッド管理下にあり、グローバルインタプリタロックを持っていることが多そう。
ということからPython管理下のスレッドがインタプリタ状態オブジェクトを取得して、それをこれからPython管理下に入れたいスレッドに渡して、そのスレッドがPyThreadState_Newしてスレッド状態オブジェクトを作る、というステップを踏むことになると思う。もしくはPyThreadState_NewするたびにPyInterpreterState_Newするか。

PyThreadState_Newしてスレッド状態オブジェクトができたらPyEval_AcquireThreadでスレッドに関連付ける。

void PyEval_AcquireThread( PyThreadState *tstate )

グローバルインタプリタロックを取得して、この関数を実行したスレッドに引数のスレッド状態オブジェクトを関連付ける。

グローバルインタプリタロックを獲得し、現在のスレッド状態を tstate に設定します。tstate は NULLであってはなりません。ロックはあらかじめ作成されていなければなりません。この関数を呼び出したスレッドがすでにロックを獲得している場合、デッドロックに陥ります。この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

PyEval_AcquireThreadの実行直後からグローバルインタプリタロックはこのスレッドのものである。
PythonAPIをいじる処理が終わったらPyEval_ReleaseThreadする。

void PyEval_ReleaseThread( PyThreadState *tstate )

引数のスレッド状態オブジェクトがこの関数を実行したスレッドに関連付けられていたら関連付けを解放し、グローバルインタプリタロックを解放する。

現在のスレッド状態をリセットして NULL にし、グローバルインタプリタロックを解放します。ロックはあらかじめ作成されていなければならず、かつ現在のスレッドが保持していなければなりません。tstate は NULLであってはなりませんが、その値が現在のスレッド状態を表現しているかどうかを調べるためにだけ使われます -- もしそうでなければ、致命的エラーが報告されます。この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

ここで再び先のサンプルコードを見返してみる。

/* Release the thread. No Python API allowed beyond this point. */
PyEval_ReleaseThread(tstate);
/* You can either delete the thread state, or save it
until you need it the next time. */
PyThreadState_Delete(tstate);

PyThreadState_Deleteではいらなくなったスレッド状態オブジェクトを削除できる。

void PyThreadState_Delete( PyThreadState *tstate )

スレッド状態オブジェクトを破壊します。インタプリタロックを保持していなければなりません。スレッド状態はPyThreadState_Clear() であらかじめリセットしておかなければなりません。

ここでへんなのはPyEval_ReleaseThreadでグローバルインタプリタロックを解放しているはずなのに、グローバルインタプリタロックを持っていないと呼べないPyThreadState_Deleteを呼んでいるところ。
インタプリタロックとグローバルインタプリタロックは別なのか?とも思ったけどそれもたぶん違う。ていうかそもそもPyThreadState_Clearしてない。

void PyThreadState_Clear( PyThreadState *tstate )

スレッド状態オブジェクト内の全ての情報をリセットします。インタプリタロックを保持していなければなりません。

多分ドキュメントが最後のステップをいろいろ省略してるんじゃないかと。その辺の流れをきちんと整理してみると以下のようになる。

ReleaseThreadした後、いらなくなったスレッド状態オブジェクトを削除したいときはスレッドの開始時にPython管理下のスレッドからインタプリタ状態オブジェクトをもらったように、今度はPython管理下のスレッドにいらなくなったスレッド状態オブジェクトのポインタを渡し、そのスレッドでグローバルインタプリタロックを取得している状態で、PyThreadState_Clearし、PyThreadState_Deleteする。

■スレッドからの一時的なグローバルインタプリタロックの解放

たとえばPython管理下に入ったCスレッドでネットにアクセスしてデータをダウンロードする処理をしたいときその間ずっとグローバルインタプリタロックを握りっぱなしだと、たとえPythonのコードがマルチスレッドで書かれていてもそれら全てのスレッドがブロックしてしまう。
そこでダウンロードする前のPythonAPIを使い終わった時点でグローバルインタプリタロックを手放し、ダウンロードが終わったあとでPythonAPIを使う前にもう一度グローバルインタプリタロックを取得しなおす必要がある。

それらをするのが

Py_BEGIN_ALLOW_THREADS
...ブロックが起きるような何らかの I/O 操作...
Py_END_ALLOW_THREADS

というマクロ。 これは

PyThreadState *_save;
_save = PyEval_SaveThread();
...ブロックが起きるような何らかの I/O 操作...
PyEval_RestoreThread(_save);

というコードがマクロ化されたもの。

PyThreadState *_save;
_save = PyThreadState_Swap(
NULL
);
PyEval_ReleaseLock();
...ブロックが起きるような何らかの I/O 操作...
PyEval_AcquireLock();
PyThreadState_Swap(_save);

という書き方もできるが、errnoが退避されずロックを手放してもう一度獲得したときに変更されていたり、pythonインタプリタのスレッドサポートが無効の場合そもそもPyEval_ReleaseLock/PyEval_AcquireLockがロック操作をしないのだとか。とりあえずPy_BEGIN_ALLOW_THREADS~Py_END_ALLOW_THREADSしとけばよさそう。

PyThreadState* PyEval_SaveThread()

(インタプリタロックが生成されていて、スレッドサポートが有効の場合) インタプリタロックを解放して、スレッド状態を NULLにし、以前のスレッド状態 (NULLにはなりません) を返します。ロックがすでに生成されている場合、現在のスレッドがロックを獲得していなければなりません。

void PyEval_RestoreThread( PyThreadState *tstate)

(インタプリタロックが生成されていて、スレッドサポートが有効の場合) インタプリタロックを獲得して、現在のスレッド状態を tstate に設定します。tstate は NULLであってはなりません。この関数を呼び出したスレッドがすでにロックを獲得している場合、デッドロックに陥ります。 (この関数はコンパイル時にスレッドサポートを無効化すると利用できません。)

■PyGILState_Ensure~PyGILState_Release

という素敵APIも登場していた。
どうもこれを使うとPyGILState_EnsureからPyGILState_Releaseまでの間で今まであったややこしいことを忘れてPythonAPIを使えるようになるらしい。
んで、これは以前と変わらずに一時的にロックを手放したいときはPy_BEGIN_ALLOW_THREADS~Py_END_ALLOW_THREADSとする。
ここまでの内容を理解した上でPyGILState_Ensure~PyGILState_ReleaseとPy_BEGIN_ALLOW_THREADS~Py_END_ALLOW_THREADSすれば完璧?

PyGILState_STATE PyGILState_Ensure()

Pythonの状態やスレッドロックに関わらず、実行中スレッドでPython C APIの呼び出しが可能となるようにします。この関数はスレッド内で何度でも呼び出すことができますが、必ず全ての呼び出しに対応して PyGILState_Release()を呼び出す必要があります。
通常、PyGILState_Ensure()呼び出しと PyGILState_Release()呼び出しの間でこれ以外のスレッド関連API を使用することができますが、Release()の前にスレッド状態は復元されていなければなりません。通常のPy_BEGIN_ALLOW_THREADSマクロと Py_END_ALLOW_THREADSも使用することができます。

戻り値はPyGILState_Acquire()呼び出し時のスレッド状態を隠蔽した"ハンドル"で、PyGILState_Release()に渡してPythonを同じ状態に保たなければなりません。再起呼び出しも可能ですが、ハンドルを共有することはできません - それぞれのPyGILState_Ensure呼び出しでハンドルを保存し、対応するPyGILState_Release呼び出しで渡してください。

関数から復帰したとき、実行中のスレッドはGILを所有しています。処理の失敗は致命的なエラーです。

バージョン 2.3 で 新たに追加 された仕様です。

void PyGILState_Release( PyGILState_STATE)

獲得したすべてのリソースを開放します。この関数を呼び出すと、Pythonの状態は対応するPyGILState_Ensureを呼び出す前と同じとなります。(通常、この状態は呼び出し元でははわかりませんので、GILState APIを利用するようにしてください。)
PyGILState_Ensure()を呼び出す場合は、必ず同一スレッド内で対応するPyGILState_Release()を呼び出してください。 バージョン 2.3 で 新たに追加 された仕様です。

以上、完璧に超意訳。
間違ってても責任負えませんっていうか間違ってたら教えてくださいm(__)m

| | コメント (0)

2006年5月29日 (月)

コピーできないPDFからコピーする

http://www.bravaviewer.jp/reader.htm

このビュアーを使うとできる。

(Ref: http://q.hatena.ne.jp/1115015484#a297395 )

| | コメント (0)

2006年5月27日 (土)

pythonスクリプトからmultipart/form-dataを送る。

Webサービスのサイトに写真とかを送信するのを自動化するにはどうするか、的な話。

最初はIEとJavaScriptで適当に送ってみようと思ったんだけどセキュリティがきつくなってて無理でした。というわけでpythonを使うことに。が、pythonもデフォルトではmultipart/form-dataはサポートしてない。ので、結局のところHTTPリクエストを自分でがりがり書くしかない。んでよくよく調べてたらurllib2をかっこよく拡張してる人を発見。

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306/

このスレッドの一番最後のコメント。

http://odin.himinbi.org/MultipartPostHandler.py

このMultipartPostHandler.pyを以下のように使うとurlとパラメータ渡すだけで通信できる。

cookies = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
                            MultipartPostHandler.MultipartPostHandler)
params = { "username" : "bob", "password" : "riviera",
         "file" : open("filename", "rb") }
opener.open("http://wwww.bobsite.com/upload/", params)

ちなみにこのMultipartPostHandlerをハンドラに加えるとopenの第二引数のエンコードを自動的にやってくれる。

| | コメント (0)

2006年5月21日 (日)

はてなブックマークに新しいウインドウで登録

はてなブックマークに新しいウインドウで登録

ブックマークレットです。タイトルのまんまです。
登録したいときはたいていそのページを読み終わってないことが多くて、デフォルトのブックマークレットだとはてなに飛んでしまって続きが読めないので別ウインドウにしてみました。

window.openしただけだから1エントリにするのも何だけどPC再インストしたときにまた書くのがめんどくさいからUP

| | コメント (0)

2006年5月20日 (土)

YouTubeからflvを直接落とすブックマークレット

  1. ここのリンクをお気に入りに登録してください。>>DownFromYouTube
  2. 適当なYouTubeの動画ファイルのページを開きます。
  3. その状態で先に登録したお気に入りを開きます。

これでそのページの動画をダウンロードするダイアログが開きます。(ダウンロードページを開くのではなくて直接落とし始めます。)
常にファイル名がget_videoになってしまいますが、ファイルの拡張子は.flvという形式です。

ちなみにgoogle videoからAVIで直ダウンロードするブックマークレットは
Google Operating System: Download Google Videos As AVI Files
にあります。
(Ref: Going My Way: Google VideoからAVI形式でダウンロード可能なBookmarklet )

さらにちなみに、VideoDownloader(直リンじゃないけどやたら多数の動画サイトからダウンロードするページを作れる)のブックマークレットは以下

Youtubeなどのムービーをダウンロードするトイブックマークレット - Ogawa::Memoranda

| | コメント (0)

2006年5月14日 (日)

USB外付けHDDの初期化

c:\windows\system32\diskmgmt.msc
を管理者権限で起動すればドライブ文字が割り振られていないドライブでもフォーマットとかができる。

| | コメント (0)

いえもん

思わず作ってみました。

http://glucose.jp/blog/11

IE系ブラウザ(IE6,Sleipnir,glucose,...)でみてるWebページのリンクにショートカットをつけて、Altキーを押しながらショートカットを入力することでリンクを飛べるようになります。
ほかにはAlt+上で上のディレクトリに移動したり、マウス右クリックしながら右にドラッグで進む、左にドラッグで戻る、といったマウスジェスチャーがIEでも可能になります。

FireFoxにおけるGreaseMonkeyみたいなことをして実装してます。
ほとんど思いつきで作ったんでかゆいところに手が届いてないですが、機会があったらもっと使えるものにしたいなーと。

| | コメント (0)

ターミナルサーバーライセンスの期限が切れたとき。

「このコンピューターで利用できるターミナル サーバー クライアント ライセンスがないため、リモートセッションは切断されました。サーバー管理者に問い合わせてください。」

というのが出たときはHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSLicensingをいじるとなんとかなったりする。
・HardwareID,Parameters,Storeをリネームするなどしてバックアップ。特にHardwareID\ClientHWIDがなくなると何とかして書き戻すまでリモートデスクトップ接続がまったく使えなくなるため注意。
・HardwareID\ClientHWIDに元の値の末尾を適当にちょこっとかえた値を設定すると繋がるようになる。

| | コメント (0)

2006年5月 4日 (木)

大体の日本語文字にヒットする正規表現

[ぁ-ヶ]|[亜-黑]

上を秀丸とかで正規表現で検索すればOK

(Ref: http://homepage2.nifty.com/zaco/rexp/rexp06.html#2)

| | コメント (1)

IE6でtextareaにwidth:80%とか指定するとフォーカスが当たったときににょきにょきサイズが変わるのを防ぐ

<div style="padding-left:10%;">
	<textarea style="width:80%"></textarea>
</div>

というHTMLがあると、以下のように表示されます。

IE6で見てる人は上のテキストエリアをクリックして何か書き込んでみてください。突然テキストエリアが右ににょきっと延びるはずです。と、まぁこういう困ったバグがあるのですが、これは親要素にwidthを指定してやることで解消されます。

<div style="padding-left:10%;width:100%">
 <textarea style="width:80%"></textarea>
</div>
(Ref : http://www.keynavi.net/ja/bugh/css_misc.html#_title_4_7 )(4.7 その他のCSSバグ::ブロックがうまく表示されない)

| | コメント (0)

2006年5月 2日 (火)

グローバル変数を使わずにsetTimeoutでthisを渡す。

Function.prototype.bind = function(object) {
	//関数のthisを取得
	var __method = this;
	//objectを退避
	var __object = object;
	//引数の配列を用意(ただし一番初めのthisをshiftして取り除く)
	var __arguments = arguments;
	for( var index = 0; index < __arguments.length-1; ++index )
		__arguments[index] = __arguments[index+1];
	__arguments.length = __arguments.length-1;
	//thisにbindした関数を実行する無名関数を返す。
	return function(){ __method.apply(__object, __arguments); }
}

function Obj1() {
	this.counter = 0;
	this.caller(2);
}

Obj1.prototype.caller = function( step ) {
	this.counter += step
	alert( this.counter );
	setTimeout( this.caller.bind(this, step), 100 )
}

obj1 = new Obj1();
こんな感じ。prototype.jsを使ってるならFunction.prototype.bindを使ったほうが早し。

| | コメント (0)

2006年4月27日 (木)

svn export

svn export [--username (username)] [--password (password)] http://~

とする。

| | コメント (0)

2006年3月27日 (月)

Premature end of script headers

Apatch2.0で遭遇したエラー。

具体的な内容については下とかが詳しいが、
http://sagittarius.dip.jp/~toshi/premature.html
http://it-revolution.seesaa.net/article/13687310.html
いろいろ試していても直る気配がない。

一か八かパーミッションを端から試していったところ、755から744に変えたら動作した…。謎。

| | コメント (0)

2006年3月20日 (月)

Donut RAPT 69 をVC8でビルドする。

  • Boost.RegExが必要。
    boost自体についてはLet's boostを参照のこと。
    Donutのビルドにはインクルードパスの設定のほかに
    bjam -sTOOLS=vc-8_0
    としてlibboost_regex_*.libとかを作って、そこにライブラリパスの設定が必要。
    このときsTOOLSの大文字小文字は間違えないこと。

    インクルードパス・ライブラリパスはツール→オプション→プロジェクトおよびソリューション→VC++ディレクトリ→ディレクトリを表示するプロジェクト→インクルードファイル・ライブラリファイルに設定してもいいし、
    プロジェクト→プロパティ→構成→すべての構成→構成プロパティ→C/C++→全般→追加のインクルードディレクトリ
    リンカ→全般→追加のライブラリディレクトリ
    リソース→全般→追加のライブラリディレクトリ
    に設定してもいい。
  • プロジェクト→プロパティ→構成→すべての構成→構成プロパティ→C/C++→詳細→指定の警告を無効にする→4996;4005;4995;4927;4244;4018;4346;4482;4454;4348;4554
  • プロジェクト→プロパティ→構成→すべての構成→構成プロパティ→C/C++→言語→forループスコープの強制準拠→いいえ
  • AtlifaceEx.idl
    右クリック→プロパティ→構成→全ての構成
    ビルドから除外→いいえ
    MIDL→MkTypLib互換→いいえ

    さらに以下の行を修正
     helpstring("IAxWinAmbientDispatchEx2 Interface"),
    interface IAxWinAmbientDispatchEx2 : IDispatch
  • DonutView.h
     CComQIPtr<IAxWinAmbientDispatchEx2> m_spAxAmbient;
  • stdafx.cpp
    //#include <atlimpl.cpp>
    MtlProfile.h
    //using WTL::wtlTraceFlags;
    DeskBandHost.h
    //AtlDumpIID(riid, _T(""), S_OK);
  • RtlMatch.h
    REG_EXPRESSION expression = REG_EXPRESSION( static_cast<LPCTSTR>(strPattern) );
  • MtlWeb.h
    inline void MtlShowInternetOptions()
     const int s_unknownOffset = 12;
    ExplorerTreeViewCtrl.h
     static void _TreeItemMask(TVITEM& item, ULONG dwAttributes)
  • RtlList.h
     typedef typename tyRtlList::iterator tyRtlListItr;
  • IDocHostUIHandlerDispatchImpl.h
       ATL::InlineIsEqualGUID(riid, IID_IDocHostUIHandlerDispatch))
    MtlDragDrop.h
       ATL::InlineIsEqualGUID(riid, IID_IDropTarget)
  • AboutDlg.h, mainfrm.h
       #define CopyCursor(pcur) ((HCURSOR)CopyIcon((HICON)(pcur)))
       hCursor = CopyCursor(LoadCursor(hInstHelp, MAKEINTRESOURCE(106)));
  • ChildFrm.h
    /*ATLTRY(*/CChildFrame* pChild = NEW CChildFrame(tabMDI, adBar, bNewWindow2, dwDLFlags)/*)*/;
  • LanguageOption.h
    g_hDllInst = _Module.m_hInstResource = g_hModuleInst;
    g_hDllInst = _Module.m_hInstResource;
  • mainfrm.h
         const UINT uDiff = (UINT)sqrt(pow(static_cast<double>(ptUp.x - ptDown.x), 2) + pow(static_cast<double>(ptUp.y - ptDown.y), 2));
        nDiff = sqrt(pow(static_cast<double>(ptBefor.x - ptUp.x), 2) + pow(static_cast<double>(ptBefor.y - ptUp.y), 2));
  • DonutLinksBarCtrl.h, ExplorerToolBarCtrl.h
      MtlRefreshBandIdealSize(CReBarCtrl(GetParent()), m_hWnd);
  • SearchBar.h, ExplorerToolBarCtrl.h
      MTL::CLogFont lf;
  • Donut.cpp
        GUID guid;
        hRes = _Module.Init(NULL, hInstance, &guid);
  • TreeOptiopnDialogT.h : CTreeOptionPageImpl, CDonutOptionDlg
    MtlCtrlw.h : CCommandBarCtrl2Impl
    FavTreeViewCtrl.h : CDonutFavoritesBarImpl
    ExplorerToolBarCtrl.h : CExpBarMenu
    DonutLinksBarCtrl.h : CDonutLinksBarCtrl
    SearchBarOption.h : CSearchBarPropertyPage
    FavoriteMenu.h : CFavoriteGroupMenu, CChildFavoriteMenu
    TabCtrl.h : CTabCtrl2Impl
    RemoveLogs.h : CRemoveLogsDlg

    以上のクラスの基底クラスに出てくる自分のクラス名にテンプレート引数をつける
    例)
    template<class T, WORD w_tDlgTemplateID>
    class ATL_NO_VTABLE CTreeOptionPageImpl : public CDialogImpl<CTreeOptionPageImpl>
     , public CWinDataExchange<CTreeOptionPageImpl>, public CMessageFilter
    template<class T, WORD w_tDlgTemplateID>
    class ATL_NO_VTABLE CTreeOptionPageImpl : public CDialogImpl<CTreeOptionPageImpl<T, w_tDlgTemplateID> >
     , public CWinDataExchange<CTreeOptionPageImpl<T, w_tDlgTemplateID> >, public CMessageFilter
    とする。

| | コメント (0)

指定のフォルダ以下のフルコントロールを得るソフト

http://mtamaki.cocolog-nifty.com/blog/2006/03/post_d1da.html

のおまけ。

http://www.aurora.dti.ne.jp/~m-tamaki/FullControler_060320.zip

指定したフォルダ以下のすべてのファイルとフォルダにFullControlerを起動したユーザーとAdministratorsのフルコントロールを付加します。

FullControler.exe -user "username"

として起動するとusernameのフルコントロールを付加することも出来ます。

| | コメント (0)

アクセス権・所有権を再設定して削除するソフト

http://www.aurora.dti.ne.jp/~m-tamaki/AdministrateDeleter_060320.zip

Windowsのセキュリティの勉強用に作ったツール。

指定のフォルダ以下のすべてのファイルとフォルダを削除します。
このとき、アクセス権や所有権がなくて削除に失敗しても所有権を分捕ってアクセス権を設定して削除しようとします。
というソフトなので、環境移行時なんかに出来てしまったどうしても消せないファイルやフォルダの削除に使えるかもしれません。

ただし、使用する際には非常にご注意ください。通常削除できないファイルもとてもがんばって一生懸命削除してしまうので本当に注意して使ってください。

| | コメント (0)

2006年3月18日 (土)

SVNのレポジトリにスペルは一緒だけど大文字小文字が違うファイルができてしまったときどうするか。

SVNのレポジトリにスペルは一緒だけど大文字小文字が違うファイルができてしまったとき、Windowsではチェックアウトやアップデートに失敗する。

こういうときはレポジトリブラウザで同名のファイルをリネームしたり、削除すればOK

| | コメント (0) | トラックバック (0)

2006年3月13日 (月)

debian+dynabook T5/X16PME+Planex GW-NS11Hで無線LANを使う

無線LANを使うのに丸2日かかりました。

とりあえず以下にやり方を記録。

  • まずapt-getでwireless-toolsをゲット。
    apt-get install wireless-tools
  • PCカードスロットにLANカードをざっくし指す。
    運がよければ電源が入って認識される。
    /etc/pcmcia/configに載ってるPCカードは使えると見ていい。
  • /sbin/iwconfig
    を実行すると認識されてるLANカードがIDとともに出てくる。eth0とかって感じで表示されるIDを覚えておく。
  • /etc/network/interfaces
    をエディタで開いて、auto:(ID)の行の前に
    wireless_essid *****
    wireless_key s:*****
    を追加。

これで起動時に自動的につながるようになるはず。
以下調べてるときのメモ。

  • KDEには無線LANの設定をするGUIツールがあるらしい。
  • 無線LANカードGW-NS11CではそもそもPCMCIAが認識しなかった。電源ランプも点灯せず。/etc/pcmcia/configに正しいmanfidをGW-NS11Hをベースに書いてやれば認識するのかも。
  • カードをGW-NS11Hにしたらあっさり認識。ただしすぐにはIPもらえなかった。とりあえずiwconfigでWEPキーを指定して再起動。
    /sbin/iwconfig eth1 key s:*****
  • 再起動したらWepキーが消えた。うぉ。
  • 挙句の果てにDHCPが動かなかった。
  • もう一度キーを指定して固定IPにしたら動いた。
  • そして再起動したらWepとIPが消えた。(;;
  • /etc/network/interfacesに書くんだろうか。
    wireless-key s:*****
    (Ref: http://www.geocities.jp/yasushi_suzudo/pub/hardware.html)

| | コメント (0) | トラックバック (0)

2006年3月10日 (金)

LANで他のPCからパスワードの入力なしにフォルダを共有する時

Windowsで他のPCからパスワードの入力なしにフォルダを共有したいときについてのメモ。

Windowsは他のPCの共有フォルダを見ようとするとき、まず自動でGuestアカウントでそのPCにログインしようとする。共有フォルダにパスワードなしでアクセスできないときはこのプロセスに失敗していることが多い。

自動でGuestアカウントにログインできるためには以下の3つの条件が必要。

  • Guestアカウントにパスワードがないこと。
    コントロールパネルの「ユーザーアカウント」でGuest→パスワードの削除
  • パスワードがないアカウントにリモートでログインできること。
    1. [スタート]-[ファイル名を指定して実行] から secpol.msc を起動します。
    2. [ローカルポリシー]\[セキュリティオプション] を展開します。
    3. 右側の「アカウント:ローカルアカウントの空のパスワードの使用をコンソールログオンのみに制限する」をダブルクリックし「無効」に設定して「OK」をクリックします。
    4. Windows を再起動します。
    (Ref: http://homepage2.nifty.com/winfaq/wxp/trouble.html#787 )
  • Guestアカウントがリモートでログインできること
    ローカルセキュリティポリシー→ローカルポリシー→ユーザー権利の割り当てを開き、ネットワーク経由でコンピュータへアクセスにGuestを加え、ネットワーク経由でコンピュータへアクセスを拒否するからGuestを外す。

| | コメント (0)

メモリの初期化忘れ

たまにしか再現しない、どうやっても再現したりしなかったりする、といった場合はメモリの初期化忘れを疑ってみよう。

これから起こるバグはかなり厄介。まず、OSが起動してから同じアプリケーションの二回目以降の起動では多くの場合同じメモリが使いまわされて、なおかつ前回のメモリ状態が残っていることが多い。
このために二回目以降は前回終了時の値が入ったままになり、そういった値は意外とアルゴリズムにとって初期値として致命的でないのでぱっと見動いてしまう。
しかし、リリースした後にほかのPCとかに入れてみると最初の挙動が変、とかそういうことになる。

ていうか、なったorz

| | コメント (0)

debianを入れてみた。

本当に入れてみただけど、とりあえずその記録。

Debian.jpからリンクをたどって流れに流れ
http://ftp.egr.msu.edu/debian/dists/sarge/main/installer-i386/current//images/floppy/
から

  • boot
  • cd-drivers
  • net-drivers
  • root

の4つの.imgファイルをダウンロード。さらに
http://ftp.egr.msu.edu/debian/tools/rwwrtwin.zip
もダウンロード。

んで、FDDを4枚フォーマットしてrwwrtwin.exeで4つのimgをFDDに書き込む。
んで、bootをマシンのFDDに入れて再起動。
するもエラーで止まりまくる。

どうもFDが古かったみたいでうまく書き込めなかった。5枚くらい失敗したのでこれからやる人は新しいFDを買ってきたほうがいいかも。

その後はダイアログに従って適当に進めて言ったらとりあえず動く環境が完成。
終了はアクション→ログアウトでシャットダウンを選んで終了。

| | コメント (0)

2006年3月 9日 (木)

MIDLとPSDK

PSDKを使ってMIDLをコンパイルするプロジェクトのソースコードをほかの場所に持っていってもう一度コンパイルしようとするとき、たいていredefinitionエラーになる。
これは新しい環境でPSDKのincludeフォルダのパスをプロジェクトかVCに設定していないため。

ただしPSDKのincludeのパスを設定してもリビルドするまできちんとコンパイルできない。

| | コメント (0) | トラックバック (0)

管理者権限を持たないで開発することのススメ

管理者権限を持たないで開発する方法 - Visual Studio
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/tchDevelopingSoftwareInVisualStudioNETWithNon-AdministrativePrivileges.asp

社本@ワック Blog より。
社本さんのBlogのタイトルからは管理者権限を持たないで開発する方法のKBって読み取れるけど、元のページを超すごいめっちゃ斜め読みした雰囲気から言うと、元のKBは「管理者権限で開発してても実際使うユーザーは管理者権限持ってなかったりするから、いざっていうときに動かなかったりセキュリティの面から言ってもよくないんだよー。というわけで開発者の皆さんには通常のユーザー権限で開発することをお勧めします。」って感じ(たぶん)。

実際、管理者権限を普段持たないのはお勧めです。共有マシンとか制限された環境で作業することが増えてきた人は多いんじゃないかな。

ちなみに具体的には以下のようにします。

  • まず自分の権限を適切に設定する。
    基本的に必要なのはUsersとDebuggerUsersです。AdministratorsとPowerUsersは外します。
  • これだけだと管理者権限が必要なときにいちいちユーザーを切り替えたりログオフログオンが必要なので、管理者権限でアプリを実行できる用意をします。
    一番必要なのが管理者権限でExplorerを実行できることだと思います。そのためには
    "C:\Program Files\Internet Explorer\IEXPLORE.EXE" C:\
    へのショートカットを作り、ショートカットのプロパティの詳細設定から「別の資格情報で実行」にチェックをしておきます。

    こうすることでショートカットを実行すると「別のユーザーで実行」ダイアログが開くようになるので、そこで管理者権限を持ったユーザーを選び、パスワードを入れればマイコンピューターが開きます。表示→エクスプローラーバー→フォルダを選択するとフォルダツリーも表示されてコントロールパネルとかも操作できます。

    後はよく管理者権限で実行するプログラムは上記の要領でショートカットを作って別の資格情報で実行にチェックしておくといいでしょう。

とまあ、たぶんこの2つを準備すればOK。前述のKBにはもっとほかのことも書いてあるかも。(そこまでちゃんと読んでない。。。)

| | コメント (0)

TortoiseSVNでBOMなしのUTF-8をDiffしたりMargeしたりするには?

TortoiseSVN(というかTortoiseMerge)ではBOMなしのUTF-8のコードページをきちんと読み込んでくれない。と、主にXMLのDiffやMergeが文字化けする。が、WinMergeの最新版だと読むことができる。

落とすときには06/03/09時点では上から2つ目のWinMerge 2.5.3.2-jp-1以降を選ぶこと。
これをインストールした後でTortoiseSVNの設定を開き、External Programsを軒並みExternalにしてパスをWinMergeU.exeにする。
あと、WinMergeの設定を開き、編集→設定で.html,.rc,.xmlのコードページ情報を検出するとmlang.dllを使用してファイルのコードページを検出するを有効にすると読めるようになる。

| | コメント (0) | トラックバック (0)

2006年3月 7日 (火)

HTMLのAタグのhrefにfoo.cgi?a=b&c=dと書くのは誤り

だということを今日はじめて知った。なんかショック。今まで書きまくってたよorz

http://www.yuhisa.com/cgi/htmllint/explain.html#bad-entity

http://www.bar.com/foo.cgi?a=b&c=d というURLは http://www.bar.com/foo.cgi?a=b&amp;c=d と書かなきゃいけない、という話。
HTMLのタグの内容の部分は実体参照にしなきゃいけないのは知ってたけど属性値は例外だと勝手に思い込んでた。ぐー。
ちなみにIEとかは上のどっちでも解釈してくれちゃったりします。でもampってキーがあったら誤認識とかのきっかけになりそうだし。気をつけます。

 

| | コメント (0)

cherrypyのサーバー解決の高速化

cherrypyというPurePythonのHTTPDがある。残念ながらHTTP1.0しか扱えないのであまり早くないが、pythonでちょっとしたローカルサーバーを作るには非常に便利なモジュール。

そのcherrypy2.1.0であるポートを開こうとしたとき、そのポートがすでに使われていると当然ながら開けずにエラーになるのだが、そのときのブロック時間が超長い。この待ち時間はconfigではどうやら変更できなさそうだ。

が、_cpserver.pyの342行目あたり、def wait_for_free_port(host, port):の for trial in xrange(50): の50を5とかにすると超早くなる。
成功するときはたいてい一発でつながるので5回も試行してだめならとっとと別のポートを開いた方がいいっす。たぶん。

| | コメント (0) | トラックバック (0)

2006年3月 5日 (日)

ひとつのエントリするほどじゃないメモs

昔のHPから今でも価値がありそうな情報だけど、ひとつのエントリにするほどのものじゃないのを片っ端からリストアップ。03年ごろの情報が多くて、たまに古いのだと95年とかだったりするので情報の信頼性は低いと見てください。ヒント程度に。

| | コメント (0)

2006年2月13日 (月)

SQLとカラム

  • whereをカラム名=値という形でなく、カラム名だけで指定するとどうなるか

    select test from tbdbsql where test
    と言うクエリを実行するとtbdbsqlのtestカラムの要素がTrueに評価される行のtestカラムの要素だけが表示される。
    0はFalseに評価され、1以上の整数はTrueに評価される。
    文字列はFalseに評価される。
  • testカラムの中の"test2"という値を持つ行を抜く。

    select test from tbdbsql where test = "test2"
    と言うクエリを実行すると、test2というカラムがあると、testカラムとtest2カラムの値が同じ要素だけが表示されてしまう。

    これは"test2"がSQLにおける文字列型でないため。'test'という感じにシングルクオーテーションを使う。

| | コメント (0)

2006年2月12日 (日)

iScrobbler Preferencesの設定ダイアログがiTunesの起動時に毎回出る

プラグインフォルダからiScrobbleWin.dllを削除すると出なくなる。

| | コメント (0)

2006年2月 8日 (水)

Javascript実行時に情報バーを表示させない

http://www.ffortune.net/rule/windowsxpsp2.htm


>セキュリティ保護のため、コンピュータにアクセスできるアクティブコンテンツは表示され
>ないよう、Internet Explorerで制限されています。オプションを表示するには、ここを
>クリックしてください。

(2)インターネットエクスプローラの「ツール」メニューから「インターネットオプション」
 を選択します。
 「詳細設定」のタブを選んで下記の2項目の所にチェックを入れて下さい。

  [マイコンピュータでの、CDのアクティブコンテンツの実行を許可する]
  [マイコンピュータのファイルでのアクティブコンテンツの実行を許可する]

| | コメント (0)

2006年1月26日 (木)

VC7でコマンドラインでソリューションをビルドする。(VC6でMakefile生成の代替手段)

Devenv コマンド ライン スイッチ

| | コメント (0)

2006年1月21日 (土)

仮想リストビューのカラム幅の自動調節

オーナードローリストビュー(ひょっとしたら仮想リストビューかも)でヘッダーの区切り(ディバイダー)をダブルクリックしてカラム幅を自動調節しようとすると、仮想リストではなく、実リストビューの中身を見に行って、中身が何もなくて、幅がほぼ0になってしまう。

そこでHDN_DIVIDERDBLCLICKで正しい幅に設定してやろうとするとできるのだが、いったん幅0になった後に設定したサイズになる。つまり幅0になるのをHDN_DIVIDERDBLCLICKだけではとめられない。散々調べた範囲ではこれはまともにはとめようがない。

が、たとえば要素が0個とか1個のリストビューでカラム幅自動調節をしようとすると一瞬幅0になるものの、すぐに指定のサイズになる。そこで自動調節時にオーナードローを一時停止させればいったん幅が0になっているのがわからないくらい高速に自動調節できる。

そのためにはリストビューのヘッダーコントロールをサブクラス化し、WM_LBUTTONDBLCLKが来たらオーナードローを一時停止して、HDN_DIVIDERDBLCLICKで幅を設定する直前にオーナードローを再び再開するようにすればよい。

| | コメント (0) | トラックバック (0)

2005年11月24日 (木)

ファイルを開くダイアログでフォルダも開く

GetOpenFileNameでファイルを開くダイアログを出すことができますが、あくまでファイルを開くダイアログなのでファイルしか開けません。
また、SHBrowseForFolderでフォルダを開くダイアログを出すことができますが、こちらはフォルダしか開けません。

ではファイルとフォルダを開きたい場合はどうすればいいか?と考えて、とりあえず今回ファイルを開くダイアログを改造してみました。

以下サンプルソースとポイントです。

TBFileDialog051124.zip

・通常GetOpenFileNameに渡したOPENFILENAME構造体のlpstrFileメンバがユーザーが選択したファイルパスになります。
 フォルダを選択できるようにするなら順当に考えればGetOpenFileNameの処理中にlpstrFileに選択しているフォルダへのパスも入れればいいということになります。
 ただ、GetOpenFileNameが処理を返すときにはダイアログが閉じてしまっています。
 ユーザーの選択しているフォルダの情報を得るならlpfnHookメンバを利用してフックプロシージャを使うことになるでしょう。

 このフックプロシージャ内でGetOpenFileNameが終了したときのlpstrFileを設定できればベストなんですが、しばらく調べても設定する方法がわかりませんでした。
 CommDlg_OpenSave_SetControlTextでダイアログ終了時にエディットボックスにパスを入れてみたりOFNOTIFYのOPENFILENAMEのlpstrFileにパスを入れてCDN_FILEOKを自分で送ってみたりとか…。
 仕方ないのでフォルダも選択できるようにするときはlpstrFileを使わず、別の変数に選択されている項目を保存することにしました。

・次に選択されているフォルダの取得の仕方ですが、CDN_SELCHANGEがフックプロシージャに送られてきたタイミングで取得します。
 しかし、CommDlg_OpenSave_GetFilePathを使ってもファイルしかえられません。OFN_ALLOWMULTISELECTを設定してフォルダとファイルをまぜこぜに選択してもきれいにファイルだけ帰ってきます。(--;
 フォルダを得るにはリストビューのハンドルを得て直接項目をリストアップする必要があります。さらにリストビューはフォルダを変更するごとに再構築されるのでハンドルはそのつど取得したほうが賢明なようです。(Ref: http://www2.tba.t-com.ne.jp/tail/prog/files/tlmultiofn1.htm )
 またフォルダが変更されたとき、選択は解除されますがCDN_SELCHANGEは送られません。CDN_FOLDERCHANGEが送られてきます。

・以上で選択しているフォルダとファイルを取得できますが、このままではフォルダを選択した状態で開くボタンを押しても、そのフォルダに移動してしまい、開けません。
 そこでOkボタンをCDN_INITDONEでサブクラス化します。そして何か選択されているときに開くが押されたら問答無用で閉じてしまいます。
 閉じるには本当はSetWindowLongPtrをつかって戻り値を返したりするようですが、今回はEndDialogで無理やり閉じてしまいました。
 閉じるとGetOpenFileNameが処理を返すのでCDN_SELCHANGEやCDN_FOLDERCHANGEで取得した選択中のファイルのリストの情報を使ってその後の処理を行います。

以下は上のアーカイブ内のTBFileDialog.cppのフォルダ選択部分の実装の重要なところの抜粋です。
これだけではコンパイルできませんが、上のポイントを実際に実装する流れをつかむヒントになるかなと。


////////////////////////////////////////////////////////////////////////////////

LRESULT __stdcall	FileDialog::OKWndProcHook(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	FileDialog*	pThis = TB_ENFORCE( NullFail(), reinterpret_cast< FileDialog* >( GetLongPtr( hWnd, GWLP_USERDATA ) ) );
	if( WM_LBUTTONUP == uMsg )
	{
		//通常OKボタンを押したときフォルダだけが選択されていたならそのフォルダに移動する。
		//ただしフォルダも選択するオプションが有効ならその操作をキャンセルして閉じる。

		//基本的にはOkボタンが押されたら閉じるが、もしMustExistフラグが立っていたら作成をユーザーに問い合わせる必要がある。
		//ただし作成を行うのはエディットボックスに何か値が入っていたら。値が入っていなければただOKボタンが押されたのを無視する。
		//これらの操作は基本的にmPathsがemptyのときだけ。
		HWND	hParent = GetParent( hWnd );
		if( pThis->mPaths.empty() )
		{
			//MustExistで、現在選択されてるファイルが存在しない場合は警告を表示
			if( pThis->mIsMustExist )
			{
				TB_STRING	file = CommDlgOpenSaveGetFilePath( hParent );
				if( !IsExist( file ) )
				{
					if( IDYES == MessageBox( hParent, (file + _T( "\nこのファイルは存在しません。\n\n作成しますか?" )).c_str(), GetTitle( hParent ).c_str(), MB_YESNO ) )
					{
						CreateFile( file );
						pThis->mPaths.push_back( file );
						EndDialog( hParent, IDOK );
					}
				}
			}
		}
		else
			EndDialog( GetParent( hWnd ), IDOK );
	}
	return CallWindowProc( TB_ENFORCE( NullFail(), pThis->mOriginalProc ), hWnd, uMsg, wParam, lParam );
}

////////////////////////////////////////////////////////////////////////////////

UINT __stdcall	FileDialog::ShowDialogHook(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if( WM_NOTIFY == uMsg )
	{
		UINT	code = reinterpret_cast< NMHDR* >( lParam )->code;
		HWND hParent = GetParent( hWnd );
		switch( code )
		{
			case CDN_INITDONE:
				//センタリング
				Move( hParent, Centering( GetScreenRect(), GetWindowRect( hParent ) ) );
			case CDN_SELCHANGE:
			case CDN_FOLDERCHANGE:
			{
				FileDialog*	pThis = TB_ENFORCE( NullFail(), reinterpret_cast< FileDialog* >( reinterpret_cast< LPOFNOTIFY >( lParam )->lpOFN->lCustData ) );
				if( !pThis->mIsFolderSelectable )
					break;
				switch( code )
				{
					case CDN_INITDONE:
					{
						//OKボタンのHWNDを取得
						HWND	hOK = GetChild( hParent, _T( "開く(&O)" ) );
						//OkボタンのユーザーデータにFileDialogのthisを仕込む。
						SetLongPtr( hOK, GWLP_USERDATA, reinterpret_cast< LONG_PTR >( pThis ) );
						//OKボタンをサブクラス化
						pThis->mOriginalProc = SubClass( hOK, OKWndProcHook );
					}
					break;
					case CDN_SELCHANGE:
					{
						//リストビューで選択されている項目のタイトルを取得して、現在選択しているフォルダのパスを先頭にくっつけてmPathsに保存
						pThis->mPaths = ( CommDlgOpenSaveGetFolderPath( hParent ) + _T( "\\" ) ) + GetSelectedItemTitle( TB_ENFORCE( NullFail(), GetChild( hParent, _T( "FolderView" ) ) ) );
					}
					break;
					case CDN_FOLDERCHANGE:
					{
						//フォルダが変更された。
						//何も選択されていないはずなので選択をクリア
						pThis->mPaths.clear();
					}
					break;
				}
			}
			return TRUE;
		}
	}
	return FALSE;
}

/////////////////////////////////////////

bool	FileDialog::ShowDialog( tDialogFunction DialogFunction )
{
	//絶対パスに変換して書き換え可能なバッファに格納(ofn.lpstrFileがconstじゃないので。)
	TB_STRING	temp = GetAbsolutePath( mRelativeBase, GetPrivateProfileString( mIniPath, mSection, mKey, mInitialPath ) );
	std::vector< _TCHAR >	result( temp.c_str(), temp.c_str() + temp.size() + 1 );
	result.resize( MAX_PATH*2, '\0' );

	//デフォルト拡張子を設定する。
	if( mDefaultExtension.empty() )
	{
		//まだ設定されていない
		//ファイルタイプが設定されていればそれの先頭の拡張子を使う。
		if( !mFileTypes.empty() )
			mDefaultExtension = mFileTypes[0].second;
		else if( '\0' != result[0])
		{
			//ファイルタイプはないけど、初期ファイル名があるならその拡張子を使う。
			TB_STRING extension = &result[0];
			TB_STRING::size_type position = extension.rfind( _T( "." ) );
			extension = extension.substr( TB_STRING::npos == position ? 0 : position );
		}
	}
	//ファイルタイプを作成する。
	std::vector< _TCHAR >	filters;
	{
		//全てのファイル(*)を追加。
		AddFileType( _T( "全てのファイル" ), _T( "*" ) );
		//二重NULL終端文字列群を作成
		FileTypes::iterator endIt = mFileTypes.end();
		for( FileTypes::iterator it = mFileTypes.begin(); it != endIt; ++it )
		{
			filters.insert( filters.end(), it->first.c_str(), it->first.c_str() + it->first.length() );
			filters.push_back( '(' );
			filters.insert( filters.end(), it->second.c_str(), it->second.c_str() + it->second.length() );
			filters.push_back( ')' );
			filters.push_back( '\0' );
			filters.insert( filters.end(), it->second.c_str(), it->second.c_str() + it->second.length() + 1 );
		}
		filters.push_back( '\0' );
	}

	OPENFILENAME	ofn = { sizeof( OPENFILENAME ) };
	ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK;
	if( mIsMultiSelect )
		ofn.Flags |= OFN_ALLOWMULTISELECT;
	if( mIsMustExist )
		ofn.Flags |= OFN_CREATEPROMPT;
	ofn.lpstrTitle = mTitle.empty() ? NULL : mTitle.c_str();
	ofn.lpstrFile = &result[0];
	ofn.nMaxFile = TB_NUMERIC_CAST< DWORD >( result.size() );
	ofn.lpstrFilter = &filters[0];
	ofn.lpstrDefExt = mDefaultExtension.c_str();
	ofn.hwndOwner = mhWnd;
	ofn.lpfnHook = ShowDialogHook;
	ofn.lCustData = reinterpret_cast< LPARAM >( this );

	if( !DialogFunction(&ofn) )
	{
		TB_ENFORCE( CommonDialog(), CommDlgExtendedError() );
		mPaths.clear();
		return false;
	}

	//FolderSelectableでない場合はresultからファイルを抽出する。
	if( !mIsFolderSelectable && mIsMultiSelect )
	{
		std::vector< _TCHAR >	path;
		_TCHAR*	pIndex2 = &result[0] + lstrlen( &result[0] );
		for( _TCHAR* pIndex = pIndex2+1; pIndex[0] != '\0'; pIndex += lstrlen( pIndex )+1 )
		{
			//まずカレントディレクトリをコピー
			path.assign( &result[0], pIndex2 );
			path.push_back( '\\' );
			path.insert( path.end(), pIndex, pIndex + lstrlen( pIndex ) + 1 );
			mPaths.push_back( &path[0] );
		}
	}

	//MustExistフラグが寝てて、フォルダを切り替えた直後にOkを押した場合、mPathが空。
	//resultの値を使う。
	if( mPaths.empty() )
		mPaths.push_back( &result[0] );

	//Iniに保存
	WritePrivateProfileString( mIniPath, mSection, mKey, mIsRelative ? GetRelativePath( mRelativeBase, mPaths[0] ) : mPaths[0] );

	return true;
}

////////////////////////////////////////////////////////////////////////////////

| | コメント (0)

2005年9月28日 (水)

BCB6::プロジェクトマネージャーでのファイルのドラッグドロップ

BCB6でプロジェクトマネージャーでファイルをドラッグドロップするときは、ほかのファイルの上を通るようにドラッグドロップしないと移動できない。

| | コメント (0) | トラックバック (0)

2005年8月22日 (月)

BCB6で共有RTLライブラリをDLLで有効にするとExe側のFreeLibraryで落ちる

なぞ。とりあえずDLL側の共有RTLライブラリは切っとくことがお勧め。

| | コメント (0) | トラックバック (0)

2005年8月19日 (金)

ファイルを開く

エクスプローラーでダブルクリックしたのと同じようにファイルを実行したいとき、
プログラムを実行するための関数を使うわけですが、いくつか種類があります。

ざっとCreateProcess(), WinExec(), ShellExecute(), ShellExecuteEx()といったところでしょうか。

もちろん上記の関数群で十分な場合も多いですが、「エクスプローラーでファイルをダブルクリックしたときと同じ」動きをするわけではありません。
CreateProcessは文字通りプロセスを生成するものでファイル名だけからでは起動させづらいでしょう。
WinExecやShellExecute、特にShellExecuteExはよく使われます。実際ShellExecuteExでほとんど事足りるでしょう。
ただ、特定の状況下で困ったりします。
たとえばショートカットファイルのプロパティで、ショートカットタブの詳細設定ボタンから開かれた詳細プロパティダイアログで別の視覚情報で実行するを設定した場合、これをShellExecuteExで実行しても普通に実行されてしまいます。
Explorerではきちんと「別のユーザーで実行」ダイアログが開きます。

では上記を解決するにはどうしたらいいか。
要するにファイルを右クリックしたときのデフォルト項目を実行すればいいわけです。
この項目を実行するにはIContextMenu::InvokeCommandを使います。
そしてIContextMenuはIShellFolder::GetUIObjectOfから取得します。

と、書くのは簡単ですが、やるのはとっても大変でした。
以下のようになります。

////////////////////////////////////////////////////////////////////////////////

//stl
#include <string>
//windows
#include <windows.h>
#include <Shlobj.h>
#pragma comment(lib, "Ole32.lib")

////////////////////////////////////////////////////////////////////////////////

#if !defined( TBENFORCEH )
	#define TB_ENFORCE( enforcer, statement )	statement
	#pragma warning( disable :4552 )	//warning C4552: '!' : 演算子にプログラム上の作用がありません。作用を持つ演算子を使用してください
#endif	//#if !defined( TBENFORCEH )

////////////////////////////////////////////////////////////////////////////////

class	System
{
public:
	virtual	~System() throw(){}
	static void	Run( const std::string& path );
};

////////////////////////////////////////////////////////////////////////////////

void	System::Run( const std::string& path )
{
	//COMを初期化
	TB_ENFORCE( NotEqualFail( S_OK ), CoInitialize( NULL ) );

	//デスクトップフォルダを取得
	IShellFolder* pDesktopFolder;
	TB_ENFORCE( NotEqualFail( S_OK ), ::SHGetDesktopFolder( &pDesktopFolder ) );

	//引数のパスへのフルパスを含むITEMIDLISTを作成
	LPITEMIDLIST pFullPathIDList;
	{
		//まず引数をユニコードに変換
		OLECHAR	convertedPath[MAX_PATH*2+1];
		TB_ENFORCE( API(), ::MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path.c_str(), -1, convertedPath, MAX_PATH*2 ) );

		//ITEMIDLISTを取得
		ULONG	chEaten, dwAttributes;
		TB_ENFORCE( NotEqualFail( S_OK ), pDesktopFolder->ParseDisplayName( NULL, NULL, convertedPath, &chEaten, &pFullPathIDList, &dwAttributes ) );
	}

	//引数のパスの親フォルダのIShellFolderを取得。
	IShellFolder*	pCurrent = pDesktopFolder;
	{
		//IMallocを取得しておく。
		IMalloc*	pMalloc;
		::SHGetMalloc( &pMalloc );
		//IShellFolderをトラバース
		do
		{
			//フルパスを示すITEMIDLISTから今見ているフォルダのITEMIDLISTをコピー
			LPITEMIDLIST pCurrentIDList;
			{
				//コピー元のアイテムのサイズを取得
				int	srcSize = pFullPathIDList->mkid.cb;
				//コピー先のサイズはUSHORTのサイズを足したもの
				int	dstSize = srcSize + sizeof(USHORT);
				//新しいアイテム用メモリを確保
				pCurrentIDList = reinterpret_cast< LPITEMIDLIST >( TB_ENFORCE( NullFail(), pMalloc->Alloc( dstSize ) ) );
				//内容をコピー
				memcpy( pCurrentIDList, pFullPathIDList, srcSize );
				//余りは0で埋める。
				memset( reinterpret_cast<LPBYTE>(pCurrentIDList) + srcSize, 0, dstSize - srcSize );
			}
			//今見てるフォルダのITEMIDLISTからIShellFolderを取得
			IShellFolder*	pTemp;
			TB_ENFORCE( NotEqualFail( S_OK ), pCurrent->BindToObject( pCurrentIDList, NULL, IID_IShellFolder, reinterpret_cast< void**>( &pTemp ) ) );
			//取得できたので前のIShellFolderは解放
			pCurrent->Release();
			//pCurrentを置き換える。
			pCurrent = pTemp;
			//ついでにITEMIDLISTもいらないので解放
			pMalloc->Free( pCurrentIDList );
			//ItemIDListを次に進める。
			pFullPathIDList = reinterpret_cast<LPITEMIDLIST>( ( reinterpret_cast<LPBYTE>( pFullPathIDList ) ) + pFullPathIDList->mkid.cb	);
		}
		while( reinterpret_cast<LPITEMIDLIST>( ( reinterpret_cast<LPBYTE>( pFullPathIDList ) ) + pFullPathIDList->mkid.cb  )->mkid.cb != 0 );
		//↑pFullPathIDListの次がsize==0ならループを終わる。

		//IMallocを解放
		pMalloc->Release();
	}
	//pCurrentがpathの指すファイルの親フォルダ。
	//pFullPathIDListは今ファイル名を指す。

	//以上の情報からそのファイルの右クリックメニューを取得して既定のコマンドを実行

	//まずIContextMenuを取得。
	IContextMenu*	pContextMenu;
	TB_ENFORCE( NotEqualFail( S_OK ), pCurrent->GetUIObjectOf( NULL, 1, ( LPCITEMIDLIST* )(&pFullPathIDList), IID_IContextMenu, 0, reinterpret_cast< void ** >( &pContextMenu ) ) );

	{//コマンドを実行
		//IContextMenu::InvokeCommandの引数用構造体を用意
		CMINVOKECOMMANDINFO	invokeInfo = { sizeof( invokeInfo ) };
		//lpVerbメンバにメニューの識別子を設定
		HMENU	hMenu = TB_ENFORCE( API< HMENU >( NULL ), ::CreatePopupMenu() );
		TB_ENFORCE( TrueFail(), FAILED( pContextMenu->QueryContextMenu( hMenu, 0, 1, 0xffff, CMF_EXPLORE ) ) );
		invokeInfo.lpVerb = MAKEINTRESOURCE( TB_ENFORCE( EqualFail( -1 ), GetMenuItemID( hMenu, TB_ENFORCE( API(-1), GetMenuDefaultItem( hMenu, TRUE, NULL ) ) ) )-1 );
		invokeInfo.nShow = SW_SHOWNORMAL;
		//実行
		HRESULT	result = pContextMenu->InvokeCommand( &invokeInfo );
		//「0x800704c7 この操作はユーザーによって取り消されました。」の場合はエラーとしない。
		if( 0x800704c7 != result && S_OK != result )
			TB_ENFORCE( FalseFail(), !"pContextMenu->InvokeCommand( &invokeInfo )" );
	}
	//COMを解放
	CoUninitialize();
}

////////////////////////////////////////////////////////////////////////////////

| | コメント (0) | トラックバック (0)

2005年8月 3日 (水)

RTTIのプリプロセッサでの判別。

VC7ではRTTIが有効な場合_CPPRTTIというマクロが定義される。
BCB6ではそれに相当するマクロはないが、基本的にRTTIが有効であり、さらに__rttiキーワードを指定したクラスの派生クラスは全てRTTIが有効になる。(RTTIがオフでも。)

BCB6では__rttiキーワードで解決できないこーどは無効にするなどするしかない。

| | コメント (0) | トラックバック (0)

2005年7月10日 (日)

ファイルを削除したいのに誰かプロセスが使っていて削除できないとき。

ProcessExplorerというソフトがあります。
http://www.sysinternals.com/Utilities/ProcessExplorer.html
こいつを使うとファイルパスでけんさくすることでそのパスを使ってるプロセスを探し出すことが出来き、おまけにそのハンドルをクローズできます。

| | コメント (0) | トラックバック (0)

2005年5月13日 (金)

アクセスできないファイルをアクセスできるようにする。

Windowsのアクセス制御はかなり微妙。設定のやり方の詳しい資料もないし。

アクセスできないファイルがあったらとりあえず管理者権限を持ったユーザーでプロパティを開く。
次にプロパティのセキュリティタブを開く。ここでエラー(セキュリティを変更する権限がない)といわれたらまず所有権の取得を行う。
詳細設定ボタンから所有者タブでAdministratorsを選ぶ。そしていったんOKを押してプロパティを閉じる。

次に開くとアクセス許可の欄が有効になっているはず。
ここで任意のユーザーのアクセス許可を有効にできる。
Everyoneの設定はすべてのユーザーに優先して使われるらしい。
ユーザーの追加は追加ボタンで入力欄にEveryoneならeと入れるとほかにかぶる名前のユーザーがなければ自動的に補完してくれる。

| | コメント (0) | トラックバック (0)

2005年4月20日 (水)

CUnknownのありか。

C:\Program Files\Microsoft DirectX 9.0 SDK (April 2005)\Extras\DirectShow\Samples\C++\DirectShow\BaseClasses\combase.h
にある。

#define AM_NOVTABLE
#include <wxdebug.h>
#include <Combase.h>

こうすると通る。

| | コメント (0) | トラックバック (0)

2005年4月13日 (水)

BCB6でDX9をを使う。

DirectX9のSDKはVC用に作られていて、そのままではBCBでリンクが通らない。
以前のDXはcoff2omf.exeというツールでBCB用に変換できたが、DX9のは無効なファイルが出来上がってしまって使えない。

DLLからlibを作る方法もあるが、libしかないファイルもあり、やはりリンクができない場合がある。
が、BCB用のlibを提供しているところがあった。

(Ref: http://clootie.narod.ru/cbuilder/index.html )

libsやdllsを置いてるページがくそ重なので直りん
http://clootie.narod.ru/delphi/DX92/Clootie_DX92_dlls.zip
http://clootie.narod.ru/delphi/DX92/CBuilder_DX92_libs.zip

中ほどの"DirectX libraries for C++Builder"の"CBuilder_DX92_libs.zip"がlibファイル群となる。
これをダウンロードして展開し、BCBのプロジェクトオプションのディレクトリ設定のライブラリファイルのところに展開先のパスを指定してやればよい。

これでリンクは通るが、実行時にDLLがないといってエラーになる。これは先のページの"Clootie_DX92_dlls.zip"を落としてきて展開し、中の2つのDLLをwindows\system32のようなパスの通ったディレクトリに放り込めば解決される。

| | コメント (0) | トラックバック (0)

CVS・SVN::Ignore file(.cvsignore)

 *.#* *.~* *.aps *.avi *.bak *.cgl *.csm *.ddp *.dll *.dsk *.exe *.exp *.gid *.i *.ilk *.idl *.ini *.jpg *.JPG *.lib *.lnk *.mrg.* *.nbc *.ncb *.obj *.opt *.pdb *.plg *.prej *.pyc *.pyd *.res *.str *.suo *.tds *.tlb *.txt *.user *release* boost* *Debug* *debug* HTML *Release* Thumbs.db

このくらい登録しておくと便利。svn:ignoreはなんで.cvsignoreみたいに設定を継承したりできなくなったのかなぁ…。

| | コメント (0) | トラックバック (0)

cvs::sourceforge

set CVSROOT=:pserver:anonymous@cvs.sourceforge.net:/cvsroot/tortoisecvs
cvs login
cvs -z3 checkout TortoiseCVS
cvs logout

| | コメント (0)

2005年4月 8日 (金)

DX9のGraphEdit

DX8についてきたGraphEditなどのDirectShow周りのツールはDX9では標準ではついてこないらしい。

Extraというのを別途落とせとググると出てくるのだが日本語のサイトにはぱっと見、ない。
英語のところに行くとある。
http://msdn.microsoft.com/directx/directxdownloads/

| | コメント (0) | トラックバック (0)

2005年3月31日 (木)

BCB6でコントロールのフォーカス移動を無効にする。

BCB6のVCLはコントロールのフォーカス移動を複雑なキーイベント処理によってエミュレートしています。
WindowsのIsDialogMessageを直接使っているわけではないようです。
(Ref:http://www.asahi-net.or.jp/~HA3T-NKMR/vcl3-3.htm)

そのBCB6で矢印キーでのフォーカス移動をやめたい場合は、CM_DIALOGKEYというメッセージを横取りすることでうまくいきます。


class	TForm1 : public TForm
{
	//	:
protected:
	void __fastcall OnDialogKey(Messages::TWMKey &Message)
	{
		if( Message.CharCode != VK_LEFT )
			TForm::Dispatch( &Message );
	}
	BEGIN_MESSAGE_MAP
		MESSAGE_HANDLER( CM_DIALOGKEY, Messages::TWMKey, OnDialogKey );
	END_MESSAGE_MAP(TForm)
};
上のような感じです。他の無効にしたいキーも同じく記述します。

| | コメント (0)

2005年3月24日 (木)

BCB6でboost1_32_0でvaridate_generatorあたりでコンパイルエラー

BCB6のパッチバージョン4を当てると解消されることがある。

| | コメント (0) | トラックバック (0)

2005年3月11日 (金)

漢字変換(MS-IME)Tips(F10で半角に変換)

漢字変換起動中に起動していないつもりで入力し、F10を押すと半角、つまり漢字変換が起動していない状態で同じキー入力をした状態に変換されます。例えば、

"m。たまき’s"と入力してF10を押せば"m.tamaki's"となります。
また、MS-IMEでの話ですが、漢字変換起動中、未確定文字列の中に半角スペースを入れたいときはCtrl+Spaceを押します。
また、漢字変換起動中、全角かな漢字を入力していてShift+アルファベットを入力すると勝手に半角英数字モードに切り替わる経験を持つ人は多いと思います。
例えば
"このサイトのLibraryの"
と入力するとき、"このサイトのLibrary"までは違和感なく入力できますが、"の"が"no"となってしまいます。

この半角英数字モードはShift空打ちで全角かな漢字モードに戻せます。
つまり、"このサイトのLibrary"まで入力したらShiftを一回押します。その後"の"と書けばちゃんと全角で"の"となります。

| | コメント (0) | トラックバック (0)

Sleipnir::検索結果にハイライトを行う。

Sleipnirオプション→検索→検索結果にハイライトを行う

で検索後どのページもグーグルキャッシュ状態(キーワードが強調表示)になります。
最近の更新で追加されたオプションですが、デフォルトでオンになっていないバージョンがあり、
結構気づいてなかったりするようです。

| | コメント (0)

2005年3月 6日 (日)

ES_READONLYのEDITコントロールの背景色

WM_CTLCOLORSTATICの戻り値でハンドルを返すだけだと、本当に背景色のみ変更。
どうやらWM_CTLCOLORSTATICのWPARAMのHDCを使ってテキストの背景色も設定してやるときちんと全体の背景色を設定できるようだ。

| | コメント (0) | トラックバック (0)

2005年3月 5日 (土)

Boost.Lambdaとプリコンパイル

VC7.1でもBoost.Lambdaはプリコンパイルできない。
どうやらヘッダ内にてグローバル変数が使われているようだ。

| | コメント (0) | トラックバック (0)

2005年2月24日 (木)

BCB6のテンプレートクラスのテンプレート引数

BCB6のテンプレートクラスのメンバ関数をクラス外で宣言するとき、テンプレート引数の引数名自体が同一でないとコンパイルエラーになる。

| | コメント (0)

自動閾値判別分析二値化 - 大津の方法

Uint	Binarize::GetThresholdByOptimizedOtsu( const std::vector< Uint8 >& picture )
{
	//画像の先頭ポインタを取得
	const Uint8 * pPicture = &picture[0];

	//ヒストグラム用バッファを用意
	std::vector<double> histogram(256, 0);

	//ヒストグラムを作成
	TB_LOOP_1( picture.size() )
		++histogram[*pPicture++];

	//ヒストグラムの各要素を画像サイズで除算して正規化ヒストグラム(濃度の確率分布)を作成
	std::transform(histogram.begin(), histogram.end(), histogram.begin(), std::bind2nd(std::divides<double>(), picture.size()));

	//濃度分布の0次モーメント用のバッファを用意
	std::vector<double> partial0thMoment(256);

	//全体の部分和を計算({1,2,3,4}→{1,3,6,10})
	std::partial_sum(histogram.begin(), histogram.end(), partial0thMoment.begin());

	//濃度分布の1次モーメント用のバッファを用意
	std::vector<double> partial1stMoment(256);
	//正規化ヒストグラムに輝度をかけたものをバッファにコピー
	TB_LOOP_1( 256 )
		partial1stMoment[index] = index*histogram[index];
	//それの部分和を計算
	std::partial_sum(partial1stMoment.begin(), partial1stMoment.end(), partial1stMoment.begin());
	//全平均輝度レベル
	double total1stMoment = partial1stMoment.back();

	//クラス内分散
	std::vector<double> interClassVariance(256);
	TB_LOOP_1( 256 )
	{
		//クラス内分散を求める。(全平均輝度レベル×輝度i間での濃度分布の0次モーメント-輝度i間での濃度分布の1次モーメント)^2を
		double temp = total1stMoment * partial0thMoment[index] - partial1stMoment[index];
		//輝度i間での濃度分布の0次モーメント×(1-輝度i間での濃度分布の0次モーメント)で割る。
		interClassVariance[index] = temp*temp / (partial0thMoment[index]*(1-partial0thMoment[index]));
		//この辺は基本的なアルゴリズムを数学的にかなり畳み込んでる。
		//おそらく原本は"Feature Extraction & Image Processing" "Mark S. Nixon, Alberto Aguado" ISBN: 0750650788 の p77~。
		//訳だと"はじめての画像処理技術    ビギナーズブックス" "岡崎 彰夫" ISBN: 4769351224 の p78~。
	}
	//もともとはクラス間分散/クラス内分散でもっとも大きな値をとるような閾値が最適閾値なのだけど、
	//数学的にクラス間分散/クラス内分散が最大となる閾値とクラス内分散が最も大きくなる閾値が等価。よってクラス内分散の最大値となる閾値を求める。
	return std::max_element(interClassVariance.begin(), interClassVariance.end()) - interClassVariance.begin();
}

| | コメント (0)

2005年2月18日 (金)

SHGetFolderLocation

VC6SP5+PSDKの環境でもスタティックリンクできない。ダイナミックリンクすればとりあえずコンパイルは通る。

| | コメント (0) | トラックバック (0)

2005年2月 9日 (水)

重なり合うコントロールのInvalidRect

子コントロール同士で重なりあっていて、それぞれWM_PAINTで描画処理している場合、下のコントロールがWM_PAINTを処理してしまって上のコントロールにWM_PAINTが行かないときがある。
BeginPaint中はValidateRectは使えないので、下のコントロールのWM_PAINTの処理が終わり次第上のコントロールにInvalidateRectしてやればよい。

| | コメント (0) | トラックバック (0)

2005年2月 3日 (木)

JNetHack::倉庫番::4F

続きを読む "JNetHack::倉庫番::4F"

| | コメント (0)

JNetHack::倉庫番::3F

続きを読む "JNetHack::倉庫番::3F"

| | コメント (0) | トラックバック (0)

JNetHack::倉庫番::2F

続きを読む "JNetHack::倉庫番::2F"

| | コメント (0) | トラックバック (0)

2005年2月 1日 (火)

他プロセスのコンソールアプリケーションを全画面表示にする

あんまり自信が無いんだけど、この手の資料の日本語のものが無いので参考までに。


void	Process::SetFullScreenMode()
{
	//プロセス起動直後にAttachConsoleしようとするとたいていうまく行かない。
	Sleep(1000);
	if( AttachConsole( TB_ENFORCE( API(), GetProcessId( mProcess ) ) ) )
	{
		TB_ENFORCE_TO_THROW
		{
			DllProc< BOOL (WINAPI*) (HANDLE,DWORD,COORD*) >	SetConsoleDisplayMode( FilePath( FolderPath::GetWindowsFolder(), "kernel32.dll" ), "SetConsoleDisplayMode" );
			COORD	coord;
			const int CONSOLE_FULLSCREEN_MODE = 1;
			TB_ENFORCE( API(), SetConsoleDisplayMode()( TB_ENFORCE( API(INVALID_HANDLE_VALUE), GetStdHandle( STD_OUTPUT_HANDLE ) ), CONSOLE_FULLSCREEN_MODE, &coord ) );
		}
		TB_ENFORCE_CATCH
		{
			//まずコンソールウインドウのハンドルを何とかしてとってくる。
			//そのためにウインドウタイトルをユニークなIDで置き換える。
			//同じアルゴリズムを使うプログラムと競合する可能性が(0%に近いけど)あるのでIdはGUIDを使う。
			std::string	guid = "{6A47F119-E665-4533-99CB-BDF3ABE2B1D0}";
			//タイトルをバックアップ
			StringReceiver	title;
			TB_ENFORCE( API(), GetConsoleTitle(title.Lock( 1024 ), 1023 ) );
			//置き換える。
			TB_ENFORCE( API(), SetConsoleTitle(guid.c_str()) );
			//別プロセスなのでウインドウタイトルをセットするためのコンテキストスイッチを起こして待つ。
			Sleep(50);
			//ウインドウタイトルを検索してハンドルを得る。
			//win9x系のコンソールのウインドウクラスはtty
			HWND	hConWnd = TB_ENFORCE( API<HWND>(NULL), FindWindow("tty", guid.c_str()) );
			//タイトルを元に戻す
			TB_ENFORCE( API(), SetConsoleTitle(title.UnLock().c_str()) );

			// pause before changing to fullscreen
			//Sleep(450);

			//コンソールウインドウにWM_COMMAND,57359,0を送ると全画面化する。
			SendMessage( hConWnd, WM_COMMAND, 57359, 0 );
		}
		TB_ENFORCE( API(), FreeConsole() );
	}
}

XPのSP2ならAttachConsole→SetConsoleDisplayModeで一発。
ただしSetConsoleDisplayModeはMSDN::English::Win32API::SetConsoleDisplayModeに"Header Declared in Wincon.h; include Windows.h."って書いてあるのに最新版のプラットフォームSDKのWincon.hにも無いのでKernel32.dllから気合でLoadLibrary&GetProcAddressしないとだめです。

挙句にAttachConsoleがなぜかかなりの確率で失敗します。
エラーメッセージは「ハンドルが無効です。」…いや引数ハンドルじゃないし。ハンドルはハンドルで有効だし。もちろんプロセスIDだってあってるよ!
ていうかGetLastError設定してないんじゃなかろか。


while( !AttachConsole( mProcessID ) )
	Sleep(500);
みたいなコードを書いても一度失敗したらなぜか二度と成功しません(アタッチしたい相手先のコンソールは元気に動いてます)。
というわけでAttachConsoleで失敗したら泣く泣く素直にあきらめてます。

また、Win9x系ではコンソールウインドウにWM_COMMANDのWPARAMが57359を送ると全画面化するそうです。
(Ref: http://mureakuha.com/koodikirjasto/635)

ただしこの技はNTでは使えません。
WPARAMが違うのかと思ってSPYでWM_COMMANDメッセージをみようと思ったらSPYがサブクラス化に失敗するので全ウインドウのWM_COMMANDを見張ったところ、確かにモードが変わったとき異なるWPARAMが送られてたのでそいつを上のコードの57359の変わりに送ってみたところ…変化なし。
SetConsoleDisplayModeはなんか"Requires Windows XP."だそうで、結局2000とかのコンソールを最大化させる手段はAlt+Enterをコンソールウインドウに送りつけるとかしかないかも。

| | コメント (0)

NetHack 3.4.3 における祭壇の利用法

寺院を乗っ取る方法

上記の通りノームの鉱山あたりの寺院を乗っ取ると序盤でかなりおいしい思いができます。
が、3.4.3になってパッチがあたったのか上記のページのやり方では乗っ取れません。
「手順」の


   ------ ------ ------ ------ ------ ------ ------ ------
   |....| |....| |....| |....| |....| |....| |....| |....|
   |....| |^...| |^...| |^...| |^O..| |^O..| |^O..| |^@..|
   |.._.| |.^O.| |P^O.| |P^O.| |P^@.| |P^_.| |P^_.| |O^_.|
   |....| |.P@.| |..@.| |...@| |....| |.@..| |.^@.| |P^..|
   -.---- -.---- -.---- -.---- -.---- -.---- -.---- -.----
で、僧侶が梃子でも動こうとしなくなるのです。というかプレイヤーが寺院に入るとほとんど祭壇の周り1マスから離れようとしなくなります。

もう乗っ取れないのかな?と思いがちですが、まだまだ抜け穴があります。
必要なものは祝福された大地の巻物とつるはしです。(あと固い兜があるとダメージを受けなくてすみます。)

まず運命の大迷宮の神託所レベルの更に1階下のレベルにある2つの上り階段の片方から入れる倉庫番レベルに行きます。
倉庫番の1階には大地の巻物が落ちているのでそれを拾ってきます。
そして1枚を祝福します。…何気にこれが大変ですが。運命の大迷宮を駆けずり回って自分の属性の祭壇を探してください。

祝福された大地の巻物が用意できたら寺院へ行って祭壇の上に立ちます。そして兜をかぶって巻物を読みます。
すると自分の上を含めて9マスに岩が落ちてきます。


------	------
|...@|	|....|
|P...|	|.```|
|.._.|	|.P@`|
|....|	|.```|
-.----	-.----
@:プレイヤー
P:僧侶
_:祭壇
このとき僧侶は自分の隣のますにいます。表示されていませんがこの僧侶の上にも岩があります。
岩を落としたら僧侶の隣の岩をつるはしで砕いて脱出します。


------	------	------
|....|	|....|	|.@..|
|.*``|	|.@``|	|.*``|
|.P@`|	|.P``|	|.P``|
|.```|	|.```|	|.```|
-.----	-.----	-.----
んでしばらく足踏みをすると僧侶が動きます。祭壇の周り1マスのうち動けるのはさっき砕いたマスだけなのでそこに移動するでしょう。


------
|.@..|
|.P``|
|.```|
|.```|
-.----
一見このまま僧侶はどこにも行かず固まるのかと思いきや、しばらく足踏みしてると別のマスに移動を始めます。


------
|.@..|
|.*``|
|P```|
|.```|
-.----
こうなったらしめたもの、砕いた岩のますのところに落とし穴を掘って祭壇のまわり一マスに僧侶をいけなくします。
後は僧侶を寺院から追い出したい方向に落とし穴や岩で閉じ込めます。
また、僧侶がこれない方向の岩を砕いて祭壇までの通り道を作ります。


------	------	------	 ------  ------  ------  ------  -.----  -@----  -.----  -.----
|....|	|....|	|....|	 |.P..|  |.P`.|  |P`@.|  |`@..|  |`@..|  |`...|  |....|  |....|
|.@``|	|@^``|	|.^``|	 |.^``|  |.^`@|  |.^`.|  |P^`.|  |P^`.|  |P^`.|  |@^`.|  |.^`@|
|P```|	|P```|	|.```.	 |.```@  |.```.  |.```.  |.```.  |.```.  |.```.  |````.  |``*`.
|.```|	|.```|	|P```|@  |.```|  |.```|  |.```|  |.```|  |.```|  |.```|  |P```|  |P```|
-.----	-.----	-.----	 -.----  -.----  -.----  -.----  -.----  -.----  -.----  -.----
と、まぁこんな感じで乗っ取り完了です。後は祭壇に死体をささげて転換して、レベル低ければ幽霊レベルアップ、その後は僧侶に蟻を召還してもらってレベル上げ&アーティファクトゲット!詳しくは前述のページを参照のこと。

| | コメント (0)

2005年1月31日 (月)

Nethack::テレポート能力でつるはしを持ったまま店に入って商品が未払いのまま穴を掘って下に下りようとすると

「注意してください。穴から落ちますよ。」と店主に言われる。
その後落ちる寸前に店主が背負い袋だけつかんで自分は落ちる。残るのはつるはしと装備品のみ。
要するに盗みはできないらしい…。

| | コメント (0) | トラックバック (0)

2005年1月29日 (土)

Dynabook T5/X16PMEについて

いいところ

画面がSXGA+(1400*1050)なので広い。
ノートパソコンのスピーカーにしてはいい音がする。

悪いところ。

もろい。ちょっと本体を捻ると結構な確率でハードエラーで青画面になったりする。
でかい。とことんでかい。
CD-ROMドライブが打たれ弱い。(どのノートでもそうだけど…。)

4年くらい使うとCPUファンのグリスが切れます。油とかさすと吉。

| | コメント (0) | トラックバック (0)

専門語辞書参照サービス

英単語を調べるのに便利。
Sleipnirの検索に登録するとよいかと。

http://wwwd.nova.co.jp/webdic/webdicc.cgi?&lang=X&dic=BASE&dic=MATH&dic=MEDI&dic=AERO&dic=BUSI&dic=COMP&dic=META&dic=ARCH&dic=OCEA&dic=TRAD&dic=ELEC &dic=CHEM&dic=PLAN&dic=DEFE&dic=FINA&dic=MECH&dic=BIO&dic=ENER&dic=ECOL&dic=LAW&dic=ENTE&magazine=yes

うえのURLにさらに
&adrs=emailアドレス
&word
=検索文字列
を追加して使います。

| | コメント (0) | トラックバック (0)

2005年1月28日 (金)

元に戻す

元に戻すを実装しようとして四苦八苦。
元に戻すはごみ箱からの復元だけでなく、コピーや移動の処理も取り消せる。
ということは結果的にIDragDropやMoveFileExを利用する方法は使えず、IContextMenuを利用して元に戻すを実行するしかないと考えて組んでみたものの。
以下のようなコードを実行したけれど元に戻すのメニューは取得できなかった。


int __stdcall	WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	IMalloc*	pMalloc;
	TB_ENFORCE( NotEqualFail( S_OK ), CoGetMalloc( 1, &pMalloc ) );
	LPITEMIDLIST	pIdList;
	TB_ENFORCE( NotEqualFail( S_OK ), SHGetFolderLocation( NULL, CSIDL_DESKTOP, NULL, NULL, &pIdList ) );

	IShellFolder*	pDesktop;
	TB_ENFORCE( NotEqualFail( NOERROR ), SHGetDesktopFolder( &pDesktop ) );
	// IContextMenuを取得します。
	// m_pDFolderはデスクトップフォルダを示すIShellFolderです。
	IContextMenu*	pContextMenu;
	TB_ENFORCE( NotEqualFail( S_OK ), pDesktop->GetUIObjectOf( NULL, 1, (LPCITEMIDLIST*)(&pIdList), IID_IContextMenu, 0, reinterpret_cast< void ** >( &pContextMenu ) ) );

	HMENU	hMenu = TB_ENFORCE( API< HMENU >( NULL ), ::CreatePopupMenu() );

	// メニューを取得します。
	HRESULT	re = pContextMenu->QueryContextMenu( hMenu, 0, 1, 0xffff, CMF_NORMAL );
	Uint	itemIndex = UINT_MAX;
	//
	TB_LOOP_1( GetMenuItemCount( hMenu ) )
	{
		StringReceiver	receiver;
		if( 0 != GetMenuString( hMenu, index, receiver.Lock( 255 ), 254, MF_BYPOSITION ) )
		{
			//文字列が取得できた。
			if( std::string::npos != receiver.UnLock().find("元に戻す") )
			{
				itemIndex = index;
				break;
			}
		}
	}

	if( UINT_MAX == itemIndex )
	{
		//「元に戻す」がない==削除や移動をしていなくて元に戻すファイルがない。
		return 0L;
	}

	// コマンドを実行します。
	CMINVOKECOMMANDINFO	invokeInfo = { sizeof( CMINVOKECOMMANDINFO ) };
	invokeInfo.fMask = 0;
	invokeInfo.hwnd = NULL;
	invokeInfo.lpVerb = MAKEINTRESOURCE( itemIndex );//MAKEINTRESOURCE( iCommand - 1 );
	invokeInfo.lpParameters = NULL;
	invokeInfo.lpDirectory = NULL;
	invokeInfo.nShow = SW_SHOWNORMAL;
	invokeInfo.dwHotKey = 0;
	invokeInfo.hIcon = NULL;

	TB_ENFORCE( NotEqualFail( NOERROR ), pContextMenu->InvokeCommand( &invokeInfo ) );

	window.Wait();
	pContextMenu->Release();
	pDesktop->Release();
	pMalloc->Free( pIdList );
	pMalloc->Release();

	return 0L;
}


エクスプローラー(&X)とかは取れるんだけどなぁ。
フォルダの拡張メニューはIShellFolderからGetUIObjectOfしたのではだめなのかもしれない。


上の記事の後に前進があったのでメモ

・CoInitializeとCoUninitializeを忘れてた。追加したら突如としてQueryContextMenuが以前より多い数のメニューを登録してきた。
謎。エラーになってるなら最初からエラー終了してほしい。

・フォルダのコンテキストメニューは無事取得成功。
したのはいいんだけど冷静になって考えると「元に戻す」があるのはフォルダの右クリックメニューではなくて、エクスプローラーの右ペインの何も無いところを右クリックしたときに出てくるメニュー。
これをとるためにIShellViewをとって、それのGetItemObjectを使った。
無事コンテキストメニューが取れた…のだが。

ない。狙ったように元に戻すの項目だけメニューに追加されてこない。
QueryContextMenuのパラメーターをいろいろといじるものの二進も三進も行かず。
IContextMenuでInvokeCommandすればいいなんて言い出したのは誰なんだ…。教えてほしい(^^;
とりあえずいったんあきらめた次第。

・IShellViewのメソッドをにらむにTranslateAcceleratorというのがある。これでCTRL+Zを送ってやろうかと思ったものの失敗。
MSG構造体を偽造するのは難しい。

int __stdcall	WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	CoInitialize( NULL );

	IMalloc*	pMalloc;
	TB_ENFORCE( NotEqualFail( S_OK ), CoGetMalloc( 1, &pMalloc ) );
	LPITEMIDLIST	pIdList;
	TB_ENFORCE( NotEqualFail( S_OK ), SHGetFolderLocation( NULL, CSIDL_DESKTOPDIRECTORY, NULL, NULL, &pIdList ) );

	IShellFolder*	pDesktop;
	TB_ENFORCE( NotEqualFail( NOERROR ), SHGetDesktopFolder( &pDesktop ) );

	// IContextMenuを取得します。
	IContextMenu*	pContextMenu;
	IShellView*		pView;
	TB_ENFORCE( NotEqualFail( S_OK ), pDesktop->CreateViewObject( NULL, IID_IShellView, reinterpret_cast< void ** >( &pView ) ) );
	TB_ENFORCE( NotEqualFail( S_OK ), pView->GetItemObject( SVGIO_BACKGROUND, IID_IContextMenu, reinterpret_cast< void ** >( &pContextMenu ) ) );

	// IContextMenu2へQueryInterface
	IContextMenu3*	pContextMenu2;
	TB_ENFORCE( NotEqualFail( S_OK ), pContextMenu->QueryInterface( IID_IContextMenu3, reinterpret_cast< void ** >( &pContextMenu2 ) ) );
	pContextMenu->Release();

	HMENU	hMenu = TB_ENFORCE( API< HMENU >( NULL ), ::CreatePopupMenu() );

	// メニューを取得します。
	HRESULT	re = pContextMenu2->QueryContextMenu( hMenu, 0, 1, 0xffff, CMF_NODEFAULT );
	Uint	itemIndex = UINT_MAX;
	//
	TB_LOOP_1( GetMenuItemCount( hMenu ) )
	{
		StringReceiver	receiver;
		if( 0 != GetMenuString( hMenu, index, receiver.Lock( 255 ), 254, MF_BYPOSITION ) )
		{
			//文字列が取得できた。
			if( std::string::npos != receiver.UnLock().find("元に戻す") )
			{
				itemIndex = index;
				break;
			}
		}
	}

	if( UINT_MAX == itemIndex )
	{
		//「元に戻す」がない==削除や移動をしていなくて元に戻すファイルがない。
		return 0L;
	}

	// コマンドを実行します。
	CMINVOKECOMMANDINFO	invokeInfo = { sizeof( CMINVOKECOMMANDINFO ) };
	invokeInfo.fMask = 0;
	invokeInfo.hwnd = NULL;
	invokeInfo.lpVerb = MAKEINTRESOURCE( 7 );//itemIndex );//MAKEINTRESOURCE( iCommand - 1 );
	invokeInfo.lpParameters = NULL;
	invokeInfo.lpDirectory = NULL;
	invokeInfo.nShow = SW_SHOWNORMAL;
	invokeInfo.dwHotKey = 0;
	invokeInfo.hIcon = NULL;

	TB_ENFORCE( NotEqualFail( NOERROR ), pContextMenu2->InvokeCommand( &invokeInfo ) );

	pContextMenu2->Release();
	pDesktop->Release();
	pMalloc->Free( pIdList );
	pMalloc->Release();

	CoUninitialize();

	return 0L;
}

| | コメント (0)

2005年1月27日 (木)

ごみ箱から指定のファイルを「元に戻す」方法

ごみ箱にファイルを「元に戻せるように」捨てるのは

void	FileSystem::Delete( const FilePath& path )
{
	CountDownTimer	timer( 2000 );
	while( timer() )
	{
		//ゴミ箱に捨てる。
		MultiString	deletefilename;

		SHFILEOPSTRUCT shFileOp;
		shFileOp.hwnd = NULL;
		shFileOp.wFunc = FO_DELETE;
		shFileOp.pFrom = deletefilename.Make( vector< string >( 1, path() ) );
		shFileOp.pTo = NULL;
		shFileOp.fFlags = FOF_ALLOWUNDO|FOF_NOCONFIRMATION|FOF_NOERRORUI;
		shFileOp.fAnyOperationsAborted = TRUE;
		if( SHFileOperation( &shFileOp ) == 0 )
			return;
		//直前まで書き込みが行われていた場合など、エラーになるときがある。2秒まで再試行
	}
	TB_ENFORCE( TrueFail(), ("FileSystem::Deleteに失敗:" + path()).c_str() );
}

まぁなんかこんな感じでSHFileOperationとFOF_ALLOWUNDOを使えばよい、というのはググればすぐに出てくるが、
肝心の「元に戻す」方法はほとんど資料がない。どうも話によるとごみ箱のコンテキストメニューをIFolderとIContextMenuを使って取得して
InvorkCommandで元に戻すを実行すればできる…らしいという話はある。

が、それも直前の操作を元に戻すものでしかない。ごみ箱に捨てた○○というファイルを元に戻すにはどうすればいいのか。

まずごみ箱はたいていC:\RECYCLERの下にある。ちなみにNT系を前提とする。しかも今回紹介する方法は2k以降でしか使えない。
C:\RECYCLERの下に、SID(セキュリティ識別子)と呼ばれるたいてい"S-1-5-21"といった文字列から始まるIDのフォルダが切られて、その中にごみ箱のファイルがある。ちなみに"Dc"+番号.拡張子という名前にリネームされている。

元のファイル名の情報はどこにあるのかというと、同じフォルダに"INFO2"という隠しファイルがあってそれがデータベースとなっている。
INFO2のフォーマットは
ヘッダ(謎)+(元のファイル名(ASCII)+NULL領域+Dcの後に来る番号(2バイト)+NULL領域+マルチバイトに変換されたファイル名+NULL領域)*
というもののようだ。元に戻した場合ファイル名のレコードは削除されないが、元のファイル名の最初の一文字が削られることで無効フラグとなっているらしい。

と、いうわけでかなり怪しげなものの、以下の方法でごみ箱から指定のファイルを元に戻すことができる。
・ごみ箱のINFO2ファイルを開く。
・元の位置のパスでINFO2を検索。
・見つかったらそのファイル名の後ろのNULL領域の次に来る2バイトがごみ箱内のファイルの番号。
・ごみ箱のファイルを元の位置に移動する。

INFO2ファイルのパスはIShellFolderを利用することで取得できる。
データベースファイルはどの環境でも"INFO2"というファイル名なのか、中のフォーマットは本当に正しいのか、ごみ箱のリネーム後のファイル名のヘッダはどこでも"Dc"なのか、などこの辺は確かな情報はない模様。

とりあえずうちで動作する「元に戻す」関数。

//! ごみ箱から指定のファイルを検索して復元
void	FileSystem::Restore( const FilePath& restoreFile )
{
	//ごみ箱内のファイルをリストアップ
	std::vector< FilePath >	paths;
	{
		//ShellAPIを使う。
		IMalloc*	pMalloc;
		TB_ENFORCE( NotEqualFail( S_OK ), CoGetMalloc( 1, &pMalloc ) );
		LPITEMIDLIST	pIdList;
		TB_ENFORCE( NotEqualFail( S_OK ), SHGetFolderLocation( NULL/*hwnd*/, CSIDL_BITBUCKET, NULL, NULL, &pIdList ) );
		IShellFolder*	pDesktop;
		TB_ENFORCE( NotEqualFail( NOERROR ), SHGetDesktopFolder( &pDesktop ) );
		IShellFolder*	pTrash;
		TB_ENFORCE( NotEqualFail( S_OK ), pDesktop->BindToObject( pIdList, NULL, IID_IShellFolder, reinterpret_cast< void ** >( &pTrash ) ) );
		LPENUMIDLIST	pEnumIDList;
		TB_ENFORCE( NotEqualFail( NOERROR ), pTrash->EnumObjects( NULL/*hwnd*/, SHCONTF_NONFOLDERS |SHCONTF_INCLUDEHIDDEN | SHCONTF_FOLDERS, &pEnumIDList ) );
		LPITEMIDLIST	pFileIDList;
		ULONG			result;
		while( pEnumIDList->Next( 1, &pFileIDList, &result ) == NOERROR )
		{
			// ファイルパスの取得。
			STRRET		stFileName;
			TB_ENFORCE( NotEqualFail( NOERROR ), pTrash->GetDisplayNameOf( pFileIDList, SHGDN_FORPARSING, &stFileName ) );
			paths.push_back( String::ToAscii( stFileName.pOleStr ) );
			pMalloc->Free( stFileName.pOleStr );
			pMalloc->Free( pFileIDList );
		}
		pMalloc->Free( pEnumIDList );
		pTrash->Release();
		pDesktop->Release();
		pMalloc->Free( pIdList );
		pMalloc->Release();
	}
	//ごみ箱が空なら帰る。
	TB_ENFORCE( TrueFail(), paths.empty() );
	//ごみ箱の元のファイル名データベースを開く。
	FilePath	info2Path( paths[0].GetFolder(), "INFO2" );
	TB_ENFORCE( FalseFail(), FileSystem::IsExist( info2Path ) );
	//INFO2を開く。
	BinaryFile	info2( info2Path );
	//検索
	std::string	restoreString = restoreFile();
	char*	pInfo2 = reinterpret_cast< char* >( info2() );
	char*	pInfo2End = pInfo2 + info2.GetSize();
	char*	pFilePosition = std::search( pInfo2, pInfo2End, restoreString.begin(), restoreString.end() );
	TB_ENFORCE( TrueFail(), pFilePosition == pInfo2End );
	//見つかった。パスの後にNULLが連続していて、その後にファイルの番号が来る。
	pFilePosition += restoreString.length();
	while( pFilePosition < pInfo2End && '\0' == pFilePosition[0] )
		++pFilePosition;
	//ごみ箱内からIDを検索する
	std::string	id = String( *reinterpret_cast< Uint16* >( pFilePosition ) )();
	FilePath	source;
	TB_FOR_EACH( std::vector< FilePath >, paths )
	{

		std::string	basename = it->GetBaseName();
		std::string::size_type	index = basename.find( id );
		if( std::string::npos == index )
			continue;
		//Dc198.lzhで、9番がヒットするのを防ぐ。
		//見つけた位置にIDの長さを加えたらベース名の長さになること。
		if( basename.length() != index + id.length() )
			continue;
		//ありえるのかわからないけど、ファイル名部分がIDと一緒ならいいとする。
		//198.lzhみたいな。
		if( basename.length() != id.length() )
		{
			//IDが見つかった位置はベース名の最後。
			//サイズが違うのならindexは0以上。
			//index-1が数字だったらやり直す。
			//98のときに198.lzhみたいな。
			if( '0' <= basename[index-1] && basename[index-1] <= '9' )
				continue;
		}
		//ここまでくれば見つかったことになる。
		source = *it;
		break;
	}
	//元に戻す
	FileSystem::Move( source, restoreFile );
}

| | コメント (0) | トラックバック (0)

2005年1月26日 (水)

JNethack::倉庫番::1F

 

続きを読む "JNethack::倉庫番::1F"

| | コメント (0) | トラックバック (0)

2005年1月24日 (月)

ディスクの管理を一発で起動する。

AirG Development (Windowsで比較的に使用頻度の高いコマンド)
を参照のこと。

| | コメント (0) | トラックバック (0)

2005年1月14日 (金)

ファイルサイズの取得

エクスプローラでファイルのプロパティを見ると「サイズ」という項目と「ディスク上のサイズ」という項目がある。
この「ディスク上のサイズ」をとるのは工夫がいる。
以下のような感じ。SetupAPIをつかう。
{
	HDSKSPC	hDiskSpace = SetupCreateDiskSpaceList( NULL, NULL, SPDSL_IGNORE_DISK );
	SetupAddToDiskSpaceList( hDiskSpace, path().c_str(), GetSize( path ), FILEOP_COPY, NULL, NULL );
	LONGLONG	size;
	SetupQuerySpaceRequiredOnDrive( hDiskSpace, path.GetDriveLetter().c_str(), &size, NULL, NULL );
	SetupDestroyDiskSpaceList( hDiskSpace );
}
「サイズ」はGetFileSizeで取得できるように思うが、実際にはこの関数はそれほど性能がよくない。
GetFileSizeを使うにはハンドルが必要でファイルをCreateFileで開かなければならない。
が、たまに非共有モードですでにファイルが開かれていたりするとCreateFileは失敗する。

んじゃぁどうするのかというとFindFile系を使う。以下のような感じ。

{
	const char*	pFolder = "c:\\*";
	const char*	pFileName = "test.txt";
	const char*	pFullPath = "c:\\test.txt";

	WIN32_FIND_DATA findData;
	HANDLE	hFind = FindFirstFile(pFullPath,&findData);
	if( hFind != INVALID_HANDLE_VALUE )
	{
		FindClose( hFind );
		return findData.nFileSizeHigh*MAXDWORD+findData.nFileSizeLow;
	}
	else
	{
		//FindFirstFileではだめなのに、FindNextFileだと見つかる場合がある。ものすっごく謎。
		//アクセス権がないファイルの場合などに起こるみたいな気がする。
		hFind = FindFirstFile( pFolder, &findData );

		for( BOOL result = TRUE; result; result = FindNextFile( hFind, &findData ) )
		{
			if( lstrcmp( pFileName, findData.cFileName ) == 0 )
			{
				FindClose( hFind );
				return findData.nFileSizeHigh*MAXDWORD+findData.nFileSizeLow;
			}
		}
		//なければエラー
		FindClose( hFind );
		assert( !"ファイルが見つかりませんでした。" );
		return 0;
	}
}
ちゃんとやろうとすればするほどコードが長くなるというお話。

| | コメント (0)

2005年1月12日 (水)

クラスのデストラクタについて

基本的にクラスのデストラクタは仮想にしてthrow()をつける。

class A
{
public:
	virtual ~A() throw(){}
};

仮想にするのは派生クラスのデストラクタを確実に呼ぶため。

class	A{ public: ~A(){ assert(0); } };
class	B : public A{};

{
	B	b;
}

上記の場合、Aのデストラクタは呼ばれない。

派生しないクラスの場合仮想にすると仮想関数テーブルの分だけクラスのサイズが増えるし、書かなくても自動でデストラクタが作られるが、上記が問題となるバグはvirtualの付け忘れに起因することが多い。
そしてたいていこのバグは非常に見つかりにくく、3時間くらいは時間を無駄にしてしまう。
だからたとえ派生させるつもりがないクラスでも習慣的に仮想デストラクタを書く癖をつけたほうがいいと思う。

throw()をつけるのはデストラクタから例外を呼ばないことを保障するため。
class	A{ public: virtual	~A(){ throw 0; } };

try
{
	A	a5;
	{
		A	a4;
		{
			A	a3;
			{
				A	a2;
				{
					A	a1;
				}	//1
			}
		}
	}
}
catch( int& i )
{
	//2
}

上記のコードを追っていくと1のところでa1がスコープアウトしてAのデストラクタが呼ばれ、throw 0;によりスコープの巻き戻しが始まる。
次にa2が解体され、a2のデストラクタで再び例外が投げられる。
このとき例外処理中に例外が起こったことでterminateが呼ばれてプログラムは終了してしまう。
つまりa3~a5は解体されないし、2のところまで実行されることもない。

このためデストラクタでは例外は呼ばないほうがいい。
たとえ例外がネストしないような設計で組んでいても、ふと疲れたときやコードの複雑化で間違えてネストさせてしまうとまたしても解決に時間のかかるバグとなる。

| | コメント (0)

2005年1月11日 (火)

std::copy

std::copyは内部でmemmoveを使っている。
安全性を考えると仕方ないが、遅い。速度をとりたいときはmemcpy

| | コメント (0)

2004年12月20日 (月)

BoostとVC6

VC6では最新(1.32.0)のSplitはコンパイルすらされない。
そこでSpritが使える最後のバージョンである1.30.2を使おうとすると、今度は

C:\Program Files\boost-1.30.2\boost/random/exponential_distribution.hpp(43)
C:\Program Files\boost-1.30.2\boost/random/normal_distribution.hpp(49)
C:\Program Files\boost-1.30.2\boost/random/uniform_01.hpp(44)

の3箇所の行にある
BOOST_STATIC_ASSERT(!std::numeric_limits<RealType>::is_integer);<br>


error C2027: 認識できない型 'STATIC_ASSERTION_FAILURE' が使われています。

というエラーが起こる。
http://boost.cppll.jp/HEAD/more/int_const_guidelines.htmの「numeric_limits に気をつけなさい」の項にあるようにこれはVC6&boostのバグのようだ。

これを回避するには

#ifdef BOOST_MSVC
	BOOST_STATIC_CONSTANT(bool, check = !std::numeric_limits::is_integer);
	BOOST_STATIC_ASSERT(check);
#else
	BOOST_STATIC_ASSERT(!std::numeric_limits::is_integer);
#endif

に前述の行を置き換える。

| | コメント (0)

2004年11月15日 (月)

BCB::子フォームを全て一気に閉じる

for( int index = MDIChildCount-1; index >= 0; --index )
	MDIChildren[index]->Close();

| | コメント (0)

2004年10月25日 (月)

Doxygenマニュアル日本語訳

Doxygen を使おうDoxygenマニュアル日本語訳が古くなっていたので新しい分を追加したものです。

Library/DoxygenReference.jp/index.html

訳に責任は持ちませんのであしからず。あと結構前に訳したものなので最新のは載ってません。

| | コメント (0)

2004年10月13日 (水)

IDirectDrawClipper::Release

すでにどれかのサーフェィスに関連付けられていると失敗する。
サーフェィスを先に解放するかIDirectDrawSurface::SetClipperでNULLを渡してクリッパを解除すること。

| | コメント (0)

2004年10月 2日 (土)

免許試験問題

そろそろ卒免学科を受ける。
それにあたって勉強でもするかと思っていたら、一緒に受ける予定の友達がこんなものを見つけてきた。

http://www.vector.co.jp/soft/dl/win95/edu/se131606.html

試験対策ソフトとしては頭が悪いことこの上ない。System3.xをこんな風に使うとはw
まぁ今でこそ古臭いUIだけど、System3.Xは古くからある優秀なスクリプト環境なわけでもっとこういう使われ方をされてもいいのかもしれない。
Vectorの解説が結構まともなのが笑いに拍車を掛けてくれる。

が、何気に中身は結構まとも。
問題もまっとうだし、間違えたときにちゃんと正解を表示してくれる。
教習所で仮免卒免の問題集を1000円で買ったけど、それの半分くらいの問題数はあるだろう。

これから免許取ろうという人にお勧め…かもしれない。

| | コメント (0)

2004年10月 1日 (金)

PC自作時トラブルシューティング系各種URL

http://okweb.jp/kotaeru.php3?q=474463からの転載。便利そうだったので。

バイオス警告音
   http://www.redout.net/data/bios.html
メモリーテスト方法
   http://www.memtest86.com/#download0
   http://www.kiss.taihaku.sendai.jp/~fuji/memtest86.html
   http://intel800.tripod.co.jp/mem86.htm
Q&A
   http://www6.milkcafe.to/~torim/br/01/
   http://www.geocities.jp/ryo3302/KOMA04.HTM
   http://homepage2.nifty.com/winfaq/w2k/w2kfaq.html
役に立つ所
   http://www.asahi-net.or.jp/~rr5k-inue/makepaso.html
   http://support.microsoft.com/
   http://winfaq.jp/
   http://www6.milkcafe.to/~torim/br/01/
   http://www.ne.jp/asahi/takaman/pc/index.html
組み立てマニュアル
   http://www.dospara.co.jp/support/manual/
   http://vmag.vwalker.com/feature/art.asp?newsid=3557
   http://www.kumitate.web-info.jp/index.htm
   http://bb.goo.ne.jp/special/IT/wpc.html (動画)
電源関係
   http://terasan.okiraku-pc.net/dengen/index.html
   http://www.ne.jp/asahi/takaman/pc/case.html
   http://vmag.vwalker.com/feature/art.asp?newsid=4315
   http://www.ne.jp/asahi/takaman/pc/psu_calc.html
電源単体テスト方法 (14ピンと13又は15ピンを繋ぐ)
    http://homepage2.nifty.com/~amaki/sei/cont20-20.htm
BIOS書き替え
   http://www.aopen.co.jp/tech/download/mbbios/mbflash.htm
   http://www.aopen.co.jp/tech/faq/mb/mbbios.htm
AMD ヒートシンク取り付け方法
   http://www.ne.jp/asahi/takaman/pc/athlon_heatsink_install.html
INTEL ヒートシンク取り付け方法
   http://www.intel.com/jp/support/processors/pentium4/installation_478.htm
RAID
   http://www.adaptec.co.jp/raid/whatraid/index.html )
BIOS
   http://www.tadachi-net.com/desktop_pc/bki810/v16/bios.html
   http://www.plaza.across.or.jp/~kusunoki/bios.htm
メモリーの相性
   http://www.atmarkit.co.jp/fsys/kaisetsu/008mem_test_tool/mem_test0

| | コメント (0)

2004年9月30日 (木)

TortoiseCVSとWinCVS

WinCVSでpserverのレポジトリをチェックアウトして、その作業コピーをTortoiseCVSでUpdateやCommitしようとしてもうまくいかないことがある。


cvs server: cannot open (...): Permission denied
protocol error: directory '(...)' not within root '(...)'
その場合、上記のnot within root '(...)'の(...)部分のパス区切り文字は、directory '(...)'の(...)部分のパス区切り文字と同一だろうか。

自分の場合not within rootのほうが'\'でdirectoryのほうが'/'になっていた。
WinCVSが気を利かせて'/'から'\'に変換してしまうのだろうか。
WinCVSはうまく動いてたもののTortoiseCVSはこれが違うだけで上記のようなエラーを出していた。

これを修正するには各CVSフォルダのRootファイルの中のパス区切り文字をCVSROOTにあわせればよい。

| | コメント (0)

2004年9月10日 (金)

ごり押しデバッグ

ヘッダファイルにinline関数を書くとき、inline指定を書き忘れることがある。
このとき、BCBだとヘッダに初期化データがあるためプリコンパイルヘッダが作成できないというエラーが出る。
…それだけならいいんだけど、問題はエラーが発生する行が頓珍漢なファイルと行であることがある。
たいていvclのヘッダファイルでエラーとなるようだ。

追跡するのは非常にめんどくさい。

まずcppファイルの先頭から末尾までをコメントアウトする。
そしてcppファイル単体でコンパイル。
この時点ではエラーは出ないはず。(出たらプロジェクトオプションなど、コード以外の問題の可能性が高い)

次にヘッダファイルをインクルードしている行を1行コメントアウトされているところから抜き出してコンパイル。
これを1行ごとに繰り返す。
エラーが出たらそのヘッダファイルが原因の可能性が高い。
そのヘッダファイルをすべてコメントアウトした後、1行づつ有効にさせてコンパイルを繰り返す。

上記をひたすら繰り返せば原因となる行が特定できる。これはどこでエラーになっているのかわからないエラーを解明するのに使える。

| | コメント (0)

2004年8月30日 (月)

dequeを使うとき。

はじめてSTLのdequeを使った。
使うことあんのかこんなクラスと思っていたが…意外。

リストボックスを使うWin32アプリで、追加ボタンを押すと新しい項目がリストボックスの先頭に追加される。
リストボックスと同期したSTLコンテナを使う。
つまりこの場合STLコンテナにはpush_frontがほしい。
また、リストボックスの任意の項目が選択されて実行ボタンが押されたら、それに対応するデータをSTLコンテナから取り出したい。
このとき得られる情報はリストボックスでのオフセット。つまりランダムアクセスを使う。
この場合dequeが適任だった。あるんだねぇ使うとき。

| | コメント (0)

2004年6月13日 (日)

子ウインドウの半透明化はどうやら不可能。

子ウインドウの半透明化をしようと思って挑戦したんですが挫折しました。
その記録まで。

・ウインドウを半透明化するには拡張ウインドウスタイルにWS_EX_LAYEREDを指定します。
これはCreateWindowExの時でもいいですし、作成後やすでにあるウインドウに対してSetWindowLongを行っても構いません。
WS_EX_LAYEREDを指定したらそのウインドウに対してSetLayeredWindowAttributesを実行します。

と、半透明化自体は結構簡単に出来ます。
ただ、子ウインドウに対してWS_EX_LAYEREDを適用しようとすると失敗します。
しかもSetWindowLongはエラーを設定しません。

HWND	hWnd;

	//hWndに対象のウインドウハンドルを取得。

//対象の拡張スタイルを取得
LONG	style = GetWindowLong(hWnd, GWL_EXSTYLE);

	//半透明化フラグを追加。
	style |= WS_EX_LAYERED;

//エラーフラグをクリア
SetLastError(0);

//拡張スタイルをセット
if(!SetWindowLong(hWnd, GWL_EXSTYLE, style))
{
	//戻り値が0。エラーフラグを確認
	if( GetLastError() != 0 )
		assert(0);	//エラーフラグが0以外ならエラー
}

//確認のため
LONG style2 = GetWindowLong(hWnd, GWL_EXSTYLE);

assert( style == style2 );	//…のはず。
上記のソースでassert(0)にはならない(つまりSetWindowLongは成功している)くせにassert( style == style2 );は成立しません。(もともとWS_EX_LAYEREDフラグがたっていないとした場合。)
つまり、SetWindowLongはちゃんと働いたフリをしてるけど実は何もしてないと。

ちなみに
style &= ~WS_EX_CLIENTEDGE;
という風に、他のフラグをいじった場合は普通に動作します。
また、
style &= ~WS_EX_CLIENTEDGE;
style |= WS_EX_LAYERED;
と、他のフラグと一緒にいじると他のフラグごとSetWindowLongは無視します。つまりWS_EX_LAYEREDを子ウインドウに設定しようとするとSetWindowLongは無視するわけです。

| | コメント (0)

2004年6月 1日 (火)

BCB6とlocale

builderではlocaleが__BORLAND指定されていないとプリコンパイルできない。
指定をしている状態で、boost/format.hppを使うと内部コンパイルエラーになる。
boost/format/feed_args.hppの無名名前空間の中身をboost::io::detailsに出すとコンパイルが通るようになる。
ネストされすぎているからか・・・?

| | コメント (0)

2004年4月11日 (日)

WinSCPでファイルをリネームするときは気をつけよう。

すでにあるファイルと同名にリネームすると元のファイルが上書きされる…。orz

| | コメント (0)

2004年2月16日 (月)

自己言及型テンプレート

template< typename T >
class	X{};

class	A :
	public X< A >
{
};
これは合法。


class	B{};

template< typename T >
class	X{};

template< typename U >
class	A :
	public X< A< B > >
{};
これも合法。
でも、Bの代わりにXを指定することは出来ない。当たり前だけど。


template< typename T >
class	X{};

template< typename U >
class	A :
	public X< A< X < ... > > >
{};
こう、順を追って行けばわかりやすいけどいきなり最後のが出てきたりして、AやXのテンプレート引数が4つも5つもあるとそろそろ死ねる。
気づくのにえらい時間がかかった。

| | コメント (0)

2004年2月14日 (土)

クラステンプレートのテンプレートメンバ関数。

#include <windows.h>

template< typename T >
class	A
{
public:
	template< typename U >
	void	foo( A<U> pU );
};
template< typename T >
template< typename U >
inline void	A<T>::foo( A<U> pU )
{
	int t = 0;
}


int __stdcall	WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	A<int>	a;
	A<char>().foo( a );
	return 0;
}
俺は今まで


template< typename T, typename U >
inline void	A<T>::foo( A<U> pU )
{
	int t = 0;
}
こう書くもんだと思ってたんですが実は


template< typename T >
template< typename U >
inline void	A<T>::foo( A<U> pU )
{
	int t = 0;
}
こうらしいですね。

| | コメント (0)

2004年1月 3日 (土)

VC2003のマクロエクスプローラーの挙動

VC.NETで既存のマクロプロジェクトを追加した後、マクロエクスプローラーで見てもモジュールは表示されるものの個々のマクロが表示されない
→右クリックから「新しいマクロ」を選択するとマクロIDEが開く。この後またVC.NETに戻るとここのマクロもちゃんと表示されている。VC.NETのバグ?

| | コメント (0)

2003年12月 9日 (火)

LaTeXのコマンド

見やすいLaTeXのコマンドリファレンスってあんまないですよね。

LaTeXしよう!
TeX書くならここみると便利。たいていのコマンドのってます。

| | コメント (0) | トラックバック (0)

2003年11月30日 (日)

ルートのConfig.MSIフォルダはなに?

MSインストーラーがインストールに使うテンポラリファイルを保存しとく場所です。
削除しちゃってもOKです。

| | コメント (0)

2003年11月25日 (火)

秀丸とVisual Studio .NETの連携方法

| | コメント (0) | トラックバック (0)

秀丸とVisual Studio .NETの連携方法

http://www.sofarts.com/oldnew/computer/env-soft/windows/app_programming/vs/net/hidemaru.htm

ここで出てくるvscmd.zipをコンパイルしてexeにしたもの。
vscmd_031125.zip
コンパイルしなくても、回答して出てきたvscmd.exeを秀丸のフォルダに放り込むだけで使えます。

| | コメント (0)

2003年11月24日 (月)

Cランタイムライブラリの関数で処理の所要時間を計るには。

clockを使います。

#include <stdio.h>
#include <time.h>

////////////////////////////////////////////////////////////////////////////////

void	a()
{
	for( int i = 0; i < 1000000000; ++i );
}

////////////////////////////////////////////////////////////////////////////////

void	b()
{
	for( int i = 0; i < 100000000; ++i );
}

////////////////////////////////////////////////////////////////////////////////

int	main()
{
	clock_t last = clock();
	a();
	double span = clock() - last;
	printf( "%f", span / CLOCKS_PER_SEC );

	last = clock();
	b();
	span = clock() - last;
	printf( "%f", span / CLOCKS_PER_SEC );

	return 0;
}

////////////////////////////////////////////////////////////////////////////////

| | コメント (0) | トラックバック (0)

Visual C++ 6.0でprofileが使えない。

プログラム中の関数の実行時間などを測れるプロファイラですが、VC6.0のプロファイラはバグまみれでなかなか使えません。




まず、メニューのビルド->プロファイルが無効になっているケース。
この場合は
HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\General
のProfilerInstalledキーに1が設定されているかどうかをチェックしよう。
ちなみにこれが有効にするためのレジストリファイル




次に、プロファイルを使うにはプロジェクトオプションを変更しなければなりません。

メニューのプロジェクト→設定→リンクタブ

このウインドウに「プロファイルを行う(&E)」というメニューがあるものの、チェックしてもプロファイルは有効にはなりません。
そこでプロジェクトオプションと云うフォームがあるのでそこに/profileと手書きで追加します。
(もしくはAlt+Eでもいいです。)

Ref: http://www.cmpt.phys.tohoku.ac.jp/~genya/vcprof.html

| | コメント (0)

「Windows Update エラー」となってアップデートできない。

パソコンの日付が狂ってるケースが多いのでチェックしてみましょう。

| | コメント (0)

メモリリークに関する考察。

new/deleteにまつわるメモリリークを検出しようと考えた。
new/deleteに関するメモリは全てスマートポインタであるPointerクラスで管理しているので、
Pointerクラスの機能としてメモリリークチェッカーを作ることにした。

newされたメモリは即座にPointerクラスのSetメソッドに渡される。これはPointerクラスの仕様。
そしてdeleteは全てPointerクラス内で行われる。

つまりSetメソッドに入ってきたアドレスとdelete直前のアドレスのログをとればメモリリークがあるかどうかチェックできる。
たとえばSetメソッドに入ってきたアドレスがdeleteされていなかったらメモリリークとなる。




が。C++には多重継承があるのでnewで返ったアドレスと、deleteに渡すアドレスがたとえ同じオブジェクトでも同一とは限らない。
このケースは確かに特殊で、多重継承していて、なおかつ基底クラスが仮想関数を持っていることが条件となる。
が、なんにせよアドレスが違ってしまうので単にnewのアドレスとdeleteのアドレスを==で比較して違っていたらメモリリーク、とは出来ない。


class	A
{
public:
	virtual ~A(){}
};
class	C
{
public:
	virtual ~C(){}
};
class	B :
	public A,
	public C
{};

int __stdcall	WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	B*	pB = new B;
	A*	pA = pB;
	C*	pC = pB;

	//pAとpCはアドレスが異なる!!

	size_t b = sizeof(B);
	size_t c = sizeof(C);

	delete	pA;
}
このコードでは実際にそれを検証している。

以下がdelete文の直前でのウオッチウインドウである。







+ pA 0x00372f88 A *
+ pB 0x00372f88 B *
+ pC 0x00372f8c C *
b 8 unsigned int
c 4 unsigned int


pAとpBは等しいが、pBとpCが異なるのがわかると思う。

ここで注目したいのがbとcである。
B型とC型ではサイズが異なる。B型のほうが大きい。


|372f88-------------B:8-----------------|

+----+----+----+----+----+----+----+----+

			|372f8c----C:4------|
このようにBはCを含んでいる。

よくよく考えてみれば、Bは基底クラスであるAとCの情報を含んでいなければならない。そうでなければAとCに安全にキャストできない。
つまり「派生クラスの使用領域は基底クラスの使用領域を含む」と推論できる。多分そう外れてないと思う。




newされたオブジェクトをさらに派生した型に安全にアップキャストすることは出来ない。
つまりnewされたときの型より派生されることはない。=newされたときのオブジェクトの使用領域が最大である、といえる。
deleteするときにはそれと同じか、それより少ない領域となっている。また、その領域はnew時のものに含まれる。

これを利用すればメモリリークは検出できるだろう。…と思う。

| | コメント (0) | トラックバック (0)

_CrtSetDbgFlag

_CrtSetDbgFlagや_CrtCheckMemoryで_CRTDBG_CHECK_CRT_DFを使うと大量にメモリリークのログが出るけど、どうも自分のプログラムと関係がないっぽい。

ためしに、


#include <windows.h>
#include <crtdbg.h>

////////////////////////////////////////////////////////////////////////////////

int __stdcall	WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	_CrtSetDbgFlag( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_CRT_DF );
	return 0L;
}

////////////////////////////////////////////////////////////////////////////////
上記のコードでやってみたんですが、結果は


Detected memory leaks!
Dumping objects ->
_file.c(137) : {42} crt block at 0x003707B0, subtype 0, 2048 bytes long.
 Data: < ~B	 B 0 B P B > F0 7E 42 00 10 7F 42 00 30 7F 42 00 50 7F 42 00
onexit.c(198) : {41} crt block at 0x00372EC8, subtype 0, 128 bytes long.
 Data: <  A		   > C3 10 41 00 CD CD CD CD CD CD CD CD CD CD CD CD
stdenvp.c(130) : {40} crt block at 0x00372BF8, subtype 0, 654 bytes long.
 Data: <_ACP_PATH=C:\Pro> 5F 41 43 50 5F 50 41 54 48 3D 43 3A 5C 50 72 6F
	:
stdenvp.c(117) : {4} crt block at 0x003716F0, subtype 0, 148 bytes long.
 Data: <  7 0 7	 7 ( 7 > C0 17 37 00 30 18 37 00 B8 18 37 00 28 19 37 00
stdargv.c(130) : {3} crt block at 0x00371648, subtype 0, 102 bytes long.
 Data: <P 7	   c:\docum> 50 16 37 00 00 00 00 00 63 3A 5C 64 6F 63 75 6D
ioinit.c(130) : {1} crt block at 0x00370670, subtype 0, 256 bytes long.
 Data: <		   > FF FF FF FF C1 0A CD CD FF FF FF FF C1 0A CD CD
Object dump complete.
はぁ?って感じですが。

どうもCラインタイムライブラリの段階でがっつりメモリリークしてるのかなぁ。
crtdbg.hってどーもしょぼしょぼみたいだし、_CrtSetDbgFlag周りのコードがバグってるのかも。
…まぁランタイムもデバッグツールもちゃんと動いててただの俺の勘違いの可能性がないわけじゃないですが…。

たぶんユーザーコードの問題じゃないと思います。
_CrtSetDbgFlagつかうなら_CRTDBG_CHECK_CRT_DFをはずして使うといいかも。

| | コメント (0)

2003年11月23日 (日)

C標準ライブラリを用いて、"02/07/18 14:16"といった任意のフォーマットの時間文字列を得るには?

#include <time.h>
#include <stdio.h>

{
	//timeでシステム時刻を得る。
	time_t	t = time( NULL );
	//localtimeでtm構造体を得る。
	/*
		struct tm
		{
			int tm_sec;	/* seconds after the minute - [0,59] */
			int tm_min;	/* minutes after the hour - [0,59] */
			int tm_hour;	/* hours since midnight - [0,23] */
			int tm_mday;	/* day of the month - [1,31] */
			int tm_mon;	/* months since January - [0,11] */
			int tm_year;	/* years since 1900 */
			int tm_wday;	/* days since Sunday - [0,6] */
			int tm_yday;	/* days since January 1 - [0,365] */
			int tm_isdst;	/* daylight savings time flag */
		};
	*/
	tm*	pTM = localtime( &t );

	//tm構造体のデータをsprintfに通して文字列にする。
	char	timeString[256];
	sprintf( timeString,  "%02d/%02d/%02d %02d:%02d",  pTM->tm_year%100, pTM->tm_mon, pTM->tm_mday, pTM->tm_hour, pTM->tm_min );
}

| | コメント (0)

2003年8月18日 (月)

VS.NETマクロで、VS6のときのようなイベントルーチンを記述する方法

Visual Studio .NET

Visual Studio .NET による開発

開発環境の操作

マクロの使用による反復操作の自動化

マクロと EnvironmentEvents プロジェクト アイテム

やり方は

左のクラスビューを開く

EnvironmentEvents モジュールをダブルクリックして表示

右上の[クラス名] ボックスをクリックし、[TaskListEvents] などのイベント タイプを選択

選択したイベント タイプで利用できるイベント (タスク一覧イベントなど) が、[メソッド名] ボックスに表示されるので洗濯。

マクロにイベントが挿入されて、イベント プロシージャにコードを追加できるようになる。

| | コメント (0)

2003年5月24日 (土)

AssertionからEnforceへ。

Andrei Alexandrescuっつー人がいます。
C++とかで有名な人だったような。
C++のテンプレートとマクロを駆使した奇抜なトリックがお得意の人だったような。
よく覚えてません。

この人の記事をいくつか読みました。

Assertions
http://www.cuj.com/documents/s=8248/cujcexp2104alexandr/

Enforcements
http://www.cuj.com/documents/s=8250/cujcexp2106alexandr/

Assertionsは従来のC的なマクロで実現されるアサーションをテンプレートとかを使って便利(というよりC++的)にできないか、といった内容でした。
まぁこっちはこっちで面白いのですが、より興味を引かれたのがEnforcements。
元の記事は英語だし日本ではあんまり議論されてないみたいなんでちょっと紹介してみたり。



ふつーエラー処理は

{
	FILE*	pFile = fopen(...);
	if( ! pFile )
		;//エラー処理。例外投げるとか。returnで関数を終わるとか。

	//以下通常の処理。
}
みたいな感じで書きます。if文を使ってエラーが起こったら回復処理を行うといった感じ。

これの欠点はif文を使って回復処理を書かなければならないこと。そのまんまですが。
例えば以下のような例がわかりやすいかと。
DirectDraw::DirectDraw( ... )
{
	if( DirectDrawCreate( NULL, &lpDirectDraw, NULL ) != DD_OK )
		return;

	//プライマリ作るんでまず強調レベルを普通にセットする。
	if( lpDirectDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL ) )
		return;

	bool	 bResult = false;
	lpPrimary = new PrimarySurface( bResult, lpDirectDraw, rScreen );
	if( !bResult )
		return;

	if( bFullScreen )
		lpMode = new FullScreenMode( bResult, hWnd, lpDirectDraw );
	else
		lpMode = new WindowMode( bResult, hWnd, lpDirectDraw, lpPrimary->GetSurface() );
	if( !bResult )
		return;

	if( !SetDisplayMode( iWidth, iHeight, iColorDepth, bFullScreen ) )
		return;

	lpSecondary = new TemporarySurface( bResult, lpDirectDraw, iWidth, iHeight );
	if( !bResult )
		return;
}
はるか昔に書いたDirectDrawの初期化部分なんですが、こんなふーに1文書くごとにif文が挟まってます。
仮にif文がなかったら下のような感じ。
DirectDraw::DirectDraw( ... )
{
	DirectDrawCreate( NULL, &lpDirectDraw, NULL );
	lpDirectDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL );
	lpPrimary = new PrimarySurface( bResult, lpDirectDraw, rScreen );

	if( bFullScreen )
		lpMode = new FullScreenMode( bResult, hWnd, lpDirectDraw );
	else
		lpMode = new WindowMode( bResult, hWnd, lpDirectDraw, lpPrimary->GetSurface() );

	SetDisplayMode( iWidth, iHeight, iColorDepth, bFullScreen );
	lpSecondary = new TemporarySurface( bResult, lpDirectDraw, iWidth, iHeight );
}
後者の方が何やってるか見通しがいいような気がしませんか?
つまり、エラー処理に必要なif文を使った回復処理のコードを何とかできないだろうか。と言うのが記事「Enforcements」の主旨です。

例えば
bool    foo();
と言う関数があって、戻り値がfalseなら例外Exceptionを投げるようなエラー処理を行いたいとします。
普通に書くならこうです。
{
	if( ! foo() )
		throw Exception();
}
Andrei先生は以下のような関数Enforceを導入したらどうかと提案しています。
template< typename tException >
inline void	 Enforce( bool isCondition )
{
	if( !isCondition ) throw tException();
}
これを使うと、
{
	Enforce< Exception >( foo() );
}
とまぁ、こんな感じ。とりあえず1行にまとまります。
今回の記事のメインの主張はこのEnforceです。

if文を使って
{
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
	if( ! foo() )
		throw Exception();
}
こう書くのと
{
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
	Enforce< Exception >( foo() );
}
こう書くの、どちらが本来の処理の流れが見やすいだろうか、と。
毎回Enforceでくくられるのもそれなりに微妙ですが、行数も減るしなかなかナイスではあります。

さて、特に面白いのはここからです。
先ほどのEnforceというアイディアをAndrei先生お得意のトリックで味付けしていきます。

まず、冒頭で紹介した
{
	FILE*	pFile = fopen(...);
	if( ! pFile )
		;//エラー処理。例外投げるとか。returnで関数を終わるとか。

	//以下通常の処理。
}
この処理を考えてみましょう。

この場合戻り値pFileをあとで利用するので、今までのEnfoce関数を使うと、
{
	FILE*	pFile = fopen(...);
	Enforce< Exception >( pFile );
	//以下通常の処理。
}
となってしまい芸がありません。
そこでEnforceを次のように改造します。
template< typename tException, typename tArgument >
inline tArgument&		Enforce( tArgument& argument )
{
	if( !argument ) throw tException();
}
引数に受け取った値をそのまま戻り値として返す、「パイプ」のような関数になりました。
これを使えば
{
	FILE*	pFile = Enforce< Exception >( fopen(...) );
	//以下通常の処理。
}
見事にfopenのエラー処理をenforceにまとめることができます。
あとは手抜き解説。具体的な実装は記事のほう参照してください。

今までは引数をboolとしてチェックしてfalseならエラーとしていましたが、他にもある値と同一ならエラー(DirectDrawならDD_OKとか)といったこと
をしたいときもあるでしょう。
class Predicateを導入してif文の部分を抽象化することでいろんなエラーチェックに対応できるようにできます。
Enforce< HandlerPledicate, Exception >( fopen(...) )
とかって感じ。

あと記事にはないですが、がんばれば多分例外を投げる関数に対応するエラー処理もEnforceできます。
マクロを使ってやります。機会があれば実装したいなぁ。

あと面白いトリックだったんですが、
Enforce< Exception >( fopen(...) ) << "fopen failed.\nfilename : " << __FILE__ << "\nline : "__LINE__ << endl;

こんなこともできます。
これは<<でExceptionにエラー文字列を渡しています。
つまりエラーに応じて任意の情報をエラー処理に渡せると。素敵です。

| | コメント (0) | トラックバック (0)

2003年5月10日 (土)

Doxygenの日本語対策状況

Doxygenは1.2.18から文字化け対策が行われた。それまではツリーのフレームを使うと顕著に文字化けが起こった。Doxywizardは1.2.18ではまだ日本語のフォルダ名を含むパスを正しく扱えなかった。

| | コメント (0) | トラックバック (0)

2003年4月23日 (水)

WMPミニプレイヤーモード

タスクバーの何もないところで右クリック→「ツールバー」→「Windows Media Player」
後はWMPを最小化するだけ。

| | コメント (0)

2003年4月21日 (月)

VCでCの文法を使う。

割と知らない人もいるようなのでメモ。

.cppじゃなくて.cという拡張子にしてファイルをVCのプロジェクトに追加するとCの文法でコンパイルしてくれます。たとえば変数をスコープの先頭におかないとコンパイルエラーになります。

| | コメント (0) | トラックバック (0)

2003年4月11日 (金)

Hit any key

#include <stdio.h>
#include <conio.h>

int	main()
{
	printf( "Hit any key...\n" );
	while( !_kbhit() );
	return 0;
}

| | コメント (0)

2003年3月28日 (金)

HDD復旧紀行

そもそもの始まりは謎のパーティションの分け方だった。

2つのHDDがパソに刺さっていた。
そしてCドライブとDドライブがそのシステムにはあった。

ここまでは普通だ。

Cドライブは1つ目のHDDの前半分と2つ目のHDDの前半分で構成されていて、
Dドライブは1つ目のHDDの後半分と2つ目のHDDの後半分で構成されていた。

は?

どーりでCドライブからDドライブへのコピーが鬼のように遅いわけである。

つーわけでDドラのバックアップの中で重要そうなのを俺のノートにコピって論理ドライブを削除することにした。
Windows2000のCDでブートして4つある論理ドライブをすべて削除する。

論理ドライブっつーのは削除すると未使用領域になる。
ふつう、物理ドライブ中に2つの論理ドライブがあって、それを2つとも削除すると1つの未使用領域ができる。
2つの論理ドライブを削除したからといって2つの未使用領域ができたりはしない。

2つのドライブ中の計4つの論理ドライブを削除したら計6個の未使用領域ができた。

謎。

何が困るって要するに1つのドライブが3つに分割されてしまってるので、たとえば10GのHDDならCドライブに最大3Gしか割り当てられない。
ふつうは未使用領域は1つしかない。すべての未使用領域は1つに統合されるのだが。

とにかくこの3×2の未使用領域を1つにまとめなければならない。

再起動しても分かれたまま。むしろどういう仕組みなのか知りたい。

結局、適当な未使用領域の中に適当な大きさのパティーションを作って、それを消したら未使用領域が1つになった。
謎の連続だった。

| | コメント (0) | トラックバック (0)

2003年3月12日 (水)

JSのOnClick

JavaScript/IE6.0ででの話ですが。
クリックを結構厳密に取得したいのならOnClickイベントではなくてOnMouseUpイベントのほうがいいようです。
連打するとOnClickイベントが発生しないことがあります。

| | コメント (0) | トラックバック (0)

2003年3月 8日 (土)

音声がAC-3のAVIファイルの再生

DVDをりっぴんぐしたAVIファイル。こいつを再生するのはちょっとめんどくさい
もともとDVDの音声は5.1ch。
こいつのCodecはAC-3。
つまるところMP3ではない。
つまるところふつーのDivXではない。

結論から言うに
http://doom9.org/Soft21/Filters/にいってaudiofilters.zipを落とそう。
(直リンすると落とせない。謎)
んでなかのregisterfilters-2k.cmdというのを実行すると見れる。

| | コメント (0)

2003年3月 4日 (火)

Desktopフォルダの名前の変更

ふつうにWindowsを入れるとデスクトップフォルダは"デスクトップ"になる。
いろいろ不便だからセーフモードで起動してフォルダ名をDesktop、レジストリの値もDesktopに変えるとよい。
セーフモードじゃないと変更できない。
レジストリのキーの名前は
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Desktop

| | コメント (0)

users.gr.jpのりすと

ここは自分がどのMLに加入してるかを管理するUIがないので、「とにかく全部から退会したい」というとき地味に大変。たぶん全MLに退会メールを送ることになるんだけど、そのためのMLのアドレスリストが以下。

xml-ctl@ml.users.gr.jp,
education-ctl@ml.users.gr.jp,
admins-ctl@ml.users.gr.jp,
itpro-ctl@ml.users.gr.jp,
wsh-ctl@ml.users.gr.jp,
windows-ctl@ml.users.gr.jp,
win2k-ctl@ml.users.gr.jp,
winxp-ctl@ml.users.gr.jp,
Win2003-ctl@ml.users.gr.jp,
visio-ctl@ml.users.gr.jp,
VBNET-ctl@ml.users.gr.jp,
vb-ctl@ml.users.gr.jp,
tse-ctl@ml.users.gr.jp,
sql-ctl@ml.users.gr.jp,
siteserver-ctl@ml.users.gr.jp,
sharepoint-ctl@ml.users.gr.jp,
pocketpc-ctl@ml.users.gr.jp,
office-ctl@ml.users.gr.jp,
kylix-ctl@ml.users.gr.jp,
media-ctl@ml.users.gr.jp,
isa-ctl@ml.users.gr.jp,
exchange-ctl@ml.users.gr.jp,
dotNET-ctl@ml.users.gr.jp,
dhtml-ctl@ml.users.gr.jp,
delphi-ctl@ml.users.gr.jp,
cs-ctl@ml.users.gr.jp,
com-ctl@ml.users.gr.jp,
aspx-ctl@ml.users.gr.jp,
asp-ctl@ml.users.gr.jp,
access-ctl@ml.users.gr.jp

| | コメント (0) | トラックバック (0)

2003年2月 8日 (土)

DMonkeyの謎

DMonkeyはDelphiのコンポーネントでDelphiで作るプログラムに簡単にスクリプト機能を追加できるもの。
IriaやIrvineの作者さんが作ってます。
Irvineの遺跡
DMonkey Download
Irvineスクリプトっていろいろできるのにあんま作られてないよなーとか。

以下ではそのDMonkeyで以前であった謎現象をご紹介。とはいえ03/02/08でのことなので最新版では改善されてるかも。

while( true )
{
	try
	{
		break;
		WScript.Echo();
	}
	catch( e )
	{
	}
}
Echoが実行される。
for( index = irvine.ItemCount - 1; 0 <= index; --index )
{
	if( irvine.Current.Items[index].Data.indexOf( url ) == 0 )
		break;
}
breakのあとに--indexが実行される。

| | コメント (0) | トラックバック (0)

DDNSメモ

ADSLの人は常時接続とはいえ、ルーターに割り振られるIPは結構変わります。
光の人も固定IPとはいえ、IPアドレスを覚えるのは大変です。
そこでDDNSが登場します。

DDNSとは要するにIPにわかりやすい名前をつけましょーということです。
ついでに、ADSLのようなIPが変わりやすい人のことを考えて、IPをあとから変更できるようになってます。

てなわけで導入の解説。
まず、DDNSのひとつ
http://www.zive.org/
にいきます。
んで右上の「ユーザー登録」を押して、規約をよく読んで同意した後なんか好きなゆーざーIDを入れてメールアドレスを入れます。

するとしばらくするとメールが届くので、先ほどのユーザーIDとそのメールにかかれたパスワードを使ってログインします。
そしたら「ホスト登録」で好きなURLを作ります。

次は「ホスト設定」です。
さっき作ったurlをクリックして、
IPアドレスを設定しましょう。
一番下の設定するを押せばとりあえず完了です。
これで○○.zive.netというURLにあなたのPCのIPが割り振られました。

このURLはネット関連のものにはいろいろ使えます。
自鯖でHPを公開したりFTP鯖を公開したり。
ネットゲームでピアピア接続のときのIP代わりとか。
SideWinderGameVoiceの設定なんかもいけます。

あとこのDDNSというのには問題があって、IPが変わったときいちいち設定画面からIPを変えなきゃいけないということ。
めんどくさすぎです。

そこでDiCEの登場
このサイトからDiCE for Windowsをダウンロードしましょう。
落としたインストーラーを実行してインストールします。
そして起動したらメニューの「イベント」→追加

サービス:ZiVE
ホスト名:○○.zive.netの○○部分
ドメイン名:zive.net
ユーザー名:ユーザー名
パスワード:パスワード
IPアドレス:空白
頻度:IPアドレス変化時
変化がないとき:24時間毎

とします。

次の詳細ページの「有効期間」を13にします。
保存の後、できた「ZiVEの更新」を右クリックして「今すぐ実行」しましょう。
Successとか右下に出たら成功です。

もしDiCEのProfessionalを購入するとNTサービスとして使用できます。
のーまるのDiCEの場合、スタートアップに登録して、タスクトレイに入れて常駐させることになるのでDiCEが動いていることを意識しなければなりませんが、
Professionalだとサービスとしてインストールできるのでまったく意識せずに自動的にIPの更新を行えます。
Professionalのサービスとしてのインストールは、上のイベントの作成までを行った後、
スタートメニューのSurvice Installを実行します。
実行したらコントロールパネル→管理ツール→サービスでDiCEを選択、プロパティで
スタートアップの種類:自動
を設定した後で開始を押します。

これで以降はWindowsの起動時に勝手にサービスとして起動されてIPが変更されるか7日たてば勝手に更新を行ってくれます。

| | コメント (0)

2003年2月 6日 (木)

Windowsにおけるパス名

http://www.ipa.go.jp/security/awareness/vendor/programming/b08_01_main.html

| | コメント (0) | トラックバック (0)

2003年2月 1日 (土)

IMEのOn/Off

ImmAssociateContext(NULL,m_hIME); // IME On
m_hIME=ImmAssociateContext(NULL,NULL); // IME Off

| | コメント (0)

2003年1月28日 (火)

Javaの仮想キーコード

普通のカーソルキーの↓を検出したかったのになぜかVK_KP_DOWNをつかってた。
正しくはVK_DOWN
VK_KP_DOWNはNumパッド用。

| | コメント (0) | トラックバック (0)

Java::KeyListener,KeyEentについて。

まず、KeyListenerには
public void keyReleased( KeyEvent e )
public void keyPressed( KeyEvent e ){}
public void keyTyped( KeyEvent e ){}
という3つのメソッドがあるが、keyTypedとほかの2つは意味が違う、
2つのほうがより低レベル。
んでもってこの2つの場合は、キーコードを取得するのにKeyEvent.getKeyChar()ではなく、KeyEvent.getKeyCodeを使う。

| | コメント (0)

2003年1月27日 (月)

&lt;と&gt;の由来

http://www.asahi-net.or.jp/~hi5k-stu/compt/gt_lt.htm

しらんかった。

| | コメント (0) | トラックバック (0)

いけないTips

  • Tips1
    ・<body>タグは書かない。
    ・リンク色の指定などはスタイルシートで。
    ・bodyタグは書かなくてもちゃんと表示される。(IEとねすけで確認)
    ・ファイルの末尾に
    <xmp style="position:absolute; clip:rect(0,0,0,0)">
    <body>
    とかく。
    shtmlにするっていう手もある。
  • Tips2
     CGIがcgi-bin/xxx.cgiというファイル名なら、
    xxx.htmlというのを作り、
    <meta http-equiv="refresh" content="0; url=cgi-bin/xxx.cgi">
    <xmp style="position:absolute; clip:rect(0,0,0,0)">
    <body>
    と書く。

| | コメント (0)

2003年1月26日 (日)

BCB::タスクマネージャにアプリケーションのアイコンが表示されない

Delphi で作られるソフトすべてでこの問題が発生します。ちなみに、Delphi 自身も同じ問題を抱えています。
アプリケーションのクラスアイコンをセットしたら表示されるようになると思います。
プロジェクトソースかメインフォームの OnCreate イベントで、

SetClassLong(Application.Handle,
GCL_HICON,
Application.Icon.Handle);

を実行して下さい。


ref: http://www2.big.or.jp/~osamu/Delphi/Tips/key.cgi?key=28

| | コメント (0) | トラックバック (0)

とつぜんキー入力が半角カナになったら。

Ctrl+Capsを気持ち長めに押してみよう。
どうも入力のタイミングがシビアらしくなかなか認識しないが、気長に20回くらいは試すとたいてい元に戻る。元に戻ったのに気づかないで連打してるとまた半角カナになるから要注意。

それでもだめならキーボードドライバを削除して再起動で直ることも。

| | コメント (0) | トラックバック (0)

2003年1月15日 (水)

Windows Messenger と MSN Messenger との違い

http://life.freespace.jp/impp/msn/doc/xp_difference_xpwin.html

| | コメント (0) | トラックバック (0)

2003年1月14日 (火)

ZDNetのTips

  • フォントやアイコンの表示がおかしくなった
    ttfCacheとShellIconCacheの削除
    http://www.zdnet.co.jp/help/tips/windows/w0022.html
  • 再起動バッチファイル?
    http://www.zdnet.co.jp/help/tips/windows/w0023.html
  • rundll.exe user.exe,exitwindows
    http://www.zdnet.co.jp/help/tips/windows/w0030.html
  • フォルダやデスクトップ上でファイルのプロパティを表示するには,アイコン上で右クリックをして「プロパティ(R)」を選択するだろう。
    しかし,「Alt」キーを押しながらダブルクリックするとすぐに情報表示させることが可能だ。
    http://www.zdnet.co.jp/help/tips/windows/w0039.html
  • Windows 2000で任意のアプリケーションがクラッシュ(一般保護違反)した場合,「クラッシュダンプファイル」が作成される。

     この際,使用メモリ量とほぼ同じファイル容量がいちどに書き出されるため,場合によってはハードディスクのアクセスが急に激しくなってしまう。
     メモリ状況によっては,ほかの作業ができないほど中断されるだろう。

     クラッシュダンプファイルはプログラマなどが解析用に利用できるものの,一般ユーザーには不要である。
     このため次の手順でファイル作成を無効にしても問題はない。
    http://www.zdnet.co.jp/help/tips/windows/w0045.html
  • Windows 2000では,ネットワーク上で共有されているディレクトリ(フォルダ)にアクセスすると,
    下の写真のようにローカルの「マイネットワーク」の中にショートカットが自動作成される。

     特定の共有フォルダだけにアクセスする人であれば便利な機能だが,セキュリティ面も考慮すると,この機能を中止したい人もいるだろう。
     ここでは,その方法を紹介しよう。
    http://www.zdnet.co.jp/help/tips/windows/w0057.html
  • Windowsのインストール時や,プリインストールパソコンで初めて起動する場合には「ユーザー名」や「会社名」を入力する。
    しかし,後で変えたいと思ったことがないだろうか? これは,レジストリを変更すれば簡単に修正が可能だ。
    http://www.zdnet.co.jp/help/tips/windows/w0063.html
  • CDの自動再生を停止させたい
    http://www.zdnet.co.jp/help/tips/windows/w0065.html
  • 複数のテキストファイルを結合させたい
    http://www.zdnet.co.jp/help/tips/windows/w0066.html
  • オートコンプリート機能を停止させたい
    http://www.zdnet.co.jp/help/tips/windows/w0069.html
  • コントロールパネル内のアイコンを削除したい
    http://www.zdnet.co.jp/help/tips/windows/w0070.html
  • Windows2000をインストール後のMBR(マスターブートレコード)には,専用のブートローダーが書き込まれる。
    このブートローダーによって,Windows2000とWindows(MS-DOS)の切り替え起動が可能だ。

     しかし,このMBR領域に不具合が生じブートローダーが表示されなくなってしまった場合,Windows2000が起動できなくなってしまう。
     このような場合には,Windows2000のシステムCD-ROM内のインストーラを起動して,「修復セットアップ」を試そう。

    http://www.zdnet.co.jp/help/tips/windows/w0072.html
  •   MS-IMEで半角カナを使えないようにしたい
    http://www.zdnet.co.jp/help/tips/windows/w0077.html
  •   「Invalid System Disk」と表示されてWindowsが起動しない

     パソコンを起動しようとすると,BIOS初期化後の画面で「Missing Operating System」,「Operating System Not Found」,
     「Invalid system disk」などのエラーメッセージが表示されることがある。
    http://www.zdnet.co.jp/help/tips/windows/w0078.html
  • 「Shift」キー+ホイールでページ移動
     「Shift」キーを押しながらホイールを回転させると前後のページに移動する。慣れるとメニュー上の矢印ボタンをクリックするよりも便利だろう。

    「Ctrl」キー+ホイールでフォントの大きさの変更ができる。

    http://www.zdnet.co.jp/help/tips/windows/w0081.html
  • タスクトレイのスピーカアイコンを消したい

    http://www.zdnet.co.jp/help/tips/windows/w0084.html
  •   ウィンドウが画面からはみ出て動かせなくなってしまった

     Windowsを利用していると,作業中のウィンドウが画面の外にはみ出してしまって困ってしまうことがある。
     最大化すれば表示されるのに,元のサイズに戻すと表示されないのがこの現象だ。

     この場合はあわてずに,はみ出してしまったウィンドウと同じ名称のタスクバーのボタンを右クリックし,「移動(M)」を選択すればよい。

     この状態で,カーソルキー(矢印キー)を使い動かしてみよう。なかなか表示されない場合は,そのままの状態でマウスを動かしてみるとよいだろう。

    http://www.zdnet.co.jp/help/tips/windows/w0090.html

| | コメント (0) | トラックバック (0)

2003年1月12日 (日)

SleipnirでJavaAppletが表示できない時

たまにこういうことがあってなぜなのかずーっと悩んでいたのだが、原因はMSのVMであった。
MSのVMのバージョンは1.1相当であり、今まで作っていたAppletは1.4相当だったためである。
最新のJAVA JREをインストールするとIEをカスタマイズして1.4が使えるようになるのだが、
Sleipnirには1.4への変更の設定が利かずにMS VMを使い続けることになる。

javacのオプションに-targetで1.1を指定するととりあえずSleipnirでも見れるようになる。
でもぜひぜひ1.4でもSleipnirなどでも見れるようにしていただきたいものだ。

| | コメント (0) | トラックバック (0)

2003年1月 9日 (木)

Javaでコンポーネントのサイズ情報を保持したままコンテナに追加する方法

	Panel p = new Panel();
	Button	b = new Button();
	b.setSize( 10, 10 );
	addToContainer( p, b );
	void	addToContainer( Container aContainer, Component aComponent )
	{
		//LayoutManagerが設定されているContainerにaddすると、addしたItemのSize情報が無視されてしまうので、
		//いったんLayoutManagerを設定していないPanelを作って、それからaddするというめんどくさいことをしないといけない。
		Panel	panel = new Panel( null );
		panel.setSize( aComponent.getSize() );
		panel.add( aComponent );
		aContainer.add( panel );
	}

| | コメント (0)

2003年1月 3日 (金)

右クリックの新規作成の中身

  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Discardable\PostSetup\ShellNew
  • HKEY_USERS\S-1-5-21-1316817595-2163411867-2876842818-1005\Software\Microsoft\Windows\CurrentVersion\Explorer\Discardable\PostSetup\ShellNew

| | コメント (0)

2002年12月24日 (火)

Javaでの半透明処理

class RedFilter extends RGBImageFilter
{
	public int	filterRGB( int x, int y, int rgb )
	{
		return 0x99000000 | (rgb & 0x00ffffff);		  // アルファ値は 0x99
	}
}

public class Animation extends Applet
{
	Image	mImage;
	public void	init()
	{
		mImage = getImage( getCodeBase(), "Res\\A.jpg" );
		mImage = createImage( new FilteredImageSource( mImage.getSource(), new RedFilter() ) );
	}

	public void	paint( Graphics aGraphic )
	{
		aGraphic.drawImage( mImage, 0, 0, this );
	}
}

| | コメント (0)