远程进程的 Visual Studio 2012 调试未按预期工作

Posted

技术标签:

【中文标题】远程进程的 Visual Studio 2012 调试未按预期工作【英文标题】:Visual Studio 2012 debugging of remote process not working as expected 【发布时间】:2012-11-18 14:47:12 【问题描述】:

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

这是场景:

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

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

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

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

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

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

我也见过this question 并尝试实现this proposed solution(看起来很有希望),但在我的场景中它无法工作。它没有在启动应用程序之前启动调试,而是似乎什么都不做 - 调试器和应用程序都没有启动,并且不显示调试邀请对话框。但是,我已经验证了这种技术在我的环境中有效(通过使用它来启动 Notepad.exe),所以很明显我的应用程序或我启动它的方式是导致问题的原因。

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

非常感谢您的想法,

提姆

【问题讨论】:

调试器永远不会为子进程启动的事实意味着错误应该发生在父进程中。如果您正确设置了Image File Execution Options(使用 GFlags 程序使用 Microsoft 提供的免费 Windows 调试工具最容易做到这一点),那么这意味着您永远不会开始创建子项。 您如何准确地调用具有较低权限的新流程? 【参考方案1】:

调试器永远不会为子进程启动这一事实意味着错误应该发生在 PARENT 服务器进程中。如果您正确设置了Image File Execution Options(使用 GFlags 程序使用 Microsoft 的免费 Windows 调试工具最容易做到),那么这意味着您永远不会开始创建子项。最简单的测试方法是在代码中添加一个断言,就在创建子进程调用之前,在调试模式下构建父服务,将其安装/注册为服务,然后启动它。当 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 还将管理启动的子进程,允许您从启动时交互式地DEBUG子进程。由于 -g 选项,WinDbg 将启动 ParentService 并让它运行,而不是像正常调试行为那样在加载时停止它。这将阻止 Windows SCM 将其关闭并启动新实例。

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

tcp:Port=5000:9000,Server=[机器名]

连接后,您就可以使用 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 及其内置调试器那么简单,但它可以让您以交互方式调试父服务及其启动的子进程。

【讨论】:

【参考方案2】:

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

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

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

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

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

如果在您的异常处理程序到达之前生成异常,另一种可能是使用assembly binding log viewer。这是一个非常有用的工具。

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);
    

【讨论】:

感谢您的建议,我同意这无疑是一种管理服务调试的灵活方式。但是,我已经成功地从服务启动调试器(通过在服务代码中插入 Debugger.Launch() )并逐步启动单独的进程。您是说有某种方法可以直接从一个流程过渡到另一个流程吗?据我所见,子进程一启动,原来的调试会话就独立于它了。之后,子进程几乎立即崩溃。 鉴于当两个进程以相同权限运行时它正在工作,我怀疑子进程几乎立即失败并出现权限错误。今晚回家后我会进行测试,看看如何调试它。 感谢@RoadWarrior - 我期待您提供任何线索。 您更新的帖子肯定让我更接近解决方案。您对缩小权限问题搜索范围的建议也很有帮助,尽管我怀疑我的问题属于您提到的更困难的情况。尽管如此,我会对此进行试验并发布更新。为了回答你的问题,我的子进程是一个隐藏的 Winforms 应用程序,它承载了一个 WebBrowser 控件(它本身就增加了各种复杂性)。再次感谢。 我修改了我的代码以包含您的建议,但它在能够记录任何内容之前一直崩溃。很遗憾,因为我非常希望这是一个可行的解决方案,但显然这个问题比这更难以捉摸。无论如何,非常感谢您的想法和您所做的所有调查工作。【参考方案3】:

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

【讨论】:

【参考方案4】:

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

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

HTH

【讨论】:

以上是关于远程进程的 Visual Studio 2012 调试未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 远程调试 - 在同一域中找不到计算机

难以让 Visual Studio 2010 附加到远程进程

Visual Studio 2012 中的远程调试

Visual Studio - 以编程方式将调试器附加到远程进程

Visual Studio 远程调试正在运行的进程

Visual Studio 2012 远程调试:对内存位置的无效访问