Windows でバッチファイルと格闘

cuzic です。

今日はいろいろと事情があって、かなぁり久しぶりにバッチファイル(.bat のファイル)を作っていました。

私は MS-DOS => Windows3.1 => Windows95 と、正当な進化の歴史を歩んできた人間なので、もちろん昔はバッチファイルを使っていました。
(とはいえ、Windows 3.1 の頃はほとんど MS-DOS ばかり使っていたが)

EDLINvim の ex モードのようなラインエディタ)でバッチファイルなどを作っていたら、そのうち EDIT (私にとってはSEDIT だったが)のようなスクリーンエディタが登場してとても使いやすくなって感動したものです。

おっさん向けの昔話は置いておくとして、その頃は ERRORLEVEL も駆使したバッチファイルを製作していたのです。しかし、今となってみると、まったく思い出せないので、完全に1から学習するハメになっちゃいました(トホホ)。

すると、あまりにも忘れている上に、奇妙な挙動にすごくびっくりしました。

備忘録的に、バッチを書く上で大事なことを書いておきます。

  • 空白や "" のクオートはシェルではなく、実行するコマンドが解釈する
  • エスケープは ^

例:

 C:\Documents and Settings\cuzic> echo ^(^-^)
 (-)
  • ファイルを渡すときは、DOS形式のファイル名が空白が便利
○ C:\Documents and Settings\cuzic> type "C:\Documents and Settings\cuzic\ntuser.ini"
× C:\Documents and Settings\cuzic> type C:\Documents^ and^ Settings\cuzic\ntuser.ini
   空白は、type コマンドによりファイルの区切りとして解釈されます
○ C:\Documents and Settings\cuzic> type C:\Docume~1\cuzic\ntuser.ini
   DOS形式のファイル名は有効
  • 連続して、コマンドを実行するときは、& でつなげるとできる
      • && だと、前のコマンドが正常実行の場合のみ、|| だと失敗の場合のみに実行できます
  • リダイレクトの位置はいろいろ変えられるみたい
○ C:\Documents and Settings\cuzic> echo hoge > tmp
   C:\Documents and Settings\cuzic> type tmp
   hoge

○ C:\Documents and Settings\cuzic> > tmp echo hoge
   C:\Documents and Settings\cuzic> type tmp
   hoge

○ C:\Documents and Settings\cuzic> echo > tmp hoge
   C:\Documents and Settings\cuzic> type tmp
   hoge
      • 一番先頭で書くのが、列をそろえられてお洒落らしい
  • 上記から、 & を含む文字列を環境変数に格納させて、表示するのは難しい
× set satc=Sex & The City
  ( set satc=Sex ) & The City と解釈され、The コマンドが見つからないため
× set satc=Sex ^& The City
   echo %satc%
  ( echo Sex ) & The City と解釈されます
× set satc=Sex ^^& The City
  ( set satc=Sex ^ ) & The City と解釈されます
○ set satc=Sex ^^^& The City
   echo %satc%
  echo Sex ^& The City と解釈されます

(補足)
ダブルクオートがついたままになるが、

 set satc="Sex & The City"
 echo %satc%
 "Sex & The City"

とすることもできます。
どうもダブルクオートで囲まれると & の解釈は抑止されるようです。

  • start コマンドの挙動はいろいろと謎。
    • 32bit GUI アプリの場合(ほとんどのイマドキの実行ファイル)は処理終了を待たずにプロンプトに戻るが、そうでなければ(DOS コマンドの場合など)終了を待ちます
    • 空白を ^ でエスケープしても、空白を含むファイルは渡せません
    • 第1引数が "" でクオートされていると、コマンドを意図していたとしても、ウインドウタイトルとして解釈されます。
× start "C:\Program Files\Microsoft Office\OFFICE11\EXCEL.EXE"
○ start "" "C:\Program Files\Microsoft Office\OFFICE11\EXCEL.EXE"

バッチの構文解析については ここにまとまっていますが、なんともカオスすぎて意味わかりません。

More C++ Idioms

