« 2007年8月 | トップページ | 2007年10月 »

2007年9月30日 (日)

IE/HTAで非同期割込処理。n秒後に自動的に閉じるMsgBox()

WScript.ShellのPopUp()の待機秒数指定は、IE/HTAなどでは使えません。:-(

そこで、htmlfileのwindow.setTimeout()を利用した代替方法です。

<html>
<head>
<script language=vbscript>
Set wShell=CreateObject("WScript.Shell")
Set d=CreateObject("htmlfile")
Sub proc()
If wShell.AppActivate("ユニークなタイトル") Then wShell.SendKeys "{ESC}"
End Sub
Sub button1_onclick()
wShell.PopUp "5秒経っても自動的に閉じません。",5,"ユニークなタイトル"
d.parentWindow.setTimeout GetRef("proc"),5000
MsgBox "5秒後に自動的に閉じます。",,"ユニークなタイトル"
End Sub
</script>
</head>
<body>
<button id=button1>Push</button>
</body>
</html>

window.setTimout()に関数ポインタを指定しています。

2007年9月29日 (土)

「Web ページ、HTML のみ」で保存できないときは、デザインモードで回避する。

「Web ページ、HTML のみ」なのに、なぜか以下のエラーで、Web ページが保存できないことがあります。:-(

---------------------------
Web ページの保存エラー
---------------------------
Web ページに含まれているファイルのいくつかがないため、この Web ページは保存できません。
---------------------------

これまた、なぜか、デザインモードに変えると、保存できたりします。

2007年9月28日 (金)

WSHで非同期割込処理。メッセージを出しながら時間の掛かる処理

WSHはシングルスレッドなので、本当の多重処理はできませんが、非同期割込による擬似的な多重処理はできます。

処理中メッセージを出して、時間の掛かる処理を実行する例

Set d=CreateObject("htmlfile")
d.parentWindow.setTimeout GetRef("proc"),100
MsgBox "時間の掛かる処理中です。。。というメッセージを出しっぱなしにして"

Sub proc
Set wShell=CreateObject("WScript.Shell")
wShell.PopUp "時間の掛かる処理のつもり。。。",5
WScript.Timeout=1
WScript.Sleep 2000
End Sub

割り込まれた本処理のほうは、割込処理が抜けるまで、実行されません。

ここでは、割込処理を抜けても、WScript.Quitしても、MsgBoxに応答するまで、本処理が終了しないので、WScript.Timeoutで、MsgBox諸共、終了させます。

WScript.Timeoutは、undocumentedです。起動オプションの//T:に対応します。
ScriptControlのTimeoutは、CPU timeですが、WScript.Timeoutは、elapsed timeです。
なので、MsgBoxやWScript.Sleepでもカウントされます。
WScript.Timeoutの最小値は1秒なので、WScript.Sleepで1秒以上待たせると、1秒で処理が打ち切られます。なので、終了まで1秒の遅延が発生します。:-p

2007年9月27日 (木)

<hta:application singleinstance=yes /> の振舞い

以下のHTAを2つ起動してみると分かります。

<script language=vbscript>
msgbox 1
</script>
<hta:application singleinstance=yes applicationname=single />
<script language=vbscript>
msgbox 2
</script>

一つ目は、1と2が表示されますが、
二つ目は、1だけが表示され、2が表示されず、終了して、
代わりに、一つ目がアクティブになります。

2007年9月26日 (水)

HTAウィンドウは、メインウィンドウでない。

HTAのウィンドウは、.NETの Process.MainWindowHandle や Process.MainWindowTitle で取れません。

そのためか、WScript.Shell の Terminate() が、2秒遅延します。

WScript.Shell の Terminate() は、メインウィンドウに WM_CLOSE メッセージを投げて、2秒待ちます。
それでも終了しないときに、プロセスを強制終了させます。

ところが、HTAのウィンドウは、メインウィンドウでないので、必ず、2秒待つことになります。
また、必ず、強制終了になります。

2007年9月25日 (火)

<hta:application border=dialog /> は何が違う?

<hta:application border= の thick と dialog の違いは何か?

見た目は変わらないのですが、resizable=yes/noの代わりのようです。

2007年9月24日 (月)

「フレームを名前を付けて保存」がない?

昔、「フレームを名前を付けて保存」があったような。。。
しかし、今のIE6.0にはありませんねー。:-(

特に、MSのサイトがフレーム構造になってて、困るんですよねー。:-(

そこで、フレームの右クリックで、
 そのフレームを新規のウィンドウで開く、
 そのフレームを現在のトップウィンドウで開き直す、
 そのフレームを名前を付けて保存する、
ように、コンテキストメニューを拡張します。

FrameOpenNew.htm
<script language=jscript defer>
open(external.menuArguments.location.href);
</script>

FrameOpenTop.htm
<script language=jscript defer>
external.menuArguments.open(external.menuArguments.location.href,"_top");
</script>

FrameSaveAs.htm
<script language=jscript defer>
external.menuArguments.document.execCommand("SaveAs");
</script>

FrameOpen.reg

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt]

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\フレームを新規作成(&N)]
@="C:\\フォルダ\\FrameOpenNew.htm"

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\フレームを開く(&L)]
@="C:\\フォルダ\\FrameOpenTop.htm"

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\フレームを保存(&M)]
@="C:\\フォルダ\\FrameSaveAs.htm"

