異邦人になってみた~~上海生活写真ブログ

中国上海市在住です。もう10年を超えました。休日は星空(天の川)撮影やポートレート撮影等、連休時はカメラを持って中国各地を旅行してます。最近は内モンゴル自治区によく行っています。

 このブログについて(about)
  中国旅行/観光/写真記事一覧(省別)

Excelのプロセスが閉じない?!Ruby+railsのwin32oleでハマったこと

今日はプログラムのお話です。

redmineで簡単なプラグインを作ってくれとのことでRubyなんて使った来なかったのですが、
少し触ってみました。redmineのパッケージはRubyの上にrailsというフレームワークがあってそれに乗っかっているようです。
めんどくさいですね。
一歩一歩進む感じで書いていきます。



環境周りでも相当苦戦しましたが、いざソースを書いてみるとこれまたいろいろと問題が。。。。
今回はエクセル関連のハマったことについて書いていきます。

Excelの元ファイルがあったのと、使うひとが限られているので、もうエクセルのWEB表示でいいんじゃないと考え、
とりあえず簡単なコンソールプログラムを使って、win32oleでExcelをいろいろさわってみます。
VBAだと中国語の文字を読みこむと文字が「?」になっちゃったりしていろいろ苦戦しますが、Rubyだと関係ありませんね。
まあそれなりに動いたのでWEBに持っていって動かそうとしました。

WEBに持っていった瞬間Workbooks.Openでエラーになる

普通コンソールで使用していれば問題なかったのですが、WEBに持っていった瞬間Workbooks.Openでエラーになる
これは以下のフォルダがない場合に起こるようです。

[Windows Server が 32bit の場合] か、 [Windows Server が 64bit かつ Office が 64bit の場合]
C:\Windows\System32\config\systemprofile\Desktop
Windows Server が 64bit かつ Office が 32bit の場合は以下のように設定
C:\Windows\SysWOW64\config\systemprofile\Desktop
元ソース↓
http://blog.sorceryforce.net/?p=178


この問題はフォルダを作るとあっさり解決です。

EXCEL.EXEが終了しない

そして次の問題。無事プログラムが起動しましたが、タスクマネージャーを見てみるとEXCEL.EXEが大量にwww
Excelオブジェクトは

excel = WIN32OLE.new('Excel.Application')

で作りますが、最後に必ず

excel.Close

とやらないとEXCEL.EXEというプロセスが残ったままになりますが、今まで大丈夫だったのでもう一度ソースを確認。。
ちゃんと閉じてるよね。。。ログを前後に入れて見てみましたが、excel.Closeは通過していますが、プロセスは終了していません。
いろいろ調べてみるとole_freeを入れろということなのでexcel.ole_freeを入れてみますが、これでもダメでした。
これも調べてみます。この↓のサイトに答えがあるようです。

https://docs.ruby-lang.org/ja/latest/class/WIN32OLE.html

book = excel.Workbooks.Open(filepath)
----処理
book.Close
book.ole_free
excel.Close
excel.Quit
excel.ole_free

コンソールの場合はRuby自体が終了するのでexcel.Quitで終了するようですが、WEBの場合Rubyが終了しないので思ったようにはいかないようです。
excel.Workbooks.Openしてしまうと、どこかで無名オブジェクトが作られる事になるのでしょうかね?ガーベッジコレクションにオブジェクトの終了が伝わらない感じになるようです。
ということで一つ一つフリーしないとだめなようです。

books = excel.Workbooks
book = books.Open(in_file_path)


book.Close
book.ole_free
books.ole_free
excel.Close
excel.Quit
excel.ole_free


しか~しこれだけでは駄目でした。。。
もしかしてこのへんも全て必要なのかと思い全部ワンクッションおいて見ます。

sheets = excel.Worksheets
sheet = sheets.Item("シート名")
cells = sheet.Cells
cell = cells.Item(row,col)

フリーを入れまくります。

cells.ole_free
sheet.ole_free
sheets.ole_free
cell.ole_free

こうするとエクセルのプロセスが消えました。成功です。
でもこれって。。。。。。特にcellなんてループの中で書くことが多いので毎回フリーは面倒ですね。
まあこれ以外にもいろいろと問題は出たのですが、中国語の問題とか、日付の違いとか。。まあ今度書こうと思います。

これでやってみたのですが、プログラム量が多くなると結構めんどくさいです。Renge,cells,Rows、その下の階層全てにこれをやってたら物凄い大変です。
そして繰り返し処理で別メソッドに飛ばしたりすると。。。これが原因なのか?プロセスが落ちないことがあります。
仕方ないので、無理やりですが。。。。。強制終了することにしました。
result = system("taskkill /im excel.exe /f")

全てのExcelを閉じてしまうので、WEBだと使えないかもですね。