作者:朱金灿

来源:http://blog.csdn.net/clever101

继续编写VisualStudio插件。这次我编写的插件叫DevAssist(意思是开发助手)。在看了前面的文章之后你知道了一个VisualStudio插件一般是由两个工程组成的:功能dll和资源dll。首先我们先建一个功能dll——DevAssist,具体过程请参考第一篇:自己动手编写一个VS插件(一)。然后我们再建一个资源dll——DevAssistUI。

编译一下DevAssistUI工程,结果出错:

generalerror c1010070: Failed to load and parse the manifest

上网查了下,发现编译一个空工程会出现这个错误,于是把一个位图资源导入进去再编译就没有这个错误了。再编译DevAssistUI工程,还有错误:

1>------已启动生成: 项目: DevAssistUI, 配置: Debug Win32 ------

1>正在链接...

1>LINK: error LNK2001: 无法解析的外部符号 __DllMainCRTStartup@12

1>E:\PIE3_src\outdir/Debug/1033\DevAssistUI.dll: fatal error LNK1120: 1 个无法解析的外部命令

1>生成日志保存在“file://E:\PIE3_src\Intdir\Debug\DevAssistUI\BuildLog.htm”

1>DevAssistUI - 2 个错误,0 个警告

========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

在工程设置里把该工程改为无入口点即可,如下图: 

现在我们开始实现创建一个工具栏并把它显示出来。首先需要在AddIn.rgs中指定资源dll,具体是在HKCU段添加SatelliteDllName和SatelliteDllPath两个变量,具体如下:

HKCU

{

NoRemove 'SOFTWARE'

{

NoRemove 'Microsoft'

{

NoRemove 'VSA'

{

NoRemove '9.0'

{

NoRemove 'AddIns'

{

ForceRemove'DevAssist.Connect'

{

valLoadBehavior = d 1

valCommandLineSafe = d 0

valCommandPreload = d 0

valFriendlyName = s 'DevAssist -开发助手。'

valDescription = s 'DevAssist - 用于辅助开发。'

valAboutBoxDetails = s '有关DevAssist的详细信息,请参见DevAssist 站点\r\nhttp://blog.csdn.net/clever101\r\n关于客户支持,请致电: 1-800-xxx-xxxx。\r\n版权所有(C) 2013 DreamStudio Inc.'

valAboutBoxIcon = s '%MODULE%,1'

val SatelliteDllName = s 'DevAssistUI.dll'

valSatelliteDllPath = s '%MODULE_PATH%'

}

}

}

}

}

}

NoRemove 'SOFTWARE'

{

NoRemove 'Microsoft'

{

NoRemove 'VisualStudio'

{

NoRemove '9.0'

{

NoRemove 'AddIns'

{

ForceRemove'DevAssist.Connect'

{

valLoadBehavior = d 1

valCommandLineSafe = d 0

valCommandPreload = d 0

valFriendlyName = s 'DevAssist -开发助手。'

valDescription = s 'DevAssist - 用于辅助开发。'

valAboutBoxDetails = s '有关DevAssist的详细信息,请参见DevAssist 站点\r\nhttp://blog.csdn.net/clever101\r\n关于客户支持,请致电: 1-800-xxx-xxxx。\r\n版权所有(C) 2013 DreamStudio Inc.'

valAboutBoxIcon = s '%MODULE%,1'

val SatelliteDllName = s 'DevAssistUI.dll'

valSatelliteDllPath = s '%MODULE_PATH%'

}

}

}

}

}

}

}

然后开始添加创建工具栏的代码:

