Currently I'm trying my best to have a single application that can be used via command prompt or via windows form application. 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()
. 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". Then, once I hit [ENTER]
The console just goes back to the default command prompt. 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. But I need to build it as a windows application
. I think the issue is using AttachConsole(-1)
is not the same as building it as a console application.