2007年9月23日 (日)

VBSファイルの文字コードはシフトJISまたはUnicodeです。

シフトJIS または Little Endian の Byte Order Mark 付きの Unicode です。
メモ帳で ANSI または Unicode で保存した形式です。

2007年9月22日 (土)

VBAからJScriptのArrayオブジェクトのsort()を利用する。

Sub a()
Set sc = CreateObject("scriptcontrol")
sc.Language = "jscript"
sc.AddObject "me", Me
sc.AddCode "function compare(x,y){return(me.compare(x,y));}"
Set ar = sc.eval("new Array()")
ar.push 3
ar.push 5
ar.push 1
ar.push 4
ar.push 2
CallByName ar, "sort", VbMethod, sc.codeobject.compare
MsgBox ar
End Sub

Function compare(x, y)
compare = x - y
End Function

VBAの配列をJScriptのArrayに入れて、VBA側の比較関数を変えると、
配列のN番目の要素の順で配列の集合をソートしたり、
複数キーでソートしたりも、出来ます。

オブジェクトを入れて、比較関数でプロパティを比較すると、
ホットスポットの比較関数のコストが高くなるので、
オブジェクトとプロパティの配列を入れるようにして、
プロパティのカラムを比較するほうがよいでしょう。

2007年9月21日 (金)

VBAからJScriptのescape()を使う。

