2022年5月
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
無料ブログはココログ

トップページ | 2006年10月 »

2006年9月28日 (木)

ZIPファイルやCABファイルの中身を直接取り出すプロトコル擬似

res: protocolは、EXEやDLLなどバイナリファイルからリソースを取り出します。
同様に、ZIPファイルやCABファイルから中身を直接取り出すプロトコルがあったら便利なのに。
と思いません? 誰か、CやVBなどで作ってくださいな。 私はスクリプトでやってみました。

ZIP使用例.HTA
<frameset>
<frame src='vbscript:GetObject("script:C:\ZIP.WSC").Extract("C:\tip.zip","tip.htm")' application=yes>
</frameset>

CAB使用例.HTA
<frameset>
<frame src='vbscript:GetObject("script:C:\CAB.WSC").Extract("C:\tip.cab","tip.htm")' application=yes>
</frameset>

HTMLやスクリプトなどのテキストはこんなやり方で取り出せますが、<img src=... >の場合はどうすりゃいいんでしょう?

CABファイルの中身を直接取り出すスクリプトコンポーネント

Extract(CABパス名、ファイル名)

CAB.WSC
<?xml version="1.0" encoding="shift_jis"?>
<package>
<component>
<public>
<method name="Extract" dispid="0" />
</public>
<script language="VBScript"><![CDATA[
Option Explicit
Public Function Extract(Path,Name)
Dim fso
Dim Shell
Dim Folder
Dim FolderItem
Set fso=CreateObject("Scripting.FileSystemObject")
Set Shell=CreateObject("Shell.Application")
Set Folder=Shell.NameSpace(Path)
For Each FolderItem In Folder.Items()
  If LCase(fso.GetFileName(FolderItem.Path))=LCase(Name) Then Exit For
Next
Set Folder=Shell.NameSpace(fso.GetSpecialFolder(2).Path)
Folder.CopyHere FolderItem
Set FolderItem=Folder.Items().Item(Name)
Extract=fso.OpenTextFile(FolderItem.Path).ReadAll()
fso.DeleteFile FolderItem.Path
End Function
']]></script>
</component>
</package>

ZIP.WSCと微妙に違いますが、その微妙に違うところが重要です。
また、ZIP.WSCではCABが扱えませんが、CAB.WSCはZIPも扱えます。

使用例.VBS
MsgBox GetObject("script:"&WScript.ScriptFullName&"\..\CAB.WSC").Extract(WScript.ScriptFullName&"\..\tip.CAB","tip.htm")
MsgBox GetObject("script:"&WScript.ScriptFullName&"\..\CAB.WSC").Extract(WScript.ScriptFullName&"\..\tip.ZIP","tip.htm")

ZIPファイルの中身を直接取り出すスクリプトコンポーネント

Extract(ZIPパス名、ファイル名)

ZIP.WSC
<?xml version="1.0" encoding="shift_jis"?>
<package>
<component>
<public>
<method name="Extract" dispid="0" />
</public>
<script language="VBScript"><![CDATA[
Option Explicit
Public Function Extract(Path,Name)
Dim fso
Dim Shell
Dim Folder
Dim FolderItem
Set fso=CreateObject("Scripting.FileSystemObject")
Set Shell=CreateObject("Shell.Application")
Set Folder=Shell.NameSpace(Path)
Set FolderItem=Folder.Items().Item(Name)
Set Folder=Shell.NameSpace(fso.GetSpecialFolder(2).Path)
Folder.CopyHere FolderItem
Set FolderItem=Folder.Items().Item(Name)
Extract=fso.OpenTextFile(FolderItem.Path).ReadAll()
fso.DeleteFile FolderItem.Path
End Function
']]></script>
</component>
</package>

使用例.VBS
MsgBox GetObject("script:"&WScript.ScriptFullName&"\..\ZIP.WSC").Extract(WScript.ScriptFullName&"\..\tip.ZIP","tip.htm")

2006年9月27日 (水)

ファイルをドロップすると、パス名をウィンドウ表示します。

about protocolの利用例です。

ショートカットを作成して、リンク先に以下を設定します。

"C:\Program Files\Internet Explorer\iexplore.exe" about:

ファイルをドロップすると、パス名をウィンドウ表示します。
複数の場合は空白区切で表示します。

START IExplore.exe about:%*
というバッチファイルを作っても同じです。

※IE7(?)/IE8では使えなくなりました。MSHTAなら使えます。

"%SYTEMROOT%\System32\mshta.exe" about:

START mshta.exe about:%*

コマンドラインからメッセージをウィンドウ表示する。

about protocolの利用例です。

コマンドプロンプトやバッチファイルから、体裁はさておき、とにかく簡単に、メッセージをウィンドウ表示したい、というときに。

START IExplore.exe about:終了しました。リターンコード (ERRORLEVEL) : %ERRORLEVEL%

※IE7(?)/IE8では使えなくなりました。MSHTAなら使えます。

mshta.exe about:終了しました。リターンコード (ERRORLEVEL) : %ERRORLEVEL%

セキュリティレベルを上げるとブックマークレットが使えなくなる。

JSファイルやVBSファイルでブックマークレットのようなことが出来ます。

javascript:~~~
vbscript:~~~
のようなURLをアドレスバーに入れて実行したり、
インターネットショートカットにして、「お気に入り」などから実行することが出来ます。

ただし、スクリプトが動くか、どうかは、そのときのセキュリティゾーンのセキュリティレベルに依存します。
なので、セキュリティレベルを上げると、動かなくなります。

そういうときは、URLファイルをJSファイルやVBSファイルに変えましょう。

このときに、鍵になるのが、これです。

SHDocVw.ShellWindows の既定メンバ
Function Item([index]) As Object
    Return the shell window for the given index

ここで、インデクスが省略可能になっています。省略すると、現在のIEオブジェクトを返します。

Set Shell=CreateObject("Shell.Application")
Set ie=Shell.Windows().Item()
Set window=ie.Document.parentWindow

これを利用すると、ブックマークレットのようなことが、JSファイルやVBSファイルで、自在に出来ます。

コマンドラインからクリップボードを使う。

MSHTA.EXEを利用すると、コマンドラインからクリップボードにテキストを入れたり、取り出したりできます。

ClipOut.CMD
@MSHTA.EXE vbscript:Execute("s=clipboardData.getData(""text""):If Not IsNull(s) Then CreateObject(""Scripting.FileSystemObject"").GetStandardStream(1).Write(s):End If:close:") | MORE

ToClip.CMD
@MORE | MSHTA.EXE vbscript:Execute("clipboardData.setData ""text"",CreateObject(""Scripting.FileSystemObject"").GetStandardStream(0).ReadAll():close:")

コマンドラインでクリップボードからテキストを取り出す。
ClipOut.CMD
ClipOut.CMD > ファイル
ClipOut.CMD | コマンド

コマンドラインからクリップボードにテキストを入れる。
ToClip.CMD
ToClip.CMD < ファイル
コマンド | ToClip.CMD

バッチファイル内では、CALLで使うか、インラインに展開して使うとよいでしょう。

コンソール入出力の場合は、MOREを挟みます。パイプ、リダイレクションでは、MOREは不要です。

2006年9月26日 (火)

MHTMLファイルは危険です。

例えば、http://download.windowsupdate.comなどを「信頼済みサイト」や「イントラネットゾーン」に指定しているひとが多いと思うのですが、 悪意ある者がそういう、よく指定されそうなサイトを、
Content-Location: http://download.windowsupdate.com/
のように埋め込んだMHTMLファイルを一般ユーザにメールで送りつけたり、インターネットからローカルに取り込ませたりして、これを、一般ユーザが、関連付けなどで、IEで開くと、 「信頼済みサイトゾーン」や「イントラネットゾーン」で動くので、「マイコンピュータゾーン」のロックダウンが効きません。

このように、他人の作ったMHTMLファイルは安易に開くと、とても危険です。開く前に中身を確かめましょう。

また、標準設定では、MHTMLファイルはIEにデフォルトの関連付けがしてあるので、これを外しておくことを強く推奨します。

ローカルのMHTMLファイルは、 ZoneFolde.VBSで作成した「インターネットゾーン」のフォルダや「制限付きサイトゾーン」のフォルダに入れて開くと安全です。

※Vista/IEでは、MHTMLファイルのセキュリティゾーンの決定論理がいまいち不明です。

「イントラネットゾーン」の設定は危険です。

最近は、「マイコンピュータゾーン」のロックダウンで、ローカルに置かれたHTMLファイルが安全に開けるようになりました。

と思ったら、そうでもありません。

「Mark of the Web」(MOTW)と言う、訳の分からない機能によって、意図せず/意思に反して、ローカルに置かれたHTMLファイルが「インターネットゾーン」や「イントラネットゾーン」で動く危険があります。

「Mark of the Web」は、IE4以降でサポートされています。

もし「イントラネットゾーン」のサイトにURLを登録していたり、「イントラネットゾーン」の設定を初期設定のままにしていたら、とっても危険です。

例えば、http://www.microsoft.com/などを「信頼済みサイト」や「イントラネットゾーン」に指定していると、 悪意ある者がそういう、よく指定されそうなURLを、
<!-- saved from url=(0025)http://www.microsoft.com/ -->
のように埋め込んだWebページをインターネットに置いておき、一般ユーザが、IE以外のブラウザやダウンローダで、ローカルにそのまま取り込み、これを、一般ユーザが、関連付けなどで、IEで開くと、 「インターネットゾーン」や「イントラネットゾーン」で動くので、 「マイコンピュータゾーン」のロックダウンが効きません。

「インターネットゾーン」ならリスクは少ないですが、「イントラネットゾーン」の場合は危険です。

メールなどで受け取ったHTMLファイルでも同様です。ローカルで開く前に中身を確認しましょう。

「イントラネットゾーン」のサイト指定で
「ほかのゾーンにないローカル(イントラネット)のサイトをすべて含める」
をチェックしていると、「イントラネットゾーン」の汎用指定
<!-- saved from url=(0017)http://localhost/ -->
や(nnnn)を故意に短く指定した怪しい指定でも「イントラネットゾーン」で動くので、ここのチェックは必ず外しておきましょう。

「Mark of the Web」機能は無効にすることが出来ないようなので、「イントラネットゾーン」のサイト登録は使わないことを強く推奨します。

また「イントラネットゾーン」のセキュリティレベルは高に設定しておきましょう。
「イントラネットゾーン」は、使わないだけでは対策は不十分で、使えないようにしておく必要があります。

※Vista/IEの保護モード:有効で、危険が軽減されるケースがありますが、そうでないケースもあるので、やはり危険です。

2006年9月25日 (月)

JScriptやVBScriptのスクリプトエンコードをデコードする。

JScriptのヘルプを見ると、次のように書いてあります。
| メモ   関数を呼び出すときは、必須の引数とかっこを記述してください。かっこを付けずに関数を呼び出すと、関数の結果ではなく、関数のテキストが返されます。

例えば、

function hoge(){return 1;}
WScript.Echo(hoge);

をfox.jsとして実行すると、

function hoge(){return 1;}

が表示されます。

これ自体はちっとも面白くないのですが、

function hoge(){ #@~^NQAAAA==W!x^DkKxP4WTn`* .+DE.U,Fi)@#@&q?mMr2Yc21tG`4GT+bi@#@&dBAAAA==^#~@ }
WScript.Echo(hoge);

(タブを全角空白に変えてます。)

の全角空白をタブに戻して、fox.jseとして実行すると、
functionの中のスクリプトエンコードがデコードされて表示されます。

VBScriptのスクリプトエンコードの場合は、

function hoge(){/* VBScriptのスクリプトエンコード */}
WScript.Echo(hoge);

とコメントで囲んでやれば、同様にデコードできます。

2006年9月24日 (日)

ERRORLEVELの簡単表示

コマンドプロンプトで、いちいち
ECHO %ERRORLEVEL%
なんて、やってられません!

以下のバッチファイルを!.CMDの名前でPATHに入れておきます。

@%*
@ECHO リターンコード (ERRORLEVEL) : %ERRORLEVEL%

使用法は、

! コマンド

または、

コマンド
!

です。

コレクションのループの中での、作成、削除、変更は御法度ですよ。

Folder.Filesでファイルを作成、削除、リネーム。
Folder.SubFoldersでフォルダを作成、削除、リネーム。
Folder.Items()でFolderItemを削除、リネーム。
Shell.Windows()でieをQuit。
EnumNetworkDrivesでMapNetworkDrive、RemoveNetworkDrive。
EnumPrinterConnectionsでAddPrinterConnection、AddWindowsPrinterConnection、RemovePrinterConnection。

数値インデクスを降順にループしながらの削除は問題ないことがありますが、
保証されている訳ではないので避けたほうが無難でしょう。

削除、変更したいとき、コレクションのループの中では、配列に転写するなどしておいて、
コレクションのループを抜けてから、配列のループなどで処理を実行します。

2006年9月23日 (土)

文字列の連結は、ArrayのPush()とJoin()を使おう。

文字列を繰り返し連結するとき、
文字列連結演算子を使うと、性能問題を起こします。

Lines=""
For Each Item In Items
  Lines=Lines & vbLf & Item
Next
MsgBox Lines

こういうときは、ArrayのPush()とJoin()を使いましょう。

Lines=Array()
For Each Item In Items
  Push Lines,Item
Next
MsgBox Join(Lines,vbLf)

VBScriptでは、ArrayのPush()代替関数を使おう。

JScriptのArrayにはpush()があって、配列要素の追加が簡単にできますが、
VBScriptにはありません。そこで、代替関数を作って、使い回します。

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

frameのsrcを外部ファイルでなく、HTML内で与えるインラインframe

どういう役に立つかは分りませんが、frameのsrcを外部ファイルでなく、
HTML内で与えることができます。

<script>
function source(){return '<'+'html>\r\n'+'<'+'head>\r\n'+'<'+'/head>\r\n'+'<'+'body>\r\n'+'<'+'img src=tips.gif>\r\n'+'<'+'/body>\r\n'+'<'+'/html>\r\n\r\n';}
</script>
<frameset>
<frame src="javascript:parent.source()">
</frameset>

PAUSEコマンドのメッセージ変更。

SET /Pの応用です。

@ECHO OFF
SETLOCAL
SET /P z=press any key...<NUL
PAUSE>NUL
ECHO;

MSHTA.EXEを使えば、GUI版のPAUSEが出来ます。

MSHTA.EXE "javascript:alert('続行するにはOKボタンを押してください . . .');close();"

筆者に連絡する。

筆者への連絡手段として、メールなどの代わりに、この記事にコメントしてください。
コメントはブログ上に表示しない設定にしてあります。
コメントを反映する際にコメントを引用することがあります。

2006年9月20日 (水)

現在の日時を何回も取り出さないこと!

現在の日付と時刻を別々に取り出したり、
日時の形式を加工するときに、現在の日時を複数回参照しそうですが、それらは間違いです。

VBScriptで、

MsgBox Date & " " & Time

MsgBox Year(Now) & "年" & Month(Now) & "月" & Day(Now) & "日" & Hour(Now)  & "時" & Minitue(Now)  & "分" & Second(Now) & "秒"

コマンドラインで、

ECHO %DATE% %TIME%

ECHO %DATE:~-10,4% & "年" & %DATE:~-5,2% & "月" & %DATE:~-2% & "日" & %TIME:~0,2% & "時" & %TIME:~3,2%  & "分" & %TIME:~5,2% & "秒"

これらはすべて間違いです。もし、業務システムで使用すると障害です。

例えば、MsgBox Date & " " & Timeでは、
2006/09/20 23:59:59に、Dateが実行され、
2006/09/21 00:00:00に、Timeが実行されると、
表示は、2006/09/20 0:00:00となって、実際の時刻から24時間ずれます。

個人利用の範囲では、コストとリスクのトレードオフを勘案して選択します。

正しくは以下のようにします。

dt=Now
MsgBox Year(dt) & "年" & Month(dt) & "月" & Day(dt) & "日" & Hour(dt)  & "時" & Minitue(dt)  & "分" & Second(dt) & "秒"

Do
  d=Date
  t=Time
Loop Until d=Date
MsgBox d & " " & t

:LOOP
SET d=%DATE%
SET t=%TIME%
IF NOT %d%==%DATE% GOTO :LOOP
ECHO %d% %t%
ECHO %d:~-10,4% & "年" & %d:~-5,2% & "月" & %d:~-2% & "日" & %t:~0,2% & "時" & %t:~3,2%  & "分" & %t:~5,2% & "秒"

また、[yy]yymmddhhmmss形式にするときなど、
ゼロサプレスの配慮では、文字列操作より数値演算を利用するとよいでしょう。

yymmdd=Right(CStr(Year(d)*10000+Month(d)*100+Day(d)),6)
yyyymmdd=Right(CStr(100000000+Year(d)*10000+Month(d)*100+Day(d)),8)
hhmmss=Right(CStr(1000000+Hour(t)*10000+Minute(t)*100+Second(t)),6)

SET yyyymmdd=%d:~-10,4%%d:~-5,2%%d:~-2%

SET hhmmss=%t:~0,2%%t:~3,2%%t:~6,2%
SET /A hhmmss+=1000000
SET hhmmss=%hhmmss:~1%

2006年9月18日 (月)

コマンドラインで、フォルダとファイルを判別する。

IF EXISTでは、フォルダでもファイルでも、存在すれば真です。
しかし、末尾に\を付けると、IF EXISTで区別できるようです。

IF EXIST folder  真
IF EXIST file    真
IF EXIST folder\ 真
IF EXIST file\   偽

IF EXIST something IF NOT EXIST something\ ECHO ファイルです。

IF EXIST something IF EXIST something\ ECHO フォルダです。

HTMLで、逆引用符は引用符か?

規格では、逆引用符は引用符になってないようですが、現実のブラウザでは、引用符のようです。

試しに、

<html><body><form>
<input type=text >
<input type=text >
</form></body></html>

というHTMLをIEで開いて、
ひとつ目に、`、
二つ目に、`><script>alert(1)</script>、
を入れると、
document.documentElememt.outerHTMLから取り出したHTMLは、

<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=shift_jis"></HEAD>
<BODY>
<FORM><INPUT value=`> <INPUT value="`><script>alert(1)</script>"> </FORM></BODY></HTML>

になります。このHTMLを保存して開くと、入力したスクリプトが動きます。

属性値をHTMLEncodeして単一/二重引用符で囲んでいる分には問題ないようです。

もし、属性値を逆引用符で囲むことがあるならば、逆引用符もエンコードするように
HTMLEncodeを修正する必要がありますが、それはきっとないのでしょう。

2006年9月17日 (日)

HTAファイルをWEBアーカイブ形式(MHTML)にする。

HTMLファイルならWEBアーカイブ形式で単一のMHTMLファイルに纏めることができます。

そこで、まず、HTAファイルの拡張子をHTMLに変えて、IEで開き、MHTMLで保存します。
或いは、CDO.Messageを使って、MHTMLファイルを作成します。

このMHTMLファイルの先頭に、
@START MSHTA.EXE "mhtml:file://%~f0" %*
@GOTO :EOF
の2行を追加して、拡張子をCMDに変えます。

或いは、

拡張子をMHTAに変え、この拡張子を
MSHTA.EXE "mhtml:file://%1" %*
に関連付けます。

メリット

配布や管理が1ファイルで簡単。

HTAファイルをバッチファイルにする。

HTAファイルの先頭に以下の3行を加えて、拡張子をCMDに変えます。

@START MSHTA.EXE "%~f0" %*
@GOTO :EOF
<script language=vbscript>document.body.innerText=""</script>

メリット

拡張子がCMDなので、PATH配下に置いて、ベース名だけで起動できます。

2006年9月16日 (土)

MSHTA.EXEの使用法、URLの記述法

MSHTA.EXE {ファイル|URL} [引数...]

ファイルはフルパスで指定します。
もしファイルがないと、非表示でダンマリになるので注意。
URLは最大511バイトです。
もし超えると、バッファオーバランでDEPに引っ掛かります。
URLに空白を含むときや、CMD.EXEで<>|を含むときは、二重引用符で囲みます。

"javascript:スクリプト"

スクリプト中では、'文字列'を使う。"は使わない。文字列中では、\'を使う。
文字列中では、\は\\を使う。なのでファイル名の文字列は要注意です。
ファイル名の文字列を使うときは、次のvbscript:のほうがよいでしょう。

vbscript:Execute("スクリプト")

スクリプト中では、""文字列""を使う。文字列中では、""""を使う。
文は、:で区切ります。
IfはEnd If文で閉じます。
If 式 Then:文:文:文:End If:
If 式 Then:文:文:文:Else:文:文:文:End If:

"about:HTML記述"

HTML中では、'文字列'を使う。"は使わない。文字列中では、\'を使う。
文字列中では、\は\\を使う。なのでファイル名の文字列は要注意です。
ファイル名の文字列を使うときは、先のvbscript:のほうがよいでしょう。

javascript:スクリプト

vbscript:スクリプト
は、
<0s>:<1s>
として、以下のように実行されます。

<HTML><SCRIPT LANGUAGE=<0s>>var __w=<1s>;if(__w!=null)document.write(__w);</SCRIPT></HTML>

※ このHTMLは、MSHTML.DLLの中にあります。

つまり、eval(スクリプト)の評価結果がundefinedやEmptyでないときは、HTMLとして扱われます。

したがって、
"javascript:'HTML記述'"

vbscript:"HTML記述"
は、
"about:HTML記述"
と同じです。

逆に、
"javascript:スクリプト;close();"

vbscript:Execute("スクリプト:close:")
とすれば、HTAウィンドウを表示する前に終了するので、非表示でスクリプトが実行できます。

バッチファイルでファイルを読む方法

「ファイルの先頭行だけを読む。」

SET x=
SET /P x=<ファイル

※ 空行を読むと、変数の中身は変化しないので、読む前に初期化しておく。

「ファイルの先頭から3行を読む。」

(
SET x1=
SET /P x1=
SET x2=
SET /P x2=
SET x3=
SET /P x3=
)<ファイル

「ファイルの4行目だけを読む。」

同上で、読み飛ばす。

(
FOR /L %%n IN (1,1,3) DO SET /P x=
SET x=
SET /P x=
)<ファイル

「ファイルの全行を読む。」

× FOR /F "delims=" %%0 IN (ファイル) DO ECHO %%0

※ 空行が読めないので、駄目。

SETLOCAL ENABLEDELAYEDEXPANSION
(FOR /F %%0 IN ( 'FIND /C /V ""' ) DO SET m=%%0)<ファイル
FOR /L %%n IN (1,1,%m%) DO (
SET x=
SET /P x=
ECHO;!x!
)
)<ファイル

※ EOFが認識できないので、先に行数を調べる。

2006年9月15日 (金)

コマンドラインで、空のファイルを作る、改行のない文字列を出力する。

「空のファイルを作る。」

CALL >ファイル

TYPE NUL >ファイル

※ 出力のない内部コマンドとリダイレクションを使う。
※ 「概要」(SummaryInfo)などは消える。

COPY [/Y] NUL ファイル

※ 上書きが有り得るときは/Y。
※ 「概要」(SummaryInfo)などは元のまま残る。

× ECHO; >ファイル

※ 改行が入るので、駄目。

「改行のある文字列を出力する。」

× ECHO %変数%

※ 空の文字列で、「ECHO は <ON> です。」

ECHO;%変数%

※ 空の文字列が有り得るときは区切文字=,;+/[]を使う。

「改行のない文字列を出力する。」

SET /P X=文字列<NUL

SET /P X=文字列<NUL >ファイル

SET /P X=文字列<NUL >>ファイル

「ステータスバー擬似が可能。」

********** のような棒グラフで進捗状況を表します。

@ECHO OFF
FOR /L %%n IN (1,1,79) DO (
SET /P z=*<NUL
FOR /L %%k IN (1,1,999) DO IF EXIST %0 REM 時間稼ぎ
)

「BackSpaceを使えば文字の上書きも可能。」

1%から100%までカウントアップします。

@ECHO OFF
FOR /L %%n IN (1,1,100) DO (
SET /P X=%%n%%<NUL
FOR /L %%k IN (1,1,999) DO IF EXIST %0 REM 時間稼ぎ
)

※ メモ帳でBS()の入力はできないが、貼り付けは可能。

2006年9月13日 (水)

unix tee擬似バッチファイル

標準入力を読んで、標準出力と標準エラーに出力します。

JScriptとVBScriptの2通りで。お好みで選んでください。

TeeHJS.CMD

@MSHTA.EXE "javascript:new ActiveXObject('WScript.Shell').SendKeys('%%{tab}');var fso=new ActiveXObject('Scripting.FileSystemObject');var StdIn=fso.GetStandardStream(0);var StdOut=fso.GetStandardStream(1);var StdErr=fso.GetStandardStream(2);while(!StdIn.atEndOfStream){var Char=StdIn.Read(1);StdOut.Write(Char);StdErr.Write(Char);}close();"

TeeVBS.CMD

@MSHTA.EXE vbscript:Execute("CreateObject(""WScript.Shell"").SendKeys ""%%{tab}"":Set fso=CreateObject(""Scripting.FileSystemObject""):Set StdIn=fso.GetStandardStream(0):Set StdOut=fso.GetStandardStream(1):Set StdErr=fso.GetStandardStream(2):Do While Not StdIn.atEndOfStream:Char=StdIn.Read(1):StdOut.Write(Char):StdErr.Write(Char):Loop:close:")

使用法

コマンドプロンプトでは、

MORE | Tee.CMD 2>ファイル | MORE

バッチファイル内では、

MORE | CALL Tee.CMD 2>ファイル | MORE

MORE | MSHTA.EXE ~~~ 2>ファイル | MORE

前後のMOREは、適宜、別のコマンドかリダイレクションに変えてください。

また、こういうバッチファイルの書き方もあります。

TeeHTA.CMD

@MSHTA.EXE "%~f0"
@GOTO :EOF
<script language=vbscript>
Set wShell=CreateObject("WScript.Shell")
wShell.SendKeys "%{tab}"
Set fso=CreateObject("Scripting.FileSystemObject")
Set StdIn=fso.GetStandardStream(0)
Set StdOut=fso.GetStandardStream(1)
Set StdErr=fso.GetStandardStream(2)
Do While Not StdIn.atEndOfStream
Char=StdIn.ReadLine(1)
StdOut.Write(Char)
StdErr.Write(Char)
Loop
close
</script>

使用法は同様です。

※SendKeys"%{tab}"は、フォーカスが非表示のHTAに移ることへの対策です。

VB6アプリやEXCEL VBAなどのWindowsアプリから標準入出力を使用する方法

VB6アプリやEXCEL VBAなどのWindowsアプリから、(Win32APIを使わないと)、
標準入出力が使えない、と思ってませんか?

VB6やVBAには、Scripting.FileSystemObjectが装備されていて、
そのGetStandardStreamメソッドを使えば、VB6アプリ、EXCEL VBA、WSH(WScript.exe)、
HTA(mshta.exe)、HTML(IE)など、Windowsアプリからも標準入出力が使えます。

仕様は以下にあります。
http://msdn.microsoft.com/library/en-us/script56/html/6ae9a1dc-35ae-4e06-94b2-1578ba153fce.asp?frame=true

Function GetStandardStream(StandardStreamType As StandardStreamTypes, [Unicode As Boolean]) As TextStream
    Retrieve the standard input, output or error stream

StandardStreamType
0 標準入力
1 標準出力
2 標準エラー

Unicode
False シフトJIS
True Unicode (バイナリで読み書きしたいときに、これで代用したりします。)

アプリを起動するときの注意は、
標準入出力がコンソールだと、Windowsアプリの起動時に、OSが閉じてしまうので、
この条件を避けること。

標準入出力がパイプやファイルにリダイレクションされていれば、閉じられません。

Windowsアプリに関連付けされているファイルを、関連付けで起動しても駄目です。
アプリから起動すること。

つまり、例えば、Excelファイルを起動するときは、

Excel.exe hoge.xls <stdin >stdout 2>stderr

more|Excel.exe hoge.xls 2>&1 |more

などのようにします。

次は、これを利用したunix tee擬似を紹介します。

unixコマンド擬似バッチファイル(nl,wc-l,tail,reverse,head)

バッチファイルでunix風のコマンドを幾つか作ってみました。

nl.CMD 行番号を振る。

@ECHO OFF
REM Usage: nl file
FIND /N /V "" <%1

wc-l.CMD 行数をカウントする。

@ECHO OFF
REM Usage: wc-l file
FIND /C /V "" <%1

tail +n ファイルの先頭からn行目以降を表示する。

MORE +# file ここで、n-1を指定する。

tail.CMD ファイルの末尾からn行を表示する。

@ECHO OFF
REM Usage: tail [-#] file
SETLOCAL
SET /A k=-1
SET n=%~1
IF NOT %n:~0,1%==- GOTO :1
SET /A k=n
SHIFT
:1
(FOR /F %%n IN ('FIND /C /V ""') DO SET n=%%n)<%1
SET /A n=n+k
MORE +%n% %1

reverse.CMD ファイルを逆順に表示する。(revは左右逆だが、これは上下逆)

@ECHO OFF
REM Usage: reverse file
SORT /+10000 %1

head.CMD ファイルの先頭からn行を表示する。

@ECHO OFF
REM Usage: head [-#] file
SETLOCAL
SET /A k=-1
SET n=%~1
IF NOT %n:~0,1%==- GOTO :1
SET /A k=n
SHIFT
:1
(FOR /F %%n IN ('FIND /C /V ""') DO SET n=%%n)<%1
SET /A n=n+k
SORT /+10000 %1 | MORE +%n% | SORT /+10000 | MORE +1

2006年9月12日 (火)

「ファイルを開く」「名前を付けて保存」ダイアログのプレースバーを変更、拡張する方法

「ファイルを開く」、「名前を付けて保存」ダイアログでは、
目的のフォルダに辿りつくまでが大変です。

それを簡単にするのが、ダイアログの左側にある領域です。

しかし、これも、使い勝手があまりよくないですね。
カストマイズできれば、まだしも、そのやり方が分からない。

そんなとき、やっと、この領域の名称が分りました。
プレースバー(PlacesBar)と言うらしい。

名前さえ分れば。

インターネットを「プレースバー」で検索すると、カストマイズの方法がいっぱい。
なんだ、Powertoys for Windows XP Tweak UI でも、できるじゃないか。

MSDNライブラリをPlacesBarで検索すると、
http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/userinput/commondialogboxlibrary/aboutcommondialogboxes/openandsaveasdialogboxes.asp?frame=true

プレースバーは、標準状態で、
「最近使ったファイル」
「デスクトップ」
「マイ ドキュメント」
「マイ コンピュータ」
「マイ ネットワーク」
の5個です。

この中で、「最近使ったファイル」は役立たずなので、例えば、「お気に入り」に変更したい。

また、よく使うフォルダを追加したい。

先の記事によれば、変更は可能です。

以下は、そういうレジストリファイルです。好きに変更して、結合してください。

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Comdlg32]

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Comdlg32\Placesbar]
"Place0"=dword:00000006
"Place1"=dword:00000000
"Place2"=dword:00000005
"Place3"=dword:00000011
"Place4"=dword:00000012

一方、追加は出来ないようです。そこで、代替方法です。

「お気に入り」に、例えば、「フォルダ」というフォルダを作成します。
ここに、「好きなフォルダのショートカット(.lnk)」を置きます。

ダイアログでは、「プレースバー」から「お気に入り」、「フォルダ」、
「好きなフォルダのショートカット(.lnk)」と辿ります。

また、事前に置いてなくても、その場で、ダイアログの「プレースバー」から
「お気に入り」、「フォルダ」と辿り、そこにエクスプローラなどから
「好きなフォルダ」をドラッグして、ALTキーを押しながらドロップして、
ショートカット(.lnk)を作成し、それをクリックして辿ることが出来ます。

また、「プレースバー」には、「お気に入り」でなく、この「フォルダ」を登録して
おくとよいでしょう。そして、「フォルダ」に「お気に入り」のショートカット(.lnk)
を作成しておきます。

"Place0"="C:\Documents and Settings\ユーザ名\Favorites\フォルダ"

2006年9月11日 (月)

HTAやHTMLで、ウィンドウの位置やサイズを取得する方法

IEをWSHなど、外部から操作するときは、
Set ie=CreateObject("InternetExplorer.Application")
ie.Left=ie.Left
ie.Top=ie.Top
ie.Width=ie.Width
ie.Height=ie.Height
で、ウィンドウの位置やサイズを参照、変更することができます。

しかし、HTAやHTMLでは、内部のスクリプトから、
window.moveTo
window.resizeTo
で、ウィンドウの位置やサイズを変更することはできますが、
ウィンドウの位置やサイズを取得する機能がありません。

なんで、こういう非対称な機能設計にするのか、と理解に苦しみます。

ウィンドウの位置については、
window.screenLeft
window.screenTop
でクライアント領域の位置は取れますが、
window.moveToの指定位置とは異なります。

一次方程式で書くと、
window.screenLeft=ie.Left+x
window.screenTop=ie.Top+y

このままでは解けないので、moveToで条件を変えて連立方程式を作ります。

screenLeft1=Left1+x
screenTop1=Top1+y

moveTo Left2,Top2

screenLeft2=Left2+x
screenTop2=Top2+y

これを変形すると、

x=screenLeft2-Left2
y=screenTop2-Top2
Left1=screenLeft1-x=screenLeft1-screenLeft2+Left2
Top1=screenTop1-y=screenTop1-screenTop2+Top2

元の位置が分かるので、戻します。

moveTo Left1,Top1

ウィンドウのサイズについても、同様に、

一次方程式で書くと、
window.offsetWidth=ie.Width-x
window.offsetHeight=ie.Height-y

連立方程式は、
offsetWidth1=Width1-x
offsetHeight1=Height1-y

resizeTo Width2,Height2

offsetWidth2=Width2-x
offsetHeight2=Height2-y

解くと、
x=Width2-offsetWidth2
y=Height2-offsetHeight2
Width1=offsetWidth1+x=offsetWidth1+Width2-offsetWidth2
Height1=offsetHeight1+y=offsetHeight1+Height2-offsetHeight2

元のサイズに戻します。
resizeTo Width1,Height1

こうやって代替関数が作れます。

Sub GetPosition(Left1,Top1)
Dim screenLeft1
Dim screenTop1
Dim Left2
Dim Top2

screenLeft1=window.screenLeft
screenTop1=window.screenTop
Left2=screenLeft1
Top2=screenTop1
moveTo Left2,Top2
Left1=screenLeft1+Left2-window.screenleft
Top1=screenTop1+Top2-window.screenTop
moveTo Left1,Top1
End Sub

Sub GetSize(Width1,Height1)
Dim offsetWidth1
Dim offsetHeight1
Dim Width2
Dim Height2

offsetWidth1=document.body.offsetWidth
offsetHeight1=document.body.offsetHeight
Width2=offsetWidth1
If offsetHeight1>270 Then Height2=offsetHeight1 Else Height2=270
resizeTo Width2,Height2
Width1=offsetWidth1+Width2-document.body.offsetWidth
Height1=offsetheight1+Height2-document.body.offsetHeight
resizeTo Width1,Height1
End Sub

HTA、HTML(IE)で使えます。

サイズの取得については閾値があって、小さい領域では、
サイズが正確に取れなかったり、サイズが変化したまま戻らなかったりします。

なので、HTML(IE)の場合は、以下の方法がよいかも知れません。

<object id=ShellWindows classid=clsid:9BA05972-F6A8-11CF-A442-00A0C90A8F39></object>
<script language=vbscript>
Dim ie
For Each ie In ShellWindows
  If TypeName(ie.Document)<>"HTMLDocument" Then
  ElseIf ie.Document.parentWindow Is window Then
    Exit For
  End If
Next

MsgBox Join(Array(ie.Left,ie.Top,ie.Width,ie.Height))
</script>

HTML内から自身のIEを見つけて、ie.Left/ie.Top/ie.Width/ie.Heightで、
ウィンドウの位置やサイズを変更、参照することができます。

なので、HTAでは使えません。

また、オブジェクトを使用するので、セキュリティレベルを前述の方法より下げる必要があります。

Windows XP SP2(IE6.0/WSH5.6)でのみ確認しています。
Windows 2000でも、動くと思いますが、確認できません。

トップページ | 2006年10月 »