I was able to get close enough to what I wanted by creating a Visual Studio plugin with a self hosted Owin server exposing a basic WebApi.
This allowed me to open files from a browser with a link like:
http://localhost:9000/VisualStudioApi/OpenFile?Path=.\Url\Escaped\Path\Relative\To\Solution\File
Any web server hosting this button would need to hardcode the links to http://localhost:9000
, which brings up an issue of having multiple Visual Studio instances running, so there would need to be some logic about how to map a .sln file to a known port. But absent an official Visual Studio solution, this gets the job done for the most part.
In case this helps someone in the future, here are the code snippets:
VS Package
[ComVisible(true)]
[Guid("B77F7C65-0F9F-422A-A897-C06FDAEC9604")]
[ProvideObject(typeof(InitializerPackage))]
[ProvideAutoLoad(UIContextGuids80.SolutionExists)]
public class InitializerPackage : Package
{
protected override void Initialize()
{
base.Initialize();
//Get copy of current DTE
var dte = (DTE)GetService(typeof(DTE));
var dte2 = dte as DTE2;
dte2.Events.SolutionEvents.Opened += () =>
OwinVisualStudioApiListenerManager.StartServer(dte2);
dte2.Events.SolutionEvents.AfterClosing += () =>
OwinVisualStudioApiListenerManager.StopServer();
}
}
Owin Initializer
public static class OwinVisualStudioApiListenerManager
{
private static IDisposable _runningServer;
public static DTE2 VisualStudioApi { get; set; }
public static void StartServer(DTE2 visualStudioApi)
{
if (null != _runningServer)
_runningServer.Dispose();
VisualStudioApi = visualStudioApi;
//nothing fancy about OwinStartup
//see github file http://tinyurl.com/zt2bm8b
_runningServer = WebApp.Start<OwinStartup>("http://localhost:9000");
}
public static void StopServer()
{
if (null != _runningServer)
_runningServer.Dispose();
VisualStudioApi = null;
}
}
WebApi
public class VisualStudioApiController : ApiController
{
// GET /VisualStudioApi/OpenFile/?path=
[HttpGet]
public string OpenFile(string path)
{
var fullPath = Path.Combine(
Path.GetDirectoryName(
OwinVisualStudioApiListenerManager.VisualStudioApi.Solution.FullName),
HttpUtility.UrlDecode(path));
//https://mcmap.net/q/1471758/-how-to-open-file-programmatically-using-envdte-in-c/1224069
OwinVisualStudioApiListenerManager.VisualStudioApi
.ExecuteCommand(
"File.OpenFile",
fullPath);
return "success";
}
}