2009年11月
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          

最近のコメント

最近のトラックバック

無料ブログはココログ

2009年11月 1日 (日)

PowerShellスクリプトで自分をSTAで起動し直す。

MTAで動かないPowerShellスクリプトは自分をSTAで起動し直します。

ClipOut.ps1

if([Threading.Thread]::CurrentThread.GetApartmentState() -eq "MTA"){
  PowerShell -Sta -File $MyInvocation.MyCommand.Path
}else{
  Add-Type -AssemblyName System.Windows.Forms
  [Windows.Forms.Clipboard]::GetText()
}

2009年10月31日 (土)

PowerShell 2.0があれば、バッチファイルからそのコンソールウィンドウを非表示/最小化/最大化/元に戻す。

コンソールウィンドウを非表示にします。

PowerShell -WindowStyle Hidden -Command Exit

コンソールウィンドウを最小化します。

PowerShell -WindowStyle Minimized -Command Exit

コンソールウィンドウを最大化します。

PowerShell -WindowStyle Maximized -Command Exit

コンソールウィンドウを元に戻します。

PowerShell -WindowStyle Normal -Command Exit

バッチファイルだけでなく、PowerShellスクリプトでも同じです。

2009年10月30日 (金)

PowerShell 2.0 PS1ファイルの関連付け

PowerShell 2.0の初期状態では、拡張子が仮設のファイルタイプへ向いているので、本来のファイルタイプへ向け直します。

[HKEY_CLASSES_ROOT\.PS1]
@="PS1_auto_file"

[HKEY_CLASSES_ROOT\.PS1]
@="Microsoft.PowerShellScript.1"

更に、ファイルタイプのデフォルト動詞が、メモ帳に向いているので、PowerShellに向け直します。

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell]
@="Open"

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell]
@="0"

更に、関連付けのコマンドラインが引数を忘れているようなので、%*を追加します。

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\0\Command]
@="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" \"-file\" \"%1\""

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\0\Command]
@="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" \"-file\" \"%1\" %*"

更に、ファイルをPS1ファイルにドロップするには、WSHのドロップハンドラを使用します。

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\ShellEx\DropHandler]
@="{60254CA5-953B-11CF-8C96-00AA00B8708C}"

ただし、WSHのドロップハンドラはOpen動詞に作用するので、動詞の名前を入れ替えます。

0 → Open
Open → Edit
Edit → ISE

同時に、ファイルタイプのデフォルト動詞を戻します。

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell]
@="0"

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell]
@="Open"

2009年10月15日 (木)

GetCacheEntryInfoW()を使ってURLからキャッシュのパスを調べる。

GetCacheEntryInfoW()を使ってURLからキャッシュのパスを調べるVBAのサンプルです。

Option Explicit

Private Type FILETIME
  dwLowDateTime As Long
  dwHighDateTime As Long
End Type
Private Type INTERNET_CACHE_ENTRY_INFO
  dwStructSize As Long
  lpszSourceUrlName As Long
  lpszLocalFileName As Long
  CacheEntryType As Long
  dwUseCount As Long
  dwHitRate As Long
  dwSizeLow As Long
  dwSizeHigh As Long
  LastModifiedTime As FILETIME
  ExpireTIme As FILETIME
  LastAccessTime As FILETIME
  LastSyncTime As FILETIME
  lpHeaderInfo As Long
  dwHeaderInfoSize As Long
  lpszFileExtension As Long
  dwReserved As Long
  Buffer(0 To 1023) As Byte
End Type

Private Declare Function GetUrlCacheEntryInfo Lib "wininet.dll" Alias "GetUrlCacheEntryInfoW" (ByVal sUrlName As Long, lpCacheEntryInfo As Any, lpdwCacheEntryInfoBufferSize As Long) As Long

Public Function GetCacheFileName(ByVal lpszUrl As String) As String
Dim dwEntrySize As Long
Dim lpCacheEntry As INTERNET_CACHE_ENTRY_INFO
dwEntrySize = Len(lpCacheEntry)
If GetUrlCacheEntryInfo(StrPtr(lpszUrl), lpCacheEntry, dwEntrySize) Then
  GetCacheFileName = MidBW(lpCacheEntry.Buffer, lpCacheEntry.lpszLocalFileName - VarPtr(lpCacheEntry) - 80)
End If
End Function

Function MidBW(Bytes() As Byte, lb As Long) As String
If lb < 0 Then Exit Function
Dim s As String
s = Bytes
s = MidB(s, lb + 1)
MidBW = Left(s, InStr(s, vbNullChar) - 1)
End Function

Sub aaa()
Debug.Print GetCacheFileName(URL)
End Sub

2009年10月 7日 (水)

IE7/IE8のWinINet関数のANSI版(~A)では、シフトJISではなく、UTF-8を使う。

GetCacheEntryInfoA
RetrieveUrlCacheEntryFileA
DeleteUrlCacheEntryA
などのWinINet関数のANSI版(~A)でURLを指定するときは、シフトJISではなく、UTF-8を使います。
シフトJISだと見つかりません。

また、
FindFirstUrlEntryA
GetCacheEntryInfoA
RetrieveUrlCacheEntryFileA
などで得られるINTERNET_CACHE_ENTRY_INFO.lpszSourceUrlNameもシフトJISではなく、UTF-8です。
ところが、一方、INTERNET_CACHE_ENTRY_INFO.lpszLocalFileNameはシフトJISです。なんとも。。。

面倒なので、Unicode版(~W)を使ったほうがよさそう。

IE6で、どうだったかは不明。

2009年10月 6日 (火)

Vistaで、IEのキャッシュフォルダ(Temporary Internet Files)の実フォルダを開く。

IEのキャッシュフォルダ(shell:cache)は仮想フォルダで、なぜか実在するキャッシュファイルの一部しか見えません。
ならば。キャッシュフォルダの実フォルダを開こうとしても、shell:cache\Content.IE5の複数のサブフォルダに分かれています。
なので、エクスプローラでshell:cache\Content.IE5を開いても、サブフォルダが見えるだけです。
そこで、「高度な検索」で、
「場所」「検索先の選択」に、shell:cache\Content.IE5 を入れて、
「インデックスのないファイル、...」をチェックし、
「名前」を空のままエンターします。
この検索条件を保存すると、検索フォルダができます。
ただし、これで見えるのは、保護モード「無効」のコンテンツだけです。

Vistaでは、保護モード「有効」のコンテンツは、shell:cache\Low\Content.IE5 にあります。
なので、同様に、shell:cache\Low\Content.IE5の検索フォルダを作ります。
あるいは、「検索先の選択」に、shell:cache\Content.IE5 と shell:cache\Low\Content.IE5 の両方を入れた検索フォルダを作ります。

2009年10月 5日 (月)

Vistaでは、IEのキャッシュフォルダ(Temporary Internet Files)で保護モード「有効」のコンテンツが見えない。

巷では、保護モードを外せ、とか、信頼済みサイトに登録しろ、とかセキュリティを骨抜きにする俗説が流布されてますが、騙されてはなりません。

explorer.exeを整合性レベル「低」で起動すれば、ちゃんと見えます。

LowCMD.exe /c start explorer.exe /separate,shell:cache

このショートカットを作るには、最小化で、

cmd.exe /c LowCMD.exe /c start explorer.exe /separate,shell:cache

2009年10月 4日 (日)

Vistaでは、WinINet関数で保護モード「有効」のコンテンツが見えない。

FindFirstUrlEntry
GetCacheEntryInfo
RetrieveUrlCacheEntryFile
DeleteUrlCacheEntry
などのWinINet関数では、 保護モード「有効」のコンテンツが見えません。

WinINet関数を使って、IEのキャッシュを一覧、削除、参照しているアプリは注意しましょう。

アプリを整合性レベル「低」で起動すれば見えます。

LowCMD.exe /c hoge.exe

LowCMD.exe /c start hoge.exe

2009年10月 3日 (土)

ShellExecute()で整合性レベル「低」のアプリを起動すると「開いているファイル - セキュリティの警告」ダイアログが出る。

これを迂回するには、ShellExecute()でなく、CreateProcess()で起動すればよい。

例えば、

cmd.exe /c LowCMD.exe

2009年10月 2日 (金)

Run Applications at a Low Integrity Level

アプリを整合性レベル「低」で起動するにはどうするか?

まず、整合性レベル「低」で動く LowCMD.exe を作ります。

copy c:\windows\system32\cmd.exe LowCMD.exe

icacls LowCMD.exe /setintegritylevel Low

この LowCMD.exe からアプリを起動すると、そのアプリプロセスも整合性レベル「低」になります。

LowCMD.exe /c hoge.exe

LowCMD.exe /c start hoge.exe

参考
Designing Applications to Run at a Low Integrity Level
http://msdn.microsoft.com/en-us/library/bb625960.aspx

2009年9月22日 (火)

コマンドラインで、サスペンド(スリープ)、ハイバネーション(休止)する。(その7)

sleep.vbs

CreateObject("WScript.Shell").SendKeys "^{esc}{tab}{tab}{right}{right}s"

その他は、

W sWitch user
L Logoff
O lOck
R Reboot
S Sleep
H Hibanate
U shUtdown

2009年9月20日 (日)

コマンドラインで、サスペンド(スリープ)、ハイバネーション(休止)する。(その6)

suspend.vbs

CreateObject("Shell.Application").ShutdownWindows
Set wShell=CreateObject("WScript.Shell")
Do While Not wShell.AppActivate("Windows のシャットダウン")
  WScript.Sleep 100
Loop
wShell.SendKeys "%w{end}{up}{up}%k"

hibernate.vbs

CreateObject("Shell.Application").ShutdownWindows
Set wShell=CreateObject("WScript.Shell")
Do While Not wShell.AppActivate("Windows のシャットダウン")
  WScript.Sleep 100
Loop
wShell.SendKeys "%w{end}{up}%k"

日本語が送れるSendKeysならもっと確実に選択できるのですが、WScript.ShellのSendKeysは駄目なのです。英語圏は楽にできていいなぁ。
というか、ドロップダウンリストにはユニークなショートカットキーを付けてほしいものです。

2009年9月16日 (水)

WScript.Echo()で文字色や背景色などを変更する。(その2)

スクリプトからExcel経由でWin32APIを呼び出します。

Set Application=CreateObject("Excel.Application")
hwnd=Application.ExecuteExcel4Macro("CALL(""user32"",""FindWindowA"",""JCJ"",""ConsoleWindowClass"",0)")
pid=Application.ExecuteExcel4Macro("CALL(""user32"",""GetWindowThreadProcessId"",""2JN"","& hwnd &","& pid &")")
Call Application.ExecuteExcel4Macro("CALL(""kernel32"",""AttachConsole"",""JJ""," & pid & ")")
hConsole=Application.ExecuteExcel4Macro("CALL(""kernel32"",""CreateFileA"",""JCJJJJJJ"",""CONOUT$"",-1073741824,3,0,3,0,0)")

Echo 12,"赤"
Echo 9,"青"
Echo 14,"黄"
Echo &Hca,"赤"
Echo &H9a,"青"
Echo &Hea,"黄"
Echo 7,""

Call Application.ExecuteExcel4Macro("CALL(""kernel32"",""CloseHandle"",""JJ""," & hConsole & ")")

Sub Echo(wAttribute,Text)
Call Application.ExecuteExcel4Macro("CALL(""kernel32"",""SetConsoleTextAttribute"",""JJJ""," & hConsole & "," & wAttribute & ")")
WScript.StdOut.Write Text
End Sub

2009年9月15日 (火)

WScript.Echo()で文字色や背景色などを変更する。

xcolor.exeを使います。

文字列「赤青黄」をその文字色と緑の背景色で出します。

cscript cecho.vbs

cecho 10*16+12,"赤"
cecho &Ha9,"青"
cecho "&Hae","黄"
cecho 7,""

sub cecho(color,text)
createobject("wscript.shell").exec("xcolor.exe " & color).stdout.readall
wscript.stdout.write text
end sub

2009年9月14日 (月)

文字色や背景色などのコンソールテキスト属性を変更する。(その2)

VB.NETからWin32APIのSetConsoleTextAttribute()を呼び出します。

XColor.exe 属性

属性は、文字色(0~15)、背景色(&H00~&Hf0)、下線(&H8000)など。Color/?参照。
10進数または16進数(&H~)で。CMDでのエスケープは、"&H~" か ^&H~ で。

vbc XColor.VB

Public Class Class1

Private Const GENERIC_READ As Int32 = &H80000000
Private Const GENERIC_WRITE As Int32 = &H40000000
Private Const FILE_SHARE_READ As Int32 = &H00000001
Private Const FILE_SHARE_WRITE As Int32 = &H0000002
Private Const OPEN_EXISTING As Int32 = 3

Private Declare Auto Function CreateFile Lib "kernel32.dll" ( _
  ByVal lpFileName As String, ByVal dwDesiredAccess As Int32, _
  ByVal dwShareMode As Int32, ByVal lpSecurityAttributes As IntPtr, _
  ByVal dwCreationDisposition As Int32, ByVal dwFlagsAndAttributes As Int32, _
  ByVal hTemplateFile As IntPtr) As IntPtr
Private Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal hObject As IntPtr) As Boolean
Private Declare Function SetConsoleTextAttribute Lib "kernel32" (ByVal hConsoleOutput As Integer, ByVal wAttributes As Integer) As Integer

Public Shared Sub Main(ByVal Args() As String)
Dim wAttributes As Integer
If Args.Length=1 AndAlso IsNumeric(Args(0)) Then
  wAttributes = CInt(Args(0))
Else
  Console.WriteLine("Usage: XColor Attribute")
  Exit Sub
End If
Dim hConsoleOutput As Integer = CreateFile("CONOUT$", GENERIC_READ Or GENERIC_WRITE,  FILE_SHARE_READ Or FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, IntPtr.Zero, IntPtr.Zero)
SetConsoleTextAttribute(hConsoleOutput, wAttributes)
CloseHandle(hConsoleOutput)
End Sub
End Class

使用例、文字列「赤青黄」をその文字色で出します。

@echo off
xcolor 12
set <NUL /p x=赤
xcolor 9
set <NUL /p x=青
xcolor "&he"
set <NUL /p x=黄
xcolor 7

2009年9月13日 (日)

文字色や背景色などのコンソールテキスト属性を変更する。

Colorコマンドはコンソール全体の文字色や背景色を変えますが、部分的に変えることはできません。

そこで、部分的に文字色や背景色を変えるColorXコマンドを、OS(Vista)標準搭載のVB.NETで作ります。

ColorX.exe 文字色 [背景色]

文字色や背景色は、数字(0~15)か色の名前(Black~White)で指定します。

vbc ColorX.vb

Public Class Class1
Public Shared Sub Main(ByVal Args() As String)
If Args.Length = 0 OrElse Args.Length > 2 Then
  Console.WriteLine("Usage: ColorX ForegroundColor [BackgroundColor]")
  For Each ColorName As String In ConsoleColor.GetNames(GetType(ConsoleColor))
    Console.WriteLine("{0,2} {1}", [Enum].Format(GetType(ConsoleColor), CType([Enum].Parse(GetType(ConsoleColor), ColorName), ConsoleColor), "d"), ColorName)
  Next
  Exit Sub
End If
Try
  If Args.Length = 1 Then
    Console.ForegroundColor = CType([Enum].Parse(GetType(ConsoleColor), Args(0), True), ConsoleColor)
  Else
    Console.ForegroundColor = CType([Enum].Parse(GetType(ConsoleColor), Args(0), True), ConsoleColor)
    Console.BackgroundColor = CType([Enum].Parse(GetType(ConsoleColor), Args(1), True), ConsoleColor)
  End If
Catch
  Console.Error.WriteLine("Source" & vbTab & vbTab & Err.Source & vbLf & "Number" & vbTab & vbTab & Err.Number & vbLf & "Description" & vbTab & Err.Description & vbLf & "DLL Error" & vbTab & vbTab & Err.LastDLLError)
End Try
End Sub
End Class

使用例、文字列「赤青黄」をその文字色で出します。

@echo off
colorx red
set <NUL /p x=赤
colorx blue
set <NUL /p x=青
colorx yellow
set <NUL /p x=黄
colorx 7

2009年9月 9日 (水)

comm1バッチファイル

ファイル1にあって、ファイル2にない行を標準出力に出します。

comm1 ファイル1 ファイル2

@echo off
findstr /X /L /V /G:%2 %1

unixのcommは入力がsortされている必要がありますが、こちらは必要ありません。
ただし、ファイル1は重複がないように、uniqされてる必要があります。もし、重複があると、重複します。

2009年9月 8日 (火)

comm3バッチファイル

ファイル1とファイル2に共通な行を標準出力に出します。

comm3 ファイル1 ファイル2

@echo off
findstr /X /L /G:%2 %1

unixのcommは入力がsortされている必要がありますが、こちらは必要ありません。
ただし、ファイル1は重複がないように、uniqされてる必要があります。もし、重複があると、重複します。

2009年9月 7日 (月)

uniqバッチファイル

重複行を削除します。

uniq.cmd 入力ファイル 出力ファイル

@echo off
type nul >%2
for /f "delims=" %%I in (%1) do findstr /X /L "%%I" %2 >NUL || (echo;%%I)>>%2

unixのuniqは、入力がsortされている必要がありますが、こちらはsortは必要ありません。

2009年9月 6日 (日)

タスクバーにCPU負荷を表示する。

バッチファイルで、

@echo off
for /L %%L in (0 0 1) do for /F "delims== tokens=2" %%I in ('wmic cpu get LoadPercentage /value') do title %%I%% & timeout 1 >NUL

ショートカットファイルで、

cmd.exe /q /c for /L %L in (0 0 1) do for /F "delims== tokens=2" %I in ('wmic cpu get LoadPercentage /value') do title %I% & start /wait /b timeout 1 >NUL

これらを最小化します。

Vistaでは、HomeBasicにも、wmicがあります。sleepは相変わらずありませんが、代わりに、timeoutやwaitforが使えます。

2009年9月 5日 (土)

wmicの出力は、リダイレクションとパイプで文字コードが異なる。

ファイルへのリダイレクションは、BOM付きUnicodeLEです。こちらの改行文字は正常です。CR+LF

一方、パイプは、シフトJISです。こちらの改行文字が変です。CR+CR+LF

ところで、for /F で読めるのは、シフトJISです。Unicodeは読めません。

なので、wmicの出力をファイルに出して、for /F %I in (ファイル) では読めません。
for /F %I in ('type ファイル') なら、シフトJISに変換されるので、読めます。改行文字の問題もありません。

また、for /F %I in ('コマンド') はパイプなので、wmicの出力がそのまま読めます。ただし、改行文字が。。。

2009年9月 4日 (金)

pingやwmicなど、改行文字がおかしい(CR+CR+LF)コマンド出力をforコマンドで処理する。

pingやwmicなどのコマンドの出力は改行文字がおかしい。CR+CR+LFになってます。
これをforコマンドで処理しようとすると、余分なCRがいろいろ悪さをします。

例えば、
wmic CPU GET LoadPercentage
の出力、
---
LoadPercentage 

2               

---
からCPU負荷を環境変数に取り出そうと、

for /F %%I in ('wmic CPU GET LoadPercentage') do set X=%%I
echo;%X%

のようにすると、取れないはずの空行が、余分なCRのために、余計に取れて邪魔をします。

面倒ですが、callを噛ましてCRを消し、ifで空行を除外します。

for /F %%I in ('wmic CPU GET LoadPercentage') do (call set J=%%I) & if defined J (call set X=%%J%%)
echo;%X%

あるいは、forをもう一段噛ます方法もあります。

for /F %%I in ('wmic cpu get LoadPercentage') do for /F %%J in ("%%I") do set X=%%J
echo;%X%

2009年9月 3日 (木)

pingやwmicなど、改行文字がおかしい(CR+CR+LF)コマンド出力を補正する。

pingやwmicなどのコマンドの出力は改行文字がおかしい。CR+CR+LFになってます。

見た目では分からないので、コマンドプロンプトで以下を試してみると、

for /f "delims=" %i in ('ping') do @echo;`%i'

行末の'が行頭の`を上書きして見えます。

あるいは、ファイルに書き出して、

