NPOIでエクセルファイルを読み取り専用で開く


NPOIについて

NPOIはOfficeのファイルを操作できるライブラリApache POI(Java)が.NETに移植されたものです。
https://qiita.com/hukatama024e/items/37427f2578a8987645dd

.NETからOfficeファイルを扱う、というとCOMの呼び出しを使ったものが定番ですが、こちらを使うとOfficeのプログラムを介さずして直接ファイルを取り扱えるようです。
API体系もVBA(≒COM呼び出し)とは違いますがシンプルなので、VBAにあまり馴染みがない、という人にとってはこちらのほうがとっつきやすいかもしれません。

本題

エクセルワークブック(xlsx)を開くためには以下。

OpenXLSX.cs
IWorkbook workbook = WorkbookFactory.Create(xlsxFilePath);

でもこれだと、ファイルの排他ロックを取りにいってしまいます。
エクセルファイルを開いて編集しながら、一方でそれを読み込んでプログラムで処理、
みたいなことをしようにも排他ロックなので、一度エクセルを閉じて実行する必要があります。

読み取り専用モードで開くことができれば、上記の問題は解消します。
ファンクションにReadOnlyModeのような引数はないし…と諦めかけていたら、
なんと、CreateメソッドにFileStreamオブジェクトを受け取るオーバーロードがあることに気づきました。

このFileStreamを読み取り専用にできれば、問題は解決するのではなかろうか。
ということでやってみたら、解決しました。
作成したのは以下のコードです。

OpenXLSXReadOnlyMode.cs
FileStream inputStream = new FileStream(xlsxFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
IWorkbook workbook = WorkbookFactory.Create(inputStream);

これで、エクセルファイルを開いて編集状態で、
別プログラムからNPOIでデータを取得することができるようになりました。

NPOIについての雑感

VBAに慣れ親しんでいない人にとっては有用かも、と書きましたが、逆にVBAが身についている人であれば、
今は素直にCOM呼び出しでコントロールしたほうがいいかもしれません。
というのも、NPOIはOfficeのファイル仕様である「Open XML」を操作するためのライブラリという印象で、
Officeの機能そのものを持っているわけではありません。

単純なデータのIN/OUTくらいならできるのですが、たとえば数式の評価をしようと思ったら、
ちょっと大変な実装が必要です。
NPOIはXMLをただ読んでいるだけなので、数式はただの文字列として拾ってきてしまいます。

さらに数式のエラー判定(#REFとかの判定)となるともうお手上げで、
結局そのあたりはCOMの力を借りることになりました。
COMはエクセルならエクセルそのものを操作できるAPIなので、エクセルでできること(≒VBAでできること)
は基本的にはすべてできます。
たとえばNPOIがこのあたりに対応してきたとしても、やはりエクセルそのものではないので、
エクセル側の仕様が変わることによって意図しない動作をする可能性もあります。

厳密にエクセル準拠の動きをさせたいならCOM、
サクッとファイルの入出力をしたいだけならNPOI、
といった使い分けができるかもしれません。

ただ今のところ私が知る限りNPOIでできることはすべてCOMでできるので(逆はない)、
どちらかを選択するなら今はCOMかなと思います。