分享
 
 
 

Comment/Uncomment selected code in Visual C++

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

给VC增加注释/反注释的功能

——杨科 注:本文可随意转载, 但请保留我的署名(CopyLeft)

用过VB,PowerBuilder的朋友一定知道在它们的工具条上有两个按钮,用来完成对选中的代码进行注释或反注释,而在VC中如果要注释一段选中的代码,除了在要注释的代码中添加/* 和 */外,就是对每一行都使用单行注释//。其实在VC中提供了编写插件的功能,VC将开发环境中的各种对象以COM接口的形式暴露出来,而且提供的插件应用程序向导可以完成大部分的框架代码,所以我们只需要添加我们想要的功能就可以了。所以我们可以利用这些COM接口来对VC的开发环境进行操作,这样我们就可以完成对选中的代码的注释和反注释功能。

下面具体描述以下开发这个插件的过程:

首先创建一个新工程,类型选择DevStudio Add-in Widzard,填写工程名称后,单击OK继续。在接下来的页面中选中Provides a toolbar,这会使我们创建的插件具有一个工具条,不需要选中Responds to Developer Studio events,因为我们不需要对开发环境中的事件作出响应,在上面的两个文本框中,可以随便输入一些你对这个插件的描述及功能介绍,单击Finish完成向导。

对了,首先声明以下,在VC中每一个Add-in都是一个COM组件,在Add-in的向导中生成的程序是用MFC和ATL共同实现的,所以在生成的原代码中你会看到两个分别叫做theApp和_Module的全局变量。另外向导为我们生成了一个成为ICommands的接口,我们必须在这个接口添加适当的方法来完成我们需要的功能。

让我们看一下AppWidzard为我们生成了哪些类,首先我们会看到一个CCommands的类,而且其中实现了我们在上面提到的ICommands接口。一个称为CDSAddIn的类,这个类中实现两个方法OnConnection(), OnDisconnection(), 这两个方法我们不会直接在程序中调用,而是由VC集成环境调用的,当VC启动时它会首先查询关于Add-ins的注册信息,然后调用相应组件的OnConnection()方法,所以在这个方法中我们应该把我们要实现的命令添加到VC的继承环境中,当这个插件被卸载或VC关闭时,VC会调用OnDisconnection()方法,在这里我们应该释放我们在OnConnection()中分配的资源。

剩下的就是应用程序类了,另外还有一些必须的全局函数DllGetClassObject, DllCanUnloadNow, DllRegisterServer和DllUnregisterServer是COM组件的几个通用实现,可以查阅关于COM的书籍来了解这些知识。

AppWidzard生成的代码中在ICommands接口中实现了一个与应用程序同名的方法,通常这个方法名称并不是我们需要的,所以下面就开始来改写这个方法,把它改成CommentCode。双击ICommands接口中的与你应用程序同名的方法,VC会打开一个与你的应用程序同名的一个扩展名是odl的文件,找到一行叫做HRESULT YourAppCommandMethod()并把YourAppCommandMethod改成CommentCode(),然后打开Commands.cpp和Commands.h把其中的YourAppCommandMethod都改成CommentCode()。

现在我们修改DSAddIn.cpp,找到OnConnection方法,直接找到下面这断代码:

LPCTSTR szCommand = _T("YourAppCommand");

VARIANT_BOOL bRet;

CString strCmdString;

strCmdString.LoadString(IDS_CMD_STRING);

strCmdString = szCommand + strCmdString;

CComBSTR bszCmdString(strCmdString);

CComBSTR bszMethod(_T("YourAppCommandMethod"));

CComBSTR bszCmdName(szCommand);

VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_dwCookie, &bRet));

并把它改成下面的样子:

LPCTSTR szCommand = _T("CommentCode");

VARIANT_BOOL bRet;

CString strCmdString;

strCmdString.LoadString(IDS_CMD_STRING);

strCmdString = szCommand + strCmdString;

CComBSTR bszCmdString(strCmdString);

CComBSTR bszMethod(_T("CommentCode"));

CComBSTR bszCmdName(szCommand);

VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_dwCookie, &bRet));

由于上面的代码涉及到了字符串资源,所以我们再来看看上面提到的IDS_CMD_STRING的内容,打开这个字符串资源改成下面的形式:

\nComment Code\nComment the selected code\nComment the selected code

现在我们可以编译这个应用程序,应该不会出现错误,单击运行会弹出一个对话框要你指定一个宿主应用程序来调用这个DLL, 因为我们是为VC开发插件应用,所以找到你VC的可执行文件目录并选中msdev.exe。当另一个VC启动后,选择Tools¦Customize...,丛属性页种选择Add-ins and Macro Files标签,单击Browse按钮,选择你刚生成的DLL文件,这时旁边的列表框,会出现你编写的插件的名字,单击Close关闭对话框,这是在工具条的下面会出现一个小工具条,将鼠标移动工具条的按钮上面,会出现Comment the selected code的代码提示, 单击按钮会弹出一个对话框,这是AppWidzard对这个命令的缺省实现。

