Winforms Windows服务的运行时调试提示?

Winforms Windows服务的运行时调试提示?,winforms,debugging,windows-services,Winforms,Debugging,Windows Services,我有一个Windows服务,它监视连接到供应商硬件的COM端口。这是一个非常繁忙的硬件,它不断轮询线路上的其他设备(这是一个双绞线RS485“网络”)。我的软件需要在这条线上模拟X个硬件设备,因此我有一个多线程的多层状态机,随时跟踪通信协议的位置 Windows服务(这是我的第一个服务,顺便说一句)的问题是,您需要一些调试来让您知道这些东西是否正常工作。当我第一次开发这个状态机/多线程代码时,我有一个带有RichTextBox的windows窗体,其中显示了行中来回的ASCII字符。似乎我不能真

我有一个Windows服务,它监视连接到供应商硬件的COM端口。这是一个非常繁忙的硬件,它不断轮询线路上的其他设备(这是一个双绞线RS485“网络”)。我的软件需要在这条线上模拟X个硬件设备,因此我有一个多线程的多层状态机,随时跟踪通信协议的位置

Windows服务(这是我的第一个服务,顺便说一句)的问题是,您需要一些调试来让您知道这些东西是否正常工作。当我第一次开发这个状态机/多线程代码时,我有一个带有RichTextBox的windows窗体,其中显示了行中来回的ASCII字符。似乎我不能真正有一个服务的GUI友好。我尝试通过另一个程序在服务中打开一个表单,该程序发送通过OnCustomCommand()处理程序接收的服务消息,但似乎不起作用。我检查了“允许服务与桌面交互”以及所有内容。我正在使用调试窗体的Show()和Hide()方法


我想我不需要看到所有的人物都出现在台词上,但那肯定很好(我想我真的需要看到他们:-))。有人有什么疯狂的想法可以帮我吗?我不想让系统陷入某些IPC的泥潭,这些IPC不适用于肯定会通过的大量数据。不过,这只是非常短期的调试,只是确认程序、RS485到USB加密狗和硬件都正常工作。

您可以将输出写入日志文件,然后使用其他应用程序查看该文件。此问题概述了使用windows查看日志文件的几个选项

我不知道它是否适用于您,但我总是使用一个额外的Main来构建我的服务,将它们构建为控制台应用程序,以获得调试输出

编辑:

例如:

class Worker : ServiceBase
{

#if(RELEASE)
        /// <summary>
        /// The Main Thread where the Service is Run.
        /// </summary>
        static void Main()
        {
            ServiceBase.Run(new Worker());
        }
#endif

#if(DEBUG)
        public static void Main(String[] args)
        {
            Worker worker = new Worker();
            worker.OnStart(null);
            Console.ReadLine();
            worker.OnStop();
        }
#endif

        // Other Service code
}
类工作程序:ServiceBase
{
#如果(发布)
/// 
///运行服务的主线程。
/// 
静态void Main()
{
Run(newworker());
}
#恩迪夫
#如果(调试)
公共静态void Main(字符串[]args)
{
工人=新工人();
worker.OnStart(null);
Console.ReadLine();
worker.OnStop();
}
#恩迪夫
//其他服务代码
}

用于写入调试缓冲区,然后用于监视它。如果您在Windows XP或更早版本上运行,则可以使用查看通过串行端口传输的原始字节。与日志文件相比,它的优点是开销非常小,特别是在您不查看它的情况下。您甚至可以从另一台计算机上运行DebugView并远程监视您的服务。

在使用Windows服务时,我通常会创建它,以便它可以作为服务或普通的旧命令行应用程序运行。通过检查Environment.UserInteractive,可以轻松检查您是否作为服务运行。如果此属性为true,则您正在从命令行运行。如果属性为false,则表示您是作为服务运行的。将此代码添加到Program.cs,并在通常调用ServiceBase.Run(servicesToRun)的地方使用它

///运行提供的服务类。
///要运行的服务类。
///要传递给服务类的命令行参数。
专用静态void运行服务(IEnumerable servicesToRun、IEnumerable args)
{
var serviceBaseType=typeof(ServiceBase);
var onStartMethod=serviceBaseType.GetMethod(“OnStart”,BindingFlags.Instance | BindingFlags.NonPublic);
foreach(servicesToRun中的var服务)
{
Invoke(服务,新对象[]{args});
Console.WriteLine(service.ServiceName+“已启动”);
}
控制台。WriteLine(“按任意键退出”);
Console.ReadKey();
var onStopMethod=serviceBaseType.GetMethod(“OnStop”,BindingFlags.Instance | BindingFlags.NonPublic);
foreach(servicesToRun中的var服务)
{
调用(服务,null);
Console.WriteLine(service.ServiceName+“已停止”);
}
}

现在你可以调试你的服务,设置断点,任何你想要的。当你运行应用程序时,你会看到一个控制台窗口,适合显示控制台消息,它会一直打开直到你按下一个键。

我在这里回答我自己的问题。我在这里尝试了一些建议,但以下是我最终所做的

我用一个按钮和RichTextBox创建了一个Windows窗体应用程序。此应用程序在其端构建了NamedPipeServerStream。该按钮的任务是向Windows服务发送“调试打开”(命令128)或“调试关闭”(命令129)。初始值为“调试关闭”。单击按钮时,将向Windows服务发送128的命令以打开调试。在Windows服务中,这触发了一个内部变量为true,加上它使用NamedPipeClientStream连接到表单应用程序,并开始使用BinaryWriter在COM端口上接收或发送字符。在表单端,创建了一个BackgroundWorker,用于在管道上创建WaitForConnection()。当它获得连接时,使用BinaryReader.ReadString()从管道中读取数据并将其发送到RichTextBox


我快到了。再次单击“调试”按钮时,我正在断开管道,随后的单击无法正确地重做管道。总而言之,我对它很满意。如果有人感兴趣,我可以发布任何代码。谢谢你的回复

我一定是做错了什么,因为它不起作用。如果我理解正确,我应该能够在任何地方转储Debug.WriteLine()语句并运行此程序,但我什么也得不到。我想我现在有了它,但我必须使用Debug.WriteLine()。
/// <summary>Runs the provided service classes.</summary>
/// <param name="servicesToRun">The service classes to run.</param>
/// <param name="args">The command-line arguments to pass to the service classes.</param>
private static void RunServices(IEnumerable<ServiceBase> servicesToRun, IEnumerable args)
{
    var serviceBaseType = typeof(ServiceBase);
    var onStartMethod = serviceBaseType.GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (var service in servicesToRun)
    {
        onStartMethod.Invoke(service, new object[] { args });
        Console.WriteLine(service.ServiceName + " started.");
    }

    Console.WriteLine("Press any key to exit.");
    Console.ReadKey();

    var onStopMethod = serviceBaseType.GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (var service in servicesToRun)
    {
        onStopMethod.Invoke(service, null);
        Console.WriteLine(service.ServiceName + " stopped.");
    }
}