11

I have a managed syntax highlighter using the new VS extensibility API's and it gives me an ITextBuffer, which is great.

In another part of my extension I am getting a DTE object and attaching to the active window changed event, which gives me an EnvDTE.Window object.

var dte = (EnvDTE.DTE)this.GetService(typeof(EnvDTE.DTE));
dte.Events.WindowEvents.WindowActivated += WindowEvents_WindowActivated;
// ...

private void WindowEvents_WindowActivated(EnvDTE.Window GotFocus, EnvDTE.Window LostFocus)
{
  // ???
  // Profit
}

I would like to get the ITextBuffer out of Window in this method. Can anyone tell me a straight forward way to do that?

justin.m.chase
  • 13,061
  • 8
  • 52
  • 100

1 Answers1

13

The solution I used was to get the Windows path then use it in conjuction with IVsEditorAdaptersFactoryService and VsShellUtilities.

var openWindowPath = Path.Combine(window.Document.Path, window.Document.Name);
var buffer = GetBufferAt(openWindowPath);

and

internal ITextBuffer GetBufferAt(string filePath)
{
  var componentModel = (IComponentModel)GetService(typeof(SComponentModel));
  var editorAdapterFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>();
  var serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(MetaSharpPackage.OleServiceProvider);

  IVsUIHierarchy uiHierarchy;
  uint itemID;
  IVsWindowFrame windowFrame;
  if (VsShellUtilities.IsDocumentOpen(
    serviceProvider,
    filePath,
    Guid.Empty,
    out uiHierarchy,
    out itemID,
    out windowFrame))
  {
    IVsTextView view = VsShellUtilities.GetTextView(windowFrame);
    IVsTextLines lines;
    if (view.GetBuffer(out lines) == 0)
    {
      var buffer = lines as IVsTextBuffer;
      if (buffer != null)
        return editorAdapterFactoryService.GetDataBuffer(buffer);
    }
  }

  return null;
}
justin.m.chase
  • 13,061
  • 8
  • 52
  • 100
  • 1
    +1, but note that `IsDocumentOpen()` sometimes returns false when the document is still in the process of being opened (VS calls out to user code to do, e.g., file parsing before it updates its internal list of open windows). The only workaround I've found for that is to enumerate all the window frames, get the VSFPROPID_ExtWindowObject for each one, and compare it to the EnvDTE.Window (whose document's FullName matches the desired path). – Cameron Jan 16 '14 at 18:45
  • 2
    Also, embedded peek document windows don't have a `IVsWindowFrame` as far as I can tell, so `IsDocumentOpen` will fail even though the document is "open" in the running document table. You can still get the buffer by using `IVsRunningDocumentTable`'s `FindAndLockDocument` method directly (which is what `IsDocumentOpen` is calling internally). Obviously, if you have an `EnvDTE.Window` object in the first place, this doesn't apply, but if you just have the path it's important to know about this corner case. – Cameron Aug 21 '14 at 17:58