下面我们该实现我们的具体功能的代码了。由于VC把它集成环境中对象通过COM接口暴露出来,所以我们就利用这些对象来实现注释代码的功能。这里有两个问题:1、我们怎么能知道VC中当前打开的文档是否是源代码而不是图形或对话框,2、我们怎么才能知道当前源代码文档中的被选择的文本。下面我通过实际的代码来解释这两个问题。

1、查找当前打开的源代码编辑其中的文档并查找当前选中的文本。这里我写了一个函数因为在实现反注释的时候我们还需要完成相同的功能。请参阅代码中的注释(注意相应接口指针的释放):

HRESULT CCommands::GetTextSelection(IApplication *pApplication, ITextSelection** pTextSelection)

{

IDispatch* pDispatch;

//通过有集成环境传进来的应用程序对象查找当前处于活动状态的文档对象,注意返回的是一个IDispatch接口指针。

HRESULT hr = pApplication->get_ActiveDocument(&pDispatch);

//此处应注意即使get_ActiveDocument()函数成功返回pDispatch指针仍有可能为空(即VC中没有文档打开),所以此处要判 //断pDispatch是否为空

if (SUCCEEDED(hr) && pDispatch != NULL)

{

//取得IGenericDocument接口的指针,我们可以通过该接口指针来查询当前的活动文档是否是文本文档。

IGenericDocument* pDocument;

hr = pDispatch->QueryInterface(IID_IGenericDocument, (void**)&pDocument);

if (FAILED(hr))

{

pDispatch->Release();

return E_NOINTERFACE;

}

pDispatch->Release();

//在此处查询ITextDocument接口,如果能成功返回,就说明当前文档是文本文档。

ITextDocument* pTextDocument;

hr = pDocument->QueryInterface(IID_ITextDocument, (void**)&pTextDocument);

if (FAILED(hr))

{

pDocument->Release();

return E_NOINTERFACE;

}

pDocument->Release();

//现在我们有了当前的活动的文本文档,我们就可以通过get_Selection函数来获得当前选中的文本,这里要求的还是 //一个IDispatch接口的指针

hr = pTextDocument->get_Selection(&pDispatch);

if (FAILED(hr))

{

pTextDocument->Release();

return E_NOINTERFACE;

}

pTextDocument->Release();

//通过QueryInterface()方法取回ITextSelection的接口指针。

hr = pDispatch->QueryInterface(IID_ITextSelection, (void**)pTextSelection);

if (FAILED(hr))

{

pDispatch->Release();

return E_NOINTERFACE;

}

pDispatch->Release();

}

else

return E_NOINTERFACE;

return S_OK;

}

上面的方法已经解决了这两个问题,接下来的任务就是实现注释被选中的代码。请参阅下面的代码(省略了错误处理的部分代码)

HRESULT CCommands::CommentSelectedCode(ITextSelection *pTextSelection)

{

long lTopLine = -1;

long lBottomLine = -1;

long lCurLine;

long lCurColumn;

//取得被选中文本的最上面一行的行号

HRESULT hr = pTextSelection->get_TopLine(&lTopLine);

//取得被选中文本的最下面一行的行号

hr = pTextSelection->get_BottomLine(&lBottomLine);

long iLine;

CString s;

_variant_t v((long)dsMove);

//循环,针对选中的每一行,在前面加上VC的单行注释//

for(iLine = lTopLine; iLine <= lBottomLine; iLine++)

{

hr = pTextSelection->MoveTo(iLine, 1, v);

if (SUCCEEDED(hr))

{

pTextSelection->SelectLine();

BSTR bstrLineText;

hr = pTextSelection->get_Text(&bstrLineText);

if (SUCCEEDED(hr))

{

s = bstrLineText;

s = _T("//") + s;

pTextSelection->put_Text(s.AllocSysString());

}

}

}

return S_OK;

}

上面的代码就完成了对选中的代码的注释问题,下面添加对选中的代码的反注释功能,其中的代码大体相同,这里主要讲解如何间命令添加到VC的开发环境中去。

在ClassView中右键单击ICommands接口,选择Add method...,添加UncommentCode方法,参照上面的代码实现相应的功能。

打开CDSAddIn类的OnConnection方法,在添加CommentCode方法的下面添加下面的代码:

szCommand = _T("UncommentCode");

strCmdString.LoadString(IDS_CMD_UNCOMMENT);

strCmdString = szCommand + strCmdString;

bszCmdString = strCmdString;

bszMethod = _T("UncommentCode");

CComBSTR bszCmdUncommentName = szCommand;

VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 1, m_dwCookie, &bRet));

然后在OnConnection()方法的下面的代码中

if (bFirstTime == VARIANT_TRUE)

{

VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));

}

添加一行,如下面的代码

if (bFirstTime == VARIANT_TRUE)

{

VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));

VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdUncommentName, m_dwCookie));

}

在上面的pApplication->AddCommand()调用中,第三个参数1,指定了该命令所对应的图像在位图资源中的位置。

适当的修正工程的资源,完成的应用程序应该可以完成VB,PowerBuilder中的代码的注释和反注释功能。

另外,由于VC中的插件是通过COM技术实现的,所以能支持COM规范的语言都可以用来编写VC插件。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有