// 按钮信息结构体
struct stCommandInfo
{
LPCTSTR m_name; // 按钮名字
LPCTSTR m_strTip; // 按钮提示
BOOL m_hasIcon; // 是否有图标
UINT m_bitmapID; // 对应的位图ID
UINT m_cmdID; // 命令ID
}; HRESULT CConnect::FindToolbarByName( _CommandBars* pCommandBars, LPCWSTR szToolbarName, CommandBar** pOut )
{
if(pCommandBars == NULL || pOut == NULL)
return E_FAIL; CComVariant varToolbarName(szToolbarName);
int nToolBars = 0;
HRESULT hr = pCommandBars->get_Count(&nToolBars);
for(int idx = 1; idx <= nToolBars; idx++)
{
CComPtr<CommandBar> pCommandBar;
hr = pCommandBars->get_Item(CComVariant(idx), &pCommandBar);
if(FAILED(hr))
continue; BSTR bsName = NULL;
if(pCommandBar != NULL)
pCommandBar->get_Name(&bsName);
if(CComVariant(bsName) == varToolbarName)
{
*pOut = pCommandBar;
(*pOut)->AddRef();
break;
}
SysFreeString(bsName);
hr = E_FAIL;
}
return hr;
} // When run, the Add-in wizard prepared the registry for the Add-in.
// At a later time, if the Add-in becomes unavailable for reasons such as:
// 1) You moved this project to a computer other than which is was originally created on.
// 2) You chose 'Yes' when presented with a message asking if you wish to remove the Add-in.
// 3) Registry corruption.
// you will need to re-register the Add-in by building the MyAddin21Setup project
// by right clicking the project in the Solution Explorer, then choosing install.
void UnregisterCommand(CComPtr<EnvDTE::Commands>& pCommands, LPCWSTR commandName)
{
CComPtr<EnvDTE::Command> pCommand; //
// COMMAND_NAME_FULL must be the module name plus the COMMAND_NAME.
// For this case, the module name can be found at the CvsInVC7.rgs and
// CvsInVC8.rgs files.
//
WCHAR item[256];
wcscpy_s(item, 256, MODULE_NAME);
wcscat_s(item, 256, commandName); HRESULT hr = pCommands->Item(CComVariant(item), -1, &pCommand); if (SUCCEEDED(hr))
{
pCommand->Delete();
}
} // CConnect
STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication, ext_ConnectMode ConnectMode, IDispatch *pAddInInst, SAFEARRAY ** /*自定义*/ )
{
HRESULT hr = S_OK;
pApplication->QueryInterface(__uuidof(DTE2), (LPVOID*)&m_pDTE);
pAddInInst->QueryInterface(__uuidof(EnvDTE::AddIn), (LPVOID*)&m_pAddInInstance); if(ConnectMode == AddInDesignerObjects::ext_cm_CommandLine)
{
::MessageBox(GetActiveWindow(),_T("CConnect::OnConnection, CommandLine Mode"),_T("DevAssist"),MB_OK);
return S_OK;
} CComPtr<IDispatch> pDisp;
CComPtr<EnvDTE::Commands> pCommands;
CComPtr<Commands2> pCommands2;
CComQIPtr<_CommandBars> pCommandBars;
CComPtr<CommandBar> pCommandBar; IfFailGoCheck(m_pDTE->get_Commands(&pCommands), pCommands);
pCommands2 = pCommands; // Get the set of command bars for the application.
IfFailGoCheck(m_pDTE->get_CommandBars(&pDisp), pDisp);
pCommandBars = pDisp; // See if the toolbar has been created.
BOOL bRecreateToolbar = FALSE;
hr = FindToolbarByName(pCommandBars, TOOLBAR_NAME, &pCommandBar);
if (SUCCEEDED(hr))
{
CComPtr<CommandBarControls> pCommandBarControls;
pCommandBar->get_Controls(&pCommandBarControls);
int count = 0;
pCommandBarControls->get_Count(&count); if (count == 0)
{
bRecreateToolbar = true;
}
pCommandBar = NULL;
}
else
{
bRecreateToolbar = TRUE;
} if(ConnectMode == 5 || bRecreateToolbar) //5 == ext_cm_UISetup
{
// See if the CodeLib toolbar has been created.
hr = FindToolbarByName(pCommandBars, TOOLBAR_NAME, &pCommandBar);
if(FAILED(hr))
{
pDisp = NULL;
// The toolbar hasn't been created yet. Add it.
hr = pCommands->AddCommandBar(CComBSTR(TOOLBAR_NAME),
EnvDTE::vsCommandBarTypeToolbar,
NULL,
1,
&pDisp); // Yes, this code is unnecessary, but it serves to prove that
// the command bar creation actually worked.
hr = FindToolbarByName(pCommandBars, TOOLBAR_NAME, &pCommandBar);
} int curToolbarPosition = 1;
int nMaxCommand = sizeof(s_commandList)/sizeof(s_commandList[0]);
for(int curCommand = 0; curCommand < nMaxCommand; ++curCommand)
{
CComPtr<EnvDTE::Command> pCreatedCommand;
const stCommandInfo* commandInfo = &s_commandList[curCommand];
UnregisterCommand(pCommands, commandInfo->m_name); HRESULT hr2 = pCommands2->AddNamedCommand2(m_pAddInInstance,
CComBSTR(commandInfo->m_name),
CComBSTR(commandInfo->m_name),
CComBSTR(commandInfo->m_strTip),
VARIANT_FALSE,
CComVariant(commandInfo->m_bitmapID),
NULL,
(EnvDTE::vsCommandStatusSupported + EnvDTE::vsCommandStatusEnabled),
EnvDTE80::vsCommandStylePict,
EnvDTE80::vsCommandControlTypeButton,
&pCreatedCommand);
if(SUCCEEDED(hr2) && (pCreatedCommand) && commandInfo->m_hasIcon)
{
//Add the control:
pDisp = NULL;
IfFailGoCheck(pCreatedCommand->AddControl(pCommandBar, curToolbarPosition, &pDisp),pDisp);
curToolbarPosition++;
}
}
} Error: return hr;
}

