VS2017でOfficeToPDFをコンパイル＆実行してみた

Visual Studio 2017 (15.9.4) Community版を利用して、OfficeToPDFをビルドしたので、その時のメモを残します。

> 別のPCのVS2017 (15.9.26)で試したところ、コンパイル時にエラーになったので、その対策を加筆しています。

# 環境

* 【ビルド環境】Windows 10 Pro 1803 (64bit) / 2004 (64bit)
    * PowerShell 5.1
* Visual Studio 2017 Community (15.9.4) / (15.9.26)
    * 【ワークロード】.Netデスクトップ開発
    * 【ワークロード】Office/Sharepoint開発
    * 【機能拡張】Github Extension for Visual Studio
* 【実行環境】Windows7 32bit

[GithubのREADME.md](https://github.com/cognidox/OfficeToPDF/blob/master/README.md)にもありますが、Windows7では、[Visual Studio 2010 Tools for Office Runtime](https://www.microsoft.com/en-GB/download/details.aspx?id=48217)と[2007 Microsoft Office Add-in: Microsoft Save as PDF or XPS](http://www.microsoft.com/downloads/en/details.aspx?familyid=4d951911-3e7e-4ae6-b059-a2e79ed87041&displaylang=en)を導入しておく必要があります。

Windows10で実行する場合には、Windows7で必要だったパッケージの導入は必要ないはずです。

# 概要
他の部署からCD-Rで保存用に資料をもらったところ、サブディレクトリ(sub directory)に様々なワード(.doc)、パワーポイント(.ppt)が配置されていました。

過去に、保存されていたppt95形式のファイルが開けない問題があったりしたので、現在はポリシーとしてPDFファイルにした上でM-DISCに保存するようにしています。

バッチ的にMicrosoft Officeで作成されたファイルをPDFに変換したかったので、いろいろ探したところ[CodePlex Archiveにofficetopdf](https://archive.codeplex.com/?p=officetopdf)を見つけて試してみようと思いました。

[Cognidox](https://www.cognidox.com/)が作成し、[Githubで現在は公開されている](https://github.com/cognidox/OfficeToPDF)とのことなのですが、バイナルは配布されていない様子だったのでVisual Studio2017を使ってビルドしてみることにしました。

# Visual Studio 2017 (VS2017) の導入
公式サイトからダウンロードしたインストーラーを起動し、VS2017を導入します。

## VS2017の構成
インストール時に選択する構成は、事後でもメニューから*Visual Studio Installer*を起動することで行なえます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/9affbcf2-c946-0b7f-21c4-920428c9757e.png)


## .Netデスクトップ と Office開発用コンポーネントの選択
開発対象を選択する画面が表示されるので、*.Netデスクトップ開発*とOffice Developer Tools for Visual Studioをインストールするために、*Office/Sharepoint開発*を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/e1a938d0-eebf-e8f7-172b-bbd513f506c2.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/2f10e7d4-f928-70d9-0d8e-1e90eeb0685a.png)

## Githubに対応した機能拡張のインストール

機能拡張はInstallerではなく、通常のVS2017のツールメニューから操作をします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/b4d6d248-dc9f-3785-8eed-1bb2a4832dae.png)


Githubからコードをダウンロードする方法はいろいろありますが、VS2017上でコードをGithubからcloneできるように拡張機能を導入します。

VS2017の拡張機能を探す時には、左側のペイン(pane)で*オンライン*を選択してから、検索ウィンドウでキーワードを入力します。

Github Extension for Visual Studioはダウンロード数が上位にあるので、すぐに見つけられるはずです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/a8ea2c93-60bc-64fe-acec-0296a655d4d9.png)

インストールした後はVS2017のウィンドウを全て閉じて終了するとインストールが始まります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/880696bf-c65d-d2dc-3ff9-78f210308b05.png)


# コードのダウンロード(Clone)

Githubからソースコードをcloneし、VS2017で認識できるようにしていきます。

VS2017を起動し、ファイルメニューから*ソース管理から開く*を選択し、チームエクスプローラーを起動します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/ff029991-a3aa-b661-4650-8a90220443f6.png)


チームエクスプローラーが起動するので、コンセントのアイコンを選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/72ac7880-639d-3a84-9445-eb9da815d929.png)

画面下のローカルGitリポジトリの複製メニューからGithubのURLと適当な空のディレクトリを選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/f0dd3a71-7dc8-2d1c-bdf4-07b8f4e6de87.png)

登録された*OfficeToPDF*をダブルクリックするか、コンセントアイコンの隣りにあるホームアイコンをクリックし、チームエクスプローラーのホームを表示します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/43a78f93-886e-1d1b-8ce3-f1f1cfcd4860.png)

画面下に認識されたソリューションが表示されるため、OfficeToPDF.slnをクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/489668a3-3045-7bd7-280f-1253153d0762.png)

# ビルド

ソリューションを開いた後は、ビルドメニューから、"OfficeToPDFのビルド"を選択し、実行ファイルを生成します。

## ビルド時のエラー

VS2017 (15.9.26)でビルドすると次のようなエラーメッセージが出力されました。

