PyInstallerを使ってExcelVBA感を消したい

## 思い立ったきっかけ
- Excelでマクロを作って配布すると、既に開いているExcelが消えたり(見えなくなる)するので社内で不評の嵐となり定着しなかった
- ええ感じのツールを作ったのでみんなにもっと使ってもらいたい
- どうにかええ感じのツール感を出したい
- ついでにアイコンとかも拘ってExcel感を出さないようにしてオリジナリティを出したい
- できればインスタンスを分けて既存のExcelアプリに影響しないようにしたい

## Excel感を出さないようにしたい
Excel(.xlsm)を実行させるときには、〇〇〇.xlsmを直接開いたりExcelから開くが基本だが、せっかく作りこんだのでできればアイコンとかも変えて作りこんだった感を出したい。

**僕「ツールができました」**

**同僚「どうせExc...Excelじゃないッ？！」**

**僕（ぱっと見.exeなだけなんだけどね。）**

要するにこれがやりたい。

## .xlsmを別の方法で実行する
タイトルにもなっているが、今回はPythonモジュールの一つであるPyInstallerを使って実行する


## PyInstallerとは
- .pyファイルを実行ファイル(.exe)にしてあげるPythonモジュール
- Pythonの環境がなくても実行できるので配布が楽
- 実行速度はあんまり早くない


## PyInstallerを導入
Python導入済みのWindows端末で下記のコマンドを実行する。
ユーザごとにモジュールインストールさせる場合は`--user`を最後につける
```
py -m pip install PyInstaller --user
```

```python:実行結果
Collecting PyInstaller
  Downloading pyinstaller-5.4.1-py3-none-win_amd64.whl (1.2 MB)
     ---------------------------------------- 1.2/1.2 MB 4.8 MB/s eta 0:00:00
Collecting altgraph
  Downloading altgraph-0.17.2-py2.py3-none-any.whl (21 kB)
Collecting pefile>=2022.5.30
  Downloading pefile-2022.5.30.tar.gz (72 kB)
     ---------------------------------------- 72.9/72.9 KB 2.0 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Collecting pyinstaller-hooks-contrib>=2021.4
  Downloading pyinstaller_hooks_contrib-2022.10-py2.py3-none-any.whl (244 kB)
     ---------------------------------------- 244.7/244.7 KB 1.2 MB/s eta 0:00:00
Collecting pywin32-ctypes>=0.2.0
  Downloading pywin32_ctypes-0.2.0-py2.py3-none-any.whl (28 kB)
Requirement already satisfied: setuptools in c:\users\user\appdata\local\programs\python\python310\lib\site-packages (from PyInstaller) (58.1.0)
Collecting future
  Downloading future-0.18.2.tar.gz (829 kB)
     ---------------------------------------- 829.2/829.2 KB 4.8 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Using legacy 'setup.py install' for pefile, since package 'wheel' is not installed.
Using legacy 'setup.py install' for future, since package 'wheel' is not installed.
Installing collected packages: pywin32-ctypes, altgraph, pyinstaller-hooks-contrib, future, pefile, PyInstaller
  Running setup.py install for future ... done
  Running setup.py install for pefile ... done
  WARNING: The scripts pyi-archive_viewer.exe, pyi-bindepend.exe, pyi-grab_version.exe, pyi-makespec.exe, pyi-set_version.exe and pyinstaller.exe are installed in 'C:\Users\user\AppData\Roaming\Python\Python310\Scripts' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed PyInstaller-5.4.1 altgraph-0.17.2 future-0.18.2 pefile-2022.5.30 pyinstaller-hooks-contrib-2022.10 pywin32-ctypes-0.2.0
WARNING: You are using pip version 22.0.4; however, version 22.2.2 is available.
You should consider upgrading via the 'C:\Users\user\AppData\Local\Programs\Python\Python310\python.exe -m pip install --upgrade pip' command.
```

なんかWarningとか色々出てますが、最終的にSuccessfullyが表示されていればOK。
pipコマンドで楽々導入できますね。楽でよい。


## win32comでExcelを操作するモジュールを入れる
```
py -m pip install pywin32
```
これもPyInstallerと同じ要領でインストールしましょう。

## .pyを書く
とりあえずなんでもいいのでExcelを展開する.pyファイルを書く。
ここでは`c:\temp\py`に保存したとする。
```python:Excel開く.py
import win32com.client

filePath = ".xlsmのパス"

App = win32com.client.Dispatch("Excel.Application")
workbook = App.Workbooks.Open(filePath, UpdateLinks=0, ReadOnly=True)
```

