如何调试从外部应用程序调用的类库?

41

有一个外部工作流可以执行C#脚本,并且能够使用DLL文件(我的类库)。

是否可以将调试附加到我的类库项目中,以便在此工作流调用它时触发断点?

谢谢


你有这个库的源代码吗?如果没有,你可以在调试输出中使用它抛出的异常。 - nikola-miljkovic
请参见:https://dev59.com/C3RB5IYBdhLWcg3wV196 - Blachshma
我没有应用程序的源代码,只是使用我的DLL。因此,https://dev59.com/C3RB5IYBdhLWcg3wV196打开了两个解决方案... - Sergejs
nikolaMM94,如果我无法将调试器附加到我的DLL上,异常会如何帮助我? - Sergejs
请告诉我是否不可能,我必须记录对象内容? - Sergejs
5个回答

38

是的,你可以使用Visual Studio来实现。你有两个选项:

配置项目以启动外部程序

  1. 打开你的DLL项目。

  2. 在项目属性中,选择 调试 选项卡。

  3. 选择 启动外部程序 并提供调用你的DLL的外部程序的路径,以及任何可能需要提供的命令行参数和工作目录(如果相关)。

  4. 保存项目。

  5. 在代码中设置断点。

  6. 按F5开始调试。(此时,你的断点将显示无法命中,因为未加载符号。暂时不必担心。)

  7. 执行任何使外部应用程序加载库并运行代码的操作。

Visual Studio将检测到模块加载、加载符号,并停止在断点处。

连接到现有进程

如果你无法启动进程,而是必须连接到已经运行的进程,则也可以这样做:

(附注:如果你使用的是Visual Studio的“Express”版本,则我认为它没有此功能,但我不能确定。很容易判断:你要么有下面第4步中提到的菜单项,要么没有。)

  1. 确保进程正在运行。

  2. 打开你的DLL项目。

  3. 设置断点等。

  4. 调试 菜单中选择 连接到进程...

  5. 在弹出的对话框中找到进程,选中它,并点击 连接

  • Visual Studio会进入调试模式。(此时,您的断点将显示它们不会被命中,因为符号尚未加载。暂时不用担心这个问题。)

  • 执行使外部进程加载并运行您的代码的操作。

  • Visual Studio将检测外部进程中的模块加载、加载您的符号,并停止在您的断点处。


    N.B. 在两种情况下,如果外部进程从除项目的 bin/Debug 文件夹以外的其他位置加载您的DLL,您必须确保每次构建后都将DLL复制到该其他位置(您可以在项目选项中设置自动完成此操作)。否则,Visual Studio将无法检测到正在加载的DLL是否是您要调试的那个。


    Crowser,你描述的两种解决方案在我这里(Visual Studio 2013)的工作方式不同:正确附加到进程会加载PDB并命中断点,而“启动外部程序”似乎根本没有加载PDB。在这种情况下,当我转到调试>窗口>模块时,我找不到我的DLL,但代码仍然按预期执行;只是断点无法命中。在启动外部程序期间加载PDB是否有任何特定要求? - Sasha
    1
    我可以确认,对于 Visual Studio 2015 Express for Web,此功能是可用的。我能够附加到从 Linqpad 运行的查询。虽然这不是明显的流程,但最终我选择了一个可行的方法。感谢您提供的答案。 - pwilcox

    4
    您可以使用“调试”菜单中的“附加到进程”来调试您的DLL项目。如果无法通过本机代码进行调试,则可能需要使用混合模式调试。可以通过在“附加到进程”窗口内单击“选择”按钮时出现的窗口中选择“托管和本机”代码类型来完成此操作。
    如果您使用的Visual Studio版本支持宏,那么您可以创建一个新的宏,并使用以下代码自动化所有操作:
    Imports System
    Imports EnvDTE
    Imports EnvDTE80
    Imports EnvDTE90
    Imports EnvDTE90a
    Imports EnvDTE100
    Imports System.Diagnostics
    
    Public Module AttachToProcess
    
        Public Sub DebugMyDLL()
            DTE.ExecuteCommand("Build.BuildSelection")
            Dim ApplicationExePath As String = "C:\Program Files (x86)\foo\bar.exe"
            Shell(ApplicationExePath)
            Try
                Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger
                Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")
                Dim dbgeng(2) As EnvDTE80.Engine
                dbgeng(0) = trans.Engines.Item("Managed (v4.0)")
                dbgeng(1) = trans.Engines.Item("Native")
                Dim proc2 As EnvDTE80.Process2 = dbg2.GetProcesses(trans, "<QualifierName>").Item("bar.exe")
                proc2.Attach2(dbgeng)
            Catch ex As System.Exception
                MsgBox(ex.Message)
            End Try
        End Sub
    
    End Module
    

    上述宏尝试构建您的项目,启动外部应用程序,然后自动将您的DLL附加到该程序。您可以从“附加到进程”窗口中获取系统的限定名。此外,托管代码("Managed (v4.0)"在本例中)的版本取决于您使用的.NET框架的版本。


    3

    如果你不想或者不能使用外部应用程序,你可以直接从Visual Studio调用类库:Ctrl + Alt + I显示"Immediate"窗口,然后你就可以在那里调用任何来自你的类库的方法(使用断点)。你需要输入完全限定的名称(即命名空间)。


    1
    谢谢!其他解决方案在我的特定用例中没有起作用。 - Ory Zaidenvorm

    2

    来自微软的一个有用链接:在托管DLL项目中指定调用应用程序

    1. 打开您的类库项目
    2. 转到[项目]
    3. 在底部选择项目的[属性...]
    4. 进入[调试]
    5. 在“启动”行中,选择"可执行文件"
    6. 选择.exe文件的路径
    7. 设置断点
    8. 运行项目。

    来自Microsoft的链接还包含其他有用的调试场景。

    我使用的是Visual Studio 16.4.5。


    -3

    我认为现在创建一个单元测试项目来执行您的库代码更加实际。这样,您就可以一举两得:能够在同一解决方案中调试项目,并开始通过测试覆盖您的代码。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接