一人情シスのつぶやき

名古屋の中小企業で一人情シスをしている作者が、日々の業務で思うことをつぶやきます。

C#でExcelのバージョンに依存しないCOM経由での操作

C#での社内プログラムでExcelを操作する際、大部分はClosedXMLを利用しているのだが、ActiveXを使用しているなどでうまく動作しない場合にはCOM経由で操作している。

Visual Studioで参照ツリーにExcelのCOM参照を追加するのだが、その時点でPCにインストールされているOfficeのバージョンに対応したCOMを追加する形になる。
ビルド時に参照ツリーにあるCOMを参照するため、参照追加時のPCとビルド時のPCでインストールされているOfficeのバージョンが異なると、ビルド時に警告、またはエラーとなる。

参照追加時には2013、ビルド時に2016だったためにビルド時に警告が出て、そのまま実行すると該当箇所でRuntime Errorでコケる事象が発生した。 社内には、今後2013, 2016が混在する予定のため、どちらかだけしか対応できないとなると困るので、対応方法を調査した。

参照ツリーに追加して開発する形式を事前バインディング、実行時にCOMの名前から該当のCOMを参照する形式を遅延(動的)バインディングというらしい。

  • 事前バインディング
    • Visual StudioでCOMオブジェクトの仕様を把握しているため、補完が効いて開発効率が高い
    • 型情報なども取得済みでコンパイルするため、実行速度は遅延バインディングと比較して速い
    • 使用するOfficeのバージョンを指定する必要がある。
  • 遅延バインディング
    • 使用するOfficeのバージョンを指定する必要がない
    • Visual Studioでの補完は効かず、各オブジェクト、メソッドの情報を調べながら呼び出す必要がある。大変。
    • 実行時に型チェックを行うため、遅い。実行時エラーが出る可能性も。

遅延バインディングは、各メソッドをInvokeMemberで引数を調査しながら呼び出す必要があり、とても大変。以下のサイトにこの大変さをWrapするコードが公開されていた。

