生成AIやPythonなど、様々な便利なITコンテンツが出回っている日々ですが、そのような中でもExcelマクロはしぶとく生き続けているかと思います。
Excelマクロが複雑で「実行のたびにExcelがフリーズしてしまう。。。」なんて経験をされた方も少なくないのではないでしょうか?
今回は私の職場にて使用されている重た~いExcelマクロをPythonでサクサクにした話をPythonのサンプルコードも交えて説明していきたいと思います。
背景
私の職場では売上/原価情報の更新を以下のような流れで管理しています。
①社内の売上/原価情報管理用のポータルサイトより、指定の売上/原価情報をダウンロード
②(Excelマクロ使用)上記①にてダウンロードしたものをExcelマクロ「売上/原価情報取込」にて取り込み。Excelファイルの各セルに売上/原価情報が反映される
③上記②にて取込した情報を必要に応じて更新。
④上記③にて更新した内容を売上/原価情報管理用のポータルサイトへアップロードすることにより、売上/原価情報を反映する
背景
私の職場では売上/原価情報の更新を以下のような流れで管理しています。
①社内の売上/原価情報管理用のポータルサイトより、指定の売上/原価情報をダウンロード
②(Excelマクロ使用)上記①にてダウンロードしたものをExcelマクロ「売上/原価情報取込」にて取り込み。Excelファイルの各セルに売上/原価情報が反映される
③上記②にて取込した情報を必要に応じて更新。
④上記③にて更新した内容を売上/原価情報管理用のポータルサイトへアップロードすることにより、売上/原価情報を反映する
背景
私の職場では売上/原価情報の更新を以下のような流れで管理しています。
①社内の売上/原価情報管理用のポータルサイトより、指定の売上/原価情報をダウンロード
②(Excelマクロ使用)上記①にてダウンロードしたものをExcelマクロ「売上/原価情報取込」にて取り込み。Excelファイルの各セルに売上/原価情報が反映される
③上記②にて取込した情報を必要に応じて更新。
④上記③にて更新した内容を売上/原価情報管理用のポータルサイトへアップロードすることにより、売上/原価情報を反映する