先週末は、友人と一緒に本を読んでいたのだが、その中で C++ソースコードが出てきた。

私は、オブジェクト指向C++ から学んだ世代なので C++ソースコードを読んでいると郷愁を感じる。

しかし、いまもう一度読むと C++ はなかなか難しいというか、書くべきことが多い言語だな、と思い直した。

今日、ふと C++ を勉強しなおそうと思って検索してみたら
More C++ Idioms
がみつかった。

ここには、97種類も C++ のいろいろなイディオムが紹介されている。

いろいろと見て勉強してみたが、覚えているものもあるし、知らなかったテクニックもある。ここ数年間はそれほど C++ の知識をブラッシュアップしていなかったので、最近のモダンな C++ 向けのテクニックなどについては改めて勉強になった。

とはいえ、C++ は書かなければならない量が多い。

簡単な例だが、単純な足し算を行う Functor を定義するだけで、

#include 

class plus_binded {
private:
  int #
public:
  plus_binded(int &n) : num(n) {}
  int operator()(int n) { return num+n; }
};

int main() {
  plus_binded plus = plus_binded(10);
  cout << plus(5); // 15 を表示
  return 0;
}

となる。Functor の定義だけで7行だ。コーディング規約が {} を1行にすることが認めていない場合は10行以上にもなりうる。
(もちろんこの例であれば、std::bind1st などでもっと短く実現できるが)

ほかにも Pimpl (Bridge, Handle Body とも呼ばれる)パターンを実現するために、何十行もコードを書き続ける単純作業に耐えなければならなかったりとかして、耐え難かった。
Pimpl イディオムに関連した単純作業では、Java や C# ではそもそも必要なくなり、大きく楽になったと感じたものだった。

とはいえ、C++ は昔大変お世話になった言語なので本格的に使う機会はもうないかもしれないが、C++0x など今後も新しい仕様についても追いかけていきたいと思う。

JS-WMI (2)

cuzic です。

JS-WMI というライブラリを開発していて、 WMI の Qaulifier について、ちょっと詳しくなりました。

WMI ではオブジェクト、プロパティ、メソッド、メソッドパラメータについて、付加的な情報を Qualifier で取得できます。
Qualifier は CIM Studio を使うことで、実際にどのような値が割り振られているかを確認できます。

頻繁に利用する Qualifier の中には Description というものがあります。Description から、そのプロパティ等の概要について、知ることができます。WMI の各オブジェクト等にはこの Description がかなり充実して記載されているのです。

そこで、この Description 情報を利用して、JS-WMI では各メソッド等についてコメントを作成するようにしました。

具体的には下記のとおりです。
WMI クラスとしては、Win32_Process の Terminate メソッドを例にしています。

/** METHOD NAME: Terminate
/* Terminate メソッドはプロセスとそのスレッドのすべてを強制終了します。メソッドは次のように変換される整数値を返します: 
0 - 正常に完了しました。
2 - ユーザーは要求された情報に対するアクセス権がありません。
3 - ユーザーには十分な特権がありません。
8 - 不明なエラーです。
9 - 指定されたパスは存在しません。
21 - 指定されたパラメータは無効です。
その他 - 上の一覧以外の整数値については Win32 エラー コードのドキュメントを参照してください。*/
//  (in) Reason AS uint32
/*   Reason パラメータにより、プロセスとこの呼び出しの結果強制終了したスレッドすべてに対する終了コードが指定されます。*/
    function Terminate(Reason){
        var in_params = wmiobject_.Methods_.Item("Terminate").InParameters.SpawnInstance_();
        if(Reason !== undefined ){
            in_params.Properties_.Item("Reason").Value = Reason;
        }

        var out_params = wmiobject_.ExecMethod_("Terminate", in_params);
        return WMIClass_convert_out_params(out_params);
    }

この JS-WMI で自動生成されたコードを見ることで、WMI メソッドが実際にはどのようなメソッドで、何を引数にとり、何を返すのかが JavaScript の形で簡単に理解できるようになっています。

ソースコードDoxygen 形式で出力させて、自動的にドキュメントを生成するということもできそうです。

