12

我正在努力应对一个相当困难的调试挑战,并希望有人可能会提供一些线索来完成这项工作。

这是场景:

我有一个 C# Windows 服务,它在具有管理员权限的用户帐户下运行,并在具有标准用户权限的用户帐户下启动一个单独的可执行进程。这两个进程旨在使用 WCF 进行通信。

不幸的是,当子进程启动时,它立即崩溃,事件日志中没有任何内容表明发生了什么。父进程继续运行,无一例外。

有关信息:这两个应用程序在父进程是桌面应用程序的配置中可靠地协同工作。我也成功地将父级作为 Windows 服务,但仅当两个进程在具有管理员权限的同一用户帐户下运行时。

我现在需要重新配置它们的关系来限制子进程的权限,但这是崩溃发生的时候。

为了证明我正在尝试做的事情是可行的,我创建了两个存根应用程序并以所需的配置成功启动它们。因此,我可以推断出我真正的子应用程序包含与此配置不兼容的内容,甚至在代码开始执行之前就导致崩溃。不幸的是,由于子进程是基于一些相当复杂的遗留代码,在我消除问题之前要隔离它的元素并不容易,所以我真的需要一种可靠的方法来逐步完成它。

如果我修改子进程的代码以在启动时立即启动调试,它会邀请我附加一个调试器,但无法完成附件,并显示一条消息指示The Just-in-time debugger does not have permission to debug the process.

我也看到了这个问题,并试图实施这个提议的解决方案(看起来很有希望),但它在我的场景中无法工作。而不是在启动应用程序之前启动调试,它似乎什么都不做 - 调试器和应用程序都没有启动,并且不显示调试邀请对话框。但是,我已经验证了这种技术在我的环境中有效(通过使用它来启动 Notepad.exe),因此很明显我的应用程序或我启动它的方式有一些问题导致了问题。

如果有人有任何建议,我很乐意进行实验并分享有关我的测试结果的更多详细信息。

非常感谢您的想法,

蒂姆

4

4 回答 4

4

调试器永远不会为子进程启动的事实意味着错误应该发生在服务器进程中。如果您正确设置Image File Execution Options(使用 GFlags 程序使用 Microsoft 的免费 Windows 调试工具最容易做到),那么这意味着您永远不会开始创建子项。最简单的测试方法是在代码中添加一个 Assert,在创建子进程调用之前,在调试模式下构建父服务,将其安装/注册为服务,然后启动它。当 Assert 弹出时,附加到进程,然后从那里开始调试。然后,您应该会看到父级中发生的创建过程错误。

如果要交互调试父服务和子进程,可以使用WinDbg和GFlags,但是会比较复杂。

您将需要 WinDbg 和 GFlags。这些工具作为 Windows 调试工具的一部分从 Microsoft 免费提供。您可以在此处找到该免费软件包:http: //msdn.microsoft.com/en-us/windows/hardware/gg463009.aspx

使用 GFlag 通过以下调试器选项为您的 PARENT SERVICE 设置执行选项:

"C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\WinDbg.exe" -server tcp:port=5000:9000 -o -g 

当 Windows 启动你的父服务时,它会在 WinDbg 下这样做。由于-o选项,WinDbg 还将管理启动的子进程,允许您从启动时交互式地调试子进程。由于-g选项,WinDbg 将启动 ParentService 并让它运行,而不是像正常调试行为那样在加载时停止它。这将阻止 Windows SCM 将其关闭并启动新实例。

因为你正在运行一个服务,它不能访问桌面,所以它的宿主 WinDbg 也不能。您必须将另一个调试器附加到运行 ParentService 的 WinDbg 的运行实例您可以使用另一个 WinDbg 实例来执行此操作。为此,启动第二个 WinDbg 实例,并使用菜单项“文件|连接到远程会话...”进行远程连接。在对话框中,输入:

tcp:端口=5000:9000,服务器=[机器名]

连接后,您将能够使用 ParentService.exe,当它创建 ChildProcess 时,执行上下文将切换到它,您也将能够对其进行调试。

我以前曾使用这种技术来调试由 Windows 服务创建的子进程。这并不像在其 IDE 中的 Visual Studio 内置调试​​器中调试某些东西那么简单,但它确实有效。

