Win32::OLEを使ったMicrosoft Wordの操作

[Perl Advent Calendar 2019](https://qiita.com/advent-calendar/2019/perl) の12日目です。
昨日はcodehexさんでした。

#きっかけ
　紙文化の職場のため、決裁書類や契約書類など様々な定型書類に囲まれています。
　共通する内容を複数の書類に転記する必要があり、ミスの原因にもなりますし、時間が掛かっていました。
　そこで、入力フォームを作成してボタン一つで作成可能にするなど、業務量の圧縮を図りました。
　その際の知見を共有できたら良いなと感じましたので投稿します。

#前提等
　現在、VBAを使っている人にはおすすめできると思います。
　Windows7とStrawberry Perl 5.30(いずれも32bit版)で行いました。

#Win32::OLE
- 良いところ
    - VBAで叩きを作っておけば、それをperlに書き直すだけ
    - Officeのマクロの記録機能を使用するとVBAコードが作成されるので、時間短縮が図れる
    - 定数などもVBAのイミディエイトウインドウで簡単に調べられるので良い（後述）
- 悪いところ
    - 文字コードを常に意識する必要がある（CP932に変換しないとうまく動作しない）
    - VBAに慣れていると引数がはまりどころになる（後述）

##Microsoft Office VBAでの定数の調べ方
　イミディエイトウインドウでTrueやFalse等の定数を調べることができます。
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542578/6a18417d-bb10-0cdf-cf97-2ebab32a2809.png)
　図では、True = -1, False = 0, wdSendToNewDocument = 0 だと判ります。

##引数について
　関数への引数はハッシュのリファレンスで渡します。
　下図の赤枠はWordのマクロ記録で作成されたVBAです。Tドライブのサンプル文書というファイルを開いたものです。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542578/6b403d61-a66d-5aa1-cc1a-c8257d5ea808.png)
　この場合perlでは以下のようになります。

```perl:sample1.pl
# 引数準備
my %openparam;	
$openparam{"FileName"} = Encode::encode("CP932","t:\\サンプル文書.docx");
$openparam{"ReadOnly"} = 0; # False

# ファイルを開く
my $doc = $word->Documents->Open(\%openparam);	# 引数のハッシュはリファレンスで渡す
```

もしくは

```perl:sample2.pl
my $openparam;
$openparam->{"FileName"} = Encode::encode("CP932","t:\\サンプル文書.docx");
$openparam->{"ReadOnly"} = 0; # False

# ファイルを開く
my $doc = $word->Documents->Open($openparam);
```

#例）Wordファイルを開き、文書を差し込み、ファイルを保存する
　例は、以下の5つを行っています。
1. 〔サンプル文書.docx〕を開く
2. 差込元データとして〔サンプル差込データ.xlsx〕のsheet1を指定する
3. 差し込みされた新規文書を作成する
4. 新規文書を印刷する
5. 新規文書を〔サンプル保存.docx〕として保存する

```perl:sample.pl
#!perl
use strict;

use Win32::OLE;
use Encode qw(encode);
use utf8;

my $word = Win32::OLE->new("Word.Application");
$word->{Visible} = -1;	# 可視状態にする (True = -1)

# documentは $word->Documents->Open で開く
# その時の引数をハッシュで準備する
my %openparam;	
$openparam{"FileName"} = Encode::encode("CP932","t:\\サンプル文書.docx");
$openparam{"ReadOnly"} = -1; # True 読み取り専用で開く

# ファイルを開く
my $doc = $word->Documents->Open(\%openparam);	# 引数のハッシュはリファレンスで渡す

# 差し込み印刷
my %dbparam;
$dbparam{"Name"} = encode("CP932","t:\\サンプル差込データ.xlsx");	# 差し込み印刷用のデータ
$dbparam{"SQLStatement"} = "SELECT * FROM `Sheet1\$`";	# シート名は〔`〕で囲む。また名称末尾には＄

$doc->MailMerge->OpenDataSource(\%dbparam);	# 差し込み印刷の設定 ハッシュをリファレンスで渡す	
$doc->MailMerge->{Destination} = 0; # wdSendToNewDocument = 0
$doc->MailMerge->{SuppressBlankLines} = -1; # True = -1
$doc->MailMerge->DataSource->{FirstRecord} = 1; # wdDefaultFirstRecord = 1 レコードの最初から
$doc->MailMerge->DataSource->{LastRecord} = -16; # wdDefaultLastRecord = -16 レコードの最後まで
$doc->MailMerge->Execute(0);	# 差し込み印刷実行

# 差し込み印刷された文書を印刷
my $countdoc = $word->Documents->Count; # 文書数、最後に作られた文書は 文書数-1で指定できる
my $newdoc = $word->Documents->Item($countdoc - 1);

my %printparam;
$printparam{"Copies"} = 1;	# コピー枚数
$printparam{"Background"} = 0;	# False = 0
$newdoc->PrintOut(\%printparam);

# 保存
$newdoc->SaveAs2(encode("CP932","t:\\サンプル保存.docx"));

# 閉じる
$newdoc->Close(0); # False = 0
$doc->Close(0);
$word->Quit(0);

undef $doc;
undef $word;
```

#おわりに
　マクロの記録を使えばVBAコードが作成されますので、「ブックマークに移動」「表を結合」「斜線を引く」等も簡単にperlに移行できたりします。
　Excelも同様にマクロを記録して出来たVBAを加工すると、少し楽が出来ます。

---
明日、13日目はomokawa_yasuです。
ありがとうございました。