Excelの参照を追加せずにExcelを使う[C#]zenmai.wordpress.com とても素晴らしいのでぜひ利用しようと考えたのだが、(当然ながら)COMオブジェクトのすべてが実装されているわけではないので不足個所を追加実装する必要があり、結構大幅な追加が必要と思われた。

で、たどり着いたのがこちらの記事。

teratail.com

なんと、dynamicという宣言に変更するだけで、ビルド時のチェックはやめて実行時に動的に呼び出してくれるとのこと。 (COMオブジェクトの生成部分は固有の書き方への変更が必要)。素晴らしい!!

実際にdyamicに変更したところ、WorkbookオブジェクトへのReleaseComObject呼び出し時にエラーが発生。

hiro-syumi.ldblog.jp こちらの記事を参照させてもらってエラー箇所のみobject型へのキャスト処理を追加したところ、問題なく動作するようになった。

このdynamicの利用だが、最初からこれを前提に行うと上記の通りVisualStudioによるサポートが効かないので開発効率はかなり落ちると思われる。今回のようにCOM参照を追加して事前バインディングで実装したうえで、dynamicに書き換えるという形が効率が良いと感じた。

Windows 2016でプログラムが起動しない

当社で利用している手形管理のシステムにて、辞書の更新をしようとしたところ、なぜかexeをクリックしても起動しない症状が発生した。

イベントログにて、以下のような内容が出力されている

ProviderName : Microsoft-Windows-Immersive-Shell
Id           : 5973
Message      : アプリ Microsoft.Windows.Apprep.ChxApp_cw5n1h2txyewy:App.AppXc99k5qnnsvxj5szemm7fp3g7y08we5vm.mca のライセンス認証がエラーで失敗しました: このアプリは、ビルトイン Administrator ではアクティブ化できません。。詳しくは、Microsoft-Windows-TWinUI/Operational ログをご覧ください。

ググっては見たが、

https://support.microsoft.com/ja-jp/help/3064045/windows-store-apps-may-not-open-and-event-id-5973-is-logged-in-the-app

のように、ストアアプリを開くときの権限の問題しかヒットしない。 エクスプローラーからダブルクリックしているのだが、ひょっとしてストアアプリ起動と認識されてしまっているのかも、と思いコマンドプロンプトで起動したところ正常に起動した。

Windows 2016の不具合なのだろうか?

thinクライアントとして使える古いPCの限界

手元に、使わなくなったノートPCが2台あったため、Windowsにリモート接続するthinクライアントとして利用してみた。 1台はメモリ512MB, もう一台は768MBでPAE非対応というところからスペックはお察し...

Linuxを探したのだが、2018/5現在、PAE非対応という時点でインストールできるディストリビューションはかなり限られる。 2,3年前はOKだったLubuntu,XubuntuですらNG。 PuppyLinuxは日本語サイトは情報が古かったため本家を見たが、PAEまたはUEFI必須のようだ... 今回はKona LinuxのLightをインストールした。

時間はかかったがインストールは成功、最初からRemminaがインストールされていたためRDP接続もできた。 ...が、遅い。キーボードの入力が画面に反映されるのにワンテンポ、タイミングによっては数秒遅れるため、作業が止まってしまう。 さすがに、この程度のスペックでは、Thinクライアントにはスペック不足ということのようだ。10年前にはストレスなく動作したはずなので、kernelが重くなったのか、Remminaが軽くないのか、RDPの通信量がそもそも少なくないのか...

少なくともメモリは2GB、CPUは2つ以上、要は無印のKona Linuxがインストールできるくらいのスペックがないと再利用すらできないことを実感した。

GitBookでフッターを変更する方法、目次にページ数を記載する方法がわからない

最近、マニュアル等を単体ではmarkdown、複数文書をまとめる必要がある場合はGitBookで作成している。

GitBookで出版、のような記事も見受けられるため、かなりのことができるのかと思っていたのだが、タイトルにある以下の2件がどうしても解決できない

  • フッターのカスタマイズ([現ページ]/[総ページ数]のような表示)
  • 目次でページ数を表示

1点目は、book.jsonにカスタマイズを加える方法、

[GitBook] GitBookから生成されるPDF・HTMLのデザインを変更する方法 - Qiita

及び、_layouts/ebook/pdf_footer.htmlを作成する方法

How to get custom footer and header for pdf (ebook) ? · Issue #1661 · GitbookIO/gitbook · GitHub

いずれもフッターは変更できなかった。

2点目は、ページ数が表示されてほしいところになぜか1.1のような表示がされてしまい、GitHubで掲載されている、表示自体を消す方法しか見つからなかった。

Conversion to PDF with calibre should show table of content with page numbers · Issue #1223 · GitbookIO/gitbook · GitHub

Windows 2016のWindows Update

今年に入って稼働を始めたWindows Server 2016で、原因不明の再起動が発生。 Hyper-Vホストでも発生するため、ゲストOSが未起動の状態で朝を迎え、利用者から問い合わせを受けていた。

原因はタイトルの通り。 信じられないのだが、Windows 2016ではどのように設定しても一度Windows Updateを動かすとactive hourの後で勝手に再起動するらしい...

Windows Server 2016 automatically restart

上記記事の最後に、荒っぽいタスクを30分おきに起動して再起動を止める、というworkaroundが紹介されていた。

Command: schtasks
Arguments: /change /tn \Microsoft\Windows\UpdateOrchestrator\Reboot /DISABLE

うちはそれほど社員数もおらず、夜間にサーバー使えないと困るという人はいないので再起動は止めず、再起動後にゲストOSを自動起動するよう設定した。 併せて、以下の記事を参考に再起動後にメール、LINE通知するようにした。

kokura.hatenadiary.jp

改めて、Windows奥が深い

特定PCから特定サーバのみホスト名でUNCアクセスできない

社内情シスには常について回る、ファイルサーバーアクセスできない問題。 私も過去何度も経験しているし、WINS, SMBなどの知識もあるのでそんなに悩むことはここのところありませんでした。

しかし、今回は様子が異なり、以下のような症状。

  • \\SVRNAME でアクセスできない。
  • \\SVRNAME.domain.name のようなFQDNでもダメ
  • 他のサーバに対しての\\SVRNAME2 のアクセスは可能。ダメなのは1台のサーバのみ(?)
  • ping SVRNAME は通る(!?)
  • \\IPアドレス であれば該当のサーバでもアクセスできる
  • NetBIOS over TCP/IPは有効になっている
  • %WINDIR%\system32\drivers\etc にあるhosts, lmhosts にエントリはない

上から見ていって、1,2番目まではよくあるNetBIOSの名前解決エラーかと思ったのだが、3,4番目あたりからどうも様子が違う...

結果は、Windowsのコンパネ-[ユーザーアカウント]-[資格情報マネージャ]に、サーバー名で認証情報が登録されており、指定されたアカウントが無効となっていたため。 こんなところで指定しなくてもADログインすれば自動で認証するので、登録情報を削除したところアクセス可能となった。

Windows は奥が深い #いい意味ではない

COM経由でのExcel操作は地獄?

VBAExcelを操作するプログラムをC#に移行。 印刷に使用するActiveXオブジェクトを含む雛形ファイルを対象とするため、Managedな操作をするClosedXMLやNPOIではうまく動作せず、Interop.Excel経由で行った。

ClosedXML等と比較して圧倒的に動作が遅いのは当然として、シートをある程度(10枚以上?)コピーするとtmpファイルを書き込めない旨のエラーが発生する。 タイミングは実施するたびに違い、運が良ければエラーが発生しないこともある。

同様のエラーを探したが、結論としてはエラー時にリトライする処理を追加するくらいしかないらしい...

answers.microsoft.com

こういった対処方法は、仕方ないとしても凹みますね。 機械があればネックとなっているActiveXオブジェクトをManagedな操作で置き換えていきたい。