WinDbg 有大量可用的文档,包括来自 Microsoft 和其他在线来源的文档。我在上面提供的 URL 包含指向 WinDbg 文档的链接。

我建议使用 GFlags,因为它会对您的注册表进行所有必要的编辑,以便在您选择的调试器下运行可执行文件。它还有更多功能,值得花时间学习。

WinDbg 启动时可以设置断点和设置各种选项。我将-g选项替换为命令行选项:

-c "$$<c:\MyDebugCommands.txt"

这指示 WinDbg 运行命令,该命令是运行名为“MyDebugCommands.txt”的 WinDbg 脚本。我用我需要的所有设置更改(例如加载符号选项)填充 MyDebugCommands.txt 文件,以及设置我感兴趣的断点,文件中的最终命令是-g

正如我所说,它不像使用 VS IDE 及其内置调试器那么简单,但它可以让您交互式地调试您的父服务及其启动的子进程。

于 2012-12-04T19:16:27.750 回答
2

根据我基于上述场景的测试(父进程是具有管理员权限的服务,子进程是没有管理员权限的控制台),当我人为地强制子进程立即抛出权限异常时,我看到与您相同的调试错误它开始。此实例中的错误消息可能具有误导性,因为不清楚这实际上是调试器权限问题

了解您的子进程是什么类型的应用程序会很有用,因为这会影响您拥有的调试选项。

我尝试调试的第一种方法是在我的子进程(控制台应用程序)中拦截所有未处理的异常。您可以通过在子应用程序的启动过程中添加以下代码来做到这一点:

AppDomain.CurrentDomain.UnhandledException += new 
    UnhandledExceptionEventHandler(App_UnhandledException);

然后我将代码添加到我的App_UnhandledException过程以记录异常。这对我有用,我可以看到权限错误的原因。唯一需要注意的是,这不会拦截由于权限问题而导致您的应用程序甚至无法加载的异常。但是这种方法至少应该减少您在理解权限问题方面的搜索空间。

如果在您的异常处理程序到达之前生成异常,另一种可能性是使用程序集绑定日志查看器。这是一个非常有用的工具。

FWIW,您可以通过在 Visual Studio 中启动服务来单步执行您的服务代码(但遗憾的是不能进入您的子进程)。下面在名为 DEBUG 的 switch case 中显示的代码将允许您在 VS 中启动/调试您的服务。

// This is the entry point
static void Main(string[] args)
{
    // If parameter passed, act on it
    if ( args.Length > 0 )
    {
        switch (args[0] )
        {
            // Debug the service as a normal app from within Visual Studio
            case DEBUG:
                MyService DebugService = new MyService();
                DebugService.OnStart(null);
                break;
            // Install the service programatically
            case INSTALL:
                ManagedInstallerClass.InstallHelper(new string[] _
                { Assembly.GetExecutingAssembly().Location });
                break;
            // Un-install the service programatically
            case UNINSTALL:
                ManagedInstallerClass.InstallHelper(new string[] +
                { UNINSTALL, Assembly.GetExecutingAssembly().Location });
                break;
            // We don't understand this parameter!
            default:
                message = string.Concat(DEBUG, " to run service manually.",     Environment.NewLine);
                message += string.Concat(INSTALL, " to install service.",     Environment.NewLine);
                message += string.Concat(UNINSTALL, " to un-install service.",     Environment.NewLine);
                message += string.Concat("Do not understand the command-line parameter ", args[0]);
                throw new System.NotImplementedException(message);
        }
    }
    // If no parameter passed, just start the service normally
    else
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new MyService() };
        ServiceBase.Run(ServicesToRun);
    }
}
于 2012-11-30T11:37:49.523 回答
2

您是否尝试过以管理员身份运行 Visual Studio 并调用Process.EnterDebugMode()方法?

于 2012-12-05T08:09:49.060 回答
1

如果我修改子进程的代码在启动时立即启动调试,它会邀请我附加一个调试器,但无法完成附件,并显示一条消息表明即时调试器没有调试权限过程

以管理员身份运行 secpol.msc,并在“本地策略 | 用户权限管理”选择“调试程序”。然后将“用户”组添加到其中。看看这是否解决了权限问题。

高温高压

于 2012-12-11T04:49:25.330 回答