作者:朱金灿

来源: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. OVS中对于用户层和datapath层的多个通道利用epoll进行控制

    这里先临时记录下代码流程,有待完好. static int construct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = o ...

  2. UVA 11859 - Division Game

    看题传送门 题目大意 有一个n * m的矩阵,每个元素均为2~10000之间的正整数,两个游戏者轮流操作.每次可选一行中的1个或者多个大于1的整数把它们中的每个数都变成它的某个真因子,比如12可以变成 ...

  3. Oracle以系统管理员的方式登录失败

    解决方法: 因为SYS是在数据库之外的超级管理员,所以我们在登录的时候输入sys后在输入命令:password as sysdba 就可以!例如:输入口令: m1234 as sysdba 参考文章 ...

  4. RISC-V工具链环境(基于Debian/Linux操作系统)

    RISC-V工具链环境(基于Debian/Linux操作系统) 提要 Debian/Linux虚拟机导入 启动虚拟机 SiFive/Nuclei SDK运行指南 Debian/Linux虚拟机存储位置 ...

  5. iTestin云测工具

    软件概述 iTestin是免费服务移动App开发者的真机自动化云测试客户端工具.基于真实的智能终端设备录制一个测试脚本然后运行,并输出运行结果.覆盖Android和iOS两大设备平台,支持Pad/Ph ...

  6. VC6.0 MFC中WebBrowser控件禁止新窗口弹出的解决办法

    http://blog.csdn.net/gnorth/article/details/7258293 分类: WebBrowser MFC 禁止新窗口2012-02-14 15:25 1787人阅读 ...

  7. JS和PHP和JAVA的正则表达式的区别(java没有分解符,java中的转义字符是\\)

    JS和PHP和JAVA的正则表达式的区别(java没有分解符,java中的转义字符是\\) 一.总结 js正则:var patrn=/^[0-9]{1,20}$/; php正则:$pattern='/ ...

  8. Strut2与Hibernate的一个web分页功能

    代码没有进行过多的封装,可能看起来有点action代码部分,hibernate在这里只起到了一个查询记录集的作用. import java.util.ArrayList; import java.ut ...

  9. 摘录-解压版mysql配置(版本5.7)

    1.下载解压2.创建my.ini文件基础配置:(注意编码必须为ANSI)#代码开始[Client]# 设置mysql客户端默认字符集default-character-set=utf8[mysqld] ...

  10. 基于 Android NDK 的学习之旅-----JNI LOG 打印

    程序都是调出来的. 下面我介绍下JNI层的log打印方法的使用,类似与Android sdk提供的log 1.Android 应用层 MainActivity.java 主要功能代码 a)       ...