编译运行工程,工具栏出来了,但是按钮都是灰的。到网上找了个插件工程参考,发现需要修改CConnect类的基类,具体如下:

//class ATL_NO_VTABLE CConnect :
// public CComObjectRootEx<CComSingleThreadModel>,
// public CComCoClass<CConnect, &CLSID_Connect>,
// public IDispatchImpl<_IDTExtensibility2, &IID__IDTExtensibility2, &LIBID_AddInDesignerObjects, 1, 0> class ATL_NO_VTABLE CConnect :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CConnect, &CLSID_Connect>,
public IDispatchImpl<EnvDTE::IDTCommandTarget, &__uuidof(EnvDTE::IDTCommandTarget), &EnvDTE::LIBID_EnvDTE, 8, 0>,
public IDispatchImpl<_IDTExtensibility2, &IID__IDTExtensibility2, &LIBID_AddInDesignerObjects, 1, 0>

修改后工具栏按钮都亮了。

遇到的一些问题及解决办法:

a. error+C2872:+“ULONG_PTR”:+不明确的符号

1>Connect.cpp

1>d:\programfiles\microsoft visual studio 9.0\vc\atlmfc\include\cstringt.h(2253) : errorC2872: “ULONG_PTR”: 不明确的符号

1>        可能是“c:\program files\microsoftsdks\windows\v6.0a\include\basetsd.h(139) : __w64 unsigned long ULONG_PTR”

1>        或      “e:\pie3_src\intdir\debug\devassist\dte80a.tlh(464) :EnvDTE::ULONG_PTR”

1>        d:\program files\microsoft visual studio9.0\vc\atlmfc\include\cstringt.h(2250): 编译类 模板 成员函数“boolATL::CStringT<BaseType,StringTraits>::CheckImplicitLoad(const void *)”时

1>        with

1>        [

1>            BaseType=wchar_t,

1>           StringTraits=ATL::StrTraitATL<wchar_t,ATL::ChTraitsCRT<wchar_t>>

1>        ]

1>        d:\program files\microsoft visualstudio 9.0\vc\atlmfc\include\cstringt.h(2686): 参见对正在编译的类 模板 实例化“ATL::CStringT<BaseType,StringTraits>”的引用

1>        with

1>        [

1>            BaseType=wchar_t,

1>            StringTraits=ATL::StrTraitATL<wchar_t,ATL::ChTraitsCRT<wchar_t>>

1>        ]

1>        d:\program files\microsoft visualstudio 9.0\vc\atlmfc\include\atlstr.h(1139): 参见对正在编译的类 模板 实例化“ATL::CStringElementTraits<T>”的引用

1>        with

1>        [

1>            T=ATL::CAtlStringW

1>        ]

1>正在生成代码...

原因分析:对于ULONG_PTR类型,VS2008的类型库和windowsSDK出现重定义。解决办法是:在导入VS2008的类型库时重命名ULONG_PTR类型(LONG_PTR也有类似问题),如下:

	#import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids rename("ULONG_PTR","ULONG_PTRDTE") rename("LONG_PTR","LONG_PTRDTE")

b. 链接器工具错误 LINK : fatal error LNK1168: 无法打开..\outdir\Debug\DevAssist.dll 进行写入。

解决办法:

在VS里的外接程序管理器里把启动去掉,如下图:

然后编译如果还要错误就重启VS,如果还有错误就打开任务管理器,杀死所有explorer.exe,然后新建一个explorer进程。

c.修改工具栏按钮的位图资源或提示,但是工具栏总是不更新。

在工具栏的自定义对话框中将工具栏删掉,如下:

然后重启VS再启动插件即可看到工具栏的更新状态。

