ひがきの日記

2010-04-21

謎のウイルス検知が止まらない

Windows では WDS (Windows Desktop Search) を使ってメール、その他を検索している。*1

ある日、トロイの木馬を添付したメールを受け取った。
すると翌日から NOD32 (アンチウイルスソフト) が毎朝ウイルスを検知するようになった。

おそらく

  1. POP からメールを取得
  2. WDS がインデックスを作成 ...
  3. と同時に NOD32トロイの木馬を含むメールを駆除 (削除)

こうして WDS がおかしくなった模様。

かくして、毎朝 WDS が起動すると、%USERPROFILE%\...\Desktop Search\Temp にトロイの木馬付きメールを復元し、その瞬間 NOD32 が駆除するという不毛な処理を繰り返していた。

NOD32 でディスクをフルスキャンしても問題のファイルは見つからず、WDS のインデックスを生成し直しても状況は変わらない。
もちろん該当のメールは NOD32 が駆除して存在しない。
あきらめて 2 年以上放置していた。

ところが、以下の手順で解消した。

  1. PC がウイルス感染していないことを確認。
  2. LAN ケーブルを抜く。
    • とにかく外界から遮断する。
  3. NOD32 を一時的に無効にする。
  4. WDS でインデックスを再作成。

インデックスの再作成時にも作業ファイルが駆除されていたらしい。

とにかく治ってよかった。

*1Mac では Spotlight を使っている。

2010-01-22

IronRuby を使ってみた

仕事で C# を使うことになったので「Ruby on Windows」を見ながら RubyCLR を試してみた。

Rubyist Magazine出張版 Ruby on Windows

ところが Ruby 1.9.1 に対応していないらしく、require すると SyntaxError が出る。

case
when: ...

when の : をひたすら ; に置換するなどした。

それでも activesupport 絡みでエラーが出る*1ので諦めた。

そこで IronRuby ですよ

IronRubyは、.NET Framework上で動作する、マイクロソフトによるRubyの実装である。
(Wikipedia より)

http://ironruby.net/ からバイナリダウンロードして適当なディレクトリに展開。

bin/ir.exe というのが本体らしい。

$ ir
/path/to/IronRuby/bin/ir: line 5: mono: command not found

なんじゃこりゃー

$ cat /path/to/IronRuby/bin/ir
#!/usr/bin/env bash

fpath=`which $0`
fdir=`dirname $fpath`
mono $fdir/ir.exe $*

なにこれ、うける。

拡張子も指定して ir.exe を起動する。

$ ir.exe
IronRuby 0.9.3.0 on .NET 2.0.0.0
Copyright (c) Microsoft Corporation. All rights reserved.

>>>

MRIスクリプト全体をパースしてから実行するけど、IronRubyスクリプトを逐次読み込んで実行するらしい。

ir.exe に標準入力からコードを与えると対話的に実行してくれる。

>>>puts 'Hello IronRuby'
Hello IronRuby
=> nil

>>> require 'System'
=> true

>>> System::Console.WriteLine 'Hello .NET'
Hello .NET
=> nil

もうちょっと複雑なのも試してみる。

require 'System'
require 'System.Data'

con = System::Data::SqlClient::SqlConnectionStringBuilder.new
con.DataSource = '(local)'
con.UserID = 'scott'
con.Password = 'tiger'

db = System::Data::SqlClient::SqlConnection.new
db.ConnectionString = con.to_s
begin
  db.Open

  sql = db.CreateCommand
  sql.CommandText = "SELECT 'Hello SQL' FROM DUAL"
  sql.ExecuteReader.each do |rec|
    System::Console.WriteLine rec[0]
  end
ensure
  db.Close
end

#> Hello SQL

う〜ん、気持悪い。
もうちょっと Ruby らしく書けないもんだろうか。

例えば IDisposable なクラスなら C# で using が使えるように、ブロック付きメソッドで自動解放してくれるとか。

なんか .NET のクラスをそのまま見せてくれてるだけで、ちっとも嬉しくない。
Ruby っぽいラッパークラスとかないのかなぁ。

String (Ruby) と System::String (.NET) さえも区別してる。

