[我已经创建了一个程序,该程序也可以作为服务运行,它也将允许我使用Program.cs
启动文件中的以下内容对其进行调试。
using System;
using System.Linq;
using System.Windows.Forms;
using System.ServiceProcess;
using System.Reflection;
using System.Threading;
using crs.Includes;
using crs.Service;
using System.IO;
namespace crs
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//Convert all arguments to lower
args = Array.ConvertAll(args, e => e.ToLower());
//Create the container object for the settings to be stored
Settings.Bag = new SettingsBag();
//Check if we want to run this as a service
bool runAsService = args.Contains("-service");
//Check if debugging
bool debug = Environment.UserInteractive;
//Catch all unhandled exceptions as well
if (!debug || debug)
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
if (runAsService)
{
//Create service array
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new CRSService()
};
//Run services in interactive mode if needed
if (debug)
RunInteractive(ServicesToRun);
else
ServiceBase.Run(ServicesToRun);
}
else
{
//Start the main gui
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainGUI());
}
}
#region Functions
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
string stackTrace = ex.Message + "/n";
while (ex.InnerException != null)
{
ex = ex.InnerException;
stackTrace += ex.Message + "/n";
}
stackTrace = stackTrace.Substring(0, stackTrace.Length - 2);
string msg = "UNHANDLED EXCEPTION!/n/n" + stackTrace;
//Write all log messages to a debug log
try
{
string currentDate = DateTime.Now.ToString("yyyy-MM-dd");
string debugFilePath = AppDomain.CurrentDomain.BaseDirectory + @"debugLogs\";
string debugFilename = Application.ProductName + "-debug-" + currentDate + ".log";
if (!Directory.Exists(debugFilePath))
{
//Create the debug log files directory
Directory.CreateDirectory(debugFilePath);
}
if (!File.Exists(debugFilePath + debugFilename))
{
//Create the new file
using (StreamWriter w = File.CreateText(debugFilePath + debugFilename))
{
w.WriteLine("Debug log file for " + Application.ProductName + ".");
w.WriteLine("Created on " + currentDate + ".");
w.WriteLine("");
}
}
//Write the log message to the file
using (StreamWriter w = File.AppendText(debugFilePath + debugFilename))
{
w.WriteLine(DateTime.Now.ToString() + " :: " + msg);
}
}
catch
{ }
}
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.ReadKey();
Console.WriteLine();
MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (ServiceBase service in servicesToRun)
{
Console.Write("Stopping {0}...", service.ServiceName);
onStopMethod.Invoke(service, null);
Console.WriteLine("Stopped");
}
//Keep the console alive for a second to allow the user to see the message.
Console.WriteLine("All services stopped.");
Thread.Sleep(1000);
}
#endregion
}
}
除了Console.ReadKey();
方法下的RunInteractive()
行之外,所有工作均按预期进行。如果要尝试在控制台窗口中手动运行该服务,那么我将没有任何问题,它运行良好,并等待我按下Enter键以启动服务停止过程。但是,在IDE中运行它时,会将所有内容分散到DEBUG窗口中,没有任何东西可以抓住ReadKey。
在IDE中进行调试时,如何解决这个问题?在IDE中进行调试时是否可能以某种方式强制其在命令窗口中运行?
代替console.write,您可以创建一个日志文件,您可以在其中记录程序的状态。我建议使用log4net nuget软件包。
经过一番挖掘之后,我想出了一个新班来满足我的需要。感谢post by Pavlo,我确实能够获得文本,供我在不存在时需要创建的新控制台窗口读取和写入。
RunInteractive
功能:private static void RunInteractive(ServiceBase[] servicesToRun)
{
//Account for running this application without a console window (debugging in IDE)
if (!ConsoleWindow.Exists() && !ConsoleWindow.Create())
return;
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.ReadKey();
Console.WriteLine();
MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (ServiceBase service in servicesToRun)
{
Console.Write("Stopping {0}...", service.ServiceName);
onStopMethod.Invoke(service, null);
Console.WriteLine("Stopped");
}
//Keep the console alive for a second to allow the user to see the message.
Console.WriteLine("All services stopped.");
Thread.Sleep(1000);
}
注意:这里唯一添加的是函数顶部的这一点。
//Account for running this application without a console window (debugging in IDE)
if (!ConsoleWindow.Exists() && !ConsoleWindow.Create())
return;
ConsoleWindow
类:using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace crs.Includes
{
public class ConsoleWindow
{
#region Constants
private const UInt32 GENERIC_WRITE = 0x40000000;
private const UInt32 GENERIC_READ = 0x80000000;
private const UInt32 FILE_SHARE_READ = 0x00000001;
private const UInt32 FILE_SHARE_WRITE = 0x00000002;
private const UInt32 OPEN_EXISTING = 0x00000003;
private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
#endregion
#region WinAPI external functions
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport(
"kernel32.dll",
SetLastError = true
)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeConsole();
[DllImport(
"kernel32.dll",
SetLastError = true
)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AllocConsole();
[DllImport(
"kernel32.dll",
EntryPoint = "CreateFileW",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall
)]
private static extern IntPtr CreateFileW(
string lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFil
);
#endregion
#region Public class methods
public static bool Exists()
{
if (GetConsoleWindow() == IntPtr.Zero)
return false;
else
return true;
}
public static bool Create()
{
try
{
if (!AllocConsole())
throw new Exception("Error! Could not get a lock on a console window and could not create one.");
InitializeOutStream();
InitializeInStream();
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
#endregion
#region Functions
private static void InitializeOutStream()
{
FileStream fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
if (fs != null)
{
StreamWriter writer = new StreamWriter(fs) { AutoFlush = true };
Console.SetOut(writer);
Console.SetError(writer);
}
}
private static void InitializeInStream()
{
FileStream fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
if (fs != null)
Console.SetIn(new StreamReader(fs));
}
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode, FileAccess dotNetFileAccess)
{
SafeFileHandle file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
if (!file.IsInvalid)
{
FileStream fs = new FileStream(file, dotNetFileAccess);
return fs;
}
return null;
}
#endregion
}
}