(for /f "delims=" %i in ('ping') do @echo;`%i')>a.txt

メモ帳で見ると、行末の'の前でカーソル移動が1回停滞します。

さて、一度、ファイルに出力してから、改行文字を補正するには、以下を参照。

改行文字変換フィルタをバッチファイルで作る。(その2)
http://scripting.cocolog-nifty.com/blog/2008/11/post-8809.html

直接、コマンド出力を補正するには、callを噛まします。

for /f "delims=" %i in ('ping') do @call echo;%i

あるいは、コマンド出力を補正してファイルに書き出すには、

(for /f "delims=" %i in ('ping') do @call echo;%i)>a.txt

2009年9月 2日 (水)

FileSystemObjectの~.Pathがネットワークで文字化け?

FSOの旧版(~5.6)では、共有名に日本語などを使うと、~.Pathが文字化けします。
文字化けパターンからすると、共有名がUnicodeLEなのに、シフトJISだと思って、シフトJIS→Unicode変換しているようです。

そこで、~.Pathの代替関数を作って、~.Path の代わりに GetPathName(~) を使います。

ここで、~はFileまたはFolderオブジェクトです。

Dim fso
Set fso=CreateObject("Scripting.FileSystemObject")
MsgBox GetPathName(fso.GetFolder("\\サーバ名\共有名"))

Function GetPathName(fObject)
If fObject.Attributes And 16 Then
  If fObject.IsRootFolder Then
    GetPathName=fObject.Drive.Path & "\"
  Else
    GetPathName=fso.BuildPath(GetPathName(fObject.ParentFolder),fObject.Name)
  End If
Else
  GetPathName=fso.BuildPath(GetPathName(fObject.ParentFolder),fObject.Name)
End If
End Function

2009年9月 1日 (火)

バッチファイルでDEL文字を使う。

メモ帳では、CTRL+BS や ALT+(numキーの)127 で入力できますが、見た目では見えないので、環境変数に割り当てて使うとよいでしょう。

for /f %%I in ('cmd/u /cecho 使') do set DL=%%I
set DL=%DL:~0,1%
echo %DL%

2009年8月31日 (月)

ファイル名にDEL文字が使える!

意外なことに、ファイル名にDEL文字()(0x7F、127)が使えます。それも短いファイル名に使えます。ということは、DOS時代からの仕様?
エクスプローラではCTRL+BSで入力できますが、コマンドプロンプトでは入力できません。
しかし、エクスプローラでは見えません。コマンドプロンプトでは見えます。

他の制御文字(0x00~0x1F)が使えないのに、DEL文字だけが使えるのは「一貫性のない仕様」です。

また、入力できるのに表示されない「仕様の非対称性」はセキュリティ上の脆弱性の懸念があります。

2009年8月30日 (日)

IEの特定のタブをアクティブにする。(その2)

VB.NETに移植。

Function TabActivate(URL As String) As String
Dim Shell As Object=CreateObject("Shell.Application")
Dim ie As Object=Nothing
For Each ie In Shell.Windows()
  If ie.LocationURL=URL Then Exit For
  ie=Nothing
Next
If ie Is Nothing Then
  TabActivate="Not Found 1"
  Exit Function
End If
Dim wShell As Object=CreateObject("WScript.Shell")
If wShell.AppActivate(ie.Document.title) Then
  TabActivate=True
  Exit Function
End If
Dim ix As Object=Nothing
Dim LocationURL As String
For Each ix In Shell.Windows()
  If ix.HWND=ie.HWND Then
    If ix.Document.title<>"" Then If wShell.AppActivate(ix.Document.title) Then Exit For
    LocationURL=ix.LocationURL
    If Left(LocationURL,8)="file:///" Then
      LocationURL=Replace(Uri.UnescapeDataString(Mid(LocationURL,9)),"/","\")
    End If
    If wShell.AppActivate(LocationURL) Then Exit For
  End If
  ix=Nothing
Next
If ix Is Nothing Then
  TabActivate="Not Found 2"
  Exit Function
End If
For Each ix In Shell.Windows()
  If ix.HWND=ie.HWND Then
    wShell.SendKeys("^{tab}")
    Threading.Thread.Sleep(100)
    If wShell.AppActivate(ie.Document.title) Then
      TabActivate=True
      Exit Function
    End If
  End If
Next
TabActivate="Not Found 3"
End Function

2009年8月29日 (土)

バッチファイルで文字色と背景色を変えて文字列をECHOする。

cecho.cmd

@echo off
setlocal
if not defined color set color=c
md ___
pushd ___
<nul >"%*" cmd /k prompt $h
findstr /a:%color% "." "%*" nul
popd
rd /s /q ___

使用法

set color=背景色文字色
call cecho.cmd テキスト

「背景色文字色」は、color /? 参照。
「テキスト」にはファイル名に使えない文字は使えません。

使用例

「青赤黄」をその色で出します。続けて背景色で出します。

@echo off
setlocal
set color=9
call cecho 青
set color=c
call cecho 赤
set color=e
call cecho 黄
set color=90
call cecho 青
set color=c0
call cecho 赤
set color=e0
call cecho 黄
set color=a0
call cecho 緑

2009年8月27日 (木)

名前でもってwindowオブジェクトを捕捉する。

これならShellWindowsを使わずにできます。

msgbox typename(win)

function getwin(name)
nop="javascript:void(0)"
set getwin=open(nop,name)
if getwin.location.href<>nop then exit function
getwin.close
set getwin=nothing
end function

2009年8月25日 (火)

同じ名前のウィンドウを(もしあれば)閉じて、常に新しく開く。

例えば、

set win=open("about:blank","hoge","resizable=no")
win.close
set win=open("about:blank","hoge","resizable=no")

は、うまく行きません。

closeが非同期なので、遅延して、時間軸上で、

set win=open("about:blank","hoge","resizable=no")
set win=open("about:blank","hoge","resizable=no")
win.close

のように動いて失敗します。

set win=open("about:blank","hoge","resizable=no")
win.close
do while not win.closed
' wscript.sleep 100 ' WSHならsleepがありますが、HTMLにはないので。
loop
set win=open("about:blank","hoge","resizable=no")

のように待ち合わせれば回避できますが、スピンループなので避けましょう。

set win=open("about:blank","hoge","resizable=no")
win.name=""
win.close
set win=open("about:blank","hoge","resizable=no")

あるいは、さらに、

set win=open("about:blank","hoge","resizable=no")
win.name=""
win.close
set win=open("about:blank","_blank","resizable=no")
win.name="hoge"

とすれば、非同期に耐えられます。

2009年8月24日 (月)

window.open()やwindow.close()は非同期です。

忘れてると、タイミング障害が思い出させてくれます。

例えば、以下で、非同期であることが分かります。

set win=open("about:blank","hoge","resizable=no")
win.close
msgbox win.closed

2009年8月21日 (金)

window.focus()とwindow.document.focus()の違い。

window.focus()は複数タブで効かないが、window.document.focus()は複数タブのアクティブタブでは効く。

逆に、シングルタブでwindow.blurした後、window.focusは効くが、window.document.focusは効かない。

同様に、シングルタブで最小化した状態で、window.focusは効くが、window.document.focusは効かない。

2009年8月20日 (木)

IE7/IE8で、window.open()で作った子ウィンドウから親ウィンドウを.focus()でアクティブにする。(その3)

MsgBox(vbSystemModal)を利用します。
親ウィンドウがバックグラウンド タブの場合は、そのタブバーが点滅します。

child msg.htm

<html>
<head>
<script language=vbscript>
Sub MsgActivate
opener.focus
opener.document.focus
If opener.document.hasFocus() Then Exit Sub
opener.setTimeout "MsgBox ""focus moving..."",vbSystemModal",0,"vbscript"
End Sub
</script>
</head>
<body>
<button onclick="opener.focus">opener.focus</button>
<button onclick="opener.document.focus">opener.document.focus</button>
<button onclick='opener.setTimeout "MsgBox ""focus moving..."",vbSystemModal",0,"vbscript"'>MsgBox</button>
<button onclick="MsgActivate">MsgActivate</button>
</body>
</html>

2009年8月19日 (水)

IE7/IE8で、window.open()で作った子ウィンドウを.focus()でアクティブにする。(その3)

IE7/IE8では、「常に新しいタブでポップアップを開く」等の設定により、window.open()が新しいタブで開かれます。
そのような場合は、子ウィンドウを.focus()でアクティブにするのは困難です。
外部オブジェクトを使えば、なんとかなりますが、それも大変です。

そこで、スクリプトだけの簡便な方法です。MsgBox(vbSystemModal)を利用します。

<html>
<head>
<script language=vbscript>
Dim win
Sub MsgActivate
win.focus
win.document.focus
If win.document.hasFocus() Then Exit Sub
win.setTimeout "MsgBox ""focus moving..."",vbSystemModal",0,"vbscript"
End Sub
</script>
</head>
<body>
<button onclick='Set win=window.open("child msg.htm")'>open new tab</button>
<button onclick='Set win=window.open("child msg.htm","_blank","resizable=no")'>open new window</button>
<button onclick="win.focus">win.focus</button>
<button onclick="win.document.focus">win.document.focus</button>
<button onclick='win.setTimeout "MsgBox ""focus moving..."",vbSystemModal",0,"vbscript"'>MsgBox</button>
<button onclick="MsgActivate">MsgActivate</button>
</body>
</html>

子ウィンドウがバックグラウンド タブの場合は、そのタブバーが点滅します。

2009年8月18日 (火)

IE7/IE8で、window.open()で作った子ウィンドウから親ウィンドウを.focus()でアクティブにする。(その2)

もし、複数タブのケースを除外するなら、比較的簡単です。WScript.ShellのAppActivateを使用します。

child app.htm

<html>
<head>
<script language=vbscript>
Sub Activate
opener.focus
opener.document.focus
If opener.document.hasFocus() Then Exit Sub
Dim wShell
Set wShell=CreateObject("WScript.Shell")
If wShell.AppActivate(opener.document.title) Then Exit Sub
Dim LocationURL
LocationURL=opener.location.href
If Left(LocationURL,8)="file:///" Then LocationURL=Replace(UnEscape(Mid(LocationURL,9)),"/","\")
If LocationURL="about:blank" Then LocationURL="空白のページ"
If wShell.AppActivate(LocationURL) Then Exit Sub
End Sub
</script>
</head>
<body>
<button onclick="opener.focus">opener.focus</button>
<button onclick="opener.document.focus">opener.document.focus</button>
<button onclick='CreateObject("WScript.Shell").AppActivate opener.document.title'>AppActivate</button>
<button onclick="Activate">Activate</button>
</body>
</html>

親ウィンドウのタイトルはユニークにしておきます。
ただし、タイトルがタイトルバーに出ないで、URLのままのときがあります。

親ウィンドウが複数タブであっても、それがアクティブタブなら効きます。

2009年8月17日 (月)

IE7/IE8で、window.open()で作った子ウィンドウを.focus()でアクティブにする。(その2)

もし、複数タブのケースを除外するなら、比較的簡単です。WScript.ShellのAppActivateを使用します。

<html>
<head>
<title>opener</title>
<script language=vbscript>
Option Explicit
Dim win
Sub Activate
win.focus
win.document.focus
If win.document.hasFocus() Then Exit Sub
Dim wShell
Set wShell=CreateObject("WScript.Shell")
If wShell.AppActivate(win.document.title) Then Exit Sub
Dim LocationURL
LocationURL=win.location.href
If Left(LocationURL,8)="file:///" Then LocationURL=Replace(UnEscape(Mid(LocationURL,9)),"/","\")
If LocationURL="about:blank" Then LocationURL="空白のページ"
If wShell.AppActivate(LocationURL) Then Exit Sub
End Sub
</script>
</head>
<body>
<button onclick='Set win=window.open("child app.htm")'>open new tab</button>
<button onclick='Set win=window.open("child app.htm","_blank","resizable=no")'>open new window</button>
<button onclick="win.focus">win.focus</button>
<button onclick="win.document.focus">win.document.focus</button>
<button onclick='CreateObject("WScript.Shell").AppActivate win.document.title'>AppActivate</button>
<button onclick="Activate">Activate</button>
</body>
</html>

子ウィンドウのタイトルはユニークにしておきます。
ただし、タイトルがタイトルバーに出ないで、URLのままのときがあります。

2009年8月15日 (土)

IE7/IE8で、IEオブジェクトがアクティブなタブか?を判定する。

IE7/IE8で、IEオブジェクトがアクティブなタブか?の判定

Function TabIsActive(ie)
ie.StatusText=CStr(ie.HWND)
TabIsActive=(ie.StatusText=CStr(ie.HWND))
End Function

ie.StatusTextを変更可能なのは、アクティブなタブだけなので、試しに変更してみる。
変更文字列には、ie.HWNDを乱数代わりに使います。

判定できるのは、そのIEウィンドウ内でアクティブなタブか、であって、そのIEウィンドウがアクティブかどうかは不明です。

2009年8月14日 (金)

IE7/IE8で、window.open()で作った子ウィンドウを.focus()でアクティブにする。

IE7/IE8では、「常に新しいタブでポップアップを開く」等の設定により、window.open()が新しいタブで開かれます。
そのような場合は、子ウィンドウを.focus()でアクティブにするのは困難です。

<html>
<head>
<object id=ShellWindows classid=clsid:9BA05972-F6A8-11CF-A442-00A0C90A8F39></object>
<object id=wShell classid=clsid:72C24DD5-D70A-438B-8A42-98424B88AFB8></object>
<script language=vbscript>
Option Explicit
Dim win
Dim n
Sub TabActivate
Dim ie,ix
For Each ie In ShellWindows
  If TypeName(ie.Document)<>"HTMLDocument" Then
  ElseIf ie.Document.parentWindow Is win Then
    Exit For
  End If
Next
If IsEmpty(ie) Then
  MsgBox "Not Found 1"
  Exit Sub
End If
win.focus
win.document.focus
If win.document.hasFocus() Then Exit Sub
If AppActivate(ie) Then Exit Sub
For Each ix In ShellWindows
  If ix.HWND=ie.HWND Then
    ix.Document.focus
    If ix.Document.hasFocus() Then Exit For
    If AppActivate(ix) Then Exit For
  End If
Next
If IsEmpty(ix) Then
  MsgBox "Not Found 2"
  Exit Sub
End If
n=0
NextTab
End Sub
Sub NextTab
If win.document.hasFocus() Then Exit Sub
If n<ShellWindows.Count Then
  n=n+1
  wShell.SendKeys "^{tab}"
  setTimeout "NextTab",100
  Exit Sub
End If
MsgBox "Not Found 3"
End Sub
Function AppActivate(ie)
AppActivate=True
If wShell.AppActivate(ie.Document.title) Then Exit Function
Dim LocationURL
LocationURL=ie.LocationURL
If Left(LocationURL,8)="file:///" Then LocationURL=Replace(UnEscape(Mid(LocationURL,9)),"/","\")
If LocationURL="about:blank" Then LocationURL="空白のページ"
If wShell.AppActivate(LocationURL) Then Exit Function
AppActivate=False
End Function
</script>
</head>
<body>
<button onclick='Set win=window.open("child.htm")'>open new tab</button>
<button onclick='Set win=window.open("child.htm","_blank","resizable=no")'>open new window</button>
<button onclick="win.focus">win.focus</button>
<button onclick="win.document.focus">win.document.focus</button>
<button onclick="TabActivate">TabActivate</button>
</body>
</html>

2009年8月13日 (木)

IEの特定のタブをアクティブにする。

AppActivate()では、バックグラウンドのタブはアクティブにできません。

そこで、指定されたURLのタブをアクティブにします。

Function TabActivate(URL)
TabActivate=True
Set Shell=CreateObject("Shell.Application")
For Each ie In Shell.Windows()
  If ie.LocationURL=URL Then Exit For
Next
If IsEmpty(ie) Then
  TabActivate="Not Found 1"
  Exit Function
End If
Set wShell=CreateObject("WScript.Shell")
If AppActivate(wShell,ie) Then Exit Function
For Each ix In Shell.Windows()
  If ix.HWND=ie.HWND Then If AppActivate(wShell,ix) Then Exit For
Next
If IsEmpty(ix) Then
  TabActivate="Not Found 2"
  Exit Function
End If
For Each ix In Shell.Windows()
  If ix.HWND=ie.HWND Then
    wShell.SendKeys "^{tab}"
    WScript.Sleep 100
    ie.StatusText=CStr(ie.HWND)
    If ie.StatusText=CStr(ie.HWND) Then Exit Function
  End If
Next
TabActivate="Not Found 3"
End Function

Function AppActivate(wShell,ie)
AppActivate=True
If ie.Document.title<>"" Then If wShell.AppActivate(ie.Document.title) Then Exit Function
LocationURL=ie.LocationURL
If Left(LocationURL,8)="file:///" Then LocationURL=Replace(UnEscape(Mid(LocationURL,9)),"/","\")
If LocationURL="about:blank" Then LocationURL="空白のページ"
If wShell.AppActivate(LocationURL) Then Exit Function
AppActivate=False
End Function

2009年8月12日 (水)

Vista+IE8 で、mhtmlファイルを開くと、ゴミファイルが残る。(その3)

XP SP3 + IE8 で、mhtmlファイルを開くと、Tempフォルダにゴミファイル(wbk*.tmp)がいっぱい残りました。
http://scripting.cocolog-nifty.com/blog/2009/05/ie8-mhtml-ae86.html

Vista+IE8の場合は、Temp\Lowフォルダにゴミファイル(wbk*.tmp)がいっぱい残ります。

%Temp%\Low

C:\Users\user\AppData\Local\Temp\Low

Vista用ごみ掃除ショートカットは、

cmd.exe /V:ON /C"COLOR F0 & TITLE %TEMP% & DIR "%TEMP%\Low\wbk*.tmp" & SET /P RESPONSE=Delete ? y or Cancel & IF /I "!RESPONSE!"=="Y" DEL "%TEMP%\Low\wbk*.tmp""

ところで、Temp\Low の整合性レベルは低なので、Vistaでは、一応、セキュリティは考えられているようです。

というか、Vista用に作られたIE8をそのままXPに持って行ったため、XPでは穴になっているのかも知れません。

2009年8月 8日 (土)

バッチファイルを暗号化する。(その2)

暗号化と言っても英字を半角カタカナにするだけですが。。。

ただし、全角文字は使えません。また、英小文字は英大文字に変換されます。

encrypt.vbs バッチファイル...

Set fso=CreateObject("Scripting.FileSystemObject")
For Each File In WScript.Arguments
  Text=fso.OpenTextFile(File).ReadAll
  If Right(Text,1)<>vbLf Then Text=Text & vbCrLf
  Text=Text & "chcp 932"
  Set oFile=fso.CreateTextFile(File & ".cmd")
  oFile.WriteLine "chcp 20127"
  For k=1 To Len(Text)
    c=Mid(Text,k,1)
    If 32<Asc(c) And Asc(c)<96 Then
      oFile.Write Chr(Asc(c)+128)
    ElseIf 96<Asc(c) And Asc(c)<123 Then
      oFile.Write Chr(Asc(c)+96)
    Else
      oFile.Write c
    End If
  Next
  oFile.Close
Next

例えば、aaa.cmd

echo abc
pause

は、aaa.cmd.cmd

chcp 20127
ナテネマ チツテ
ミチユモナ
テネテミ ケウイ

に暗号化されます。

2009年8月 5日 (水)

IEのFullScreen、ToolBar、AddressBar、MenuBar、StatusBarをトグル。

IE7/IE8のメニューには、アドレスバーの表示切り替えがありません。
IEのメニューには、そもそもフルスクリーンの切り替えがありません。
そこで、以下のhtmファイルをお気に入りバーに入れて使います。

ToggleBar.htm

<html>
<head>
<script language=vbscript>
Dim ie
Sub window_onload
For Each ie In ShellWindows
  If TypeName(ie.Document)<>"HTMLDocument" Then
  ElseIf ie.Document.parentWindow Is window Then
    Exit For
  End If
Next
End Sub
</script>
</head>
<body>
<object id=ShellWindows classid=clsid:9BA05972-F6A8-11CF-A442-00A0C90A8F39></object>
<button onclick="ie.FullScreen=Not ie.FullScreen">FullScreen</button>
<button onclick="ie.ToolBar=ie.ToolBar-1">ToolBar</button>
<button onclick="ie.AddressBar=Not ie.AddressBar">AddressBar</button>
<button onclick="ie.MenuBar=Not ie.MenuBar">MenuBar</button>
<button onclick="ie.StatusBar=Not ie.StatusBar">StatusBar</button>
</body>
</html>

アドレスバーとツールバーは、他にタブがあると、効きません。
フルスクリーンは、他にタブがあると、他のタブが見えなくなります。

2009年8月 4日 (火)

IE7/IE8で、現在または最後にアクティブなIEを捕捉する。

Win32APIのFindWindow()を併用すれば、正確に捕捉できます。

hwnd=CreateObject("Excel.Application").ExecuteExcel4Macro("CALL(""user32"",""FindWindowA"",""JCJ"",""IEFrame"",0)")
For Each ie In CreateObject("Shell.Application").Windows()
  If hwnd=ie.HWND Then
    ie.StatusText=CStr(hwnd)
    If ie.StatusText=CStr(hwnd) Then Exit For
  End If
Next
If IsEmpty(ie) Then
  MsgBox "Not Found"
Else
  MsgBox ie.LocationURL
End If

アクティブなIEウィンドウはFindWindow("IEFrame",0)で分かりますが、アクティブなタブは分かりません。
しかし、StatusTextが変更可能なのはアクティブなタブだけなようなので、それで区別できます。

2009年8月 3日 (月)

Vista+IE8(IE7?)で、制限付きサイトゾーンのフォルダが使える。

以前(XP+IE6)から、shell:cookiesフォルダは、制限付きサイトゾーンで、ここに作ったフォルダやファイルも制限付きサイトゾーンになりましたが、残念なことに、クッキーの削除で消されてしまいました。
なので、あまり実用にならなかったのですが、Vista+IE8(IE7?)では、消されなくなりました。
これで、shell:cacheのインターネットゾーンのフォルダと同様に使えます。

エクスプローラで、shell:cookies を開いて、新しいフォルダを作成し、そのショートカットをお気に入りリンクやデスクトップに入れて使います。

shell:cacheの場合は、エクスプローラで、shell:cache\content.ie5 を開いて、そこでコマンドプロンプトを開き、
md ..\新しいフォルダ名
で、親フォルダに新しいフォルダを作ります。
エクスプローラで、shell:cache\新しいフォルダ名 を開いて、アドレスバーのアイコンをお気に入りリンクやデスクトップにドラッグ&ドロップしてショートカットを作成して使います。

2009年8月 2日 (日)

IE8で、現在または最後にアクティブなシェルを捕捉する。

Win32APIのFindWindow()を併用すれば、正確に捕捉できます。

hwnd=CreateObject("Excel.Application").ExecuteExcel4Macro("CALL(""user32"",""FindWindowA"",""JCJ"",""CabinetWClass"",0)")
For Each ie In CreateObject("Shell.Application").Windows()
  If ie.hwnd=hwnd Then Exit For
Next
If IsEmpty(ie) Then
  MsgBox "Not Found"
Else
  MsgBox ie.Document.Folder.Self.Path
End If

2009年7月31日 (金)

Vistaで、IE.Navigate()する。IEオブジェクトを乗り継ぐ。(その3)

いろいろ補足。

wShell.Run """" & FullName & """ -nohome",0

ie.FullNameは、ドライブ名を除き、文字大小が保存される。ドライブ名は大文字固定。

IExplore.exeのオプション

-Embedding

COM用、非表示、ホームページを開かない。

保護モード: 無効 -> 有効 のNavigate()でIEウィンドウが家出する。

-nohome

ホームページを開かない。表示、なぜか新プロセスの代用に。

例えば、IExplore.exe URLは新プロセスになるとは限らないが、IExplore.exe -nohome URLは必ず新プロセスになる。

非表示は、ShowWindow=0で制御。

-new

(obsolete) 昔は、新プロセスだったが、今は、逆に既存プロセス同居に。無視なのかも。

-e

(obsolete) 昔は、エクスプーラに化かす、だったが、今は、無視。

2009年7月29日 (水)

Vistaで、IE.Navigate()する。IEオブジェクトを乗り継ぐ。(その2)

(マイコンピュータゾーンや信頼済みサイトから)インターネットゾーンなどに、IE.Navigate()するとき、保護モード: 無効 -> 有効 の場合は、現IEオブジェクトはそのままで、新規に別のIEオブジェクトとウィンドウが生成されるようです。
しかし、GUIでは、そういうことはないので、スクリプトでも可能なはずです。

Set wShell=CreateObject("WScript.Shell")
Path=wShell.RegRead("HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\iexplore.exe\")
Set Shell=CreateObject("Shell.Application")
Randomize
Do
  FullName=UCase(Mid(Path,1,1))
  For k=2 To Len(Path)
    If Fix(Rnd*2) Mod 2 Then
      FullName=FullName & UCase(Mid(Path,k,1))
    Else
      FullName=FullName & LCase(Mid(Path,k,1))
    End If
  Next
  For k = Shell.Windows().Count To 1 Step -1
    If Shell.Windows().Item(k - 1).FullName = FullName Then Exit For
  Next
Loop While k
wShell.Run """" & FullName & """ -nohome",0
Do
  For Each ie In Shell.Windows()
    If ie.Visible Then
    ElseIf ie.ReadyState<>0 Then
    ElseIf ie.LocationURL<>"" Then
    ElseIf ie.FullName=FullName Then
      Exit Do
    End If
  Next
  WScript.Sleep 100
Loop
ie.Visible=True

Navigate URL

Sub Navigate(URL)
hwnd=ie.HWND
LocationURL=ie.LocationURL
ie.Navigate URL
Do
  If TypeName(ie)<>"IWebBrowser2" Then
    For Each ie In Shell.Windows()
      If hwnd=ie.HWND Then Exit For
    Next
  End If
  If ie_LocationURL(ie)<>"" Then If LocationURL<>ie_LocationURL(ie) Then If Not ie.Busy Then If ie.ReadyState=4 Then Exit Do
  WScript.Sleep 100
Loop
MsgBox ie.LocationURL
End Sub

Function ie_LocationURL(ie)
On Error Resume Next
ie_LocationURL=ie.LocationURL
End Function

2009年7月28日 (火)

Vistaで、IE.Navigate()する。IEオブジェクトを乗り継ぐ。

(インターネットゾーンから)インターネットゾーンなどに、IE.Navigate()するとき、保護モード: 有効 -> 有効 の場合は、従来どおり。

Set ie=CreateObject("InternetExplorer.Application")
ie.Visible=True
ie.Navigate URL
Do While ie.Busy Or ie.ReadyState<>4
  WScript.Sleep 100
Loop
MsgBox ie.LocationURL

(インターネットゾーンから)マイコンピュータゾーンや信頼済みサイトなどに、IE.Navigate()するとき、保護モード: 有効 -> 無効 の場合は、IEオブジェクトが勝手に消滅して、別のIEオブジェクトが生成されるようです。
ただし、IEウィンドウは継承されるようなので、それを目印にすれば再発見できます。

Set Shell=CreateObject("Shell.Application")
Set ie=CreateObject("InternetExplorer.Application")
ie.Visible=True
hwnd=ie.HWND
LocationURL=ie.LocationURL
ie.Navigate URL
Do
  If TypeName(ie)<>"IWebBrowser2" Then
    For Each ie In Shell.Windows()
      If hwnd=ie.HWND Then Exit For
    Next
  End If
  If ie_LocationURL(ie)<>"" Then If LocationURL<>ie_LocationURL(ie) Then If Not ie.Busy Then If ie.ReadyState=4 Then Exit Do
  WScript.Sleep 100
Loop
MsgBox ie.LocationURL

Function ie_LocationURL(ie)
On Error Resume Next
ie_LocationURL=ie.LocationURL
End Function

2009年7月26日 (日)

Vistaで制限付きサイトを「Web ページ、HTML のみ (*.htm;*.html)」で保存すると、インターネットゾーンになる。

以前のXP+IE6では、Web ページを保存した場合、
「Web アーカイブ、単一のファイル (*.mht)」なら「Content-Location: URL」
「Web ページ、完全 (*.htm;*.html)」なら、Mark Of The Web の <!-- saved from url=(00nn)URL -->
で、セキュリティゾーンの情報が付きました。
しかし、「Web ページ、HTML のみ (*.htm;*.html)」で保存したときは、何の情報も付かず、マイコンピュータゾーンになりました。

ところが、Vista+IE8では、保護モードで保存すると、ファイルにアクセス制御の「整合性レベル 低」という属性が付きます。
これは「Web ページ、HTML のみ (*.htm;*.html)」の場合に限りません。
そして、IEで「整合性レベル 低」という属性が付いたファイルを開くとインターネットゾーンになります。

2009年7月22日 (水)

お気に入り、お気に入りバー、リンクにスクリプトやショートカットを入れるときは、移動で入れる。

お気に入り、お気に入りバー、リンクにスクリプトやショートカットを新規作成(コピーを含む)すると、起動時に「開いているファイル - セキュリティの警告」が出ます。
なので、他所で作るかコピーしたものを移動します。
そこで作ってしまったものは、他所へコピーして、それを移動します。


これも、アクセス制御の「整合性レベル 低」という属性が付かないようにするためです。

2009年7月21日 (火)

ショートカットの起動で、なぜか「開いているファイル - セキュリティの警告」が出る。

Vista+IE8で、アクセサリの電卓をクイック起動バーに入れたら、警告が出るようになりました。

開いているファイル - セキュリティの警告
このファイルを開きますか?
名前: C:\Users\...\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\Calculator.lnk
発行元: 不明な発行元
種類: ショートカット
発信元: C:\Users\...\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\Calculator.lnk

ZoneIDの場合と違って、このケースでは、「□この種類のファイルであれば常に警告する(W)」がありません。

元のショートカットとバイナリ比較しても同じです。代替ストリ-ムもありません。プロパティを見ても変わりません。「ブロックの解除」もありません。
コピーして、コピーのほうを使えば直ります。

原因は、たぶん、アクセサリの電卓をリンクにコピーしてからクイック起動に移動したのではないかと思う。


これも、アクセス制御の「整合性レベル 低」という属性が付いたせいです。
ShellExecute()で、この属性を見て「開いているファイル - セキュリティの警告」を出しているのは、Attachment Execution Service(添付ファイル実行サービスor添付ファイルマネージャ)です。

2009年7月18日 (土)

Vistaでは、ShellWindows.Item()が「現在のシェル」から「デスクトップ シェル」に変更された。

「デスクトップ シェル」が取れるようになったのは朗報ですが、「現在のシェル」が取れなくなったのは、影響大です。

IE7から「現在のIE」が取れなくなりましたが、これはコンテキストメニュー拡張で代替しました。
Vistaからの「現在のシェル」が取れなくなったのは、どう代替するか?

ディレクトリのバックグラウンドのコンテキストメニューで代替します。

[HKEY_CLASSES_ROOT\Directory\Background\shell\メニュー名\command]
@="wscript.exe \"C:\\どこか\\現在のシェル.VBS\" %V"

%Vでディレクトリパスが取れるので、それを目印に探します。

For Each ie In CreateObject("Shell.Application").Windows()
  If InStr(LCase(ie.FullName),"explorer.exe") Then If ie_Document_Folder_Self_Path=WScript.Arguments.Item(0) Then Exit For
Next
If IsEmpty(ie) Then
  MsgBox "Not Found"
Else
  MsgBox ie.Document.Folder.Self.Path
End If

Function ie_Document_Folder_Self_Path
On Error Resume Next
ie_Document_Folder_Self_Path=ie.Document.Folder.Self.Path
End Function

同じディレクトリパスで複数のエクスプローラを開いていると、最初のが取れちゃいます。

2009年7月17日 (金)

Vistaではショートカットへのドロップが使えない!が修正された。

Vistaではショートカットへのドロップが使えない!
http://scripting.cocolog-nifty.com/blog/2008/12/vista-7deb.html

はその後、障害と認めて修正したようですね。(SP2)

Windows Vista ベースのコンピューター上のショートカットに複数のファイルをドラッグするときに実行可能ファイルを開始できないことがあります。
http://support.microsoft.com/kb/943059/ja

2009年7月16日 (木)

Vistaで、任意のフォルダやファイルをインターネットゾーンにする。

XPでは、「shell:cache」以下のフォルダやファイルだけがインターネットゾーンになりました。

Vistaでは、任意のフォルダやファイルをインターネットゾーンにできます。

「お気に入り」以下のフォルダで、ファイルやフォルダを作成するとインターネットゾーンになります。それを他の場所へ移動しても変わりません。

この属性は、フォルダやvbsファイルの場合は、移動では消えませんが、コピーでは消えます。

この属性の有無は、ファイルの場合、プロパティでは分かりません。フォルダの場合、エクスプローラのステータスバーでは分かりません。そのフォルダに新規作成で、テキストファイルを作って、拡張子をvbsに変えて、起動してみれば分かります。「開いているファイル - セキュリティの警告」でブロックされます。あるいは、拡張子をhtmに変えて、開いてみれば、IEのステータスバーで分かります。


これは、アクセス制御の「整合性レベル 低」という属性です。

表示は、

icacls フォルダorファイル

 Mandatory Label\Low Mandatory Level:(I)(NW)

設定は、

icacls フォルダ /setintegritylevel (OI)(CI)low

icacls ファイル /setintegritylevel low

解除は、?

2009年7月15日 (水)

Vistaの「コマンド ウィンドウをここで開く」をシフトキー抜きに変える。

Shift + 右クリック は面倒なので、シフトキー抜きに変えましょう。

Extended値を削除するか、名前を変えて無効化します。

[HKEY_CLASSES_ROOT\Directory\Background\shell\cmd]
"Extended"=-

[HKEY_CLASSES_ROOT\Directory\shell\cmd]
"Extended"=-

一方、「パスとしてコピー」もシフトキーが面倒なので、できれば変えたいところですが、こちらはできそうにない。。。

2009年7月14日 (火)

Vistaのエクスプローラで、旧リンクバーの代替に、「お気に入りリンク」を使う。

C:\Users\ユーザ\Links
shell:Links

エクスプローラの「お気に入りリンク」にアプリのショートカットを入れると、「詳細 >>」から起動することができます。
フォルダへのショートカット以外のものを入れても、「お気に入りリンク」に現れません。
しかし、項目が溢れたときの「詳細 >>」をクリックすると、フォルダへのショートカット以外のものも現れます。
これを利用します。

2009年7月13日 (月)

Vistaのエクスプローラで、旧リンクバーの代替に、ディレクトリのバックグラウンドのコンテキストメニューを使う。

VistaのIEの旧リンクバーの代替には、お気に入りバーがありますが、エクスプローラにはありません。

そんなとき、ディレクトリのバックグラウンドのコンテキストメニューが使えます。

HKCR\directory\background\shell\

commandのパラメタは、%Vでディレクトリパスが取れます。

「コマンドウィンドウをここで開く」を参考にするとよいでしょう。

2009年7月12日 (日)

コマンドラインから「デスクトップの表示」。

start shell:::{3080F90D-D7AD-11D9-BD98-0000947B0257}

デスクトップを開く、のとはちがいますYO!

昔の「デスクトップの表示.SCF」の後継です。ToggleDesktopです。

コマンドラインから「ウィンドウを切り替える」。

start shell:::{3080F90E-D7AD-11D9-BD98-0000947B0257}

2009年7月11日 (土)

Vistaの「ファイル名を指定して実行」の代わりに、エクスプローラの「アドレスバー」を使う。

IEの「アドレスバー」は使えませんが、エクスプローラの「アドレスバー」は使えます。
ファイルが見つからないときは、IEが開くのがちょっと。。。

タスクバーに「アドレスバー」を置いても、代用できますが、場所を取るので、お好みで。

2009年7月 9日 (木)

VistaのIEの「お気に入りセンタ」の選択項目が薄い。

運用で代替するしかないようです。

「お気に入りセンタ」にフォーカスを移動すれば、濃くなります。

なので「お気に入りセンタ」を固定しない。

あるいは「お気に入りセンタ」を固定する場合は、
バックグラウンドをクリックする。なければ、
どこかを右クリックする。
フィードや履歴と切り替える。

ショートカットキーの Alt+C か Ctrl+I を使う。

2009年7月 8日 (水)

Vistaのエクスプローラの「お気に入りリンク」と「フォルダ」の境界は上端に移動する。

分けて使うと。狭くて、使いにくいので、境界を上端に移動し、「お気に入りリンク」と「フォルダ」を切り替えて使う。
「開く」ダイアログや「名前をつけて保存」ダイアログでも同様です。

2009年7月 7日 (火)

Vistaのエクスプローラの「お気に入りリンク」には、フォルダへのショートカットを入れる。

C:\Users\ユーザ\Links
shell:Links

フォルダへのショートカット以外のものを入れても、エクスプローラの「お気に入りリンク」に現れません。

「お気に入りリンク」は「開く」ダイアログや「名前をつけて保存」ダイアログでも使われます。
つまり、プレースバー(Places Bar)の後継でもあるようですが、プレースバー(Places Bar)も、まだ使われるケースがあるようです。

2009年7月 6日 (月)

Vistaの「リンク」は、用途別に3分割されました。

ツールバーの「リンク」は、「お気に入り」の中の「リンク」です。
C:\Users\ユーザ\Favorites\リンク
shell:Favorites\リンク
実行ファイルへのショートカットを入れます。

IEの「お気に入りバー」は、「お気に入り」の中の「お気に入りバー」です。
C:\Users\ユーザ\Favorites\Links
shell:Favorites\Links
インターネットショートカットを入れます。

エクスプローラの「お気に入りリンク」は、これまた別です。
C:\Users\ユーザ\Links
shell:Links
フォルダへのショートカットを入れます。

2009年7月 5日 (日)

Vistaのタスクバーのツールバーは、画面の左側面に置く。

Vistaのタスクバーのツールバーは最初、タスクバーの左端にあるので、これは閉じます。
何かのフォルダを画面の左側面にドロップすると、ツールバーになります。

2009年6月19日 (金)

Adobe Readerはマルチインスタンスサーバです。

Adobe Readerはサーバなんですね。だから、1プロセスが基本形。
なので、AcroRD32.exeを複数、起動しても、1プロセスになります。
2番手以降はクライアント、1番手のサーバに処理を依頼して、完了を待たず、即終了。
逆に印刷が完了しても、1プロセスの場合は終了しないで、サーバとして居座ります。

これらの特性に逆らうには、/nで別プロセスにする。
印刷完了でプロセスを終了させるには、先にプロセスを1個起こしておいて、/n /tで印刷する。
印刷完了でプロセスが終了した後に、先に起こしたプロセスを終了させる。
もし、/nがないと、1番手のサーバに処理を依頼して、即終了するため、印刷完了の待ち合わせができません。
そのため、複数ファイルを連続印刷すると、印刷処理が集中してパンクしたり、順番が不定になったりします。
もし、先にプロセスを1個起こしておかないと、印刷が完了しても、そのプロセスが終了しないため、永久に待ちが解けません。

ところで、Adobe Reader 9からインプロセスサーバに変わりました。

Adobe Reader 8までは、アウトプロセスサーバだったので、IEでPDFファイルを開くと、AcroRD32.exeのプロセスができましたが、Adobe Reader 9からはインプロセスサーバに変わったので、AcroRD32.exeのプロセスはできません。
例えば、GetObject(PDFファイル)で、AcroRD32.exeのプロセスができることを期待していると、外れます。

2009年6月18日 (木)

バッチファイルで複数のPDFファイルを連続印刷する。(その3)

XP Home Editionの場合は、WSHでやるのが素直ですが、無理矢理バッチでやると。

PdfPrintHE.CMD PDFファイル...(複数可、ワイルドカード可)

@echo off
setlocal
set ACTIVE=
for /f %%I in ('start /min /wait /b mshta.exe vbscript:execute("createobject(""scripting.filesystemobject"").getstandardstream(1).write createobject(""wscript.shell"").appactivate(""adobe reader""):close:"^)') do set ACTIVE=%%I
if /i %ACTIVE%==true goto :STARTED
START ACRORD32.EXE /n /h
:STARTING
for /f %%I in ('start /min /wait /b mshta.exe vbscript:execute("createobject(""scripting.filesystemobject"").getstandardstream(1).write createobject(""wscript.shell"").appactivate(""adobe reade""):close:"^)') do if /i %%I==false goto :STARTING
:STARTED
for %%I in (%*) do START /WAIT ACRORD32.EXE /n /t %%I
if /i %ACTIVE%==false start /min /wait /b mshta.exe vbscript:execute("set wshell=createobject(""wscript.shell""):if wshell.appactivate(""adobe reade"") then wshell.sendkeys ""^q"":close:")

ここで、
appactivate("adobe reader")

appactivate("adobe reade")
の微妙な違いには意味があります。後者は前方一致です。

2009年6月17日 (水)

バッチファイルで複数のPDFファイルを連続印刷する。(その2)

XP Pro以上なら、

PdfPrintXP.CMD PDFファイル...(複数可、ワイルドカード可)

@echo off
setlocal
set /a n=0
for /f "delims=" %%I in ('tasklist /nh /fi "imagename eq acrord32.exe"') do set /a n+=1
if not %n%==0 goto :STARTED
START ACRORD32.EXE
:STARTING
ping -n 2 localhost >NUL
for /f "delims=" %%I in ('tasklist /nh /fi "windowtitle eq Adobe Reader" /fi "imagename eq acrord32.exe"') do goto :STARTED
goto :STARTING
:STARTED
for %%I in (%*) do START /WAIT ACRORD32.EXE /n /t %%I
if %n%==0 taskkill /im ACRORD32.EXE

アプリがしっかり立ち上がるのを待ち合わせる必要があります。

2009年6月16日 (火)

バッチファイルで複数のPDFファイルを連続印刷する。

/pの場合は、コマンドで印刷完了の待ち合わせができないので、1コマンドで複数ファイルを指定しちゃいます。

START ACRORD32.EXE /p /h "ファイル1" "ファイル2" ...

/tの場合は、1ファイル1コマンドで待ち合わせします。

START ACRORD32.EXE
START /WAIT ACRORD32.EXE /n /t "ファイル1"
START /WAIT ACRORD32.EXE /n /t "ファイル2"
...

最終的にアプリが1個残ります。

XP Pro以上なら、

setlocal
set /a n=0
for /f "delims=" %%I in ('tasklist /nh /fi "imagename eq acrord32.exe"') do set /a n+=1
if %n%==0 START ACRORD32.EXE & ping -n 2 localhost >NUL
START /WAIT ACRORD32.EXE /n /t "ファイル1"
START /WAIT ACRORD32.EXE /n /t "ファイル2"
...
if %n%==0 taskkill /im ACRORD32.EXE

XP Home Editionの場合は、TLIST.EXEとKILL.EXEを入手して代替するか、WSHで。

2009年6月11日 (木)

260文字以上の長いパス名を短いパス名に変える。

例えば、fso.GetFile("長いパス名").ShortPath で簡単に短いパス名に変えられそうですが、fso.GetFile("260文字以上の長いパス名").ShortPath はエラーになって使えません。
そこで、ディレクトリ階層を上に遡って、上から順に短くして行きます。

Set fso=CreateObject("Scripting.FileSystemObject")

MsgBox GetShortPathName("長~いパス名")

Function GetShortPathName(Path)
GetShortPathName=Path
If Len(Path)>259 Then
  GetShortPathName=fso.BuildPath(GetShortPathName(fso.GetParentFolderName(Path)),fso.GetFileName(Path))
  If Len(GetShortPathName)>259 Then Exit Function
End If
If fso.FileExists(GetShortPathName) Then
  GetShortPathName=fso.GetFile(GetShortPathName).ShortPath
Else
  GetShortPathName=fso.GetFolder(GetShortPathName).ShortPath
End If
End Function

2009年6月10日 (水)

「名前を付けて画像を保存」するコンテキストメニュー拡張(その2)

画像のプロパティからURLをコピーして、URLを指定して画像を開き、「名前を付けて保存」しても、ダメな場合は、
<a href=URL>ほげ</a>
というhtmlファイルを作成して、開き、右クリックして「対象を保存」します。
これはさらに面倒なので、コンテキストメニューを拡張します。

SaveAsImg2.htm

<script language=vbscript defer>
If UCase(external.menuArguments.event.srcElement.tagName)="IMG" Then
  set a1=external.menuArguments.document.createElement("A")
  external.menuArguments.event.srcElement.applyElement a1
  a1.href=external.menuArguments.event.srcElement.src
  a1.focus
  CreateObject("WScript.Shell").SendKeys "+{f10}a"
End If
</script>

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\名前を付けて画像を保存(&2)]
@="C:\\どこか\\SaveAsImg2.htm"
"Contexts"=dword:00000002

これは、<img src=URL> を <a href=URL><img src=URL></a> に書き換えて、画像の右クリックで「対象を保存(&A)」します。

2009年6月 9日 (火)

「名前を付けて画像を保存」するコンテキストメニュー拡張

画像の右クリックで「名前を付けて画像を保存」が、オリジナルの名前と形式でなく、「無題.bmp」や「untitled.bmp」に化けてしまうことがあります。
これは<img src=URL>のURLでキャッシュを探して見つからないときにそうなります。
そのような場合、画像のプロパティからURLをコピーして、アドレスバーに張り付けて開き、「名前を付けて保存」すれば、オリジナルの名前と形式で保存できます。※ダメなときもあります。
それは面倒なので、コンテキストメニューを拡張します。

SaveAsImg1.htm

<script language=vbscript defer>
If UCase(external.menuArguments.event.srcElement.tagName)="IMG" Then
  Set ie=CreateObject("InternetExplorer.Application")
  ie.Navigate "about:blank"
  Do While ie.Busy Or ie.ReadyState<>4
  Loop
  ie.Navigate external.menuArguments.event.srcElement.src
  Do While ie.Busy Or ie.ReadyState<>4
  Loop
  Const OLECMDID_SAVEAS=4
  Const OLECMDEXECOPT_PROMPTUSER=1
  ie.ExecWB OLECMDID_SAVEAS,OLECMDEXECOPT_PROMPTUSER
  ie.Quit
End If
</script>

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\名前を付けて画像を保存(&1)]
@="C:\\どこか\\SaveAsImg1.htm"
"Contexts"=dword:00000002

これは、画像をIEで開いて、「名前を付けて保存」します。

2009年6月 8日 (月)

「名前を付けて画像を保存」で日本語ファイル名が「untitled.bmp」や「無題.bmp」に化ける。

<img src="http://~/日本語.jpg">のようにURLに日本語が含まれる場合は、クライアントの送り方とサーバの受け取り方に選択肢があるため、合わないと表示ができません。
表示はできても、「名前を付けて画像を保存」すると、日本語ファイル名が「untitled.bmp」や「無題.bmp」に化けることがあります。
これは、たぶん、IEが「名前を付けて画像を保存」するときに、シフトJISのURLでキャッシュを探すせいだろうと想像してます。

で、対処法の一つは、URLをUTF-8のURLエンコードすることですが、その場合、「名前を付けて画像を保存」すると、日本語ファイル名が短いファイル名になります。

なので、URLに日本語を使うときは、<a href=URL><img src=URL></a> のように、<a href=URL>を併用するとよいでしょう。
これなら、「名前を付けて画像を保存」の代わりに「対象を保存」で、日本語ファイル名で保存できます。

2009年6月 7日 (日)

「名前を付けて画像を保存」でファイル名が「untitled.bmp」や「無題.bmp」に化ける。

何らかの理由で、<img src=URL>のURLがキャッシュに見つからないときに、そうなるようです。
(a) キャッシュが壊れている。
→ キャッシュを削除する。
→ URLを開いて「名前を付けて保存」する。
(b) キャッシュが溢れている。開いた直後は正しいのに時間が経つと化ける。
→ キャッシュを削除する。拡張する。
→ URLを開いて「名前を付けて保存」する。
(c) URLがhttps:のとき。「暗号化されたページをディスクに保存しない」設定で。
→ <a href=URL>に変えて「対象を保存」する。
(d) URLが変換される。例えば、MSDNライブラリなどのms-help:は、キャッシュではms-itss:になる。
→ <a href=URL>に変えて「対象を保存」する。
(e) ファイル名に日本語が使われている。
→ <a href=URL>に変えて「対象を保存」する。

参考:

http://support.microsoft.com/kb/810978/ja
Internet Explorer で画像がビットマップ (.bmp ファイル) として保存される
この現象は、破損しているプログラム ファイル (たとえば、ActiveX または Java のオブジェクト) がハード ディスクの SystemRoot\Downloaded Program Files フォルダにダウンロードされている場合に発生することがあります。

http://support.microsoft.com/kb/880559/ja
インターネットのページ (Web ページ) 上の JPEG 形式の画像が BMP 形式でしか保存できない場合の対処方法
インターネット一時ファイルや履歴を削除することによって、問題を解決できる可能性があります。

http://support.microsoft.com/kb/260650/ja
Internet Explorer でグラフィック データ ファイルが適切な形式で保存されない
一時インターネット ファイルのキャッシュがいっぱいになっている場合
[インターネット オプション] ダイアログ ボックスの [詳細設定] タブにある [暗号化されたページをディスクに保存しない] チェック ボックスがオンで、セキュリティで保護された接続 (https) を使用してページを読み込んでいる場合

2009年6月 6日 (土)

インターネットショートカット(.url)でWebページを疑似最大化で開く。

スクリプトで最大化と同じウィンドウの位置と大きさに変更するhtmlファイルを利用します。

maximize.htm

←ここにMark Of The Web行を置く。
<script language=vbscript>
moveTo 0,0
resizeTo screen.availWidth,screen.availHeight
navigate Mid(location.search,2)
</script>

インターネットショートカットを以下のように変更します。
[InternetShortcut]
URL=URL

[InternetShortcut]
URL=file:///C:/どこか/maximize.htm?URL

ただし、マイコンピュータゾーンでは、スクリプトがブロックされるので、もし、インターネットゾーンが許しているなら、インターネットのMark Of The Web行を先頭に追加します。
<!-- saved from url=(0014)about:internet -->
さもなく、もし、イントラネットゾーンが許しているなら、イントラネットのMark Of The Web行を先頭に追加します。
<!-- saved from url=(0017)http://localhost/ -->
それも駄目なら、信頼済みサイトゾーンで、

maximize.mht

MIME-Version: 1.0
Content-Type: text/html
Content-Location: http://windowsupdate.microsoft.com/

<script language=vbscript>
moveTo 0,0
resizeTo screen.availWidth,screen.availHeight
navigate Mid(location.search,2)
</script>

[InternetShortcut]
URL=file:///C:/どこか/maximize.mht?URL

※それも駄目なら、イントラネットのZoneIDで、
(
Echo [ZoneTransfer]
Echo ZoneId=1
) > "maximize.htm:Zone.Identifier"


スクリプトで変更したウィンドウの位置や大きさは、レジストリに記憶されません。

2009年6月 5日 (金)

インターネットショートカット(.url)を最大化で開く。

Webページのインターネットショートカットは、START /MAX ほげほげ.url や .urlファイルの ShowCommand=3 が効きません。

コマンドラインでは、
START /MAX IExplore.exe -nohome "ほげほげ.urlのフルパス"

GUIでは、いろいろやり方がありますが、「最大化で開くIEのショートカットファイル」を使う方法。

最大化で開くIEのショートカットファイル IExplorerMax.lnk

「リンク先」「"C:\Program Files\Internet Explorer\iexplore.exe" -nohome」
「実行時の大きさ」「最大化」

(A)「最大化で開くIEのショートカットファイル」を作って、インターネットショートカットをドロップする。

(B)「最大化で開くIEのショートカットファイル」をSendToフォルダに置いて、インターネットショートカットを「送る」。

(C)「最大化で開くIEのショートカットファイル」を作って、インターネットショートカットの関連付けに追加する。

[HKEY_CLASSES_ROOT\InternetShortcut\shell\openmax]
@="最大化で開く(&X)"

[HKEY_CLASSES_ROOT\InternetShortcut\shell\openmax\command]
@="RunDLL32.EXE Shell32.DLL,ShellExec_RunDLL \"C:\\どこか\\IExplorerMax.lnk\" %1"


一般に、IEは、最大化やウィンドウの位置と大きさをレジストリに記憶して、次に開くときに復元します。
ただし、インターネットショートカット(.url)のopen動詞で開くと、これらを記憶せず、最大化も復元しません。
これは、IExplore.exe -Embedding で起動していることと関係がありそう。
-Embedding は、COMの起動方法で普通、非表示。
例えば、START /MAX IExplore.exe -Embedding すると、非表示。

2009年6月 4日 (木)

IE や Webページを最大化で開く。

START /MAX IExplore.exe や ショートカットの「実行時の大きさ」「最大化」が、効くとき と 効かないとき があります。
その違いは、別プロセスを起こすか、既存プロセスに同居するか、の違いのようです。
IE7までは、IExplore.exeで起こすと、別プロセスになりました。
しかし、IE8からは、条件により(ドメイン?)、既存プロセスに同居するようになりました。

最大化で開くIEのショートカットファイル

「リンク先」「"C:\Program Files\Internet Explorer\iexplore.exe" -nohome」
「実行時の大きさ」「最大化」

最大化で開くWebページのショートカットファイル

「リンク先」「"C:\Program Files\Internet Explorer\iexplore.exe" -nohome URL」
「実行時の大きさ」「最大化」

インターネットショートカット(.url)ではできないのでショートカットファイル(.lnk)に変えます。


Explorer.exeも既存プロセスに同居しますが、それでも START /MAX Explorer.exe は効きます。
なので、IE8のこの仕様は、「障害」と思います。

2009年6月 3日 (水)

メモ帳やIEなどのアプリを関連付けで開くとき、常に最大化や最小化で開く。

アプリを最大化や最小化で開くには、ショートカットファイル(.lnk)の「実行時の大きさ」を「最大化」や「最小化」にしておけばよいのですが、関連付けの場合はどうするか?

関連付けで、
cmdw.exe /c start /max "" "アプリ" %1
という方法もありますが、標準環境でやるには?

まず、アプリを最大化や最小化で開くショートカットファイル(.lnk)を作成します。
関連付けでそのショートカットファイル(.lnk)を起動します。
RunDLL32.EXE Shell32.DLL,ShellExec_RunDLL "ショートカットファイルのパス" %1

関連付けの変更箇所が結構多いのが難点かな。


メモ帳やIEなどのアプリは、最後に終了したときのウィンドウの位置や大きさを記憶しているので、それを利用した方法がよく出てますが、どうなんでしょう?
IEは「最大化」も記憶するようですが、メモ帳は「通常のウィンドウ」の位置と大きさだけみたいだし、「通常のウィンドウ」の位置と大きさを「最大化」と同じにするのは、ちょっと違うし。

2009年6月 2日 (火)

バッチファイルからMsgBox()を使う。

バッチファイルの場合は、mshta.exeが便利です。

for /f "delims=" %%I in ('start /min /b /wait MSHTA.EXE vbscript:Execute("CreateObject(""Scripting.FileSystemObject"").GetStandardStream(1).Write MsgBox(""text"",1,""title""):close"^)') do echo %%I

for /f in (' ')の中では、) は " か ^ でエスケープする必要があります。
('" "')でエスケープすることもできますが、もし、中で " を使っていると、エスケープ範囲が逆転してします。

もし、start /min /b /wait がないと、HTAウィンドウが一瞬開きます。
目立たないように、resizeTo 0,0 としても、タイトルバーが見えてしまいます。

2009年5月31日 (日)

バッチファイルから「ファイルを開く」ダイアログを使う。

バッチファイルの場合は、mshta.exeが便利です。

<input type=file>では、初期値、フィルタ、タイトルが固定です。

for /f "delims=" %%I in ('mshta.exe "about:<input type=file id=FILE><script>FILE.click();new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).Write(FILE.value);close();resizeTo(0,0);</script>"') do echo %%I

HtmlDlgHelperのopenfiledlg()なら、初期値、フィルタ、タイトルが変えられます。

@echo off
set "File=C:\Program Files\*.txt"
set "Filter=テキスト (*.txt)|*.txt|すべてのファイル (*.*)|*.*|"
set "Title=ファイルの選択"
for /f "delims=" %%I in ('MSHTA.EXE "about:<object id=HtmlDlgHelper classid=CLSID:3050f4e1-98b5-11cf-bb82-00aa00bdce0b></object><script>resizeTo(0,0);function window.onload(){var Env=new ActiveXObject('WScript.Shell').Environment('Process');new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).Write(HtmlDlgHelper.object.openfiledlg(Env('File'),null,Env('Filter'),Env('Title')).replace(/\0.*/,''));close();}</script><hta:application caption=no showintaskbar=no />"') do echo %%I

2009年5月28日 (木)

IE8で、<input type=file>の.valueがc:\fakepath\filenameを返す。(その2)

IEでも頑張ればできます。<input type=file>の.valueを見る代わりに、<input type=file>の編集ボックスをクリップボードにコピーして、それを<textarea>に張り付けて、<textarea>の.valueを見ます。

Option Explicit

MsgBox ieFileOpen("C:\Program Files\*.txt")

Function ieFileOpen(FileName)
Dim ie
Dim wShell
Set ie=CreateObject("InternetExplorer.Application")
ie.Navigate "about:blank"
Do While ie.Busy Or ie.ReadyState<>4
  WScript.Sleep 100
Loop
ie.Document.body.innerHTML="<input type=file id=FILE><textarea id=TEXT></textarea>"
ie.Document.parentWindow.setTimeout "FILE.click();"
Set wShell=CreateObject("WScript.Shell")
Do While Not wShell.AppActivate("ファイルの選択")
  WScript.Sleep 100
Loop
wShell.SendKeys "%n"&FileName&"%o"
Do While ie.Busy
  WScript.Sleep 100
Loop
ieFileOpen=ie.Document.all.FILE.value
If InStr(ieFileOpen,":\fakepath\")=2 Then
  Const OLECMDID_COPY = 12
  Const OLECMDID_PASTE = 13
  Const OLECMDID_SELECTALL = 17 '(&H11)
  Const OLECMDEXECOPT_DODEFAULT = 0
  ie.Document.all.FILE.focus
  ie.ExecWB OLECMDID_SELECTALL,OLECMDEXECOPT_DODEFAULT
  ie.ExecWB OLECMDID_COPY,OLECMDEXECOPT_DODEFAULT
  ie.Document.all.TEXT.focus
  ie.ExecWB OLECMDID_PASTE,OLECMDEXECOPT_DODEFAULT
  ieFileOpen=ie.Document.all.TEXT.value
End If
ie.Quit
End Function

2009年5月27日 (水)

IE8で、<input type=file>の.valueがc:\fakepath\filenameを返す。

WSHからIEを使って、ファイル選択ダイアログを出す方法が使えなくなりました。

これは、IE8でセキュリティが強化されたせいです。なので、セキュリティを緩めれば回避できますが。

[インターネットオプション][セキュリティ][インターネット][レベルのカストマイズ]
「サーバにファイルをアップロードするときにローカルディレクトリのパスを含める」
を「有効にする」。

セキュリティを緩めないで回避することができるか?

IEでも頑張ればできます。が、HTAならセキュリティ制約を受けません。なので、WSHからmshta.exeを使用する方法が簡単です。

Set wShell=CreateObject("WScript.Shell")
Set oExec=wShell.Exec("mshta.exe ""about:<input type=file id=FILE><script>FILE.click();new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(FILE.value);close();resizeTo(0,0);</script>""")
MsgBox oExec.StdOut.ReadAll

バッチファイルでも、

for /f "delims=" %%I in ('mshta.exe "about:<input type=file id=FILE><script>FILE.click();new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(FILE.value);close();resizeTo(0,0);</script>"') do echo %%I

2009年5月26日 (火)

AcroPDFでPDFファイルを開く、閉じるには?

PDFファイルを開くには、
AcroPDF.Src=ファイルパス または URL
または、
成否=AcroPDF.LoadFile(ファイルパス)
LoadFile()でURLは使えない。

AcroPDFには、PDFファイルを閉じるClose()メソッドがない。
別のファイルを開けば、先のファイルが閉じるので、
AcroPDF.Src="NUL"
または、
失敗=AcroPDF.LoadFile("NUL")
で代替する。

2009年5月25日 (月)

IE8では、PDFファイルを続けてNavigateしないほうがよい。

でないと、PDFファイルがIE終了時まで解放されなくなる。

続けてでなく、間にWebページを挟むとよい。それは、about:blankでもよい。(※1)(※2)

あるいは、PDFファイルを続けて開くときは、後続の
ie.Navigate PDFファイル
の代わりに、
ie.Document.Src=PDFファイル
または、
ie.Document.LoadFile PDFファイル
とするとよいかも。

ここで、TypeName(ie.Document)="AcroPDF" です。

※1 IEでなく、.NETのWebBrowserコントロールの場合は、さらに CoFreeUnusedLibraries()を呼び出す必要があるようです。

※2 最初にPDFファイルを開くと、Webページを開いても解放されない。なので、最初は、about:blankなどで避けたほうがよい。

※※ 6月のMSとAdobeの修正のどれかで直ったみたいです。

2009年5月20日 (水)

ウィンドウタイトルが数字文字列のウィンドウをAppActivateする。

WScript.ShellのAppActivate()では、数字文字列がプロセスIDに解釈されて、ウィンドウタイトルが数字文字列のウィンドウをAppActivateすることができません。

例えば、123という名前のフォルダを開いて、AppActivate("123")とやっても駄目です。
エクスプローラは、複数インスタンスが1プロセスに同居するので、プロセスIDも指定できません。
IE6以前であればIEでフォルダを開いて、AppActivate("123 - Microsoft Internet Explorer")とすることができたのですが、IE7以降はそれもできません。

しかし、タイトル文字列をUnicodeのバイト配列で指定するとよいようです。

Set Stream=CreateObject("ADODB.Stream")
Stream.Open
Stream.Type=2
Stream.WriteText "123"
Stream.Position=0
Stream.Type=1
Stream.Position=2
TitleBytes=Stream.Read(-1)
Set wShell=CreateObject("WScript.Shell")
r=wShell.AppActivate(TitleBytes)
WScript.Echo CStr(r),TitleBytes

2009年5月19日 (火)

IEを使って、UTF-8 の BOM を除去する。

拡張子は .txt 限定ですが、IEで開いて、上書き保存すれば、UTF-8 の BOM を削除できます。

ie-utf8-bom.vbs ファイル...

Option Explicit
Const OLECMDID_SAVE=3
Const OLECMDEXECOPT_DODEFAULT=0
Dim ie
Dim Path

Set ie=CreateObject("InternetExplorer.Application")
For Each Path In WScript.Arguments
  ie.Navigate Path
  Do While ie.Busy Or ie.ReadyState<>4
    WScript.Sleep 100
  Loop
  if ie.Document.Charset="utf-8" Then ie.ExecWB OLECMDID_SAVE,OLECMDEXECOPT_DODEFAULT
Next
ie.Quit

2009年5月18日 (月)

ADODB.Streamを使って、UTF-8 の BOM を除去する。

UTF-8 に BOM があれば、削除して、書き戻します。

utf8-bom.vbs ファイル...

Option Explicit
Const adTypeBinary=1
Const adTypeText=2
Const adSaveCreateOverWrite=2
Dim Path
Dim Src
Dim BOM
Dim Buf

Set Src=CreateObject("ADODB.Stream")
Src.Open
Src.Type=adTypeText
Src.Charset="utf-8"
Src.WriteText ""
Src.Position=0
Src.Type=adTypeBinary
BOM=CStr(Src.Read(3))

For Each Path In WScript.Arguments
  Src.LoadFromFile Path
  Buf=CStr(Src.Read(3))
  If Buf=BOM Then
    Buf=Src.Read(-1)
    Src.Position=0
    Src.Write Buf
    Src.SetEos
    Src.SaveToFile Path,adSaveCreateOverWrite
  End If
Next
Src.Close
Set Src=Nothing

2009年5月17日 (日)

バッチファイルだけで、UnicodeLE や UTF-8 の BOM を除去する。

UnicodeLE の BOM を除去する。

cmd /u /c type BOMありUnicodeファイル > BOMなしUnicodeファイル

これを利用して、

UTF-8 の BOM を除去する。

utf8-bom.cmd BOMありUTF-8ファイル BOMなしUTF-8ファイル

start /min /wait cmd /c chcp.com 65001 ^& cmd /u /c type %1 ^>$$$ ^& cmd /c type $$$ ^>%2 ^& del $$$

こちらは上書き保存も可。

さらに、複数のBOMあり/なし不定のUTF-8ファイルをワイルドカードやドロップで、BOMがあれば削除して上書きする。

for-utf8-bom.cmd BOMあり/なしUTF-8ファイル...

for %%1 in (%*) do start /min /wait cmd /v:on /c chcp.com 65001 ^& cmd /u /c type %%1 ^>$$$ ^& set /p x=^<$$$ ^& ^( if "!x:~0,2!"=="" cmd /c type $$$ ^>%%1 ^) ^& del $$$

2009年5月15日 (金)

forコマンドの憂鬱。ファイルセットのリカージョン

例えば、ファイル名の先頭に文字列を付加しようと、

for %%I in (*.txt) do rename "%%I" "hoge_%%I"

なんてやると、リカージョンを起こします。hoge_hoge_aaa.txt みたいに。

この問題を避けるために、

for /f "delims=" %%I in ('dir /b *.txt') do rename "%%I" "hoge_%%I"

なんてやると、今度は、非シフトJIS文字を含むファイル名が扱えなくなります。

まぁ、そういうケースはあまりないと思いますが。。。

そこで、ファイルセットをいったん環境変数に溜めて、そこから再び取り出します。

setlocal enabledelayedexpansion
set LF=^

set BUF=
for %%I in (*.txt) do set BUF=!BUF!!LF!%%I
for /f "delims=" %%I in ("!BUF!") do rename "%%I" "hoge_%%I"

遅延展開でLF文字を使って結合すると、「複数行の文字列」が作れます。
for /f in ("文字列")を使うと、「複数行の文字列」から1行ずつ順次取り出せます。

2009年5月14日 (木)

フォルダの表示モードを切り替える。

フォルダの表示モードを[写真]→[縮小版]→[並べて表示]→[アイコン]→[一覧]→[詳細]の順に切り替えます。

ToggleViewMode.vbs を「リンク」などに入れて使います。

Set ie=CreateObject("Shell.Application").Windows().Item()
ie.Document.CurrentViewMode=Array(5,3,2,4,7,6,1)(CLng(ie.Document.CurrentViewMode) Mod 7)

あるいは、以下のショートカットファイルをデスクトップなどに作成し、ショートカットキーを割り当てて使います。

mshta vbscript:execute("Set ie=CreateObject(""Shell.Application"").Windows().Item():ie.Document.CurrentViewMode=Array(5,3,2,4,7,6,1)(CLng(ie.Document.CurrentViewMode) Mod 7):close")

2009年5月10日 (日)

バッチファイルで英大文字チェック

文字が英大文字かどうか、をチェックするには、どうするか?

setlocal enabledelayedexpansion
set x=A
set y=a
set z=ABCDEFGHIJKLMNOPQRSTUVWXYZ
if not %z%==!z:%x%=! echo %x%は英文字ですが、大文字か小文字かは不明です。
if not %z%==!z:%y%=! echo %y%は英文字ですが、大文字か小文字かは不明です。

if not %z%==!z:%x%=! if %z%==!z:%x%=%x%! echo %x%は英大文字です。
if not %z%==!z:%y%=! if %z%==!z:%y%=%y%! echo %y%は英大文字です。

setの置換は大文字小文字を区別しません。
ifの比較は大文字小文字を区別します。

2009年5月 9日 (土)

バッチファイルで文字列の長さチェック

文字列の長さが、例えば、8文字かどうか、をチェックするには、どうするか?

まず、文字列の長さが、例えば、9文字以上かどうか、をチェックします。

set x=12345678
set y=%x:~8%
if defined y echo 9文字以上

同様に、8文字以上か、チェックすれば、8文字かどうか、判定できます。

set x=12345678
set y=%x:~8%
if defined y echo 9文字以上
set z=%x:~7%
if defined z echo 8文字以上
if defined z if not defined y echo 8文字

2009年5月 8日 (金)

IE8 で、mhtmlファイルを開くと、ゴミファイルが残る。(その2)

Cacheフォルダにも、サイズ=0のファイルが残ります。:-(

C:\Documents and Settings\user\Local Settings\Temporary Internet Files\Content.IE5\????????\????????

???????? はランダムな英大文字、数字の8文字。

こちらも、ときどき掃除したほうがよいと思いますが、どうしましょう?

@echo off
setlocal enabledelayedexpansion
set A=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
cd /d "%homepath%\Local Settings\Temporary Internet Files\Content.IE5"
for /F "delims=" %%I in ('dir /b /ad') do (
  for %%J in ("%%I\????????") do (
    if %%~zJ==0 (
      set x=%%~nxJ
      set y=!x:~8!
      if not defined y (
        set n=0
        for /L %%K in (0,1,7) do (
          for /F %%C in ("!x:~%%K,1!") do (
            if NOT !A!==!A:%%C=! if !A!==!A:%%C=%%C! set /a n+=1
          )
        )
        if !n!==8 echo del %%J
      )
    )
  )
)

もし、これで問題なければ、echoを外して使います。(未テスト)

2009年5月 7日 (木)

IE8で、mhtmlファイルを開くと、ゴミファイルが増殖する。

XP SP3 + IE8 で、mhtmlファイルを開くと、ゴミファイルがいっぱい残ります。
一度、Tempフォルダを確認してみましょう。
エクスプローラのアドレスバーや「ファイル名を指定して実行」に %temp% と入れます。

C:\Documents and Settings\user\Local Settings\Temp\wbk???.tmp

??? は16進3桁の通番。

なので、次のショートカットを作成して、ときどき掃除しましょう。

cmd.exe /V:ON /C"COLOR F0 & TITLE %TEMP% & DIR "%TEMP%\wbk*.tmp" & SET /P RESPONSE=Delete ? y or Cancel & IF /I "!RESPONSE!"=="Y" DEL "%TEMP%\wbk*.tmp""

IE8は、mhtmlファイルを開くと、そのIEでの初回はCacheフォルダに、二回目以降はTempフォルダに、中身を展開します。
これらは、ページを閉じても残ります。そして、ページを開く度に増殖して行きます。
IEを終了するとき、サイズ>0のファイルは削除されます。
なので、ときどき、IEを終了しましょう。でないと、終了処理にとっても時間が掛ります。
しかし、サイズ=0のファイルはそのまま残ります。

ところで、Tempフォルダなんか使って、セキュリティが大丈夫なのか、ちょっと心配ですね。

2009年5月 5日 (火)

Temporary Internet Filesフォルダの開き方

エクスプローラのアドレスバー、「ファイル名を指定して実行」、startやexplorer.exeの引数に、

shell:cache では、仮想フォルダが開きます。

shell:cache\Content.IE5 で、物理フォルダが開きます。

2009年5月 4日 (月)

インラインMSHTAスクリプトはフォーカスが奪われる。

インラインMSHTAスクリプトは、とても便利なのですが、唯一、最大の問題は、フォーカスが奪われることです。:-(
例えば、「コマンドラインからクリップボードを使う。」の
http://scripting.cocolog-nifty.com/blog/2006/09/post_52b4.html

ToClip.CMD

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

を実行すると、フォーカスが奪われてしまいます。:-(
なので、コンソールをクリックして、フォーカスを取り戻さなくてはなりません。:-(
これが、とても不便です。:-(

これは、htaファイルでも、同じです。
htaファイルを起動すると、フォーカスが奪われます。htaが終了すれば、戻って来ます。
しかし、ここで、もし、<hta:application windowstate=minimize >タグがあれば、いっとき奪われますが、すぐに戻ります。
そこで、<hta:application windowstate=minimize >タグをインラインMSHTAスクリプトに応用します。

ToClip.CMD

@MORE | MSHTA.EXE "about:<hta:application windowstate=minimize /><script>clipboardData.setData('text',new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(0).ReadAll());close();</script>"

2009年5月 3日 (日)

コマンドラインで、サスペンド(スタンバイ)、ハイバネーション(休止)する。(その5)

PowerShellがあれば、バッチファイルやショートカットやコマンドラインで、

サスペンド

powershell.exe ^& {[void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');[void][System.Windows.Forms.Application]::SetSuspendState([System.Windows.Forms.PowerState]'Suspend',$false,$false);}

ハイバネーション

powershell.exe ^& {[void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');[void][System.Windows.Forms.Application]::SetSuspendState([System.Windows.Forms.PowerState]'Hibernate',$false,$false);}

コマンドラインで、サスペンド(スタンバイ)、ハイバネーション(休止)する。(その4)

PowerShellのps1ファイルで、

Suspend.ps1

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Windows.Forms.Application]::SetSuspendState([System.Windows.Forms.PowerState]"Suspend",$false,$false)

Hibernate.ps1

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Windows.Forms.Application]::SetSuspendState([System.Windows.Forms.PowerState]"Hibernate",$false,$false)

コマンドラインで、サスペンド(スタンバイ)、ハイバネーション(休止)する。(その3)

Vistaなら標準環境で、VB.NETで、exeを作って、

vbc /t:winexe Suspend.vb

Imports System.Windows.Forms
Public Class Class1
Public Shared Sub Main()
Application.SetSuspendState(PowerState.Suspend,False,False)
End Sub
End Class

vbc /t:winexe Hibernate.vb

Imports System.Windows.Forms
Public Class Class1
Public Shared Sub Main()
Application.SetSuspendState(PowerState.Hibernate,False,False)
End Sub
End Class

2009年5月 2日 (土)

VB.NETで、モニターの電源を切る。入れる。

.NETがあれば、VB.NETで、exeを作って、モニターの電源を切れます。

vbc /t:winexe MonitorPowerOff.vb

Public Class Class1
Private Declare Function SendMessageA Lib "user32" (hWnd As Integer,  Msg As Integer, wParam As Integer, lParam As Integer) As Integer
Public Shared Sub Main()
SendMessageA(-1, 274, 61808, 2)
End Sub
End Class

vbc /t:winexe MonitorPowerOn.vb

Public Class Class1
Private Declare Function SendMessageA Lib "user32" (hWnd As Integer,  Msg As Integer, wParam As Integer, lParam As Integer) As Integer
Public Shared Sub Main()
SendMessageA(-1, 274, 61808, -1)
End Sub
End Class

PowerShellで、モニターの電源を切る。入れる。

PowerShellがあれば、PowerShellスクリプトで、モニターの電源を切れます。

MonitorPowerOff.ps1

[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form=new-object System.Windows.Forms.Form
$message=[System.Windows.Forms.Message]::Create($form.Handle,274,61808,2)
$nativeWindow=new-object System.Windows.Forms.NativeWindow
$nativeWindow.DefWndProc([ref]$message)

MonitorPowerOn.ps1

[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form=new-object System.Windows.Forms.Form
$message=[System.Windows.Forms.Message]::Create($form.Handle,274,61808,-1)
$nativeWindow=new-object System.Windows.Forms.NativeWindow
$nativeWindow.DefWndProc([ref]$message)

コマンドで、モニターの電源を切る。入れる。

Excelがあれば、コマンドでも、モニターの電源を切れます。

mshta vbscript:execute("CreateObject(""Excel.Application"").ExecuteExcel4Macro ""CALL(""""user32"""",""""SendMessageA"""",""""JJJJJ"""",-1,274,61808,2)"":close")

モニターの電源を入れる。

mshta vbscript:execute("CreateObject(""Excel.Application"").ExecuteExcel4Macro ""CALL(""""user32"""",""""SendMessageA"""",""""JJJJJ"""",-1,274,61808,-1)"":close")

バッチファイルで、モニターの電源を切る。入れる。

Excelがあれば、バッチファイルでも、モニターの電源を切れます。

MonitorPowerOff.cmd

@if(0)==(0) ECHO OFF
CScript.exe //NoLogo //E:JScript "%~f0"
GOTO :EOF
@end
new ActiveXObject("Excel.Application").ExecuteExcel4Macro('CALL("user32","SendMessageA","JJJJJ",-1,274,61808,2)');

MonitorPowerOn.cmd

@if(0)==(0) ECHO OFF
CScript.exe //NoLogo //E:JScript "%~f0"
GOTO :EOF
@end
new ActiveXObject("Excel.Application").ExecuteExcel4Macro('CALL("user32","SendMessageA","JJJJJ",-1,274,61808,-1)');

VBScriptで、モニターの電源を切る。入れる。

Excelがあれば、VBScriptで、モニターの電源を切れます。

MonitorPowerOff.vbs

CreateObject("Excel.Application").ExecuteExcel4Macro "CALL(""user32"",""SendMessageA"",""JJJJJ"",-1,274,61808,2)"

MonitorPowerOn.vbs

CreateObject("Excel.Application").ExecuteExcel4Macro "CALL(""user32"",""SendMessageA"",""JJJJJ"",-1,274,61808,-1)"

2009年4月30日 (木)

IE8 で「お気に入り」や「リンク」からのアプリ起動が再び可能になりました。

IE8 では「リンク」フォルダを「お気に入りバー」フォルダと呼ぶようです。紛らわしい。:-(

IE7 で「お気に入り」や「リンク」からのアプリ起動が制限されました。:-(
http://scripting.cocolog-nifty.com/blog/2008/05/ie7_shell_ie_d297.html
これが、IE8で、再び可能になりました。:-)

なので、「IE7の「リンク」バーからアプリを起動する。」
http://scripting.cocolog-nifty.com/blog/2008/09/ie7-5a02.html
の回避策は不要になりました。:-)

と言うか、この回避策は、IE8では、危険です。
もし「フォルダのショートカット」があると、IEがループします。:-(

2009年4月28日 (火)

XP SP3 + IE7 / IE8 で、エクスプローラの履歴バーがおかしい。(障害)

XP SP3 + IE7 / IE8 の環境で、エクスプローラの履歴バーが異常です。
履歴フォルダ(shell:history)も異常です。
IEの履歴バーは正常です。

例えば、デスクトップのファイルを選択すると、
---------------------------
file:///C:/Documents%2520and%2520Settings/user/デスクトップ/hoge.txt
指定されたパスは存在しません。
パスが正しいか確認してください。
---------------------------

半角空白を%20にエンコードして、更にもう一度、%をエンコードしてるようです。
それをエクスプローラは1回だけ、IEは2回デコードしてるようです。

IE7で履歴まわりを変更したのではないかと疑っています。

ところで、こういう一貫性のない振舞いは、セキュリティの脆弱性攻撃に使われそうな気がしません?

2009年4月26日 (日)

ファイルの関連付けで、長いファイル名を短いファイル名に変換する。(その2)

関連付けの書き方をちょっと変えるだけで、できちゃいます。

例えば、

"%systemroot%\system32\wscript.exe" "%1" %*

と書く代わりに、

%systemroot%\system 32\..\system32\wscript.exe %1 %*

ここで、

空白を含む長いファイル名を""で囲まないと、%1を短いファイル名に変換するのはShellの仕様です。
KB142275 Long File & Path Names Shorten When Launched Using Association

空白を含む長いファイル名を""で囲まなくても実行できるのは、Win32APIのCreateProcess()の仕様です。

空白を含まない場合は、OSのファイル名のコンベンションの\..\を利用して、架空のフォルダ名に空白を入れます。

2009年4月24日 (金)

ファイルの関連付けで、長いファイル名を短いファイル名に変換する。

コンソールアプリなら、cmd.exeを使って、例えば、

exe "%1" %*

の代わりに、

cmd.exe /q /c for %%I in ("%1") do for %%J in ("%~dpsI%~nxI") do exe %~sJ %*

ウィンドウアプリの場合は、cmdw.exeを使って、

cmdw.exe /c for %%I in ("%1") do for %%J in ("%~dpsI%~nxI") do start exe %~sJ %*

ここで、

cmd /c でのfor変数は%文字ですが、関連付けで%英数字は関連付け変数に解釈されるので、%%でエスケープします。

forを二段重ねているのは、%~sIの障害対策です。

2009年4月23日 (木)

cmd.exeをコンソールアプリの起動に使う。

コンソールアプリには、cmd.exeが、コンソールアプリのままで、いろいろ使えます。

「ファイル名を指定して実行」の「名前」のコマンドライン
「ショートカット」の「リンク先」のコマンドライン
「関連付け」の「command」キー値のコマンドライン
「タスクスケジューラ」の「実行するファイル名」のコマンドライン
などに使用すると、

作業フォルダを指定する。
cmd.exe /c cd /d "%1\.." & exe "%1" %*

長いファイルを短いファイル名に変える。
cmd.exe /q /c for %%I in ("%1") do for %%J in ("%~dpsI%~nxI") do exe %~sJ %*

環境変数を設定変更する。
cmd.exe /c set name=value & exe "%1" %*

などが可能になります。例は関連付け。

ただし、最小化、最大化を指定するのには、コンソールが開くので、使えません。

2009年4月22日 (水)

cmdw.exeで、バッチファイルを非表示にできるか?

バッチファイルを実行すれば、非表示にできるか? Yes and No。

バッチファイルが内部コマンドとウィンドウアプリだけなら、Yes。

もし、コンソールアプリを実行すると、コンソールが開くので、No。

2009年4月21日 (火)

ウィンドウアプリに化かしたcmd.exeを使う。

コンソールアプリのcmd.exeをウィンドウアプリのcmdw.exeに変えると、いろいろ使えます。

「ファイル名を指定して実行」の「名前」のコマンドライン
「ショートカット」の「リンク先」のコマンドライン
「関連付け」の「command」キー値のコマンドライン
「タスクスケジューラ」の「実行するファイル名」のコマンドライン
などに使用すると、

最小化、最大化を指定する。
cmdw.exe /c start /min exe "%1" %*

作業フォルダを指定する。
cmdw.exe /c start /d "%1\.." exe "%1" %*

長いファイルを短いファイル名に変える。
cmdw.exe /c for %%I in ("%1") do for %%J in ("%~dpsI%~nxI") do start exe %~sJ %*

環境変数を設定変更する。
cmdw.exe /c set name=value & start exe "%1" %*

などが可能になります。例は関連付け。

cmd.exeと異なって、コンソールが開かない。:-)

2009年4月20日 (月)

コンソールアプリをウィンドウアプリに化かす。

コンソールアプリ、例えば、cmd.exeをウィンドウアプリのcmdw.exeに変えるにはどうするか?

方法1

link.exeを持っていれば、
copy cmd.exe cmdw.exe
link /edit /subsystem:windows cmdw.exe

方法2

なければ、
copy cmd.exe cmdw.exe
c2w.vbs cmdw.exe

c2w.vbs

Option Explicit
Const adTypeBinary=1
Const adTypeText=2
Const adSaveCreateOverWrite=2
Dim Path
Dim Stream
Dim Buf
Dim Bytes

Path=WScript.Arguments.Item(0)
Set Stream=CreateObject("ADODB.Stream")
Stream.Open
Stream.Type=adTypeText
Stream.WriteText Chr(2)
Stream.Position=0
Stream.Type=adTypeBinary
Stream.Position=2
Bytes=Stream.Read(1)
Stream.Close
Stream.Open
Stream.Type=adTypeBinary
Stream.LoadFromFile Path
Buf=Stream.Read(512)
Stream.Position=AscB(MidB(Buf,1+&H3c,1))+AscB(MidB(Buf,2+&H3c,1))*256+AscB(MidB(Buf,3+&H3c,1))*256*256+AscB(MidB(Buf,4+&H3c,1))*256*256*256+(&H4+&H14+&H44)
Stream.Write Bytes
Stream.SaveToFile Path,adSaveCreateOverWrite
Stream.Close
Set Stream=Nothing

2009年4月14日 (火)

バッチファイルを Unicode や UTF-8 で書く。

ただし、拡張子は .cmdu と .cmd8 です。

バッチファイルは、シフトJIS、EUC、EBCDIC などで書けますが、Unicode や UTF-8 では書けません。:-(
しかし、Unicode や UTF-8 であっても、日本語文字だけなら、シフトJISにコード変換すれば、実行できます。
そこで、.cmdu と .cmd8 の拡張子を追加し、関連付けでコード変換します。

.cmdu

cmd.exe /c type "%1">"%1.cmd" & "%1.cmd" %* & del "%1.cmd"

.cmd8

cmd.exe /c start /min /wait cmd /c chcp.com 65001 ^& cmd /u /c type "%1"^>"%1.$$$" & type "%1.$$$" >"%1.cmd" & "%1.cmd" %* & del "%1.$$$" "%1.cmd"

レジストリ定義は、

[HKEY_CLASSES_ROOT\.cmdu\shell\open\command]
@="cmd.exe /c type \"%1\">\"%1.cmd\" & \"%1.cmd\" %* & del \"%1.cmd\""

[HKEY_CLASSES_ROOT\.cmdu\shell\edit\command]
@="notepad.exe %1"

[HKEY_CLASSES_ROOT\.cmd8\shell\open\command]
@="cmd.exe /c start /min /wait cmd /c chcp.com 65001 ^& cmd /u /c type \"%1\"^>\"%1.$$$\" & type \"%1.$$$\" >\"%1.cmd\" & \"%1.cmd\" %* & del \"%1.$$$\" \"%1.cmd\""

[HKEY_CLASSES_ROOT\.cmd8\shell\edit\command]
@="notepad.exe %1"

2009年4月12日 (日)

バッチファイルを暗号化する。

暗号化と言ってもEBCDICにするだけですが。。。

ただし、全角文字は使えません。半角カタカナはOK。

encrypt.cmd 元バッチファイル名 新バッチファイル名

echo chcp.com 20290 ^>nul ^& call :crypt %%* ^& chcp.com 932 ^>nul ^& goto :eof>%2
echo :crypt>>%2
( set /p x=""<nul & cmd /u /c type %1 ) >$$$
start /min /wait cmd /c chcp.com 20290 ^& cmd /c type $$$ ^>^>%2
del $$$

2009年4月 9日 (木)

IE8のHTAで、ローカルソースファイルを編集するコンテキストメニュー拡張

IE7までは、HTAのコンテキストメニューの「ソースの表示」でローカルソースファイルが編集できました。
しかし、IE8では、HTAのコンテキストメニューの「ソースの表示」で、キャッシュのソースファイルを開くようになりました。
そこで、コンテキストメニューに「ソースの編集」を追加します。

EditSource.htm

<script>
new ActiveXObject("Shell.Application").ShellExecute(external.menuArguments.top.document.URL,null,null,"edit");
</script>

レジストリ

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\ソースの編集]
@="C:\\どこか\\EditSource.htm"

2009年4月 7日 (火)

HTAで、シングルクリックとダブルクリックで排他的に分けて処理する。

シングルクリックとダブルクリックとで処理を分けようとしても、ダブルクリックすると、onclickとondblclickの両方がこの順序で発生するので、不可分です。

例えば、

<script language=vbscript>
sub document_onclick()
document.body.innerText=document.body.innerText & vbCrLf & window.event.type
end sub
sub document_ondblclick()
document.body.innerText=document.body.innerText & vbCrLf & window.event.type
end sub
</script>

では、シングルクリックでonclickだけが動き、ダブルクリックでonclickとondblclickの両方が動きます。

では、どうすれば、シングルクリックでonclickだけを動かし、ダブルクリックでondblclickだけを動かすことができるのでしょうか?

もし、ondblclick、onclickの順序で発生するのであれば、onclickでondblclickが先に発生しているか調べることができます。

<script language=vbscript>
dim type1
sub document_onclick()
type1=window.event.type
'settimeout "sub1",createobject("wscript.shell").regread("HKCU\Control Panel\Mouse\DoubleClickSpeed")
settimeout "sub1",createobject("shell.application").GetSystemInformation("DoubleClickTime")
end sub
sub document_ondblclick()
type1=window.event.type
end sub
sub sub1()
document.body.innerText=document.body.innerText & vbCrLf & type1
end sub
</script>

ダブルクリック時間は外部オブジェクトを使わないと取れない?
HTAなら使えますが、IEではセキュリティの制約で難しいかも。

2009年4月 5日 (日)

WSFファイルをCScript.exeで実行するための拡張子(.wsfc)を追加する。

VBSファイルやJSファイルは、//Eオプションを使って、拡張子の追加変更が可能ですが、WSFファイルの場合は.wsf固定です。

しかし、バッチファイルの場合と異なり、WSFファイルの場合は、短いファイル名の拡張子がWSFであればよいようです。

例えば、長いファイル名の hoge.wsfc の短いファイル名が HOGE~1.WSF なら、

cscript HOGE~1.WSF

で実行できます。

ならば、長いファイル名の拡張子(.wsfc)を作って、関連付けを、

cmd.exe /q /c for %%I in ("%1") do for %%J in ("%~dpsI%~nxI") do cscript.exe %~sJ %*

とすればよいのです。

[HKEY_CLASSES_ROOT\.wsfc\shell\open\command]
@="cmd.exe /q /c for %%I in (\"%1\") do for %%J in (\"%~dpsI%~nxI\") do cscript.exe %~sJ %*"

2009年4月 2日 (木)

常駐のWSHスクリプトを外部からの指示で終了する。(その3)

以下のスクリプトを実行して、出てきたIEのページで「終了」ボタンを押すと、スクリプトもIEも終了します。

Set ie=CreateObject("InternetExplorer.Application")
ie.Visible=True
ie.Navigate "about:blank"
Do While ie.Busy Or ie.ReadyState<>4
  WScript.Sleep 100
Loop
ie.document.body.innerHTML="<button id=button1>終了</button>"
Set ie.document.all.button1.onclick=GetRef("sub1")
MsgBox "waiting..."

Sub sub1()
ie.quit
WScript.Timeout=1
End Sub

2009年4月 1日 (水)

常駐のWSHスクリプトを外部からの指示で終了する。(その2)

htmlfileのwindow.setInterval()を利用して指示をポーリングする方法です。
ここではファイルを削除すると終了指示になります。

以下のスクリプトを実行して、作成されたスクリプトファイル名.Runというファイルを削除すると、スクリプトが終了します。

Set fso=CreateObject("Scripting.FileSystemObject")
fso.CreateTextFile(WScript.ScriptFullName & ".Run")
Set doc=CreateObject("htmlfile")
doc.parentWindow.setInterval GetRef("proc"),1000
MsgBox "waiting..."

Sub proc()
If Not fso.FileExists(WScript.ScriptFullName & ".Run") Then WScript.Timeout=1
End Sub

2009年3月30日 (月)

常駐のWSHスクリプトを外部からの指示で終了する。

常駐のWSHスクリプトを、強制終了は避けて、なおかつ外部からの指示で終了するには、WScript.Timeoutを操作すればよいのです。
例えば、WScitptオブジェクトの参照を外部に渡すのも、ひとつの方法です。
その他に、IEなど、アウトプロセスサーバのイベント出口を使う方法もあります。

例えば、以下のスクリプトを実行して、出てきたIEを閉じると、スクリプトも終了します。

Set ie=WScript.CreateObject("InternetExplorer.Application","IE_")
ie.Visible=True
MsgBox "waiting..."

Sub IE_OnQuit()
WScript.Timeout=1
End Sub

2009年3月27日 (金)

WSHスクリプトの強制終了は避けましょう。

Excelなど、アウトプロセスサーバのオブジェクトを参照するWSHスクリプトを強制終了すると、参照カウンタが残って、サーバが終了できなくなったりします。

APIのTerminateProcess()
タスクマネジャの「プロセスの終了」
タスクスケジューラの「タスクの終了」
TaskKill.exe
CScriptのCTRL+C
WScript.ShellのExec().Terminate()
などの強制終了は避けられるなら避けたほうがよいでしょう。

一方、WScript.Timeoutによる終了ではオブジェクト参照が解放されます。

万一の暴走やハングアップの対策に、WScript.Timeoutを設定しておくとよいでしょう。
スクリプトのソース内で、WScript.Timeout=秒
コマンドラインのオプションで、//T:秒
ショートカットファイル(.WSH)で、Timeout=秒

2009年3月19日 (木)

WSHから他のWSHを操作する。(その3)

VBScriptもJScriptもCOMオブジェクトです。
なので、それぞれのオブジェクトを他方に渡せば、他方のオブジェクトを操作したり、関数を呼び出したりもできます。

では、VBScriptからJScriptへ、JScriptからVBScriptへ、それぞれの関数のオブジェクト参照を渡して、互いに呼び出してみましょう。

コマンドプロンプトを二つ開いて、一方で、

cscript f1.vbs

CreateObject("Shell.Application").Windows().Item(0).PutProperty "VBS",GetRef("Sub1")
Dim fWait
fWait=True
Do While fWait
  WScript.Sleep 100
Loop
Set JS=CreateObject("Shell.Application").Windows().Item(0).GetProperty("JS")
JS "なんだよ"

Sub Sub1(s)
WScript.Echo s
fWait=False
End Sub

他方で、

cscript f2.js

WScript.CreateObject("Shell.Application").Windows().Item(0).PutProperty("JS",func1);
var VBS=WScript.CreateObject("Shell.Application").Windows().Item(0).GetProperty("VBS");
VBS("おい");
WScript.Sleep(10000);

function func1(s){
WScript.Echo(s);
WScript.Timeout=1;
WScript.Quit(1);
}

関数はオブジェクトのデフォルトメソッドになってます。

2009年3月16日 (月)

WSHから他のWSHを操作する。(その2)

WSH間で会話も可能です。

グローバルオブジェクトもCOMオブジェクト(VBScriptTypeInfo)です。
先の例ではWScriptオブジェクトを渡していましたが、ここで、もしグローバルオブジェクトを渡せば、他方のグローバル変数を操作したり、関数を呼び出したりができます。

では、グローバルオブジェクト(グローバルコンテキストでのMe)を渡して、グローバル変数を操作してみましょう。

コマンドプロンプトを二つ開いて、一方で、

cscript g1.vbs

CreateObject("Shell.Application").Windows().Item(0).PutProperty "WSH",Me
Dim fWait
Dim WSH2
fWait=True
Do While fWait
  WScript.Sleep 100
Loop
WSH2.Echo "なんだよ"
WSH2.Quit 9

他方で、

cscript g2.vbs

Set Me1=CreateObject("Shell.Application").Windows().Item(0).GetProperty("WSH")
Me1.WScript.Echo "おい"
Set Me1.WSH2=WScript
Me1.fWait=False
WScript.Sleep 10000

グローバル変数や関数はグローバルオブジェクトのプロパティとメソッドになってます。

2009年3月12日 (木)

WSHから他のWSHを操作する。

WScriptオブジェクトはCOMオブジェクト(IHost_Class)です。
なので、スクリプト1からそのWScriptオブジェクトの参照をスクリプト2へ渡せば、スクリプト2からスクリプト1のWScript.Echoを呼び出したり、終了させたりできます。

では、実験してみましょう。

コマンドプロンプトを二つ開いて、一方で、

cscript s1.vbs

CreateObject("Shell.Application").Windows().Item(0).PutProperty "WSH",WScript
MsgBox "Waiting..."

他方で、

cscript s2.vbs

Set WSH1=CreateObject("Shell.Application").Windows().Item(0).GetProperty("WSH")
WSH1.Echo "Stopping..."
WSH1.Timeout=1

2009年3月 8日 (日)

FOR /Fコマンドでファイル更新するときの出力リダイレクト

FOR /Fコマンドでは、ファイルを読み込んで、同じファイルに書き出すことができます。

for /f %i in (file) do echo %i >>file

とすれば、元のファイルの後ろに追加書きされます。

上書きするには、最初のdoコマンドでファイルを空にします。

set n=
for /f %i in (file) do (
  if not defined n type nul >file
  set /a n+=1
  echo %i >>file
)

2009年3月 7日 (土)

FORコマンドの出力リダイレクト

FORコマンドの出力をファイルにリダイレクトするとき、

for %i in (a b) do echo %i >file

と書くと、doコマンドの繰り返しごとにリダイレクトされます。
この場合、毎回、上書きになってファイルには最後の1行しか入りません。

for %i in (a b) do echo %i >>file

と書くと、追加書きになって、すべて入ります。
しかし、最初からfileが空でないと、その追加書きになります。

もし上書きにしたいときは、最初に空にします。

type nul >file
for %i in (a b) do echo %i >>file

あるいは、forコマンド全体をリダイレクトします。

(for %i in (a b) do echo %i) >file

ただし、このとき、doコマンドのコマンドエコーも一緒にリダイレクトされるので、注意が必要です。

echo off
(for %i in (a b) do echo %i) >file

または、

echo on
(for %i in (a b) do @echo %i) >file

あるいは、

(for %i in (a b) do echo %i >&4) 4>file

とすれば、doコマンドのコマンドエコーはリダイレクトされません。

2009年3月 6日 (金)

ファイル名に!を使うと、環境変数の遅延展開と相性が悪い。

もし、ファイル名に!があると、バッチ変数(%1など)やFOR変数(%%iなど)の展開でファイル名中の!が遅延展開されちゃいます。
これは、"~"では、エスケープできません。

これを回避するには、バッチ変数やFOR変数の展開時に遅延展開を無効にすることです。
しかし、遅延展開はSETLOCAL抜きに切り替えができません。
しかも、SETLOCALのネストには上限もあります。

また、バッチファイルの遅延展開の初期状態は呼び出し元に依存します。
なので、バッチファイルの先頭に、SETLOCAL DISABLEDELAYEDEXPANSION を必ず入れておくことを推奨します。

2009年2月23日 (月)

ファイル名に%を使うと、バッチファイルにドロップしたとき、環境変数に展開される。

もし、ファイル名に%cd%や%temp%などの環境変数似の文字列があると、バッチファイルにドロップしたときに、環境変数に展開されちゃいます。
これは、"~"では、エスケープできません。

これを回避するには、バッチ変数(%1など)を使わないことです。
じゃ、どうやってファイル名を受け取るのかって?
バッチ変数の代わりにCMDCMDLINEを参照します。

SET ARG=%CMDCMDLINE:*" =%
SET ARG=%ARG:~0,-1%
ECHO;%ARG%
PAUSE

あるいは、CMDCMDLINEからバッチ変数に代入し直します。

SETLOCAL ENABLEDELAYEDEXPANSION
SET ARG=!CMDCMDLINE:*" =!
SET ARG=!ARG:~0,-1!
CALL :MAIN !ARG:%%=%%%%!
GOTO :EOF
:MAIN
REM 元のバッチファイルをここに置く
ECHO;%*
PAUSE

2009年2月19日 (木)

ファイル名に&を使うときは、半角空白も併せて使うべし。(その2)

複数ファイルのドロップにも対応するには、CMDCMDLINEをパーズします。

@echo off
setlocal enabledelayedexpansion
set cmdline=!cmdcmdline:*" =!
set cmdline=!cmdcmdline:~0,-1!
set fq=
set fg=
set fb=true
set cmdline2=
set n=0
if not defined cmdline goto :next
:for
  set c=!cmdline:~%n%,1!
  if not defined c goto :next
  if defined fq (
    if !c!==^" set fq=& if defined fg set cmdline2=!cmdline2!^"
  ) else (
    if !c!==^  if not defined fb (
      set fb=true
      if defined fg (
        set fg=
        set cmdline2=!cmdline2!^"
      )
    )
    if not !c!==^  if defined fb (
      set fb=
      if not !c!==^" (
        set fg=true
        set cmdline2=!cmdline2!^"
      )
    )
    if !c!==^" set fq=true& if defined fg set cmdline2=!cmdline2!^"
  )
  set cmdline2=!cmdline2!!c!
  set /a n+=1
  goto :for
:next
if defined fq set cmdline2=!cmdline2!^"& if defined fg set cmdline2=!cmdline2!^"
if defined fg set cmdline2=!cmdline2!^"
call :main !cmdline2!
exit
:main
rem 元のバッチファイルをここに置く
echo;%*
pause

2009年2月18日 (水)

ファイル名に&を使うときは、半角空白も併せて使うべし。

もし、ファイル名に&を使っていて、パス名に半角空白がないと、バッチファイルにドロップしたときに、&以降がコマンドとして実行されちゃいます。桑原桑原
もし、パス名に半角空白があれば、"~"でエスケープされるので、安全です。

バッチ側でこの問題を回避するには、CMDCMDLINEを使います。

1ファイルだけなら、手抜きですが、以下を試してみてください。

SETLOCAL ENABLEDELAYEDEXPANSION
SET ARG=!CMDCMDLINE:*" =!
SET ARG="!ARG:"=!"
ECHO;!ARG!
PAUSE
EXIT

2009年2月11日 (水)

ファイル名に=;,や全角空白を含むファイルをバッチファイルにドロップすると、ファイル名が分割される。(その4)

バッチファイル側、バッチファイル機能だけで対処します。

@echo off
setlocal enabledelayedexpansion
set cmdline=%*
set fq=
set fg=
set fb=true
set cmdline2=
set n=0
if not defined cmdline goto :next
:for
  set c=!cmdline:~%n%,1!
  if not defined c goto :next
  if defined fq (
    if !c!==^" set fq=& if defined fg set cmdline2=!cmdline2!^"
  ) else (
    if !c!==^  if not defined fb (
      set fb=true
      if defined fg (
        set fg=
        set cmdline2=!cmdline2!^"
      )
    )
    if not !c!==^  if defined fb (
      set fb=
      if not !c!==^" (
        set fg=true
        set cmdline2=!cmdline2!^"
      )
    )
    if !c!==^" set fq=true& if defined fg set cmdline2=!cmdline2!^"
  )
  set cmdline2=!cmdline2!!c!
  set /a n+=1
  goto :for
:next
if defined fq set cmdline2=!cmdline2!^"& if defined fg set cmdline2=!cmdline2!^"
if defined fg set cmdline2=!cmdline2!^"
call :main !cmdline2!
goto :eof
:main
rem 元のバッチファイルをここに置く
echo;%*

2009年2月 5日 (木)

IF ERRORLEVELの零判定

ERRORLEVELが負数のときには、
IF ERRORLEVEL 1 ECHO 正
IF NOT ERRORLEVEL 1 ECHO 零または負
なので、零判定、非零判定に使えまへん。

ほな、どないしまひょ。零判定は2段重ねかな。

IF ERRORLEVEL 0 IF NOT ERRORLEVEL 1 ECHO 零
IF NOT ERRORLEVEL 1 IF ERRORLEVEL 0 ECHO 零

一方、非零判定は、正と負の二つに分かれてしまいます。

IF ERRORLEVEL 1 (ECHO 正) ELSE IF ERRORLEVEL 0 (ECHO 零) ELSE ECHO 負

ERRORLEVELを絶対値に変換すれば楽かも。

IF NOT ERRORLEVEL 0 CMD/CEXIT %ERRORLEVEL:~1%
IF ERRORLEVEL 1 ECHO 非零
IF NOT ERRORLEVEL 1 ECHO 零

IF ERRORLEVELを使わない零判定なんてのもあります。

CMD/CEXIT %ERRORLEVEL% && ECHO 零 || ECHO 負

2009年1月28日 (水)

コマンドプロンプトやバッチファイルで使えるNOPコマンドは?

CMD.EXEの内部コマンドの
CALL
TITLE
が、引数なしで、NOPになります。

例えば、空のファイルを作成するには、
CALL > 空のファイル
TITLE > 空のファイル

また、
IF NOT 条件 コマンド
は、
IF 条件 (CALL) ELSE コマンド
IF 条件 (TITLE) ELSE コマンド
と書けます。

CALLはERRORLEVELを設定しますが、TITLEは変えません。
「CALL」 ERRORLEVEL=1
「CALL 」ERRORLEVEL=0
空白があるかどうかでERRORLEVELが変わります。

2009年1月26日 (月)

バッチファイルでコンソールウィンドウのタイトルを取得する。

TITLEコマンドでコンソールウィンドウのタイトルを変更することはできても、元のタイトルを調べることはできません。:-(
そこで、tasklist.exeを使ってコンソールウィンドウのタイトルを調べます。

GetTitle.CMD

@echo off
setlocal enabledelayedexpansion
set LF=^

set TASKLIST=
for /f "delims=" %%i in ('tasklist /v /fo csv /nh') do set TASKLIST=!TASKLIST!!LF!%%i
for /f "tokens=2 delims=," %%i in ('title @@@^&tasklist /v /fo csv /nh /fi "windowtitle eq @@@"') do set pid=%%i
for /f "delims=" %%i in ("!TASKLIST!") do call :sub %%i
goto :eof
:sub
if %2==%PID% echo %9
goto :eof

2009年1月25日 (日)

バッチファイルでプロセスIDを取得する。

tasklist.exeを使って、コンソールウィンドウの所有プロセスのプロセスIDを調べます。

GetPID.CMD

@echo off
for /f "tokens=2 delims=," %%i in ('title @@@^&tasklist /v /fo csv /nh /fi "windowtitle eq @@@"') do echo %%~i

厳密には、コンソールウィンドウの所有プロセスは、そのバッチファイルを実行中のシェルプロセスとは限りません。その親や御先祖かも知れません。

コンソールウィンドウのタイトルを一時的に変えて、そのタイトルのウィンドウの所有プロセスのプロセスIDを調べます。
サブシェルでタイトルを変えた場合は、元のタイトルに戻ります。

2009年1月18日 (日)

バッチファイルで、dllのアイコンをインデックスで一覧表示する。

例えば、shell32.dllのアイコンをインデックスで一覧表示するには、空のフォルダで、以下のバッチファイルを実行します。

hoge.cmd

for /l %%n in (-338,1,237) do (
echo [InternetShortcut]
echo IconFile=C:\WINDOWS\system32\shell32.dll
echo IconIndex=%%n
) > shell32.%%n.url

2009年1月13日 (火)

ホットキーが設定されてるショートカットを表示する。

ホットキーが有効なのは、デスクトップとスタートメニューにあるショートカットなので、そこを探します。

Hotkeys.vbs

Option Explicit
Dim fso
Dim wShell
Dim Shell

Set fso=CreateObject("Scripting.FileSystemObject")
Set wShell=CreateObject("WScript.Shell")
Set Shell=CreateObject("Shell.Application")

Files fso.GetFolder(wShell.SpecialFolders.Item("Desktop"))
Files fso.GetFolder(wShell.SpecialFolders.Item("AllUsersDesktop"))
SubFolders fso.GetFolder(wShell.SpecialFolders.Item("StartMenu"))
SubFolders fso.GetFolder(wShell.SpecialFolders.Item("AllUsersStartMenu"))

Sub Files(Folder)
Dim File
Dim Link
Dim FolderItem

For Each File In Folder.Files
  Select Case fso.GetExtensionName(File.Name)
  Case "lnk"
    Set Link=wShell.CreateShortcut(File.Path)
    If Link.HotKey<>"" Then WScript.Echo Join(Array(_
      "FullName" & vbTab & Link.FullName,_
      "TargetPath" & vbTab & Link.TargetPath,_
      "Arguments" & vbTab & Link.Arguments,_
      "WorkingDirectory" & vbTab & Link.WorkingDirectory,_
      "WindowStyle" & vbTab & Link.WindowStyle,_
      "Hotkey" & vbTab & Link.Hotkey,_
      "IconLocation" & vbTab & Link.IconLocation,_
      "Description" & vbTab & Link.Description),vbLf)
  Case "url"
    Set FolderItem=Shell.NameSpace(Folder.Path).Items().Item(File.Name)
    Set Link=FolderItem.GetLink
    WScript.Echo Join(Array(_
      "FullName" & vbTab & FolderItem.Path,_
      "Path" & vbTab & Link.Path,_
      "WorkingDirectory" & vbTab & Link.WorkingDirectory,_
      "ShowCommand" & vbTab & Link.ShowCommand,_
      "Hotkey" & vbTab & Link.Hotkey,_
      "IconLocation" & vbTab & GetIconLocation(FolderItem.Path),_
      "IconIndex" & vbTab & Link.GetIconLocation(""),_
      "Description" & vbTab & Link.Description),vbLf)
  End Select
Next
End Sub

Sub SubFolders(Folder)
Dim SubFolder
Files Folder
For Each SubFolder In Folder.SubFolders
  Files SubFolder
Next
End Sub

Function GetIconLocation(Path)
Dim File
Dim Line
Set File=fso.OpenTextFile(Path)
Do While Not File.AtEndOfStream
  Line=Trim(File.ReadLine)
  If InStr(UCase(Line),"ICONFILE=") Then
    GetIconLocation=Trim(Mid(Line,10))
    Exit Function
  End If
Loop
End Function

2009年1月 3日 (土)

cmd.exeでUnicode

バッチファイルはUnicode系にできませんが、CMD.EXEでUnicodeが使えない訳ではありません。

ファイルからUnicode系のコードページで読み込めないだけで、引数、環境変数、ファイル名からUnicode文字を取り込むことはできます。

例えば、ショートカットのリンク先や「ファイル名を指定して実行」に、

cmd /u /c echo 야후 >야후.txt

と入れて、実行できます。

あるいは、
---
CreateObject("WScript.Shell").Run "cmd /u /c echo 야후 >야후.txt"
---
というVBSファイルをUnicodeで保存して、実行できます。

2008年12月29日 (月)

バッチファイルの文字コードをEBCDIC-Katakanaにしてみる。

これでバッチファイルのコードが素人目には隠蔽できるかも。

HOGE.CMD
---
chcp 20290
fdiw@≠ヤ%
---

バッチファイル(cmd.exeとchcp.com)でEBCDIC-Katakanaの変換

全角文字が使えないので、あまり使い道があると思えませんが、一応は可能です。

EBCDIC-Katakana.cmd Unicodeファイル EBCDIC-Katakanaファイル

start /min /wait cmd /c chcp.com 20290 ^& cmd /c type %1 ^>%2

xEBCDIC-Katakana.cmd EBCDIC-Katakanaファイル Unicodeファイル

start /min /wait cmd /c chcp.com 20290 ^& ^( set /p x=""^<nul ^& cmd /u /c type %1 ^) ^>%2

2008年12月26日 (金)

バッチファイルの文字コードをEUC-JPにしてみる。

あまり実用的とは思えませんが、どうしても、EUC-JPでバッチファイルを書いて実行してみたい、という物好きな人は、

まず、以下のようなサンプルバッチファイルを作ります。

HOGE.CMD
---
echo 、「、、、ヲ、ィ、ェ
、「、、、ヲ、ィ、ェ.cmd
---

あいうえお.cmd
---
echo abc
---

最初に、コンソールのプロパティでフォントをMSゴシックに変えます。
必ず、その後で、コードページをEUC-JPに変えます。

chcp 20932

サンプルバッチファイルを実行します。

HOGE.CMD

必要なら、コードページをシフトJISに戻します。

chcp 932

2008年12月25日 (木)

バッチファイルの文字コードは?

シフトJIS(932) だけ? いいえ、そんなことはありません。例えば、EUC-JP(20932) も可能です。しかし、Unicode(1200)、UTF-7(65000)、UTF-8(65001)、JIS(50220) はダメです。
なので、実際上?は、シフトJIS(932) だけのようなものです。

これらコードページのバッチファイル可否は、%SystemRoot%System32\C_コードページ.NLS の有無によるようです。

もし、コンソールのコードページを UTF-7(65000)、UTF-8(65001)、JIS(50220)に変えると、バッチファイルが全く実行できなくなります。

また、これらのコードページでは、単にバッチファイルが実行できなくなるだけでなく、TYPE コマンドを除いて、ファイルの読み書きが全くできなくなるようです。

これはたぶん、ファイルの読み書きでは、C_コードページ.NLS を使用した文字コード変換が行なわれているせいなのでしょう。

逆に、UTF-7(65000)、UTF-8(65001)、JIS(50220) の C_コードページ.NLS を作ってやれば、それらのバッチファイルも可能になるかも知れません。

2008年12月23日 (火)

cmd.exeとchcp.comだけで、文字コード(Unicode、UTF-8、UTF-7、JIS、EUC-JP、SJIS)を変換する!

Unicode、UTF-8、UTF-7、JIS、EUC-JP、SJISなどの文字コードがcmd.exeとchcp.comだけで変換できます。

UTF-7.cmd Unicodeファイル UTF-7ファイル

start /min /wait cmd /c chcp.com 65000 ^& cmd /c type %1 ^>%2

UTF-8.cmd Unicodeファイル UTF-8ファイル

start /min /wait cmd /c chcp.com 65001 ^& cmd /c type %1 ^>%2

JIS.cmd Unicodeファイル JISファイル

start /min /wait cmd /c chcp.com 50220 ^& cmd /c type %1 ^>%2

EUC-JP.cmd Unicodeファイル EUC-JPファイル

start /min /wait cmd /c chcp.com 20932 ^& cmd /c type %1 ^>%2

SJIS.cmd Unicodeファイル SJISファイル

type %1 >%2

xUTF7.cmd UTF-7ファイル Unicodeファイル

start /min /wait cmd /c chcp.com 65000 ^& ^( set /p x=""^<nul ^& cmd /u /c type %1 ^) ^>%2

xUTF8.cmd UTF-8ファイル Unicodeファイル

start /min /wait cmd /c chcp.com 65001 ^& ^( set /p x=""^<nul ^& cmd /u /c type %1 ^) ^>%2

xJIS.cmd JISファイル Unicodeファイル

start /min /wait cmd /c chcp.com 50220 ^& ^( set /p x=""^<nul ^& cmd /u /c type %1 ^) ^>%2

xEUC-JP.cmd EUC-JPファイル Unicodeファイル

start /min /wait cmd /c chcp.com 20932 ^& ^( set /p x=""^<nul ^& cmd /u /c type %1 ^) ^>%2

xSJIS.cmd SJISファイル Unicodeファイル

( set /p x=""<nul & cmd /u /c type %1 ) >%2

2008年12月22日 (月)

ショートカットのホットキーで多重に起動する。

ショートカットのホットキーは多重に起動できません。
しかし、インターネットショートカットのホットキーは多重に起動できます。
なので、多重に起動したいときは、ショートカットをインターネットショートカットに変えるとよいでしょう。

どうも、ショートカットのホットキーは、終了同期しているようです。

そこで、ショートカットのリンク先の先頭に、
rundll32 shell32,ShellExec_RunDLL 元のリンク先
を付けてもよいでしょう。

rundll32は元のリンク先を起動してすぐに終了するみたいなので。

2008年12月19日 (金)

バッチファイルで、カレントディレクトリを長いファイル名から短いファイル名に変更する。

CD2SFN.CMD

@ECHO OFF
FOR %%0 IN ("%CD%") DO FOR %%1 IN ("%%~dps0%%~nx0") DO CD %%~s1

2008年12月18日 (木)

バッチファイルで、カレントディレクトリを短いファイル名から長いファイル名に変更する。

CD2LFN.CMD

@ECHO OFF
SETLOCAL
CALL :GetLongFileName "%CD%"
(
ENDLOCAL
CD %LFN%
)
GOTO :EOF
:GetLongFileName
IF "%~p1"=="\" (SET LFN=%~d1) ELSE CALL :GetLongFileName "%~dp1."
FOR /F "delims=" %%1 IN ('ATTRIB "%LFN%\%~nx1"') DO SET LFN=%%1
SET LFN=%LFN:~11%

2008年12月17日 (水)

バッチファイルで、長いファイル名から短いファイル名に変換する。

GetShortFileName.CMD 長いファイル/パス名

@ECHO OFF
IF NOT EXIST "%~1" (
ECHO ファイルが見つかりません - %1
GOTO :EOF
)
FOR %%1 IN ("%~dps1%~nx1") DO ECHO %%~s1

2008年12月16日 (火)

バッチファイルで、短いファイル名から長いファイル名に変換する。

GetLongFileName.CMD 短いファイル/パス名

@ECHO OFF
SETLOCAL
IF NOT EXIST "%~1" (
ECHO ファイルが見つかりません - %1
GOTO :EOF
)
CALL :GetLongFileName %1
ECHO %LFN%
GOTO :EOF
:GetLongFileName
IF "%~p1"=="\" (SET LFN=%~d1) ELSE CALL :GetLongFileName "%~dp1."
FOR /F "delims=" %%1 IN ('ATTRIB "%LFN%\%~nx1"') DO SET LFN=%%1
SET LFN=%LFN:~11%

2008年12月11日 (木)

Windows XPでも「パスとしてコピー」(その2)

実用性は?ですが、原理が単純で、面白いので一度お試しあれ。:-)

[HKEY_CLASSES_ROOT\AllFilesystemObjects\Shell\パス名リストを作成\Command]
@="cmd /c echo \"%1\">>パス名リスト.txt"

フォルダでファイルなどを選択した後に、右クリックメニューから「パス名リストを作成」を実行します。
コンソールウィンドウが繰り返し開くのはご愛嬌です。:-p
そのフォルダにパス名リストを含む「パス名リスト.txt」が作成されます。

2008年12月10日 (水)

Windows XPでも「パスとしてコピー」

Windows Vistaには「パスとしてコピー」がありますが、Windows 2000やWindows XPにはありません。

そこで、以下のショートカットを作成して、「リンク」フォルダに入れます。

パスとしてコピー.lnk

mshta.exe "javascript:var i=new ActiveXObject('Shell.Application').Windows().Item().Document.SelectedItems();var a=new Array();for(var k=0;k<i.Count;k++){a[k]='"'+i.Item(k).Path+'"';}clipboardData.setData('text',a.join('\r\n'));close();"

フォルダでファイルなどを選択した後に、「リンク」から「パスとしてコピー」を実行します。

SendToフォルダにスクリプトなどを入れて「送る」場合は、引数の最大長の制限で、多数のファイルは送れません
しかし、ここでのやり方は、引数を使わないので、無制限に多数のファイルのパスをコピーできます。

2008年12月 9日 (火)

Vistaではショートカットへのドロップが使えない!

KB938129 - Windows Vista で 複数のファイルをアプリケーションのショートカットにドラッグ アンド ドロップすると起動に失敗することがある
http://support.microsoft.com/kb/938129/ja

これは、MS-DOS時代のコマンドラインの引数長127バイトの悪夢の再来だ。

Windows 2000/XPでは、コマンドライン長が2047/8191文字に拡張されて、あまり気にすることなく使えた。

XPでは、ショートカットに書けるコマンドライン長は260文字に制限されたが、ドロップで追加される引数はその別枠で、すべてを含めて8191文字に制限される。

しかし、Vistaでは、ショートカットのコマンドライン長が、ドロップで追加される引数も含めて、260文字に制限された。

これじゃ、ショートカットへのドロップは、まともに使えない。2、3個で駄目になる。最悪1個でも駄目になる。

Vistaでは、ドロップを使うときは、ショートカットをやめて、バッチファイルやスクリプトにでもするのだろうか?

2008年12月 5日 (金)

バッチファイルで、文字化けファイル名を補正する。

ファイル名の中ににシフトJIS以外の文字があれば、_にリネームします。

yFileName.cmd ファイルセット...

@echo off
setlocal
for %%1 in (%*) do for /f "delims=" %%2 in ('echo %%1') do (
if not "%%~nx1"=="%%~nx2" (
echo %%1
echo %%2
set name="%%~nx2"
call rename "%%~1" %%name:?=_%%
)
)

2008年12月 3日 (水)

正規表現での改行文字の罠

Windows OSでのテキストファイルの改行文字は\r\nです。
一方、正規表現の改行文字は\nです。
なので、正規表現でテキストファイルを見ると、行末に\rがあります。
つまり、(.*)には、末尾に\rが付きます。また、行末hogeはhoge$に掛かりません。
このようなトラブルを回避するには、先に\rを除去しておくとよいでしょう。
あるいは、$の代わりに\r?$を使い、.の代わりに[^\r\n]を使うとよいでしょう。

2008年12月 2日 (火)

ひとつのショートカットで複数のアプリを起動するには?

一般には、バッチファイルやスクリプトを作って、そのショートカットを作ることが行われていますが、ショートカットだけで十分です。

「リンク先」に、

cmd.exe /c start "" "アプリ1" & start "" "アプリ2"

「実行時の大きさ」は「最小化」にしておきます。

2008年12月 1日 (月)

ファイルをドロップするとパス名や内容をクリップボードに送るショートカット

複数ファイルのパス名を改行区切りでクリップボードに送ります。

cmd.exe /c for /l %n in (1,1,2) do if %n==2 ((for %q in (%x%) do @echo %q)|MSHTA.EXE "javascript:clipboardData.setData('text',new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(0).ReadAll());close();") else set x=

複数ファイルのパス名を空白区切りでクリップボードに送ります。

cmd.exe /c for /l %n in (1,1,2) do if %n==2 (echo %x%|MSHTA.EXE "javascript:clipboardData.setData('text',new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(0).ReadAll());close();") else set x=
または、
cmd.exe /c for /l %n in (1,1,2) do if %n==2 (MSHTA.EXE "javascript:clipboardData.setData('text',new ActiveXObject('WScript.Shell').ExpandEnvironmentStrings('%x%'));close();") else set x=

テキストファイルの内容をクリップボードに送ります。

cmd.exe /c for /l %n in (1,1,2) do if %n==2 ((for %q in (%x%) do @type %q)|MSHTA.EXE "javascript:clipboardData.setData('text',new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(0).ReadAll());close();") else set x=

2008年11月27日 (木)

コマンドラインでショートカットを作成、変更、表示するバッチファイル

以前は、Shortcut.exeがありましたが、最近は、入手困難です。
そこで、コマンドラインで、ショートカット(.lnk)とインターネットショートカット(.url)を作成、変更、表示するバッチファイルです。

shortcut.cmd [/t:TargetPath] [/a:Arguments] [/w:WorkingDirectory] [/s:WindowStyle] [/k:HotKey] [/i:IconLocation] [/d:Description] name.{lnk|url}

@if(0)==(0) ECHO OFF
CScript.exe //NoLogo //E:JScript "%~f0" %*
GOTO :EOF
@end
var TargetPath;
var Arguments;
var WorkingDirectory;
var WindowStyle;
var HotKey;
var IconLocation;
var Description;
f1:for(var k=0;k<WScript.Arguments.Count();k++){
  switch(WScript.Arguments.Item(k).substr(0,3).toLowerCase()){
  case "/t:":
    TargetPath=WScript.Arguments.Item(k).substr(3);
    break;
  case "/a:":
    Arguments=WScript.Arguments.Item(k).substr(3);
    Arguments=Arguments.replace(/`/g,'"');
    break;
  case "/w:":
    WorkingDirectory=WScript.Arguments.Item(k).substr(3);
    break;
  case "/s:":
    WindowStyle=WScript.Arguments.Item(k).substr(3);
    WindowStyle=parseInt(WindowStyle);
    if(isNaN(WindowStyle)){
      WScript.Echo("Invalid Window Style - " + WScript.Arguments.Item(k));
      WScript.Quit();
    }
    break;
  case "/k:":
    HotKey=WScript.Arguments.Item(k).substr(3);
    break;
  case "/i:":
    IconLocation=WScript.Arguments.Item(k).substr(3);
    break;
  case "/d:":
    Description=WScript.Arguments.Item(k).substr(3);
    break;
  default:
    break f1;
  }
}
if(k==WScript.Arguments.Count()){
  WScript.Echo("Usage: Shortcut [/t:TargetPath] [/a:Arguments] [/w:WorkingDirectory] [/s:WindowStyle] [/k:HotKey] [/i:IconLocation] [/d:Description] name.{lnk|url}");
  WScript.Quit();
}
var FullName=WScript.Arguments.Item(k);
if(FullName.substr(FullName.length-4).toLowerCase()==".url"){
  var fso=new ActiveXObject("Scripting.FileSystemObject");
  var FullName=fso.GetAbsolutePathName(FullName);
  var Shell=new ActiveXObject("Shell.Application");
  if(!fso.FileExists(FullName)) fso.CreateTextFile(FullName);
  var FolderName=fso.GetParentFolderName(FullName);
  var FileName=fso.GetFileName(FullName);
  var Folder=Shell.NameSpace(FolderName);
  var FolderItem=Folder.Items().Item(FileName);
  var Link=FolderItem.GetLink;
  if(k){
    if(TargetPath!=undefined) Link.Path=TargetPath;
    if(WorkingDirectory!=undefined) Link.WorkingDirectory=WorkingDirectory;
    if(WindowStyle!=undefined) Link.ShowCommand=WindowStyle;
    if(HotKey!=undefined) Link.HotKey=HotKey;
    if(IconLocation!=undefined) Link.SetIconLocation(IconLocation.split(",")[0],parseInt(IconLocation.split(",")[1]));
    if(Description!=undefined) Link.Description=Description;
    Link.Save();
  }else{
    WScript.Echo(new Array(
    "FullName\t " + FolderItem.Path,
    "TargetPath\t " + Link.Path,
    "WorkingDirectory " + Link.WorkingDirectory,
    "ShowCommand\t " + Link.ShowCommand,
    "Hotkey\t\t " + Link.Hotkey,
    "IconLocation\t " + GetIconLocation() + "," + Link.GetIconLocation(""),
    "Description\t " + Link.Description).join("\n"));
  }
}else{
  var wShell=new ActiveXObject("WScript.Shell");
  var Link=wShell.CreateShortcut(FullName);
  if(k){
    if(TargetPath!=undefined) Link.TargetPath=TargetPath;
    if(Arguments!=undefined) Link.Arguments=Arguments;
    if(WorkingDirectory!=undefined) Link.WorkingDirectory=WorkingDirectory;
    if(WindowStyle!=undefined) Link.WindowStyle=WindowStyle;
    if(HotKey!=undefined) Link.HotKey=HotKey;
    if(IconLocation!=undefined) Link.IconLocation=IconLocation;
    if(Description!=undefined) Link.Description=Description;
    Link.Save();
  }else{
    WScript.Echo(new Array(
    "FullName\t " + Link.FullName,
    "TargetPath\t " + Link.TargetPath,
    "Arguments\t " + Link.Arguments,
    "WorkingDirectory " + Link.WorkingDirectory,
    "WindowStyle\t " + Link.WindowStyle,
    "Hotkey\t\t " + Link.Hotkey,
    "IconLocation\t " + Link.IconLocation,
    "Description\t " + Link.Description).join("\n"));
  }
}
function GetIconLocation(){
  var r=fso.OpenTextFile(FolderItem.Path).ReadAll().match(/IconFile=([^\r]*)/im);
  return r?r[1]:"";
}

