IVsIntellisenseless
geek, programming, rant, visual studio December 3rd, 2008Just dropping in a quick post about what might be coming next… Still hunting down the holy grail of intellisense.
And as far as the Visual Studio SDK goes? You’ll never see a more wretched hive of undocumented interfaces and secret behaviors. I’ve down-shifted all the way to ATL C++ on this one to see eye-to-eye with the components the language service is integrating with, and it’s still a massive undertaking of black-box reverse engineering.
It’s not the VS SDK’s fault really. It’s the nature of the beast when you’re talking COM and OLE integration style. Lots of reasons for that.
1) COM objects are based on interfaces, which have a wonderful declarative idl and even a machine readable tlb for late binding, but the coclasses themselves have zero support for declaration and reflection. Interfaces on a com object are not declared in the tlb, or the registery, and the object has no obligation (or standard support for) enumerating them.
In fact when if comes right down to it when I’ve worked with particularly obtuse com suites I’ve used a “dump interfaces” trick where you enumerate all of the keys in HKCR/Interfaces (thousands?), QueryInterface on an object in question, and debug trace the name of all the interfaces which are returned. Needing to do that seems even more insane after becoming so used to the visual studio debugger and Reflector.
Which brings up the other reason flailing is the nature of COM is 2) unmanaged code can’t be understood by static examination. Reflector’s wonderful in that way – Asp.Net web internals, Wcf architecture, relationships between the many forms stream, textreaders, encodings, etc. All of it laid out for you to peruse and it’s more accurate than any documentation could possibly hope to be. But when you’re in C++ or MC++ COM world again it’s black box again, and you end up trying to figure out how to tango with these objects without any clear understanding of what they’ll do and why.
So what do you do? Speculate and experiment. Page through the IDL looking for potentially interesting methods, and try to figure out how to get yours hands on the interface. Is it a service you can ask for? What IServiceProvider site and service GUID does it come from? Maybe it’s an interface you didn’t know is on one of the objects you already have?
Really makes you appreciate csharp.
Here’s an example where you can see some of the “easter egg” interfaces and some flailing in the dark trying to figure out how to bring in IVsIntellisense.
HRESULT Init(IServiceProvider* site, IVsTextLines *primaryBuffer)
{
_site = site;
_primaryBuffer = primaryBuffer;
HRESULT hr = S_OK;
if (SUCCEEDED(hr))
hr = primaryBuffer->QueryInterface(&_key);
// Get the moniker for the text buffer
CComPtr<IVsUserData> userData;
if (SUCCEEDED(hr))
hr = primaryBuffer->QueryInterface(&userData);
CComVariant moniker;
if (SUCCEEDED(hr))
hr = userData->GetData(__uuidof(IVsUserData), &moniker);
if (SUCCEEDED(hr))
hr = moniker.ChangeType(VT_BSTR);
// Locate the project hierarchy and buffer's itemid
CComPtr<IVsRunningDocumentTable> docTable;
if (SUCCEEDED(hr))
hr = _site->QueryService(__uuidof(IVsRunningDocumentTable), &docTable);
if (SUCCEEDED(hr))
hr = docTable->FindAndLockDocument(0, V_BSTR(&moniker), &_hierarchy, &_itemid, NULL, NULL);
// initialize coordinator, primary buffer, and secondary buffer
CComPtr<ILocalRegistry> reg;
if (SUCCEEDED(hr))
hr = _site->QueryService(__uuidof(ILocalRegistry), ®);
if (SUCCEEDED(hr))
hr = reg->CreateInstance(__uuidof(VsTextBufferCoordinator), NULL, __uuidof(IVsTextBufferCoordinator), CLSCTX_INPROC_SERVER, (void**)&_bufferCoordinator);
if (SUCCEEDED(hr))
hr = reg->CreateInstance(__uuidof(VsTextBuffer), NULL, __uuidof(IVsTextLines), CLSCTX_INPROC_SERVER, (void**)&_secondaryBuffer);
if (SUCCEEDED(hr))
SiteObject(_secondaryBuffer);
if (SUCCEEDED(hr))
SiteObject(_bufferCoordinator);
if (SUCCEEDED(hr))
hr = _secondaryBuffer->SetLanguageServiceID(__uuidof(CSharp));
if (SUCCEEDED(hr))
hr = _bufferCoordinator->SetBuffers(_primaryBuffer, _secondaryBuffer);
// get csharp language factory and create contained language object
//
//CComPtr<IVsIntellisenseProjectManager> projectManager;
//if (SUCCEEDED(hr))
// hr = _site->QueryService(__uuidof(SVsIntellisenseProjectManager), &projectManager);
CComPtr<IVsIntellisenseProject> intellisenseProvider;
if (SUCCEEDED(hr))
hr = reg->CreateInstance(__uuidof(CSharpIntellisenseProvider), NULL, __uuidof(intellisenseProvider), CLSCTX_INPROC_SERVER, (void**)&intellisenseProvider);
CComPtr<IVsContainedLanguageFactory> containedFactory;
if (SUCCEEDED(hr))
hr = intellisenseProvider->GetContainedLanguageFactory(&containedFactory);
//if (SUCCEEDED(hr))
// hr = projectManager->GetContainedLanguageFactory(CComBSTR(L"CSharp"), &containedFactory);
//if (SUCCEEDED(hr))
// hr = _site->QueryService(__uuidof(CSharp), &containedFactory);
CComPtr<IVsContainedLanguage> containedLang;
if (SUCCEEDED(hr))
hr = containedFactory->GetLanguage(_hierarchy, _itemid, _bufferCoordinator, &_containedLanguage);
CComPtr<IVsContainedLanguageHost> host;
if (SUCCEEDED(hr))
hr = ContainedLanguageHost::Create(this, &host);
if (SUCCEEDED(hr))
hr = _containedLanguage->SetHost(host);
return hr;
}
January 18th, 2012 at 11:45 pm
Thanks for the blog post.Much thanks again. Will read on…