4.23.2012

HTA: Inconvenient of the HtmlDlgHelper.openfiledlg()'s return string

HTA: HtmlDlgHelper.openfiledlg() の戻り値をそのまま使ってはいけない

以前、HTAから「ファイルを開く」ダイアログを利用する方法を紹介した(http://mogproject.blogspot.jp/2012/04/launching-file-dialog-in-hta.html)が、
そのままでは問題があることがわかった。

MsgBox Len(HtmlDlgHelper()) のようなコードを実行すると、選択したファイルのパスに関わらず
その結果は常に 260 と表示される。(ただし、ダイアログがキャンセルされた場合は0)

この関数の結果として返る文字列は サイズ 260 の文字配列をそのままの形で受け取っているようで、
NULL文字(文字コード0)以降のメモリ内容もそのままコピーされているためだ。

あまり奇麗ではないが、対応するにはこのようなラッパーを作るしかない。

Function OpenFileDialog()
  Dim path
  path = HtmlDlgHelper.openfiledlg()
  If path <> "" Then OpenFileDialog = Left(path, InStr(path, Chr(0)) - 1)
End Function

参考:
http://cocococoa.dtiblog.com/blog-entry-19.html

4.22.2012

Calendar control in HTA

HTA: カレンダーコントロール

HTA から Microsoft Office の ActiveX コントロールを呼び出す例。(要Office環境/Office 2010を除く)

・calendar.hta

<html><!--
'*****************************************************************************
'*
'* NAME           : Simple Calendar
'*
'* DESCRIPTION    : Displays the selected date.
'*
'* VERSION HISTORY:
'* 1.0  2012-04-22  Initial release by mogproject
'*
'*****************************************************************************
--><head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>&nbsp;</title>

<script language="VBScript"><!--
'************ Initialize window size ************
resize_window 300, 300

'*****************************************************************************
'* Sub resize_window()
'*
'* Purpose: Resize a window to the specified width and height,
'*          and then set its position to center.
'* Input  : width - Sets the width of the window, in pixels (Integer)
'*          height - Sets the height of the window, in pixels (Integer)
'* Output : None
'*****************************************************************************
Sub resize_window(width, height)
  If width > screen.width Then width = screen.width
  If height > screen.height Then height = screen.height
  window.resizeTo width, height
  window.moveTo (screen.width - width) / 2, (screen.height - height) / 2
End Sub
--></script>

<HTA:APPLICATION
 ID             ="hta"
 APPLICATIONNAME="Simple Calendar"
 VERSION        ="1.0"
 ICON           =""
 BORDER         ="normal"
 BORDERSTYLE    ="normal"
 CAPTION        ="yes"
 INNERBORDER    ="yes"
 MAXIMIZEBUTTON ="yes"
 MINIMIZEBUTTON ="yes"
 SHOWINTASKBAR  ="yes"
 SINGLEINSTANCE ="yes"
 SYSMENU        ="yes"
 WINDOWSTATE    ="normal"
 SCROLL         ="no"
 SCROLLFLAT     ="yes"
 SELECTION      ="no"
 CONTEXTMENU    ="yes"
 NAVIGABLE      ="no" />

<script language="VBScript"><!--
'*****************************************************************************
'* Sub Window_OnLoad()
'*
'* Purpose: Initialization tasks.
'* Input  : None
'* Output : None
'*****************************************************************************
Sub Window_OnLoad()
  document.title =  hta.applicationName & " v" & hta.version
  Calendar_AfterUpdate
End Sub

'*****************************************************************************
'* Sub Calendar_AfterUpdate()
'*
'* Purpose: Update date string.
'* Input  : None
'* Output : None
'*****************************************************************************
Sub Calendar_AfterUpdate()
  divDate.innerHtml = Calendar.Value
End Sub
--></script>

<style type="text/css"><!--
body {
  font-family     : Verdana, Arial, Helvetica, Sans-serif;
  font-size       : 14px;
  font-weight     : normal;
  background-color: #ccffcc;
  color           : #000000;
  margin          : 0px 0px 0px 0px;
}
table {
  border-collapse: collapse;
  border-spacing : 0px;
  border-width   : 0px;
  border-style   : solid;
}
--></style>
</head>

<body>
<table width="100%">
<tr>
 <td>
  <object id="Calendar" classid="clsid:8e27c92b-1264-101c-8a2f-040224009c02"
   width="100%" height="200px"></object>
 </td>
</tr>
<tr height="40px">
 <td align="center"><div id="divDate" /></td>
</tr>
</table>
</body>
</html>

Japanese charactors get garbled in iPod

iPod で日本語の文字化け

本体を再起動すると直る。

1. HOLDをOFFにして、ONにする。
2. MENU ボタンと センターボタンを長押し。

参考:
http://support.apple.com/kb/HT1320?viewlocale=ja_JP

4.21.2012

Launching 'Open File' dialog in HTA

HTA: 「ファイルを開く」ダイアログの利用

HTA で Open File Dialog を開くのは、「HtmlDlgHelper」を使うのが一番簡単だ。

<body> タグ内部に以下の要素を追加する。

<object id=HtmlDlgHelper classid="CLSID:3050f4e1-98b5-11cf-bb82-00aa00bdce0b"
 style="width:0px; height:0px;" />

尚、<body> ではなく <head> の内部に記述した場合は、オブジェクトを利用できない。

そして、スクリプトからの呼び出し例は以下のとおり。

path = HtmlDlgHelper.openfiledlg( _
    "C:\Program Files\*.*", , _
    "すべてのファイル (*.*)|*.*|" & _
    "GIF および JPEG (*.gif, *.jpg)|*.gif;*.jpg|" & _
    "テキスト (*.txt)|*.txt|" & _
    "HTML (*.htm, *.html)|*.htm;*.html|", _
    "Open File Dialog")

※2012/04/23 利用上の注意について追記
http://mogproject.blogspot.jp/2012/04/hta-inconvenient-of-htmldlghelperopenfi.html

4.20.2012

Kindle DX Japanese font hack

Kindle DX の日本語フォントハック

Kindle DX (2.5.8) で日本語フォントが使えるようにしてみる。当然ながら At your own risk。

1. jailbreak (脱獄)

http://wiki.mobileread.com/wiki/Kindle_Font_Hack_for_all_2.5.x_Kindles
ここから「kindle-jailbreak-0.10.N.zip」をダウンロードし、解凍。

PC と Kindle を USB で繋ぐ。

Kindle のルートディレクトリに「update_jailbreak_0.10.N_dxg_install.bin」をコピー。
例えば Kindle が Fドライブに出現したら、「F:\update_jailbreak_0.10.N_dxg_install.bin」とする。
Kindle を安全に PC から取り外す。

Kindle の操作。
「Home」ボタン→「Menu」ボタン→「Settings」を選択→「Menu」ボタン→「Update Your Kindle」。

「update was not successful」エラーとともに、画面左下に「U006」と表示されれば成功。

2. フォント準備

http://wiki.mobileread.com/wiki/Kindle_Font_Hack_for_all_2.5.x_Kindles
同様に、ここから「Kindle-fonts-5.0.N-k2.zip」をダウンロードし、解凍。

さらに、適当な Unicode 対応フォントを用意する。例えば、フリーの「VLゴシックフォント」。
http://vlgothic.dicey.org/

そして、そのフォント「*.ttf」を、「kindle-fonts-5.0.N-k2\src\linkfonts\fonts」配下に存在するファイル全て(12ファイル)と同名でコピー(上書き)する。
つまり、「Mono_Bold.ttf」などのパスが読みこまれた際に、その内容を日本語フォントにすり替えるのである。

3. フォントハック!

PC と Kindle を USB で繋ぐ。

「kindle-fonts-5.0.N-k2\src\linkfonts」フォルダを丸ごと Kindle のルートディレクトリにコピー。
同様に「update_fonts_5.0.N_dxg_install.bin」を Kindle のルートディレクトリにコピー。
例えば Kindle が Fドライブに出現したら、「F:\linkfonts」「F:\update_fonts_5.0.N_dxg_install.bin」とする。
Kindle を安全に PC から取り外す。

Kindle の操作。
「Home」ボタン→「Menu」ボタン→「Settings」を選択→「Menu」ボタン→「Update Your Kindle」。

今後はアップデートに成功する。自動的に再起動が行われたら、晴れて日本語化成功である。
今後、Kindle をアップデートする際には、本ハックをアンインストールすることを忘れずに。

参考:
http://aikotobaha.blogspot.jp/2010/07/kindle-dx-graphite-255.html

4.14.2012

Will we be killed by algorithm?

人はアルゴリズムに殺されるのか?

TED (Technology, Entertainment, Design の略) という非営利の組織がある。

http://www.ted.com/
このサイトでは、「広めるに値するアイデアの共有」を目的に、多数の超一流プレゼン(いずれも20分以内!)が公開されている。

そのうちの一つ、
Kevin Slavin: How algorithms shape our world
には感銘を受けた。ノイタミナで放映されていた「C」のコンセプトを彷彿とさせるものがあった。

英語
http://www.ted.com/talks/lang/en/kevin_slavin_how_algorithms_shape_our_world.html

日本語字幕付き
http://www.ted.com/talks/lang/ja/kevin_slavin_how_algorithms_shape_our_world.html

4.13.2012

Getting started with Scala, the functional and object-oriented language

Scala の世界へ

関数型言語であり、完全にオブジェクト指向である Scala に触れてみたいと思い、インストール。

1. Java ランタイム環境の準備

Scala で書かれたアプリケーションは、全て Java プラットフォーム上(1.5以降)で動作する。

 

2. ダウンロードと展開

このページから、Scala をダウンロードすることができる。
http://www.scala-lang.org/downloads

「Current Stable Release」から適切なファイルを選べばよい。(UNIX 系は tgz ファイル、Windows は zipファイル)
また、「IzPack Installer」の jar ファイルが用意されている場合、それを使えばグラフィカルなインストーラでセットアップできる。

手元の Linux で /usr/local/lib に展開したのはこんなコマンド。

# cd /usr/local/lib
# tar zxvf path_to_the/scala-2.9.1-1.tgz

 

3. 環境変数の設定

こちらのページを参考に、環境変数「SCALA_HOME」と「PATH」を設定する。
http://www.scala-lang.org/node/166

PATH を通すのが面倒なら、/usr/bin にシンボリックリンクを張る方法でもよさそうだ。

横着なやり方

# cd /usr/local/lib/scala-2.9.1-1/bin
# for file in `ls |grep -v bat`; do ln -s `pwd`/$file /usr/bin/$file; done

 

4. Hello, world!

これで、Scala が使える状態となった。コマンドラインから「scala」を起動してみよう。
"Hello, world!" を表示してインタープリタから抜けるまでの流れはこのようになる。

$ scala
Welcome to Scala version 2.9.1-1 (OpenJDK 64-Bit Server VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.
 
scala> println("Hello, world!")
Hello, world!
 
scala> :q

JVM を起動するのに少し待たされるのはご愛嬌。
ちなみに、「scala –version」でバージョンを確認することができる。

 

5. Scala IDE for Eclipse のインストール

動作確認ができたら、やはり Eclipse 上で開発を行いたいもの。

下記のページから、Scala のバージョンに応じた適切な URL を得る。
http://scala-ide.org/download/current.html

Eclipse を起動し、メニューから「Help」-「Install New Software…」を選択。
「Work with」に調べたURL(Scala 2.9.x なら http://download.scala-ide.org/releases-29/stable/site)を入力し、
「Add…」をクリック。「Scala」などの適当な名前をつけて登録すれば、「Scala IDE for Eclipse」が出てくる。
あとは進めてゆくのみ。
※download.scala-ide.org に繋がらない(ネットワークへ到達できない)時があったが、暫く経ってリトライしたら大丈夫だった

ヒープサイズが1GB未満だと警告メッセージが出るので、eclipse.ini を編集して -Xmx の指定を 1024MB以上にする。

参考:
プログラミング言語Scala 日本語情報サイト
https://sites.google.com/site/scalajp/

4.10.2012

Using the '–mtime' option in the 'find' command

UNIX: find –mtime コマンドについて

あるディレクトリ配下でn日間更新されていないファイルを削除する場合、などのケースで
「find」コマンドの「-mtime」オプションを使うのは古来からある由緒正しき方法である。

例えばこのように。

find target -mtime +7 |xargs rm -f

このとき、n日前に更新されたファイルは削除されるのか?
やや直感的でない部分があるので整理しておく。

man ページを見たところ、処理のポイントは以下2点である。

1. 「-mtime」で得られる数値

まず、検査対象のファイルそれぞれに対して「find コマンドが実行された時刻」から「ファイルの最終更新時刻」が差し引かれた値(秒単位)が求められる。
そして、それが何日分に相当するかが算出される。
このとき 1日に満たない時間は切り捨てられる。(よって結果は必ず整数となる)

例えば、1秒前に更新されたファイルなら「0」、ちょうど3日前(3*24*60*60=259,200秒前)に更新されたファイルなら「3」、
その1秒後(現在から259,199秒前)に更新されたファイルなら「2」となる。

2. 数値の比較

そして、パラメータとして指定された値と比較される。ここで符号は次のように解釈される。

  +   : 指定された値より大きい場合にマッチ
符号なし: 指定された値と一致する場合のみマッチ
  -   : 指定された値より小さい場合にマッチ

例えば、符号を付けずに「-mtime 3」と指定した場合は上記の1.の計算結果が「3」のものだけが
find コマンドの結果として得られることとなる。
「-mtime -3」であれば「2」以下のものが、「-mtime +3」であれば「4」以上のものが抽出される。

従って、コマンドの結果は以下の図のようになる。(図で白い丸は端点を含まないこと、黒い丸は端点を含むことを表す)

image

一般に、「-mtime +n」と指定した場合、最終更新時刻がちょうど(n+1)日前よりも古いファイルが抽出される。
(秒単位でちょうど(n+1)日前のものも含む)

・実機での確認 (Solaris 11)

 ファイルの状態と現在時刻

$ ls -l
total 4
-rw-r--r-- 1 mog staff 0 2012-04-03 00:00 04030000.txt
-rw-r--r-- 1 mog staff 0 2012-04-04 00:00 04040000.txt
-rw-r--r-- 1 mog staff 0 2012-04-05 00:00 04050000.txt
-rw-r--r-- 1 mog staff 0 2012-04-06 00:00 04060000.txt
-rw-r--r-- 1 mog staff 0 2012-04-07 00:00 04070000.txt
-rw-r--r-- 1 mog staff 0 2012-04-08 00:00 04080000.txt
-rw-r--r-- 1 mog staff 0 2012-04-09 00:00 04090000.txt
-rw-r--r-- 1 mog staff 0 2012-04-10 00:00 04100000.txt
$ date
2012年04月09日 (月) 23時48分58秒 JST
$

 find コマンド実行結果

$ find *.txt -mtime +3 -ls
   235    1 -rw-r--r--   1 mog      staff           0  4月  3 00:00 04030000.txt
   234    1 -rw-r--r--   1 mog      staff           0  4月  4 00:00 04040000.txt
   184    1 -rw-r--r--   1 mog      staff           0  4月  5 00:00 04050000.txt
$ find *.txt -mtime 3 -ls
   183    1 -rw-r--r--   1 mog      staff           0  4月  6 00:00 04060000.txt
$ find *.txt -mtime -3 -ls
   134    1 -rw-r--r--   1 mog      staff           0  4月  7 00:00 04070000.txt
   186    1 -rw-r--r--   1 mog      staff           0  4月  8 00:00 04080000.txt
   190    1 -rw-r--r--   1 mog      staff           0  4月  9 00:00 04090000.txt
   198    1 -rw-r--r--   1 mog      staff           0  4月 10 00:00 04100000.txt
$

・ソースコードの確認 (OpenSolarisの場合)

 find コマンド開始時刻の取得

     if (time(&now) == (time_t)(-1)) {
        (void) fprintf(stderr, gettext("%s: time() %s\n"),
            cmdname, strerror(errno));
        exit(1);
    }

 ファイルの最終更新時刻との差分算出

        case MTIME:
             t = statb->st_mtime;
        days:
            l = (now-t)/A_DAY;
            goto num;

 結果の比較

        num:
            if (np->second.i == '+')
                val = (l > np->first.l);
            else if (np->second.i == '-')
                val = (l < np->first.l);
            else
                val = (l == np->first.l);
            break;

・未来の更新日時のファイル

最後に、やや特殊な例であるがファイルの更新日時が find コマンドの実行時刻よりも新しい場合も考えられる。
実装はカーネルに依存する部分も多いが、それらはマイナス値として正しく判定されるよう工夫がなされているようだ。

参考:
OpenSolaris の find コマンドのソース
http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/find/

GNU project 'findutils'
http://ftp.gnu.org/pub/gnu/findutils/

4.08.2012

How to define constants using variables in VBScript

VBScript: 変数や演算子を使って定数を定義する方法

VBScript では読み取り専用の変数を作るために Const ステートメントが用意されている。
しかし、そこで指定できるのはリテラル定数のみであり、このようなステートメントは許可されていない。

Const kConstant0 = 1 + 2
Const kConstant1 = "Hello, " & "World"

これは大変不便である。そこで、このような定数を定義するためのラッパーを作成した。

・ConstWrapper.vbs

Function Constant(ByVal name, ByVal value)
  Dim literal

  Select Case VarType(value)
  Case vbEmpty: literal = "Empty"
  Case vbNull : literal = "Null"
  Case vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbBoolean, _
      vbDecimal, vbByte
    literal = value
  Case vbString
    If InStr(value, vbCr) Or InStr(value, vbLf) Then
      Err.Raise 1, "Constant", _
          "ValueError: value contains a newline"
    End If
    literal = """" & Replace(value, """", """""") & """"
  Case vbDate
    literal = "#" & _
        Join(Array(Year(value), Month(value), Day(value)), "-") & " " & _
        Join(Array(Hour(value), Minute(value), Second(value)), ":") & "#"
  Case Else
    Err.Raise 2, "Constant", _
        "ValueError: type '" & TypeName(value) & "' cannot be a constant"
  End Select

  Execute "Const " & name & "=" & literal
End Function

・使用例

Constant "kConstant0", 1 + 2
Constant "kConstant1", "Hello, " & "World"

・テストコード

If WScript.FullName <> WScript.Path & "\cscript.exe" Then
  Dim args, i, shell
  args = """" & WScript.ScriptFullName & """"
  For Each i In WScript.Arguments: args = args & " """ & i & """": Next
  Set shell = CreateObject("WScript.Shell")
  shell.Run WScript.Path & "\cscript.exe //NOLOGO " & args, 1, False
  WScript.Quit
End If

Dim gCount: gCount = 0

Check "0"
Check "1"
Check "0000000000000000001"
Check "32767"        ' 2^15-1
Check "32768"        ' 2^15
Check "2147483647"   ' 2^31-1
Check "2147483648"   ' 2^31
Check "-0"
Check "-1"
Check "-32767"       ' -(2^15-1)
Check "-32768"       ' -(2^15)
Check "-2147483647"  ' -(2^31-1)
Check "-2147483648"  ' -(2^31)

Check "&O000000"       ' 0
Check "&O00000000000"  ' 0
Check "&O00000000001"  ' 1
Check "&O77777"        ' 2^15-1
Check "&O17777777777"  ' 2^31-1
Check "&O177777"       ' -1
Check "&O37777777777"  ' -1
Check "&O100001"       ' -(2^15-1)
Check "&O100000"       ' -(2^15)
Check "&O20000000001"  ' -(2^31-1)
Check "&O20000000000"  ' -(2^31)

Check "&H0000"      ' 0
Check "&H00000000"  ' 0
Check "&H00000001"  ' 1
Check "&H7fff"      ' 2^15-1
Check "&H10000"     ' 2^15
Check "&H7fffffff"  ' 2^31-1
Check "&Hffff"      ' -1
Check "&Hffffffff"  ' -1
Check "&H8001"      ' -(2^15-1)
Check "&H8000"      ' -(2^15)
Check "&H80000001"  ' -(2^31-1)
Check "&H80000000"  ' -(2^31)

Check "0."
Check "3.14159265358979"
Check "1e2"
Check "1.2e-3"

Check """"""
Check """"""""""
Check """Hello, World"""

Check "#1-2#"
Check "#01-02#"
Check "#1/2#"
Check "#1 2#"
Check "#Jan-2#"
Check "#2-Jan#"
Check "#1-2-3#"
Check "#2003-1-2#"
Check "#2-3-Jan#"
Check "#2003-2-Jan#"
Check "#4:5#"
Check "#04:05#"
Check "#4:5:6#"
Check "#04:05:06#"
Check "#1-2-3 4:5:6#"
Check "#4:5:6 1-2-3#"
Check "#2003-01-02 04:05:06#"

Check "True"
Check "False"

Check "Empty"
Check "Null"

Check "CSng(1.2)"
Check "CCur(1.2)"
Check "CByte(255)"

' Error cases
ON ERROR RESUME NEXT
Check "vbCrLf"
WScript.Echo "[Error:" & Err.Number & "]", Err.Source, Err.Description
Check "WScript.Arguments"
WScript.Echo "[Error:" & Err.Number & "]", Err.Source, Err.Description
Check "Array()"
WScript.Echo "[Error:" & Err.Number & "]", Err.Source, Err.Description
ON ERROR GOTO 0

MsgBox "Click OK to exit."


Function Check(ByVal description)
  PackString description, 24
  Execute "PackString VarType(" & description & _
      ") & "": "" & TypeName(" & description & "), 20"
  Execute "Constant ""k" & gCount & """," & description
  Execute "If IsNull(k" & gCount & _
      ") Then PackString ""null"", 24 Else PackString k" & gCount & ", 24"
  Execute "PackString VarType(k" & gCount & _
      ") & "": "" & TypeName(k" & gCount & "), 20"
  WScript.Echo
  gCount = gCount + 1
End Function

Function PackString(ByVal value, ByVal width)
  WScript.StdOut.Write value & String(width - Len(value), " ")
End Function

・出力例

0                       2: Integer          0                       2: Integer
1                       2: Integer          1                       2: Integer
0000000000000000001     2: Integer          1                       2: Integer
32767                   2: Integer          32767                   2: Integer
32768                   3: Long             32768                   3: Long
2147483647              3: Long             2147483647              3: Long
2147483648              5: Double           2147483648              5: Double
-0                      2: Integer          0                       2: Integer
-1                      2: Integer          -1                      2: Integer
-32767                  2: Integer          -32767                  2: Integer
-32768                  3: Long             -32768                  3: Long
-2147483647             3: Long             -2147483647             3: Long
-2147483648             5: Double           -2147483648             5: Double
&O000000                2: Integer          0                       2: Integer
&O00000000000           2: Integer          0                       2: Integer
&O00000000001           2: Integer          1                       2: Integer
&O77777                 2: Integer          32767                   2: Integer
&O17777777777           3: Long             2147483647              3: Long
&O177777                2: Integer          -1                      2: Integer
&O37777777777           2: Integer          -1                      2: Integer
&O100001                2: Integer          -32767                  2: Integer
&O100000                3: Long             -32768                  3: Long
&O20000000001           3: Long             -2147483647             3: Long
&O20000000000           3: Long             -2147483648             5: Double
&H0000                  2: Integer          0                       2: Integer
&H00000000              2: Integer          0                       2: Integer
&H00000001              2: Integer          1                       2: Integer
&H7fff                  2: Integer          32767                   2: Integer
&H10000                 3: Long             65536                   3: Long
&H7fffffff              3: Long             2147483647              3: Long
&Hffff                  2: Integer          -1                      2: Integer
&Hffffffff              2: Integer          -1                      2: Integer
&H8001                  2: Integer          -32767                  2: Integer
&H8000                  3: Long             -32768                  3: Long
&H80000001              3: Long             -2147483647             3: Long
&H80000000              3: Long             -2147483648             5: Double
0.                      5: Double           0                       2: Integer
3.14159265358979        5: Double           3.14159265358979        5: Double
1e2                     5: Double           100                     2: Integer
1.2e-3                  5: Double           0.0012                  5: Double
""                      8: String                                   8: String
""""                    8: String           "                       8: String
"Hello, World"          8: String           Hello, World            8: String
#1-2#                   7: Date             2012/01/02              7: Date
#01-02#                 7: Date             2012/01/02              7: Date
#1/2#                   7: Date             2012/01/02              7: Date
#1 2#                   7: Date             2012/01/02              7: Date
#Jan-2#                 7: Date             2012/01/02              7: Date
#2-Jan#                 7: Date             2012/01/02              7: Date
#1-2-3#                 7: Date             2003/01/02              7: Date
#2003-1-2#              7: Date             2003/01/02              7: Date
#2-3-Jan#               7: Date             2003/01/02              7: Date
#2003-2-Jan#            7: Date             2003/01/02              7: Date
#4:5#                   7: Date             4:05:00                 7: Date
#04:05#                 7: Date             4:05:00                 7: Date
#4:5:6#                 7: Date             4:05:06                 7: Date
#04:05:06#              7: Date             4:05:06                 7: Date
#1-2-3 4:5:6#           7: Date             2003/01/02 4:05:06      7: Date
#4:5:6 1-2-3#           7: Date             2003/01/02 4:05:06      7: Date
#2003-01-02 04:05:06#   7: Date             2003/01/02 4:05:06      7: Date
True                    11: Boolean         True                    11: Boolean
False                   11: Boolean         False                   11: Boolean
Empty                   0: Empty                                    0: Empty
Null                    1: Null             null                    1: Null
CSng(1.2)               4: Single           1.2                     5: Double
CCur(1.2)               6: Currency         1.2                     5: Double
CByte(255)              17: Byte            255                     2: Integer
vbCrLf                  8: String           [Error:1] Constant ValueError: value contains a newline
WScript.Arguments       9: Object           [Error:2] Constant ValueError: type 'Object' cannot be a constant
Array()                 8204: Variant()     [Error:2] Constant ValueError: type 'Variant()' cannot be a constant

Error型、Decimal型、非オートメーションオブジェクトは VBScript では作成が難しかったので省略。
渡された引数を一度 String型 に変換してから Execute 文を実行しているため、型が変わるケースもある。
尚、Execute 文の特性上、改行を含む文字列はどうしても定義することはできない。

Research on literal constants in VBScript

VBScript: リテラル定数にまつわる調査

VBScript において、Const ステートメントで使用できるリテラル定数は以下のように分類される。

(1) 数値

 1-1 Integer
  ・16ビットの符号付き整数 (-2^15 .. 2^15)
   例:0, 32767, -32767, 01 (先頭の0は無視される), &O10 (8進数), &H7fff (16進数), &10 (8進数と同等)
   ※-32768, &H8000 はInteger型ではない。他の言語と異なるので注意。

 1-2 Long
  ・32ビットの符号付き整数 [-2^31 .. -2^15], [2^15 .. 2^31)
   例:-2147483647, –32768, 32768, 2147483647, &H7fffffff, &Hffffffff
   ※-2147483648 は Long型でない(Double型)が、&H80000000 は Long型である。奇妙な仕様。

 1-2 Double
  ・小数点付きの数値表現
  ・科学表記 (表現可能は範囲の限界は 1e+308 程度?)
  ・Long の範囲を超えた整数
   例:0., 3.14159265358979, 1e2, 1.2e-3, 2147483648, –2147483648

(2) 文字列

 2-1 String
  ・文字列を "" で囲むと文字列定数となる
   例:""(空の文字列), """" (1個のダブルクォーテーション), "Hello, World"
   ※VBScript によって定義されている定数を、Const に再利用することはできない。
     vbCrLf (Windows の改行文字: キャリッジリターン+ラインフィード),
     vbNullChar (1個のnull文字), vbNullString (長さが0の文字列、ただし""と比較しても一致しない) など

(3) 日付

 3-1 Date
  ・フォーマットに従った日付表記を ## で囲むと日付・時刻を示す定数となる
   例:#1-2#, #01-02#, #1/2#, #1 2#, #Jan-2#, #2-Jan#, "2,January" (現在の年、1月2日 0時0分0秒)
     #1-2-3#, #2003-1-2#, #2-3-Jan#, #2003-2-Jan#, (2003年1月2日 0時0分0秒)
     #4:5#, #04:05#, #4:5 AM# (1899年12月30日 4時5分0秒)
     #4:5:6#, #04:05:06#, (1899年12月30日 4時5分6秒)
     #1-2-3 4:5:6#, #4:5:6 1-2-3#, #2003-01-02 04:05:06# (2003年1月2日 4時5分6秒)
   ※日付表記において、「-」・「/」・「,」・連続したホワイトスペース文字(全角半角空白、タブ)は同じ意味となるようだ。
   ※日付フォーマットはOSのロケール設定に依存する

(4) 真偽値

 4-1 Boolean
  ・True (=-1) または False(=0)

(5) Empty値、Null値

 5-1 Empty
  ・Empty (未初期化値)

 5-2 Null
  ・Null (無効な値)

 

・検証用コード(CheckType.vbs)

If WScript.FullName <> WScript.Path & "\cscript.exe" Then
  Dim args, i, shell
  args = """" & WScript.ScriptFullName & """"
  For Each i In WScript.Arguments: args = args & " """ & i & """": Next
  Set shell = CreateObject("WScript.Shell")
  shell.Run WScript.Path & "\cscript.exe //NOLOGO " & args, 1, False
  WScript.Quit
End If


Check "0"
Check "1"
Check "0000000000000000001"
Check "32767"        ' 2^15-1
Check "32768"        ' 2^15
Check "2147483647"   ' 2^31-1
Check "2147483648"   ' 2^31
Check "-0"
Check "-1"
Check "-32767"       ' -(2^15-1)
Check "-32768"       ' -(2^15)
Check "-2147483647"  ' -(2^31-1)
Check "-2147483648"  ' -(2^31)

Check "&O000000"       ' 0
Check "&O00000000000"  ' 0
Check "&O00000000001"  ' 1
Check "&O77777"        ' 2^15-1
Check "&O17777777777"  ' 2^31-1
Check "&O177777"       ' -1
Check "&O37777777777"  ' -1
Check "&O100001"       ' -(2^15-1)
Check "&O100000"       ' -(2^15)
Check "&O20000000001"  ' -(2^31-1)
Check "&O20000000000"  ' -(2^31)

Check "&H0000"      ' 0
Check "&H00000000"  ' 0
Check "&H00000001"  ' 1
Check "&H7fff"      ' 2^15-1
Check "&H10000"     ' 2^15
Check "&H7fffffff"  ' 2^31-1
Check "&Hffff"      ' -1
Check "&Hffffffff"  ' -1
Check "&H8001"      ' -(2^15-1)
Check "&H8000"      ' -(2^15)
Check "&H80000001"  ' -(2^31-1)
Check "&H80000000"  ' -(2^31)

Check "0."
Check "3.14159265358979"
Check "1e2"
Check "1.2e-3"

Check """"""
Check """"""""""
Check """Hello, World"""

Check "#1-2#"
Check "#01-02#"
Check "#1/2#"
Check "#1 2#"
Check "#Jan-2#"
Check "#2-Jan#"
Check "#1-2-3#"
Check "#2003-1-2#"
Check "#2-3-Jan#"
Check "#2003-2-Jan#"
Check "#4:5#"
Check "#04:05#"
Check "#4:5:6#"
Check "#04:05:06#"
Check "#1-2-3 4:5:6#"
Check "#4:5:6 1-2-3#"
Check "#2003-01-02 04:05:06#"

Check "True"
Check "False"

Check "Empty"
Check "Null"

MsgBox "Click OK to exit."


Function Check(ByVal description)
  WScript.StdOut.Write description & String(24 - Len(description), " ")
  Execute "WScript.Echo VarType(" & description & ") & "": "" & TypeName(" & description & ")"
End Function

・出力結果

0                       2: Integer
1                       2: Integer
0000000000000000001     2: Integer
32767                   2: Integer
32768                   3: Long
2147483647              3: Long
2147483648              5: Double
-0                      2: Integer
-1                      2: Integer
-32767                  2: Integer
-32768                  3: Long
-2147483647             3: Long
-2147483648             5: Double
&O000000                2: Integer
&O00000000000           2: Integer
&O00000000001           2: Integer
&O77777                 2: Integer
&O17777777777           3: Long
&O177777                2: Integer
&O37777777777           2: Integer
&O100001                2: Integer
&O100000                3: Long
&O20000000001           3: Long
&O20000000000           3: Long
&H0000                  2: Integer
&H00000000              2: Integer
&H00000001              2: Integer
&H7fff                  2: Integer
&H10000                 3: Long
&H7fffffff              3: Long
&Hffff                  2: Integer
&Hffffffff              2: Integer
&H8001                  2: Integer
&H8000                  3: Long
&H80000001              3: Long
&H80000000              3: Long
0.                      5: Double
3.14159265358979        5: Double
1e2                     5: Double
1.2e-3                  5: Double
""                      8: String
""""                    8: String
"Hello, World"          8: String
#1-2#                   7: Date
#01-02#                 7: Date
#1/2#                   7: Date
#1 2#                   7: Date
#Jan-2#                 7: Date
#2-Jan#                 7: Date
#1-2-3#                 7: Date
#2003-1-2#              7: Date
#2-3-Jan#               7: Date
#2003-2-Jan#            7: Date
#4:5#                   7: Date
#04:05#                 7: Date
#4:5:6#                 7: Date
#04:05:06#              7: Date
#1-2-3 4:5:6#           7: Date
#4:5:6 1-2-3#           7: Date
#2003-01-02 04:05:06#   7: Date
True                    11: Boolean
False                   11: Boolean
Empty                   0: Empty
Null                    1: Null
参考:
VarType 関数
http://msdn.microsoft.com/ja-jp/library/cc392346.aspx

4.04.2012

VBScript: How to get the size of an array

VBScript: 配列の要素数を取得する

ある配列が与えられたとき、その要素数(>= 0)を調べるにはどのようにしたらよいのだろうか?
UBound() 関数は、要素数が 0 の配列に対して使用すると、インデックスエラーとなってしまう。

その一つの解が For Each .. In .. ステートメントである。

Function GetArraySize(ByVal arr)
  Dim i
  For Each i In arr
    GetArraySize = UBound(arr) + 1
    Exit Function
  Next
  GetArraySize = 0
End Function

配列の要素数が 1 以上であれば、For Each .. ブロックの内部に進み、UBound() の結果 + 1 が返される。
配列の要素数が 0 ならば、For Each .. はスキップされ、0 が返される。

これを応用すれば、要素数を保持しなくても、配列の末尾に要素を追加する処理を行える。

Dim x()
AppendToArray x, 123
AppendToArray x, 456
AppendToArray x, 789
WScript.Echo x(0), x(1), x(2)  ' 123 456 789

Function AppendToArray(ByRef arr, ByVal value)
  Dim i, size: size = 0
  For Each i In arr
    size = UBound(arr) + 1
    Exit For
  Next

  ReDim Preserve arr(size)
  arr(size) = value
End Function

例外を捕捉するよりは、スマートではないだろうか。

4.01.2012

Re-launch VBScript with Cscript

VBScript: Cscript で自動的に開き直す

VBScript のスクリプトエンジンが Wscript の場合、自動的に Cscript で開き直す例。

これのような処理を最初に行うことで、わざわざデフォルトのスクリプトエンジンを変えたり
コマンドラインで Cscript を立ち上げたりすることなく、スクリプトエンジンを限定させることができる。

ただし、終了直前に入力待ちの状態(MsgBox() または WScript.StdIn.Read() など)
にしないとコンソールが終了し、内容を確認できなくなるので注意。

If WScript.FullName <> WScript.Path & "\cscript.exe" Then
  Dim args, i, shell
  args = """" & WScript.ScriptFullName & """"
  For Each i In WScript.Arguments: args = args & " """ & i & """": Next
  Set shell = CreateObject("WScript.Shell")
  shell.Run WScript.Path & "\cscript.exe //NOLOGO " & args, 1, False
  WScript.Quit
End If

For i = 1 To 10
  WScript.StdOut.Write "."
  WScript.Sleep 100
Next
WScript.Echo

MsgBox "Click OK to exit."

'WScript.Echo "Press ENTER key to exit . . ."
'WScript.StdIn.Read(1)

WScript.Arguments を継承しているのがポイント。
WScript.FullName でエンジンのフルパスを判定するのが一般的だが、たしか別の方法もあったと思う。

2012/04/04 追記

Windows XP / Windows Server 2003 では Microsoft.CmdLib オブジェクトを使う方法もあった。
詳細は %SystemRoot%\system32\cmdlib.wsc に記述されており、他にも便利な関数がいくつかある。
また、「eventquery.vbs」はこのオブジェクトを利用した処理を行なっている。

If CInt(CreateObject("Microsoft.CmdLib").checkScript()) <> 2 Then
  ' Do something ...
End If

checkScript() の戻り値は、エンジンが cscript なら "2" が、それ以外(wscript)なら "0" が
いずれも「文字列」として返される。
そのため文字列で比較するか、上記のように CInt が必要となる。

これほど素晴らしいスクリプトにもかかわらず、何故か Vista 以降では(スクリプトを手動で移植しない限りは)
使うことができない。「cmdlib.wsc」自体が採用されていないためだ。

残念ながら、ベストプラクティスには程遠いだろう。

参考:
Microsoft.CmdLib を活用して表を作る (Hey, Scripting Guy!)
http://lab.technet.microsoft.com/en-us/magazine/cc510336
http://lab.technet.microsoft.com/ja-jp/magazine/cc510336

A general-purpose HTA template

HTA テンプレート

汎用的なHTA(HTML Application)のテンプレート。

・template.hta

<html><!--
'*****************************************************************************
'*
'* NAME           :
'*
'* DESCRIPTION    :
'*
'* VERSION HISTORY:
'* 1.0  yyyy-mm-dd  Initial release by AUTHOR
'*
'*****************************************************************************
--><head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>&nbsp;</title>

<script language="VBScript"><!--
'************ Initialize window size ************
resize_window 300, 84

'*****************************************************************************
'* Sub resize_window()
'*
'* Purpose: Resize a window to the specified width and height,
'*          and then set its position to center.
'* Input  : width - Sets the width of the window, in pixels (Integer)
'*          height - Sets the height of the window, in pixels (Integer)
'* Output : None
'*****************************************************************************
Sub resize_window(width, height)
  If width > screen.width Then width = screen.width
  If height > screen.height Then height = screen.height
  window.resizeTo width, height
  window.moveTo (screen.width - width) / 2, (screen.height - height) / 2
End Sub
--></script>

<HTA:APPLICATION
 ID             ="hta"
 APPLICATIONNAME="Application"
 VERSION        ="1.0"
 ICON           =""
 BORDER         ="normal"
 BORDERSTYLE    ="normal"
 CAPTION        ="yes"
 INNERBORDER    ="yes"
 MAXIMIZEBUTTON ="yes"
 MINIMIZEBUTTON ="yes"
 SHOWINTASKBAR  ="yes"
 SINGLEINSTANCE ="yes"
 SYSMENU        ="yes"
 WINDOWSTATE    ="normal"
 SCROLL         ="no"
 SCROLLFLAT     ="yes"
 SELECTION      ="no"
 CONTEXTMENU    ="yes"
 NAVIGABLE      ="no" />

<script language="VBScript"><!--
'*****************************************************************************
'* Sub Window_OnLoad()
'*
'* Purpose: Initialization tasks.
'* Input  : None
'* Output : None
'*****************************************************************************
Sub Window_OnLoad()
  document.title =  hta.applicationName & " v" & hta.version
End Sub
--></script>

<style type="text/css"><!--
body {
  font-family     : Verdana, Arial, Helvetica, Sans-serif;
  font-size       : 14px;
  font-weight     : normal;
  background-color: #ccffcc;
  color           : #000000;
  margin          : 0px 0px 0px 0px;
}
table {
  border-collapse: collapse;
  border-spacing : 0px;
  border-width   : 0px;
  border-style   : solid;
}
--></style>
</head>

<body>
<table width="100%">
<tr height="40px">
 <td align="center">Hello, World</td>
</tr>
</table>
</body>
</html>

・ウィンドウのリサイズ(window.resizeTo)は hta:application タグの前に記述しないと、起動時に一瞬通常サイズのウィンドウが表示されてしまう。

調べてみると、色々と奥が深い。

・実行結果
image

 

Related Posts

JavaScript版 HTAテンプレート
http://mogproject.blogspot.jp/2012/11/hta-template-with-javascript.html

 

References

HTA Template Generator
http://gallery.technet.microsoft.com/scriptcenter/HTA-Template-Generator-v1-af7273b1