65.9K
CodeProject 正在变化。 阅读更多。
Home

Comments Workbench for C++ - Visual Studio .NET 插件

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.67/5 (5投票s)

2004年6月9日

5分钟阅读

viewsIcon

48166

downloadIcon

633

Comments workbench for C++ - a Visual Studio .NET add-in.

Main screen

引言

所以,你已经写完了所有的代码,正悠闲地等着同事来评审。你打电话给你的女朋友,约她晚上见面,因为你所有的工作都终于结束了。你正准备下班,你的评审人打来电话,告诉你他一个字都看不懂你的代码,问你有没有上过高中,如果上过,你就应该知道代码在送审之前需要注释得很好吧?你终于打开了你最喜欢的编辑器,它以树状视图显示了所有的函数、类等,你创建了一个基本的注释模板。现在,你逐个函数地粘贴你的模板,然后逐个函数地编辑这个模板以适应代码并解释清楚。完成所有这些之后,你会想,这是否是最好的方法,或者你有没有可能更快地完成(而不取消你的约会)?我想,即使没有这个虚构的故事(我只是为了填补这个介绍部分;)),我们都明白注释的重要性。

幕后到底发生了什么?

嗯,在完全归功于代码之前,我必须感谢 Microsoft Visual Studio 7 架构师们向插件开发者公开了代码结构,否则这个项目是不可能实现的。那些看过或用过 C++ 语法的人会同意我的观点——它非常庞大且令人困惑!我见过很多类似的宏,但它们猜测参数、类型等,并且每一个都强制执行一种特定的注释风格。

实现这个插件的另一个目的是为了更多地学习 ATL,以及微软如何通过 COM 公开其接口来编写与产品无缝集成的插件。那些有 COM/ATL 经验的人,在看到代码后,会立刻意识到我只是这个学科的初学者,代码离稳定还有很长的路要走。

现在长话短说,让我们看看幕后到底发生了什么。

我们已经有什么了?

当你使用向导创建一个新项目时,你会得到以下内容:

  • 一个预定义的、半实现的类,名为 CConnect,它有以下声明:
    class ATL_NO_VTABLE <CODE>CConnect : 
        public CComObjectRootEx<CComSingleThreadModel>,
        public CComCoClass<CConnect, &CLSID_Connect>,
        public IDispatchImpl<EnvDTE::IDTCommandTarget, \
        &EnvDTE::IID_IDTCommandTarget, &EnvDTE::LIBID_EnvDTE, 7, 0>,
        public IDispatchImpl<AddInDesignerObjects::_IDTExtensibility2, \
        &AddInDesignerObjects::IID__IDTExtensibility2, 
        &AddInDesignerObjects::LIBID_AddInDesignerObjects, 1, 0>

    CConnect 派生的每个类的函数将在下面解释:

    1. CComObjectRootEx - 用于通过 Query interface 机制公开你的接口。你只需要在 BEGIN_COM_MAPEND_COM_MAP 宏中使用 COM_INTERFACE_ENTRY 输入你的代码,其余的将由这个类处理。
    2. CComCoClass - 这个类有助于创建类的实例并获取其属性。简而言之,它有助于你声明一个类工厂,并声明你的类可以被聚合。另一个宏 OBJECT_ENTRY_AUTO 有助于在注册表中更新信息,并在 DLL 被调用时立即实例化类的对象。
    3. IDispatchImpl - 这个类提供了 EnvDTE::IDTCommandTarget 接口的 IDispatch 部分的默认实现。因此,我们实现了 EnvDTE::IDTCommandTarget 以便能够添加命名命令,并让宿主知道我们有可执行的命令。我们还实现了 AddInDesignerObjects::_IDTExtensibility2IDispatch,以便宿主可以通知我们启动、连接和关闭等各种操作。

    嗯,以上大部分内容都是从 MSDN 复制或派生而来的,因为细节超出了文章的范围。我这里想说的是,向导完成了大部分工作,我们无需担心我们的插件将如何加载或销毁。下面,我们来看看为了实现我们的目标需要做什么。

  • 我们还为我们的类 CConnect 派生的各种接口提供了存根实现。
    1. CConnect::OnConnection - 在宿主连接到插件时调用。这是添加命令和工具栏对象到宿主的好时机。为此,我们使用 EnvDTE::_DTE 的一个实例,其中 EnvDTE 是类型库,_DTE 是类接口,它提供了对宿主公开的代码结构和各种项的访问。
    2. CConnect::OnDisconnection - 我们可以释放连接时获取的所有资源。通常,因为我们在连接时在插件中存储了 EnvDTE::_DTE 的引用,我们可以在这里释放它。
    3. CConnect::QueryStatus - 由宿主调用,以获取插件支持的命令名称。
    4. CConnect::Exec - 由宿主调用,以在插件内执行命令。这就是所有动作发生的地方。

我们该做什么?

如果你仔细研究了上面的部分,你将立即意识到 EnvDTE::_DTE 对象将为我们提供各种代码结构细节。现在,从这里开始,对于任何 C++ 爱好者来说都是小菜一碟。简而言之,我做了以下事情:

  1. 创建了主对话框,显示代码树和两个编辑框(一个用于注释,一个用于代码部分)。
  2. 主对话框显然使用 _DTE 对象来填充代码树。
  3. 当你点击树中的某个项目时,它会再次使用 _DTE 对象来获取代码项在编辑器中的位置,然后查找它前面的注释。如果找到,它会检查注释是否与配置的模板一致。如果一致,它会显示准确的注释,否则,它会显示“/*”和“*/”之间的内容。
  4. 模板对话框可以从主对话框调用,你可以在其中配置用于配置每个项目注释格式的模板。

已知问题

  1. 下拉列表不下拉 :)。你必须使用键盘来更改下拉列表选中的值。
  2. 根据风格反转注释尚未完美。
  3. 它绝不完美或安全,并且可能会搞乱你的代码。因此,请谨慎使用!

对其他语言的支持

这是一个 beta 版本,因此仅用于测试此类工具的需求。如果需求量很大,我可能会考虑为各种语言支持此工具。不过,目前这并不容易(由于一些设计上的决定)。

© . All rights reserved.