## .exe化する
上記で作成した`Excel開く.py`が配置してあるディレクトリに移動する。
```
cd c:\temp\py
```

PyInstallerを使って.exe化する
色々オプションがあるけど、とりあえず基本は次のコマンド
```
py -m PyInstaller Excel開く.py
```
```python:実行結果
499 INFO: PyInstaller: 5.4.1
499 INFO: Python: 3.10.5
516 INFO: Platform: Windows-10-10.0.22000-SP0
516 INFO: wrote c:\Temp\py\Excel開く.spec
516 INFO: UPX is not available.
531 INFO: Extending PYTHONPATH with paths
['c:\\Temp\\py']
3281 INFO: checking Analysis
3281 INFO: Building Analysis because Analysis-00.toc is non existent
3281 INFO: Initializing module dependency graph...
～～～～～～～～～～～～～中略～～～～～～～～～～～～
12624 INFO: Copying 0 resources to EXE
12624 INFO: Embedding manifest in EXE
12624 INFO: Updating manifest in c:\Temp\py\build\Excel開く\Excel開く.exe.notanexecutable
12717 INFO: Updating resource type 24 name 1 language 0
12720 INFO: Appending PKG archive to EXE
12721 INFO: Fixing EXE headers
13132 INFO: Building EXE from EXE-00.toc completed successfully.
13132 INFO: checking COLLECT
13132 INFO: Building COLLECT because COLLECT-00.toc is non existent
13141 INFO: Building COLLECT COLLECT-00.toc
13734 INFO: Building COLLECT COLLECT-00.toc completed successfully.
```
こんな感じで最後にsuccessfullyが表示されたら.exe化完了！

できあがった.exeはどこにあるかというと、今のディレクトリ構成は次のようになっています。

```:dirで確認
2022/09/25  20:19    <DIR>          .
2022/09/25  20:18    <DIR>          ..
2022/09/25  20:19    <DIR>          build
2022/09/25  20:19    <DIR>          dist
2022/09/25  20:18               179 Excel開く.py
2022/09/25  20:19               956 Excel開く.spec
```

何やら新しいファイルやフォルダが生成されていますね。
`Excel開く.spec`と`build`、`dist`ができています。

`dist`の中に`Excel開く.exe`が生成されているのですが、今回の生成方法だと`dist`の中にできているその他諸々のファイル群と同じ場所に配置しないと正常に動作しないファイルなので作り直します。

## 単体でも動く.exeを作る
PyInstallerを使って.exe化する
オプションをつけて実行しよう。
```
py -m PyInstaller Excel開く.py --onefile --noconsole --icon='xxx.ico'
```
`--onefile`：.exeに必要なファイル群をまとめるので単体実行できるようになる
`--noconsole`：実行時、コンソールを表示しないようにする
`--icon='xxx.ico'`：アプリケーションのアイコンを指定する

## できあがった.exeを実行してみると
僕「**よっしゃ！！動いた！！！**」

というわけで動くようになりました。
が、

僕「アレ？ほかのExcelとインスタンス同じじゃね？」

というわけで、`Excel開く.py`を書き直します。
```diff_python:Excel開く.py
import win32com.client

filePath = ".xlsmのパス"

- App = win32com.client.Dispatch("Excel.Application")#消す
+ App = win32com.client.Dispatchex("Excel.Application")#dispatchexにする

workbook = App.Workbooks.Open(filePath, UpdateLinks=0, ReadOnly=True)
workbook = App.Workbooks.Open(filePath, UpdateLinks=0, ReadOnly=True)
```

この状態でもう一度.exe化して・・・

**できました！！！！！！！**

## まとめ
- 今回はpywin32を利用してExcelを別インスタンスで開きました。
win32com.client.Dispatchexを使うようにしよう！
- PyInstallerで.exe化するときはオプションをつけるようにしよう。
用途を考えてコンソールの表示・非表示は使い分けると吉！

## あとがき
初めてQiitaに記事を投稿してみました。マークダウン記法って便利ですね。
Excelで作ったツールをExcel感出さずに使うってそもそもExcel使わずに別の使えよ！！！！って感じですが、こういう需要ある(ある？？)と思ったので備忘がてら書いてみました。
誰かのお役に立てれば幸いです。それでは。