Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visual Studio Protocol Handler - Open File

Does Visual Studio have a Protocol Handler that includes a command to open a specific file?

They have one for Git clone, as described in Announcing the GitHub Extension for Visual Studio:

The Open in Visual Studio button [in GitHub] calls a new protocol handler called “git-client://”. We designed this new generic protocol together with GitHub to allow a website pass standard Git operations to any IDE

Essentially what I want to do is described in this GitHub Issues:

enter image description here

When I click on the Button, Visual Studio should open the specific file (preferably with the correct Solution loaded as well).

If this isn't possible directly with Protocol Handlers, could it be done as a Visual Studio Extensions with a Web View that would achieve the same purpose (ie allow opening files from a Web Page)?

like image 728
Philip Pittle Avatar asked Jan 05 '16 03:01

Philip Pittle


1 Answers

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

Opening files in Visual Studio via a web browser

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));

          //http://stackoverflow.com/q/5039226/1224069
          OwinVisualStudioApiListenerManager.VisualStudioApi
                .ExecuteCommand(
                    "File.OpenFile",
                    fullPath);

           return "success";
    }
}
like image 162
Philip Pittle Avatar answered Oct 20 '22 03:10

Philip Pittle