在依赖项关系图上可视化代码依赖项

若要查看和理解您的代码及其依赖项组织,可以在最终的 Visual Studio 中创建依赖项关系图。 什么是如何依赖项关系图和它们是有用的?

支持的项目:

  • visual C# .NET 和 Visual Basic .NET 项目,包括 Windows 存储 app 和程序集文件(.exe 或.dll)。

  • Visual C++项目,包括 Windows 存储 app,具有 C 或 C++代码(托管或本机),标头文件(.h 或 #include)和二进制文件。

视频:

主题内容

  • 如何启动?

  • 在代码中可视化的依赖项

  • 共享依赖项关系图

  • 生成批处理的关系图

  • 疑难解答

如何启动?

  • 获取您的解决方案概述:在 体系结构 菜单中,选择 生成依赖项关系图针对解决方案

    您看到一个显示顶级程序集的关系图。 通过展开这些程序集此时测试这些程序集。 将鼠标指针悬停在程序集的顶部,然后选择尖括号(^ )按钮,当出现。 执行相同的命名空间、类型和成员的才能继续测试代码。

    程序集的顶级依赖项关系图

    若要查看跨组依赖关系,请选择在关系图上的项。

    具有选定项和展开组的关系图

    有关更多信息,请参见 获取您的解决方案概述。 还可以运行分析器查找代码中的潜在问题。 请参见 在依赖项关系图上查找代码中的潜在问题

  • 可视化在解决方案中的特定依赖关系:在 解决方案资源管理器,选择感兴趣的项目。 可以选择项目中,程序集引用,文件夹、文件、类型和成员。 若要查找特定项,请使用 解决方案资源管理器 搜索框。

    解决方案资源管理器 工具栏上,选择 创建新的关系图文档 “从选定节点创建新的关系图”按钮

    - 或 -

    从拖到 解决方案资源管理器 的项目添加到现有关系图。

    提示

    若要创建空白关系图,请在 文件 菜单上,选择 新建文件定向关系图文档。当您拖动项时,要包含项目的父层次结构,请按住 CTRL 键。

    您看到一个显示您选定项的关系图。 通过展开这些程序集此时测试这些项目。 移动鼠标指针在项目的顶部,然后选择尖括号(^ )按钮,当出现。

    有关更多信息,请参见 可视化在解决方案中的特定依赖关系。 还可以运行分析器查找代码中的潜在问题。 请参见 在依赖项关系图上查找代码中的潜在问题

什么是如何依赖项关系图和它们是有用的?

依赖项关系图提供可视方式检查代码并了解其依赖项,而不必通过代码文件和行查找。 您可以查看项目并将关系表示为节点和链接或箭头。 例如,假设您具有要执行的代码评审。 对于包含挂起的更改的设置文件。 可以通过创建在中生成代码和依赖项这些更改的从这些文件。 可视化在解决方案中的特定依赖关系参见。

默认情况下,容器关系表示为 组,可以展开和折叠。

带有分组节点的依赖项图

提示

重新提取子级 按钮 “重新提取子级”图标 可以检索存在于代码中的组成员,而不出现在关系图上。我们还可以将不同于调用链接,以便您可以更轻松地看到它。请参见 编辑和自定义依赖项关系图

还可以查看容器关系作为链接。 打开关系图的快捷菜单,选择 组合关闭分组:

带有节点和链接的依赖项图

还可以运行分析器查找代码中的潜在问题。 请参见 在依赖项关系图上查找代码中的潜在问题

当您首次生成找到的依赖项关系图,Visual Studio 索引所有依赖项。 此过程可能需要一些时间,特别是的大型解决方案或图形与许多链接。 但是,索引提高最新操作性能。 如果代码更改,Visual Studio 会用仅更新代码。

如果不希望等待关系图完成生成,则可以在+任何+时间取消此步骤并尝试以下建议:

  • 关系图关注您的依赖项。

  • 在生成整个解决方案的关系图之前,请减小解决方案范围。

尽管 Visual Studio 可以运行具有 1 GB 内存,建议您的计算机有避免使用的内存至少 2 GB 长时间的延迟,当 Visual Studio 创建代码索引并生成关系图时。

备注

Visual Studio 将生成一个关系图,如果至少有一个成功生成项目。它显示成功生成的代码的依赖关系。如果生成错误为某些元素生成,则这些元素的错误出现在关系图上。在基于关系图做出体系结构决策时,请确保组件实际生成并且有依赖项。

在项目项的 复制到输出目录 属性设置为 始终复制时,它可能需要更多时间创建关系图或将项添加到关系图从解决方案资源管理器。这可能导致问题增量生成和 Visual Studio 都会重新生成项目。若要提高性能,请将此属性更改 如果较新则复制 或 PreserveNewest。请参见 增量生成

在代码中可视化的依赖项

  • 获取您的代码概述

  • 可视化在代码中的特定依赖关系

  • 可视化 C 或 C++之间的依赖项源文件和标头文件

  • 生成过程使用 GraphCmd.exe 的批的关系图

提示

若要生成 C++项目的完全图形,在这些项目必须设置浏览信息编译器选项(/FR)。否则,消息并提示您设置此选项。如果显示消息,可以通过选择 确定设置仅当前关系图选项,也可以设置选项和隐藏所有最新关系图的消息。若要使消息为最新关系图显示,请将以下注册表项。0 或删除密钥:

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0\NativeProvider : AutoEnableSbr

请参见 /FR、/Fr(创建 .Sbr 文件)

获取您的代码概述

您可以创建自己的整个解决方案或 对于程序集或二进制文件的依赖项关系图。

Dd409453.collapse_all(zh-cn,VS.110).gif获取您的解决方案概述

  1. 体系结构 菜单中,选择 生成依赖项关系图针对解决方案

    您看到一个显示顶级程序集和是聚合链接它们之间的关系图。 复合链接的粗细指示多少单独的依赖关系该链接表示。 外部 组包含任何内容。您的解决方案之外,包括平台依赖项。 外部程序集公开仅使用的项。

    程序集的顶级依赖项关系图

  2. 通过展开这些程序集此时测试这些程序集。 将鼠标指针悬停在程序集的顶部,然后选择尖括号(^ )按钮,当出现。 (键盘:选择项,然后选择 加号 键(+)。)执行相同选件命名空间、类和成员的才能继续测试代码。

  3. 获取有关项目或链接的更多信息,将指针移到该项的顶部,直至出现工具提示。

  4. 若要检查聚合链接和依赖关系表示的项目,请首先选择该链接,然后打开其快捷菜单。 选择 显示当前关系图中的参与链接新关系图中的参与链接

    Visual Studio 展开组在链接的两端并显示参与该链接的那些项目和依赖项。 若要查看存在于代码,但从的组中缺少的项目,请选择 重新提取子级 “重新提取子级”图标

请参见:

Dd409453.collapse_all(zh-cn,VS.110).gif获取程序集或双精度概述

  • 创建空白关系图或打开现有关系图(.dgml 文件)。 从 Visual Studio 外部,将程序集或二进制文件拖到关系图上。

    备注

    仅当您运行的是 Windows 资源管理器和 Visual Studio 在同一用户访问控制(UAC)权限级别,您可以将程序集或二进制文件从 Windows 资源管理器。例如,如果启用了 UAC,并且您正在管理员身份运行 Visual Studio,则 Windows 资源管理器将阻止拖动操作。若要解决此问题,请确保 Visual Studio 以管理员身份运行,或者关闭 UAC。请注意在 Windows 8 中,Windows 资源管理器是文件资源管理器。

    - 或 -

    1. 体系结构 菜单中,选择 Windows体系结构资源管理器

    2. 在第一列,在 文件系统下,选择 选择文件

    3. 打开 框中,找到并选择该程序集或二进制文件。 选择 打开 将它们添加到 体系结构资源管理器的下一列。

    4. 体系结构资源管理器,选择程序集或二进制文件。

    5. 在包含选定的程序集或二进制文件的列的右侧,选择折叠的操作列将其展开。

      提示

      当您将指针移到它上面时,折叠的操作列将显示。

    6. 在操作列中,在 命令下,选择 打开 生成依赖项关系图。

      请参见 使用体系结构资源管理器查找代码

Dd409453.collapse_all(zh-cn,VS.110).gif创建空白关系图

  1. 若要打开一个新的空白关系图不将其添加到解决方案中,在 文件 菜单上,选择 新建文件

    - 或 -

    若要将空白关系图添加到解决方案中的 解决方案项 文件夹中,打开顶级解决方案节点的快捷菜单。 选择 添加新建项

  2. 已安装下,选择 常规

  3. 在右窗格中,选择 定向关系图文档

    还可以将空白关系图添加到建模项目 体系结构 从菜单中选择 新建关系图

可视化代码中的特定依赖关系

可以在解决方案或 在程序集和 double可以创建特定项目的依赖项关系图。

可视化在解决方案中的特定依赖关系

如何可视化特定代码

如何可视化特定代码和父代码

Dd409453.collapse_all(zh-cn,VS.110).gif

  1. 解决方案资源管理器,选择感兴趣的项目。 可以选择项目中,程序集引用,文件夹、文件、类型和成员。 若要查找特定项,请使用 解决方案资源管理器 搜索框。

    提示

    若要查找项。在类型或成员的依赖关系,请打开该类型的快捷 解决方案资源管理器的菜单或成员。选择依赖项类型。解决方案资源管理器中显示具有依赖项指定的项目。选择结果。

  2. 若要绘制您的项目以及它们的成员,解决方案资源管理器 工具栏中,选择 创建新的关系图文档 “从选定节点创建新的关系图”按钮

    - 或 -

    要包括在关系图的父层次结构,打开 创建新的关系图文档… 列表中 解决方案资源管理器 工具栏上的,然后选择 新建包含上级的依赖项关系图

    提示

    还可以将项拖动到关系图中。首先,创建空白关系图 或打开现有关系图(.dgml 文件)。当您拖动项时,要包含父容器层次结构,请按住 CTRL 键。

    您看到一个显示您选定项的关系图。

  3. 测试项目,可以展开这些部分。 移动鼠标指针在项目的顶部,然后选择尖括号(^ )按钮,当出现。 若要展开所有项目中,打开关系图的快捷菜单。 选择 组合全部展开

    备注

    此命令不可用,如果展开所有组生成一个不可用的关系图或内存问题。

  4. 从的组中检索丢失的项目,请选择该组内的 重新提取子级 “重新提取子级”图标

  5. 若要查看多个项目与个相关在关系图上,打开项目的快捷菜单。 选择 显示 和感兴趣项目的关系。

    对于程序集,选择:

    引用的程序集

    将此程序集引用的程序集。 外部程序集在 外部 组中显示。

    引用程序集

    添加引用此程序集在解决方案中的程序集。

    对于选件类,请选择:

    基类型

    对于选件类中,添加基类和实现的接口。

    对于接口,添加基接口。

    派生类型

    对于选件类中,添加派生类。

    对于接口,添加派生的接口和实现的选件类或结构。

    所有基类型

    递归添加基类或接口层次结构。

    所有派生类型

    对于选件类,请递归添加所有派生类。

    对于接口,添加所有派生的接口和递归实现选件类或结构。

    包含层次结构

    添加父容器的层次结构。

    使用的类型

    添加所有选件类和此选件类使用自己的成员。

    使用者类型

    添加使用此选件类的所有选件类及其成员。

    对于方法中,选择:

    包含层次结构

    添加父容器的层次结构。

    所调用的方法

    将此方法的方法。

    调用者方法

    添加调用此方法的方法。

    在基类型的重写的方法

    对于重写其他方法或者实现接口方法的方法中,添加在重写的基类,并且,如果有,则实现的接口的方法的所有抽象方法或虚方法。

    引用的字段

    将此方法引用的字段。

    对于字段中,选择:

    包含层次结构

    添加父容器的层次结构。

    引用方法

    添加引用此字段的方法。

请参见:

Dd409453.collapse_all(zh-cn,VS.110).gif可视化程序集或二进制文件中的特定依赖关系

  1. 体系结构 菜单中,选择 Windows体系结构资源管理器

  2. 在第一列,在 文件系统下,选择 选择文件

  3. 打开 框中,找到并选择该程序集或二进制文件。 选择 打开 将它们添加到 体系结构资源管理器的下一列。

  4. 在下一列中,选择程序集或二进制文件。

    默认情况下,下一列将显示选定的项目所包含的项。

    提示

    若要选择其他相关项目,请在与您选择的列右侧展开折叠的列。在 节点导航下,选择感兴趣的项目。在 出站导航入站导航下,选择感兴趣的关系的种类。请参见 使用体系结构资源管理器查找代码

  5. 找到并选择要使该关系图中的所有项目。

  6. 若要创建新的关系图,请在 体系结构资源管理器 工具栏,选择 创建新的关系图从所有选定节点文档 “从选定节点创建新的关系图”按钮

    Visual Studio 创建关系图并将其打开。

    - 或 -

    若要将所选内容添加到关系图,请执行以下步骤:

    1. 打开关系图的.dgml 文件或 创建空白关系图。

    2. 体系结构资源管理器 工具栏,选择 添加任何所选节点到当前可见关系图文档 “将所有选定节点添加到关系图”按钮

      - 或 -

      体系结构资源管理器 的项拖动到关系图。

可视化 C 和 C++之间的依赖项源文件和标头文件

查看以下二者之间的依赖项

操作步骤

解决方案中的所有源文件和标头文件

体系结构 菜单中,选择 生成依赖项关系图对于包含文件

本机代码的依赖项关系图

当前打开的文件和相关源文件和标头文件

  1. 打开源文件或标头文件。

  2. 打开快捷菜单中的任意位置文件中。 选择 生成包含文件的关系图

.h 文件的第一级依赖项关系图

备注

当您打开包含 Visual C++项目的解决方案时,可能需要花时间更新 IntelliSense 数据库。此时,您可能无法创建标头(.h 或 #include)文件的依赖项关系图,直到 IntelliSense 数据库完成更新。您可以监视在 Visual Studio 状态栏的更新的进度。若要解决问题或显示的消息,因为某些 IntelliSense 设置被禁用,请参见 疑难解答。

共享依赖项关系图

使用 Visual Studio 的其他用户保存和共享图形

  • 使用 文件 菜单将关系图。

    - 或 -

    为特定项目的一部分,若要将关系图,打开关系图的快捷菜单图面。 选择 移动 <DependencyGraphName.dgml> into 和项目希望保存该关系图的位置。

    Visual Studio 将关系图另存为可以与最终 Visual Studio 的其他用户共享的.dgml 文件,Visual Studio 高级专业版和 Visual Studio 专业版。

    备注

    在使用 Visual Studio 高级专业版和 Visual Studio professional 的人共享图形之前,请确保展开所有组,显示隐藏的节点和跨组链接和检索您的关系图希望其他发现的任何已删除的节点。否则,其他用户无法查看这些项目。

    保存建模项目中的图形或从建模项目复制到其他位置的图形时,可能会出现以下错误:

    “无法在项目目录外保存 fileName。不支持链接的项。”

    Visual Studio 显示错误,但是,仍将创建保存的版本。若要避免错误,请在建模项目外创建图形。然后,可将图形保存到所需的位置。若只是将文件复制到解决方案中的其他位置,那么尝试保存图形将不起作用。

导出关系图,图像,因此您可以复制到其他应用程序中,如 Microsoft Word 文档或 PowerPoint

  1. 打开关系图的快捷菜单图面。 选择 编辑复制图像

  2. 将该图像粘贴到另一个应用程序中。

导出关系图,XPS 文件,以便您可以看到它(如 Internet Explorer 的 XML 或 XAML 浏览器

  1. 从打开关系图的快捷菜单图面。 选择 保存为 XPS

  2. 另存为 对话框中,浏览到要保存文件的位置。

  3. 命名该图形。 确保 保存类型 框设置为 XPS 文件(*.xps)。 选择 保存

生成批处理的关系图

可以以批处理模式生成关系图文档(.dgml 文件)使用 GraphCmd.exe 命令行工具。 例如,可以运行该工具,在每次生成查找已更改生成之间的依赖项之后。 若要查找此工具,请在以下文件夹中进行查找:C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE。

备注

GraphCmd.exe 仅支持.NET 代码并生成依赖项仅供参考程序集或.dgml 文件,在 Visual Studio 解决方案或项目文件的不是源代码。GraphCmd.exe 在 Visual Studio 之外运行,因此,对于在 DGQL 查询的操作的支持是有限的。

为 GraphCmd.exe 使用以下语法:

GraphCmd -? -all -exceptions -input File_Name -query File_Name -exec "DGQL_Statement" -output File_Name -path alias=path

提示

可以多次指定下列选项:-input-query-exec-path

下表描述 GraphCmd.exe 的选项:

-?

显示 GraphCmd.exe 的帮助信息。

-all

包括所有中间查询结果,而不仅仅是最后一个节点集的中间查询结果。

-exceptions

以关系图文档 (.dgml) 文件的形式报告查询异常。

-input File_Name

处理指定的 .dgml 文件。

这可用于后期处理大型.dgml 文件并对其进行筛选,以便您在 Visual Studio 可以更轻松地对该文件进行可视化。

-query File_Name

运行指定的定向关系图查询语言(DGQL 或 .dgql)文件。

请参见:

-exec "DGQL 语句"

运行指定的 DGQL 语句。

了解定向关系图查询语言(DGQL)参见。

-output File_Name

输出指定的 .dgml 文件。

-path alias=path

指定一个在 DGML 文档的输入和输出中使用的新别名。

例如:

GraphCmd -output MyGeneratedGraph.dgml -path "MyPathAlias=C:\Program Files\..."

常用路径的别名参见。

Dd409453.collapse_all(zh-cn,VS.110).gif常用路径的别名

常用路径的别名将减小 .dgml 文件的大小,并减少加载或保存该文件所需的时间。 若要创建别名,请在 .dgml 文件的结尾处添加 <Paths></Paths> 部分。 在此部分添加 <Path/> 元素以定义路径的别名:

<Paths>
   <Path Id="MyPathAlias" Value="C:\...\..." />
</Paths>

若要从 .dgml 文件中的某个元素引用别名,请用美元符号 ($) 和括号 (()) 将 <Path/> 元素的 Id 括起来。

<Nodes>
   <Node Id="MyNode" Reference="$(MyPathAlias)MyDocument.txt" />
</Nodes>
<Properties>
   <Property Id="Reference" Label="My Document" DataType="System.String" IsReference="True" />
</Properties>

若要编辑.dgml 文件,请参见 编辑和自定义依赖项关系图

Dd409453.collapse_all(zh-cn,VS.110).gif了解定向关系图查询语言 (DGQL)

DGQL 是一种可用于生成 DGML 的轻量查询语言。 DGQL 语句遵循以下节点选择和操作的交替模式:每个节点选择为下一个操作创建输入,而下一个操作的输出将成为下一个节点选择的输入,依此类推。

DGQL 语句的格式为:

<node selection> / <action> / <node selection> / <action> / ...

下表描述用于选择节点的 DGQL 语法:

*

选择所有节点。

+ "text"

选择所有包含“text”的节点。

+ Id.Equals("text")

选择所有其 Id 等于“text”的节点。

+ Background.Contains("text")

选择所有其 Background 特性具有包含字符串“text”的值的节点。

+ "text1" + "text2" + ...

选择所有匹配“text1”或“text2”的节点。

+ MyProperty="True"

选择所有具有名为 MyProperty 的属性(其值为“True”)的节点。

- Label.Contains("text")

选择所有节点,具有包含 (Contains) 字符串“text”的 Label 特性的节点除外。

+ Category.Is("MyCategory")

选择所有具有名为 MyCategory 的类别的节点或从 MyCategory 继承的节点。

下表描述了可对所选节点执行的直接操作的示例:

示例操作

描述

Microsoft.Contains

返回输入节点所包含的所有节点。 可以将 Contains 替换为一个不同的链接类别。

Microsoft.Open

打开输入节点的源代码。

说明说明
此操作仅在 Visual Studio 中有效。

Microsoft.AllOutBoundLinks

返回作为来自输入节点的传出链接的目标的所有节点。

Microsoft.AllInboundLinks

返回作为指向输入节点的链接的源端的所有节点。

Microsoft.Core.CreateGroupsByProperties

调用 GroupByProperties 操作。

Microsoft.AllNodes

返回目前为止整个关系图中包含的所有节点。

数据驱动的操作仅基于输入节点和链接中的数据选择项。 在使用数据驱动的操作匹配类别时,将包含继承的类别。 下表描述数据驱动的操作的示例:

类型

描述

Node:Both:Category

返回具有类别 Category 并通过指向任一方向的链接与输入节点连接的所有节点。

Link:Both:Category

返回通过指向任一方向的链接与输入节点连接并具有类别 Category 的所有节点。

Link:Backward:Category

返回利用具有类别 Category 的链接指向输入节点的所有节点。

Link:Forward:Category

返回利用具有类别 Category 的链接从输入节点指向的所有节点。

Dd409453.collapse_all(zh-cn,VS.110).gif提示

  • 通常,对于一组给定的输入节点存在一个“默认”操作,此操作由体系结构资源管理器自动选定。 若要获取相同的行为,请使用空操作://

  • 由于空白在 DGQL 中没有意义,因此必要时您可以设置查询的格式以适合某个行。 在使用带有 GraphCmd 的 –exec 选项时,这将很有用。

  • 在调试 DGQL 时,使用体系结构资源管理器中的操作“Execute Expanded"”可帮助您查看查询的每个步骤,并查找未产生预期结果的步骤。

Dd409453.collapse_all(zh-cn,VS.110).gif示例

下面的 DGQL 语句执行一条查询,如下列步骤中所述:

+ Id.Equals("Microsoft.Solution.ClassView") / "Microsoft.Solution.ClassView" / * / "Node:Both:CodeSchema_Class" / + CodeSchemaProperty_IsPublic.Equals("True")
  1. 选择在 体系结构资源管理器的第一列中的 类视图 节点。

  2. 执行操作“"Microsoft.Solution.ClassView",返回该解决方案中的所有命名空间。 

  3. 使用 * 选择所有命名空间。

  4. 通过指向任一方向的链接选择具有 CodeSchema_Class 类别且与这些命名空间相关的所有节点。 它们通常是包容链接。

  5. 仅从生成的类中筛选出具有属性 CodeSchemaProperty_IsPublic="True" 的类。

从技术上说,"Microsoft.Solution.ClassView" 操作不是必需的操作,因为它是**“类视图”**节点的“默认”操作。 因此,可以将此操作替换为 // 以简化查询,并在单个行上设置其格式,如下所示:

+Id.Equals("Microsoft.Solution.ClassView")//*/"Node:Both:CodeSchema_Class"/+CodeSchemaProperty_IsPublic.Equals("True")

疑难解答

以下各项是不用于 C 和 C++代码支持:

  • 基类型不显示在包含父层次结构的关系图。

  • 大多数 显示 菜单项用于 C 和 C++代码不可用。

可能会出现以下问题,当创建 C 和 C++代码依赖项关系图时:

问题

可能的原因

解决方法

未能依赖项关系图生成。

在解决方案中的项目未成功生成。

修复生成的生成错误并重新生成关系图。

当您尝试从“体系结构”菜单中生成依赖项关系图时,Visual Studio 无响应。

程序数据库 (.pdb) 文件可能已损坏。

.pdb 文件将存储调试信息,例如,类型、方法和源文件信息。

请参见 [OBSOLETE] 程序数据库文件 (C++)

重新生成解决方案,然后重试。

禁用 IntelliSense 浏览器数据库的某些设置。

Visual Studio 的“选项”对话框中可能已禁用某些 IntelliSense 设置。

打开设置以启用它们。

请参见 选项,文本编辑器,C/C++,高级

消息“未知方法”将出现在方法节点上。

由于无法解析方法的名称,导致出现此问题。

二进制文件可能没有基重定位表。

在链接器中打开 /FIXED:NO 选项。

请参见 /FIXED(固定基址)

无法生成程序数据库 (.pdb) 文件。

.pdb 文件将存储调试信息,例如,类型、方法和源文件信息。

有关更多信息,请参见[OBSOLETE] 程序数据库文件 (C++)

在链接器中打开 /DEBUG 选项。

请参见 /DEBUG(生成调试信息)

无法在预期位置打开或找到 .pdb 文件。

确保 .pdb 文件位于预期位置。

已从 .pdb 文件中去除调试信息。

如果链接器中已使用 /PDBSTRIPED 选项,则改为包含完整的 .pdb 文件。

请参见 /PDBSTRIPPED(去除私有符号)

调用方不是函数,它是二进制文件中的形式转换 (thunk) 或数据节中的指针。

当调用方是形式转换 (thunk) 时,尝试使用 _declspec(dllimport) 以避免形式转换 (thunk)。

请参见:

不用我是否能执行?

在何处可以如何获取更多信息?

类别

链接

论坛

博客