23

I need to create a custom application domain to work around a bug in the .NET runtime's default behavior. None of the sample code I've seen online is helpful since I don't know where to place it, or what it needs to replace within my Main() method.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131

2 Answers2

42

It should probably be noted that creating AppDomains just to get around something that can be fixed with a constant string is probably the wrong way to do it. If you are trying to do the same thing as the link you noted, you could just do this:

var configFile = Assembly.GetExecutingAssembly().Location + ".config";
if (!File.Exists(configFile))
    throw new Exception("do your worst!");

Recursive Entry Point :o)

static void Main(string[] args)
{
    if (AppDomain.CurrentDomain.IsDefaultAppDomain())
    {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);

        var currentAssembly = Assembly.GetExecutingAssembly();
        var otherDomain = AppDomain.CreateDomain("other domain");
        var ret = otherDomain.ExecuteAssemblyByName(currentAssembly.FullName, args);

        Environment.ExitCode = ret;
        return;
    }

    Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
    Console.WriteLine("Hello");
}

Quick sample using a nonstatic secondary entry point and MarshalByRefObject...

class Program
{
    static AppDomain otherDomain;

    static void Main(string[] args)
    {
        otherDomain = AppDomain.CreateDomain("other domain");

        var otherType = typeof(OtherProgram);
        var obj = otherDomain.CreateInstanceAndUnwrap(
                                 otherType.Assembly.FullName,
                                 otherType.FullName) as OtherProgram;

        args = new[] { "hello", "world" };
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
        obj.Main(args);
    }
}

public class OtherProgram : MarshalByRefObject
{
    public void Main(string[] args)
    {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
        foreach (var item in args)
            Console.WriteLine(item);
    }
}
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
  • 1
    Thank you. Is there a reason to prefer one approach to the other? – Dan Is Fiddling By Firelight Apr 15 '10 at 20:18
  • The first would probably be cleaner. It would allow you to use a standard entry point instead of having to create an object that would need to be marshaled across the appdomains. The second approach is more typical. But it is normally used for plug-ins and not the main entry point of the app. – Matthew Whited Apr 15 '10 at 20:22
  • 1
    There is also a `.ExecuteAssembly(...)` method on the `AppDomain` object that you could provide the path to another assembly that contains an entry point. That might allow for a slightly cleaner design, but would require at least two assemblies. – Matthew Whited Apr 15 '10 at 20:25
  • 1
    I knew it had to be possible... the trick was figuring out how to do it without referencing `mscoree.tlb`. – Matthew Whited Apr 16 '10 at 04:15
  • The recursive approach is quite spiffy, but it's a bit problematic with .NET 4 due to obsoletion of the CAS model. `ExecuteAssemblyByName` with the Evidence overload will throw an exception unless `` is in the app.config. What would be the proper way to convert that to "simple sandboxing": `AppDomain.CreateDomain( string friendlyName, Evidence securityInfo, AppDomainSetup info, PermissionSet grantSet, params StrongName[] fullTrustAssemblies)`? – Matt Chambers Aug 03 '17 at 19:37
  • @MattChambers, challenge accepted :) ... apparently you just need to remove the evidence parameter. – Matthew Whited Aug 03 '17 at 20:13
  • Yes, but I need the evidence parameter. :) My use case is overriding the way the default settings provider calculates the path to `user.config` - it uses the application path (which in my case includes the version, so it changes by defintiion). By creating a new AppDomain with a constant `Security.Policy.Url`, I was able to circumvent that. But I don't know the security implications of the `` thingy. Thanks for accepting the necropost-challenge. :) – Matt Chambers Aug 03 '17 at 20:38
  • The evidence parameter isn't supported anymore. you have no option but to use that legacy feature. – Matthew Whited Aug 03 '17 at 20:39
  • The only issue I know about that flag is the fact you are going to still use CAS... which is no longer supported. – Matthew Whited Aug 03 '17 at 20:39
  • Does that mean it'll blow up on Windows 10? I'm testing on Windows 7 and it seems to work (except I need to do a bit more tweaking to make the child domain fully trusted so it can run "unverifiable code" like SQLite! – Matt Chambers Aug 03 '17 at 20:52
  • 1
    I have no idea. – Matthew Whited Aug 03 '17 at 21:13
  • It looks like it's dependant on the version of the runtime and not the OS. – Matthew Whited Aug 03 '17 at 21:14
6

You need to:

1) Create an instance of AppDomainSetup object and populate it with the setup information you want for your domain

2) Create your new domain by using AppDomain.CreateDoman method. The AppDomainSetup instance with configuration parameters is passed to the CreateDomain method.

3) Create an instance of your object in the new domain by using the CreateInstanceAndUnwrap method on the domain object. This method takes typename of the object you want to create and returns a remoting proxy you can use in yuor main domain to communicate with the object created in the new one

Once you are through with these 3 steps you can call methods in the other domain through the proxy. You can also unload the domain after you are done and reload it again.

This topic in MSDN help has pretty detailed example of what you need

mfeingold
  • 7,094
  • 4
  • 37
  • 43
  • 2
    That's more or less what I've seen in the examples I saw elsewhere, but doesn't provide any of the information I'm still lacking. Do I just call Application.Run(new MyForm)? Do I chop out all the existing startup code from my Main method, plunk it in a new method, and call that to start my app? None of the above because I am even more confused than I think I am? – Dan Is Fiddling By Firelight Apr 15 '10 at 20:03
  • 1
    The object you are trying to get the proxy of must be `MarshalByRefObject` or else it will just try to serialize a copy back to the original AppDomain. – Matthew Whited Apr 15 '10 at 20:13
  • 1
    @Dan I never tried to run the UI related stuff in non-primary domain. As far as the UI goes I am afraid you will have some trouble getting it to work, primarily because it is based on the old unamanaged code. If you feel bold, try to push the entire thing to the secondary domain and see how it will work. I mean instantiate the Application class in the secondary domain and invoke the Run method – mfeingold Apr 15 '10 at 20:29
  • 1
    The recursive entry method suggested by Matthew Whited is working without any GUI issues. – Dan Is Fiddling By Firelight Apr 15 '10 at 20:50