防止程序在控制台窗口关闭时关闭

5

所发生的情况是我的控制台程序打开,然后从文本文件运行外部C#程序/代码。这一切都很顺利,但当我关闭原始窗体时,执行的程序/代码也关闭了。有没有办法防止我正在运行的文本文件代码关闭?

这是它调用运行程序的代码:

static void MemExe(byte[] buffer)
        {
            Assembly asm = Assembly.Load(buffer);

            if (asm.EntryPoint == null)
                throw new ApplicationException("No entry point found!");

            MethodInfo ePoint = asm.EntryPoint;
            ePoint.Invoke(null, null);
        }

缓冲区是以字节为单位的代码/程序

这是我的主要程序

static void Main(string[] args)
        {
            var data = File.ReadAllText(@"program.txt");
            MemExe(data);
        }

为什么不直接创建一个新进程?https://msdn.microsoft.com/zh-cn/library/system.diagnostics.process.start(v=vs.110).aspx - Benj
当代码托管在执行进程中时,一旦进程终止,就没有框架来托管/运行代码...为什么需要终止主机进程? - Benj
如果您希望进程不可见,则只需创建一个Windows.Forms应用程序,并在非后台线程中运行ePoint.Invoke(null, null)。如果您关闭主窗口,您的进程仍应该在运行(不可见)。 - Benj
真的,但是如果您在项目的应用程序属性中切换到"Windows应用程序",就不会遇到这个问题。 - Benj
@Benj 你是对的,将其设置为“Windows应用程序”是一个不错的解决方法。如果你想将其设置为解决方案,我可以接受。 - jLynx
显示剩余5条评论
2个回答

3
我不会深入讨论你是否应该做你想做的事情。起初,这似乎是一个不好的做法。但是考虑到你有理由这样做...
当进程关闭时,它正在执行的任何操作都会自动停止。为了防止这种行为,你有两个选择:
选项1 - 运行第二个进程
不要创建一个C#项目,而是创建两个。主要的一个使用Process.Start来激活第二个。如果主进程关闭,第二个进程将继续执行直到完成。
选项2 - 禁用关闭按钮
如果你不介意与本机Windows代码交互,从CMD手动禁用关闭按钮可以防止你的代码在其他环境中执行,现在官方已经支持VS 2015。
        [DllImport("user32.dll")]
        static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
        [DllImport("user32.dll")]
        static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
        internal const UInt32 SC_CLOSE = 0xF060;
        internal const UInt32 MF_ENABLED = 0x00000000;
        internal const UInt32 MF_GRAYED = 0x00000001;
        internal const UInt32 MF_DISABLED = 0x00000002;
        internal const uint MF_BYCOMMAND = 0x00000000;


        static void Main(string[] args)
        {
            EnableCloseButton(this, false);
        }

        public static void EnableCloseButton(IWin32Window window, bool bEnabled)
        {
            IntPtr hSystemMenu = GetSystemMenu(window.Handle, false);
            EnableMenuItem(hSystemMenu, SC_CLOSE, (uint)(MF_ENABLED | (bEnabled ? MF_ENABLED : MF_GRAYED)));
        }

参考资料:https://social.msdn.microsoft.com/forums/vstudio/en-us/545f1768-8038-4f7a-9177-060913d6872f/disable-close-button-in-console-application-in-c

该链接提供了如何在C#控制台应用程序中禁用关闭按钮的解决方案。

我想在程序运行后关闭控制台窗口。但由于我运行的程序不是exe文件,所以我不能使用Process.Start,因为没有东西可以启动。这就是为什么我正在使用我的MemExe函数并将代码字节传递给它。至于您提到的选项2,我希望能够关闭该控制台窗口,因此那也不一定可行。 - jLynx
1
@DarkN3ss 我认为他是说要有一个主控制台应用程序,然后有一个独立的服务应用程序。从您的控制台应用程序中使用Process.Start启动服务应用程序,然后在服务应用程序启动时运行代码文件。因此,代码文件不会在您的控制台应用程序结束时结束,因为它的框架是服务应用程序。 - Mark Balhoff
@MarkBalhoff,您能否提供一个例子?我很难理解您的意思。 - jLynx
@DarkN3ss 你运行控制台应用程序 MainApp.exeMainApp.exe 在服务应用程序 WorkerApp.exe 上调用 Process.Start();MainApp.exe 现在可以关闭了。WorkerApp.exe 继续以对用户不可见的方式运行文本文件代码。因此,将您的代码从主应用程序移到工作应用程序中。主应用程序只调用工作应用程序。或者,如果主应用程序只是这样做,请将整个内容编写为服务应用程序。 - Mark Balhoff

1
一个解决方法是在“项目属性-应用程序-输出类型”中更改项目的输出类型,将其从控制台应用程序更改为Windows应用程序(参见截图)。

How to Change from Console Application to Windows Application

这样就不会创建控制台窗口,因此该进程既不会显示为两个正在运行的进程,也无法通过关闭控制台窗口终止它。
这是我会采取的方法。你的方法在非后台线程中执行,这样一来当主线程终止时就无法终止进程。但是,你仍然无法关闭控制台窗口。因此,我建议切换到“Windows 应用程序”。
using System;
using System.IO;
using System.Reflection;
using System.Threading;

namespace StackOverflow
{
    public static class Program
    {
        static void Main(string[] args)
        {
            RunExternalFunctionThread t = new RunExternalFunctionThread(File.ReadAllBytes(@"program.txt"));
            t.Run();
        }

        private class RunExternalFunctionThread
        {
            private Byte[] code;

            public RunExternalFunctionThread(Byte[] code)
            {
                this.code = code;
            }

            public void Run()
            {
                Thread t = new Thread(new ThreadStart(this.RunImpl));

                t.IsBackground = false;
                t.Priority = ThreadPriority.Normal;
                t.SetApartmentState(ApartmentState.STA);

                t.Start();
            }

            private void RunImpl()
            {
                Assembly asm = Assembly.Load(this.code);

                if (asm.EntryPoint == null) throw new ApplicationException("No entry point found!");

                MethodInfo ePoint = asm.EntryPoint;
                ePoint.Invoke(null, null);
            }
        }
    }
}

我认为这个答案的想法是正确的,但是当我关闭控制台窗口时,从txt文件执行的程序也会关闭。 - jLynx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接