Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 999
FunctionallyFirst Avatar asked May 15 '18 12:05

FunctionallyFirst


People also ask

How do I create an app for my domain?

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.

What is AppDomain in IIS?

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.

What is AppDomain and CurrentDomain?

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.

What is the difference between AppDomain assembly process and a thread?

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.


1 Answers

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.

like image 69
Funk Avatar answered Sep 23 '22 01:09

Funk