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?
You can also create application domains from which you execute code. You create a new application domain using one of the overloaded CreateDomain methods in the System. AppDomain class. You can give the application domain a name and reference it by that name.
Application Domain is an ASP.NET concept which provides isolation for each ASP.NET application. Application Pool is an IIS concept which also provides isolation but at the process level. Application Domain is available only for ASP.NET applications.
The CurrentDomain property is used to obtain an AppDomain object that represents the current application domain. The FriendlyName property provides the name of the current application domain, which is then displayed at the command line.
A process is an executing application (waaaay oversimplified). A thread is an execution context. The operating system executes code within a thread. The operating system switches between threads, allowing each to execute in turn, thus giving the impression that multiple applications are running at the same time.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With