[{"data":1,"prerenderedAt":409},["ShallowReactive",2],{"content-/autohotkey-excel-window-manager":3,"all-pages-for-dir":407,"og-image-/autohotkey-excel-window-manager":408},{"id":4,"title":5,"body":6,"category":386,"description":387,"extension":388,"meta":389,"navigation":390,"path":391,"project_name":392,"published":393,"publishedAt":394,"seo":395,"stem":396,"tags":397,"todo":404,"updatedAt":405,"__hash__":406},"pages/2026-04/2026-04-08/autohotkey-excel-window-manager.md","Excel全ウィンドウをウルトラワイドモニター中央に一括配置するStream Deck用スクリプト",{"type":7,"value":8,"toc":378},"minimark",[9,14,18,21,26,34,63,66,84,86,90,97,109,131,134,136,140,155,168,230,237,239,243,256,267,282,285,287,290,298,327,329,332,374],[10,11,13],"h1",{"id":12},"excel全ウィンドウをウルトラワイドモニター中央に一括配置する","Excel全ウィンドウをウルトラワイドモニター中央に一括配置する",[15,16,17],"p",{},"ウルトラワイドモニターでExcelを使っていると、ブックが画面の左端や右端に散らばる。8つのブックを開いた状態でモニター中央（1920x1080、座標4600,180）に全部並べ直す作業を毎日やっていた。Stream Deckのボタン一発で片付けたい――そう思ってスクリプトを書き始めたが、ここから4段階の試行錯誤が始まった。",[19,20],"hr",{},[22,23,25],"h2",{"id":24},"step-1-バッチファイルにpowershellを埋め込む-行結合で壊れる","Step 1: バッチファイルにPowerShellを埋め込む → 行結合で壊れる",[15,27,28,29,33],{},"Stream Deckから呼ぶ最もシンプルな方法として、",[30,31,32],"code",{},".bat","にPowerShellのhere-stringを埋め込んだ。",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-bat shiki shiki-themes vitesse-light vitesse-light","powershell -Command \"$code = @'\nAdd-Type ...\n'@; ...\"\n","bat","",[30,42,43,51,57],{"__ignoreMap":40},[44,45,48],"span",{"class":46,"line":47},"line",1,[44,49,50],{},"powershell -Command \"$code = @'\n",[44,52,54],{"class":46,"line":53},2,[44,55,56],{},"Add-Type ...\n",[44,58,60],{"class":46,"line":59},3,[44,61,62],{},"'@; ...\"\n",[15,64,65],{},"実行するとhere-stringがバッチの行結合で壊れ、構文エラーが出た。バッチファイルのエスケープルールとPowerShellのhere-stringは相性が悪い。",[15,67,68,72,73,76,77,79,80,83],{},[69,70,71],"strong",{},"修正",": ",[30,74,75],{},".ps1","を分離して、",[30,78,32],{},"から ",[30,81,82],{},"powershell -ExecutionPolicy Bypass -File move-excel.ps1"," で呼ぶ構成に切り替えた。構文エラーが消えて、スクリプト本体の開発に集中できるようになった。",[19,85],{},[22,87,89],{"id":88},"step-2-1ウィンドウしか動かない-全プロセスをループに変更","Step 2: 1ウィンドウしか動かない → 全プロセスをループに変更",[15,91,92,93,96],{},"最初のPowerShellスクリプトでは",[30,94,95],{},"Get-Process EXCEL | Select -First 1","で1つだけ取得していた。当然、8つのうち1つしか動かない。",[15,98,99,72,101,104,105,108],{},[69,100,71],{},[30,102,103],{},"Select -First 1","を外して",[30,106,107],{},"Get-Process EXCEL","の全結果をループで回した。",[35,110,114],{"className":111,"code":112,"language":113,"meta":40,"style":40},"language-powershell shiki shiki-themes vitesse-light vitesse-light","Get-Process EXCEL | ForEach-Object {\n    [Win32]::MoveWindow($_.MainWindowHandle, 4600, 180, 1920, 1080, $true)\n}\n","powershell",[30,115,116,121,126],{"__ignoreMap":40},[44,117,118],{"class":46,"line":47},[44,119,120],{},"Get-Process EXCEL | ForEach-Object {\n",[44,122,123],{"class":46,"line":53},[44,124,125],{},"    [Win32]::MoveWindow($_.MainWindowHandle, 4600, 180, 1920, 1080, $true)\n",[44,127,128],{"class":46,"line":59},[44,129,130],{},"}\n",[15,132,133],{},"ところが結果は変わらず、1つのウィンドウしか移動しない。ここで手が止まった。",[19,135],{},[22,137,139],{"id":138},"step-3-excelは1プロセスで複数ブックを持つ-enumwindowsで直接列挙","Step 3: Excelは1プロセスで複数ブックを持つ → EnumWindowsで直接列挙",[15,141,142,143,146,147,150,151,154],{},"原因を調べて腑に落ちた。Excelは複数ブックを開いていても",[69,144,145],{},"OSのプロセスは1つ","しか立たない。",[30,148,149],{},"Get-Process","で列挙しても",[30,152,153],{},"MainWindowHandle","は1個だけ返る。",[15,156,157,159,160,163,164,167],{},[69,158,71],{},": Win32 APIの",[30,161,162],{},"EnumWindows","を使い、ウィンドウクラス名",[30,165,166],{},"XLMAIN","を持つウィンドウを直接列挙する方式に切り替えた。",[35,169,171],{"className":111,"code":170,"language":113,"meta":40,"style":40},"# EnumWindowsのコールバックでXLMAINクラスを収集\n[Win32]::EnumWindows({\n    param($hwnd, $lParam)\n    $cls = New-Object Text.StringBuilder 256\n    [Win32]::GetClassName($hwnd, $cls, 256)\n    if ($cls.ToString() -eq \"XLMAIN\") {\n        # $hwndをリストに追加\n    }\n    return $true\n}, [IntPtr]::Zero)\n",[30,172,173,178,183,188,194,200,206,212,218,224],{"__ignoreMap":40},[44,174,175],{"class":46,"line":47},[44,176,177],{},"# EnumWindowsのコールバックでXLMAINクラスを収集\n",[44,179,180],{"class":46,"line":53},[44,181,182],{},"[Win32]::EnumWindows({\n",[44,184,185],{"class":46,"line":59},[44,186,187],{},"    param($hwnd, $lParam)\n",[44,189,191],{"class":46,"line":190},4,[44,192,193],{},"    $cls = New-Object Text.StringBuilder 256\n",[44,195,197],{"class":46,"line":196},5,[44,198,199],{},"    [Win32]::GetClassName($hwnd, $cls, 256)\n",[44,201,203],{"class":46,"line":202},6,[44,204,205],{},"    if ($cls.ToString() -eq \"XLMAIN\") {\n",[44,207,209],{"class":46,"line":208},7,[44,210,211],{},"        # $hwndをリストに追加\n",[44,213,215],{"class":46,"line":214},8,[44,216,217],{},"    }\n",[44,219,221],{"class":46,"line":220},9,[44,222,223],{},"    return $true\n",[44,225,227],{"class":46,"line":226},10,[44,228,229],{},"}, [IntPtr]::Zero)\n",[15,231,232,233,236],{},"これで8つのExcelウィンドウハンドルが全て取得できた。",[30,234,235],{},"MoveWindow","をループで回すと、8つ全てが指定座標に移動した――が、サイズがおかしい。",[19,238],{},[22,240,242],{"id":241},"step-4-リサイズが効かない-sw_shownormalに変更","Step 4: リサイズが効かない → SW_SHOWNORMALに変更",[15,244,245,246,248,249,252,253,255],{},"最大化や最小化されたウィンドウに対して",[30,247,235],{},"を呼んでも、位置は変わるがサイズが元に戻らない。最初は",[30,250,251],{},"ShowWindow($hwnd, 9)","（SW_RESTORE）を呼んでから",[30,254,235],{},"していたが、最大化状態のウィンドウは復元後に直前のサイズに戻るだけで、こちらが指定したサイズにはならなかった。",[15,257,258,72,260,263,264,266],{},[69,259,71],{},[30,261,262],{},"ShowWindow($hwnd, 1)","（SW_SHOWNORMAL）に変更した。SW_SHOWNORMALは最大化・最小化フラグを完全にクリアしてから通常状態に戻す。その後に",[30,265,235],{},"を呼ぶと、指定サイズが正しく適用された。",[35,268,270],{"className":111,"code":269,"language":113,"meta":40,"style":40},"[Win32]::ShowWindow($hwnd, 1)  # SW_SHOWNORMAL\n[Win32]::MoveWindow($hwnd, 4600, 180, 1920, 1080, $true)\n",[30,271,272,277],{"__ignoreMap":40},[44,273,274],{"class":46,"line":47},[44,275,276],{},"[Win32]::ShowWindow($hwnd, 1)  # SW_SHOWNORMAL\n",[44,278,279],{"class":46,"line":53},[44,280,281],{},"[Win32]::MoveWindow($hwnd, 4600, 180, 1920, 1080, $true)\n",[15,283,284],{},"8つのExcelウィンドウが全て1920x1080でモニター中央に並んだ。",[19,286],{},[22,288,289],{"id":289},"最終構成",[35,291,296],{"className":292,"code":294,"language":295},[293],"language-text","StreamDeck → move-excel.bat → move-excel.ps1\n","text",[30,297,294],{"__ignoreMap":40},[299,300,301,309],"ul",{},[302,303,304,72,307],"li",{},[69,305,306],{},"move-excel.bat",[30,308,82],{},[302,310,311,72,314,317,318,320,321,324,325],{},[69,312,313],{},"move-excel.ps1",[30,315,316],{},"Add-Type","でWin32 API定義 → ",[30,319,162],{},"でXLMAINクラス列挙 → ",[30,322,323],{},"ShowWindow(1)"," + ",[30,326,235],{},[19,328],{},[22,330,331],{"id":331},"学んだこと",[299,333,334,346,359,368],{},[302,335,336,339,340,342,343,345],{},[69,337,338],{},"Excelのプロセスモデル",": 複数ブックを開いても",[30,341,149],{},"は1行しか返さない。ウィンドウ単位で操作するには",[30,344,162],{}," + クラス名フィルタが必要",[302,347,348,72,351,354,355,358],{},[69,349,350],{},"SW_RESTOREとSW_SHOWNORMALの違い",[30,352,353],{},"SW_RESTORE(9)","は「直前の状態に戻す」、",[30,356,357],{},"SW_SHOWNORMAL(1)","は「最大化/最小化フラグをクリアして通常表示」。リサイズを効かせたいなら後者",[302,360,361,364,365,367],{},[69,362,363],{},"バッチとPowerShellの分離",": here-stringの埋め込みは行結合で壊れる。最初から",[30,366,75],{},"を分離した方が速い",[302,369,370,373],{},[69,371,372],{},"デバッグ手順",": 「動かない」の切り分けは、ハンドルが取れているか → 取れたハンドルの数は正しいか → API呼び出しの戻り値は何か、の順に追う",[375,376,377],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":40,"searchDepth":53,"depth":53,"links":379},[380,381,382,383,384,385],{"id":24,"depth":53,"text":25},{"id":88,"depth":53,"text":89},{"id":138,"depth":53,"text":139},{"id":241,"depth":53,"text":242},{"id":289,"depth":53,"text":289},{"id":331,"depth":53,"text":331},"dev","AutoHotkey + PowerShellでExcelの全ブックウィンドウを指定座標・サイズに一括配置するStream Deckスクリプト。EnumWindowsによるXLMAINクラス列挙とSW_SHOWNORMALの組み合わせで解決","md",{},true,"/autohotkey-excel-window-manager","misc-dev",false,"2026-04-08T00:00:00.000Z",{"title":5,"description":387},"2026-04/2026-04-08/autohotkey-excel-window-manager",[398,399,400,401,402,403],"AutoHotkey","PowerShell","Excel","Stream Deck","ウルトラワイドモニター","ウィンドウ管理","memo",null,"cHgvSdtdsNG3O7XqOONgNA1OeyxZVtT9CNIfhKusOlo",[],"https://log.eurekapu.com/favicon.svg",1775680421329]