IronRubyでExcelのEnumを参照する

# 概要
IronRubyを使い、ExcelのEnumを参照する方法の紹介です。
補足としてWordのEnumとWPFのEnumについても触れます。


# 注意点
ここで紹介する方法は独自に試行錯誤してたまたまできたものです。
最適な方法ではない可能性がありますので、ご了承ください。


# 前提
RubyとExcelのマクロについてある程度知っている方向けです。
本記事のテーマはRubyやExcelマクロの文法やコーディング作法ではなく、Ruby(IronRuby)とExcelを連携させるときのテクニックです。


# 環境
Windows 8.1（64ビット）
IronRuby 1.1
Excel 2010


# とりあえず使ってみる
Enumについてとりあえず触ってみます。

## コード
サンプルコードは次の通りです。

```excel_ref_statics_enum.rb
# coding: utf-8

# requireではダメそうだったのでload_assemblyを使った
load_assembly 'Microsoft.Office.Interop.Excel'

# 名前空間が長いのでincludeを使って定数を少ない文字数で入力できるようにする
include Microsoft::Office::Interop

# Enumの表示
# "xlShiftToLeft"と表示される
puts Excel::XlDeleteShiftDirection::xlShiftToLeft
```

## 実行方法

[1] DOS窓を開き、excel_ref_statics_enum.rb`を実行します

```
> "C:\Program Files (x86)\IronRuby 1.1\bin\ir.exe" excel_ref_statics_enum.rb
```

[2] DOS窓に"xlShiftToLeft"と表示されます

```
xlShiftToLeft
```


## 解説：Enumの情報の探し方
このサンプルコードで取り上げたEnumのAPI解説ページは次の場所にあります。
https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.office.interop.excel.xldeleteshiftdirection?view=excel-pia

次の画像はAPI解説ページの一部を抜粋したものです。
![excel_api_page_an_enum.PNG](https://qiita-image-store.s3.amazonaws.com/0/304851/644325aa-a328-f101-e10f-c8a47e8df10e.png)


上のキャプチャ画像をご覧いただければ、参照するための情報がわかると思います。
・アセンブリ：Microsoft.Office.Interop.Excel（.dllを抜かした文字列を指定する）
・名前空間：Microsoft.Office::Interop::Excel（ピリオドをコロン2つに置き換える）
・Enumの型名：XlDeleteShiftDirection
・Enumの値名：xlShiftToLeftやxlShiftUp

ほかのEnumを参照したいときは、Enumの名前をGoogle等で検索して、API解説ページからロードするべきアセンブリや名前空間を見つけてください。


## 応用：Wordでの例
Wordの場合も基本的にはExcelと同じです。

````word_ref_statics_enum.rb
load_assembly 'Microsoft.Office.Interop.Word'

include Microsoft::Office::Interop

# "wdStyleBodyText3"と表示される
puts Word::WdBuiltinStyle::wdStyleBodyText3
````

## 応用：.netのEnum
WPFなどの.netの世界のEnumを参照するときは"::"ではなく"."を使う必要があります。

````dotnet_ref_statics_enum.rb
#
# 参照するEnumを提供するアセンブリ
# これを指定しないとエラーになる
# エラーメッセージの例：
#   `const_missing': uninitialized constant System::Windows::WindowState (NameError)
#
require 'PresentationFramework'


# 成功例
# "Minimized"と表示される
# 
# "::"ではなく"."である点に注意
puts System::Windows::WindowState.Minimized

# 
# 失敗例
# 
# puts System::Windows::WindowState::Minimized

# 
# 失敗例の解説
# 
# ExcelやWordではなく、WPFなどの.netの世界のEnumを参照するときは"::"だとエラーになる
# エラーメッセージの例：
#   `const_missing': uninitialized constant System::Windows::WindowState (NameError)

````

上で利用したWindowState のAPI解説ページは次の場所にあります。
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.windowstate?view=netframework-4.7.2

次の画像はAPI解説ページの一部を抜粋したものです。

