简体   繁体   中英

Keeping a console application alive in C#

I just created a basic console application that uses my framework to compile C# code from a text file.

The way you would run this is "SecureCodeBridge.exe File.txt", this text file contains code that creates and shows a windows form which works fine.

The problem is that because my application is a console application, it will exit as soon as the method from the text file is invoked (the Form shows for a second and disappears as the console application exits...)

This is the code of my console application:

static void Main(string[] args)
{
    string entString = args[0];

    if(System.IO.File.Exists(entString))
    {
        string entContents = System.IO.File.ReadAllText(entString);
        SecureCode sC = new SecureCode(entContents);
        CompilerResults cR = sC.Compile();

        if(cR.Errors.Count > 0)
        {
            //Abort!
            foreach(CompilerError cE in cR.Errors)
            {
                Console.WriteLine(cE.ErrorText);
            }
        }
        else
        {
            sC.Run(cR);
        }
    }
}

And this is the code my framework uses to compile and run the code:

public class SecureCode
{
    string code;
    public SecureCode(string source)
    {
        code = source;
    }

    public CompilerResults Compile()
    {
        Dictionary<string, string> providerOptions = new Dictionary<string, string>
        {
            {"CompilerVersion", "v3.5"}
        };
        CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);

        CompilerParameters compilerParams = new CompilerParameters
        {
            GenerateInMemory = true,
            GenerateExecutable = false
        };
        compilerParams.ReferencedAssemblies.Add("System.dll");
        compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
        compilerParams.ReferencedAssemblies.Add("System.Drawing.dll");

        CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, code);

        return results;
    }

    public void Run(CompilerResults results)
    {
        object o = results.CompiledAssembly.CreateInstance("Code.Class");
        MethodInfo mi = o.GetType().GetMethod("Main");
        mi.Invoke(o, null);
    }
}

Is there some way to keep the console application alive while the method is being invoked? (For example, a form being shown)

Invoke will wait for the method to complete, BeginInvoke will run it asynchronously, so your method is completing (eg opening the Form) and then the console application is exiting.

If you want to wait for the application/assembly you have spun up to exit, then you could run it as a process (System.Diagnostics), you will need to think how you get your compiled code in to a callable format:

var process = Process.Start(<options>);
process.WaitForExit();

Another option would be to hang on to a weak reference, I have not run the below but it should not be far off. You would loop until the weak reference is cleaned up after the object is no longer deemed relevant by the Garbage Collector:

WeakReference(weakRef = new WeakReference(results.CompiledAssembly.CreateInstance("Code.Class"));
MethodInfo mi = weakRef.Target.GetType().GetMethod("Main");
mi.Invoke(o, null);

// Wait until the application exists AND the Garbage Collector cleans it up
while (weakRef.IsAlive)
{
     Thread.Sleep(100);
}

The last option I can think of would be to compile in an event handler (say OnExit or OnClose()) and then wire this up via reflection with a wait mechanism in the console app to close when it receives the callback. From this post .

// Get an EventInfo representing the Click event, and get the // type of delegate that handles the event. // EventInfo evClick = tExForm.GetEvent("Click"); Type tDelegate = evClick.EventHandlerType;

// If you already have a method with the correct signature,
// you can simply get a MethodInfo for it. 
//
MethodInfo miHandler = 
    typeof(Example).GetMethod("LuckyHandler", 
        BindingFlags.NonPublic | BindingFlags.Instance);

// Create an instance of the delegate. Using the overloads
// of CreateDelegate that take MethodInfo is recommended.
Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);

// Get the "add" accessor of the event and invoke it late-
// bound, passing in the delegate instance. This is equivalent
// to using the += operator in C#, or AddHandler in Visual
// Basic. The instance on which the "add" accessor is invoked
// is the form; the arguments must be passed as an array.
//
MethodInfo addHandler = evClick.GetAddMethod();
Object[] addHandlerArgs = { d };
addHandler.Invoke(exFormAsObj, addHandlerArgs);

Good luck!

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