As part of a NuGet installation we need to open a web page when the installation completes.
Our requirement is to:
- Open web page in Visual Studio in a new window if the page is not already open in another window.
Opening a web page by itself is straight forward:
... DTE.ItemOperations.Navigate(WebPageUri.ToString(), EnvDTE.vsNavigateOptions.vsNavigateOptionsNewWindow); ...
The problem has been finding a way to get a list of the currently open web pages, so that we can decide if a page needs to be opened.
I am certain the solution is simple and has been staring us in the face all along.
How do we get a list of the currently open web pages in Visual Studio using EnvDTE?
A solution to the problem is listed below. I doubt the approach is a best practice, but it does appear to work.
Web browser windows in Visual Studio have an “ObjectKind” value of “EnvDTE.Constants.vsWindowKindWebBrowser”. The “Object” value is the Internet Explorer browser and implements “IWebBrowser2”. Although “IWebBrowser2” is not .Net, “System.Windows.Forms.WebBrowser” is (apparently) a wrapper around “IWebBrowser2”. “System.Windows.Forms.WebBrowser” has an “AttachInterfaces” method which can be overridden, which allows us to pass the window object and let “System.Windows.Forms.WebBrowser” do the heavy lifting.
The end result is:
public class VisualStudioWebBrowser : System.Windows.Forms.WebBrowser
{
protected VisualStudioWebBrowser(object IWebBrowser2Object)
{
this.IWebBrowser2Object = IWebBrowser2Object;
}
protected object IWebBrowser2Object { get; set; }
public static void Evaluate(EnvDTE.Window WindowReference, Action<System.Windows.Forms.WebBrowser> OnEvaluate)
{
//Note: Window of EnvDTE.Constants.vsWindowKindWebBrowser type contains an IWebBrowser2 object
if (VisualStudioWebBrowser.IsWebBrowserWindow(WindowReference))
{
using (System.Threading.ManualResetEvent evt = new System.Threading.ManualResetEvent(false))
{
System.Threading.Thread STAThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart((o) =>
{
try
{
using (VisualStudioWebBrowser Browser = new VisualStudioWebBrowser(o))
{
try
{
OnEvaluate.Invoke((System.Windows.Forms.WebBrowser)Browser);
}
catch { }
}
}
catch { }
evt.Set();
}));
STAThread.SetApartmentState(System.Threading.ApartmentState.STA);
STAThread.Start(WindowReference.Object);
evt.WaitOne();
}
}
}
public static bool IsWebBrowserWindow(EnvDTE.Window WindowReference)
{
return (WindowReference != null && WindowReference.ObjectKind.Equals(EnvDTE.Constants.vsWindowKindWebBrowser, StringComparison.InvariantCultureIgnoreCase));
}
public static IEnumerable<EnvDTE.Window> GetWebBrowserWindows(EnvDTE.DTE EnvDTEReference)
{
List<EnvDTE.Window> BrowserWindows = new List<EnvDTE.Window>();
foreach (EnvDTE.Window WindowReference in EnvDTEReference.Windows)
{
if (VisualStudioWebBrowser.IsWebBrowserWindow(WindowReference) == true)
{
BrowserWindows.Add(WindowReference);
}
}
return BrowserWindows;
}
public static Uri GetWebBrowserWindowUrl(EnvDTE.Window WindowReference)
{
Uri BrowserUrl = new Uri("", UriKind.RelativeOrAbsolute);
VisualStudioWebBrowser.Evaluate(WindowReference, new Action<System.Windows.Forms.WebBrowser>((wb) =>
{
BrowserUrl = wb.Url;
}));
return BrowserUrl;
}
public static IEnumerable<Uri> GetWebBrowserWindowUrls(EnvDTE.DTE EnvDTEReference)
{
List<Uri> BrowserUrls = new List<Uri>();
foreach (EnvDTE.Window BrowserWindow in VisualStudioWebBrowser.GetWebBrowserWindows(EnvDTEReference))
{
try
{
Uri BrowserUrl = VisualStudioWebBrowser.GetWebBrowserWindowUrl(BrowserWindow);
if (String.IsNullOrWhiteSpace(BrowserUrl.LocalPath) == false)
{
BrowserUrls.Add(BrowserUrl);
}
}
catch { }
}
return BrowserUrls;
}
protected override void AttachInterfaces(object nativeActiveXObject)
{
base.AttachInterfaces(this.IWebBrowser2Object);
//base.AttachInterfaces(nativeActiveXObject);
}
protected override void DetachInterfaces()
{
base.DetachInterfaces();
this.IWebBrowser2Object = null;
}
}
A list of open web pages is then found by:
IEnumerable<Uri> Urls = VisualStudioWebBrowser.GetWebBrowserWindowUrls(EnvDTEReference);