![excel_api_page_dotnet_enum.PNG](https://qiita-image-store.s3.amazonaws.com/0/304851/214c316d-a634-095c-cc70-9eec0b67781a.png)

# 実際のプログラムの中で使う
こんどはEnumを実際のプログラムの中で使います。

## コード
ここではExcelマクロの定番のテクニックである、終端のセルを探すイディオムのIronRuby版を紹介します。

このコードはカーソルのある列方向と行方向の終端セルをコマンドラインに出力します。

```excel_ref_statics_max_nums.rb
# coding: utf-8

# Excelの定数を参照するために必要
load_assembly 'Microsoft.Office.Interop.Excel'

# Excelの定数の参照を楽にする
include Microsoft::Office::Interop

# 起動中のExcelのプロセスにアタッチする
begin
	excel = System::Runtime::InteropServices::Marshal::GetActiveObject("Excel.Application")
rescue
	abort "No Excel process."
end

sheet = excel.ActiveSheet


# 終端セルを得る有名なイディオム（最大の行番号）
column = excel.ActiveCell.Column
max_row = sheet.Cells(sheet.Rows.Count, column).End(Excel::XlDirection::xlUp).Row

# 見つかった終端セルをaddressを使ってわかりやすく表示
puts sheet.Cells(max_row, column).address(false, false)


# 終端セルを得る有名なイディオム（最大の列番号）
row = excel.ActiveCell.Row
max_column = sheet.Cells(row, sheet.Columns.Count).End(Excel::XlDirection::xlToLeft).Column

# 見つかった終端セルをaddressを使ってわかりやすく表示
puts sheet.Cells(row, max_column).address(false, false)

```

## 実行方法

[1] Excelを起動します

[2] 適当な場所に矩形に文字を入力します（次の図を参照。"e"の文字を矩形に入力した）

![excel_ref_statics_max_nums.PNG](https://qiita-image-store.s3.amazonaws.com/0/304851/df58d3b2-be41-b8af-8956-8dafc32b4e65.png)

また、カーソルを矩形の範囲の左上に置いてください。

[3] DOS窓を開き、` excel_ref_statics_max_nums.rb `を実行します

```
> "C:\Program Files (x86)\IronRuby 1.1\bin\ir.exe" excel_ref_statics_max_nums.rb
```

[4] 実行するとDOS窓に終端セルが表示されます

```
B11
E6
```

## 解説：数字での指定

API解説ページにはEnumの数値も書いてあります。
コードの「きれいさ」にこだわらないのであれば、数値を指定すれば十分です。

たとえば、XlDirection::xlUpは-4162で、XlDirection::xlToLeftは-4159ですので、先ほどのコードは次のように書いても動きます。
（Emumの部分が数字に変わっているところに注目）

````excel_ref_statics_max_nums02.rb
# 終端セルを得る有名なイディオム（最大の行番号）
column = excel.ActiveCell.Column
max_row = sheet.Cells(sheet.Rows.Count, column).End(-4162).Row

# 見つかった終端セルをaddressを使ってわかりやすく表示
puts sheet.Cells(max_row, column).address(false, false)


# 終端セルを得る有名なイディオム（最大の列番号）
row = excel.ActiveCell.Row
max_column = sheet.Cells(row, sheet.Columns.Count).End(-4159).Column

# 見つかった終端セルをaddressを使ってわかりやすく表示
puts sheet.Cells(row, max_column).address(false, false)
````

# WPFと組み合わせる
ここではEnumとWPFを組み合わせて使います。
名前空間に関係するちょっとした落とし穴がありますので、それを解説します。

## コード
サンプルコードは次の通りです。

````excel_ref_statics_max_nums_with_gui.rb
# coding: utf-8

require 'PresentationFramework'

include System::Windows
include System::Windows::Controls


# ExcelのEnumを参照するために必要
load_assembly 'Microsoft.Office.Interop.Excel'

# ExcelのEnumを参照を楽にする
include Microsoft::Office::Interop


# 起動中のExcelのプロセスにアタッチする
begin
	excel = System::Runtime::InteropServices::Marshal::GetActiveObject("Excel.Application")
rescue
	abort "No Excel process."
end

window = Window.new
window.title = "IronRuby"
window.Width="200"
window.Height="150"

label1 = TextBlock.new
label1.Text = "↓終端セル（最大の列番号）"

value1 = TextBlock.new

label2 = TextBlock.new
label2.Text = "↓終端セル（最大の行番号）"

value2 = TextBlock.new

button = Button.new
button.Content = "計算する"
button.Click do |sender, e|
	
	sheet = excel.ActiveSheet
	
	
	# 終端セルを得る有名なイディオム（最大の列番号）
	row = excel.ActiveCell.Row
	max_column = sheet.Cells(row, sheet.Columns.Count).End(Excel::XlDirection::xlToLeft).Column

	# 見つかった終端セルをaddressを使ってわかりやすく表示
	value1.Text = sheet.Cells(row, max_column).address(false, false)
	
	
	# 終端セルを得る有名なイディオム（最大の行番号）
	column = excel.ActiveCell.Column
	max_row = sheet.Cells(sheet.Rows.Count, column).End(Excel::XlDirection::xlUp).Row

	# 見つかった終端セルをaddressを使ってわかりやすく表示
	value2.Text = sheet.Cells(max_row, column).address(false, false)
end

panel = StackPanel.new
panel.Children.Add(label1)
panel.Children.Add(value1)
panel.Children.Add(label2)
panel.Children.Add(value2)
panel.Children.Add(button)

window.Content = panel


app = Application.new
app.run(window)
````

## 実行方法

[1] Excelを起動します

[2] 適当な場所に矩形に文字を入力します

また、カーソルを矩形の範囲の左上に置いてください。

[3] DOS窓を開き、` excel_ref_statics_max_nums_with_gui.rb `を実行します

```
> "C:\Program Files (x86)\IronRuby 1.1\bin\ir.exe" excel_ref_statics_max_nums_with_gui.rb
```

[4] 小さなウィンドウが表示されます（次の図を参照）

![excel_ref_statics_max_nums_with_gui_window.PNG](https://qiita-image-store.s3.amazonaws.com/0/304851/10e9a5fe-cd89-5a7e-f13a-854aca32ee61.png)


[5] ［計算する］ボタンを押すとウィンドウに終端セルが表示されます（次の図を参照）

![excel_ref_statics_max_nums_with_gui.PNG](https://qiita-image-store.s3.amazonaws.com/0/304851/6ef2564e-7a20-a39d-d031-93c2291edc0c.png)


## 解説
名前空間に関係する落とし穴を体験してみましょう。

` include Microsoft::Office::Interop `に加えて` include Microsoft::Office::Interop::Excel `を入力してみてください。。

変更後のコードイメージは次の通りです。★マークをつけたところが変更箇所です。

````excel_ref_statics_max_nums_with_gui02.rb
# coding: utf-8

require 'PresentationFramework'

include System::Windows
include System::Windows::Controls


# ExcelのEnumを参照するために必要
load_assembly 'Microsoft.Office.Interop.Excel'

# ExcelのEnumを参照を楽にする  ★
include Microsoft::Office::Interop
include Microsoft::Office::Interop::Excel



# 起動中のExcelのプロセスにアタッチする
begin
	excel = System::Runtime::InteropServices::Marshal::GetActiveObject("Excel.Application")
rescue
	abort "No Excel process."
end

# （以下略）
````

このコードを実行すると次のようなエラーが出ます。

```
> "C:\Program Files (x86)\IronRuby 1.1\bin\ir.exe" excel_ref_statics_max_nums_with_gui.rb
excel_ref_statics_max_nums_with_gui.rb:28: undefined method `new' for Microsoft::Office::Interop::Excel::Window:Module (NoMethodError)
```

API解説のページで調べると、”Window”だの”Application”だのといったありがちなクラス名がExcelの名前空間と.netの名前空間の両方にあることがわかります。

IronRubyのインタプリンタがどっちの"Window"なのか判別できなくなったためにエラーが出たのだと思われます。