• 自己动手编写一个VS插件(五)


    作者:朱金灿

    来源: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_srcoutdir/Debug/1033DevAssistUI.dll: fatal error LNK1120: 1 个无法解析的外部命令

    1>生成日志保存在“file://E:PIE3_srcIntdirDebugDevAssistUIBuildLog.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 站点 http://blog.csdn.net/clever101 关于客户支持,请致电: 1-800-xxx-xxxx。 版权所有(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 站点 http://blog.csdn.net/clever101 关于客户支持,请致电: 1-800-xxx-xxxx。 版权所有(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:programfilesmicrosoft visual studio 9.0vcatlmfcincludecstringt.h(2253) : errorC2872: “ULONG_PTR”: 不明确的符号

    1>        可能是“c:program filesmicrosoftsdkswindowsv6.0aincludeasetsd.h(139) : __w64 unsigned long ULONG_PTR”

    1>        或      “e:pie3_srcintdirdebugdevassistdte80a.tlh(464) :EnvDTE::ULONG_PTR”

    1>        d:program filesmicrosoft visual studio9.0vcatlmfcincludecstringt.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 filesmicrosoft visualstudio 9.0vcatlmfcincludecstringt.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 filesmicrosoft visualstudio 9.0vcatlmfcincludeatlstr.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: 无法打开..outdirDebugDevAssist.dll 进行写入。

    解决办法:

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

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

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

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

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






  • 相关阅读:
    第43周四
    第43周三
    第43周二
    第43周一
    无聊时做什么2
    2014第42周日当无聊时做什么
    第42周六
    第42周五
    Web版的各种聊天工具
    cocos2d_x_06_游戏_一个都不能死
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6470457.html
Copyright © 2020-2023  润新知