如何从命令行 C++ VSPackage 获取 DTE 对象
How to get DTE object from command-line C++ VSPackage
我有一个仅用于命令行的 C++ VSPackage。在我的 pkgdef 文件中设置以下自动加载注册表项时,不会 在从命令行 运行 宁 devenv 时强制加载 VSPackage。
[$RootKey$\AutoLoadPackages\{ADFC4E64-0397-11D1-9F4E-00A0C911004F}]
"{75726504-cacb-4781-b384-63815a289e0a}"=dword:00000000
@="UICONTEXT_NoSolution"
但是,如果我添加命令行参数,我可以让 VSPackage 按要求加载:
[$RootKey$\AppCommandLine]
"vsp"="{75726504-cacb-4781-b384-63815a289e0a}"
[$RootKey$\AppCommandLine\vsp]
"Arguments"="0"
"DemandLoad"=dword:00000001
"Package"="{75726504-cacb-4781-b384-63815a289e0a}"
"HelpString"="#102"
问题是,当我尝试在 OnAfterOpenSolution 事件中获取 DTE 对象时,它失败了。当我在 GUI 模式下 运行 devenv 时,相同的代码有效。
有没有更好的方法让我的命令行 vspackage 在启动时加载,或者有没有办法使用我上面使用的方法来获取 DTE 对象?
这是我用来获取 DTE 的代码:
CComPtr<EnvDTE::_DTE> SinkSolutionEvents::GetDTE(CString program)
{
CString rotEntry;
rotEntry.Format(L"!VisualStudio.DTE.14.0:%d", program, GetCurrentProcessId());
IRunningObjectTable *rot = NULL;
GetRunningObjectTable(0, &rot);
IEnumMoniker *enumMoniker;
rot->EnumRunning(&enumMoniker);
enumMoniker->Reset();
ULONG fetched = 0;
IMoniker *moniker = NULL;
while (enumMoniker->Next(1, &moniker, &fetched) == 0)
{
IBindCtx *bindCtx = NULL;
CreateBindCtx(0, &bindCtx);
LPOLESTR pwszName;
HRESULT hr = moniker->GetDisplayName(bindCtx, NULL, &pwszName);
CString displayName;
if (SUCCEEDED(hr)) {
displayName = pwszName;
CoTaskMemFree(pwszName);
if (displayName == rotEntry)
{
CComPtr<IUnknown> punk;
rot->GetObject(moniker, &punk);
CComPtr<EnvDTE::_DTE> dte;
dte = punk;
return dte;
}
}
}
return NULL;
}
我找到了问题的答案。您可以覆盖 IVsPackageImpl::SetSite() 并在指定的 IServiceProvider 上使用 QueryService 来访问 EnvDTE。
HRESULT STDMETHODCALLTYPE MyVSPackage::SetSite( __RPC__in_opt IServiceProvider *pSP)
{
CComPtr<EnvDTE::_DTE> m_pDTE = NULL;
HRESULT hr = pSP->QueryService(SID_SDTE, IID__DTE, (void **)&m_pDTE);
return IVsPackageImpl::SetSite(pSP);
}
我有一个仅用于命令行的 C++ VSPackage。在我的 pkgdef 文件中设置以下自动加载注册表项时,不会 在从命令行 运行 宁 devenv 时强制加载 VSPackage。
[$RootKey$\AutoLoadPackages\{ADFC4E64-0397-11D1-9F4E-00A0C911004F}]
"{75726504-cacb-4781-b384-63815a289e0a}"=dword:00000000
@="UICONTEXT_NoSolution"
但是,如果我添加命令行参数,我可以让 VSPackage 按要求加载:
[$RootKey$\AppCommandLine]
"vsp"="{75726504-cacb-4781-b384-63815a289e0a}"
[$RootKey$\AppCommandLine\vsp]
"Arguments"="0"
"DemandLoad"=dword:00000001
"Package"="{75726504-cacb-4781-b384-63815a289e0a}"
"HelpString"="#102"
问题是,当我尝试在 OnAfterOpenSolution 事件中获取 DTE 对象时,它失败了。当我在 GUI 模式下 运行 devenv 时,相同的代码有效。
有没有更好的方法让我的命令行 vspackage 在启动时加载,或者有没有办法使用我上面使用的方法来获取 DTE 对象?
这是我用来获取 DTE 的代码:
CComPtr<EnvDTE::_DTE> SinkSolutionEvents::GetDTE(CString program)
{
CString rotEntry;
rotEntry.Format(L"!VisualStudio.DTE.14.0:%d", program, GetCurrentProcessId());
IRunningObjectTable *rot = NULL;
GetRunningObjectTable(0, &rot);
IEnumMoniker *enumMoniker;
rot->EnumRunning(&enumMoniker);
enumMoniker->Reset();
ULONG fetched = 0;
IMoniker *moniker = NULL;
while (enumMoniker->Next(1, &moniker, &fetched) == 0)
{
IBindCtx *bindCtx = NULL;
CreateBindCtx(0, &bindCtx);
LPOLESTR pwszName;
HRESULT hr = moniker->GetDisplayName(bindCtx, NULL, &pwszName);
CString displayName;
if (SUCCEEDED(hr)) {
displayName = pwszName;
CoTaskMemFree(pwszName);
if (displayName == rotEntry)
{
CComPtr<IUnknown> punk;
rot->GetObject(moniker, &punk);
CComPtr<EnvDTE::_DTE> dte;
dte = punk;
return dte;
}
}
}
return NULL;
}
我找到了问题的答案。您可以覆盖 IVsPackageImpl::SetSite() 并在指定的 IServiceProvider 上使用 QueryService 来访问 EnvDTE。
HRESULT STDMETHODCALLTYPE MyVSPackage::SetSite( __RPC__in_opt IServiceProvider *pSP)
{
CComPtr<EnvDTE::_DTE> m_pDTE = NULL;
HRESULT hr = pSP->QueryService(SID_SDTE, IID__DTE, (void **)&m_pDTE);
return IVsPackageImpl::SetSite(pSP);
}