VBScriptにはEscape()、JScriptにはescape()があります。しかし、VBAにはありません。:-(

そこで、VBAからJScriptのescape()を利用します。

Set sc=CreateObject("scriptcontrol")
sc.Language="jscript"
MsgBox sc.CodeObject.escape(Text)

VBScriptの場合は直接呼べないので、JScriptがよいでしょう。

JScriptなら、Globalオブジェクトのメソッドが直接呼べます。
escape メソッド | eval メソッド | isFinite メソッド | isNaN メソッド | parseFloat メソッド | parseInt メソッド | unescape メソッド
encodeURI メソッド | encodeURIComponent メソッド | decodeURI メソッド | decodeURIComponent メソッド

2007年9月20日 (木)

メールの添付ファイルをIEで取り出す。

メールの添付ファイルを、メーラでなく、IEで取り出すにはどうするか?

メールファイルの拡張子をMHTにする。

メモ帳で開いて、添付ファイルのContent-Locationを適当に作る。
逆に、Content-Typeは、既知の安全なものに変える。

例えば、
Content-Type: application/x-zip-compressed
Content-Location: http://internet.e-mail

IEのアドレス欄に、

mhtml:file://C:\~~~\~~~.mht!添付ファイルのContent-Location値

例では、

mhtml:file://C:\~~~\~~~.mht!http://internet.e-mail

と入れると、ダウンロード可否のダイアログが出る。

で、保存する。

MHTMLファイルの中のファイルのURL

MHTMLファイルの中のファイルを指定するURLは、以下のとおり。

mhtml:file://C:\~~~\~~~.mht!中のファイルのContent-Location値

このシンタクスは、どういうときに使うためのものか、用途が不明???
セキュリティホールに使われて有名になったようですが。。。

2007年9月19日 (水)

バッチファイルをウィンドウアプリっぽくする。その2

バッチファイルの先頭に1行、末尾に@goto :eof以降の8行を追加します。

サンプルは、JScript.NETのソースをコンパイルするバッチファイルです。
ソースファイルをバッチファイルにドロップして起動します。

JSCW.CMD ソースファイル

@if(0)==(0) if not "%~d0"=="\\" start /min CScript.exe //e:jscript %0 %* & goto :eof
@ECHO OFF
SETLOCAL
(SET DOTNET=%SystemRoot%\Microsoft.NET\Framework)
FOR /F "delims=" %%1 IN ('DIR /AD /B /ON "%DOTNET%\v*"') DO IF EXIST "%DOTNET%\%%~1\jsc.exe" (SET DOTNET=%DOTNET%\%%~1\jsc.exe)
ECHO ON
"%DOTNET%" /nologo /t:winexe /out:"%~dpn1.exe" "%~f1"
@goto :eof
@end
var args=new Array('"\\\\.\\'+WScript.ScriptFullName+'"','2>&1');
for(var e=new Enumerator(WScript.Arguments);!e.atEnd();e.moveNext()){
  var arg=e.item();args.push(arg.indexOf(' ')+1?'"'+arg+'"':arg);}
var wShell=new ActiveXObject('WScript.Shell');
var oExec=wShell.Exec(args.join(' '));oExec.StdIn.Close();
wShell.Popup(oExec.StdOut.ReadAll());WScript.Quit(oExec.ExitCode);

起動されと、すぐにコンソールウィンドウを最小化して、結果をメッセージボックスに出します。

起動時に瞬間、コンソールウィンドウが現れますが、これは避けられないので、我慢します。

2007年9月18日 (火)

コンソールアプリやバッチファイルをウィンドウアプリっぽく実行する。

コンソールアプリやバッチファイルをウィンドウアプリっぽく実行する。

WinExec.VBS コマンド [引数...]

Option Explicit
Dim Args
Dim Arg
Dim wShell
Dim oExec
Dim StdErr
Dim StdOut
Args=Array()
For Each Arg In WScript.Arguments
  If InStr(Arg," ") Then Arg="""" & Arg & """"
  Push Args,Arg
Next
Set wShell=CreateObject("WScript.Shell")
If LCase(Right(WScript.FullName,11))="wscript.exe" Then
  Args=Array("CScript.exe","""" & WScript.ScriptFullName & """",Join(Args))
  WScript.Quit wShell.Run(Join(Args),7,True)
End If
Args=Join(Args)
Set oExec=CreateObject("WScript.Shell").Exec(Args)
oExec.StdIn.Close
StdErr=oExec.StdErr.ReadAll()
StdOut=oExec.StdOut.ReadAll()
If StdErr<>"" Then MsgBox StdErr,vbCritical,Args
If StdOut<>"" Then MsgBox StdOut,vbInformation,Args
WScript.Quit oExec.ExitStatus

Sub Push(Items,Item)
ReDim Preserve Items(UBound(Items)+1)
Items(UBound(Items))=Item
End Sub

2007年9月17日 (月)

コマンドラインでタスク スケジューラのタスクを一覧表示する。

TaskList.CMD

@if(0)==(0) ECHO OFF
CScript.exe //NoLogo //E:JScript "%~f0" %*
GOTO :EOF
@end
var Shell=new ActiveXObject("Shell.Application");
var Folder=Shell.NameSpace("::{d6277990-4c6a-11cf-8d87-00aa0060f5bf}");
var Cols=new Array();
for(var k=0;k<7;k++){
  Cols[k]=Folder.GetDetailsOf(null,k);
}
var Rows=new Array();
Rows.push(Cols.join('\t'));
for(var j=0;j<Folder.Items().Count;j++){
  var FolderItem=Folder.Items().Item(j);
  for(var k=0;k<7;k++){
    Cols[k]=Folder.GetDetailsOf(FolderItem,k);
  }
  Rows.push(Cols.join('\t'));
}
WScript.Echo(Rows.join('\n'));

2007年9月16日 (日)

英文のスペルが正しいかチェックする。

正しいスペルは教えてくれないけれど、正誤だけなら。。。

Set Excel=CreateObject("Excel.Application")
Do
  words=InputBox(Excel.CheckSpelling(words),"CheckSpelling",words)
Loop While words<>""

Microsoft Script Editor ヘルプのショートカット

"C:\Program Files\Microsoft Office\Office12\CLVIEW.EXE" "MSE" "Microsoft Script Editor"

で以下のヘルプが見れます。

Microsoft Script Editor
Script Editor の一般情報
Microsoft Office アプリケーションでの HTML
Script Editor でスクリプトと HTML を操作する
Script Editor をカスタマイズする
ビューとウィンドウを管理する
移動、検索、置換
MSXML 5.0 SDK
JScript
VBScript
Office 10 でのプログラムのデバッグ
デバッガ リファレンス
Visual Studio スタート ページ
Visual Studio リファレンス

JScriptとVBScriptのヘルプが古過ぎ。5.0 :-(

Word 2007のヘルプのショートカット

Wordを起動しないで、ヘルプだけを見るには

Microsoft Office Word ヘルプ

"C:\Program Files\Microsoft Office\Office12\CLVIEW.EXE" "WINWORD" "Microsoft Office Word"

Microsoft Visual Basic ヘルプ

"C:\Program Files\Microsoft Office\Office12\CLVIEW.EXE" "WINWORD.DEV" "Microsoft Office Word"

後者は、前者で、検索ボタン横のドロップダウンリストでオフライン開発者用ヘルプを選択しても同じです。

なぜか最初に空白ページが表示されます。:-(
最初にホームを表示するには?
なので、ホームのボタンを押してください。:-p

2007年9月15日 (土)

「Excel 4.0 マクロ → Visual Basic 対応表」は、何処?

「Excel 4.0 マクロ → Visual Basic 対応表」は、何処?

「Excel 4.0 マクロ」とVBA の対応表を探すと、色んな所に、以下のように書いてあります。

「Excel 4.0 マクロ」のマクロ関数に対応する VBA のステートメントを調べるときはオンラインヘルプを利用してください。[?] - [目次] コマンドを実行し、目次から「Microsoft Excel オンライン リファレンス」-「マクロ関数リファレンス」-「Excel 4.0 マクロ → Visual Basic 対応表」を選択します。

ところが、この先が有りません。:-(

どうも、対応表は、Office 95/97 時代の Macrofun.hlp にあるようです。

オンライン サービスの Macrofun.exe 利用可能なファイル

2007年9月14日 (金)

「新規作成」で.xlsファイルを作る。

Excel2007では、エクスプローラの「新規作成」で「Microsoft Office Excel ワークシート」を選ぶと、.xlsxファイルが出来ます。

.xlsファイルは「新規作成」できないの?

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.xls\Excel.Sheet.8]

[HKEY_CLASSES_ROOT\.xls\Excel.Sheet.8\ShellNew]
"FileName"="excel.xls"

で、エクスプローラの「新規作成」に「Microsoft Office Excel 97-2003 ワークシート」が現れます。

一応、EXCEL4.XL_ と EXCEL.XL_ が C:\WINDOWS\I386 にありますが、展開して、C:\WINDOWS\SHELLNEW に置いておく必要はない?みたいです。

ところが、これで作成して保存すると、「Microsoft Office Excel 5.0/95 ブック (*.xls)」になります。
なので、いちいち「Excel 97-2003 ブック (*.xls)」に変更しなければなりません。

これは面倒なので、旧製品の CD などから、excel9.xls を入手しましょう。

MSDN サブスクリプションの DVD では、以下にありました。

\JAPANESE\OFFICE_SYSTEM\OFFICE2003\PROFESSIONAL\E2561411.CAB\EXCEL9.XLS.1041

この EXCEL9.XLS を C:\WINDOWS\SHELLNEW に入れて、

[HKEY_CLASSES_ROOT\.xls\Excel.Sheet.8\ShellNew]
"FileName"="excel9.xls"

に変えます。

2007年9月13日 (木)

PowerShellのドキュメントを軽量化する。

PowerShell のドキュメントは、DOC や RTF などです。
これらを WORD で開くのは、苦痛というか、拷問というか。。。

そこで、もっと手軽に見れるようにします。

まず、WORD で開き、MHT に保存し直します。

これを IEで開いてもよいですが、MSHTA なら、もっと軽量です。

MHT のショートカットを作成します。プロパティで、
"C:\~~~\~~~.MHT"
の先頭部分を、
MSHTA "mhtml:file://C:\~~~\~~~.MHT"
に変えます。

ドキュメントによっては、HTM にしたり、フィルタを通しても、見た目がそう変わらないものもあります。

HTM なら、拡張子を HTA にすれば、MSHTA で開けます。

フィルタを通して MHT にするには、いったん、フィルタを通して HTM + files にして、
それをIEで開いて MHT に保存し直します。

もし、MHT の内部リンクが効かなくなった場合は、改行が置換できるエディタで、
=(改行)
を削除して、次にメモ帳で、
file://C:\~~~\~~~.htm#

#
に全置換します。

2007年9月12日 (水)

Excel 4.0 マクロ関数のヘルプ ファイルをダウンロードする。

以下のヘルプファイルを開くと、
C:\Program Files\Microsoft Office\Office12\1041\XLMACRO.CHM

こう書いてあります。

Microsoft Excel 4.0 マクロ関数のヘルプ ファイルには、Excel で使用できるすべての Excel 4.0 マクロ関数 (XLM マクロ) に関する参考情報が含まれています。このマクロ関数のヘルプ ファイルは、Microsoft Office Online Web サイトからダウンロードできます。

なので、リンク先を開くと、なぜかトップページ。:-(

そこで「Excel 4.0 マクロ関数」を検索すると、

Excel 4_0 マクロ関数 - Excel - Microsoft Office Online.htm

が見つかります。ここから、

Download details Excel 2000 Help File Running Excel 4_0 Macros.htm

へ行って、xlmacro.exeをダウンロードします。

これを実行すると、

---------------------------
Setup Error
---------------------------
Setup has detected that you do not have an Office 2000 family product installed on this machine. Before this download can be installed you must first install an Office 2000 family product. To find out which products are part of the Office 2000 family, go to http://officeupdate.microsoft.com/info/o2kprods.htm.
---------------------------
OK   
---------------------------
のエラーになります。そんなこと言われても困ります。:-(

そこで、このメッセージが出ている間に、

C:\Documents and Settings\ユーザ名\Local Settings\Temp\IXP000.TMP

のような名前のフォルダを探して開きます。

そのフォルダで、以下のVBSファイルを実行します。

Set src=CreateObject("ADODB.Stream")
src.Open
src.Type=1
src.LoadFromFile "XLMACRO.MSI"
src.Position=&H5400
Set dst=CreateObject("ADODB.Stream")
dst.Open
dst.Type=1
src.CopyTo dst
dst.SaveToFile "xlmacro.ch_"

次に以下のコマンドを実行します。

expand /r xlmacro.ch_

これで、xlmacro.chm が出来るので、これをどこかに保存して、フォルダとメッセージを閉じます。

※expandがexpnadになってました。ponkさん、バグレポ感謝。

2007年9月11日 (火)

Excelの関連付けで、読み取り専用で開く。

Excelの関連付けの「読み取り専用で開く」がバグってるようです。

シフトキーを押しながら右クリックで表示される、extendedな関連付けに、
「読み取り専用で開く」(OpenAsReadOnly)があるのですが、
このddeexecキーに誤りがあるようで、読み取り専用になりません。

(誤)[open("%1",,,,,,,,,,,,,,1,,1)]
(正)[open("%1",,1)]

これは、OPEN()マクロに対応するWorkbooks.Open()をヘルプを見れば分かります。

また、commandキーの/hも要らないような。そもそも/hオプションなんて無いような。
(誤)"C:\Program Files\Microsoft Office\Office12\EXCEL.EXE" /h /e
(正)"C:\Program Files\Microsoft Office\Office12\EXCEL.EXE" /e

或いは、DDEをやめて、
"C:\Program Files\Microsoft Office\Office12\EXCEL.EXE" /r "%1"
で起こしてもよいでしょう。

ついでに、OpenAsReadOnlyキーのExtended文字列値を削除しておけば、シフトキーなしで表示されます。

2007年9月10日 (月)

Excel2007のヘルプでソースが表示、保存できない。

Excel2007のヘルプで右クリックの「ソースの表示」をすると、

「XML ソース ファイルを表示することはできません。」

となることがあります。:-(

こういうときは、「プロパティ」を表示して「アドレス(URL)」をコピーして、
IEで表示すれば、「名前を付けて保存」できます。

「お気に入りに追加」も同様です。

或いは、
普通の方法でHTMLソースが得られないときに、HTMLソースを取り出す奥の手
でも、ソースが取れます。手間はこっちのほうが少ないかも。

2007年9月 9日 (日)

Excel 4.0 マクロを調べる。

Excel 4.0 マクロに関する資料がヘルプにも、ネットにも見当たらないのですが、
Ctrl+F11か、シートのタブで右クリックして「挿入」で「Excel 4 マクロ」を作るか、
Start Excel.exe /m
で、「マクロシート」のみのブックを作成して、
fxボタンか、Shift+F3を押すか、「数式」-「関数の挿入」でダイアログを出すと、
マクロの一覧や各マクロのシンタクス程度のことは分かります。

2007年9月 8日 (土)

WScript.ShellのAppActivate()で数字のタイトルが指定できない。

数字のタイトルは、プロセスIDと解釈されて、使えません。:-(

VBA/VBなどのAppActivate()は、型が文字列か数値かで判別します。

なら、Excel.Applicationで代替できるか?

残念ながら、Excel.Applicationには、SendKeys()は有っても、AppActivate()は有りません。:-(

ところが、Excel 4.0 マクロにAPP.ACTIVATE()があるようです。:-p

VBScriptでは、Excelを使って、

Set Excel=CreateObject("Excel.Application")
r=Excel.ExecuteExcel4Macro("APP.ACTIVATE(""123"")")

成功は、r=True
失敗は、r=Falseでなく、TypeName(r)="Error" または、VarType(r)=10 'vbError

PowerShellでは、VBを使って、

param($title)
[void][Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
$erroractionpreference="silentlycontinue"
[Microsoft.VisualBasic.Interaction]::AppActivate($title)
$r=$?
$erroractionpreference="continue"
$r

成功は、True
失敗は、False

123ならプロセスID、"123"ならタイトル。

因みに、Excel 4.0 マクロには、SEND.KEYS()もありますが、SendKeys()があるので、使うメリットがありません。

2007年9月 5日 (水)

MHTMLファイルのファイル名に#があると、IEで開けない。(障害)

MHTML周りには、障害が多いのですが、C#のように#を含むタイトルのWebページをIEで「名前を付けて保存」の「Web アーカイブ、単一のファイル」で保存すると、そのローカルのMHTMLファイルはIEで開けません。:-(

無効な構文エラー
無効なアドレス
アドレスが有効ではありません

IEで作ったファイルをIEで開けないなんて、頂けません。:-(

ファイル名の#を#などに変更すれば開けます。:-(

2007年9月 4日 (火)

バッチファイルをウィンドウアプリっぽくする。

JScript.NETのコンパイラはPATHにないので、以下のバッチファイルを作ります。

JSC.CMD ソースファイル

@ECHO OFF
SETLOCAL
(SET DOTNET=%SystemRoot%\Microsoft.NET\Framework)
FOR /F "delims=" %%1 IN ('DIR /AD /B /ON "%DOTNET%\v*"') DO IF EXIST "%DOTNET%\%%~1\jsc.exe" (SET DOTNET=%DOTNET%\%%~1\jsc.exe)
ECHO ON
"%DOTNET%" /nologo /t:winexe /out:"%~dpn1.exe" "%~f1"

これにソースファイルをドロップすると、結果不明でコンソールが閉じてしまいます。:-(
あまり、ぱっとしませんが、最後にPAUSEを入れる?

いえいえ、ここでは、先のウィンドウアプリ版のmessagbox.exeを利用します。

次の1行をバッチファイルの先頭に追加します。

@if not "%~0"=="%~dp0.\%~nx0" "%~dp0.\%~nx0" %* 2>&1 | messagebox.exe & goto :eof

このバッチファイルにソースファイルをドロップすると、結果がメッセージボックスに表示されます。
コンパイル中、コンソールが開きます。:-p

次の1行に変えると、コンソールが一瞬開きますが、すぐ最小化されて、その後、閉じます。

@if not "%~0"=="%~dp0.\%~nx0" start /min cmd.exe /c,"%~dp0.\%~nx0" %* 2^>^&1 ^| messagebox.exe & goto :eof

2007年9月 3日 (月)

標準出力と標準エラーを非同期にMessageBoxに出す。

先のコンソールアプリ版のMessageBox.exeでは、

hogehoge 2>&1 | MessageBox.exe

MessageBoxを閉じるまで、コンソールが開いたままだし、処理も止まります。

(hogehoge | MessageBox.exe) 2>&1 | MessageBox.exe

標準出力のMessageBoxを閉じるまで、標準エラーのMessageBoxが出ません。

どうもパイプの前のプロセスにパイプのハンドルの複製が残っているのでは?
なので、プロセスを終了しないとパイプのEOFが上がらない。。。

そこで、別にプロセスを起こして自分は終了するとよいようです。
ProcessStartInfo.UseShellExecute=trueでプロセスを起こすと、
ファイルハンドルを引き継がないようです。

MessageBox.JS

import System;
import System.IO;
import System.Diagnostics;
import System.Windows.Forms;
var Args:String[]=Environment.GetCommandLineArgs();
try{
  var CommandLine:String=Environment.CommandLine;
  if(CommandLine.StartsWith('"')){
    var startIndex=CommandLine.IndexOf('"',1);
    if(0<startIndex && startIndex+2<CommandLine.Length){
      CommandLine=CommandLine.Substring(startIndex+2);
    }else{
      CommandLine="";
    }
}else{
    var startIndex=CommandLine.IndexOf(' ');
    if(-1<startIndex && startIndex+1<CommandLine.Length){
      CommandLine=CommandLine.Substring(startIndex+1);
    }else{
      CommandLine="";
    }
  }
  if(CommandLine.Length){
    Environment.Exit(MessageBox.Show(CommandLine,Path.GetFileName(Args[0])));
  }
  CommandLine=Console.In.ReadToEnd();
  if(!CommandLine.Length)CommandLine="[EOF]";
  var startInfo:ProcessStartInfo=new ProcessStartInfo();
  startInfo.FileName=Args[0];
  startInfo.Arguments=CommandLine;
  Process.Start(startInfo);
  Environment.Exit(0);
}catch(e){
  MessageBox.Show(e.ToString(),Path.GetFileName(Args[0]),MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
  Environment.Exit(99);
}

これを
jsc /t:winexe MessageBox.JS
でコンパイルします。

これだと、MessageBoxを出したまま、次の処理に移ったり、
コンソールウィンドウを先に閉じたり、出来ます。:-)

2007年9月 2日 (日)

標準出力と標準エラーを入れ替えられるか?

???意味不明???

例えば、

(hoge.cmd | clip.exe) 2>&1 | messagebox.exe

と書けば、標準出力はクリップボードに送られて、
標準エラーがメッセージボックスに出ます。

これを逆にしようと、

(hoge.cmd | messagebox.exe) 2>&1 | clip.exe

とすると、今度は、標準出力のメッセージボックスを閉じるまで、
標準エラーがクリップボードに送られません。:-(

標準出力と標準エラーを別々にパイプに渡すことはできても、
このやり方では、標準出力が先、標準エラーが後になります。
これが変えられるか? という話です。

標準出力と標準エラーを入れ替えればよいのです。

(hoge.cmd 4>&2 2>&1 1>&4 | clip.exe) 2>&1 | messagebox.exe

と書けば、標準エラーはクリップボードに送られて、
標準出力がメッセージボックスに出ます。

リダイレクションの解釈は、()や|の外側から。同位なら左側から。

作業用に、4~9が使用可能です。3は、CONのようです。

ただし、サブシェルに引き継がれるのは0,1,2だけなので注意。

(hoge.cmd 2>&1 1>&4 | clip.exe) 4>&1 | messagebox.exe

でもよさそうなのに、うまく行かないのはそのせいです。

2007年9月 1日 (土)

JScript.NETでWin32APIを使う。

よく分かりませんが、こんな感じで使えるようです。

import System;
import System.Reflection;
import System.Reflection.Emit;
import System.Runtime;
import System.Text;

// Invoke a Win32 P/Invoke call.
function InvokeWin32(dllName:String, returnType:Type,
  methodName:String, parameterTypes:Type[], parameters:Object[])
{
  // Begin to build the dynamic assembly
  var domain=AppDomain.CurrentDomain;
  var name=new System.Reflection.AssemblyName('PInvokeAssembly');
  var assembly=domain.DefineDynamicAssembly(name,AssemblyBuilderAccess.Run);
  var module=assembly.DefineDynamicModule('PInvokeModule');
  var type=module.DefineType('PInvokeType',TypeAttributes.Public + TypeAttributes.BeforeFieldInit);

  // Define the actual P/Invoke method
  var method=type.DefineMethod(methodName,MethodAttributes.Public+MethodAttributes.HideBySig+MethodAttributes.Static+MethodAttributes.PinvokeImpl,returnType,parameterTypes);

  // Apply the P/Invoke constructor
  var ctor=System.Runtime.InteropServices.DllImportAttribute.GetConstructor([Type.GetType("System.String")]);
  var attr=new System.Reflection.Emit.CustomAttributeBuilder(ctor,[dllName]);
  method.SetCustomAttribute(attr);

  // Create the temporary type, and invoke the method.
  var realType=type.CreateType();
  return realType.InvokeMember(methodName,BindingFlags.Public+BindingFlags.Static+BindingFlags.InvokeMethod,null,null,parameters);
}

function FindWindow(title:String)
{
   var parameterTypes:Type[]=[Type.GetType("System.Int32"),Type.GetType("System.String")];
   var parameters:Object[]=[0,title];

   return InvokeWin32("user32.dll",Type.GetType("System.Int32"),"FindWindow",parameterTypes,parameters);
}

function GetWindowThreadProcessId(hWnd:Int32, ProcessId:Int32[])
{
   var parameterTypes:Type[]=[Type.GetType("System.Int32"),Type.GetType("System.Int32").MakeByRefType()];
   var parameters:Object[]=[hWnd,ProcessId[0]];

   var r=InvokeWin32("user32.dll",Type.GetType("System.Int32"),"GetWindowThreadProcessId",parameterTypes,parameters);
   ProcessId[0]=parameters[1];
   return r;
}

function GetWindowText(hWnd:Int32, lpString:StringBuilder, nMaxCount:Int32)
{
   var parameterTypes:Type[]=[Type.GetType("System.Int32"),Type.GetType("System.Text.StringBuilder"),Type.GetType("System.Int32")];
   var parameters:Object[]=[hWnd,lpString,nMaxCount];

   return InvokeWin32("user32.dll",Type.GetType("System.Int32"),"GetWindowTextA",parameterTypes,parameters);
}

//var hWnd=FindWindow("AAA");
//var hWnd=FindWindow("あああ");
var hWnd=FindWindow(Environment.GetCommandLineArgs()[1]);

Console.WriteLine(hWnd);

var ProcessId:Int32[]=[0];

var ThreadId=GetWindowThreadProcessId(hWnd,ProcessId);

Console.WriteLine(ThreadId);
Console.WriteLine(ProcessId[0]);

var lpString:StringBuilder=new StringBuilder(128);

var nCount=GetWindowText(hWnd,lpString,128);

Console.WriteLine(nCount);
Console.WriteLine(lpString.Length);
Console.WriteLine(lpString);

これを応用すると、スクリプトなどからWin32APIを使うための汎用のCOMオブジェクトが作れますね。

« 2007年8月 | トップページ | 2007年10月 »