简体   繁体   English

通过 IDE 调试服务

[英]Debugging a service via IDE

I have created a program that can also be ran as a service and it will allow me to debug it as well using the following in the Program.cs startup file.我创建了一个也可以作为服务运行的程序,它允许我使用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
    }
}

Everything works as expected except for the line Console.ReadKey();除了行Console.ReadKey();之外,一切都按预期工作。 under the RunInteractive() method.RunInteractive()方法下。 If I was to try and run this service manually in a console window I would have no issues what so ever, it runs great and waits for me to hit enter to start the service stopping process.如果我尝试在控制台窗口中手动运行此服务,我将不会有任何问题,它运行良好并等待我按下回车键以启动服务停止过程。 However, when running it in the IDE it's spitting everything out to the DEBUG window and there is nothing for it to grab a ReadKey on.但是,当在 IDE 中运行它时,它会将所有内容都吐出到 DEBUG 窗口,并且没有任何东西可以获取 ReadKey。

How can I go about getting around this when debugging in the IDE?在 IDE 中调试时如何解决这个问题? Is it possible to somehow force it to run in a command window when debugging in the IDE?在 IDE 中调试时,是否可以以某种方式强制它在命令窗口中运行?

After some digging around I came up with a new class to suit my needs.经过一番挖掘后,我想出了一个新课程来满足我的需要。 Thanks for the post by Pavlo I was able to actually get text to read and write to the new console window I needed to create when one was not present.感谢Pavlo 的帖子,我实际上能够读取文本并将其写入我需要在不存在时创建的新控制台窗口。

My altered RunInteractive function from my original question:我从原来的问题中修改了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);
}

Note: The only thing that was added here was this little bit at the top of the function.注意:这里唯一添加的是函数顶部的这一点。

//Account for running this application without a console window (debugging in IDE)
if (!ConsoleWindow.Exists() && !ConsoleWindow.Create())
  return;

My new ConsoleWindow class:我的新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
    }
}

Instead of console.write, you can create a log file in which you can log the status of the program.您可以创建一个日志文件来代替 console.write,您可以在其中记录程序的状态。 I recommend using the log4net nuget package.我推荐使用 log4net nuget 包。

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

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