2008年11月25日 (火)

WSHの引数で二重引用符 " をエスケープするには?(その2)

一般的なコンベンションでは、引数に"を渡すときは、引数を"で囲み、引数の中の"を""に変えて書きます。
"引""数"
アプリ側には、引数を囲む"は渡らず、引数の中の""が"に戻されて渡ります。
引"数

しかし、WSHでは、引数の"がすべて消えてしまいます。

そこで、もし、WSHで引数に"を渡したいときは、`で代用し、スクリプト側で戻します。
"引`数" → 引`数 → 引"数

ここで、もし、WSH(JScript)をバッチファイルでラップすると、一般的なコンベンションが使えます。
"引""数" → "引`数" → 引`数 → 引"数

@if(0)==(0) ECHO OFF
SETLOCAL
SET ARGS=
FOR %%1 IN (%*) DO (
SET "ARG=%%~1"
IF DEFINED ARG CALL SET "ARG=%%ARG:""=`%%"
CALL SET ARGS=%%ARGS%% "%%ARG%%"
)
CScript.exe //NoLogo //E:JScript "%~f0" %ARGS%
GOTO :EOF
@end
for(var k=0;k<WScript.Arguments.Count();k++){
  WScript.Echo(WScript.Arguments.Item(k).replace(/`/g,'"'));
}

VBScriptの場合は、別ファイルにします。

hoge.cmd
---
@ECHO OFF
SETLOCAL
SET ARGS=
FOR %%1 IN (%*) DO (
SET "ARG=%%~1"
IF DEFINED ARG CALL SET "ARG=%%ARG:""=`%%"
CALL SET ARGS=%%ARGS%% "%%ARG%%"
)
CScript.exe //NoLogo ".\hoge.vbs" %ARGS%
---

hoge.vbs
---
For Each Arg In WScript.Arguments
  WScript.Echo Replace(Arg,"`","""")
Next
---

2008年11月24日 (月)

CreateFolder() フォルダを再帰的に作成する関数

FileSystemObjectのCreateFolder()メソッドは、
指定したフォルダが既に存在していた場合は、エラーが発生します。
また、親フォルダが存在しない場合も、エラーが発生します。

そこで、これらの面倒を解消した代替関数を作ります。

Option Explicit
Dim fso
Set fso=CreateObject("Scripting.FileSystemObject")

Sub CreateFolder(ByVal Folder)
Dim ParentFolder
'WScript.Echo Folder
ParentFolder=fso.GetParentFolderName(Folder)
If ParentFolder<>"" Then If Not fso.FolderExists(ParentFolder) Then CreateFolder ParentFolder
If Not fso.FolderExists(Folder) Then fso.CreateFolder Folder
End Sub

CreateFolder "c:\a\b\c\d"
CreateFolder "x:\a\b\c\d"
CreateFolder "c:a"
CreateFolder "x:a"
CreateFolder "a\b"

再帰処理は、無限ループにならないよう、歯止めが必要です。
ここでは、If ParentFolder<>"" が歯止めになっています。
もし、この歯止めがないと、存在しないドライブ(x:)で無限ループします。
WScript.Echo Folder でログを取ると、無限ループの様子がよく分かります。

2008年11月20日 (木)

アプリやバッチファイルを非表示で起動するバッチファイル

もし、引数がなければ、バッチファイルだけで、アプリやバッチファイルを非表示で起動することができます。

HideRun.CMD ファイル

@echo off
if not exist "%~f1" (
echo not found - "%~f1"
goto :eof
)
> %temp%\.url echo;[InternetShortcut]
>>%temp%\.url echo;URL=%~f1
>>%temp%\.url echo;ShowCommand=0
%temp%\.url
del %temp%\.url

テンポラリのインターネットショートカットファイルを作って実行して削除します。

引数があるときは、引数付きのショートカットを作って、そのショートカットファイルを指定します。

2008年11月19日 (水)

インターネットショートカットで、アプリやバッチファイルを非表示で起動する。

スクリプトなどを使って、アプリやバッチファイルを非表示で起動する方法がよくありますが、もし、引数がなければ、インターネットショートカットだけでも可能です。

メモ帳などで以下のようなテキストファイルを作ります。

[InternetShortcut]
URL=アプリやバッチファイルのフルパス
ShowCommand=0

これを保存して、拡張子を.urlに変えます。

もし、引数があるときは、まず、引数付きのショートカットを作り、そのショートカットへのインターネットショートカットを作ればよいのです。

ショートカットへのショートカットは作れませんが、ショートカットへのインターネットショートカットは作れます。

また、ショートカットの「実行時の大きさ」は「非表示」が選択できませんが、インターネットショートカットの「実行時の大きさ」はメモ帳で書けます。

2008年11月18日 (火)

文字列リソースを取り出して見る。

レジストリなどに、
@%SystemRoot%\system32\SHELL32.dll,-22978
のような文字列があります。

これが具体的にどういう文字列に展開されるのか?

それをWin32APIを使わず、スクリプトだけで調べることができます。
それもWScript.Shellを使って。

[cscript] GetResource.VBS [@DLL,ID]

Option Explicit

Dim Resource
Dim Location

For Each Location In WScript.Arguments
  WScript.Echo GetResource(Location)
Next
If WScript.Arguments.Count Then WScript.Quit
Do
  Location=InputBox("Enter @DLL,ID.",WScript.ScriptName,Resource)
  If Location="" Then Exit Do
  Resource=GetResource(Location)
  If Resource="" Then Resource=Location
Loop

Function GetResource(Location)
Dim wShell
Dim Link
Set wShell=CreateObject("WScript.Shell")
Set Link=wShell.CreateShortCut(".LNK")
Link.Description=Location
GetResource=Link.Description
End Function

2008年11月14日 (金)

フォルダサイズを表示するバッチファイル

FolderSize.CMD フォルダ...(ワイルドカード可)

@echo off
setlocal
for /d %%0 in (%*) do (
for /f "tokens=1-3 delims= " %%1 in ('dir /s /a-d %%0') do if %%2==個のファイル set size=%%3
call echo;%%size%% %%0
)

2008年11月13日 (木)

「検索コンパニオン」を開く。

XPに「Windows Search」を入れると、「検索」で開くのは、「Windows Search」になって、「検索コンパニオン」は、一度「Windows Search」を開いて、そこから開くようになります。

では、「検索コンパニオン」を直接、開く方法はないものか?

「新規作成」の「テキストドキュメント」で空のファイルを作って、拡張子を.fnd にします。

というか、ファイル名を例えば、検索コンパニオン.fnd にします。

2008年11月12日 (水)

改行文字変換フィルタをバッチファイルで作る。(その4)

CR を CR+LF に変換します。

cr2dos.cmd <CR >CR+LF
---
@echo off
setlocal enabledelayedexpansion
set LF=^

for /f "delims=" %%1 in ('cmd /u /c echo;名') do (
set CR=%%1
set CR=!CR:~0,1!
)
setlocal disabledelayedexpansion
for /f "delims=" %%1 in ('find /n /v ""') do (
set LINE=%%1
setlocal enabledelayedexpansion
set LINE=!LINE:*]=!
if defined LINE (
cmd /v:on /c echo;%%LINE:!CR!=^^!CR^^!^^!LF^^!%%
) else (
echo;
)
endlocal
)
---

ここで、
for /f "tokens=1* delims=]" %%1 in ('find /n /v ""') do (
set LINE=%%2
としないのは、行頭の ] が消えるからです。

また、
set LINE=%%1
の行で遅延展開を無効にしているのは、もし有効だと、行中の ! が消えるからです。

2008年11月11日 (火)

環境変数の置換文字列に環境変数を使う。

%環境変数:文字列1=文字列2%
で「文字列1」や「文字列2」に環境変数を使うには、「文字列1」や「文字列2」を通常展開、全体を遅延展開します。

!環境変数:%文字列1%=%文字列2%!

もし、「文字列1」や「文字列2」を遅延展開する必要があるとき、「文字列2」は遅延展開できます。

%環境変数:文字列1=!文字列2!%

しかし、「文字列1」は遅延展開できません。

%環境変数:!文字列1!=文字列2% → NG

「文字列1」(と「文字列2」)を遅延展開するには、「文字列1」(と「文字列2」)を遅延展開、全体をCALL %%で遅延展開します。

CALL %%環境変数:!文字列1!=!文字列2!%%

ただし、この場合の「文字列2」の遅延展開は、1パス目に行われるので、2パス目の通常展開時に評価されます。

「文字列2」が2パス目の通常展開時に評価されないようにするには、全体をCMD %%で遅延展開、「文字列2」の遅延展開をエスケープして2パス目に遅延させます。

CMD /V:ON /C %%環境変数:!文字列1!=^^!文字列2^^!%%

2008年11月10日 (月)

環境変数の遅延展開を遅延展開する。

遅延展開有効での展開の順序は、
(1) %環境変数%
(2) %バッチ変数、%%FOR変数
(3) !環境変数!
(4) CALL %%環境変数%%
(5) CALL %%バッチ変数
(6) CMD /V:ON /C ^^!環境変数^^!

遅延展開有効での!のエスケープは^^!です。(1)と(3)で2回エスケープするため?

ただし、
CALL ECHO;^^!環境変数^^!
では、展開されません。

CMD /V:ON /C ECHO;^^!環境変数^^!
なら展開されます。

2008年11月 9日 (日)

改行文字変換フィルタをバッチファイルで作る。(その3)

CR+LF を CR に変換します。

dos2cr.cmd <CR+LF >CR
---
@echo off
setlocal enabledelayedexpansion
for /f "delims=" %%1 in ('cmd /u /c echo;名') do (
set CR=%%1
set CR=!CR:~0,1!
)
setlocal disabledelayedexpansion
for /f "delims=" %%1 in ('find /n /v ""') do (
set LINE=%%1
setlocal enabledelayedexpansion
<NUL set /p LINE=!LINE:*]=!!CR!
endlocal
)
---

2008年11月 7日 (金)

改行文字変換フィルタをバッチファイルで作る。(その2)

異常な改行文字(CR+CR+LF)を正常な CR+LF に変換します。

pingなどのunix由来のネットワーク系コマンドの改行文字は異常です。
CR+CR+LF になっています。
コンソール画面で見た目には同じですが、プログラムで処理するときに困ります。

CrCrLf.CMD <CR+CR+LF >CR+LF
---
@echo off
setlocal disabledelayedexpansion
for /f "delims=" %%1 in ('find /n /v ""') do (
set LINE=%%1
setlocal enabledelayedexpansion
call echo;!LINE:*]=!
endlocal
)
---

CRは、%環境変数%の展開時に消えます。

%%FOR変数や!環境変数!は、%環境変数%の後に展開されるので、
echo;%%FOR変数

echo;!環境変数!
では残ります。

しかし、CALL文では、展開が2回実行されるので、
call echo;%%FOR変数

call echo;!環境変数!
では、2回目で消えます。

2008年11月 6日 (木)

コマンドプロンプトやバッチファイルで、環境変数に改行文字(CR)を入れて使う。

環境変数に改行文字(CR)を入れる。

名 の unicode が 540D、これをシフトJISで読むと、0D 54 つまり、CR+T であることを利用して、CR を抜き出します。

setlocal enabledelayedexpansion
for /f "delims=" %%1 in ('cmd /u /c echo;名') do (
set CR=%%1
set CR=!CR:~0,1!
)

改行文字(CR)を使うときは、環境変数を遅延展開します。

echo aaa!CR!bbb

もし、環境変数を遅延展開しないと、改行文字(CR)は消えます。

echo aaa%CR%bbb

しかし、これはこれで、「改行文字(CR)を消す」ために利用できます。

2008年11月 5日 (水)

コマンドプロンプトやバッチファイルで、環境変数に制御文字を入れる。

元になるシフトJIS文字を選びます。

まず、候補の文字の一覧を出します。

cscript hoge.vbs > hoge.txt

For n=0 To 15
  For k=&H8140 To &Hea9e
    If k=Asc(Chr(k)) Then
      If (AscW(Chr(k)) And 255) = n Then
        WScript.StdOut.WriteLine Join(Array(Hex(k),Chr(k),Hex(AscW(Chr(k)))),vbTab)
      End If
    End If
  Next
Next

この中から、制御文字が cc なら、unicode が hhcc の文字を選びます。

例えば、CR(0D) なら 名(540D) などです。

環境変数に制御文字を入れる。

setlocal enabledelayedexpansion
for /f "delims=" %%1 in ('cmd /u /c echo;名') do (
set CR=%%1
set CR=!CR:~0,1!
)

名 の unicode が 540D、これをシフトJISで読むと、0D 54 つまり、CR+T であることを利用して、CR を抜き出します。

ただし、LF(0A)は、このやり方ではできません。

2008年11月 4日 (火)

改行文字変換フィルタをバッチファイルで作る。

テキストファイルの改行文字は、OSで異なります。
DOS/Windows CR+LF
Unix/Linux  LF
Mac/MainFrame  CR

変換フィルタは、SFUにあり、それだけコピーしても使えます。
dos2unix.exe
unix2dos.exe

SFUがなくても、バッチファイルで代替できます。

dos2unix.cmd <CR+LF >LF
---
@echo off
setlocal disabledelayedexpansion
set LF=^

for /f "delims=" %%1 in ('find /n /v ""') do (
set LINE=%%1
setlocal enabledelayedexpansion
<NUL set /p LINE=!LINE:*]=!!LF!
endlocal
)
---

unix2dos.cmd <LF >CR+LF
---
@find /v ""
---
または、
---
@echo off
setlocal disabledelayedexpansion
for /f "delims=" %%1 in ('find /n /v ""') do (
set LINE=%%1
setlocal enabledelayedexpansion
echo;!LINE:*]=!
endlocal
)
---

moreを使ってもできますが、その場合はタブが空白展開されます。

2008年11月 3日 (月)

FORコマンドで、行のフィールドを順次取り出す。

例えば、CSVのような行で、各フィールドを決め打ちで取り出すには、

FOR /F "tokens=1-3 delims=," %%1 IN ("aaa,bbb,,ccc") DO (
ECHO;"%%1"
ECHO;"%%2"
ECHO;"%%3"
)

ですが、フィールド数が可変で、各フィールドを順次取り出すには?

再帰的に先頭フィールドを取り出します。

SETLOCAL ENABLEDELAYEDEXPANSION
SET LINE=aaa,bbb,,ccc
:LOOP
FOR /F "tokens=1* delims=," %%1 IN ("!LINE!") DO (
ECHO;"%%1"
SET LINE=%%2
GOTO :LOOP
)

フィールド区切りを改行文字に変えて、複数フィールドを複数行にします。

SETLOCAL ENABLEDELAYEDEXPANSION
SET LF=^(改行)
(改行)
(改行)
SET LINE=aaa,bbb,,ccc
SET LINE=%LINE:,=!LF!%
FOR /F "delims=" %%1 IN ("!LINE!") DO (
ECHO;"%%1"
)

いずれのやり方も、連続する区切り文字や先行する区切り文字が無視されます。

そこで、ダミーのヘッダ文字を付けると、空のフィールドが取り出せます。

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET LF=^

SET LINE=aaa,bbb,,ccc
SET LINE=]%LINE:,=!LF!]%
FOR /F "delims=," %%1 IN ("!LINE!") DO (
SET LINE=%%1
ECHO;"!LINE:~1!"
)

2008年11月 2日 (日)

コマンドプロンプトやバッチファイルで、環境変数に改行文字(LF)を入れて使う。

環境変数に改行文字(LF)を入れる。

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

SET LF=^(改行)
More? (改行)
More? (改行)

バッチファイルでは、

SET LF=^(改行)
(改行)
(改行)

改行文字(LF)を使うときは、環境変数を遅延展開します。

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

CMD /V:ON
SET x=aaa!LF!bbb
ECHO !x!
EXIT

バッチファイルでは、

SETLOCAL ENABLEDELAYEDEXPANSION
SET x=aaa!LF!bbb
ECHO !x!

遅延展開しないと、改行されて、改行文字以降が消えます。

ECHO aaa%LF%bbb

また、FOR /F IN ("文字列")の中で改行文字(LF)を使うと、「複数行の文字列」になります。

FOR /F %%1 IN ("aaa!LF!bbb") DO ECHO;"%%1"

2008年11月 1日 (土)

ECHOなどの内部コマンドの区切りは、=;,を使うべし。:\./+[]は使わないこと。

ECHOの区切りに、:\./+[]を使う例がよくありますが、よくないようです。

echo.%username%

は通りますが、

setlocal enabledelayedexpansion
echo.!username:~1,2!

は通りません。

2008年10月31日 (金)

MHTMLファイルの名前の中の#!を全角に、シフトJIS以外の文字を{16進}に変える。

WSHのドロップハンドラが使えないので、VB.NETで作ります。

MhtFileName.exe ファイル...

vbc MhtFileName.VB

Imports System.IO

Public Class Class1
Public Shared Sub Main(Args() As String)
For Each Arg As String In Args
  Dim fi = New FileInfo(Arg)
  Dim xName As String = xString(fi.Name)
  If xName<>fi.Name Then fi.MoveTo(Arg + "\..\" + xName)
Next
End Sub

Private Shared Function xString(ByVal s As String) As String
Dim x As String = ""
For k As Integer = 1 To Len(s)
  Dim c = Mid(s, k, 1)
  If 31 < AscW(c) AndAlso AscW(c) < 128 Then
  ElseIf &Hff60 < AscW(c) And AscW(c) < &Hffa0 Then
  ElseIf Chr(Asc(c)) = c And Asc(c) < 0 Then
  Else
    c = Hex(AscW(c))
    c = "{" & New String("0"c, Len(c) Mod 2) & c & "}"
  End If
  x = x & c
Next
x = Replace(x, "#", "#")
x = Replace(x, "!", "!")
Return x
End Function
End Class

2008年10月30日 (木)

WSHのドロップハンドラは、ファイル名にシフトJIS以外の文字を通さない。(障害)

もし、ファイル名にシフトJIS以外の文字、例えば、®©などがあると、WSHのドロップハンドラは、その文字を通しません。

ちょうど、Chr(Asc(文字))したときのように、
®→R
©→c
似た文字がなければ→?

その結果、スクリプトからはドロップしたファイルが見つからないことになります。

一方、exeファイル用のドロップハンドラは、正しいようです。

ならば、WSHも、これで代替できればよいのでしょうが、WSHに使うとSFNになるので使えません。

まぁ、ファイル名には、シフトJIS内の文字を使うことです。

2008年10月29日 (水)

ファイル名にシフトJIS以外の文字があるか、バッチファイルで検出する。

デフォルトでは、ECHOのコンソール以外への出力は、シフトJISです。(cmd {/a|/u})

xFileName.cmd ファイルセット...

@echo off
for %%1 in (%*) do for /f "delims=" %%2 in ('echo %%1') do if not "%%~1"=="%%~2" echo %%1 & echo %%2

2008年10月28日 (火)

MHTMLファイルの名前にシフトJIS以外の文字を使うと、MHTML扱いされなくなる?

もし、MHTMLファイルの名前にシフトJIS以外の文字、例えば、®©などがあると、拡張子を.htmに変えて、IEで開いたときのように、ぐちゃぐちゃに見えます。(XP SP3 + IE7.0)
これは、たぶんセキュリティ対策(フィッシング詐欺)なんでしょう。
URLにユーザが視認できそうにない文字を含む場合は、MHTML扱いしないようにしている?

まぁ、MHTMLファイルの名前には、シフトJIS内の文字を使うことです。

もし、MHTMLファイルを開いて、ぐちゃぐちゃになったら、ファイル名を変えて開き直せばよいでしょう。

MHTML扱いされてるかどうかは、Webページのプロパティでも確認できます。
プロトコル: 不明なプロトコル
種類: 使用不可
のようになってたら、MHTML扱いされています。

もし、
プロトコル: File Protocol
種類: MHTML Document
のようになってたら、MHTML扱いされてません。

IEオブジェクトのLocationURLを見ても、区別できます。
mhtml:file://C:\~~~.mht
file://C:\~~~.mht

2008年10月27日 (月)

MHTMLファイルの名前に!を使うと、読み込めないことがある。

他にも、
「MHTMLファイルのファイル名に#があると、IEで開けない。(障害)」
http://scripting.cocolog-nifty.com/blog/2007/09/mhtmlie_8192.html
がありましたが、!もよくないようです。

!がmhtml:「MHTMLファイルへのURL」!「オリジナルの要素へのURL」というURLの構文記号であるせいだと思われます。

ファイル名に!があっても、ファイルに依って、読み込めるファイルもあれば、読み込み中のまま完了しないファイルもあります。
ちょっと試したところでは、ルートと構成要素が同じドメインだと駄目で、異なるとOKとか、Webページの構造に関係するようです。

まぁ、MHTMLファイルの名前には、!を使わないことです。!を削除するか、全角の!に変えるかですね。

2008年10月26日 (日)

ファイルのパス名をメモ帳に送るショートカット

以下のショートカットを作ります。
リンク先:cmd.exe /v:on /c for /l %n in (1,1,2) do if %n==2 ((for %q in (!x!) do @echo %q)>clip.txt&start notepad clip.txt) else set x=
作業フォルダ:作業ファイルclip.txtの置き場所。デスクトップなど。
ウィンドウの大きさ:最小化

これをSendToフォルダに置いて、(複数の)ファイルを送ると、メモ帳が開いてパス名のリストが得られます。

デスクトップに置いて、(複数の)ファイルをドロップしても同様です。

使っているのは、cmd.exeとメモ帳だけなので、どんな環境でも使えます。

2008年10月20日 (月)

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

簡単で確実なのは、属性をチェックすることです。

属性は、バッチ変数かFOR変数の%~a1で取り出せます。

drahs----

先頭のdをチェックします。

次のバッチファイルでは、環境変数の操作で先頭1文字を取り出します。

IsFolder.CMD フォルダかファイル

@ECHO OFF
SETLOCAL
SET A=%~a1
IF %A:~0,1%==d ECHO フォルダです。
IF %A:~0,1%==- ECHO ファイルです。

次のバッチファイルでは、FOR /F文のeol文字(行頭コメント文字)を利用します。

IsFolder.CMD フォルダかファイル

@ECHO OFF
FOR /F "eol=-" IN ("%~a1") DO ECHO フォルダです。
FOR /F "eol=d" IN ("%~a1") DO ECHO ファイルです。

また、バッチファイルの中では、

FOR %%1 IN ("フォルダかファイル") DO FOR /F "eol=-" IN ("%%~a1") DO ECHO フォルダです。

FOR %%1 IN ("フォルダかファイル") DO FOR /F "eol=d" IN ("%%~a1") DO ECHO ファイルです。

2008年10月19日 (日)

Where is where.exe ?

unix の which に相当するのは、Windows では whrere.exe です。

MS 純正の where.exe は、以下からダウンロードできます。

ftp://ftp.microsoft.com/reskit/y2kfix/x86/where.exe

which をバッチファイルで代替すると、

which.cmd ベース名.拡張子

@if exist "%~$PATH:1" echo;"%~$PATH:1"

この場合は、拡張子が省略できません。

拡張子を省略可にしたければ、環境変数のPATHEXTを使って、拡張子を補います。

which.cmd ベース名

@for %%1 in (%*) do @for %%2 in (.;%PATHEXT%) do @for %%3 in ("%%~1%%2") do @if exist "%%~$PATH:3" echo;"%%~$PATH:3"

GUIでは、
「新規作成」「ショートカット」
で、ベース名を与えれば、PATHで解決してくれます。
作成したショートカットのプロパティでリンク先を見ます。

WSHでも、同じ原理が利用できます。

which.vbs ベース名

Set wShell=CreateObject("WScript.Shell")
Set Link=wShell.CreateShortcut("hoge.lnk")
On Error Resume Next
Link.TargetPath=WScript.Arguments.Item(0)
On Error GoTo 0
WScript.Echo Link.TargetPath

2008年10月16日 (木)

「Windows 画像と Fax ビューア」でファイルを表示または印刷するショートカット

「Windows 画像と Fax ビューア」でファイルを表示するには、

rundll32.exe shimgvw.dll,ImageView_Fullscreen フルパス引用符なし

なので、普通には、ショートカットに書けません。

そこで、ワンライナテクを使って、

表示.lnk

cmd.exe /v:on /c for /l %n in (1,1,2) do if %n==2 (for %q in (!x!) do start rundll32.exe shimgvw.dll,ImageView_Fullscreen %~fq) else set x=

また、印刷するには、ファイル名の後にプリンタ名を指定する必要があり、

rundll32.exe shimgvw.dll,ImageView_PrintTo /pt フルパス引用符付き "プリンタ名"

なので、これも、普通には、ショートカットに書けません。そこで、

印刷.lnk

cmd.exe /v:on /c for /l %n in (1,1,2) do if %n==2 (for %q in (!x!) do start rundll32.exe shimgvw.dll,ImageView_PrintTo /pt "%~fq" "プリンタ名") else set x=

2008年10月 9日 (木)

全角ひらがな、全角カタカナの判別方法

全角ひらがな、全角カタカナを判別します。

Function [全角ひらがな](ByVal c)
c=AscW(c)
[全角ひらがな]=&H3041<=c And c<=&H3093
End Function

Function [全角カタカナ](ByVal c)
c=AscW(c)
[全角カタカナ]=&H30a1<=c And c<=&H30f6
End Function

2008年10月 8日 (水)

JIS非漢字、第一水準、第二水準の判別方法

JIS非漢字、第一水準、第二水準を判別します。

Function [非漢字](ByVal c)
[非漢字]=c=Chr(Asc(c))
If [非漢字] Then
  c=Asc(c)
  [非漢字]=&H8140<=c And c<=&H889e
End If
End Function

Function [第一水準](ByVal c)
[第一水準]=c=Chr(Asc(c))
If [第一水準] Then
  c=Asc(c)
  [第一水準]=&H889f<=c And c<=&H9872
End If
End Function

Function [第二水準](ByVal c)
[第二水準]=c=Chr(Asc(c))
If [第二水準] Then
  c=Asc(c)
  [第二水準]=&H989f<=c And c<=&Hea9e
End If
End Function

2008年10月 6日 (月)

ショートカットに書けるバッチワンライナーの作り方

バッチファイルにすると、アイコンやウィンドウサイズが変えられないし、ショートカットにするとバッチスクリプトが書けないし。。。

そこで、ショートカットに書けるバッチワンライナーの作り方です。

問題は、ドロップしたファイル名が行の末尾に付くのに、それらを行の前方のコマンドから扱えないことです。

そこで、for文とif else文で、行の前後の実行順序を逆転させます。

以下はドロップしたファイル名をechoする例です。

cmd.exe /v:on /k for /l %n in (1,1,2) do if %n==2 (for %q in (!x!) do echo %q) else set x=

次はclip.exeを利用してドロップしたファイル名をクリップボードにコピーします。

cmd.exe /c for /l %n in (1,1,2) do if %n==2 ((for %q in (%x%) do @echo %q)|clip) else set x=

2008年10月 5日 (日)

LeftA(文字列, バイト長)とMidA(文字列, 開始バイト位置, バイト長)関数を作る(VB.NET)

別のやり方で。

先にLeftA()を作り、MidA()はLeftA()を2回呼び出します。

Imports System.Text

Public Class Class1
Public Shared Sub Main()
MsgBox(MidA("abcdefg", 3, 2))
MsgBox(MidA("あいうefg", 3, 2))
MsgBox(MidA("あいうefg", 3, 3))
MsgBox(MidA("aいうefg", 3, 2))
MsgBox(MidA("aいうefg", 3, 3))
End Sub

Private Shared Function MidA(Str As String, bStart As Integer, bLength As Integer) As String
Str=LeftA(Str.Substring(LeftA(Str, bStart-1).Length), bLength)
Return Str
End Function

Private Shared Function LeftA(Str As String, bLength As Integer) As String
Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")
Dim bytes() As Byte = enc.GetBytes(Str)
If bytes.Length > bLength Then
  Str = Str.Substring(0, enc.GetString(bytes, 0, bLength).Length)
  If enc.GetByteCount(Str) > bLength Then Str = Str.Remove(Str.Length-1)
End If
Return Str
End Function
End Class

2008年10月 3日 (金)

MidA(文字列, 開始バイト位置, バイト長)関数を作る(VB.NET)

更に、VB.NETに焼き直し。

Public Class Class1
Public Shared Sub Main()
MsgBox(MidA("abcdefg", 3, 2))
MsgBox(MidA("あいうefg", 3, 2))
MsgBox(MidA("あいうefg", 3, 3))
MsgBox(MidA("aいうefg", 3, 2))
MsgBox(MidA("aいうefg", 3, 3))
End Sub

Private Shared Function MidA(Str As String, bStart As Integer, bLength As Integer) As String
Dim aStart As Integer = 0
Dim aLength As Integer = -1
Dim bPosition As Integer = 1
Dim aPosition As Integer
For aPosition = 1 To Len(Str)
  If aStart = 0 Then
    If bStart = bPosition Then
      aStart = aPosition
    ElseIf bStart < bPosition Then
      aStart = aPosition - 1
      bStart = bPosition - 2
    End If
  Else
    If bStart + bLength = bPosition Then
      aLength = aPosition - aStart
      Exit For
    ElseIf bStart + bLength < bPosition Then
      aLength = aPosition - aStart - 1
      bLength = bPosition - bStart - 2
      Exit For
    End If
  End If
  bPosition = bPosition + 1 - (Asc(Mid(Str, aPosition, 1)) < 0)
Next
If aStart Then
  If aLength = -1 Then
    aLength = aPosition - aStart
    bLength = bPosition - bStart
  End If
  MidA = Mid(Str, aStart, aLength)
End If
End Function
End Class

引数の位置と長さに、補正後の値を返してほしいときは、ByRefに変えてください。

2008年10月 2日 (木)

MidA(文字列, 開始バイト位置, バイト長)関数を作る(VBA)

VBAに焼き直し。

Sub a()
MsgBox MidA("abcdefg", 3, 2)
MsgBox MidA("あいうefg", 3, 2)
MsgBox MidA("あいうefg", 3, 3)
MsgBox MidA("aいうefg", 3, 2)
MsgBox MidA("aいうefg", 3, 3)
End Sub

Function MidA(Str As String, bStart As Long, bLength As Long) As String
Dim aStart As Long
Dim aLength As Long
Dim bPosition As Long
Dim aPosition As Long
aStart = 0
aLength = -1
bPosition = 1
For aPosition = 1 To Len(Str)
  If aStart = 0 Then
    If bStart = bPosition Then
      aStart = aPosition
    ElseIf bStart < bPosition Then
      aStart = aPosition - 1
      bStart = bPosition - 2
    End If
  Else
    If bStart + bLength = bPosition Then
      aLength = aPosition - aStart
      Exit For
    ElseIf bStart + bLength < bPosition Then
      aLength = aPosition - aStart - 1
      bLength = bPosition - bStart - 2
      Exit For
    End If
  End If
  bPosition = bPosition + 1 - (Asc(Mid(Str, aPosition, 1)) < 0)
Next
If aStart Then
  If aLength = -1 Then
    aLength = aPosition - aStart
    bLength = bPosition - bStart
  End If
  MidA = Mid(Str, aStart, aLength)
End If
End Function

引数の位置と長さは、補正後の値を返すように、ByRefにしています。

2008年10月 1日 (水)

MidA(文字列, 開始バイト位置, バイト長)関数を作る(VBScript)

Mid(文字列, 開始文字位置, 文字長)でもなく、
MidB(バイト列, 開始バイト位置, バイト長)でもない、
MidA(文字列, 開始バイト位置, バイト長)関数。

Unicode文字列からシフトJISコードでの開始バイト位置とバイト長で切り出します。
当然、2バイト文字の泣き分かれの可能性があるので、その場合は、1バイト前や、1バイト短くなります。

まず、VBScriptで、

MsgBox MidA("abcdefg",3,2)
MsgBox MidA("あいうefg",3,2)
MsgBox MidA("あいうefg",3,3)
MsgBox MidA("aいうefg",3,2)
MsgBox MidA("aいうefg",3,3)

Function MidA(Str,bStart,bLength)
Dim aStart,aLength,bPosition,aPosition
aStart=0
aLength=-1
bPosition=1
For aPosition=1 To Len(Str)
  If aStart=0 Then
    If bStart=bPosition Then
      aStart=aPosition
    ElseIf bStart<bPosition Then
      aStart=aPosition-1
      bStart=bPosition-2
    End If
  Else
    If bStart+bLength=bPosition Then
      aLength=aPosition-aStart
      Exit For
    ElseIf bStart+bLength<bPosition Then
      aLength=aPosition-aStart-1
      bLength=bPosition-bStart-2
      Exit For
    End If
  End If
  bPosition=bPosition+1-(Asc(Mid(Str,aPosition,1))<0)
Next
If aStart Then
  If aLength=-1 Then
    aLength=aPosition-aStart
    bLength=bPosition-bStart
  End If
  MidA=Mid(Str,aStart,aLength)
End If
End Function

引数の位置と長さは、補正後の値を返すように、ByRefにしています。

2008年9月30日 (火)

文字列中の異文字を16進表示する関数(VB.NET)

VB.NETに焼き直して、

Public Class Class1
Public Shared Sub Main()
Dim s = "あアabc" & Chr(10) & ChrW(128)
MsgBox(xString(s))
End Sub

Private Shared Function xString(ByVal s As String) As String
Dim x As String = ""
For k As Integer = 1 To Len(s)
  Dim c = Mid(s, k, 1)
  If 31 < AscW(c) AndAlso AscW(c) < 128 Then
  ElseIf &Hff60 < AscW(c) And AscW(c) < &Hffa0 Then
  ElseIf Chr(Asc(c)) = c And Asc(c) < 0 Then
  Else
    c = Hex(AscW(c))
    c = "{" & New String("0"c, Len(c) Mod 2) & c & "}"
  End If
  x = x & c
Next
Return x
End Function
End Class

2008年9月29日 (月)

文字列中の異文字を16進表示する関数(VBA)

VBAに焼き直して、

Sub a()
s = "あアabc" & Chr(10) & ChrW(128)
Debug.Print xString(s)
End Sub

Function xString(ByVal s As String) As String
Dim k As Long, c As String
For k = 1 To Len(s)
  c = Mid(s, k, 1)
  If 31 < AscW(c) And AscW(c) < 128 Then
  ElseIf &Hff60 < AscW(c) And AscW(c) < &Hffa0 Then
  ElseIf Chr(Asc(c)) = c  And Asc(c) < 0 Then
  Else
    c = Hex(AscW(c))
    c = "{" & String(Len(c) Mod 2, 48) & c & "}"
  End If
  xString = xString & c
Next
End Function

2008年9月28日 (日)

文字列中の異文字を16進表示する関数(VBScript)

文字列中に変な文字があっても、見た目では分からない場合があります。
VBScriptやJScriptなら、escape()してみれば分かりますが、VBAやVB.NETなどではどうするか?

まず、VBScriptで、
文字列中の異文字(ASCII、半角カタカナ、JIS X 0208、以外)を16進表示に変える関数
を作って、

s="あアabc" & chr(10) & chrw(128)
msgbox xString(s)

Function xString(s)
Dim k,c
For k=1 To Len(s)
  c=Mid(s,k,1)
  If 31<AscW(c) And AscW(c)<128 Then
  ElseIf &Hff60<AscW(c) And AscW(c)<&Hffa0 Then
  ElseIf Chr(Asc(c))=c And Asc(c)<0 Then
  Else
    c=Hex(AscW(c))
    c="{" & String(Len(c) Mod 2,48) & c & "}"
  End If
  xString=xString & c
Next
End Function

2008年9月25日 (木)

バイナリファイルを16進数でダンプする。(その2)

Byte配列を使わないようにして、JScriptにして、バッチファイルにラップすると、

HexDump.CMD ファイル

@if(0)==(0) ECHO OFF
CScript.exe //NoLogo //E:JScript "%~f0" %*
GOTO :EOF
@end
var adTypeBinary=1;
var adTypeText=2;
var bStream=new ActiveXObject("ADODB.Stream");
bStream.Open();
bStream.Type=adTypeBinary;
bStream.LoadFromFile(WScript.Arguments.Item(0));
var tStream=new ActiveXObject("ADODB.Stream");
tStream.Open();
tStream.Type=adTypeText;
tStream.WriteText(String.fromCharCode(0));
tStream.Position=0;
tStream.Type=adTypeBinary;
tStream.Position=2;
var z=tStream.Read(1);
tStream.Position=2;
for(var Pos=0;Pos<bStream.Size;Pos++){
  bStream.CopyTo(tStream,1);
  tStream.Write(z);
}
tStream.Position=0;
tStream.Type=2;
for(var Pos=0;Pos*2+2<tStream.Size;Pos+=16){
  WScript.StdOut.Write(Pos.toString(16));
  var Bytes=tStream.ReadText(16);
  var Chars="";
  for(var k=0;k<16;k++){
    if(k%4==0) WScript.StdOut.Write(" ");
    if(k<Bytes.length){
      var h=Bytes.charCodeAt(k).toString(16);
      if(h.length<2) WScript.StdOut.Write("0");
      WScript.StdOut.Write(h);
      var h=Bytes.charCodeAt(k);
      if(h<32) h=32;
      Chars+=String.fromCharCode(h);
    }else{
      WScript.StdOut.Write("  ");
    }
  }
  WScript.StdOut.WriteLine(" " + Chars);
}

2008年9月24日 (水)

バイナリファイルを16進数でダンプする。

昔、DUMPコマンドがあったような。。。

cscript HexDump.VBS ファイル

Option Explicit
Dim Stream
Dim Pos
Dim Bytes
Dim Chars
Dim c
Dim k
Set Stream=CreateObject("ADODB.Stream")
Stream.Open
Stream.Type=1
Stream.LoadFromFile WScript.Arguments.Item(0)
For Pos=0 To Stream.Size-1 Step 16
  WScript.StdOut.Write Hex(Pos)
  Bytes=Stream.Read(16)
  Chars=""
  For k=1 To 16
    If k Mod 4 = 1 Then WScript.StdOut.Write " "
    If k>LenB(Bytes) Then
      WScript.StdOut.Write "  "
    Else
      WScript.StdOut.Write Mid(Hex(256+AscB(MidB(Bytes,k,1))),2)
      c=AscB(MidB(Bytes,k,1))
      If c < 32 Then c=32
      Chars=Chars & Chr(c)
    End If
  Next
  WScript.StdOut.WriteLine " " & Chars
Next

本来なら、JScriptにして、バッチファイルにラップするところですが、JScriptからはByte配列が扱えないようで、断念しました。

2008年9月23日 (火)

ショートカットにドロップすると、引数と作業フォルダの環境変数が置換されない。(障害?)

ショートカットのターゲットパスと引数と作業フォルダには、環境変数が書けて、普通に起動すると、置換されるのですが、なぜか、ドロップしたときには、このうち、引数と作業フォルダの環境変数だけが置換されません。

これを利用すれば、普通に起動した場合と、ドロップした場合を区別できますが、使い道が思い浮かばない。。。

それより、回避方法は?

リンク先を

CMD.EXE /C START "" /D 作業フォルダ ターゲットパス 引数

にすれば、一応は回避できますが。。。

2008年9月16日 (火)

ウィンドウアプリから非表示のコンソールウィンドウを開く。(その3)

SW_HIDEで非表示のコンソールウィンドウを開いて、それをAttachConsole()するサンプル。

vbc /t:winexe sample3.vb

Imports System.Diagnostics
Imports System.Threading

Public Class Class1

Private Declare Function AttachConsole Lib "kernel32" (dwProcessId As Integer) As Integer
Private Declare Function AllocConsole Lib "kernel32" () As Integer
Private Declare Function FreeConsole Lib "kernel32" () As Integer

Private Structure KEY_EVENT_RECORD
Dim bKeyDown As Integer
Dim wRepeatCount As Short
Dim wVirtualKeyCode As Short
Dim wVirtualScanCode As Short
Dim UnicodeChar As Short
Dim dwControlKeyState As Integer
End Structure

Private Structure INPUT_RECORD
Dim EventType As Short
Dim KeyEvent As KEY_EVENT_RECORD
End Structure

Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Integer) As Integer
Private Declare Function WriteConsoleInput Lib "kernel32" Alias "WriteConsoleInputW" (ByVal hConsoleInput As Integer, ByVal lpBuffer() As INPUT_RECORD, ByVal nLength As Integer, ByRef lpNumberOfEventsWritten As Integer) As Integer

Private Const KEY_EVENT As Integer = 1s
Private Const STD_INPUT_HANDLE As Integer = -10

Public Shared Sub Main()
Dim oProcess As New Process()
oProcess.StartInfo.FileName = "cmd"
oProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
oProcess.Start()
Thread.Sleep(100)
Do While AttachConsole(oProcess.Id) = 0
  AllocConsole()
  FreeConsole()
  Thread.Sleep(100)
Loop
KeyIn("exit" + vbCr)
oProcess.WaitForExit()
MsgBox(CreateObject("WScript.Shell").Exec("fc.exe").StdErr.ReadAll())
End Sub

Private Shared Sub KeyIn(s As String)
Dim lpBuffer() As INPUT_RECORD
Dim lpNumberOfEventsWritten As Integer
Dim hConsoleInput As Integer = GetStdHandle(STD_INPUT_HANDLE)
ReDim lpBuffer(Len(s)*2-1)
For k As Integer = 0 To UBound(lpBuffer)
  lpBuffer(k).EventType = KEY_EVENT
  lpBuffer(k).KeyEvent.bKeyDown = (k + 1) Mod 2
  lpBuffer(k).KeyEvent.wRepeatCount = 0
  lpBuffer(k).KeyEvent.wVirtualScanCode = 0
  lpBuffer(k).KeyEvent.wVirtualKeyCode = 0
  lpBuffer(k).KeyEvent.UnicodeChar = AscW(Mid(s,1 + (k \ 2),1))
  lpBuffer(k).KeyEvent.dwControlKeyState = 0
Next
WriteConsoleInput(hConsoleInput, lpBuffer, UBound(lpBuffer)+1, lpNumberOfEventsWritten)
End Sub
End Class

SW_HIDEで作った非表示のコンソールウィンドウはShowWindow()で再表示できます。

コンソールアプリでは、WaitForInputIdle()が使えません。
なので、代わりに、AttachConsole()をリトライループします。
このとき、AttachConsole()のエラーをリセットするために、AllocConsole()+FreeConsole()します。

CMD.EXEを終了するために、exit{Enter}をWriteConsoleInput()します。
コードの大部分は、このための処理です。
もし、面倒なら、横着ですが、Process.Kill()すれば、簡単に終了できます。

2008年9月15日 (月)

ウィンドウアプリから非表示のコンソールウィンドウを開く。(その2)

CreateNoWindowで非表示のコンソールウィンドウを開いて、それをAttachConsole()するサンプル。

vbc /t:winexe sample2.vb

Imports System.Diagnostics
Imports System.Threading

Public Class Class1

Private Declare Function AttachConsole Lib "kernel32" (dwProcessId As Integer) As Integer

Public Shared Sub Main()
Dim oProcess As New Process()
oProcess.StartInfo.FileName = "cmd"
oProcess.StartInfo.UseShellExecute = False
oProcess.StartInfo.CreateNoWindow = True
oProcess.StartInfo.RedirectStandardInput = True
oProcess.StartInfo.RedirectStandardOutput = True
oProcess.Start()
oProcess.StandardOutput.ReadLine()
AttachConsole(oProcess.Id)
oProcess.StandardInput.Close()
oProcess.WaitForExit()
MsgBox(CreateObject("WScript.Shell").Exec("fc.exe").StdErr.ReadAll())
End Sub
End Class

CreateNoWindowで作った非表示のコンソールウィンドウはShowWindow()で再表示できないようです。

コンソールアプリでは、WaitForInputIdle()が使えません。
なので、代わりに、StandardOutput.ReadLine()で待ちます。

CMD.EXEを終了するために、StandardInput.Close()します。

2008年9月14日 (日)

ウィンドウアプリから非表示のコンソールウィンドウを開く。

ウィンドウアプリでAllocConsole()すると、コンソールウィンドウが開きます。
このコンソールウィンドウを非表示にできないでしょうか?

一度、表示してから非表示にしてよいなら、

ShowWindow(GetConsoleWindow(),SW_HIDE)

で、できますが、最初から非表示にできない?

もし、ウィンドウアプリを非表示で起動していればそうなりますが。。。

ウィンドウアプリからCMD.EXEを非表示で起動して、そのコンソールウィンドウをAttachConsole()することで代替できます。

ポイントは、
非表示の指定方法(SW_HIDE or CREATE_NO_WINDOW)
AttachConsole()のタイミングの取り方(WaitForInputIdle代替 or StandardOutput.ReadLine)
CMD.EXEの終了のさせ方(WriteConsoleInput or StandardInput.Close)
の3つです。

まず、一瞬コンソールが表示されるサンプルから。

vbc /t:winexe sample1.vb

Imports System.Threading

Public Class Class1

Private Declare Function AllocConsole Lib "kernel32" () As Integer
Private Declare Function GetConsoleWindow Lib "kernel32" () As Integer
Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Integer, ByVal nCmdShow As Short) As Integer

Private Const SW_HIDE As Short = 0s
Private Const SW_SHOW As Short = 5s

Public Shared Sub Main()
AllocConsole()
Dim hwnd As Integer = GetConsoleWindow()
ShowWindow(hwnd, SW_HIDE)
Thread.Sleep(5000)
ShowWindow(hwnd, SW_SHOW)
Thread.Sleep(5000)
End Sub
End Class

2008年9月11日 (木)

.NETオブジェクトのオーバロードされたメソッドのサフィックスを調べる。

System.Text.StringBuilderなどの一部の.NETオブジェクトは、スクリプトなどから使えます。
しかし、オーバロードされたメソッドの呼び出しは、自動的には解決されません。
スクリプトからサフィックスで区別して呼び出す必要があります。
しかし、そのサフィックスはどうやって調べるのか?

ListMethods.exe 型名

ListMethods.exe system.text.stringbuilder

vbc ListMethods.VB

Imports System.Reflection
Imports System.Text
Imports System.Collections
Imports System
Imports Microsoft.VisualBasic

Public Class Class1
Public Shared Sub Main(ByVal Args() As String)
If Args.Length<>1 Then
  Console.WriteLine("Usage: ListMethods typename")
  Exit Sub
End If
Dim oType As Type = Type.GetType(Args(0),False,True)
If oType Is Nothing Then
  Console.WriteLine("Name not found - {0}",Args(0))
  Exit Sub
End If
Dim sList As SortedList = New SortedList
For Each Method As MethodInfo In oType.GetMethods()
  Dim sb As StringBuilder = New StringBuilder
  sb.Append(Method.Name)
  If sList.Contains(Method.Name) Then
    sb.Append("_")
    sb.Append(sList.Item(Method.Name).Count + 1)
  End If
  sb.Append("(")
  Dim pCount As Integer = 0
  For Each Parameter As ParameterInfo In Method.GetParameters()
    If pCount > 0 Then sb.Append(", ")
    If Parameter.IsOptional Then sb.Append("Optional ")
    sb.Append(Parameter.Name + " As " + Parameter.ParameterType.ToString())
    If Not IsDBNull(Parameter.DefaultValue.ToString()) AndAlso Parameter.DefaultValue.ToString().Length Then
      sb.Append(" = " + Parameter.DefaultValue.ToString())
    End If
    pCount += 1
  Next
  sb.Append(")")
  If Method.ReturnType.ToString()<>"System.Void" Then
    sb.Append(" As " + Method.ReturnType.ToString())
  End If
  If sList.Contains(Method.Name) Then
    sList.Item(Method.Name).Add(sb.ToString())
  Else
    Dim aList As ArrayList = New ArrayList
    aList.Add(sb.ToString())
    sList.Add(Method.Name,aList)
  End If
Next
For Each aList As ArrayList In sList.Values
  For Each Item As String In aList
    Console.WriteLine(Item)
  Next
Next
End Sub
End Class

2008年9月10日 (水)

コンソールで一部の文字列の文字色と背景色を変えるEchoコマンド拡張

EchoX.exe 文字色 背景色 文字列 [文字色 背景色 文字列]...

文字色と背景色は、数字または名前で指定します。

0 Black
1 DarkBlue
2 DarkGreen
3 DarkCyan
4 DarkRed
5 DarkMagenta
6 DarkYellow
7 Gray
8 DarkGray
9 Blue
10 Green
11 Cyan
12 Red
13 Magenta
14 Yellow
15 White

vbc EchoX.VB

Public Class Class1
Public Shared Sub Main(ByVal Args() As String)
If Args.Length = 0 OrElse Args.Length Mod 3 Then
  Console.WriteLine("Usage: EchoX ForegroundColor BackgroundColor String [F B S]...")
  For Each ColorName As String In ConsoleColor.GetNames(GetType(ConsoleColor))
    Console.WriteLine("{0,2} {1}", [Enum].Format(GetType(ConsoleColor), CType([Enum].Parse(GetType(ConsoleColor), ColorName), ConsoleColor), "d"), ColorName)
  Next
  Exit Sub
End If
Try
  For k As Integer =0 To UBound(Args) Step 3
    Console.ForegroundColor = CType([Enum].Parse(GetType(ConsoleColor), Args(k), True), ConsoleColor)
    Console.BackgroundColor = CType([Enum].Parse(GetType(ConsoleColor), Args(k+1), True), ConsoleColor)
    Console.Write(Args(k+2))
  Next
  Console.ResetColor()
Catch
  Console.ResetColor()
  Console.Error.WriteLine("Source" & vbTab & vbTab & Err.Source & vbLf & "Number" & vbTab & vbTab & Err.Number & vbLf & "Description" & vbTab & Err.Description & vbLf & "DLL Error" & vbTab & vbTab & Err.LastDLLError)
End Try
End Sub
End Class

文字列に空白を含むときは、""で囲みます
その中に更に"を含むときは、\"でエスケープします。
このとき、偶数番目の"から奇数番目の"までの間は、CMD.EXEの制御文字^&|<>)を^でエスケープする必要があります。

行末の改行はありません。

2008年9月 9日 (火)

コンソールコマンドを実行して、そのコンソールログを採取するコマンドをVB.NETで作る(その2)

使用法は、

ConCopyX.exe [コマンドライン] >ファイル  (上書き)

ConCopyX.exe [コマンドライン] >>ファイル (追加書き)

ファイルに保存するときは、標準出力をリダイレクトします。
上書きか、追加書きかは、リダイレクションで指定します。

コマンドラインを省略すると、CMDを実行します。
つまり、サブシェルのコマンドプロンプトになります。
このときは、Exitでサブシェルを終了します。

コマンドラインの起動から終了までのコンソールログが標準出力に書き込まれます。

ただし、コンソールログの採取は、コマンドラインの実行と並行して行われるので、
コマンドの出力が多くて、速いと、ログの採取が間に合わず、抜けることがあります。
なので、コマンドプロンプトのプロパティで「画面バッファのサイズ」の「高さ」を十分大きくしておきます。

ConCopyX.exe >ファイル
~~~
~~~
~~~
Exit

2008年9月 8日 (月)

コンソールコマンドを実行して、そのコンソールログを採取するコマンドをVB.NETで作る

vbc ConCopyX.VB

Imports System.Diagnostics
Imports System.Threading

Public Class Class1

Private Structure COORD
Dim x As Short
Dim y As Short
End Structure

Private Structure SMALL_RECT
Dim Left As Short
Dim Top As Short
Dim Right As Short
Dim Bottom As Short
End Structure

Private Structure CONSOLE_SCREEN_BUFFER_INFO
Dim dwSize As COORD
Dim dwCursorPosition As COORD
Dim wAttributes As Short
Dim srWindow As SMALL_RECT
Dim dwMaximumWindowSize As COORD
End Structure

Private Structure CHAR_INFO
Dim UnicodeChar As Short
Dim Attributes As Short
End Structure

Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Integer) As Integer
Private Declare Function SetStdHandle Lib "kernel32" (ByVal nStdHandle As Integer, ByVal hHandle As Integer) As Integer
Private Declare Function GetConsoleScreenBufferInfo Lib "kernel32" (ByVal hConsoleOutput As Integer, ByRef lpConsoleScreenBufferInfo As CONSOLE_SCREEN_BUFFER_INFO) As Integer
Private Declare Function ReadConsoleOutputCharacter Lib "kernel32" Alias "ReadConsoleOutputCharacterA" (ByVal hConsoleOutput As Integer, ByVal lpCharacter As String, ByVal nLength As Integer, ByVal dwReadCoord As COORD, ByRef lpNumberOfCharsRead As Integer) As Integer
'Private Declare Function ScrollConsoleScreenBuffer Lib "kernel32" (ByVal hConsoleOutput As Integer, ByRef lpScrollRectangle As SMALL_RECT, ByRef lpClipRectangle As SMALL_RECT, ByVal dwDestinationOrigin As COORD, ByRef lpFill As CHAR_INFO) As Integer
Private Declare Function ScrollConsoleScreenBuffer Lib "kernel32" Alias "ScrollConsoleScreenBufferW" (ByVal hConsoleOutput As Integer, ByRef lpScrollRectangle As SMALL_RECT, ByVal lpClipRectangle As Integer, ByVal dwDestinationOrigin As COORD, ByRef lpFill As CHAR_INFO) As Integer
Private Declare Function SetConsoleCursorPosition Lib "kernel32" (ByVal hConsoleOutput As Integer, ByVal dwCursorPosition As COORD) As Integer
Private Declare Function SetConsoleCtrlHandler Lib "kernel32" (ByVal Handler As Integer, ByVal Add As Boolean) As Boolean

Private Const STD_OUTPUT_HANDLE As Integer = -11
Private Const STD_ERROR_HANDLE As Integer = -12

Public Shared Function Main(ByVal Arguments() As String) As Integer
Dim CommandLine As String = System.Environment.CommandLine
Dim FileName As String = ""
Dim n As Integer
For n = 1 To 2
  Dim Quoted As Boolean = False
  Dim k As Integer
  For k=0 To CommandLine.Length-1
    If CommandLine.Chars(k) = """" Then
      Quoted = Not Quoted
    ElseIf Not Quoted AndAlso CommandLine.Chars(k) = " " Then
      Exit For
    End If
  Next
  FileName = CommandLine.Substring(0,k).Replace("""","")
  CommandLine = CommandLine.Remove(0,k).Trim()
'  Console.Error.WriteLine("FileName:{0}",FileName)
'  Console.Error.WriteLine("Arguments:{0}",CommandLine)
Next
If FileName = "" Then FileName = "cmd"
Try
  Dim hConsoleOutput As Integer = GetStdHandle(STD_OUTPUT_HANDLE)
  Dim ConsoleScreenBufferInfo As CONSOLE_SCREEN_BUFFER_INFO
  GetConsoleScreenBufferInfo(hConsoleOutput, ConsoleScreenBufferInfo)
  If ConsoleScreenBufferInfo.dwSize.x Then
    Console.WriteLine("Usage: ConCopyX [command_line] > file")
    Exit Function
  End If
  Dim hConsoleError As Integer = GetStdHandle(STD_ERROR_HANDLE)
  GetConsoleScreenBufferInfo(hConsoleError, ConsoleScreenBufferInfo)
'  Console.Error.WriteLine("dwsize ({0},{1})",ConsoleScreenBufferInfo.dwSize.x,ConsoleScreenBufferInfo.dwSize.y)
'  Console.Error.WriteLine("dwCursorPosition ({0},{1})",ConsoleScreenBufferInfo.dwCursorPosition.x,ConsoleScreenBufferInfo.dwCursorPosition.y)
  Dim y0 As Short = ConsoleScreenBufferInfo.dwCursorPosition.y
  If y0 > ConsoleScreenBufferInfo.dwSize.y \ 2 Then Scroll(hConsoleError, y0, ConsoleScreenBufferInfo)
  Dim oProcess As New Process()
  oProcess.StartInfo.FileName = FileName
  oProcess.StartInfo.Arguments = CommandLine
  oProcess.StartInfo.UseShellExecute = False
  SetStdHandle(STD_OUTPUT_HANDLE, hConsoleError)
  oProcess.Start()
  SetConsoleCtrlHandler(0, True)
  SetStdHandle(STD_OUTPUT_HANDLE, hConsoleOutput)
  Do
    GetConsoleScreenBufferInfo(hConsoleError, ConsoleScreenBufferInfo)
    If y0 = ConsoleScreenBufferInfo.dwCursorPosition.y Then
      If oProcess.HasExited Then Exit Do
      If y0 > ConsoleScreenBufferInfo.dwSize.y \ 2 Then Scroll(hConsoleError, y0, ConsoleScreenBufferInfo)
      Thread.Sleep(100)
    Else
      For y As Integer = y0 To ConsoleScreenBufferInfo.dwCursorPosition.y-1
        Dim ConsoleText As String = New String(vbNullChar, ConsoleScreenBufferInfo.dwSize.x)
        Dim nLength As Integer =  ConsoleScreenBufferInfo.dwSize.x
        Dim dwReadCoord As COORD
        dwReadCoord.x = 0
        dwReadCoord.y = y
        Dim NumberOfCharsRead As Integer
        ReadConsoleOutputCharacter(hConsoleError, ConsoleText, nLength, dwReadCoord, NumberOfCharsRead)
        Console.WriteLine(ConsoleText.TrimEnd(" "c,vbNullChar))
      Next
      y0 = ConsoleScreenBufferInfo.dwCursorPosition.y
    End If
  Loop
  System.Environment.Exit(oProcess.ExitCode)
Catch
  Console.Error.WriteLine("Source" & vbTab & vbTab & Err.Source & vbLf & "Number" & vbTab & vbTab & Err.Number & vbLf & "Description" & vbTab & Err.Description & vbLf & "DLL Error" & vbTab & vbTab & Err.LastDLLError)
  System.Environment.Exit(255)
End Try
End Function

Private Shared Sub Scroll(ByVal hConsoleError As Integer, ByRef y0 As Short, ByRef ConsoleScreenBufferInfo As CONSOLE_SCREEN_BUFFER_INFO)
Dim lpScrollRectangle As SMALL_RECT
lpScrollRectangle.Top = y0 - (ConsoleScreenBufferInfo.dwSize.y \ 2)
lpScrollRectangle.Left = 0
lpScrollRectangle.Right = ConsoleScreenBufferInfo.dwSize.x - 1
lpScrollRectangle.Bottom = ConsoleScreenBufferInfo.dwSize.y - 1
Dim dwDestinationOrigin As COORD
dwDestinationOrigin.x = 0
dwDestinationOrigin.y = 0
Dim lpFill As CHAR_INFO
lpFill.Attributes = ConsoleScreenBufferInfo.wAttributes
lpFill.UnicodeChar = 32
ScrollConsoleScreenBuffer(hConsoleError, lpScrollRectangle, 0, dwDestinationOrigin, lpFill)
y0 = ConsoleScreenBufferInfo.dwSize.y \ 2
Dim dwCursorPosition As COORD
dwCursorPosition.x = ConsoleScreenBufferInfo.dwCursorPosition.x
dwCursorPosition.y = y0
SetConsoleCursorPosition(hConsoleError, dwCursorPosition)
End Sub
End Class

2008年9月 5日 (金)

ShellWindowsでWindows SearchのItem(n).Documentを見るとエラーになる。

Windows Searchを開いていると、Item(n).Documentを見るだけで、エラーになります。

エラー: クラスはオートメーションをサポートしていません。
コード: 800A01AE

この対処法は、エラーを拾ってもよいのですが、次でも迂回できます。

For Each ie In CreateObject("Shell.Application").Windows()
  If Right(ie.LocationName,17)=" - Windows Search" Then
    MsgBox "Windows Search"
  Else
    MsgBox TypeName(ie.Document)
  End If
Next

2008年9月 4日 (木)

IE7の「リンク」バーからアプリを起動する。

エクスプローラの「リンク」バーからは、アプリを起動することができます。

しかし、IE7の「リンク」バーからアプリを起動しようとすると、まず、
「ファイルのダウンロード - セキュリティの警告」
「このファイルを開くか、または保存しますか?」
のダイアログが出て、さらに、
「Internet Explorer - セキュリティの警告」
「発行元が確認できませんでした。このソフトウェアを実行しますか?」
のダイアログが出ます。

これらを回避するには、

(1) デスクトップに、「リンク2」フォルダを作ります。
この中にアプリやアプリのショートカットを入れます。

(2) デスクトップで、「リンク2」をドラッグして、スタートメニューボタンにドロップします。
すると、スタートメニューの中に「リンク2」が追加されます。

(3) 「リンク」フォルダを開き、スタートメニューから「リンク2」をドラッグして、「リンク」フォルダにドロップします。
すると、「リンク」フォルダに「リンク2」の「フォルダのショートカット」が移動します。

これで、IE7の「リンク」バーから「リンク2」のアプリが起動できるようになります。

2008年9月 3日 (水)

バッチファイルからRunAsコマンドを使う。

RunAsコマンドは、パスワードをコンソールから入力しないと使えません。
なので、バッチファイルからは、RunAsコマンドが使えません。
しかし、KeyInコマンドを使えば、バッチファイルからRunAsコマンドが使えます。

start /b cmd /c sleep 3 ^& keyin password{CR}
runas /user:computer\user command

または、

start /b runas /user:computer\user command
sleep 3
keyin password{CR}

2008年9月 2日 (火)

コンソール入力バッファにデータを書き込むコマンドをVB.NETで作る(その2)

使用法は、

KeyIn.exe 文字列

引数の文字列をそのまま、コンソール入力バッファに書き込みます。

コマンドプロンプトから、&|<>を引数に含めるときは、^でエスケープします。
改行を引数に含めるときは、{CR}で代替入力します。大小文字の区別あり。

バッチの中から、DOSKEYマクロを使うときは、

KeyIn.exe マクロ{CR}exit{CR}
cmd.exe

2008年9月 1日 (月)

コンソール入力バッファにデータを書き込むコマンドをVB.NETで作る

コマンドプロンプトでは、コマンドを入力してエンターキーを押すまでは、コマンドラインが編集できます。
しかし、コマンドからコマンドラインを出力しても、それを直接、編集して実行することはできません。
また、バッチファイルの中からDOSKEYマクロを実行することはできません。
これらは、コマンドからコンソール入力バッファに書き込めば、可能です。

vbc KeyIn.VB

Public Class Class1

Private Structure KEY_EVENT_RECORD
Dim bKeyDown As Integer
Dim wRepeatCount As Short
Dim wVirtualKeyCode As Short
Dim wVirtualScanCode As Short
Dim UnicodeChar As UShort
Dim dwControlKeyState As Integer
End Structure

Private Structure INPUT_RECORD
Dim EventType As Short
Dim KeyEvent As KEY_EVENT_RECORD
End Structure

Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Integer) As Integer
Private Declare Function WriteConsoleInput Lib "kernel32" Alias "WriteConsoleInputW" (ByVal hConsoleInput As Integer, ByVal lpBuffer() As INPUT_RECORD, ByVal nLength As Integer, ByRef lpNumberOfEventsWritten As Integer) As Integer

Private Const KEY_EVENT As Integer = 1s
Private Const STD_INPUT_HANDLE As Integer = -10

Public Shared Sub Main()
Dim CommandLine As String = System.Environment.CommandLine
Dim FileName As String = ""
Dim Quoted As Boolean = False
Dim k As Integer
For k=0 To CommandLine.Length-1
  If CommandLine.Chars(k) = """" Then
    Quoted = Not Quoted
  ElseIf Not Quoted AndAlso CommandLine.Chars(k) = " " Then
    Exit For
  End If
Next
FileName = CommandLine.Substring(0,k).Replace("""","")
CommandLine = CommandLine.Remove(0,k).Trim()
'Console.Error.WriteLine("FileName:{0}",FileName)
'Console.Error.WriteLine("Arguments:{0}",CommandLine)
KeyIn(CommandLine.Replace("{CR}",Chr(13)))
End Sub

Private Shared Sub KeyIn(s As String)
Dim lpBuffer() As INPUT_RECORD
Dim lpNumberOfEventsWritten As Integer
Dim hConsoleInput As Integer = GetStdHandle(STD_INPUT_HANDLE)
ReDim lpBuffer(Len(s)*2-1)
For k As Integer = 0 To UBound(lpBuffer)
  lpBuffer(k).EventType = KEY_EVENT
  lpBuffer(k).KeyEvent.bKeyDown = (k + 1) Mod 2
  lpBuffer(k).KeyEvent.wRepeatCount = 0
  lpBuffer(k).KeyEvent.wVirtualScanCode = 0
  lpBuffer(k).KeyEvent.wVirtualKeyCode = 0
  lpBuffer(k).KeyEvent.UnicodeChar = AscW(Mid(s,1 + (k \ 2),1))
  lpBuffer(k).KeyEvent.dwControlKeyState = 0
Next
WriteConsoleInput(hConsoleInput, lpBuffer, UBound(lpBuffer)+1, lpNumberOfEventsWritten)
End Sub
End Class

2008年8月30日 (土)

コマンドプロンプトのコンソールログを採取するコマンドをVB.NETで作る(その2)

コンソールスクリーンバッファの中身を標準出力に取り出します。

使用法

ConCopy.exe >ファイル (上書き)

ConCopy.exe >>ファイル (追加書き)

コンソールスクリーンバッファから溢れたログは消えます。
なので、コマンドプロンプトのプロパティで「画面バッファのサイズ」の「高さ」を十分大きくしておきます。

過去のログが不要なときは、先にclsコマンドでコンソールスクリーンバッファを消去しておきます。

ファイルに保存するときは、標準出力をリダイレクトします。
上書きか、追加書きかは、リダイレクションで指定します。

cls
~~~
~~~
concopy > ファイル
cls
~~~
~~~
concopy >> ファイル

2008年8月29日 (金)

コマンドプロンプトのコンソールログを採取するコマンドをVB.NETで作る

コンソールスクリーンバッファの中身を標準出力に取り出します。

vbc concopy.vb

Public Class Class1

Private Structure COORD
Dim x As Short
Dim y As Short
End Structure

Private Structure SMALL_RECT
Dim Left As Short
Dim Top As Short
Dim Right As Short
Dim Bottom As Short
End Structure

Private Structure CONSOLE_SCREEN_BUFFER_INFO
Dim dwSize As COORD
Dim dwCursorPosition As COORD
Dim wAttributes As Short
Dim srWindow As SMALL_RECT
Dim dwMaximumWindowSize As COORD
End Structure

Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Integer) As Integer
Private Declare Function GetConsoleScreenBufferInfo Lib "kernel32" (ByVal hConsoleOutput As Integer, ByRef lpConsoleScreenBufferInfo As CONSOLE_SCREEN_BUFFER_INFO) As Integer
Private Declare Function ReadConsoleOutputCharacter Lib "kernel32" Alias "ReadConsoleOutputCharacterA" (ByVal hConsoleOutput As Integer, ByVal lpCharacter As String, ByVal nLength As Integer, ByVal dwReadCoord As COORD, ByRef lpNumberOfCharsRead As Integer) As Integer

Private Const STD_INPUT_HANDLE As Integer = -10
Private Const STD_OUTPUT_HANDLE As Integer = -11
Private Const STD_ERROR_HANDLE As Integer = -12

Public Shared Sub Main()
Try
  Dim hConsoleOutput As Integer = GetStdHandle(STD_ERROR_HANDLE)
  Dim ConsoleScreenBufferInfo As CONSOLE_SCREEN_BUFFER_INFO
  GetConsoleScreenBufferInfo(hConsoleOutput, ConsoleScreenBufferInfo)
  For y As Integer = 0 To ConsoleScreenBufferInfo.dwCursorPosition.y-1
    Dim ConsoleText As String = New String(vbNullChar, ConsoleScreenBufferInfo.dwSize.x)
    Dim nLength As Integer =  ConsoleScreenBufferInfo.dwSize.x
    Dim dwReadCoord As COORD
    dwReadCoord.x = 0
    dwReadCoord.y = y
    Dim NumberOfCharsRead As Integer
    ReadConsoleOutputCharacter(hConsoleOutput, ConsoleText, nLength, dwReadCoord, NumberOfCharsRead)
    Console.WriteLine(ConsoleText.TrimEnd(" "c,vbNullChar))
  Next
Catch
  Console.Error.WriteLine("Source" & vbTab & vbTab & Err.Source & vbLf & "Number" & vbTab & vbTab & Err.Number & vbLf & "Description" & vbTab & Err.Description & vbLf & "DLL Error" & vbTab & vbTab & Err.LastDLLError)
End Try
End Sub
End Class

2008年8月28日 (木)

コンソールウィンドウの表示を変更するコマンドをVB.NETで作る

ShowConsoleWindow.exe [ウィンドウスタイル]

ウィンドウスタイルの値は、
0 非表示
1 普通サイズ
2 最小化
3 最大化
省略すると、元のサイズに戻す。

vbc ShowConsoleWindow.VB

Imports Microsoft.VisualBasic
Imports System

Public Class Class1

Private Declare Function GetConsoleWindow Lib "kernel32" () As Integer
Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Integer, ByVal nCmdShow As Short) As Integer

Private Const SW_HIDE As Short = 0s
Private Const SW_SHOWNORMAL As Short = 1s
Private Const SW_SHOWMINIMIZED As Short = 2s
Private Const SW_SHOWMAXIMIZED As Short = 3s
Private Const SW_SHOWNOACTIVATE As Short = 4s
Private Const SW_SHOW As Short = 5s
Private Const SW_MINIMIZE As Short = 6s
Private Const SW_SHOWMINNOACTIVE As Short = 7s
Private Const SW_SHOWNA As Short = 8s
Private Const SW_RESTORE As Short = 9s
Private Const SW_SHOWDEFAULT As Short = 10s

Public Shared Function Main(ByVal CmdArgs() As String) As Integer
Dim nCmdShow As Short = SW_RESTORE
If CmdArgs.Length=0 Then
ElseIf CmdArgs.Length=1 AndAlso IsNumeric(CmdArgs(0)) Then
  nCmdShow=Convert.ToInt16(CmdArgs(0))
Else
  Console.WriteLine("Usage: ShowConsoleWindow [nCmdShow]")
  Return 0
End If
Dim hwnd As Integer = GetConsoleWindow()
Console.WriteLine(hwnd)
If hwnd Then ShowWindow(hwnd, nCmdShow)
Return hwnd
End Function
End Class

2008年8月27日 (水)

コンソールアプリを非表示で実行するラッパーをVB.NETで作る。(その3)

コンソールアプリを非表示で実行するコマンドとしても使えます。

コンソールアプリ [引数...]

の代わりに、

HideExec.exe コンソールアプリ [引数...]

とします。

ここで、コンソールアプリの標準入出力は、NULになります。

もし、ここで、リダイレクションやパイプを指定すれば、それが有効になります。

これは、一般のウィンドウアプリに共通して言えることです。

特に、.NETでは、Console.WriteやConsole.Error.Writeが、エラーハンドリングなしに、NOPになります。

2008年8月26日 (火)

コンソールアプリを非表示で実行するラッパーをVB.NETで作る。(その2)

このラッパーをWScript.ShellのExec()で使うには、

Set oExec=wShell.Exec("コンソールアプリ [引数...]")

の代わりに、

Set oExec=wShell.Exec("HideExec.exe コンソールアプリ [引数...]")

とします。

使用例(1)

Set wShell=CreateObject("WScript.Shell")
Set oExec=wShell.Exec("HideExec.exe more.com")
oExec.StdIn.WriteLine "aaa"
oExec.StdIn.WriteLine "bbb"
oExec.StdIn.Close
MsgBox oExec.StdOut.ReadAll,,"StdOut"
MsgBox oExec.StdErr.ReadAll,,"StdErr"

Exec()のStdOutやStdErrは、ノンブロッキングReadができないので、StdOutとStdErrのどちらに出るか予測できない会話はできません。
もし、StdOutとStdErrのどちらに出るか予見可能なら、会話も可能です。

使用例(2)

Set wShell=CreateObject("WScript.Shell")
Set oExec=wShell.Exec("HideExec.exe fc")
MsgBox oExec.StdOut.ReadAll,,"StdOut"
MsgBox oExec.StdErr.ReadAll,,"StdErr"

2008年8月25日 (月)

コンソールアプリを非表示で実行するラッパーをVB.NETで作る。

ウィンドウアプリとしてコンパイルします。

vbc /t:winexe HideExec.VB

Imports Microsoft.VisualBasic
Imports System
Imports System.Diagnostics
Imports System.Threading
Imports System.Text
Imports System.IO

Public Class HideExec

Private Shared InputWriter As StreamWriter = Nothing
Private Shared OutputReader As StreamReader = Nothing
Private Shared ErrorReader As StreamReader = Nothing

Public Shared Function Main(ByVal Arguments() As String) As Integer
Dim CommandLine As String = System.Environment.CommandLine
Dim FileName As String = ""
Dim n As Integer
For n = 1 To 2
  Dim Quoted As Boolean = False
  Dim k As Integer
  For k=0 To CommandLine.Length-1
    If CommandLine.Chars(k) = """" Then
      Quoted = Not Quoted
    ElseIf Not Quoted AndAlso CommandLine.Chars(k) = " " Then
      Exit For
    End If
  Next
  FileName = CommandLine.Substring(0,k).Replace("""","")
  CommandLine = CommandLine.Remove(0,k).Trim()
'  Console.WriteLine("FileName:{0}",FileName)
'  Console.WriteLine("Arguments:{0}",CommandLine)
Next
Dim oProcess As New Process()
oProcess.StartInfo.FileName = FileName
oProcess.StartInfo.Arguments = CommandLine
oProcess.StartInfo.UseShellExecute = False
oProcess.StartInfo.CreateNoWindow = True
oProcess.StartInfo.RedirectStandardInput = True
oProcess.StartInfo.RedirectStandardOutput = True
oProcess.StartInfo.RedirectStandardError = True

Try
  oProcess.Start()
  OutputReader = oProcess.StandardOutput
  Dim OutputThread As New Thread(AddressOf OutputDataHandler)
  OutputThread.Start()
  ErrorReader = oProcess.StandardError
  Dim ErrorThread As New Thread(AddressOf ErrorDataHandler)
  ErrorThread.Start()
  InputWriter = oProcess.StandardInput
  Dim InputThread As New Thread(AddressOf InputDataHandler)
  InputThread.Start()
  oProcess.WaitForExit()
  OutputThread.Join()
  ErrorThread.Join()
  System.Environment.Exit(oProcess.ExitCode)
Catch
  MsgBox("Source" & vbTab & vbTab & Err.Source & vbLf & "Number" & vbTab & vbTab & Err.Number & vbLf & "Description" & vbTab & Err.Description & vbLf & "DLL Error" & vbTab & vbTab & Err.LastDLLError)
  System.Environment.Exit(255)
End Try
End Function

Private Shared Sub OutputDataHandler()
Do
  Dim c As Integer = OutputReader.Read()
  If c = -1 Then Exit Do
  Console.Write(Convert.ToChar(c))
Loop
End Sub

Private Shared Sub ErrorDataHandler()
Do
  Dim c As Integer = ErrorReader.Read()
  If c = -1 Then Exit Do
  Console.Error.Write(Convert.ToChar(c))
Loop
End Sub

Private Shared Sub InputDataHandler()
Try
  Do
    Dim c As Integer = Console.Read()
    If c = -1 Then Exit Do
    InputWriter.Write(Convert.ToChar(c))
  Loop
Catch
End Try
InputWriter.Close()
End Sub

End Class

2008年8月24日 (日)

WScript.ShellのExec()で、コンソールアプリを非表示で実行するラッパー(その2)

このラッパーをWScript.ShellのExec()で使うには、

Set oExec=wShell.Exec("コンソールアプリ [引数...]")

の代わりに、

Set oExec=wShell.Exec("WScript.exe ""フルパス\HideExec.VBS"" コンソールアプリ [引数...]")
(もし入力があれば、oExec.Writeなど)
oExec.StdIn.Close

とします。

使用例(1)

Set wShell=CreateObject("WScript.Shell")
Set oExec=wShell.Exec("WScript.exe HideExec.VBS more")
oExec.StdIn.WriteLine "aaa"
oExec.StdIn.WriteLine "bbb"
oExec.StdIn.Close
MsgBox oExec.StdOut.ReadAll,,"StdOut"
MsgBox oExec.StdErr.ReadAll,,"StdErr"

入力は最初にまとめて与えます。ラッパーの都合で、会話的にはできません。

使用例(2)

Set wShell=CreateObject("WScript.Shell")
Set oExec=wShell.Exec("WScript.exe HideExec.VBS fc")
oExec.StdIn.Close
MsgBox oExec.StdOut.ReadAll,,"StdOut"
MsgBox oExec.StdErr.ReadAll,,"StdErr"

ラッパーの都合で、入力がなくても、最初にoExec.StdIn.Closeしないと駄目です。

また、スクリプトホストの設定に関係なく、WScript.exeを指定しないと駄目です。
関連付けの起動では、標準入出力がinheritされないからです。

2008年8月23日 (土)

WScript.ShellのExec()で、コンソールアプリを非表示で実行するラッパー

WScript.ShellのExec()で、コンソールアプリを実行すると、コンソールウィンドウが表示されて、鬱陶しいですね。

そこで、コンソールウィンドウを非表示にする、コンソールアプリのラッパーをVBScriptで作ります。

HideExec.VBS

Option Explicit
Dim fso
Dim wStdIn
Dim wStdOut
Dim wStdErr
Dim TempPath
Dim pStdIn
Dim pStdOut
Dim pStdErr
Dim StdIn
Dim StdOut
Dim StdErr
Dim wShell
Dim Command
Dim arg

Set fso=CreateObject("Scripting.FileSystemObject")
Set wStdIn=fso.GetStandardStream(0)
Set wStdOut=fso.GetStandardStream(1)
Set wStdErr=fso.GetStandardStream(2)
TempPath=fso.GetSpecialFolder(2).Path
pStdIn=fso.BuildPath(TempPath,fso.GetTempName())
pStdOut=fso.BuildPath(TempPath,fso.GetTempName())
pStdErr=fso.BuildPath(TempPath,fso.GetTempName())
Set StdIn=fso.CreateTextFile(pStdIn)
Sub Try
Do While Not wStdIn.AtEndOfStream
  StdIn.Write wStdIn.Read(1)
Loop
End Sub
Sub Catch
On Error Resume Next
Try
End Sub
Catch
StdIn.Close
Set wShell=CreateObject("WScript.Shell")
Command=Array()
For Each arg In WScript.Arguments
  If InStr(arg," ") Then arg="""" & arg & """"
  ReDim Preserve Command(UBound(Command)+1)
  Command(UBound(Command))=arg
Next
Command=Join(Command)
Command=Replace(Command,"`","""")
Call CreateObject("WScript.Shell").Run("CMD.EXE /S /C <""" & pStdIn & """ "& Command & " 1>""" & pStdOut & """ 2>""" & pStdErr & """""",0,False)
Do While Not fso.FileExists(pStdOut)
  WScript.Sleep 1000
Loop
Set StdOut=fso.OpenTextFile(pStdOut)
Do While Not fso.FileExists(pStdErr)
  WScript.Sleep 1000
Loop
Set StdErr=fso.OpenTextFile(pStdErr)
Do
  Do While Not StdOut.AtEndOfStream
    On Error Resume Next
    wStdOut.Write StdOut.Read(1)
    On Error GoTo 0
  Loop
  Do While Not StdErr.AtEndOfStream
    On Error Resume Next
    wStdErr.Write StdErr.Read(1)
    On Error GoTo 0
  Loop
  If AtEndOfStream(pStdOut) And AtEndOfStream(pStdErr) And StdOut.AtEndOfStream And StdErr.AtEndOfStream Then Exit Do
  WScript.Sleep 1000
Loop
StdOut.Close
StdErr.Close
fso.DeleteFile(pStdIn)
fso.DeleteFile(pStdOut)
fso.DeleteFile(pStdErr)

Function AtEndOfStream(Path)
On Error Resume Next
Call fso.OpenTextFile(Path,8)
AtEndOfStream=CBool(Err.Number=0)
End Function

2008年8月22日 (金)

renameコマンドのワイルドカードの怪?

renameコマンドのワイルドカードの「仕様」が「正しく」書いてあるところがないようです。

ren 旧ファイル名 新ファイル名

旧ファイル名のワイルドカードは、普通の解釈のとおり。

新ファイル名のワイルドカードは、普通の解釈と異なる!

基本形1

abc*

左から3文字(3バイトではなく)をabcに変えて、4文字目以降は元ファイル名を変えない。

基本形2

*xyz

元ファイル名を右から探して最初のxを見つけて、そこから右をxyzに変える。
元ファイル名にxがないときは末尾に追加。

応用形1

abc*xyz

基本形1と基本形2の組み合わせ。つまり、
左から3文字(3バイトではなく)をabcに変えて、
元ファイル名の4文字目以降を右から探して最初のxを見つけて、そこから右をxyzに変える。
元ファイル名の4文字目以降にxがないときは末尾に追加。

応用形2

*xyz*

基本形2と基本形1の組み合わせ。つまり、
元ファイル名を右から探して最初のxを見つけて、そこから右をxyzに変え、それ以降は元ファイル名を変えない。
元ファイル名にxがないときは末尾に追加。

応用形3

*.xyz

とすれば拡張子をxyzに変更する。

応用形4

*.

とすれば拡張子を削除する。

ワイルドカードの仕様としては、.に普通の文字以上の特別な意味はない。
ファイル名の仕様として、最後の.は削除される。

このように、新ファイル名のワイルドカードと、旧ファイル名のワイルドカードは無関係です。
なので、旧ファイル名にワイルドカードを使わなくても、新ファイル名だけにワイルドカードが使えます。

結局、renameコマンドでは、ワイルドカードを使って文字位置の変更はできません。文字の削除や追加が可能なのは末尾だけです。
そういう場合は、for文とfor変数や環境変数の編集機能を使います。

2008年8月21日 (木)

システム標準のVBSファイルをCMD化する。

c:\windows\system32には、システム標準のVBSファイルがあります。

dir c:\windows\system32\*.vbs

これらは、cscript.exeで、フルパス指定で、呼ばねばならず、使い勝手が悪い。

cscript.exe c:\windows\system32\prnjobs.vbs -l

そこで、

hoge.cmd
---
for %%1 in ("c:\windows\system32\*.vbs") do echo @cscript.exe //nologo "%%~dpn0.vbs" %%* >>"%%~dpn1.cmd"
---
を実行すると、それぞれのVBSファイルに対応して、ラッパーのCMDファイルを作ります。

例えば、prnjobs.vbsには、prnjobs.cmd
---
@cscript.exe //nologo "%~dpn0.vbs" %*
---
中身は同じで、名前(ベース名)で対応します。

その結果、

prnjobs -l

だけで呼べるようになります。

バッチファイルの中では、

call prnjobs -l

また、c:\windows\system32以外のPATHも、同じフォルダに作れば同様です。

2008年8月17日 (日)

スクリプトから.NETのSystem.IO.StringWriterのWriteLine()を使う。

スクリプトから.NET Framework version 2.0のSystem.Text.StringBuilderのAppendLine()は、なぜか使えません。

しかし、System.IO.StringWriterのWriteLine()なら使えます。

Set sw=CreateObject("System.IO.StringWriter")
sw.WriteLine_13 "aaa"
sw.WriteLine_13 "bbb"
MsgBox sw.GetStringBuilder.ToString()

オーバロードは以下。

WriteLine()
WriteLine_2(Char value)
WriteLine_3(Char[] buffer)
WriteLine_4(Char[] buffer, Int32 index, Int32 count)
WriteLine_5(Boolean value)
WriteLine_6(Int32 value)
WriteLine_7(UInt32value)
WriteLine_8(Int64 value)
WriteLine_9(UInt64 value)
WriteLine_10(Single value)
WriteLine_11(Double value)
WriteLine_12(Decimal value)
WriteLine_13(String value)
WriteLine_14(Object value)
WriteLine_15(String format, Object arg0)
WriteLine_16(String format, Object arg0, Object arg1)
WriteLine_17(String format, Object arg0, Object arg1, Object arg2)
WriteLine_18(String format, Params Object[] arg)

2008年8月16日 (土)

スクリプトから.NETの書式指定を使って、Format()関数を代替する。

スクリプトから.NETのFormat()は使えませんが、StringBuilderのAppendFormat()なら使えます。

例えば、数字を桁区切りして、右寄せ(左埋め)するとか。

Function LPadNum(n,m)
Dim sb
Set sb=CreateObject("System.Text.StringBuilder")
sb.AppendFormat "{0," & m & ":n0}",n
LPadNum=sb.ToString()
End Function

2008年8月15日 (金)

スクリプトから.NETを使って、右寄せ(左埋め)、LPad()、PadLeft()関数を代替する。

スクリプトから.NETのString型のPadLeft()は使えませんが、StringBuilderのAppendFormat()なら使えます。

Function LPad(s,n)
Dim sb
Set sb=CreateObject("System.Text.StringBuilder")
sb.AppendFormat "{0," & n & "}",s
LPad=sb.ToString()
End Function

2008年8月14日 (木)

スクリプトから.NETを使って、文字の繰り返し関数を代替する。

文字の繰り返し関数は、VBScriptでは、String(n,c)ですが、.NETでは、String型のコンストラクタに。

このように、.NETでは、従来、言語側に実装されて来た機能が、.NET側に移っています。

なので、VBScriptやJScriptから.NETに実装された機能が利用できます。

スクリプトから.NETのString型のコンストラクタは使えませんが、StringBuilderのAppend()なら使えます。

Function Strings(n,c)
Dim sb
Set sb=CreateObject("System.Text.StringBuilder")
sb.Append Asc(c),n
Strings=sb.ToString()
End Function

2008年8月13日 (水)

地域と言語の設定に依存しない数字の桁区切り(VBScript)

同様に、VBScriptで、符号や少数点のない整数の場合、

Function NumberGroup(n,g)
Dim s
Dim a
Dim k
s=CStr(n)
ReDim a((Len(s)+g-1) \ g -1)
k=Len(s) Mod g
If k Then a(0)=Left(s,k)
For k=k+1 To Len(s) Step g
  a((k+g-2) \ g)=Mid(s,k,g)
Next
NumberGroup=Join(a,",")
End Function

2008年8月12日 (火)

地域と言語の設定に依存しない数字の桁区切り(JScript)

数字の桁区切りは、VBScriptのFormatNumber()やJScriptのtoLocaleString()で可能ですが、これらは地域と言語の設定に依存します。

依存しないようにするには自作するしかありません。

符号や少数点のない整数の場合、

function NumberGroup(n,g){
  var s=n.toString();
  var a=new Array();
  var k=s.length%g;
  if(k) a.push(s.substr(0,k));
  for(k=k;k<s.length;k+=3) a.push(s.substr(k,3));
  return a.join(",");
}

2008年8月11日 (月)

JScriptで、右寄せ(左埋め)、LPad()、PadLeft()関数は?

function LPad(s,n,c){
  return new Array(Math.max(n-s.length,0)+1).join(c)+s;
}

2008年8月10日 (日)

VBScriptで、右寄せ(左埋め)、LPad()、PadLeft()関数は?

OracleにはLPAD()、.NETには、PadLeft()がありますが、VBScript、VB6、VBAでは?

Function LPad(s,n,c)
Dim m
m=n-Len(s)
If m<0 Then m=0
LPad=String(m,c) & s
End Function

2008年8月 9日 (土)

JScriptで、文字、文字列の繰り返し関数は?

VBScriptで、文字の繰り返し関数はString(n,c)ですが、JScriptは?

function string(n,c){
  return new Array(n+1).join(c);
}

2008年8月 6日 (水)

Windows Media エンコーダを使って、DVD-VideoからWMVへ変換する。(その2)

TV録画したDVD-VideoモードのDVDから、.VOBファイルを、Zune用に[Windows Media Video 9]の.WMVファイルに変換します。

CVRで、

for %%1 in ("D:\VIDEO_TS\*.VOB") do ^
cscript.exe "C:\Program Files\Windows Media Components\Encoder\WMCmd.vbs" ^
-input %%1 ^
-output ".\%%~n1.wmv" ^
-a_setting 128_44_2 ^
-v_bitrate 736000 -v_width 320 -v_height 240 -v_preproc 1 -v_framerate 30 -v_performance 0

または、VBRで、

for %%1 in ("D:\VIDEO_TS\*.VOB") do ^
cscript.exe "C:\Program Files\Windows Media Components\Encoder\WMCmd.vbs" ^
-input %%1 ^
-output ".\%%~n1.wmv" ^
-a_setting 128_44_2 ^
-v_mode 4 -v_bitrate 736000 -v_peakbitrate 1500000 -v_width 320 -v_height 240 -v_preproc 1 -v_framerate 30 -v_performance 0

この変換時間は、-v_performanceで大きく変わります。
最短の -v_performance 0 では、変換時間が、録画時間の
CVRで、30%
VBRで、50%
くらいです。

また、このように、最初から[Windows Media Video 9]にしておけば、Syncがコピーで済みます。

2008年8月 4日 (月)

フォルダサイズを一覧表示する。

どのフォルダのサイズが大きいかを調べます。

FolderList.CMD フォルダ

@if(0)==(0) ECHO OFF
CScript.exe //NoLogo //E:JScript "%~f0" %*
GOTO :EOF
@end
var fso=new ActiveXObject("Scripting.FileSystemObject");
var Folder=fso.GetFolder(WScript.Arguments.Count()?WScript.Arguments.Item(0):".");
var TotalSize=0;
for(var e=new Enumerator(Folder.SubFolders);!e.atEnd();e.moveNext()){
  var f=e.item();
  if(f.Name!="System Volume Information"){
    var Size=f.Size;
    WScript.Echo(LPad(Size,15),f.Name);
    TotalSize+=Size;
  }
}
for(var e=new Enumerator(Folder.Files);!e.atEnd();e.moveNext()){
  var f=e.item();
  var Size=f.Size;
  WScript.Echo(LPad(Size,15),f.Name);
  TotalSize+=Size;
}
WScript.Echo(LPad(TotalSize,15),Folder.Path);
function LPad(str,n){
  str=str.toLocaleString();
  str=str.substr(0,str.length-3);
  return new Array(Math.max(n-str.length,0)+1).join(" ")+str;
}

2008年8月 3日 (日)

FileInfo.MediaInfo.VBS (その2)

頑張って、文字化けを補正しました。

FileInfo.MediaInfo.VBS メディアファイル

Option Explicit
Dim FileInfo
Dim File
Dim re
Set re=New RegExp
re.Pattern="^(  形式: )(....)$"
re.MultiLine=True
Set FileInfo=CreateObject("FileInfo.MediaInfo")
For Each File In WScript.Arguments
  FileInfo.FileName=File
  WScript.Echo re.Replace(FileInfo.MediaInfo,GetRef("replaceFunc"))
Next
Function replaceFunc(matchedString,subMatch1,subMatch2,matchPos,source)
replaceFunc=submatch1 & Chr(AscB(MidB(submatch2,1,1))) & Chr(AscB(MidB(submatch2,3,1))) & Chr(AscB(MidB(submatch2,5,1))) & Chr(AscB(MidB(submatch2,7,1)))
End Function

2008年8月 2日 (土)

FileInfo.MediaInfo.VBS

Windows Media Encoderが扱うファイルのメディア情報を表示します。

FileInfo.MediaInfo.VBS メディアファイル...

Option Explicit
Dim FileInfo
Dim File

Set FileInfo=CreateObject("FileInfo.MediaInfo")
For Each File In WScript.Arguments
  FileInfo.FileName=File
  WScript.Echo FileInfo.MediaInfo
Next

出力例
---------------------------
ファイル名: VTS_01_1.wmv

  ファイルの長さの合計: 3466.37 s

オーディオ:
  サンプリング レート (Hz): 44100
  チャンネル数: 2
  サンプルあたりのビット数: 16

ビデオ:
  幅: 320
  高さ: 240
  フレーム レート: 30.00
  形式: W??3
  ビット/ピクセル: 24
  フレーム数: 103991
---------------------------

形式の中2文字は文字化けです。(障害)
WMV3のunicodeのW_M_V_3_がW_MVV33_に化けてます。_はChrB(0)。
なので、頑張れば、補正できます。

2008年7月30日 (水)

エクスプローラで、MP4ファイルのサムネールを表示する。

エクスプローラの「表示」「縮小版」で、MP4ファイルのサムネールを表示するには、

Windows Live フォト ギャラリー (バージョン 2008)
http://www.microsoft.com/downloads/details.aspx?FamilyID=3c04d993-dbb4-4fb1-96dd-4b806a9ef1a4&displaylang=ja

をインストールするとよいようです。

ところが、エクスプローラの動作が異常に遅くなったので、我慢がならず、アンインストールしました。

サムネール表示は、我慢したほうがよさそうです。

2008年7月28日 (月)

バッチファイルで、改行をエスケープすると継続行の行頭文字もエスケープされる。

改行をエスケープして、行を継続すると、継続行の行頭の文字、例えば、次の例では、二重引用符もエスケープされます。これは「仕様」です。

for %%1 in (a) do ^
"hoge hoge.cmd" ^
%%1

そのため、"hoge hoge.cmd"が実行されず、hogeを実行しようとします。

さらに、行頭の二重引用符がエスケープされたため、行末の^が2番目の二重引用符でエスケープされます。

そのため、3行目が継続行になりません。

回避策は、

(甲)
for %%1 in (a) do "hoge hoge.cmd" ^
%%1

(乙)
for %%1 in (a) do (
"hoge hoge.cmd" ^
%%1
)

(丙)
for %%1 in (a) do ^
call "hoge hoge.cmd" ^
%%1

(丁)
for %%1 in (a) do ^
h"oge hoge.cmd" ^
%%1

2008年7月27日 (日)

IEのuserAgent文字列が肥大化する。

.NET Frameworkも、たくさんのバージョンができて、IEのuserAgent文字列がどんどん長くなります。

先日、.NET Frameworkのバージョンをインストールしたら、突然、特定のWebサーバにアクセスできなくなりました。

そんなときは、アドレスバーに、

javascript:alert(navigator.userAgent)

を入れて、IEのuserAgent文字列を確認しましょう。

おーっ、長いですね。

これらの文字列は、以下のレジストリにあります。削除しちゃいましょう。

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform]
".NET CLR 1.0.3705"=""
".NET CLR 2.0.50727"=""
".NET CLR 1.1.4322"=""
".NET CLR 3.0.04506.30"=""
".NET CLR 3.0.04506.648"=""
".NET CLR 3.5.21022"=""

2008年7月24日 (木)

スクリプトから.NETのSystem.Text.StringBuilderを使う。(その2)

.NET Framework version 2.0のAppendLine()は、使えないようです。

なので、代わりに、AppendFormat()を使います。

Set sb=CreateObject("System.Text.StringBuilder")
sb.AppendFormat "{0}" & vbCrLf,"aaa"
sb.AppendFormat "{0}" & vbCrLf,"bbb"
MsgBox sb.ToString()

Set sb=CreateObject("System.Text.StringBuilder")
sb.AppendFormat_2 "{1}{0}",vbCrLf,"aaa"
sb.AppendFormat_2 "{1}{0}",vbCrLf,"bbb"
MsgBox sb.ToString()

オーバーロードのサフィックス一覧は、

AppendFormat(String format, Object arg0)
AppendFormat_2(String format, Object arg0, Object arg1)
AppendFormat_3(String format, Object arg0, Object arg1, Object arg2)
AppendFormat_4(String format, Params Object[] args)
AppendFormat_5(IFormatProvider provider, String format, Params Object[] args)

2008年7月23日 (水)

スクリプトから使えるよう、.NETのオーバーロードのサフィックスを調べる。

例えば、System.Text.StringBuilderなら、PowerShellで、

$sb=new-object system.text.stringbuilder
get-member -inputobject $sb | format-list

その結果、

TypeName   : System.Text.StringBuilder
Name       : Append
MemberType : Method
Definition : System.Text.StringBuilder Append(Char value, Int32 repeatCount), S
             ystem.Text.StringBuilder Append(Char[] value, Int32 startIndex, In
             t32 charCount), System.Text.StringBuilder Append(String value), Sy
             stem.Text.StringBuilder Append(String value, Int32 startIndex, Int
             32 count), System.Text.StringBuilder Append(Boolean value), System
             .Text.StringBuilder Append(SByte value), System.Text.StringBuilder
              Append(Byte value), System.Text.StringBuilder Append(Char value),
              System.Text.StringBuilder Append(Int16 value), System.Text.String
             Builder Append(Int32 value), System.Text.StringBuilder Append(Int6
             4 value), System.Text.StringBuilder Append(Single value), System.T
             ext.StringBuilder Append(Double value), System.Text.StringBuilder
             Append(Decimal value), System.Text.StringBuilder Append(UInt16 val
             ue), System.Text.StringBuilder Append(UInt32 value), System.Text.S
             tringBuilder Append(UInt64 value), System.Text.StringBuilder Appen
             d(Object value), System.Text.StringBuilder Append(Char[] value)

この並びの順にサフィックスが付くようです。

2008年7月22日 (火)

スクリプトから.NETのSystem.Text.StringBuilderを使う。

Set sb=CreateObject("System.Text.StringBuilder")
sb.Append_3 "aaa"
sb.Append_3 "bbb"
MsgBox sb.ToString()

.NETのオーバーロードは、スクリプトでは自動的には解決されないので、サフィックスで区別します。

Append(Char value, Int32 repeatCount)
Append_2(Char[] value, Int32 startIndex, Int32 charCount)
Append_3(String value)
Append_4(String value, Int32 startIndex, Int32 count)
Append_5(Boolean value)
Append_6(SByte value)
Append_7(Byte value)
Append_8(Char value)
Append_9(Int16 value)
Append_10(Int32 value)
Append_11(Int64 value)
Append_12(Single value)
Append_13(Double value)
Append_14(Decimal value)
Append_15(UInt16 value)
Append_16(UInt32 value)
Append_17(UInt64 value)
Append_18(Object value)
Append_19(Char[] value)

2008年7月18日 (金)

ListVideoCodecs.VBS

cscript WMCmd.vbs -v_codecs
では、スクリプトが一部のコーデックをマスクして、すべては表示されません。

ListVideoCodecs.VBS

Option Explicit

Const WMENC_VIDEO=2

Const WMV7_FOURCC=827739479
Const WMV8_FOURCC=844516695
Const WMV9_FOURCC=861293911
Const WMS9_FOURCC=844321613
Const MP41_FOURCC=1395937357
Const UNCOMP_FOURCC=0

Const WMV7="WMV7"
Const WMV8="WMV8"
Const WMV9="WMV9"
Const WMS9="WMS9"
Const MP41="MP41"
Const UNCOMP="UNCOMP"

Dim Profile
Dim CodecName
Dim FourCC
Dim VBRMode
Dim CodecId
Dim Count
Dim dic

Set dic=CreateObject("Scripting.Dictionary")
Set Profile=CreateObject("WMENCEng.WMEncProfile2")
dic.Add dic.Count,"Video Codecs: "
For VBRMode=1 To 4
  Profile.VBRMode(WMENC_VIDEO,0)=VBRMode
  dic.Add dic.Count,Array("CBR Mode :",_
    "Peak Bit Rate-Based VBR Audio Mode :",_
    "Quality-Based VBR Audio Mode :",_
    "Bit Rate-Based VBR Audio Mode :")(VBRMode-1)
  For Count=0 To Profile.VideoCodecCount-1
    Profile.EnumVideoCodec Count,CodecName
    FourCC=Profile.GetCodecFourCCFromIndex(WMENC_VIDEO,Count)
    Select Case FourCC
      Case WMV7_FOURCC CodecId=WMV7
      Case WMV8_FOURCC CodecId=WMV8
      Case WMV9_FOURCC CodecId=WMV9
      Case WMS9_FOURCC CodecId=WMS9
      Case MP41_FOURCC CodecId=MP41
      Case UNCOMP_FOURCC CodecId=UNCOMP
      Case Else       CodecId=FourCC
    End Select
    dic.Add dic.Count,vbTab &  "[" & Count & "] "  & CodecId & " : " & CodecName
  Next
Next
WScript.Echo Join(dic.Items(),vbLf)

2008年7月16日 (水)

Windows Media エンコーダで、[Windows Media MPEG-4 Video V3]を使う。

[Windows Media エンコーダ]や[Windows Media エンコード スクリプト]や[Windows Media プロファイル エディタ]では、[Windows Media MPEG-4 Video V3]が指定できません。

しかし、既存のプロファイルで指定すれば使えます。

なので、仮にCBSのプロファイルを作成して、メモ帳で以下の2箇所を書き換えます。
  <wmmediatype subtype="{3334504D-0000-0010-8000-00AA00389B71}" 
  bicompression="MP43"

このプロファイルを[Windows Media エンコーダ]や[Windows Media プロファイル エディタ]で読み込むと、[Windows Media MPEG-4 Video V3]が現れます。

[Windows Media エンコード スクリプト]では、-loadprofileでプロファイルを指定します。

2008年7月14日 (月)

Zuneのコーデックは?

Zuneから取り出した[Zune.wmv]を見てみると、
320x240 24Bit Windows Media Video 9 29.97fps 2000.00kb/s
Windows Media Audio 9.2 44.10kHz 16Bit 2ch 192.04kb/s
が標準的なようです。

最初から、これに合わせてWMVファイルを作ればよいのですが、[Windows Media エンコーダ]で重い[Windows Media Video 9]にすると、映像がカクカクします。

なので、一旦、軽い[ISO MPEG-4 Video V1]で作ります。
320x240 24Bit ISO MPEG-4 Video V1 30.00fps 2000.00kb/s
Windows Media Audio 9.2 48.00kHz 16Bit 2ch 192.00kb/s

これをZuneにSyncすると、Sync時に[Windows Media Video 9]にエンコードし直すようで、Syncに時間が掛かります。

あるいは、これを[Windows ムービー メーカ]で作り直すと、
320x240 24Bit Windows Media Video 9 30.00fps 1900.00kb/s
Windows Media Audio 9.2 44.10kHz 16Bit 2ch 64.04kb/s
になって、Sync時はコピーだけになります。

2008年7月10日 (木)

ZuneでFM Radioを聴く。

settingsで、radioをjapanにする。

ヘッドホンのコードがアンテナになっているので、コードを伸ばして、あっちこっちに向けてみる。
でも、なかなか、入りません。
そこで、何かのACコードにヘッドホンのコードを沿わせてやると、いいみたいです。
うーん。。。

2008年7月 9日 (水)

Windows Media エンコーダを使って、DVD-VideoからWMAへ変換する。

ZuneでTV録画の音声だけを聴くには、Windows Media エンコーダを使って、DVD-Videoモードから、.VOBファイルを、.WMAファイルに変換します。

□はチェックを外す。◎はチェックする。

(0) Windows Media エンコーダを起動する。
(1)「プロパティ」ボタンを押して「セッションのプロパティ」パネルを出す。
(2)「セッションのプロパティ」の「ソース」タブで、
  「入力ソース」◎「ファイル」
  「ファイル名」「D:\VIDEO_TS\VTS_01_1.VOB」
  □「ビデオ」◎「オーディオ」
(3)「セッションのプロパティ」の「出力」タブで、
  □「エンコーダからプル」
  ◎「ファイルにエンコード」
  「ファイル名」「どこか\なにか.wma」
(4)「セッションのプロパティ」の「圧縮」タブで、「編集」ボタンを押して「独自のエンコード設定」ダイアログを出す。
(5)「独自のエンコード設定」ダイアログの「全般」タブで、
  ◎「オーディオ」「CBR」「Windows Media Audio 9.2」
(6)「独自のエンコード設定」ダイアログの「??? Kbps」タブで、
  「オーディオ形式」「192 kbps, 48 kHz, stereo CBR」
(7)「独自のエンコード設定」ダイアログの「OK」ボタン
(8)「セッションのプロパティ」の「圧縮」タブの「詳細」は、以下のとおり。
  オーディオ エンコード モード: CBR
  配信対象: 199 Kbps
  オーディオ コーデック: Windows Media Audio 9.2
  オーディオ形式: 192 kbps, 48 kHz, stereo CBR
(9)「エンコードの開始」ボタンを押す。

コマンドラインでは、

cscript.exe "C:\Program Files\Windows Media Components\Encoder\WMCmd.vbs" -input "D:\VIDEO_TS\VTS_01_1.VOB" -output "どこか\なにか.wma" -audioonly -a_setting 192_48_2

2008年7月 8日 (火)

Windows Media エンコーダを使って、DVD-VideoからWMVへ変換する。

ZuneでTV録画を見るには、Windows Media エンコーダを使って、DVD-VideoモードのDVDから、.VOBファイルを、.WMVファイルに変換します。

Windows Media エンコーダ 9 シリーズは、Windows Media ダウンロード センターから。

□はチェックを外す。◎はチェックする。

(0) Windows Media エンコーダを起動する。
(1)「プロパティ」ボタンを押して「セッションのプロパティ」パネルを出す。
(2)「セッションのプロパティ」の「ソース」タブで、
  「入力ソース」◎「ファイル」
  「ファイル名」「D:\VIDEO_TS\VTS_01_1.VOB」
(3)「セッションのプロパティ」の「出力」タブで、
  □「エンコーダからプル」
  ◎「ファイルにエンコード」
  「ファイル名」「どこか\なにか.wmv」
(4)「セッションのプロパティ」の「圧縮」タブで、「編集」ボタンを押して「独自のエンコード設定」ダイアログを出す。
(5)「独自のエンコード設定」ダイアログの「全般」タブで、
  ◎「オーディオ」「CBR」「Windows Media Audio 9.2」
  ◎「ビデオ」「CBR」「ISO MPEG-4 Video V1」
(6)「独自のエンコード設定」ダイアログの「??? Kbps」タブで、
  「オーディオ形式」「192 kbps, 48 kHz, stereo CBR」
  「ビデオ サイズ」「320」x「240」 □「ビデオ入力と同じ」
  「ビデオ ビット レート」「2000K」bps
(7)「独自のエンコード設定」ダイアログの「OK」ボタン
(8)「セッションのプロパティ」の「圧縮」タブの「詳細」は、以下のとおり。
  オーディオ エンコード モード: CBR
  ビデオ エンコード モード: CBR
  配信対象: 2201 Kbps
  オーディオ コーデック: Windows Media Audio 9.2
  オーディオ形式: 192 kbps, 48 kHz, stereo CBR
  ビデオ コーデック: ISO MPEG-4 Video V1
  ビデオ ビット レート: 2000 Kbps
  ビデオ サイズ: 320 x 240
  フレーム レート: 29.97 fps
  キー フレーム: 8 秒
  画像の品質: 60
  バッファ サイズ: 5 秒
(9)「セッションのプロパティ」の「処理」タブで、
  「ビデオ」
  □「なし」
  ◎「ノンインターレース化」
(10)「エンコードの開始」ボタンを押す。

コマンドラインでは、

cscript.exe "C:\Program Files\Windows Media Components\Encoder\WMCmd.vbs" -input "D:\VIDEO_TS\VTS_01_1.VOB" -output "どこか\なにか.wmv" -a_setting 192_48_2 -v_codec MP41 -v_bitrate 2000000 -v_width 320 -v_height 240 -v_preproc 1

2008年7月 7日 (月)

Zune使用上の注意

PC上では日本語でも表示されますが、Zune本体は日本語フォントを搭載してないので、Zune上では、日本語が□に化けます。
なので、以下のプロパティは、英字にしておきましょう。
ファイル名やフォルダ名は日本語でも構いません。

music
アーティスト artist
アルバムのタイトル album
ジャンル genre
タイトル song

video
アーティスト artist
タイトル video

プロパティの各項目は一度同名にすると、別名に分離するのが困難なので注意しましょう。
特に、タイトルは同名にならないように注意しましょう。
タイトルを同名にすると、個別に管理できなくなります。

2008年7月 4日 (金)

window.close()で確認ダイアログを出さないで閉じる。

いろいろ方法があるようですが、

window.close()

の代わりに、

window.open("about:blank","_self").close()

同様に、

window.parent.close()

window.top.close()

の代わりに、

window.open("about:blank","_parent").close()

window.open("about:blank","_top").close()

2008年7月 3日 (木)

バッチで、複数のコマンドを並列実行して、直後に一括して待ち合わせる。

コマンドライン1 >&2 | コマンドライン2 >&2 | コマンドライン3
とします。

標準出力がないなら >&2 は不要です。

notepad.exe | notepad.exe | notepad.exe
みたいに。

2008年7月 2日 (水)

バッチから非同期に起動したアプリの終了を待ち合わせる。

start notepad.exe >running.1
で、非同期に起動し、

del running.1
if exist running.1 (echo 実行中) else echo 終了済
で、実行中か終了済かを判定し、

:wait
del running.1
if exist running.1 sleep.exe 1 & goto :wait
で、待ち合わせます。

複数の場合は、running.1の数字を変えて。

sleep.exeがないときは、ping.exe localhost -n 2 で代替して。

2008年7月 1日 (火)

現在のセキュリティ設定では、このファイルをダウンロードできません。

ダウンロードできないとき、
---------------------------
セキュリティの警告
---------------------------
現在のセキュリティ設定では、このファイルをダウンロードできません。
---------------------------
OK   
---------------------------
というダイアログが出ます。

このとき、そのダイアログの後ろに、もうひとつ、
---------------------------
0% / URL - ファイル名 完了しました
---------------------------
ファイルの情報を取得しています...
URL - ファイル名
---------------------------
というダイアログが出ていることがあります。

後ろのダイアログがないときは、そのセキュリティゾーンのセキュリティレベルで、
「ファイルのダウンロード」を「無効にする」→「有効にする」
後ろのダイアログがあるときは、前者の設定に加えて、
「アプリケーションと安全でないファイルの起動」を「無効にする」→「ダイアログを表示する」

つまり、アプリケーションと安全でないファイルのダウンロードには二重に鍵が掛かっているということです。

2008年6月27日 (金)

IEをデザインモードに変える。

なぜか、IEのメニューにないので、コンテキストメニューを拡張します。

DesignMode.htm

<script>
external.menuArguments.top.document.designMode='On';
</script>

レジストリ

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\デザインモードに変更(&D)]
@="C:\\どこか\\DesignMode.htm"

逆の「ブラウズ表示」に戻すほうは、IEのコンテキストメニューにあります。

2008年6月25日 (水)

XP + IE7 で、Microsoft Internet Controls の参照設定が変です。

XP + IE6 では、Microsoft Internet Controls の参照先は、
C:\WINDOWS\system32\shdocvw.dll
ですが、IE7を入れると、
C:\WINDOWS\system32\ieframe.dll
に変わります。

ところが、その状態では、いろいろ問題が出るようです。

例えば、IE7のShell/IE分離に対応して、Shellを起こそうと、
Dim ie As SHDocVw.ShellBrowserWindow
Set ie = New SHDocVw.ShellBrowserWindow
とすると、エラーになります。

その場合、一度、Microsoft Internet Controls の参照設定を外して、
C:\WINDOWS\system32\shdocvw.dll
を参照設定し直します。

2008年6月24日 (火)

テキストファイルの行数を調べる。

よくあるのは、FSOで追加書き込みでオープンして、Lineプロパティを見るものです。

Function NumberOfLines(File)
Const forAppending=8
Dim TStream
Set TStream=CreateObject("Scripting.FileSystemObject").OpenTextFile(File,forAppending)
NumberOfLines=TStream.Line
End Function

しかし、このやり方には以下の問題があります。
出力オープンがエラーになることがある。
行数に1行の誤差が出ることがある。

なら、FSOでReadオープンしてSkipLineで数えれば、そういう問題はありません。

Function NumberOfLines(File)
Dim TStream
NumberOfLines=0
Set TStream=CreateObject("Scripting.FileSystemObject").OpenTextFile(File)
Do While Not TStream.AtEndOfStream
  TStream.SkipLine
  NumberOfLines=NumberOfLines+1
Loop
End Function

でも、性能が気になります。

じゃ、一度にすべて読めば、速い?

Function NumberOfLines(File)
Dim TStream
Set TStream=CreateObject("Scripting.FileSystemObject").OpenTextFile(File)
If Not TStream.AtEndOfStream Then TStream.ReadAll
NumberOfLines=TStream.Line+(TStream.Column=1)
End Function

今度は、メモリが気になります。

そこで、すべて読み飛ばします。

Function NumberOfLines(File)
Dim TStream
Set TStream=CreateObject("Scripting.FileSystemObject").OpenTextFile(File)
Do While Not TStream.AtEndOfStream
  TStream.Skip 1073741824
Loop
NumberOfLines=TStream.Line+(TStream.Column=1)
End Function

ところで、この TStream.Line+(TStream.Column=1) の意味は?

TStream.Line は、UBound(Split(vbLf & TStream..ReadAll,vbLf)) みたいなもので、末尾の改行の有無によって、1行多く数えます。
TStream.Column は、a=Split(vbLf & TStream..ReadAll,vbLf): Len(a(UBound(a)))+1 みたいなものです。
なので、改行の後に文字がない場合(TStream.Column=1)、1を減じます(True=-1)。

2008年6月22日 (日)

フレーム構成を表示するコンテキストメニュー拡張

フレームの構成を、

親フレームのURL
子フレームのURL
孫フレームのURL

みたいに、インデントを付けて新規のウィンドウに表示します。

frames.htm

<script language=jscript defer>
var win=window.open("about:blank","_blank");
var doc=win.document;
doc.open("text/html");
doc.writeln('<'+'dl>');
doc.writeln('<'+'dt>'+external.menuArguments.top.location+'<'+'/dt>');
subframes(external.menuArguments.top)
doc.writeln('<'+'/dl>');
doc.close();
doc.title="フレーム構成";

function subframes(frm){
  doc.writeln('<'+'dl>');
  try{
    for(var i=0; i<frm.frames.length; i++){
      doc.writeln('<'+'dt>'+frm.frames(i).location+'<'+'/dt>');
      subframes(frm.frames(i));
    }
  }catch(e){}
  doc.writeln('<'+'/dl>');
}
</script>

レジストリ

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\フレーム構成]
@="C:\\どこか\\frames.htm"