わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

getvba: VBAマクロ取得のWebインタフェース

ブラウザから,Excelマクロ有効ブック(拡張子がxlsmのファイル)をアップロードすると,そこからマクロの部分を取り出して表示する,簡単なWebアプリケーションを開発しました.初期画面は次のとおりです.

授業で提供しているファイルの一つ,CalendarMaker.xlsm*1をアップロードしてみると,次のように,マクロが表示されます.

olevba 0.51 - http://decalage.info/python/oletools
Flags        Filename                                                         
-----------  -----------------------------------------------------------------
OpX:M-S-HB-- ****
===============================================================================
FILE: ****
Type: OpenXML
-------------------------------------------------------------------------------
VBA MACRO ThisWorkbook.cls 
in file: xl/vbaProject.bin - OLE stream: u'VBA/ThisWorkbook'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO Sheet1.cls 
in file: xl/vbaProject.bin - OLE stream: u'VBA/Sheet1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO Module1.bas 
in file: xl/vbaProject.bin - OLE stream: u'VBA/Module1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Sub CalendarMaker()

       ' Unprotect sheet if had previous calendar to prevent error.
       ActiveSheet.Protect DrawingObjects:=False, Contents:=False, _
          Scenarios:=False
(略)
       Range("a2") = "Sunday"
       Range("b2") = "Monday"
       Range("c2") = "Tuesday"
       Range("d2") = "Wednesday"
       Range("e2") = "Thursday"
       Range("f2") = "Friday"
       Range("g2") = "Saturday"
(略)
MyErrorTrap:
       MsgBox "You may not have entered your Month and Year correctly." _
           & Chr(13) & "Spell the Month correctly" _
           & " (or use 3 letter abbreviation)" _
           & Chr(13) & "and 4 digits for the Year"
       MyInput = InputBox("Type in Month and year for Calendar")
       If MyInput = "" Then Exit Sub
       Resume
   End Sub

ファイル選択をせずにアップロードのボタンを押した場合や,ファイル名の終わりがxlsmでないファイルをアップロードした場合には,メッセージを表示し,マクロ取得は行いません.


2年前のことです.1年後期のプログラミング科目でVBAについて教えるようになり,演習課題の答案を提出してもらいまして,採点に苦労しそうだと感じました.Excelマクロ有効ブック(拡張子がxlsmのファイル)を1つ1つ開いて,Excelの画面と,Visual Basic Editorの画面を見るのは,相当な手間です.
何かコマンドで,xlsmファイルを与えると,マクロの部分だけを取り出して,出力してくれるものはないか…と探していると,oletoolsというのを見つけました.Pythonで書かれたツール集です.この中のolevbaが,求めているものでした.
CygwinでもLinuxでも,マクロを取り出してくれました.ただし日本語もそのままなので,コマンドの後ろに「| nkf -w」を書き,UTF-8で保存するようにしました.先にechoコマンドで,各学生の(学生番号込みの)ファイル名を出力しておくことで,一つのテキストファイルに,全員分のマクロが格納でき,これで,正味のプログラム部の採点が楽になりました.
今年度は,VBA授業の最終回に,Webアプリケーションとして提示し,各学生にアップロードしてもらって,今まで書いたマクロを,ブラウザ上で見ることができるようにしました.クリスマス前のことです.
Dockerコンテナとして稼働します.Dockerfileを含むファイル一式は,以下より入手できます.

dockerコマンドが利用可能であれば,docker pull takehiko/getvbaのコマンドで,Dockerイメージが取得できます.gitからという方は,上記,各ページに記載のコマンドをご使用ください.


Webサーバに関して,ApacheやNginxといった大がかりなものは採用せず,PythonでHTTPサーバを走らせました.Pythonというと,バージョン2と3の非互換性に配慮しないといけません.今後のことを考えて,バージョン3を,そしてDockerのベースイメージとしては,python:3.5を,採用しました.そこに,文字コードの変換にnkfを導入し,またpip3コマンドで前述のoletoolsもインストールするよう,Dockerfileを書き,ビルドしました.
Webアプリケーションの処理を担うのも,vbaというファイル(Pythonスクリプト)一つだけにしました.
なのですが,多数のエラーや,出力なしの状況から,バージョン3用のolevba3.pyでは,思うように動かないことに気づきました.
ある程度開発を進めたところで,バージョン2に戻りたくないなと思いながら,自作Dockerコンテナにbashで入り込んでみると,python2がコマンドとして使用可能となっていました.python:3.5には,バージョン3だけでなくバージョン2のPythonも,入っていたのでした.
結局,HTTPサーバやWebアプリケーションについては,バージョン3を使用し,olevbaについてはpython2でolevba.pyを,自作スクリプトvbaから呼び出す(コマンドとして実行する)こととしました.
他にも何やかやありました*2が,終わってしまうと過去のことです.個人的には,Pythonの書き方と,Dockerfileほかの公開の仕方を学ぶ機会となりました.そういった内部の話は,授業で伝えていませんが,ノウハウを積み重ねて,今の1年生が研究室選択をするときには,当研究室の基盤技術の一つにしていきたいところです.

*1:http://d.hatena.ne.jp/takehikom/20171208/1512745198

*2:subprocess.check_outputを用いて,出力をそのまま受け取ると,終了ステータスが0でない場合にはエラー終了になるため,かわりにsubprocess.Popenを用いていったんファイルに保存しました.そうして保存したファイルが,なぜか読み出せず,time.sleepを使って少し待てばよいと分かりました.待ち時間は,授業で使用のWebアプリケーションでは1秒でしたが,0.5秒まで減らしても,自分の実行環境では大丈夫でした.