Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 388877
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 12, 20262026-05-12T15:48:44+00:00 2026-05-12T15:48:44+00:00

Background I have a Windows service that uses various third-party DLLs to perform work

  • 0

Background

I have a Windows service that uses various third-party DLLs to perform work on PDF files. These operations can use quite a bit of system resources, and occasionally seem to suffer from memory leaks when errors occur. The DLLs are managed wrappers around other unmanaged DLLs.

Current Solution

I’m already mitigating this issue in one case by wrapping a call to one of the DLLs in a dedicated console app and calling that app via Process.Start(). If the operation fails and there are memory leaks or unreleased file handles, it doesn’t really matter. The process will end and the OS will recover the handles.

I’d like to apply this same logic to the other places in my app that use these DLLs. However, I’m not terribly excited about adding more console projects to my solution, and writing even more boiler-plate code that calls Process.Start() and parses the output of the console apps.

New Solution

An elegant alternative to dedicated console apps and Process.Start() seems to be the use of AppDomains, like this: http://blogs.geekdojo.net/richard/archive/2003/12/10/428.aspx

I’ve implemented similar code in my application, but the unit tests have not been promising. I create a FileStream to a test file in a separate AppDomain, but don’t dispose it. I then attempt to create another FileStream in the main domain, and it fails due to the unreleased file lock.

Interestingly, adding an empty DomainUnload event to the worker domain makes the unit test pass. Regardless, I’m concerned that maybe creating "worker" AppDomains won’t solve my problem.

Thoughts?

The Code

/// <summary>
/// Executes a method in a separate AppDomain.  This should serve as a simple replacement
/// of running code in a separate process via a console app.
/// </summary>
public T RunInAppDomain<T>( Func<T> func )
{
    AppDomain domain = AppDomain.CreateDomain ( "Delegate Executor " + func.GetHashCode (), null,
        new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory } );
        
    domain.DomainUnload += ( sender, e ) =>
    {
        // this empty event handler fixes the unit test, but I don't know why
    };

    try
    {
        domain.DoCallBack ( new AppDomainDelegateWrapper ( domain, func ).Invoke );

        return (T)domain.GetData ( "result" );
    }
    finally
    {
        AppDomain.Unload ( domain );
    }
}

public void RunInAppDomain( Action func )
{
    RunInAppDomain ( () => { func (); return 0; } );
}

/// <summary>
/// Provides a serializable wrapper around a delegate.
/// </summary>
[Serializable]
private class AppDomainDelegateWrapper : MarshalByRefObject
{
    private readonly AppDomain _domain;
    private readonly Delegate _delegate;

    public AppDomainDelegateWrapper( AppDomain domain, Delegate func )
    {
        _domain = domain;
        _delegate = func;
    }

    public void Invoke()
    {
        _domain.SetData ( "result", _delegate.DynamicInvoke () );
    }
}

The unit test