```text:errorメッセージ
重大度レベル	コード	説明	プロジェクト	ファイル	行	抑制状態
警告		タイプ ライブラリ "MSHTML" のラッパー アセンブリが見つかりません。次のことを確認してください。(1) COM コンポーネントが正しく登録されている。(2) ターゲット プラットフォームのビットが COM コンポーネントと同じである。たとえば、COM コンポーネントが 32 ビットの場合、64 ビットのターゲット プラットフォームは使用できません。	OfficeToPDF			
```

対応方法は、ソリューションエクスプローラーの**参照**を確認すると、MSHTMLに警告マークが付いているのが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78296/a6b38786-4fc6-6d73-7c2d-cab93fd5cfc0.png)

VS2019では問題がなかったので調べてみると、当初は一旦削除し、Microsoft.mshtml.dllを参照に追加すれば良いと思っていたのですが、regasmを再度実行するのが適切な対応策だと考え直しました。

詳細は下記のブログに記載されていて、他にも同様の記述があるようです。

* [FIX for Wrapper assembly for type library “MSHTML” cannot be found after Windows 10 upgrade.](https://akshaybarve.wordpress.com/2017/01/23/fix-for-wrapper-assembly-for-type-library-mshtml-cannot-be-found-after-windows-10-upgrade/)

管理者権限で起動したPowerShellやコマンドプロンプト内部で、Microsoft.mshtml.dllのあるディレクトリに移動し、regasmを実行します。

以下は失敗例です。

```powershell:一般ユーザーでの実行結果
PS C:\Windows\assembly\GAC\Microsoft.mshtml\7.0.3300.0__b03f5f7f11d50a3a> regasm .\Microsoft.mshtml.dll
Microsoft (R) .NET Framework Assembly Registration Utility 2.0.50727.9136
Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.

RegAsm : error RA0000 : An error occurred while writing the registration information to the registry. You must have administrative credentials to perform this task.  Contact your system administrator for assistance
```

以下は成功例です。

```powershell:管理者権限での実行結果
C:\Windows\assembly\GAC\Microsoft.mshtml\7.0.3300.0__b03f5f7f11d50a3a>regasm Microsoft.mshtml.dll
Microsoft (R) .NET Framework Assembly Registration Utility 2.0.50727.9136
Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.

Types registered successfully
```

VS2017を再起動すれば、MSHTMLについてエラーはなくなります。

## 32bitオブジェクトの生成

業務用のPCにはまだWindows7(32bit)のものがあるので、32bitの実行ファイルも生成します。
ターゲットを*Release*に、リリースに*x86*を選択した上で、ビルドメニューから*OfficeToPDFのビルド*を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78296/b9ac464f-0175-73e5-acdf-881b5349b259.png)

出力ペイン(pane)をみると
生成されたOfficeToPDF.exeファイルをコピーするなどし、ターゲットのマシンで利用します。

# OfficeToPDFの実行例

単純な例としてはトップディレクトリにOfficeToPDF.exeファイルをコピーしたとして、次のように配下のフォルダに含まれる.docxファイルをPDFファイルに変換します。

```powershell
PS \> ls -R *.docx | ForEach-Object { .\OfficeToPDF.exe $_ }
```

# 実用的な実行例

既にPDFファイルが存在している場合には処理を飛ばすような処理を加えるには次のような方法が考えられます。

```powershell:convtest1.ps1
ls -R *.docx | ForEach-Object { 
  $pdfpath = ($_.FullName) -Replace ($_.Extension + '$'), '.pdf' 
  if (Test-Path $pdfpath -PathType Leaf) {
    echo ($pdfpath + " skipping...")
  } else {
    echo ($pdfpath+ " converting...")
    .\OfficeToPDF.exe $_
  }
}
```

## 複数の拡張子に対応する

前述のconvtest1.ps1では、拡張子がどんなものでも対応できるようにしているので、次のようにカンマで区切って複数のパターンを記述することができます。

```powershell
ls -R "*.docx","*.pptx" | ForEach-Object {
```

# PowerShellスクリプトの開発・デバッグについて

UNIXのShellスクリプトでは変数の実体は常に文字列ですが、PowerShellスクリプトでは、実体は常にオブジェクトなので変数に格納されているオブジェクトの型に注目します。

型を調べる方法としては、GetType()があるので、今回の例では次のようなスクリプトの断片を実行していました。

```powershell
PS E:\> ls -R *.docx | ForEach-Object { $_.GetType() ; $_.FullName.GetType() }
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     FileInfo                                 System.IO.FileSystemInfo
True     True     String                                   System.Object
```

> Windows7標準のPowerShell2.0では ``` ls -R -include *.docx ```のように明示的に-includeを追加する必要がありました。

これでlsの実行結果から渡されるオブジェクトがSystem.IO.FileSystemInfoクラスのものだと分かります。
あとはAPIリファレンスでメソッドを調べたり、文字列に落し込んで基本的な操作で解決するようにしました。

# OfficeToPDF実行時の注意点

1. WordやExcelといったOfficeアプリを全て閉じて利用しないと、処理が止まったりPDFの生成に失敗する場合があります。
2. NAS等のネットワークドライブを対象とする場合には、.ps1ファイルを直接実行するためには、ExecutionPolicyをUnristricted (もしくはBypass)に変更する必要があります。

以上