wxRuby版Simple OLE Browser

# 最初に
ここで言う「Simple OLE Browser」というのは、RubyライブラリWin32OLEの作者でいらっしゃる助田さん作成のツールです。
オリジナルはこちらからダウンロードできます→ http://homepage1.nifty.com/markey/ruby/win32ole/

# 詳しいことは
忘れたのですが、確か使ってるRubyがMinGW版で、swinかvrubyが調達できなかった(スキル的原因で)のだったかなあ。
ともあれ、オリジナルが使えない状況に陥ったのがきっかけだったはず。
引っ掛かってるのはガワの部分なんだからガワ取りかえちゃえ、ということでこんなものを書きました。
せっかくQiitaに登録したんでついでに恥と一緒に晒してみます。

対象となるRubyとwxRubyのバージョンは

* ruby 1.9.3p429 (2013-05-15) [i386-mingw32]
* wxruby-ruby19 (2.0.1 x86-mingw32)

です。当然Windowsでしか動きませんのでご注意を。(筆者はWindows 7 Ultimate/Professional SP1 x64で動かしてます)

```ruby:wxsoleb.rbw
# -*- encoding: Windows-31J -*-

require 'wx'
require 'win32ole'

class OLEINFO
  @members = []
  def typelibs
    WIN32OLE_TYPELIB.typelibs.select{|t|
      t.name.size > 0
    }.sort {|a, b| a.name <=> b.name}
  end

  def ole_classes(tlib)
    WIN32OLE_TYPE.ole_classes(tlib).collect {|t|
      t.name
    }.sort
  end

  def ole_members(tlib, klass)
    @members = []
    k = WIN32OLE_TYPE.ole_classes(tlib).find {|t|
      t.name == klass
    }
    return [] if !k
    @members = (k.ole_methods + k.variables).sort{|m1, m2|
      m1.name <=> m2.name
    }
    (@members).collect {|m|
      m.name
    }
  end

  def ole_class_info(tlib, klass)
    k = WIN32OLE_TYPE.ole_classes(tlib).find {|t|
      t.name == klass
    }
    return "" if !k
    "#{k.ole_type}  #{k.name}\r\n" +
    "  GUID : #{k.guid}\r\n" +
    "  PROGID : #{k.progid}\r\n" +
    "  DESCRIPTION : #{k.helpstring}\r\n\r\n"
  end

  def method_info(m)
    info = ""
    info += "Event " if m.event?
    info += m.invoke_kind 
    info += " "
    info += m.return_type
    info += " "
    info += m.name
    info += "\r\n"
    info += "  Dispatch ID : #{m.dispid}\r\n"
    info += "  DESCRIPTION : #{m.helpstring}\r\n" 
    m.params.each_with_index do |param, i|
      pinfos = []
      pinfos.push "IN" if param.input?
      pinfos.push "OUT" if param.output?
      pinfos.push "OPTION" if param.optional?
      pinfo = "arg#{i+1} - #{param.ole_type} #{param.name} [#{pinfos.join(',')}]"
      pinfo += " = #{param.default}" if param.default
      info += "  #{pinfo}\r\n"
    end
    info += "\r\n"
    info += "  Event Interface : #{m.event_interface}\r\n" if m.event?
    info
  end

  def variable_info(m)
    info = "\r\n  #{m.variable_kind} #{m.ole_type} #{m.name}"
    info += " = #{m.value}" if m.value
    info
  end

  def ole_member_info(tlib, klass, member, i)  
    k = WIN32OLE_TYPE.ole_classes(tlib).find {|t|
      t.name == klass
    }
    return "" if !k
    m = (k.ole_methods + k.variables).find {|mm|
       mm.name == member
    }
    m = @members[i]
    return "" if !m
    if m.kind_of?(WIN32OLE_METHOD)
      return method_info(m)
    else
      return variable_info(m)
    end
  end
end

class SOleBApp < Wx::App
  private
  
  def on_init
    SOleBForm.new.show
  end
end

class SOleBForm < Wx::Frame
  private
  VERSION = '0.0.1a'
  
  def initialize
    super(nil, -1, "Simple OLE Browser #{VERSION}")
    @oleinfo = OLEINFO.new
    @tlibs = @oleinfo.typelibs
    
    top_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
    @lst_tlib = Wx::ListBox.new(
         self, -1, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
         nil, Wx::LB_SINGLE | Wx::LB_ALWAYS_SB)
    @lst_tlib.set(@tlibs.collect {|e| e.name.encode("UTF-8")})
    evt_listbox(@lst_tlib.get_id) {|ev| tlib_selchanged(ev) }
    top_sizer.add(@lst_tlib, 1, Wx::EXPAND | Wx::ALL, 1)
    
    middle_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
    @lst_class = Wx::ListBox.new(
         self, -1, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
         nil, Wx::LB_SINGLE | Wx::LB_ALWAYS_SB)
    evt_listbox(@lst_class.get_id) {|ev| class_selchanged(ev) }
    @lst_member =Wx::ListBox.new(
         self, -1, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
         nil, Wx::LB_SINGLE | Wx::LB_ALWAYS_SB)
    evt_listbox(@lst_member.get_id) {|ev| member_selchanged(ev) }
    middle_sizer.add(@lst_class, 1, Wx::EXPAND | Wx::ALL, 1)
    middle_sizer.add(@lst_member, 1, Wx::EXPAND | Wx::ALL, 1)
    top_sizer.add(middle_sizer, 1, Wx::EXPAND | Wx::ALL, 1)
    @info = Wx::TextCtrl.new(
         self, -1, "", Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
         Wx::TE_MULTILINE | Wx::TE_READONLY | Wx::HSCROLL)
    top_sizer.add(@info, 1, Wx::EXPAND | Wx::ALL, 1)
    set_sizer(top_sizer)
    top_sizer.set_size_hints(self)
  end
  
  def tlib_selchanged(ev)
    @lst_class.set(
      @oleinfo.ole_classes(@tlibs[ev.get_index].guid).collect {|e| e.encode("UTF-8")})
  end
  
  def class_selchanged(ev)
    @lst_member.set(@oleinfo.ole_members(
      @tlibs[@lst_tlib.get_selection].guid, @lst_class.get_string_selection).collect {|e| e.encode("UTF-8")})
    @info.set_value(@oleinfo.ole_class_info(
      @tlibs[@lst_tlib.get_selection].guid, @lst_class.get_string_selection).encode("UTF-8"))
  end
  
  def member_selchanged(ev)
    @info.set_value(
       @oleinfo.ole_class_info(
         @tlibs[@lst_tlib.get_selection].guid, @lst_class.get_string_selection).encode("UTF-8") +
       @oleinfo.ole_member_info(
         @tlibs[@lst_tlib.get_selection].guid, @lst_class.get_string_selection,
         @lst_member.get_string_selection, ev.get_index).encode("UTF-8"))
  end
  
end

SOleBApp.new.main_loop
```

# 後書き
Ruby的にもWin32OLE的にもwxRuby的にも「なんじゃこりゃ……」というようなコーディングだと思われます。使ってみようなどと言う奇特な方が居られるかどうか分かりませんがご利用の際はご注意を。
ライセンスはオリジナルにならい
> You can redistribute it under the terms specified in the COPYING file of the Ruby distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
PURPOSE.

と致します。改変もご自由に。

# 蛇足
なんてやってる内にRubyもメインが2.x系になりまして、じゃあwxRubyのサポートどうなってんのん→軽くぐぐる→ファッ!? ※1
ということで新たなガワを探す旅が始まったわけですが、その話はまたいずれ。

あ、あと起動時にめっちゃ重いので(下手すると分のオーダーで待たされる)気長に待ってやってください。

# 注
※1 ずいぶん前(2009年9月8日)に更新が止まって、wxWidgetsの最新にもRuby2.0にもついていけてないようで……