fix: initialize selected runtime for live playout

This commit is contained in:
2026-07-28 20:44:20 +09:00
parent 18d40e892f
commit 818acad646
24 changed files with 906 additions and 391 deletions

View File

@@ -17,10 +17,15 @@
## 확인된 x64 COM 등록
현재 개발 장비에서 확인할 등록 기준은 다음과 같습니다.
회사 표준 K3D 설치 루트는 정확히 `C:\K3DAsyncEngine`입니다. 다른 드라이브나
같은 하위 구조를 가진 대체 루트는 허용하지 않습니다. 현재 개발 장비에서 확인할 등록
기준은 다음과 같습니다.
| 항목 | 기대값 |
|---|---|
| 설치 루트 | `C:\K3DAsyncEngine` |
| Native DLL | `C:\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll` |
| C# Interop DLL | `C:\K3DAsyncEngine\Bin\x64\C#\Interop.K3DAsyncEngineLib.dll` |
| 레지스트리 뷰 | `HKLM\SOFTWARE\Classes``Registry64`; 관련 HKCU override 없음 |
| TypeLib GUID | `{2B7F2D64-3A8D-401C-BE73-5C0747BA342C}` |
| TypeLib 버전/대상 | `1.0` / `0\win64` |
@@ -28,9 +33,10 @@
| ProgID | `K3DAsyncEngine.KAEngine.1` |
| KAEventHandler CLSID / ProgID | `{39828C77-EFF0-4E59-979B-8673C028C718}` / `K3DAsyncEngine.KAEventHandler.1` |
| ThreadingModel | `Apartment` |
| DLL PE 대상 | TypeLib, KAEngine, KAEventHandler 모두 `AMD64` |
| 등록 경로 | TypeLib, KAEngine, KAEventHandler 모두 위의 정확한 Native DLL |
| DLL PE 대상 | Native DLL과 C# Interop DLL 모두 `AMD64` |
[Inspect-K3DRegistration.ps1](../scripts/Inspect-K3DRegistration.ps1)은 64비트 HKLM 등록에서 GUID, 버전과 `win64` TypeLib을 확인합니다. KAEngine과 KAEventHandler 각각의 CLSID→ProgID 및 ProgID→CLSID 양방향 매핑, `Apartment` 모델, `InprocServer32` 존재와 AMD64 PE 헤더 검사합니다. HKCU 64비트 `Software\Classes`에 같은 TypeLib GUID, CLSID 또는 ProgID override가 하나라도 있으면 fail-closed합니다. 레지스트리와 파일을 읽기만 하며 COM을 활성화하거나 등록을 변경하지 않습니다.
[Inspect-K3DRegistration.ps1](../scripts/Inspect-K3DRegistration.ps1)은 64비트 HKLM 등록에서 GUID, 버전과 `win64` TypeLib을 확인합니다. Registry64의 TypeLib, KAEngine 및 KAEventHandler가 모두 정확히 `C:\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll`을 가리켜야 합니다. KAEngine과 KAEventHandler 각각의 CLSID→ProgID 및 ProgID→CLSID 양방향 매핑, `Apartment` 모델, `InprocServer32` 존재와 AMD64 PE 헤더 검사하고, 표준 위치의 `Interop.K3DAsyncEngineLib.dll`도 존재하는 AMD64 파일인지 확인합니다. HKCU 64비트 `Software\Classes`에 같은 TypeLib GUID, CLSID 또는 ProgID override가 하나라도 있으면 fail-closed합니다. 레지스트리와 파일을 읽기만 하며 COM을 활성화하거나 등록을 변경하지 않습니다.
`MBN_STOCK_WEBVIEW.PlayoutSmoke --probe`는 런타임과 같은 검사기를 통해 KAEngine과 KAEventHandler 등록을 함께 확인합니다. 이 probe도 COM 객체를 생성하지 않습니다.
@@ -39,17 +45,17 @@ powershell -NoProfile -ExecutionPolicy Bypass `
-File .\scripts\Inspect-K3DRegistration.ps1
```
검사가 실패하면 x86 등록으로 대체하거나 DLL을 앱 폴더에 복사하지 않습니다. 벤더가 제공한 x64 설치 프로그램과 라이선스 절차로 장비 상태를 복구한 뒤 다시 검사합니다.
검사가 실패하면 다른 설치 루트나 x86 등록으로 대체하거나 DLL을 앱 폴더에 복사하지 않습니다. 벤더가 제공한 x64 설치 프로그램과 라이선스 절차로 `C:\K3DAsyncEngine`의 표준 설치 및 Registry64 등록 상태를 복구한 뒤 다시 검사합니다.
## 런타임 참조 방식
앱은 빌드 시점의 `Interop.K3DAsyncEngineLib.dll`을 참조하거나 패키징하지 않습니다. 런타임에는 HKLM Registry64의 TypeLib·KAEngine·KAEventHandler 세 등록이 모두 같은 `DLL\x64\Release\K3DAsyncEngine.dll`을 가리키고 HKCU override가 없을 때만, 그 벤더 루트의 정확한 `Bin\x64\C#\Interop.K3DAsyncEngineLib.dll` 하나를 사용합니다. 상위 폴더 검색이나 `obj`/`artifacts` fallback은 없습니다. 두 파일과 모든 상위 경로는 reparse point가 아니어야 하며 x64 PE인지 확인합니다. COM 객체와 호출 세부 사항은 `IPlayoutEngine` 구현 안에 격리합니다.
앱은 빌드 시점의 `Interop.K3DAsyncEngineLib.dll`을 참조하거나 패키징하지 않습니다. 런타임에는 HKLM Registry64의 TypeLib·KAEngine·KAEventHandler 세 등록이 모두 정확히 `C:\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll`을 가리키고 HKCU override가 없을 때만 `C:\K3DAsyncEngine\Bin\x64\C#\Interop.K3DAsyncEngineLib.dll` 사용합니다. 다른 설치 루트, 상위 폴더 검색 `obj`/`artifacts` fallback은 없습니다. 두 파일과 모든 상위 경로는 reparse point가 아니어야 하며 x64 PE인지 확인합니다. COM 객체와 호출 세부 사항은 `IPlayoutEngine` 구현 안에 격리합니다.
등록된 네이티브 `K3DAsyncEngine.dll`과 설치된 C# Interop은 모두 로드 전에 운영자가 승인한 SHA-256과 일치해야 합니다. 현재 프로세스 환경의 `MBN_STOCK_K3D_NATIVE_SHA256``MBN_STOCK_K3D_INTEROP_SHA256`에 각각 공백 없는 64자리 16진수만 허용하며, 일반 Test/Live 어댑터와 PGM 연결 전용 진단이 같은 두 핀을 사용합니다. 먼저 벤더 배포 해시, 신뢰된 설치 매체 또는 관리자 검수로 **두 파일을 독립적으로 승인한 뒤** 그 정확한 파일들을 대상으로 다음 값을 설정합니다.
```powershell
$approvedNative = '<vendor-or-admin-approved-x64-native-file>'
$approvedInterop = '<vendor-or-admin-approved-x64-interop-file>'
$approvedNative = 'C:\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll'
$approvedInterop = 'C:\K3DAsyncEngine\Bin\x64\C#\Interop.K3DAsyncEngineLib.dll'
$nativeHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $approvedNative).Hash
$interopHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $approvedInterop).Hash
if ($nativeHash -notmatch '^[0-9A-Fa-f]{64}$' -or
@@ -177,7 +183,7 @@ COM 경로에는 `KTAPConnect`와 성공 후 최대 한 번의 `Disconnect`만
성공 JSON의 `lastKtapConnectState: "accepted-unconfirmed"`, `completed: true`, `outcomeUnknown: false`, `renderCommandSurfaceExposed: false`, `renderCommandAttempted: false`를 확인하고 같은 시각의 Network Monitoring `[R] HELLO``[S] SUCCESS HELLO`를 사람이 확인합니다. `GetScenePlayer`를 호출하지 않으므로 이 진단의 성공은 장면 송출 준비 완료를 의미하지 않습니다.
x64 SDK의 네이티브 DLL과 정식 Interop은 위의 Registry64 단일 경로·reparse ancestry·AMD64 PE·운영자별 SHA-256 핀을 모두 통과해야 합니다. 두 파일을 쓰기/교체할 수 없게 연 핸들은 COM 활성화와 사용을 포함한 프로세스 수명 동안 유지합니다. Interop 로드 후에는 assembly/TypeLib 버전, COM import GUID와 허용 메서드 서명도 확인합니다. vendor DLL은 Git 또는 MSIX에 복사하지 않습니다.
x64 SDK의 네이티브 DLL과 정식 Interop은 위의 회사 표준 고정 경로·Registry64 일치·reparse ancestry·AMD64 PE·운영자별 SHA-256 핀을 모두 통과해야 합니다. 두 파일을 쓰기/교체할 수 없게 연 핸들은 COM 활성화와 사용을 포함한 프로세스 수명 동안 유지합니다. Interop 로드 후에는 assembly/TypeLib 버전, COM import GUID와 허용 메서드 서명도 확인합니다. vendor DLL은 Git 또는 MSIX에 복사하지 않습니다.
### 실제 PGM 컷 시퀀스 검증
@@ -198,7 +204,7 @@ Connect → Prepare(5001) → Play → 5초 관찰
| `5001.t2s` | `99CE3B689A42D8C42BEB09A86FA10C2D7C1AEF4F50D324D81276C1A1E4C4D8A7` |
| `5006.t2s` | `25CD0AE931F51E4E3B84CE3E6FD21A40DB85464F157A23CC3511D63B336D8757` |
재검증이 승인된 경우에는 임의의 `dotnet run` 대신 [Invoke-PgmCutsSequenceEvidence.ps1](../scripts/Invoke-PgmCutsSequenceEvidence.ps1)을 사용합니다. 먼저 x64 빌드 산출물과 두 컷의 변경이 없는지 독립적으로 검토하고, vendor/관리자가 승인한 네이티브·Interop 해시와 검토한 runner 산출물 해시를 인수로 전달합니다. `Approved*Sha256` 값은 실행 시점의 파일을 단순 계산해 곧바로 승인한 값으로 사용하지 않습니다. 실제 vendor 설치 경로와 컷 루트는 로컬 승인 기록에만 보관합니다.
재검증이 승인된 경우에는 임의의 `dotnet run` 대신 [Invoke-PgmCutsSequenceEvidence.ps1](../scripts/Invoke-PgmCutsSequenceEvidence.ps1)을 사용합니다. 먼저 x64 빌드 산출물과 두 컷의 변경이 없는지 독립적으로 검토하고, vendor/관리자가 승인한 네이티브·Interop 해시와 검토한 runner 산출물 해시를 인수로 전달합니다. `Approved*Sha256` 값은 실행 시점의 파일을 단순 계산해 곧바로 승인한 값으로 사용하지 않습니다. 두 K3D 파일이 회사 표준 고정 경로에 있고 Registry64와 일치하는지 확인하며 컷 루트는 로컬 승인 기록에만 보관합니다.
```powershell
$smoke = '<reviewed-x64-runner>\MBN_STOCK_WEBVIEW.PlayoutSmoke.dll'
@@ -390,7 +396,7 @@ Network Monitoring 최종 증분은 HELLO 1/1, 5001/5074 LOAD 각각 1/1, 5001 P
Round H cleanup은 승인된 TAKE OUT 1회와 두 scene unload, disconnect, 앱·진단 listener 종료, 회차 전용 Live 설정과 승인 환경 제거, PGM/Network Monitoring 창 상태 복원으로 끝났습니다. 정상 회차라 추정 rollback은 실행하지 않았습니다. 장애 rollback은 기본 `DryRun`/`Disabled`로 복귀하거나 조직 절차로 직전 승인 패키지를 복원하는 범위이며, vendor DLL·COM 등록·라이선스·실제 자산은 수정하거나 저장소에 넣지 않습니다. 실제 Live PGM 검증 범위는 허용된 5001/5074뿐이고, 35개 scene 전체 완료 근거는 자동 테스트·55-query 실데이터 smoke·매트릭스입니다.
패키지 스모크에서는 벤더 x64 COM이 장비에 정식 등록되어 있어야 합니다. MSIX에 벤더 DLL을 복사해 활성화 오류를 우회하지 않습니다. 패키지 컨텍스트에서 COM 활성화가 막히면 Live 엔진을 `Faulted`/`Disconnected`로 유지하고 HRESULT와 등록 검사 결과만 보고합니다.
패키지 스모크에서는 벤더 x64 COM이 회사 표준 K3D 경로를 대상으로 Registry64에 정식 등록되어 있어야 합니다. MSIX에 벤더 DLL을 복사해 활성화 오류를 우회하지 않습니다. 패키지 컨텍스트에서 COM 활성화가 막히면 Live 엔진을 `Faulted`/`Disconnected`로 유지하고 HRESULT와 등록 검사 결과만 보고합니다.
## 장애 및 롤백
@@ -416,4 +422,4 @@ Round H cleanup은 승인된 TAKE OUT 1회와 두 scene unload, disconnect, 앱
- 운영 호스트, 채널, 창 제목 및 로컬 허용 목록이 든 설정
- 실제 출력 캡처나 비밀정보가 포함된 진단 로그
벤더 바이너리와 자산은 승인된 설치·배포 위치에서 관리하고, 앱 저장소에는 COM 중립 인터페이스와 안전한 설정 예시만 유지합니다.
벤더 K3D 바이너리는 회사 표준 `C:\K3DAsyncEngine` 설치 위치에서 관리하고, 자산은 승인된 배포 위치에서 관리합니다. 앱 저장소에는 COM 중립 인터페이스와 안전한 설정 예시만 유지합니다.