'// Windows Script Host を使い尽くす //
>32, 36
これはグローバルな意見の代表的なものですね。そりゃあ、私だって
選択肢が複数ある場合にはベストなものを選びなさいって言います
からね。API 等を扱う必然性が発生したときに JScript with DynaCall
を勧めることは無いですよ。この辺りは 11 さんが代弁してくれて
いますし、これ以上の意見はありませんが、今回の件で言えば、このスレ
自体が "趣味と好奇心" というローカルなスコープで議論されていると
思うんですよ。そういう意味では "汎用性" や "実用性" という表現も
ありかなと。それともいっそのこと "趣味::汎用性" とか "好奇心::実用性"
とか書きましょうか?(いや冗談です)ですから私や 11 さんの発言は
ローカルスコープで捉えていただければと思います。私たちだって
グローバルな意見は皆さんと同じなんですから...。
>33, 40
実は私は JScript や VBScript が特別得意というわけではないです。
ただあちこちの言語を齧っているので、そのおいしい部分を "手法" と
いう形で盛り込んでみただけです。ですから見る人が見れば「当たり前」
ということになってしまうでしょうね。これはビット演算然り、join("\n")
による文字列結合然りです。ただそうでない人たちが面白いと感じて
くれるのであれば、公開した甲斐があったかなと思います。
>32
>>別に標準の機能で出来るんだったら簡単なことなんじゃないの?
(1) BSTR がバイナリデータを格納できるデータ型であること。
(2) JScript にはバイトコードで文字列の作成や読み込みが
可能なメソッドが提供されていること。
この二点にさえ気付けばそれほど難しいことではないのかも知れませんね。
例えばそれを象徴する例として以下のコードを挙げておきます。
汎用性は皆無ですがちゃんとマウスの座標が取得できます。
// 8バイトの BSTR 作成 (2バイト x 4文字)
var point = "hoge"; // 何でもいいんです、4文字(以上)であれば...
var UserWrap = new ActiveXObject("DynamicWrapper");
UserWrap.Register("user32.dll", "GetCursorPos", "i=w", "f=s", "r=l");
WScript.Echo("ボタンを押してから 2秒後にカーソル座標を取得します。");
WScript.Sleep(2000);
UserWrap.GetCursorPos(point);
// 結果の表示
WScript.Echo([
"X座標: " + point.charCodeAt(0),
"Y座標: " + point.charCodeAt(2),
].join("\n"));
>11
>>言おうと思ったら先を越されて残念。実際、VBSで作ったら、かなり
>>藁えるコードになると思うけど。
私の方も「11さんのことだからもう気付いているだろうな」と思いながら
書き込んでたりしました...。あと関数呼び出しの VBScript では
コードがかなり見難くなってしまうでしょうね。とはいえクラスの定義や
メソッド、プロパティの定義もちゃんと機能が用意されているので
多分実現は可能でしょう(クラス化は必須ではありませんけど)。
>38
2の階乗の除算で商を求めるときに有効ということですね。
ム板ならではのレス感謝。ということで測定!
n = 500000;
// Math.floor() の場合
d1 = new Date();
for (i=0; i<n; i++) {
Math.floor(i / 2);
// i % 2;
}
d2 = new Date();
// >> 演算子の場合
d3 = new Date();
for (i=0; i<n; i++) {
i >> 1;
// i & 1;
}
d4 = new Date();
a = d2.getTime() - d1.getTime();
b = d4.getTime() - d3.getTime();
c = ((b / a) * 100).toString();
WScript.Echo([
a,
b,
c.match(/.*\.[0-9]{2}/) + "%"
].join("\n"));
約1/2ですね。今回はコンストラクタ関数内で1度呼ばれるだけなので
処理速度に影響はないですが、反復処理内では徐々に効いてきそうですね。
ちなみにコードがスマートになるので早速使わせていただきました。
ついでに測定した 2の剰余では % より & の方が若干高速でした。
(コメント部を入れ替えて下さい)。
>33
高速化の話が出たついでに...
私が好んで使う join() ですが文字列の結合時に威力を発揮します。
n が大きいほどその効果も絶大となります。
n = 100000;
// += を使った場合
d1 = new Date();
var s = new String;
for (i=0; i<n; i++) s += "1234";
d2 = new Date();
// join() を使った場合
d3 = new Date();
var a = new Array();
for (i=0; i<n; i++) a[i] = "1234";
s = a.join("");
d4 = new Date();
a = d2.getTime() - d1.getTime();
b = d4.getTime() - d3.getTime();
c = ((b / a) * 100).toString();
WScript.Echo([
a,
b,
c.match(/.*\.[0-9]{2}/) + "%"
].join("\n"));
これはメモリの再確保が繰り返し行われる += ではその処理に
多くの時間が割かれてしまうということです。join() の方は
全体のサイズを算出してから一度だけメモリ確保の処理を
行うだけですので高速になります。また各要素のサイズは
文字列が BSTR ですから長さ情報は参照するだけで取得できます。
null コードが見つかるまでスキャンするといった処理は
介入してきません。
除算を避けるのはCPUが遅かった時代のCなんかでの常套手段
今はコンパイラが最適化してくれるから書かないけど・・
別に2のべき乗だけでなく
Math.floor(a/b) → a/b|0
とかもどう?
>44
そういった話やアイデアは面白いですね。細かいところですが
ためになりますよ。
>11
「あっちでよろしくと言われた件について」
http://pc.2ch.net/test/read.cgi/win/1009175619/l50 煽りなら放置と思っていたのだけれど、他ならぬ 11さんのご指名があったので
11さんに対する回答の意味も含めて...
_stdcall や _cdecl は関数が呼び出されるときにパラメータがスタックに
どのような順序で渡されるか、関数が値を返すときにスタックは呼び出し側
、関数側のどちらがクリアするか、などを取り決めた呼び出し規則です。
ちなみに Win32 API 関数はすべて stdcall 呼び出しになっているため(つまり
エクスポートされている関数には _stdcall 識別子が使われている)、DynaCall
では "f=s" にしておかないと正しく呼び出すことが出来ません。stdcall は
パラメータの渡し順が右から左であること、スタックに積まれたパラメータは
呼び出された側がクリアすることになっています。_cdecl は C 言語の呼び出し
形式でありスタックの積み方は同様ですがパラメータは呼び出し元でクリアする
必要があります。
DynaCall は Win32 API 関数を呼び出すためのコンポーネントであるのに
何故 _cdecl までもが用意されているのでしょうかね。はっきりとした
ことはわかりませんが、例えば VB から利用できないように _stdcall 以外で
修飾された関数も呼べるようにしたというところでしょうか(Visual Basic で
Daclare ステートメントを使う場合、DLL の関数が stdcall 呼び出し規則に
従っていることを前提としているため)。
あと別件で DynaCall のソースを見ていて気付いたんですが、呼び出し形式の
デフォルトは _stdcall になっているようで、引数に "f=s" を指定しなくても
正しく関数を呼び出すことが出来ます。私自身 "f=s" を指定しなければならない
必然性を感じていなかったので、省略可能ってことで納得です。
レアなケースで _cdecl の関数も呼び出せるという程度に考えておけばよいでしょう。
話は変わりますが DynaCall を使って文字列変数のポインタを取得する
関数を書いてみました。
Set UserWrap = CreateObject("DynamicWrapper")
str = ""
ptr = VarPtr(str)
WScript.Echo ptr
' 文字列型変数のポインタを取得する関数
Function VarPtr(arg)
UserWrap.Register "shlwapi.dll", "StrCatW", "i=wl", "f=s", "r=l"
VarPtr = UserWrap.StrCatW(arg, 0)
End Function
この関数は VB や VBA でモジュール関数として組み込まれている
VarPtr 関数と同じ機能を提供します。実際にこれらの環境で
両方の関数を実行すれば同一の値が返ってくることが確認できると
思います(その場合上記の関数の名前は変えておいてね)。第二引数は
null ポインタとして 0 を渡していますが、引数の型を w に変更
して "" (長さ0の文字列) などを渡しても構わないと思います。
私の場合は余計な文字列が生成されるのを避けただけです。
JScript の人は上記を参考に自分で作ってみてください。
今のところ試していませんが、構造体のメンバに文字列のポインタ
などを指定しなければならないようなときに使えるかなと
思っています。
Visual Basic での VarPtr 関数の確認
(1) オブジェクトブラウザで VBA ライブラリを選択する
(2) 非表示のメンバを表示させる
(3) 左ペインで _HiddenModule というモジュールを選択する
(4) 右側のペインに VarPtr が表示される
毎度毎度、長文で失礼します。情報程度と読み流しください。
昨日、紹介したポインタを取得する関数について色々調べていたところ
VBScript では不具合があることがわかりました。これはVBScript 言語
エンジンに依存してしまうため、おそらく回避は不可能と思われます。
まず結論から言いますと昨日紹介した関数は JScript で作成する必要が
あります。以下が JScript で書き直した関数です (StrCatW() はシェルの
環境に依存するので lstrcatW に変更しました)。
function JS_VarPtr(arg) {
UserWrap.Register("kernel32.dll", "lstrcatW", "i=ww", "r=l");
return(UserWrap.lstrcatW(arg, ""));
}
また昨日の VBScript による関数ですが DynaCall の仕様に従うならば
Function VBS_VarPtr(arg)
UserWrap.Register "shlwapi.dll", "StrCatW", "i=wl", "r=l"
VBS_VarPtr = UserWrap.StrCatW(CStr(arg), 0)
End Function
としなければならないところです。API 関数に渡す引数に文字列変数が
使われる場合(StrCatW() の第一引数)、CStr により文字列型の VARIANT
に変換しなければなりません。しかし CStr は変数 arg の型を直接変換
するのではなく新たに VARIANT を作成します。つまり上記のように書くと
StrCatW() に渡されるのは arg ではなく新たに生成された VARIANT に
なってしまい、取得されるポインタも arg のものではありません。
このような理由から昨日は CStr を付けなかったのですが、やはりこれが
動作に影響するようです。 DynaCall は文字列型の引数には VARIANT が
来ると期待していますが、そうではなくなるからです。つまり VBScript
では arg そのものを引数に渡すことができないようなのです。
(引数の型 's' や 'r' は内部で暗黙的な文字コードの変換が行われて
しまうため、同様の理由で不可)
その点 JScript では正しく渡すことができます。以下は引数に文字列のポインタ
使って MessageBox() を呼び出すサンプルです。引数としてポインタを渡す場合は
DynaCall 側で 'l' を指定します ("i=hllu" の部分)。また文字列は Unicode
扱いなので MessageBoxA() ではなく MessageBoxW() を呼ぶことになります。
また文字列は NULL文字で終わる必要があります。
====================
varptr.wsf
====================
<job>
<script language=JScript>
function VarPtr(arg) {
UserWrap.Register("kernel32.dll", "lstrcatW", "i=ww", "r=l");
return(UserWrap.lstrcatW(arg, ""));
}
</script>
<script language=VBScript>
Set UserWrap = CreateObject("DynamicWrapper")
UserWrap.Register "user32.dll", "MessageBoxW", "i=hllu", "r=l"
s1 = "MessageBox (UNICODE)" & vbNullChar
s2 = "From DynaWrap Object" & vbNullChar
ret = UserWrap.MessageBoxW(Null, VarPtr(s1), VarPtr(s2), 3)
WScript.Echo ret & "番のボタンが押されました。"
</script>
</job>
もし VBScript で文字列変数そのものを正常に渡す方法を
知っている人がいましたら教えてください。
PerlScriptってどうよ?
漏れはASP以外では使う意義を見出せないのだが......
TLBINF32.DLL(ProgID=TLI.TLIApplication)を使って、COMのメンバの
helpstringを読み出すには、どうしたら良いのでしょうか?
MemberInfoクラスには、Property HelpString([LCID As Long]) As Stringという
プロパティがあるのですが、これを参照しても、長さ0の文字列が返って来るのみです。
引数に何か値を与えてやればいいのでしょうか?
それとも、まったく別のやり方で、helpstringを取得することが可能なのでしょうか?
54 :
デフォルトの名無しさん:02/04/28 18:27
おーずっとsage進行だったのか、全然気づかなかった。
単にhelpstringが設定されてないだけとちがうん?
>>55 げっ、本当だ。
例として取り上げた、wshom.ocxには、もともとhelpstringが
設定されていないのですね。
他のタイプライブラリでは、問題なく動作することを確認しました。
激しく鬱だ。お騒がせして申し訳ありませんでした。
javascriptってどんなウィジェットが使えるんですか?
VBScriptってFormat関数ないのな・・・
桁揃えるだけなのに、いちいちスペース挿入するの作らにゃならんとわ・・・
Cでやればよかったよ・・・(;;)
>>58 そんなに面倒か ? 桁あふれ考えなくていいなら、
Right(Space(n) & x, n) で良いと思うが。
VSA による JScript .NET は実行開始までに時間が掛かりすぎるなあ。
お手軽スクリプトの実行にはまだ WSH による JScript が当分主流でしょう。
機能的には雲泥の差がありますけれども。VB .NET と VBScript の関係も
同様でござる。
>>60 同意。ある意味では完全に別物。
用途によるが、WSHの代わりにはなりえない。<JScript.NET
久し振りの書き込みです。
60さん、61さん、代わりにならないなら使いこなしてやりましょう!!
巷では .NET Framework SDK (日本語版) の無償提供開始により
.NET プログラミングが本格的に浸透し始めてきたように思います。
COM は消え行く運命であることに間違いはないのですがその技術は
.NET に受け継がれていくのでその知識が無駄になることはないでしょうね。
また消え行くにせよ、これまでの資産をいきなり手放すことなど出来るわけも無く
.NET では COM との相互運用が十分考慮されているようです。
例えば .NET クライアントから COM コンポーネントを利用したり、
COM クライアントから .NET コンポーネントの利用したりすることが可能です。
後者は WSH ユーザにも有益な機能であると思っています。ということで
スクリプトプログラマ向けのちょっとした .NET お遊びをしてみます。
以下は VB .NET で .NET コンポーネントを作成し
それをスクリプトから呼び出してみるサンプルです。
あらかじめ .NET Framework SDK をインストールしておいてください。
http://www.microsoft.com/japan/msdn/netframework/downloads/sdk.asp ' sample.vb
Namespace Clannad
Public Class Serius
Public Function Add (ByVal x As Integer, _
ByVal y As Integer) As Integer
Return x + y
End Function
End Class
End Namespace
まず上記ソースファイルをコンパイルします。
vbc /t:library sample.vb
すると sample.dll が作成されます。次に COM コンポーネントとして
利用できるようにレジストリに登録します。
regasm sample.dll
sample.dll は wscript.exe あるいは cscript.exe と同じフォルダに
置いておく必要があります。
では VBScript からこのコンポーネントを呼び出してみます。
' test.vbs
Set obj = CreateObject("Clannad.Serius")
WScript.Echo obj.Add(12, 24)
VS .NET に手が出ない方も無償提供される SDK をインストールすれば
.NET コンポーネントを作成することが出来ます。.NET がもう少し普及するまで
また JScript .NET、VB .NET のスクリプトホストの実行速度が改善されるまで
COM コンポーネントにラップした .NET コンポーネントを VBScript、JScript で
楽しんで見るのもいいかと思います。VBScript や JScript では手にすることが
出来なかった様々な機能を利用することが出来るんですからね。
上記のサンプルは VB .NET ですが JS .NET でも同様のことが出来ますので
JScript ユーザの方も挑戦してみてはいかがですかね?
ちょっとしたお遊びでした。
>>62-63 サンプル試してみました。
この方法なら、実行速度は気になりませんね。
しかし、わざわざレジストリに登録するのに、なぜDLLをexeと
同じディレクトリに置く必要があるんでしょう?
私はWin98SEで、SDKがインストールできないので、Webでの情報が
もっと充実するまでは.NETを利用するのは苦しい・・・。
ProgID Clannad.Serius
CLSID {9E8FF145-2B8C-3416-AD3D-1BBBCFD540C5}
レジストリ情報を眺めるとわかりますが、実際にコンポーネントとして登録されるのは
mscoree.dll であり、このライブラリが .NET コンポーネントを COM コンポーネントのように
利用するためのラップ処理を行います。つまり sample.dll のパス情報がレジストリに
登録されているわけではありません。以下は SDK ドキュメントからの引用。
//////////////////////////////////////////////////////////////////
.NET クラスを COM に登録または登録解除するには、コマンド ライン ツールの
アセンブリ登録ツール (Regasm.exe) を実行する必要があります。Regasm.exe は、
COM クライアントが特別な処理を行わなくても .NET クラスを使用できるように、
クラスに関する情報をシステム レジストリに追加します。RegistrationServices クラスは、
これと同等の API 機能を提供します。
Regasm.exe は、HKCR/CLSID レジストリ キーの下に、.NET クラスの CLSID の
新しいエントリを作成します。1 つのクラスに Regasm.exe を何回使用しても、そのクラスの
COM での CLSID は変更されません。
HKCR\CLSID\{0000...0000} キーの下には、既定値としてそのクラスの ProgID が設定され、
Class と Assembly という 2 つの名前付きの値が新しく追加されます。ランタイムは、
レジストリから Assembly の値を読み取り、その値をランタイムのアセンブリ リゾルバに渡します。
アセンブリ リゾルバは、名前やバージョン番号などのアセンブリ情報に基づいて、そのアセンブリを
検索します。ただし、アセンブリ リゾルバがアセンブリを検出する前に、そのアセンブリが署名されて
グローバル アセンブリ キャッシュにインストールされているか、またはアプリケーション パス上に
配置されている必要があります。アプリケーション パスから読み込まれたアセンブリは、
そのアプリケーションからしかアクセスできません。共有アセンブリは、署名付きでグローバル アセンブリ キャッシュに
インストールする必要があります。
Regasm.exe は HKCR\CLSID\{0000...0000} キーの下に、InProcServer32 キーも作成します。
このキーの既定値は、共通言語ランタイム (Mscoree.dll) を格納している DLL の名前に設定されます。
////////////////////////////////////////////////////////////////////////
vbc.exe, regasm.exe とも /? オプションでヘルプが表示されます。
先程書き忘れましたがレジストリ情報の削除は
regasm /u sample.dll
です。
66 :
デフォルトの名無しさん:02/05/14 01:39
私もやってみました。regsvr32とちがって、\winntとか\winnt\system32とかに
入れなきゃいかんのですよね。ちょっと残念。使用元がvb exeとかならそれと同じ
ディレクトリに置けば動くんですが。
最初jscriptから使おうとして、*.jsファイルと同じとこに置いても
動かない。web探して「厳密名つけてgacに入れるヨロシ」という記述を
見たので挑戦……半日かかりましたよ。厳密名つけるの面倒です。
>>65 なるほど。詳細なレス感謝です。
登録情報の削除は/uでいけるんですね。
ヘルプでは/unregisterとしか出ないんで・・・。
う〜ん、コンパイラはインストールできるのにドキュメントは
インストールできんとは・・・。なんていけずなんだ、M$。
scriptって手書きって意味でしょ。
参考までに JScript .NET で作成するサンプルです。
// sample2.js
package Clannad
{
public class Bamba
{
public function Add (x : int, y : int) : int
{
return x + y;
}
}
}
コンパイルは
jsc /t:library sample2.js
IUNKNOWNさんって、M$の社員かしら??
なんか、そんな気がした。(それが良い悪いということを言いたいわけじゃない)
71 :
デフォルトの名無しさん:02/05/26 09:09
HTML内で使って戻り値を得るにはどうすれば・・・
書くのも久し振りなら読むのも久し振りだったりして。といっても
やっぱりレスは少ないですね(当たり前か)。
ところで皆さん MSDN マガジンは読みましたか?WSH に関する
かなり興味深い記事に結構なページが割かれてますよ。私の所感では
MS は WSH と COM の手を切る可能性はあっても WSH の存在自体は
否定していないような気がします。過去の遺産となる COM に関しても
.NET がラップしていくことになるでしょうね。WSH は .NET と
手を組んでまだまだ進化していくのではないでしょうか?
.NET の時代、WSH = COM という図式はもう古いのかも知れません。
(記事の内容からは少々逸脱したお話になってしまった)
>IUNKNOWNさんって、M$の社員かしら??
うーん、私は M$ の社員ではないです。一体何者なんでしょうかねえ。
自分でも時々わからなくなります。
>wsc化して、汎用コードにできませんかねえ?
>そうすれば、多少は実用性も出てくるんじゃないかと。
2ヶ月近く前の話題になりますが、近々公開出来そうかなと...。
週末に少しずつ書いていたのですが、ある程度形になってきました。
これからヘルプに取り掛かろうと思っていますが、近々と言いながら
マイペースでのんびり屋の私にとってはヘルプの作成に数週間ぐらい
かかったりするかも知れません。
構造体の定義/呼び出しなどに関しては、以前公開した
JScript 版よりすっきりとした構文で書けるようにしてあります。
また構造体以外にも DynaCallを便利に利用する幾つかの機能を
盛り込んでみました。
>>72 特集2のところでしょうか?Webで読めないようで残念です。<MSDN Magazine
>>75 ありがとうございます。見てみます。
#ハンドル変えたんですか?
>74さん
え〜と、ウムラウトが使えるかなと思って試しただけです。
あんまり意味はないです...。紛らわしいことしてスンマセン。
質問。WSFからWSFは呼べませんか?
■caller.wsf
<?xml version="1.0" encoding="Shift_JIS"?>
<package>
<?component error="true" debug="true"?>
<job id="nongeneral">
<?job debug="true"?>
<script language="VBScript">
<![CDATA[
MsgBox "ここに汎用性のないコードを記述します。"
]]>
</script>
<script src="callmodule.wsf">
</script>
</job>
</package>
■callmodule.wsf
<?xml version="1.0" encoding="Shift_JIS"?>
<package>
<?component error="true" debug="true"?>
<job id="general">
<?job debug="true"?>
<script language="VBScript">
<![CDATA[
MsgBox "ここに汎用性のあるコードを記述します。"
]]>
</script>
</job>
</package>
こんな感じで出来たら、VBS Unitの作成の手前、都合いいです。
現状はエラーメッセージが「ステートメントがありません」です。
>78
VBSでインポートしちゃだめって理由はあるの?
■callmodule.vbs
MsgBox "ここに汎用性のあるコードを記述します。"
で十分かと
>79
レスありがとうございます。
元形(callmodule)のテストを行うのが目的なので、
1.テスト対象がWSFでもテストできるのか大枠をつかみたい
2.元がWSFなら簡単にスクリプトをWSC化できるかなって思いまして。
これが出来なければ、それなりに考え直すって段階です。
非常に遅れましたがスクリプトコンポーネントを公開しました。
サイトはまだ完成していません。ヘルプの作成がなかなか進まないので
サンプルコードを多めに作っておきました。大体の感じを掴めていただけたらと思います。
またサイトに掲載しているサンプルを簡単に実行できる
ユーティリティ(IEコンテキストメニュー用スクリプト)も作ったので
よろしければお試しください。
http://members.tripod.co.jp/IUnknown/WSH/dwhelper/ 現在はヘルプの作成の他、dynwrap.cpp を少々いじったりしています。
VBScript でも JScript と同様に文字列型変数を直接渡せるようにしたりとか...。
後は 9x系の環境で上手く動作しない原因も調べてみるつもりです。
※ 以前サイトに掲載していたメールアドレスが間違っていました。
×
[email protected] ○
[email protected]
>81
なんか表示されません…。
Win2000sp2+IE6。スクリプトなどは実行許可にしてあります。
>11氏
お久し振りです。
えーと、UTF-8 で見てください。
スクリプト等は使用していません。
以上よろしくです。
84 :
デフォルトの名無しさん:02/06/29 01:11
Win2KでVBS書いてるのですが、InputBoxで
キャンセルボタンが押されたか、
空白状態でOKボタンが押されたかを見分ける方法って、
どんな感じになるのでしょうか?
以下のように書いてとりあえずやりたいことは出来ましたが、
いまいちきれいじゃ無いような気がするんです。
retCode = InputBox("値をいれてください")
If retCode = "" Then
If retCode = False Then
MsgBox("キャンセルボタン")
Else
MsgBox("空白状態でOKボタン")'
End If
End If
str=InputBox("値をいれてください")
if IsEmpty(str) then msgbox("キャンセルボタン") else msgbox(str)
>>85 Thanks!!!
というか、IsEmptyでなくても、str = Falseならば、
キャンセルボタンだと言うことがわかりました。
ちょっと発想を変えればよかったのね>漏れ
すみません、質問です。
実行中のスクリプトが、自分自身のプロセスIDを得るにはどうすればよろしいでしょうか。
現在は、WMIからExecQueryで SELECT * from win32_process where Name = "wscript.exe" を
投げていますが、これでは複数のwscriptが動いている場合、どれが自分なのかが判断できず
にいます。
当方の環境はWin2000Serverでして、個人所有にかかるマシンではないため、COMを追加
しようにもできずに困っています。
88 :
デフォルトの名無しさん:02/07/24 09:15
ageage
hage
失敗禿