LJMediaPlayer.exe が起動直後にエラーを出して終了する。LJMediaPlayer.exe は、映像表示面を作るのに ActiveMovie/DirectShow のインターフェイス IBasicVideo を前提にしている。Windows 11 の Windows Media Player 互換 DLL はこのインターフェイスを提供しなくなった。結果、親を持たないコントロールが残ったまま参照され、Delphi の例外 Control '' has no parent window. で落ちる。設定でもショーデータでも回避できない構造的な非互換。LJMediaPlayer.exe だけを自作の互換プログラムに差し替えた。LightJockey 本体・ショーデータ・操作手順は一切変更していない。両者の通信仕様(窓メッセージ)を実測と逆アセンブルで完全に解読し、同じ仕様で応答するようにしたうえで、再生には Windows 標準の MCI を使う。Windows 11 上の LightJockey 2.7 で、実ショー用ライブラリのキューリストを選択実行しても、照明が一切変わらない。 音も出ない。エラーダイアログが一瞬出て消える、という報告から調査を始めました。
当該キューリストの構造は次のとおりです(本プロジェクトで .Cl2 を解析して取得)。
行 1 MP Load File ...(音源 W のパス) 行 2 MP Play 行 3 Wait for MP at 00:00:03.66 → Cue 発火 行 4 Wait for MP at 00:00:06.40 : (以下 34 行、すべて Wait for MP at)
3 行目以降がすべて 「メディアプレイヤーが返す時刻を待つ」 命令です。 したがってメディアプレイヤーが動かない限り、キューリストは 3 行目で永久に停止し、Cue は 1 つも出ません。 「選択実行できない」という症状の正体はこれでした。
原因を決めつけないため、疑わしい順に実測で潰しました。
| 仮説 | 実測結果 | 判定 |
|---|---|---|
| Windows 11 に Windows Media Player 系が入っていない | wmp.dll / wmpdxm.dll / msdxm.ocx / quartz.dll はすべて存在。レガシー ActiveX の CoClass {22D6F312-B0F6-11D0-94AB-0080C74C7E95} もレジストリに登録済み | 否定 |
| 音源ファイルが再生できない | 同じレガシー ActiveX を直接生成してショーの音源 5 本すべての再生に成功。長さも正しく取得(例: 音源 W = 299.23 秒) | 否定 |
LJMediaPlayer.exe 自体が Windows 11 で起動しない |
単体では正常に起動する。メニュー・トランスポート・タイムコード表示まで正しく描画される(従来の社内記録にあった「単体起動すると即終了する」は誤りと判明) | 否定 |
| ショーデータ側の不備(音源パス等) | 絶対パスは実機に一致。原本 140 本のキューリストを走査したが、どれも同じ作りで異常なし | 否定 |
| プレイヤーの表示設定(窓位置・サイズ・最前面) | 設定ファイルを既定値へ戻して再試行 → 同じ場所・同じ時刻で同じエラー | 否定 |
| 読み込ませるファイルの種類(生メディア/Playback 定義ファイル) | .Mps(Playback 定義)に差し替えても同一のエラー | 否定 |
MP Start 命令を入れると、
LightJockey は Windows 11 でも正しくメディアプレイヤーを起動できます。壊れているのは起動後の 1 点だけでした。
エラーダイアログが数秒で消えてしまい、文言を目視で読み取れませんでした。 そこで、プロセスの生死と新しく現れる窓を 120 ミリ秒ごとに監視して、ダイアログの文字列を自動採取する観測プログラムを用意しました。 これにより、以下が確定しました。
15:18:50.690 LJMediaPlayer 起動(LightJockey の MP Start による) 15:18:50.723 窓のタイトルが 'Retrieving Info..' に変わる 15:18:53.986 ダイアログ出現 : "Control '' has no parent window." ← 起動の 3.30 秒後 15:18:59.982 LJMediaPlayer 終了
この 3.30 秒はキューリストの待ち時間と一致し、LightJockey が「ファイルを読み込め」という指示を送った瞬間に相当します。 別の回でも 3.24 秒後・同一文言で再現しました(計 4 回、100% 再現)。
Control '%s' has no parent window は Delphi(VCL)ランタイムが出す例外で、
「親ウィンドウを持たないコントロールに対して、ウィンドウハンドルを要求した」ときに発生します。
名前が空文字列であることから、実行時に動的生成されたコントロールだと分かります。
すなわち「メディアを読み込む際に作られる表示面が、親を持たないまま使われている」。
LJMediaPlayer.exe のバイナリを走査したところ、次の GUID が埋め込まれていました。
| GUID | 意味 |
|---|---|
{22D6F312-B0F6-11D0-94AB-0080C74C7E95} | レガシー Windows Media Player コントロール(CoClass) |
{22D6F311-B0F6-11D0-94AB-0080C74C7E95} | IMediaPlayer インターフェイス |
{56A868B5-0AD4-11CE-B03A-0020AF0BA770} | IBasicVideo インターフェイス(ActiveMovie / DirectShow 系) |
そこで、Windows 11 上でこのコントロールを生成し、必要なインターフェイスを実際に要求してみました (32bit・STA・メッセージポンプ有り。プレイヤー本体と同じ条件)。
| 要求したインターフェイス | 結果 | HRESULT |
|---|---|---|
IMediaPlayer | 取得できる | S_OK |
IOleObject | 取得できる | S_OK |
IOleInPlaceObject | 取得できる | S_OK |
IBasicVideo | 取得できない | 0x80004002 E_NOINTERFACE |
IVideoWindow | 取得できない | 0x80004002 E_NOINTERFACE |
IMediaControl | 取得できない | 0x80004002 E_NOINTERFACE |
IBasicAudio | 取得できない | 0x80004002 E_NOINTERFACE |
IMediaPosition | 取得できない | 0x80004002 E_NOINTERFACE |
IMediaPlayer は生きている)が、
ActiveMovie/DirectShow の内部インターフェイスを一切公開しない。
1998 年のプレイヤーはそれらを前提に映像表示面を組み立てるため、表示面が作れず、
親を持たないコントロールが残り、例外で落ちる。取れる道は 3 つありました。
| 案 | 内容 | 採否 |
|---|---|---|
| デジタルオーディオ命令へ変換 | LightJockey には別系統の音声再生命令がある。しかし公式ヘルプにより外部プレイヤー(Winamp)が必要と判明 | 不可 |
| 時刻待ちを経過時間待ちへ置換 | ショーデータ側で全行を書き換えれば LightJockey 単体で走るが、音楽との同期が失われる | 見送り |
| プレイヤーを差し替える | LightJockey が起動する実行ファイルを、同じ通信仕様を話す自作プログラムに置き換える。本体もデータも操作手順も無改変 | 採用 |
本プロジェクトでは既に、ハードウェア用 DLL について同種の差し替え(シム)を運用しており、実績のある手法です。
差し替えにあたり本物は LJMediaPlayer_orig.exe として保存し、ファイル名を戻すだけで原状復帰できるようにしました。
差し替えるには、LightJockey とメディアプレイヤーがどう会話しているかを知る必要があります。 両者は Windows のウィンドウメッセージで通信しており、公開資料はありません。次の 2 段階で解読しました。
ウィンドウクラス名を本物と同じ TLJMediaForm、タイトルを LightJockey MediaPlayer にした
受信専用プログラムを作り、本物と入れ替えて配置しました。LightJockey はこれを本物と誤認し、通常どおり通信してきます。
受信したメッセージを全件記録することで、次が判明しました。
| 受信したもの | 内容 |
|---|---|
WM_COPYDATA dwData=0x191 | 読み込むファイルのパス(ANSI 文字列・NUL 終端)。これがファイル読み込み指示 |
WM_COPYDATA dwData=0x3 | 現在のユーザライブラリのパス |
0x07F4 (WM_USER+1012) wParam=0 | 起動直後の生存確認。100 ミリ秒周期で 48 回送られ、応答が無いと約 5 秒で諦める |
0x07F4 wParam=5 | 再生開始。lParam に返信先のウィンドウハンドルが入る |
受信だけでは、プレイヤーが LightJockey へ何を返すべきかは分かりません。 そこで本物の実行ファイルを逆アセンブルし、送信箇所を特定しました。
mov eax, [esi+0x508] ; LightJockey が渡してきた返信先ハンドル test eax, eax jbe skip push 0 ; lParam push 3 ; wParam = 3(成功) ※失敗時は 4 push 0x7F4 ; メッセージ push eax call PostMessage
各コマンドの実行後に 3=成功 / 4=失敗 を返す設計でした。
これを返さないと LightJockey は「再生は終わった」と判断し、停止指示を送ってきます。
push 0 push offset 'TCuelist2Form' ; ← 報告先はキューリスト窓 call FindWindow mov esi, eax push [ebp+0x10] ; 時刻(Double = 8 バイト) push [ebp+0x0C] call 0x460AEC ; 時刻を 32bit に畳む変換関数 push eax ; lParam push 2 ; wParam = 2(位置報告) push 0x7F4 push esi call SendMessage
変換関数 0x460AEC の中身は次のとおりでした。
fld qword [ebp+8] ; 引数は Double(秒) trunc ; → 総秒数 frac × 100.0 → trunc ; → 1/100 秒 [ebp-4] = 時 [ebp-3] = 分 [ebp-2] = 秒 [ebp-1] = 1/100 秒 mov eax, [ebp-4] ; この 4 バイトを dword として返す
すなわち送るべき値は 時 | 分<<8 | 秒<<16 | 1/100秒<<24 です
(リトルエンディアンでこの並びになります)。並びを逆にすると LightJockey は一切反応しません。実際、
最初に逆順で実装したときは無反応でした。
| 方向 | メッセージ | 意味 |
|---|---|---|
| LJ → MP | WM_COPYDATA dwData=0x191 | ファイル読み込み(生メディア/Playback 定義のどちらでも可) |
| LJ → MP | WM_COPYDATA dwData=0x3 | 現在のユーザライブラリのパス通知 |
| LJ → MP | 0x07F4 wParam=0 | 生存確認(0 以外を返すと満足する) |
| LJ → MP | 0x07F4 wParam=5 | 再生開始(lParam = 返信先ハンドル) |
| LJ → MP | 0x07F4 wParam=6 | 停止(先頭へ戻す) |
| LJ → MP | 0x07F4 wParam=7 | 一時停止 |
| LJ → MP | 0x07F4 wParam=11 | クリップを閉じる |
| MP → LJ | 0x07F4 wParam=3 / 4 | コマンド完了の応答(3=成功 / 4=失敗)。PostMessage |
| MP → LJ | 0x07F4 wParam=2 | 再生位置の報告。lParam = 時 | 分<<8 | 秒<<16 | 1/100秒<<24。SendMessage・宛先はキューリスト窓 |
0x0A40(WM_USER+1600) というメッセージも送ります。
これを模倣して wParam=0x80 を LightJockey へ送ったところ、Web ブラウザが 3 本起動しました(3 回とも再現)。
0x0A40 はメディアプレイヤー専用ではなく、LightJockey 内部の汎用メッセージとして別機能にも配線されています。
未知のメッセージを総当たりで送るのは危険です。この 1 件以降、当てずっぽうを止めて逆アセンブルに切り替えました。
差し替えプログラムの構成は次のとおりです。外部ライブラリへの依存はありません。
| 項目 | 内容 |
|---|---|
| ウィンドウ | クラス名 TLJMediaForm / タイトル LightJockey MediaPlayer を自前で登録・生成(GUI フレームワーク任せでは指定できないため、素の Win32 API で作る) |
| 再生 | Windows 標準の MCI(winmm.dll)。open / play / stop / pause / status position。ActiveMovie を一切使わない |
| Playback 定義 | .Mps を渡された場合は中身を解析して実音源のパスを取り出す |
| 位置報告 | 100 ミリ秒ごとに、上記の符号化でキューリスト窓へ送信 |
| 応答 | 各コマンドの完了時に 3(成功)/4(失敗)を返信 |
| 窓の位置 | LightJockey 本体と同じディスプレイの右下に配置(複数画面環境で見失わないため) |
| 観測性 | 受信メッセージ・応答・再生位置を逐次ログ出力。現在状態を機械可読な形式でも出力 |
FindWindow(クラス名, NULL) は、
本物・自作いずれの窓に対しても 0(見つからない)を返します。クラス名とタイトルの 2 引数なら見つかります。
本物のプレイヤーも位置報告の宛先探索にこの 1 引数形式を使っているため、Windows 11 では本物側のこの経路も機能しない可能性があります。
差し替え版では、LightJockey がコマンドの lParam で渡してくるハンドルを優先して使うことで回避しました。
差し替え後、実ショー用ライブラリのキューリストを通常どおり選択実行しました。
キューリスト窓 : 8 → Wait for MP at 00:00:34.86 ← 現在行が音源に追従して進行
Current 00:00:28.28 ← 表示時刻=音源の再生位置
LightJockey 本体: Cue [STBY] ← Cue が実際に発火
差し替えプレイヤー: 再生位置 27,957 ミリ秒 / 状態 playing
Wait for MP at 00:00:03.66 を音源の 3.66 秒で通過し、4 行目・5 行目…と進行。
Cue が順に発火することを確認しました。LightJockey 本体・ショーデータ・操作手順はいずれも無改変です。
あわせて、応答実装の効果も数値で確認できました。
| 区間 | 応答なし | 応答あり |
|---|---|---|
| ファイル読み込み指示 → 再生開始指示 | 5.06 秒(タイムアウト) | 0.07 秒 |
| 再生開始 → 停止指示(LightJockey が自発的に送る) | 3.7 秒で停止 | 停止しない |
LJMediaPlayer.exe)1 本だけを差し替えます。| ファイル | 役割 |
|---|---|
Install.cmd | これをダブルクリックすると入ります |
Uninstall.cmd | 元に戻すとき |
MediaPlayer\LJMediaPlayer.exe | 差し替え版のプレイヤー本体(約 68 MB) |
Install.ps1 / Uninstall.ps1 | 上の 2 つが内部で使うもの。直接触らなくて構いません |
Install.cmd を実行してください。
Install.cmd をダブルクリックします。黒い画面が出て、自動で進みます。LightJockey.exe があるフォルダを聞かれます。エクスプローラーのアドレス欄からコピーして貼り付け、Enter を押してください。C:\Program Files (x86)\Martin Professional\Martin LightJockey)C:\Program Files (x86)\ の下に LightJockey が入っている場合、書き込みに管理者権限が要ることがあります。
うまくいかないときは Install.cmd を右クリック →「管理者として実行」でお試しください。
今までと同じです。新しい操作はありません。LightJockey を起動 → いつもどおりキューリストを選んで実行 → 小さなプレイヤーの窓が自動で出て音が鳴り始め、明かりが音に合わせて変わります。
Current の時刻が音と同じ速さで進むCue の欄に、キューの名前が次々に出る差し替え版の窓は、LightJockey 本体と同じ画面(モニタ)の右下に出ます。複数のモニタをお使いでも見失いません。窓はドラッグで自由に動かせます。
Uninstall.cmd をダブルクリックします。控えておいた本物(LJMediaPlayer_orig.exe)が、元の名前に戻ります。LightJockey 本体・ショーデータには何も起きません。
| 項目 | 状態 |
|---|---|
| 音楽ファイルの再生(WAV / MP3 など) | できます(実機確認済み) |
| 音に合わせたキューの発火(時刻同期) | できます(実機確認済み) |
| 停止・先頭に戻す | できます(実機確認済み) |
Playback 定義ファイル(.Mps)の読み込み | できます |
| 日本語のファイル名の音源 | できます |
| 一時停止・途中からの再生(Seek) | 組み込んでありますが実機未確認です |
| 動画ファイルの再生 | できません(現在は音声のみ)。動画同期をお使いの場合はご相談ください |
Install.cmd をダブルクリックしても一瞬で閉じるLightJockey.exe があるフォルダを貼り付けてください。分からない場合は、デスクトップの LightJockey のアイコンを右クリック →「ファイルの場所を開く」で表示されるフォルダです。LJMediaPlayer_orig.exe)は絶対に削除しないでください。元に戻せなくなります。調査の過程で、本プロジェクトの互換ソフト開発に直接効く仕様が確定しました。
従来は実データに現れる 9 種のみが判明しており、うち 2 種は「推定」でした。 今回、LightJockey 本体の分岐表を解析して全 33 種の対応が取れ、推定だった 2 種も確定しました。
| コード | 命令 | コード | 命令 | コード | 命令 |
|---|---|---|---|---|---|
| 01 | Wait for CD at | 0C | DA Stop | 17 | MP Stop(推定→確定) |
| 02 | Wait for MTC at | 0D | Wait for DA at | 18 | MP Pause |
| 03 | Wait PC-Clock at | 0E | DA Cue File | 19 | MP Start(新規) |
| 04 | Wait Elapsed | 0F | DA Seek | 1A | MP Close |
| 05 | Load BGCue | 10 | DA Play | 1B | MP Close Clip |
| 06 | Link Cuelist | 12 | Execute | 1C | MP Seek(推定→確定) |
| 07 | CD Play Track | 13 | AutoSeek MTC | 1D | DA Timecode Factor |
| 08 | CD Play Time | 14 | MP Load File | 1E | Command |
| 09 | CD Stop | 15 | MP Play | 1F | Flash Seq(新規) |
| 0A | Goto Line | 16 | Wait for MP at | 20/21 | Tracking on / off |
| 0B | DA Play File | 22 | Wait PC date-change | — | — |
※ 実データに出現するのは 9 種のみのため、それ以外は「名称は確定したが実データとの突き合わせは未実施」の扱いです。
固定長 794 バイト。原本 38 本すべてで検算し、38/38 で成立しました。
| 位置 | 型 | 内容 |
|---|---|---|
+0x000 | u32 | 常に 0 |
+0x004 | ShortString | メディアファイルのパス(先頭 1 バイトが長さ) |
+0x10A | i32 × 4 | Left / Top / Width / Height(再生窓の位置とサイズ) |
検証中に音源 W の 2 分 30 秒以降でノイズが聞こえたため、原因が差し替えプレイヤー側にないかを確認しました。
すなわち元の音源ファイルに最初から入っていた欠陥で、今回の差し替えとは無関係です (録音・取り込み時のドロップアウトと推定)。孤立サンプルの補間による修復は可能で、必要なら別ファイルとして生成できます。
| 項目 | 内容 |
|---|---|
| 未検証のコマンド | MP Seek / MP Pause は実装済みだが実機未確認(今回のショーデータが使用していないため) |
| 動画ファイル | 本来のメディアプレイヤーは動画も扱える。差し替え版は現状は音声のみ。映像同期が必要になった場合は別途対応 |
| 原状復帰 | LJMediaPlayer_orig.exe を元の名前に戻すだけ。設定ファイル・ショーデータは無改変のため副作用なし |
| 適用範囲 | 本件は Windows 11 に限らず、レガシー ActiveX の内部インターフェイスが失われた環境すべてに該当します。今後の Windows 更新で改善する見込みは低いと考えられます |
「Windows 11 でメディアプレイヤーが連結できず選択実行できない」という症状は、 ショーデータの不備でも設定の問題でもなく、1998 年設計のプレイヤーが依存していた Windows の内部インターフェイスが 現在の Windows から失われたことが原因でした。
対策として、そのプレイヤー 1 本だけを、同じ通信仕様を話す互換プログラムに差し替えました。 通信仕様は公開資料が存在しないため、実機での通信記録と、実行ファイルの逆アセンブルの両面から解読しています。 結果、LightJockey 本体・ショーデータ・現場の操作手順を一切変えることなく、Windows 11 上で音楽同期が復活しました。