課題
上記の流れについて、以下の課題がありました。
・大量の売上/原価情報を取り込むため、Excelマクロの処理が重い。
・そのため、マクロ実行時に他のExcelファイルを複数開いていると、Excel全体がフリーズする。
・つまるところExcelマクロ実行中は他のExcelの作業ができない
私が考えたこと①
上記の課題を解消するために私が考えたことは以下です。
そもそもなぜExcelマクロを実行すると他のExcelファイル含めてフリーズしてしまうのか?
今回の課題解消の一番のポイントですね。普段はあまり考えたことがなかったのですが、改めて生成AIに聞いてみました。
Excelファイルを複数開いた状態でExcelマクロを実行するとフリーズする仕組みを教えてください。
以下の回答が返ってきました。
ご質問は「**Excelファイルを複数開いた状態でExcelマクロを実行するとフリーズする仕組み**」についてですね。
**複数ブックを開いた状態でのマクロ実行がフリーズする原因は、ExcelのアーキテクチャとVBAの実行モデルに起因する**ことが分かりました。以下に詳しく解説します。
---
## ✅ フリーズの仕組みと原因
### 1. Excelのシングルスレッド実行モデル
- Excel VBAは基本的に**シングルスレッド**で動作します。
- 複数のブックを開いていても、**VBAの実行は1スレッドで順次処理**されるため、他のブックでUI操作や再描画が必要になると、処理がブロックされやすくなります。
---
### 2. *Workbooks.OpenやActivateによるフォーカス切り替え**
- マクロ内Workbooks.OpenActivateを使うと、**ExcelのUIスレッドがフォーカス切り替え処理を待機**します。
- このとき、**イベントや再描画が完了するまでVBAがブロック**されるため、複数ブックを開いていると競合が発生しやすくなります。
→ 特Application.ScreenUpdating = FalseApplication.EnableEvents = Falseを忘れると、UI更新が中途半端になりフリーズに見える状態になります。
---
### 3. Shiftキーやイベント抑制の影響
- Excelには「**Shiftキーを押しながらブックを開くと自動マクロを実行しない**」という仕様があります。
- しかし、**ショートカットキーにShiftを含めている場合**Workbooks.Openで別ブックを開いた瞬間にイベントが抑制され、マクロが止まることがあります。
---
### 4. 複数インスタンス問題
- Excelは**1つのインスタンスで複数ブックを開く場合、同一プロセスでUIとVBAを共有**します。
- そのため、**別ブックのアクティブ化や計算処理が割り込むと、VBAの実行が待機状態**になり、結果として「フリーズ」に見える現象が起きます。
- 対策として、**別インスタンスでExcelを起動する(/xオプションやAltキー起動)**と、プロセスが分離されるため安定します。
---
### 5. ActiveXやイベントの競合
- 複数ブックで**ActiveXコントロールやイベント(Workbook_Openなど)**が有効な場合、**イベントキューが競合**し、処理が止まることがあります。
- 特に、**Application.Runで別ブックのマクロを呼び出す**場合、呼び出し元と呼び出し先のイベントが干渉しやすいです。
---
## ✅ まとめると
- Excel VBAはシングルスレッドでUIと同居しているため、複数ブック操作でUI更新やイベントが絡むと簡単にブロックされる
- Workbooks.OpenやActivate、ActiveX、イベント処理が原因で待機状態になる
- 別インスタンスでExcelを起動する、ScreenUpdatingやEnableEventsを適切に制御することで回避可能
---
## ✅ 対策のベストプラクティス
- Application.ScreenUpdating = False と Application.EnableEvents = False をマクロ開始時に設定し、終了時にTrueに戻す。
- DoEventsを適切に挿入してUIスレッドに処理を返す。
- 複数ブックを扱う場合は**別インスタンスでExcelを起動**する。
- ActiveXやイベントを極力使わず、処理をバッチ化する。
---
上記の中で私が着目した個所は以下です
### 1. Excelのシングルスレッド実行モデル- Excel VBAは基本的に**シングルスレッド**で動作します。 - 複数のブックを開いていても、**VBAの実行は1スレッドで順次処理**されるため、他のブックでUI操作や再描画が必要になると、処理がブロックされやすくなります。
### 2. *Workbooks.OpenやActivateによるフォーカス切り替え**- マクロ内Workbooks.OpenActivateを使うと、**ExcelのUIスレッドがフォーカス切り替え処理を待機**します。- このとき、**イベントや再描画が完了するまでVBAがブロック**されるため、複数ブックを開いていると競合が発生しやすくなります。 → 特Application.ScreenUpdating = FalseApplication.EnableEvents = Falseを忘れると、UI更新が中途半端になりフリーズに見える状態になります。
### 4. 複数インスタンス問題- Excelは**1つのインスタンスで複数ブックを開く場合、同一プロセスでUIとVBAを共有**します。- そのため、**別ブックのアクティブ化や計算処理が割り込むと、VBAの実行が待機状態**になり、結果として「フリーズ」に見える現象が起きます。- 対策として、**別インスタンスでExcelを起動する(/xオプションやAltキー起動)**と、プロセスが分離されるため安定します。
上記の個所を要約すると以下です。
・Excelは基本的に単一のスレッドで起動される。つまるところファイル自体を複数で開いていても、そのファイル同士は単一のスレッドで管理されている。
・そのためその状態でExcelマクロを実行すると、Excelマクロの実行が優先されてしまい、同一スレッド内におけるその他のファイルの操作ができない
・またExcelマクロによるUIイベント(画面描画)が優先されるため、同一スレッド内におけるその他のファイルの操作ができない
・対策としては複数のExcelファイルを開く場合は別のインスタンスでExcelを起動すること
私が考えたこと②
上記の原因を理解したところで、以下の方法で解消できないかを考えました。
ExcelマクロをPythonにて新規のインスタンスで実行できないか?
ExcelマクロをPythonより実行する際に新規のインスタンスで実行することで他のExcelファイルとは別プロセスとなり、ファイルのフリーズを防ぐことが可能です。
ExcelマクロをPythonにてバックグランドにて実行できないか?
Excelマクロをバックグランドで実行することでExcelマクロによって発生するUIイベントによる画面のちらつきを防ぐことが可能です。
上記まで考えたことをもとに、ツールの作成に入りましたが1つ課題にぶちあたりました。
ぶち当たった課題
前提
以下のようなボタンのマクロを実行するツールを作成しようとしていました。