[Test]
public void RunInAppDomainCleanupCheck()
{
    const string path = @"../../Output/appdomain-hanging-file.txt";

    using( var file = File.CreateText ( path ) )
    {
        file.WriteLine( "test" );
    }

    // verify that file handles that aren't closed in an AppDomain-wrapped call are cleaned up after the call returns
    Portal.ProcessService.RunInAppDomain ( () =>
    {
        // open a test file, but don't release it.  The handle should be released when the AppDomain is unloaded
        new FileStream ( path, FileMode.Open, FileAccess.ReadWrite, FileShare.None );
    } );

    // sleeping for a while doesn't make a difference
    //Thread.Sleep ( 10000 );

    // creating a new FileStream will fail if the DomainUnload event is not bound
    using( var file = new FileStream ( path, FileMode.Open, FileAccess.ReadWrite, FileShare.None ) )
    {
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-12T15:48:44+00:00Added an answer on May 12, 2026 at 3:48 pm

    Application domains and cross-domain interaction is a very thin matter, so one should make sure he really understands how thing work before doing anything… Mmm… Let’s say, “non-standard” 🙂

    First of all, your stream-creating method actually executes on your “default” domain (surprise-surprise!). Why? Simple: the method that you pass into AppDomain.DoCallBack is defined on an AppDomainDelegateWrapper object, and that object exists on your default domain, so that is where its method gets executed. MSDN doesn’t say about this little “feature”, but it’s easy enough to check: just set a breakpoint in AppDomainDelegateWrapper.Invoke.

    So, basically, you have to make do without a “wrapper” object. Use static method for DoCallBack’s argument.

    But how do you pass your “func” argument into the other domain so that your static method can pick it up and execute?

    The most evident way is to use AppDomain.SetData, or you can roll your own, but regardless of how exactly you do it, there is another problem: if “func” is a non-static method, then the object that it’s defined on must be somehow passed into the other appdomain. It may be passed either by value (whereas it gets copied, field by field) or by reference (creating a cross-domain object reference with all the beauty of Remoting). To do former, the class has to be marked with a [Serializable] attribute. To do latter, it has to inherit from MarshalByRefObject. If the class is neither, an exception will be thrown upon attempt to pass the object to the other domain. Keep in mind, though, that passing by reference pretty much kills the whole idea, because your method will still be called on the same domain that the object exists on – that is, the default one.

    Concluding the above paragraph, you are left with two options: either pass a method defined on a class marked with a [Serializable] attribute (and keep in mind that the object will be copied), or pass a static method. I suspect that, for your purposes, you will need the former.

    And just in case it has escaped your attention, I would like to point out that your second overload of RunInAppDomain (the one that takes Action) passes a method defined on a class that isn’t marked [Serializable]. Don’t see any class there? You don’t have to: with anonymous delegates containing bound variables, the compiler will create one for you. And it just so happens that the compiler doesn’t bother to mark that autogenerated class [Serializable]. Unfortunate, but this is life 🙂

    Having said all that (a lot of words, isn’t it? :-), and assuming your vow not to pass any non-static and non-[Serializable] methods, here are your new RunInAppDomain methods:

        /// <summary>
        /// Executes a method in a separate AppDomain.  This should serve as a simple replacement
        /// of running code in a separate process via a console app.
        /// </summary>
        public static T RunInAppDomain<T>(Func<T> func)
        {
            AppDomain domain = AppDomain.CreateDomain("Delegate Executor " + func.GetHashCode(), null,
                new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory });
    
            try
            {
                domain.SetData("toInvoke", func);
                domain.DoCallBack(() => 
                { 
                    var f = AppDomain.CurrentDomain.GetData("toInvoke") as Func<T>;
                    AppDomain.CurrentDomain.SetData("result", f());
                });
    
                return (T)domain.GetData("result");
            }
            finally
            {
                AppDomain.Unload(domain);
            }
        }
    
        [Serializable]
        private class ActionDelegateWrapper
        {
            public Action Func;
            public int Invoke()
            {
                Func();
                return 0;
            }
        }
    
        public static void RunInAppDomain(Action func)
        {
            RunInAppDomain<int>( new ActionDelegateWrapper { Func = func }.Invoke );
        }
    

    If you’re still with me, I appreciate 🙂

    Now, after spending so much time on fixing that mechanism, I am going to tell you that is was purposeless anyway.

    The thing is, AppDomains won’t help you for your purposes. They only take care of managed objects, while unmanaged code can leak and crash all it wants. Unmanaged code doesn’t even know there are such things as appdomains. It only knows about processes.

    So, in the end, your best option remains your current solution: just spawn another process and be happy about it. And, I would agree with the previous answers, you don’t have to write another console app for each case. Just pass a fully qualified name of a static method, and have the console app load your assembly, load your type, and invoke the method. You can actually package it pretty neatly in a very much the same way as you tried with AppDomains. You can create a method called something like “RunInAnotherProcess”, which will examine the argument, get the full type name and method name out of it (while making sure the method is static) and spawn the console app, which will do the rest.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Ask A Question

Stats

  • Questions 237k
  • Answers 237k
  • Best Answers 0
  • User 1
  • Popular
  • Answers
  • Editorial Team

    How to approach applying for a job at a company ...

    • 7 Answers
  • Editorial Team

    What is a programmer’s life like?

    • 5 Answers
  • Editorial Team

    How to handle personal stress caused by utterly incompetent and ...

    • 5 Answers
  • Editorial Team
    Editorial Team added an answer You possibly have to call setLayout() on the widget you… May 13, 2026 at 6:36 am
  • Editorial Team
    Editorial Team added an answer Just before you do that segmentation-violation-causing instruction, insert: printf( "%p\n",… May 13, 2026 at 6:36 am
  • Editorial Team
    Editorial Team added an answer I'd start with here on how to programmatically hit Word's… May 13, 2026 at 6:36 am

Related Questions

Background I have a BLL DLL that uses NHibernate. I share the same BLL
I have created a .Net application to run on an App Server that gets
Background Currently I have a C# Silverlight business application which uses RIA Services. The
I have a medium-sized process viewer which uses around ~40MB of private memory on

Trending Tags

analytics british company computer developers django employee employer english facebook french google interview javascript language life php programmer programs salary

Top Members

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.