通过IDE调试服务

问题描述 投票:2回答:2

[我已经创建了一个程序,该程序也可以作为服务运行,它也将允许我使用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中进行调试时是否可能以某种方式强制其在命令窗口中运行?

c# debugging service visual-studio-2017
2个回答
0
投票

代替console.write,您可以创建一个日志文件,您可以在其中记录程序的状态。我建议使用log4net nuget软件包。


0
投票

经过一番挖掘之后,我想出了一个新班来满足我的需要。感谢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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.