在F# Interactive中创建新的AppDomain

7
我需要在F#交互式环境下创建一个新的AppDomain,以便托管多个WPF应用程序。我没有任何问题让编译后的F#应用程序获得必要的功能,但是不知道为什么在F#交互式环境下无法实现此功能。
以下是最简单的情况:
#r "PresentationCore.dll"
#r "PresentationFramework.dll"
#r "System.Xaml.dll"
#r "WindowsBase.dll"

open System    
open System.Threading
open System.Windows

type myClass() = 
    let domain = AppDomain.CreateDomain("another domain")

    //this function starts a WPF app
    let funct() =                
        let WPFStart() =
            let app = Application()
            let win = Window()            
            app.Run(win) |> ignore
        let thread = Thread WPFStart
        thread.IsBackground <- true
        thread.SetApartmentState ApartmentState.STA
        thread.Start()

    do CrossAppDomainDelegate(funct) |> domain.DoCallBack

myClass();;

我总是会收到类似以下内容的回复
System.Runtime.Serialization.SerializationException: Type is not resolved 
for member 'FSI_0002+-ctor@24,FSI-ASSEMBLY, Version=0.0.0.0, 
Culture=neutral, PublicKeyToken=null'.
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at FSI_0002.myClass..ctor()
at <StartupCode$FSI_0005>.$FSI_0005.main@()
Stopped due to error

我需要做什么才能在F#交互式环境中使其运行?

你的意思是在Visual Studio中的F#交互窗口吗? - Ciaran_McCarthy
2
我记得曾经为此而苦恼过 - 我尝试了几个想法,但都没有奏效,所以我认为这可能不容易实现。 - Tomas Petricek
3
要将委托通过 AppDomain 边界传递,需要在一侧将其序列化并在另一侧进行反序列化。MethodInfo 对象被序列化为程序集名称、类型名称、方法名称和签名。由于您的方法实际上是在动态程序集中定义的,而该程序集不存在于磁盘上,因此接收 AppDomain 无法仅通过程序集名称加载它,因此它无法反序列化委托。 - user4003407
1个回答

2
来自文档的简介:
F# Interactive尝试编译代码,如果成功,它将执行代码并打印其编译的类型和值的签名。
主要的难点在于编译步骤。
typeof<myClass>.Assembly.FullName

输出:

val it : string =
  "FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
为了编译代码,fsi使用一个动态程序集来托管会话期间创建的所有类型。关键在于其他域无法解析这些类型,除非有对包含程序集的引用。然而,从其他应用程序域获取该程序集证明是非常困难的,主要是因为我们正在处理一个动态程序集。
let asm = typeof<myClass>.Assembly 
asm.IsDynamic // val it : bool = true

意思是,它仅存在于“fsi”默认应用程序域的内存中。以下两个查找都会抛出“System.NotSupportedException:在动态程序集中不支持所调用的成员”的异常。
asm.Location
asm.CodeBase

通常,您需要先将数据持久化到磁盘上,参见备注 - 有关向远程应用程序域发出信号的限制

某些情况下需要在远程应用程序域中创建和执行动态程序集。反射发射不允许直接向远程应用程序域发出动态程序集。解决方案是在当前应用程序域中发出动态程序集,将发出的动态程序集保存到磁盘上,然后将动态程序集加载到远程应用程序域中。

成功将动态程序集转换为AssemblyBuilder将暴露一个Save方法。不幸的是,这个工作流也被关闭了。
open System.Reflection.Emit
let builder = asm :?> AssemblyBuilder

抛出异常:
System.InvalidCastException:无法将类型为“System.Reflection.Emit.InternalAssemblyBuilder”的对象强制转换为类型“System.Reflection.Emit.AssemblyBuilder”
我们正在处理一个内部类型,显然我们不应该亲自动手。来自referencesource.microsoft.com的信息:
过去,当InternalAssemblyBuilder是AssemblyBuilder时,不受信任的用户可以将程序集向下转换为AssemblyBuilder,并使用最初通过DefineDynamicAssembly创建AssemblyBuilder的可信代码的提升权限发出代码。今天,这种情况不再发生,因为通过AssemblyGetAssemblies()返回的程序集将是InternalAssemblyBuilder。
或者,您可以反射动态程序集中的类型,并使用System.Reflection.Emit命名空间中的new AssemblyBuilder和其他帮助程序重建它们,但这似乎有点繁琐。
总之,目前的实现方式是将fsi生成的类型暴露给其他领域,你会像逆水行舟一样困难。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接