自己動手編寫一個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_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再啓動插件即可看到工具欄的更新狀態。




發佈了11 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章