Delphi で PowerPoint の各ページを画像ファイルにする

# はじめに
@natsutan さんの[『PythonでPowerPointの各ページを画像ファイルにする』](https://qiita.com/natsutan/items/2487e24fe3f31569b40d)という記事中にあるコードを Delphi で書いてみました。使用する Delphi のバージョンは **10.3.3 Rio** です。

# 移植
使い方などはオリジナルに準拠するものとします。

## コード
コンソールアプリケーションとして書かれており、ほぼベタ移植です。

```pascal:PPT2PNG.dproj
program PPT2PNG;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Win.ComObj,
  System.IOUtils;

const
  PPT_NAME = 'test.pptx';
  OUT_DIR  = 'images';

  procedure export_img(fname, odir: string);
  begin
    var application: OleVariant := CreateOleObject('Powerpoint.Application');
    application.Visible := True;
    var current_folder := TDirectory.GetCurrentDirectory;

    var presentation := application.Presentations.open(TPath.Combine(current_folder, fname));

    var export_path := TPath.Combine(current_folder, odir);
    presentation.Export(export_path, FilterName := 'png');

    presentation.close;
    application.quit;
  end; { export_img }

  procedure rename_img(odir: string);
  begin
     for var fname in TDirectory.GetFiles(odir, '*.png', TSearchOption.soTopDirectoryOnly) do
       begin
         var new_fname := StringReplace(fname, 'スライド', 'slide', []);
         TFile.Move(fname, new_fname);
       end;
  end; { rename_img }

begin
  export_img(PPT_NAME, OUT_DIR);
  rename_img(OUT_DIR);
end. { main }

```

ところがこれを実行するとエラーが発生してしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/ea8ad9e9-683b-64d2-0cea-ebe4dd709453.png)

```
---------------------------
デバッガ例外通知
---------------------------
プロジェクト PPT2PNG.exe は例外クラス EOleSysError （メッセージ 'CoInitialize は呼び出されていません。'）を送出しました。
---------------------------
ブレーク(B)   継続(C)   ヘルプ   
---------------------------
```

この問題はアプリケーションがコンソールアプリケーションの時に起こります。

## 問題回避
問題を回避するには、**uses** に `Winapi.ActiveX` を加え、

```pascal
uses
  System.SysUtils,
  System.Win.ComObj,
  System.IOUtils,
  Winapi.ActiveX;  // <-- 追加

```

**COM** を使っているコードを `CoInitialize` と `CoUninitialize` で括ります。

```pascal
  procedure export_img(fname, odir: string);
  begin
    CoInitialize(nil);  // <- 追加
    var application: OleVariant := CreateOleObject('Powerpoint.Application');
    application.Visible := True;
    var current_folder := TPath.GetDirectoryName(ParamStr(0));

    var presentation := application.Presentations.open(TPath.Combine(current_folder, fname));

    var export_path := TPath.Combine(current_folder, odir);
    presentation.Export(export_path, FilterName := 'png');

    presentation.close;
    application.quit;
    CoUninitialize;  // <- 追加
  end; { export_img }
``` 

ちゃんとやるなら `CoInitialize` と `CoUninitialize` は **try** ～ **finally** で括った方がいいと思います。

**See also:**

 - [COM (Wikipedia)](https://ja.wikipedia.org/wiki/Component_Object_Model)
 - [CoInitialize function (docs.microsoft.com)](https://docs.microsoft.com/ja-jp/windows/win32/api/objbase/nf-objbase-coinitialize)
 - [CoUninitialize function (docs.microsoft.com)](https://docs.microsoft.com/ja-jp/windows/win32/api/combaseapi/nf-combaseapi-couninitialize)

# おわりに
**でるお (仮)** のスライドが無事画像に変換できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/9afa5d3c-ffe5-b3b1-4b1f-f420bc5d0a44.png)

**See also:**

- [Delphi (Embarcadero)](https://www.embarcadero.com/jp/products/delphi)
- [Delphi Community Edition (Embarcadero)](https://www.embarcadero.com/jp/products/delphi/starter)
- [Delphi のマスコットキャラが欲しい件 (Togetter)](https://togetter.com/li/622377)