简体   繁体   English

调用AttachConsole(-1)后使用Console.ReadKey()将应用程序挂在ReadKey()上

[英]Using Console.ReadKey() after calling AttachConsole(-1) hangs application on ReadKey()

Currently I'm trying my best to have a single application that can be used via command prompt or via windows form application. 目前,我正在尽力提供一个可通过命令提示符或Windows窗体应用程序使用的应用程序。 This application will also be used as a service as well. 该应用程序也将用作服务。 Because of obvious reasons (like rewriting code and keeping up with changes to two applications) I only want to create one application. 由于明显的原因(例如重写代码和跟上两个应用程序的更改),我只想创建一个应用程序。

Currently I'm using the following method to make this happen (I have included all my debugging code to in case anyone had any specific suggestions)... 当前,我正在使用以下方法来实现此目的(我已包含所有调试代码,以防有人提出任何具体建议)...

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MyApplicationSpace
{
    static class Program
    {
        #region Private class APIs
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool AttachConsole(int pid);

        [DllImport("kernel32")]
        private static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FreeConsole();
        #endregion

        #region Constructor
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static void Main(string[] args)
        {
            if (args.Contains("-service"))
            {
                ServiceBase[] servicesToRun;
                servicesToRun = new ServiceBase[]
                {
                new MyServiceClass()
                };

                if (Environment.UserInteractive)
                {
                    if (!AttachConsole(-1)) AllocConsole();

                    RunInteractive(servicesToRun);

                    FreeConsole();
                    SendKeys.SendWait("{ENTER}");
                }
                else
                    ServiceBase.Run(servicesToRun);
            }
            else
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
        }
        #endregion

        #region Private class functions
        private static void RunInteractive(ServiceBase[] servicesToRun)
        {
            Console.WriteLine("Services running in interactive mode.");
            Console.WriteLine();

            MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);
            foreach (ServiceBase service in servicesToRun)
            {
                Console.Write("Starting {0}...", service.ServiceName);
                onStartMethod.Invoke(service, new object[] { new string[] { } });
                Console.Write("Started");
            }

            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press any key to stop the services and end the process...");
            Console.WriteLine("BEFORE ReadKey");

            File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "error.log", "BEFORE ReadKey");
            try
            {
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "error.log", ex.Message);
            }
            File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "error.log", "AFTER ReadKey");

            Console.WriteLine("AFTER ReadKey");
            Console.WriteLine();

            Console.WriteLine("Continuing...");

            MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
            Console.WriteLine("onStopMethod is null = " + (onStopMethod == null));

            Console.WriteLine("Foreach service in service to run count:" + servicesToRun.Count());
            foreach (ServiceBase service in servicesToRun)
            {
                Console.Write("Stopping {0}...", service.ServiceName);
                onStopMethod.Invoke(service, null);
                Console.WriteLine("Stopped");
            }

            Console.WriteLine("All services stopped.");
            // Keep the console alive for a second to allow the user to see the message.
            Thread.Sleep(1000);
        }
        #endregion
    }
}

The issue happens when I try to call Console.ReadKey() . 当我尝试调用Console.ReadKey()时,就会发生此问题。 The program seems to hang or just exit to some extent. 该程序似乎挂起或仅在某种程度上退出。 For example, when I run it from the command prompt it runs as expected, then shows "Press any key to stop the services and end the process..." as well as "BEFORE ReadKey". 例如,当我在命令提示符下运行它时,它按预期运行,然后显示“按任意键停止服务并结束该过程...”以及“ BEFORE ReadKey”。 Then, once I hit [ENTER] The console just goes back to the default command prompt. 然后,当我按[ENTER] ,控制台将返回到默认命令提示符。 Yet for some reason the application is still showing as running in task manager. 但是由于某种原因,该应用程序仍显示为在任务管理器中运行。 It seems as if it's getting hung up. 好像挂了。

If I change the application type to console application everything works as expected and it looks great. 如果我将应用程序类型更改为console application一切都会按预期运行,并且看起来很棒。 But I need to build it as a windows application . 但是我需要将其构建为windows application I think the issue is using AttachConsole(-1) is not the same as building it as a console application. 我认为问题是使用AttachConsole(-1)与将其构建为控制台应用程序不同。

The credit on this goes to Hans. 这要归功于汉斯。 I'm not sure why he deleted his answer but I wish he would not have. 我不确定他为什么删除他的答案,但我希望他不会。

Basically the command process and my application is fighting over the command window (He said it so much more elegantly then I). 基本上,命令过程和我的应用程序都在命令窗口上奋战(他说得比我优雅得多)。

He suggested that I could do one of two things. 他建议我可以做两件事之一。

  1. Create a small app "my app name.com" separate from my app "my app name.exe" and let it create a new command window itself for starting the service. 创建一个与"my app name.com"程序"my app name.com"分开的小应用程序"my app name.exe"并让其自己创建一个新的命令窗口以启动该服务。

  2. Use the Windows start command to run the application instead of trying to run it normally. 使用Windows start命令运行该应用程序,而不是尝试正常运行它。

I choose to use the ladder method. 我选择使用梯形图方法。 Using the wait switch it forces the command processor to wait until the application has finished until it tries to grab key presses again. 使用等待开关,它将强制命令处理器等待应用程序完成,直到尝试再次抓住按键。 Take note of the THIRD param when running start . 运行start时,请注意THIRD参数。 It is "" which is two double quotes to pass a blank param after the /wait param. 它是"" ,是两个双引号,用于在/wait参数之后传递一个空白参数。 This is necessary in order to pass your params after the application you are running. 为了在运行应用程序之后传递参数,这是必需的。

Before 之前

C:\\"my app name.exe" -service C:\\“我的应用程序name.exe”-服务

After

C:\\start /wait "" "my app name.exe" -service C:\\ start / wait“”“我的应用程序name.exe”-服务

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM