简体   繁体   中英

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. 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.

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?

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.

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. 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. Both lookups below throw

System.NotSupportedException: The invoked member is not supported in a dynamic assembly.

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. 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. 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'

We're dealing with an internal type, clearly we're not ment to get our hands dirty. From 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. Today, this can no longer happen because the Assembly returned via AssemblyGetAssemblies() will be an 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.

To conclude, the way it's currently implemented you'll be swimming against the stream trying to expose fsi generated types to other domains.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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