>>> con.to_s
=> "Data Source=(local);User ID=scott;Password=tiger"

>>> con.ToString
=> 'Data Source=(local);User ID=scott;Password=tiger'

>>> con.to_s.class
=> String

>>> con.ToString.class
=> System::String

やっぱり Microsoft との間には埋められない溝があるのか知らん。

*1:MissingSourceFile って何だ?

2009-09-15

Excel をもっと Ruby らしく使いたい

Excel のワークシート名をまとめて取ろうと思った。

require 'win32ole'

excel = WIN32OLE.new 'Excel.Applicatin'
at_exit{excel.Quit}

book = excel.Workbooks.Open 'sample.xls'
book.Sheets.map(&:Name)
# ~> -:10:in `method_missing': unknown property or method: `map' (WIN32OLERuntimeError)

map なんて知らんと怒られた。

sheets = book.Sheets
sheets.each do |sheet|
  puts sheet.Name
end
# >> Sheet1
# >> Sheet2
# >> Sheet3

each はあるのになぁ。

だったら Enumerable か。

sheets.extend Enumerable
sheets.map(&:Name)      # => ["Sheets1", "Sheets2", "Sheets3"]

できた。

でも、いちいち extend するのが面倒。
オープンクラスの恩恵を受けたい。

sheets.class        # => WIN32OLE え?
book.class          # => WIN32OLE え?
excel.class         # => WIN32OLE え?
なんと、全部 WIN32OLE のインスタンスなのか。
class WIN32OLE
  include Enumerable
end

book.Sheets.map(&:Name)     # => ["Sheets1", "Sheets2", "Sheets3"]
できた。
こんなことして大丈夫なんだろうか。

2009-07-31

メモリ不足ではまった

WIN32OLE を使ってタイプライブラリからオブジェクトをもらうテストをしていた。

オブジェクトをくれる関数には 8 個の引数があり、その組み合わせは全部で 7290 通り。

arg1 = [0, 1, 2]
arg2 = [false, true, nil]
  ...
arg1.product(arg2).product ...

引数の組み合わせを生成して each でガンガン呼び出しテストをしていたら、250 回目くらいで失敗する。
何回やっても 250 回前後で謎のエラー。

WIN32OLERuntimeError を調べてみたら「メモリが不足しています。」って?
ちゃんと後始末もしてるのに?

def test_foo(argv)
  types = [VT_I4, VT_BOOL, ...]
  m = @app.ole_method('getObj')
  o = @app._invoke(m.dispid, argv, types)
  ...
ensure
  o.Close       # 後始末
end

o が Ruby からアクセスできなくなっても WIN32OLE オブジェクトの参照カウンタは減らしてくれないみたい。

GC.start したら解決した。

def test_foo(argv)
  types = [VT_I4, VT_BOOL, ...]
  m = @app.ole_method('getObj')
  o = @app._invoke(m.dispid, argv, types)
  ...
ensure
  o.Close
  GC.start      # <= これ
end

正確には、前回 test_foo が呼ばれたときのゴミを回収してるんでしょうね。

2009-07-20

タイプライブラリから配列を受け取る方法

WIN32OLE を使ってタイプライブラリから配列を受け取りたい。

配列を retval で返してくれる関数なら、Ruby からそのまま Enumerable として扱える。

ole_object.func(arg1, arg2).each do |value|
  # func が返した配列を順番に処理
end

ところが、関数に渡された引数に配列を詰めて返してくれたりするイケてないタイプライブラリが存在する。

ole_object.gunc(array)  # => true  # 成功したらしい
array                   # => nil   # なぜ?

素直に実行しても配列を返してくれない。

そんなときは WIN32OLE::ARGV を使う。

ole_object.gunc(array)
array = WIN32OLE::ARGV[0]   # gunc の 0 番目の引数

array.each do |value|
  # gunc が返した配列を順番に処理
end

VARIANT の配列も問題なし。
二次元配列は Array を要素とする Array として扱える。

ところが VARIANT 配列を要素とする VARIANT 配列を返されると、うまく扱えないみたい。*1
Array#flatten されたような状態になってしまう。惜しい。

*1:推測。本当は何が返ってきてるのか不明。