IronRuby-PowerShell (1)

cuzic です。

ちょっと取り組んでみたら意外とあっさりとできたので、IronRuby による PowerShell のコマンドレットを操作できるようにするライブラリについて、書きます。

もうちょっと、頭の中を整理したいというか、練りたいので、 GitHub 等に公開は特にまだしていません。

たとえば、下記のスクリプトが動作します。

  [1, 2, 3, 4].powershell <

これは、PowerShell

 $square = {foreach($x in $input) { $x * $x }
 1, 2, 3, 4 | &$square

と同じ効果を持ちます。

iAsyncResult はあえて内部的に非同期的な呼び出しを使っているので必要になるおまじないです。内部での呼び出しを同期的にすれば解消するのですが、インタフェース的にどうするのかについて、考えているところです。

ほかに、WMI で Win32_Process を使って実現したような現在実行中のプロセスの一覧を出力する処理は、

  powershell("Get-Process") do |iAsyncResult, output|
    output.each do |i|
      puts i
    end
  end

のように簡潔に書けます。

このライブラリを使えば、変数名の最初の $ や 関数呼び出しのときの & など目障りな記号を使わずに手馴れた Ruby スクリプトで、PowerShell に用意されているコマンドレットを数多く利用することができます。

現在の仕様としては、

  • ブロック呼び出しで呼ばれた場市歩は、非同期処理、ブロックがあれば同期処理
  • Object クラスを拡張して、powershell メソッドを実装
  • System.Management.Automation.dll の位置はわりと決め打ち

というようなかんじになっています。

PowerShell の呼び出しに関する部分のコードについては、わずか50行ほどの記述で完結しておりますので、理解することは簡単でしょう。

このコードをベースに JScript.Net に移植することも考えたのですが、JScript.NetGenerics がサポートされていないとのことで、PowerShell との連携は不可能そうです。
# MSDN のあちこちで、「JScript では、ジェネリックな型およびメソッドは使用できません。」との記述が・・・。

smafile = ENV["PROGRAMFILES"] + %q(\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation)
require smafile

SMA = System::Management::Automation

class Object
  def powershell source
    powershell = SMA::PowerShell.create
    if block_given? then
    # ブロック引数があれば同期処理
      input = nil
      case self
      when Array
        # IronRuby では Generics を of メソッドで実現
        input = SMA::PSDataCollection.of(self.first.class).new
        each do |elem|
          input.Add elem
        end
        input.Complete
      else
        input = SMA::PSDataCollection.of(self.class).new
        # トップレベルメソッドとして呼ばれた場合
        unless self.inspect == "main" && self.class == Object then
          input.Add self
        end
        input.Complete
      end
      output = SMA::PSDataCollection.of(System::Object).new
      completed = false
      asyncCallback = proc do |iAsyncResult|
        completed = true if iAsyncResult.IsCompleted
        yield iAsyncResult, output
        powershell.Dispose if completed
      end
      at_exit do
        sleep 1 until completed
      end
      powershell.AddScript source
      powershell.BeginInvoke input, output,
        SMA::PSInvocationSettings.new,
        asyncCallback, nil
    else
    # ブロック引数がなければ同期処理
      begin
        powershell.add_script source
        if self.is_a? Array then
          return powershell.invoke(self)
        else
          return powershell.invoke([self])
        end
      ensure
        powershell.Dispose
      end
    end
  end
end

def test_gps
  powershell("Get-Process") do |iAsyncResult, output|
    output.each do |i|
      puts i
    end
  end
end

def test_gps_html
  powershell("Get-Process | ConvertTo-Html") do |iAsyncResult, output|
    output.each do |i|
      puts i
    end
  end
end

def test_square
  [1, 2, 3, 4].powershell <

JS-WMI (1)

cuzic です。

さて、前回、IronRuby-WMI を作ってみましたが、本来の趣旨としては WMI を JavaScript で操作するライブラリの作成を目標としていたのですが、脱線してできあがったものでした。

前回の記事を書いたあと、プロパティを実現する方法は JScript にはないと書いておりましたが、ふと Windows には Windows Script Component (以下、WSC)という技術があることを思い出しました。

WSC というのは、JScript または VBScript のような ActiveScript に対応した言語を利用して、COMクライアントから再利用可能にする技術です。

超簡単な例を書くと increment.wsc というファイルを下記のとおり作成すると、



 test wsc
 
 
  
   
  
 
 
 

increment_client.js というファイルで、

var comobj = GetObject("script:increment.wsc");
WScript.Echo(comobj.increment(1));

とすると、さきほど作成したコンポーネントを利用することができます。

このコンポーネントでは、上記の method の代わりに などとすることで、プロパティを定義できます。

しかしながら、利用する、WMI のクラスやオブジェクトとして、何を使うかは分かりません。WMI のクラスの数は数百個存在しているので、事前に作るのは困難です。

というわけで、JScript で利用するクラスごとに、動的に WMIクラスをラップする WSC を自動生成して、その WSC を読み込んで、WMIオブジェクトをラップしたものを順に渡すことができようなスクリプトを作成してみました。

githubJS-WMI という名前で公開してみました。

JScript には Ruby の require に相当する機能がなく、WSF(Windows スクリプトファイル)を利用することが一般的ですので、WSF で今回作成した wmi.js を利用した例を書くと、


  

というスクリプトで、「メモ帳」を起動して、すぐに終了することができます。
終了するときに、その終了したプログラムの名称(notepad.exe)とそのプログラムが動いていたコンピュータの名称とそのユーザを表示させています。

ポイントとしては、

  • WMIクラス「Win32_Process」の Create メソッドを、process.Create のように呼び出し可能
  • ExecQuery メソッドは非同期な呼び出しを行うようにラップしており、返ってきたオブジェクトを1個づつ、OnObjectReady コールバック関数に委譲。
  • ExecQuery メソッドでは、where 句だけを書けば、内部で WQL クエリを自動生成。

上記の場合は、下記の WQL を生成。

SELECT * FROM Win32_Process WHERE caption = "notepad.exe"
  • instance.Caption のように自然に WMI のプロパティにアクセスが可能
  • OutParameter を返り値として表現。GetOwner メソッドのように複数 OutParameterが存在する場合は、返り値を配列とする。

といった特徴があります。

WSC を利用している点がかなりズルをしている気がしなくもありませんが、JavaScript で WMI を利用する処理を書きやすくするという目標を達成できて満足しています。

MessagePack-JS (5)

cuzic です。

http://d.hatena.ne.jp/uupaa/20100525/1274729065
で、uupaa さんが私が作ったバージョンより大幅に高速なバージョンの MessagePack の JavaScript 実装を作成されたそうです。

私の JavaScript スキルはそれほど高くないですので、uupaa さんの実装を読んで、どのように実装すると高速化できるかについて、かなり勉強になりました。

私がソースコードを読んで理解した中で、高速化につながった理由について、効果があったであろう順に書きます。識者の方は間違っている点など指摘してもらえれば、うれしいです。

  • メソッド呼び出しが最小限。処理はできる限り直接記述する。
  • 下記の書き方のように、1番外側でメソッド定義。
function function_name(){ 
  ...
}
  • 変数宣言はメソッドの最初に必要な分を一気に実施。
  • 何度も使う定数については、事前に生成し、複数回計算は行わせない。
  • 何度も使うメソッドについても、別の変数に事前に代入し、不要な「.」演算などを避ける
  • switch-case の書き方、処理の流れをコンパクトに記述。
  • 上とつながるが、全体にソースコードが短く。処理が簡潔。

どれがどのくらい高速化に効いているかについては私にはよく分かりませんが、http://javascript.g.hatena.ne.jp/edvakf/20100604 によると、全体として FireFox の場合10倍程度高速化できているようなので、劇的に高速に動作するようになっていますね。

IronRuby-WMI (1)

cuzic です。

JavaScript の柔軟な使い方の勉強として、WMI (Windows Management Instrument)のラッパーを、Windows Script Host で実行する環境向けに作ってみようと思ってみました。

PowerShell と似たノリで、

var klass = get_wmiobject("Win32_Process");
var instances = klass.GetInstances();
forEach(instances, function(instance){ // forEach の実装は省略
  WScript.Echo(instance.Caption); // 実行中のプロセス名称を表示
});

というようなインタフェースで使えるようにできたらいいな、と思ったのだが、いろいろ調べてみるとなかなか大変であることが分かった。

例を使って説明すると、

  var klass = get_wmiobject("StdRegProv");
  HKEY_LOCAL_MACHINE = 0x80000002;
  var arrValueNames, arrValueTypes = klass.EnumValues(HKEY_LOCAL_MACHINE, "SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers");

のように、複数の返り値(ActiveX のメソッドでは out パラメータ)がある場合は、このように自然に書きたかったが、この仕様を満たすような実装は JavaScript では実現できない。

実現できない理由は、

  • instance.Caption のようなアクセスを許容するには、WMI オブジェクトを生で扱うよりない(JavaScript には C# でいうプロパティの実装手段がないため)
  • 一方、この場合の EnumValues を実現するには ActiveX を生で呼び出すのではなく、JavaScript のクラスなどでラップする必要がある
  • そもそも、JavaScript には多重代入がない。

からだ。

これらは、Ruby であれば実現できる。というわけで、ふと習作として Ruby で実現したら、どうなるかを考えて、JavaScript 版をどうするかを考え直してみたいと思った。

単純に、Ruby で実装してみても仕方がないので、最近話題になっている IronRuby で書いてみた。

http://github.com/cuzic/ironruby-wmi

require 'System';
require 'System.Management, Version=2.0.0.0, 
     Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

class String
  def underscore
    scan(/[A-Z][a-z]+/).to_a.map(&:downcase).join("_")
  end
end

module WMI
  class WMIError 

IronRubyRuby の柔軟性をどこまで引き出せるかのテストみたいな状態になってしまっているが、

  • const_missing を活用することで
 WMI::Win32_Process.get_instances

などのように即座に WMI クラスを利用可能

  • define_method も駆使して動的なメソッド定義を行い、WMI のプロパティを自然にアクセスできるように工夫
  • get_instances_async で非同期の WMI オブジェクトの取得の実現も可能に
  • get_instances_async はすべてのインスタンス取得が完了するまで、スクリプトを終了しないように at_exit で sleep する。

といった点を工夫したつもりである。

このライブラリを使うことで、

WMI::Win32_Process.get_instances_async do |process|
  puts caption
end

のような簡潔なコードで、現在実行中のプロセスの名称を非同期に取得できるようになる。

最初の目標から大きく外れ、IronRubyRuby の柔軟な機構をごりごりと使う実験みたいな例になってしまったが、define_method, instance_eval などを含むコードでもちゃんと動いてもらえて、非常に満足である。

なんとなく作ってしまったので、公開してみたが、今後の展開としては、

  • IronRuby ベースで PowerShell のラッパーとしても使えるライブラリの作成(この路線を発展)
  • IronRuby から、CRuby で動作する RubyCLR ベースに移植
  • IronRuby ではなく、JScript.NET に移植。(eval の連打での実装???)
  • 最初、本当に作りたかった WMI の JavaScript ラッパーの作成。

といった選択肢のどれかを進めていこうと思っている。

やってみて、一息ついてみると、JavaScriptJScript) で実現する場合での限界などについても理解が深まった。私がやりたいことは JScript.NET でプロパティの部分については実現可能そうだ。高速さは犠牲になるが、JScript.NET で動的なメソッド定義も実現可能だ。JScript.NET コンパイラは、.NET フレームワークがインストールされている環境であればインストールされているようなので、普通の XP パソコンであれば使えて、どこでも動くといいやすい。PowerShell についても JScript.NET であれば、自然に扱えそうである。そのため、JScript.NET への移植などを進めていくかもしれない。(実際、今後どうするかは私の気分で決まるわけだが)