課題事象①:VisualBasicEditorのExcelマクロのコードがパスワードロックされており、実行したいマクロ名が特定不可
職場の担当部署が全社向けに作成した業務用マクロであるからなのか、Excelマクロのコードがパスワードでロックされており、実行したいマクロ名を特定することができませんでした。
まあ担当部署からしたら勝手にマクロを改造されたらたまったものではないので仕方ないですね。。。。

課題事象②:開発タブからマクロ名を特定しようとするも見つからない
VisualBasicEditorにおけるコードがロックされているため、開発タブからマクロ名を特定しようとしたのですが、、、見つかりませんでした。

Excelマクロには大きく分けて以下の2種類があります。
フォームコントロール・・・Excelに標準搭載されている簡易的なコントロールで、ボタン、チェックボックス、リストボックスなどを配置可能
ActiveXコントロール・・・WindowsのCOM技術を利用した高機能なコントロールで、VBAと密接に連携可能
端的に説明するとActiveXコントロールのほうがフォームコントロールと比較して、より高度なマクロ操作が可能となります。
今回実行したいマクロは上記のうち、ActiveXコントロールに該当するそうで、ActiveXコントロールのマクロ情報は開発タブから特定することは不可能です。
課題解消のためにやったこと
ずばりPythonにて実行したいマクロの情報を取得できないか?という切り口で行動しました。
PythonにてExcelマクロのボタンを押下するコードを書こうとしています。
そのために必要なマクロ情報を取得したいです。
Pythonにて全シート分のフォームコントロールおよびActiveXコントロールの情報を取得するコードを教えてください。
AIから提示されたコードをもとに私にて脚色したコードは以下です。
import win32com.client
# Excelアプリケーションを起動(非表示)
excel = win32com.client.DispatchEx("Excel.Application")
excel.Visible = False
excel.DisplayAlerts = False
excel.ScreenUpdating = False
# 対象のブックを開く
workbook = excel.Workbooks.Open(r"C:\path\to\your\file.xlsm")
for sheet in workbook.Sheets:
sheet_name = sheet.Name
print(f"\n=== シート: {sheet_name} ===")
# --- フォームコントロール ---
print("■ フォームコントロール")
for shape in sheet.Shapes:
if shape.Type == 8: # msoFormControl
obj_name = shape.Name
macro_name = shape.OnAction if hasattr(shape, "OnAction") else ""
print(f" オブジェクト名: {obj_name}, マクロ名: {macro_name}")
# --- ActiveXコントロール ---
print("■ ActiveXコントロール")
for ole in sheet.OLEObjects():
obj_name = ole.Name
caption = ole.Object.Caption
print(f" オブジェクト名: {obj_name}, 表示ラベル名: {caption}")
# 後処理
workbook.Close(SaveChanges=False)
excel.Quit()
del excel
上記のPythonコードにて、職場のマクロを読ませて実行した結果が以下です。
=== シート: Sheet1 ===
■ フォームコントロール
■ ActiveXコントロール
オブジェクト名: CommandButton1, 表示ラベル名: 売上/原価情報取込
上記で取得したオブジェクト名がいわゆる実行したいマクロの情報となります。ただこれでも「マクロ名」を特定することはできませんでした。
どうやらPythonでActiveXコントロールの「マクロ名」を特定することは難しいようです。。。
仮にマクロ名を特定できていた場合のコードイメージは以下の通りです。
import win32com.client
from pywinauto import Application
import os
import sys
import tkinter
import tkinter.filedialog
import tkinter.messagebox as messagebox
#スクリプト実行フォルダを取得if getattr(sys, 'frozen', False):
exe_dir = os.path.dirname(sys.executable)
else:
exe_dir = os.path.dirname(os.path.abspath(__file__))
error_occured=False
#inputとなるxlsmファイルを選択
ftype = [ ( '.xlsm' , '.xlsm' ) ]
macro_file_path=False
macro_file_path = tkinter.filedialog.askopenfilename(filetypes=ftype,title='実行したいマクロファイルを選択してください',initialdir=exe_dir)
try:
if not macro_file_path:
messagebox.showinfo("エラー", "マクロファイルが選択されませんでした。")
else:
excel = win32com.client.DispatchEx("Excel.Application")
excel.Visible = False # TrueにするとExcel画面が見える
excel.DisplayAlerts = False
excel.ScreenUpdating = False
wb = excel.Workbooks.Open(macro_file_path)
excel.Application.Run(f"'{macro_file_path}'!(実行したいマクロ名)") # ここで実行したい「マクロ名」を指定する
wb.Save()
wb.Close()
excel.Quit()
del excel
messagebox.showinfo("完了", "マクロ処理が完了しました。")
except Exception as e:
messagebox.showinfo("エラー", "予期せぬエラーが発生しました。")
finally:
try:
if wb:
wb.Close(SaveChanges=False)
except:
pass
try:
if excel:
excel.Quit()
del excel
except:
pass
sys.exit()
ここまでの状況を整理すると以下です。
・ActiveXコントロールの「マクロ名」を特定することは困難
・一方で「オブジェクト名」は特定できた
→「オブジェクト名」ベースで実行する方法はないか?
"excel.Application.Run"はExcelマクロのコードを直接実行するメソッドとなります。そのため、マクロ名を正確に指定する必要があります。
上記を踏まえて発想を変えて以下の方法でできないかを模索してみました。
発想転換
生成AIやPythonなど、様々な便利なITコンテンツが出回っている日々ですが、そのような中でもExcelマクロはしぶとく生き続けているかと思います。
Excelマクロが複雑で「実行のたびにExcelがフリーズしてしまう。。。」なんて経験をされた方も少なくないのではないでしょうか?
今回は私の職場にて使用されている重た~いExcelマクロをPythonでサクサクにした話をPythonのサンプルコードも交えて説明していきたいと思います。
背景
私の職場では売上/原価情報の更新を以下のような流れで管理しています。
①社内の売上/原価情報管理用のポータルサイトより、指定の売上/原価情報をダウンロード
②(Excelマクロ使用)上記①にてダウンロードしたものをExcelマクロ「売上/原価情報取込」にて取り込み。Excelファイルの各セルに売上/原価情報が反映される
③上記②にて取込した情報を必要に応じて更新。
④上記③にて更新した内容を売上/原価情報管理用のポータルサイトへアップロードすることにより、売上/原価情報を反映する
背景
私の職場では売上/原価情報の更新を以下のような流れで管理しています。
①社内の売上/原価情報管理用のポータルサイトより、指定の売上/原価情報をダウンロード
②(Excelマクロ使用)上記①にてダウンロードしたものをExcelマクロ「売上/原価情報取込」にて取り込み。Excelファイルの各セルに売上/原価情報が反映される
③上記②にて取込した情報を必要に応じて更新。
④上記③にて更新した内容を売上/原価情報管理用のポータルサイトへアップロードすることにより、売上/原価情報を反映する
背景
私の職場では売上/原価情報の更新を以下のような流れで管理しています。
①社内の売上/原価情報管理用のポータルサイトより、指定の売上/原価情報をダウンロード
②(Excelマクロ使用)上記①にてダウンロードしたものをExcelマクロ「売上/原価情報取込」にて取り込み。Excelファイルの各セルに売上/原価情報が反映される
③上記②にて取込した情報を必要に応じて更新。
④上記③にて更新した内容を売上/原価情報管理用のポータルサイトへアップロードすることにより、売上/原価情報を反映する
課題
上記の流れについて、以下の課題がありました。
・大量の売上/原価情報を取り込むため、Excelマクロの処理が重い。
・そのため、マクロ実行時に他のExcelファイルを複数開いていると、Excel全体がフリーズする。
・つまるところExcelマクロ実行中は他のExcelの作業ができない
私が考えたこと①
上記の課題を解消するために私が考えたことは以下です。
そもそもなぜExcelマクロを実行すると他のExcelファイル含めてフリーズしてしまうのか?
今回の課題解消の一番のポイントですね。普段はあまり考えたことがなかったのですが、改めて生成AIに聞いてみました。
以下の回答が返ってきました。
上記の中で私が着目した個所は以下です
上記の個所を要約すると以下です。
・Excelは基本的に単一のスレッドで起動される。つまるところファイル自体を複数で開いていても、そのファイル同士は単一のスレッドで管理されている。
・そのためその状態でExcelマクロを実行すると、Excelマクロの実行が優先されてしまい、同一スレッド内におけるその他のファイルの操作ができない
・またExcelマクロによるUIイベント(画面描画)が優先されるため、同一スレッド内におけるその他のファイルの操作ができない
・対策としては複数のExcelファイルを開く場合は別のインスタンスでExcelを起動すること
私が考えたこと②
上記の原因を理解したところで、以下の方法で解消できないかを考えました。
ExcelマクロをPythonにて新規のインスタンスで実行できないか?
ExcelマクロをPythonより実行する際に新規のインスタンスで実行することで他のExcelファイルとは別プロセスとなり、ファイルのフリーズを防ぐことが可能です。
ExcelマクロをPythonにてバックグランドにて実行できないか?
Excelマクロをバックグランドで実行することでExcelマクロによって発生するUIイベントによる画面のちらつきを防ぐことが可能です。
上記まで考えたことをもとに、ツールの作成に入りましたが1つ課題にぶちあたりました。
ぶち当たった課題
前提
以下のようなボタンのマクロを実行するツールを作成しようとしていました。
課題事象①:VisualBasicEditorのExcelマクロのコードがパスワードロックされており、実行したいマクロ名が特定不可
職場の担当部署が全社向けに作成した業務用マクロであるからなのか、Excelマクロのコードがパスワードでロックされており、実行したいマクロ名を特定することができませんでした。
まあ担当部署からしたら勝手にマクロを改造されたらたまったものではないので仕方ないですね。。。。
課題事象②:開発タブからマクロ名を特定しようとするも見つからない
VisualBasicEditorにおけるコードがロックされているため、開発タブからマクロ名を特定しようとしたのですが、、、見つかりませんでした。
Excelマクロには大きく分けて以下の2種類があります。
端的に説明するとActiveXコントロールのほうがフォームコントロールと比較して、より高度なマクロ操作が可能となります。
今回実行したいマクロは上記のうち、ActiveXコントロールに該当するそうで、ActiveXコントロールのマクロ情報は開発タブから特定することは不可能です。
課題解消のためにやったこと
ずばりPythonにて実行したいマクロの情報を取得できないか?という切り口で行動しました。
AIから提示されたコードをもとに私にて脚色したコードは以下です。
上記のPythonコードにて、職場のマクロを読ませて実行した結果が以下です。
上記で取得したオブジェクト名がいわゆる実行したいマクロの情報となります。ただこれでも「マクロ名」を特定することはできませんでした。
どうやらPythonでActiveXコントロールの「マクロ名」を特定することは難しいようです。。。
仮にマクロ名を特定できていた場合のコードイメージは以下の通りです。
ここまでの状況を整理すると以下です。
・ActiveXコントロールの「マクロ名」を特定することは困難
・一方で「オブジェクト名」は特定できた
→「オブジェクト名」ベースで実行する方法はないか?
"excel.Application.Run"はExcelマクロのコードを直接実行するメソッドとなります。そのため、マクロ名を正確に指定する必要があります。
上記を踏まえて発想を変えて以下の方法でできないかを模索してみました。
発想転換