自己动手编写一个VS插件(五)的更多相关文章

  1. 自己动手编写一个Mybatis插件:Mybatis脱敏插件

    1. 前言 在日常开发中,身份证号.手机号.卡号.客户号等个人信息都需要进行数据脱敏.否则容易造成个人隐私泄露,客户资料泄露,给不法分子可乘之机.但是数据脱敏不是把敏感信息隐藏起来,而是看起来像真的一 ...

  2. 自己动手编写一个VS插件(八)

    作者:朱金灿 来源:http://blog.csdn.net/clever101 利用业余时间继续开发一个VS插件.我要开发的插件是一个代码库插件,主要是用于积累我平时要使用的代码.在之前我已经实现了 ...

  3. 自己动手编写一个VS插件(七)

    作者:朱金灿 来源:http://blog.csdn.net/clever101 继续开发VS插件.今天在添加ATL控件时出现一个"未能返回新代码元素"的错误,如下图: 解决办法是 ...

  4. 自己动手编写一个VS插件(六)

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在上篇中我们已经实现了创建和显示一个工具栏出来,它的效果图是这样的: 现在我们实现一些简单功能,具体就是单击按钮弹出一 ...

  5. 如何自己编写一个easyui插件续

    接着如何自己编写一个easyui插件继续分享一下如何从上一节写的“hello”插件继承出一个“hello2”. 参考了combobox的源码中继承combo,当然我这个简单很多了.都是根据自己的理解来 ...

  6. 如何编写一个gulp插件

    很久以前,我们在"细说gulp"随笔中,以压缩JavaScript为例,详细地讲解了如何利用gulp来完成前端自动化. 再来短暂回顾下,当时除了借助gulp之外,我们还利用了第三方 ...

  7. 从零开始编写一个vue插件

    title: 从零开始编写一个vue插件 toc: true date: 2018-12-17 10:54:29 categories: Web tags: vue mathjax 写毕设的时候需要一 ...

  8. 如何自己编写一个easyui插件

    本文介绍如何通过参考1.4.2版本的progressbar的源码自己编写一个HelloWorld级别的easyui插件,以及如何拓展插件的功能. 有利于我们理解easyui插件的实现,以及了解如何对e ...

  9. 自己动手编写Maven的插件

    Maven的插件机制是完全依赖Maven的生命周期的,因此理解生命周期至关重要.本文参考官方文档后使用archetype创建,手动创建太麻烦. 创建创建项目 选择maven-archetype-moj ...

随机推荐

  1. Android, IOS 史上最强多语言国际化,不仅第一次会尾随系统,并且会保存用户的语言设置

    劲爆消息,我提供源代码了.你能够先看完再下载.也能够先下载再看完, android源代码地址: https://github.com/hebiao6446/------Bluetooth-Androi ...

  2. JAVA 如何将String进行大小写转换

    private String convertString(String str, Boolean beginUp){ char[] ch = str.toCharArray(); StringBuff ...

  3. thinkphp模型事件(钩子函数:模型中在增删改等操作前后自动执行的事件)

    thinkphp模型事件(钩子函数:模型中在增删改等操作前后自动执行的事件) 一.总结 1.通过模型事件(钩子函数),可以在插入更新删除等前后执行一些特定的功能 2.模型事件是写在模型里面的,控制器中 ...

  4. js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数)

    js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数) 一.总结 1.JSON解析:JSON.parse(myJS ...

  5. [TypeStyle] Load raw CSS in TypeStyle

    TypeStyle tries to be an all in one CSS in JS management solution so you can always fall back to raw ...

  6. 关于stm32的串口电压问题

    在同一块板子的另一个 2号串口,因为没有使用所以就没有配置,,,所以导致这三个引脚都为0; 上面的串口接口封装是围墙座: 注意:倘若要连线,那时候要记得交叉,当然这也要看各自的设计才行

  7. 【15.07%】【codeforces 625A】Guest From the Past

    time limit per test 1 second memory limit per test 256 megabytes input standard input output standar ...

  8. php javascript的ajax

    先说基础一点的get类型的ajax function loadXMLDoc() { var xmlhttp;//首先判断浏览器是否支持xmlhttprequest,因为ie56不是这个对象,是acti ...

  9. 无线物联网中CoAP协议的研究与实现【转】

    无线物联网中CoAP协议的研究与实现 时间:2013-04-09 来源:电子科技 作者:汤春明,张 荧,吴宇平 关键字:CoAP   无线   物联网   协议 摘要:由于物联网中的很多设备都是资源受 ...

  10. DisplayPageBoundaries 打开word后自动将页面间空白隐藏 (auto+定时器)

    每次打开文档都要鼠标点击页面间空白处,将其隐藏 尝试过在 AutoOpen, AutoExec等宏中添加 ActiveWindow.View.DisplayPageBoundaries = False ...