调用 AttachConsole(-1) 后使用 Console.ReadKey() 会挂起 ReadKey() 上的应
本文关键字:ReadKey 挂起 AttachConsole 调用 Console | 更新日期: 2023-09-27 17:56:25
目前,我正在尽力拥有一个可以通过命令提示符或Windows表单应用程序使用的应用程序。 此应用程序也将用作服务。 由于显而易见的原因(例如重写代码和跟上对两个应用程序的更改),我只想创建一个应用程序。
目前,我正在使用以下方法来实现这一目标(我已经包含了我所有的调试代码,以防有人有任何具体建议)......
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
}
}
当我尝试打电话给Console.ReadKey()
时,就会出现问题。 该程序似乎在某种程度上挂起或只是退出。 例如,当我从命令提示符运行它时,它会按预期运行,然后显示"按任意键停止服务并结束进程..."以及"读取密钥之前"。 然后,一旦我点击[ENTER]
控制台就会返回到默认命令提示符。 然而,由于某种原因,应用程序仍然显示为在任务管理器中运行。 好像被挂断了。
如果我将应用程序类型更改为console application
则一切按预期工作,并且看起来很棒。 但是我需要将其构建为windows application
. 我认为问题是使用AttachConsole(-1)
与将其构建为控制台应用程序不同。
这要归功于汉斯。 我不知道他为什么删除了他的答案,但我希望他不会删除。
基本上,命令过程和我的应用程序正在争夺命令窗口(他说得比我优雅得多)。
他建议我可以做两件事之一。
-
创建一个独立于我的应用程序
"my app name.exe"
"my app name.com"
的小应用程序,并让它自己创建一个新的命令窗口来启动服务。 -
使用 Windows
start
命令运行应用程序,而不是尝试正常运行它。
我选择使用梯形图方法。 使用等待开关,它会强制命令处理器等到应用程序完成,直到它尝试再次按键。 运行start
时记下第三个参数。 ""
这是两个双引号,用于在/wait
参数之后传递空白参数。 这是必需的,以便在运行应用程序后传递参数。
以前
C:"我的应用名称.exe" -服务
后
C:''start/wait " "我的应用名称.exe" -service