@@ -27,9 +27,12 @@ public void TearDown()
2727
2828 /// <summary>生成一个程序集并落盘:含一个公共类 Marker(可被实例化验证)。</summary>
2929 private string WriteAssembly ( string fileName , string typeName = "Marker" )
30+ => WriteAssemblyTo ( _tempDir , fileName , typeName ) ;
31+
32+ private static string WriteAssemblyTo ( string dir , string fileName , string typeName = "Marker" )
3033 {
31- Directory . CreateDirectory ( _tempDir ) ;
32- var path = Path . Combine ( _tempDir , fileName ) ;
34+ Directory . CreateDirectory ( dir ) ;
35+ var path = Path . Combine ( dir , fileName ) ;
3336
3437 var ab = new PersistedAssemblyBuilder (
3538 new AssemblyName ( Path . GetFileNameWithoutExtension ( fileName ) ) , typeof ( object ) . Assembly ) ;
@@ -159,8 +162,49 @@ public void ResolvesDependencyFromSameDirectory()
159162 finally { alc . Unload ( ) ; }
160163 }
161164
162- // ── 文件锁行为(路径加载的已知权衡) ──
165+ /// <summary>
166+ /// 管理器目录里也存在宿主程序集的副本时,依赖解析必须复用宿主已加载的实例。
167+ /// 回归用例:SMM 自身就在 manager/ 目录里,若 mod ALC 从该目录再加载一份,
168+ /// mod 看到的 IModPlugin 是另一个 Type,插件识别全失败(表现为“发现 0 个 Mod”)。
169+ /// </summary>
170+ [ Test ]
171+ public void HostLoadedAssembly_WinsOverManagerDirCopy ( )
172+ {
173+ var sharedName = "HostShared_" + Guid . NewGuid ( ) . ToString ( "N" ) ;
174+ var hostDir = Path . Combine ( _tempDir , "host" ) ;
175+ var managerDir = Path . Combine ( _tempDir , "manager" ) ;
176+ var modDir = Path . Combine ( _tempDir , "mod" ) ;
177+
178+ // 两份同名程序集:一份已在宿主上下文,一份躺在管理器目录
179+ var hostPath = WriteAssemblyTo ( hostDir , sharedName + ".dll" , "SharedMarker" ) ;
180+ WriteAssemblyTo ( managerDir , sharedName + ".dll" , "SharedMarker" ) ;
181+
182+ var hostAsm = AssemblyLoadContext . Default . LoadFromAssemblyPath ( hostPath ) ;
183+ var hostMarker = hostAsm . GetType ( "SharedMarker" ) ! ;
184+
185+ // mod 的依赖 dll 引用 SharedMarker → 触发 mod ALC 的依赖解析
186+ var ab = new PersistedAssemblyBuilder ( new AssemblyName ( "DepOfShared" ) , typeof ( object ) . Assembly ) ;
187+ var mb = ab . DefineDynamicModule ( "MainModule" ) ;
188+ var tb = mb . DefineType ( "DepMarker" , TypeAttributes . Public | TypeAttributes . Sealed | TypeAttributes . Class ) ;
189+ tb . DefineField ( "Ref" , hostMarker , FieldAttributes . Public | FieldAttributes . Static ) ;
190+ tb . CreateTypeInfo ( ) ;
191+ var depPath = Path . Combine ( modDir , "DepOfShared.dll" ) ;
192+ Directory . CreateDirectory ( modDir ) ;
193+ ab . Save ( depPath ) ;
163194
195+ var alc = new ModLoadContext ( "T:HostFirst" , modDir , managerDir ) ;
196+ try
197+ {
198+ var resolved = alc . LoadFromFilePath ( depPath )
199+ . GetType ( "DepMarker" ) ! . GetField ( "Ref" ) ! . FieldType ;
200+
201+ Assert . That ( resolved , Is . SameAs ( hostMarker ) ,
202+ "宿主已加载的程序集必须复用同一实例,不能从管理器目录加载副本" ) ;
203+ }
204+ finally { alc . Unload ( ) ; }
205+ }
206+
207+ // ── 文件锁行为(路径加载的已知权衡) ──
164208 [ Test ]
165209 public void PathLoad_HoldsFileUntilUnloadCollected ( )
166210 {
0 commit comments