简体   繁体   English

在F#Interactive中创建新的AppDomain

[英]Creating new AppDomain in F# Interactive

I need the ability to create a new AppDomain in F# interactive in order to host multiple WPF applications. 我需要能够在F#interactive中创建一个新的AppDomain,以便托管多个WPF应用程序。 I don't have any problem getting the necessary functionality to work in a compiled F# application, but for some reason getting it to work in F# interactive doesn't seem to be possible. 我在编译的F#应用程序中获得必要的功能没有任何问题,但由于某种原因使其在F#interactive中工作似乎不可能。

Here is the simplest possible case:- 这是最简单的案例: -

#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();;

I always get back something along the lines of 我总是回过头来做些什么

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

What do I need to do to get this to work in F# interactive? 我需要做些什么才能在F#interactive中使用它?

Intro from the docs : 来自文档的介绍:

F# Interactive attempts to compile the code and, if successful, it executes the code and prints the signature of the types and values that it compiled. F#Interactive尝试编译代码,如果成功,它将执行代码并打印其编译的类型和值的签名。

The main gotcha lies in the compilation step 主要问题在于编译步骤

typeof<myClass>.Assembly.FullName

Output: 输出:

 val it : string = "FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" 

In order to compile the code fsi uses a dynamic assembly to host all types created during a session. 为了编译代码, fsi使用动态程序集来托管会话期间创建的所有类型。 The key element here is that other domains won't be able to resolve these types without a reference to that containing assembly. 这里的关键元素是,如果没有对包含程序集的引用,其他域将无法解析这些类型。 However, getting a hold of the assembly from other app domains proves to be non-trivial. 但是,从其他应用程序域中获取程序集证明是非平凡的。 Mainly because we're dealing with a dynamic assembly. 主要是因为我们正在处理动态装配。

let asm = typeof<myClass>.Assembly 
asm.IsDynamic // val it : bool = true

Meaning, it only exists in memory of fsi 's default appdomain. 这意味着,它只存在于FSI的默认应用程序域的内存。 Both lookups below throw 下面的两个查找都抛出

System.NotSupportedException: The invoked member is not supported in a dynamic assembly. System.NotSupportedException:动态程序集中不支持调用的成员。

asm.Location
asm.CodeBase

Typically, you'd want to persist to disk first, ref remarks - Restrictions on emitting to remote application domains : 通常,您希望首先保留到磁盘,引用注释 - 发送到远程应用程序域的限制

Some scenarios require a dynamic assembly to be created and executed in a remote application domain. 某些方案需要在远程应用程序域中创建和执行动态程序集。 Reflection emit does not allow a dynamic assembly to be emitted directly to a remote application domain. Reflection emit不允许将动态程序集直接发送到远程应用程序域。 The solution is to emit the dynamic assembly in the current application domain, save the emitted dynamic assembly to disk, and then load the dynamic assembly into the remote application domain. 解决方案是在当前应用程序域中发出动态程序集,将发出的动态程序集保存到磁盘,然后将动态程序集加载到远程应用程序域中。

Successfully casting the dynamic assembly to AssemblyBuilder would expose a Save method. 成功将动态程序集强制转换为AssemblyBuilder将公开Save方法。 Unfortunately, this workflow has been sealed off as well. 不幸的是,这个工作流程也被封锁了。

open System.Reflection.Emit
let builder = asm :?> AssemblyBuilder

Throws 抛出

System.InvalidCastException: Unable to cast object of type 'System.Reflection.Emit.InternalAssemblyBuilder' to type 'System.Reflection.Emit.AssemblyBuilder' System.InvalidCastException:无法将类型为“System.Reflection.Emit.InternalAssemblyBuilder”的对象强制转换为“System.Reflection.Emit.AssemblyBuilder”

We're dealing with an internal type, clearly we're not ment to get our hands dirty. 我们正在处理一种内部类型,显然我们并没有把手弄脏。 From referencesource.microsoft.com : 来自referencesource.microsoft.com

In the past, when InternalAssemblyBuilder was AssemblyBuilder, the untrusted user could down cast the Assembly to an AssemblyBuilder and emit code with the elevated permissions of the trusted code which origionally created the AssemblyBuilder via DefineDynamicAssembly. 在过去,当InternalAssemblyBuilder是AssemblyBuilder时,不受信任的用户可以将Assembly向下转换为AssemblyBuilder,并使用通过DefineDynamicAssembly创建AssemblyBuilder的受信任代码的提升权限发出代码。 Today, this can no longer happen because the Assembly returned via AssemblyGetAssemblies() will be an InternalAssemblyBuilder. 今天,这不再发生,因为通过AssemblyGetAssemblies()返回的程序集将是一个InternalAssemblyBuilder。

Alternatively, you could reflect over the types in the dynamic assembly and reconstruct them using a new AssemblyBuilder and other helpers in the System.Reflection.Emit namespace, but it all seems a tad on the tedious side. 或者,您可以反映动态程序集中的类型,并使用System.Reflection.Emit命名空间中的new AssemblyBuilder和其他帮助程序重新构建它们 ,但这一切似乎都在乏味的一面。

To conclude, the way it's currently implemented you'll be swimming against the stream trying to expose fsi generated types to other domains. 总而言之,它目前实现的方式是你将游戏流试图将fsi生成的类型暴露给其他域。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM