[{"data":1,"prerenderedAt":447},["ShallowReactive",2],{"content-/obs-chrome-window-position-script":3,"all-pages-for-dir":445,"og-image-/obs-chrome-window-position-script":446},{"id":4,"title":5,"body":6,"category":426,"description":427,"extension":428,"meta":429,"navigation":430,"ogImage":431,"path":432,"project_name":433,"published":434,"publishedAt":435,"seo":436,"stem":437,"tags":438,"todo":431,"unpublished":434,"updatedAt":431,"__hash__":444},"pages/2026-05/2026-05-02/obs-chrome-window-position-script.md","OBSウルトラワイド配信向けにChromeウィンドウ位置を一発固定するPowerShellスクリプトとStream Deck連携",{"type":7,"value":8,"toc":415},"minimark",[9,13,18,34,48,52,63,66,74,78,81,88,95,136,154,162,173,185,191,195,198,205,212,242,245,249,256,267,274,296,307,320,324,331,354,357,360,408,411],[10,11,12],"p",{},"OBS Studioで配信するとき、ウルトラワイドモニター（3440x1440、座標3840,0）の決まった場所にChromeウィンドウを置く儀式を毎回やっていた。マウスでつかんで、引っ張って、辺をドラッグして、OBSのキャプチャ枠に合わせて目視で揃える。1回30秒、配信前の儀式が地味に消耗するので、PowerShellで一発配置スクリプトを書いた。書き始めたら、文字化け、別アプリ誤認、PowerShellの予約変数、Stream Deckのダイアログと、4つの罠を順番に踏み抜いた一日になった。",[14,15,17],"h2",{"id":16},"朝-過去のexcel版スクリプトを思い出す","朝: 過去のExcel版スクリプトを思い出す",[10,19,20,21,25,26,29,30,33],{},"去年、Excelウィンドウを毎回同じ場所に置きたくて ",[22,23,24],"code",{},"Resize-ExcelWindow.ps1"," を書いていた。Win32 APIの ",[22,27,28],{},"SetWindowPos"," をP/Invokeで呼び出すだけのやつ。あれをChrome用に書き換えれば終わりだと判断して、",[22,31,32],{},"C:\\Users\\numbe\\Git_repo\\OBS\\Resize-ChromeWindow.ps1"," を新規作成した。",[10,35,36,37,40,41,44,45,47],{},"雛形は迷わず書けた。",[22,38,39],{},"Add-Type"," でWin32 APIを定義して、",[22,42,43],{},"Get-Process"," でChromeプロセスを引いて、メインウィンドウハンドルに ",[22,46,28],{}," を投げる。10分で初版が動くはずだった。",[14,49,51],{"id":50},"罠1-日本語コメントが文字化け","罠1: 日本語コメントが文字化け",[10,53,54,55,58,59,62],{},"書き終えて実行したら、コメント行が全部 ",[22,56,57],{},"?????"," になっていた。スクリプト自体は動くけれど、ターミナル出力の ",[22,60,61],{},"Write-Host \"Chromeウィンドウを配置しました\""," が壊れて読めない。",[10,64,65],{},"PowerShellのデフォルトエンコーディングと、ファイル保存時のエンコーディングがズレている定番の問題。BOM付きUTF-8で保存し直しても、Stream Deck経由で実行したときに別の挙動になる予感がした。",[10,67,68,69,73],{},"ここで方針を変えて、",[70,71,72],"strong",{},"コメント・出力メッセージは全部英語に書き換えた","。配信用の個人スクリプトに日本語を残しても自分しか読まないので、英語で十分。文字化けの可能性をゼロにする方を取った。",[14,75,77],{"id":76},"罠2-aqua-voice-vs-code-memento-mori-までchrome扱い","罠2: Aqua Voice / VS Code / Memento Mori まで「Chrome」扱い",[10,79,80],{},"英語化が済んで再実行したら、Chromeのウィンドウだけでなく、Aqua Voice（音声入力ツール）、VS Code、自作アプリの Memento Mori まで一緒に画面中央に飛んできた。配信中にVS Codeが勝手に動いたら事故なので、これは絶対に潰す必要があった。",[10,82,83,84,87],{},"原因は、ウィンドウクラス名 ",[22,85,86],{},"Chrome_WidgetWin_1"," だけで対象を絞り込んでいたこと。Chromiumベースのアプリは全部このクラス名を共有しているので、Electron系（VS Code、Memento Mori）も Chromiumベースのユーティリティ（Aqua Voice）も全部ヒットしてしまう。",[10,89,90,91,94],{},"プロセス名 ",[22,92,93],{},"chrome.exe"," の追加フィルタを噛ませて解決した。",[96,97,102],"pre",{"className":98,"code":99,"language":100,"meta":101,"style":101},"language-powershell shiki shiki-themes vitesse-light vitesse-light","# Get only real Chrome windows (filter by both window class AND process name)\n$chromeWindows = [Win32]::EnumerateWindows() | Where-Object {\n    $_.ClassName -eq 'Chrome_WidgetWin_1' -and\n    (Get-Process -Id $_.ProcessId -ErrorAction SilentlyContinue).ProcessName -eq 'chrome'\n}\n","powershell","",[22,103,104,112,118,124,130],{"__ignoreMap":101},[105,106,109],"span",{"class":107,"line":108},"line",1,[105,110,111],{},"# Get only real Chrome windows (filter by both window class AND process name)\n",[105,113,115],{"class":107,"line":114},2,[105,116,117],{},"$chromeWindows = [Win32]::EnumerateWindows() | Where-Object {\n",[105,119,121],{"class":107,"line":120},3,[105,122,123],{},"    $_.ClassName -eq 'Chrome_WidgetWin_1' -and\n",[105,125,127],{"class":107,"line":126},4,[105,128,129],{},"    (Get-Process -Id $_.ProcessId -ErrorAction SilentlyContinue).ProcessName -eq 'chrome'\n",[105,131,133],{"class":107,"line":132},5,[105,134,135],{},"}\n",[10,137,138,141,142,145,146,149,150,153],{},[22,139,140],{},"Get-Process -Id"," で ",[22,143,144],{},"ProcessName"," を引いて、",[22,147,148],{},"chrome"," 完全一致で絞る。",[22,151,152],{},"-ErrorAction SilentlyContinue"," を入れたのは、ウィンドウ列挙とプロセス取得の間にプロセスが消える可能性を考えてのこと。これで Chrome 本体だけが対象になった。",[14,155,157,158,161],{"id":156},"罠3-pid-は-powershell-の予約変数だった","罠3: ",[22,159,160],{},"$pid"," は PowerShell の予約変数だった",[10,163,164,165,168,169,172],{},"プロセスIDを格納する変数として、何の気なしに ",[22,166,167],{},"$pid = $window.ProcessId"," と書いていた。実行すると ",[22,170,171],{},"Cannot overwrite variable PID because it is read-only or constant."," というエラー。",[10,174,175,177,178,181,182,184],{},[22,176,160],{}," は ",[70,179,180],{},"現在のPowerShellプロセスのプロセスIDを保持する自動変数"," で、上書き不可だった。Windows系のスクリプトを書いていてポート絞りで ",[22,183,160],{}," を使ったときも踏んだことがある罠で、また同じところに落ちた。",[10,186,187,190],{},[22,188,189],{},"$processId"," にリネームして即解決。地味だが、配信前の本番でこのエラーに当たったら冷や汗ものなので、開発中に踏んでおいてよかった。",[14,192,194],{"id":193},"罠4-配置はできたでも上のバーがobsに映ってる","罠4: 配置はできた、でも上のバーがOBSに映ってる",[10,196,197],{},"スクリプトが動くようになって、「Chromeを画面中央(3840+760, 100)に1920x1080で置く」が一発でできるようになった。配信を立ち上げてキャプチャを確認したら、ブックマークバーとURL欄とタブバーがOBSの収録領域にがっつり映り込んでいた。",[10,199,200,201,204],{},"OBS側のキャプチャ領域は座標 (4600, 5) から (6520, 1260) の 1920x1255。",[70,202,203],{},"ブラウザの上部バー（ブックマーク・URL欄・タブ）はOBSの画面外に出して、コンテンツだけを映したい","。",[10,206,207,208,211],{},"上端をOBSの上端 -175px に逃がせば、上部バーがちょうど画面外に追い出される。下端は OBS キャプチャ下端 y=1260 にぴったり合わせる。Chromeのウィンドウ高さを 1255+175 = 1430px... ではなく、上端を y=5-175=-170 の位置に取って、高さは 1260-(-170) = 1430、ではなく実測で調整して最終 ",[70,209,210],{},"1920x1255 @ (4600, 5)","（上端は OBS 上端と同じ y=5、上部UIはOBSの上端で見切れる扱い）に落ち着いた。",[96,213,215],{"className":98,"code":214,"language":100,"meta":101,"style":101},"# Final coordinates: tuck top bar above OBS capture area, align bottom to OBS bottom\n$targetX = 4600\n$targetY = 5\n$targetWidth = 1920\n$targetHeight = 1255\n",[22,216,217,222,227,232,237],{"__ignoreMap":101},[105,218,219],{"class":107,"line":108},[105,220,221],{},"# Final coordinates: tuck top bar above OBS capture area, align bottom to OBS bottom\n",[105,223,224],{"class":107,"line":114},[105,225,226],{},"$targetX = 4600\n",[105,228,229],{"class":107,"line":120},[105,230,231],{},"$targetY = 5\n",[105,233,234],{"class":107,"line":126},[105,235,236],{},"$targetWidth = 1920\n",[105,238,239],{"class":107,"line":132},[105,240,241],{},"$targetHeight = 1255\n",[10,243,244],{},"数値はOBSのキャプチャプレビューを見ながら何度か微調整した。座標を直接書き換えて再実行、を5回くらい繰り返して、ブックマークバーが消えて下端のページコンテンツも切れない位置を探り当てた。",[14,246,248],{"id":247},"罠5-stream-deckから-ps1-を起動するとダイアログが出る","罠5: Stream Deckから .ps1 を起動するとダイアログが出る",[10,250,251,252,255],{},"スクリプトが完成したので、Stream Deckの「System: Open」アクションに ",[22,253,254],{},"Resize-ChromeWindow.ps1"," のパスを登録して、ボタン一発で起動できるようにする予定だった。",[10,257,258,259,262,263,266],{},"押したら ",[70,260,261],{},"「.ps1ファイルを何で開きますか？」のダイアログ"," がデスクトップ中央に出てきた。Windowsのファイル関連付けで ",[22,264,265],{},".ps1"," は「メモ帳」になっていたり、ユーザーが開くアプリを選ぶようになっているので、Stream Deckからの「Open」では PowerShell が直接起動しない。",[10,268,269,270,273],{},"これは ",[22,271,272],{},".bat"," ラッパーを噛ませる定石で解決。",[96,275,279],{"className":276,"code":277,"language":278,"meta":101,"style":101},"language-bat shiki shiki-themes vitesse-light vitesse-light","@echo off\nREM Launch Resize-ChromeWindow.ps1 with no profile for faster startup\npowershell.exe -NoProfile -ExecutionPolicy Bypass -File \"%~dp0Resize-ChromeWindow.ps1\"\n","bat",[22,280,281,286,291],{"__ignoreMap":101},[105,282,283],{"class":107,"line":108},[105,284,285],{},"@echo off\n",[105,287,288],{"class":107,"line":114},[105,289,290],{},"REM Launch Resize-ChromeWindow.ps1 with no profile for faster startup\n",[105,292,293],{"class":107,"line":120},[105,294,295],{},"powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"%~dp0Resize-ChromeWindow.ps1\"\n",[10,297,298,299,302,303,306],{},"ポイントは ",[22,300,301],{},"-NoProfile"," で、PowerShellの起動時に ",[22,304,305],{},"$PROFILE"," の読み込みをスキップする。普段使っているプロファイルが重いので、これがあるとないとで起動体感が0.5秒くらい違う。Stream Deck からの「ボタンを押した瞬間に動いてほしい」用途では、この差が大きい。",[10,308,309,312,313,316,317,319],{},[22,310,311],{},"%~dp0"," でバッチファイル自身のディレクトリを取れるので、",[22,314,315],{},"OBS"," ディレクトリごとどこに移動しても動く。Stream Deckには ",[22,318,272],{}," の方を登録して、ダイアログ問題は完全に消えた。",[14,321,323],{"id":322},"締め-readme-を残す","締め: README を残す",[10,325,326,327,330],{},"最後に ",[22,328,329],{},"README.md"," を書いた。次に触るのは半年後の自分なので、",[332,333,334,345,348],"ul",{},[335,336,337,338,340,341,344],"li",{},"どのファイルが何をするのか（",[22,339,254],{}," 本体、",[22,342,343],{},"Resize-ChromeWindow.bat"," ラッパー）",[335,346,347],{},"座標の意味（OBSキャプチャ領域とブラウザ上部バーの関係）",[335,349,350,351,353],{},"Stream Deck への登録手順（",[22,352,272],{}," の方を登録すること）",[10,355,356],{},"を書き残した。半年後の自分が「なんで .bat が並んでるんだっけ」「なんで -175px なんだっけ」と悩まないために、罠4と罠5の経緯だけはちゃんと文字に残しておいた。",[14,358,359],{"id":359},"学び",[332,361,362,373,388,402],{},[335,363,364,204,367,369,370,372],{},[70,365,366],{},"Chromiumベースアプリの判別はクラス名だけでは無理",[22,368,86],{}," は VS Code、Electron系、Aqua Voice等が共有している。プロセス名 ",[22,371,93],{}," との AND で絞るのが必須",[335,374,375,380,381,383,384,387],{},[70,376,377,379],{},[22,378,160],{}," は PowerShell 自動変数なので使うな","。プロセスIDを入れたいときは ",[22,382,189],{}," や ",[22,385,386],{},"$targetPid"," にする",[335,389,390,204,396,398,399,401],{},[70,391,392,393,395],{},"Stream Deckから ",[22,394,265],{}," 直接起動は不可",[22,397,272],{}," ラッパー + ",[22,400,301],{}," で起動高速化までセットで設計する",[335,403,404,407],{},[70,405,406],{},"PowerShellスクリプトの日本語コメントは文字化けリスクがある","。配信用の個人ツールなら最初から英語で書いた方が安全",[10,409,410],{},"配信前の手動配置の儀式が、Stream Deckのワンボタンに置き換わった。30秒×毎日 = 月15分の節約だが、何より「儀式を忘れて変な場所のChromeが配信に映る事故」がゼロになったのが大きい。",[412,413,414],"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":101,"searchDepth":114,"depth":114,"links":416},[417,418,419,420,422,423,424,425],{"id":16,"depth":114,"text":17},{"id":50,"depth":114,"text":51},{"id":76,"depth":114,"text":77},{"id":156,"depth":114,"text":421},"罠3: $pid は PowerShell の予約変数だった",{"id":193,"depth":114,"text":194},{"id":247,"depth":114,"text":248},{"id":322,"depth":114,"text":323},{"id":359,"depth":114,"text":359},"dev","OBS Studioで3440x1440ウルトラワイドモニター(座標3840,0)を使う配信で、毎回Chromeを手動で動かしていた手間をPowerShellで撲滅。文字化け→英語化、Aqua Voice/VS Code/Memento Mori誤認→プロセス名フィルタ、$pid予約変数衝突、Stream Deckの.ps1ダイアログ問題まで一日で潰した記録。","md",{},true,null,"/obs-chrome-window-position-script","daily-log",false,"2026-05-02T00:00:00.000Z",{"title":5,"description":427},"2026-05/2026-05-02/obs-chrome-window-position-script",[439,440,441,442,443],"OBS Studio","PowerShell","Chrome","Stream Deck","ウルトラワイドモニター","YqgKh2rBuX_07_9hxNJzpmT-UNdq4hgOm2HKcLf6mw0",[],"https://log.eurekapu.com/og/blog/obs-chrome-window-position-script.png?v=2026-05-02T00%3A00%3A00.000Z&title=OBS%E3%82%A6%E3%83%AB%E3%83%88%E3%83%A9%E3%83%AF%E3%82%A4%E3%83%89%E9%85%8D%E4%BF%A1%E5%90%91%E3%81%91%E3%81%ABChrome%E3%82%A6%E3%82%A3%E3%83%B3%E3%83%89%E3%82%A6%E4%BD%8D%E7%BD%AE%E3%82%92%E4%B8%80%E7%99%BA%E5%9B%BA%E5%AE%9A%E3%81%99%E3%82%8BPowerShell%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%A8Stream%20Deck%E9%80%A3%E6%90%BA&author=Kei%20Komatsu&sig=0a4de8ed5bc6bb40",1782528832094]