How create a custom browser URI scheme and C# protocol handler client that supports opening and editing of remotely hosted documents through WebDAV

Recently I came across a situation where I needed to create a one-click web-based solution, that allows clients to open and edit PhotoShop, AutoCAD, WordPerfect, PDF, LibreOffice and other files hosted remotely from my WebDAV enabled Apache web server by using their own desktop tools. Essentially I needed a solution for opening and editing remotely hosted documents (located on my web server) through a simple HREF link on a web page. This link, when clicked, would automatically open the PSD file hosted on my server, but do so in the PhotoShop installed on the client’s Windows computer, allowing them to seamlessly open and edit such files, without any need to download and upload the files back and forth to my server.  Initially, it seemed like an impossible, task, but then I realized that I should be able to create my very own custom browser URI scheme and some kind of a Windows-based client that handles opening of remote files in local installation Photoshop. After a bit of playing around with it, I got it working and created a solution capable of opening for remote editing not only PhotoShop files, but virtually any other remotely hosted file types in their associated applications, meaning in the software installed on the client’s desktop computer .  The following article is how I went about it.

DEMO

Following is a demo of the final product, a one-click web-based solution, that allows opening and editing of remote documents such as PhotoShop, AutoCAD, WordPerfect, PDF, LibreOffice, PhotoShop, Visual Studio, LibreOffice and other unsupported web types in locally installed desktop software over WebDAV.

 

Prerequisites

WebDAV server

First of all, we need to have a WebDAV support enabled in our web server. What is WebDAV? Well, it’s a Web Distributed Authoring and Versioning extension of the Hypertext Transfer Protocol (HTTP) which allows clients to perform remote Web content authoring operations. Essentially, it allows us to open any document for editing directly from the remote server with a single click and save it back to the server without any download and upload steps, simply just by leveraging the ‘Save’ button in our application.

Anyhow, I am using Apache 2.4.x, and it takes roughly 5 minutes to enable the WebDAV module. If you don’t know how to do this, here is a short article I wrote on How to enable WebDAV on Apache HTTP Server 2.4.x.

Once the WebDAV is enabled, simply place any dummy test.PSD file into WebDAV folder on your web server – this is the file we’ll attempt to edit remotely.

Register the new Uniform Resource Identifier Scheme

The opening of remotely hosted PhotoShop, AutoCAD and other unusual file types directly in the desktop installation of such tools is not is not supported in the web browser. However, we know that Microsoft can do this for MS Word and MS Excel, where we can open and edit OneDrive or SharePoint hosted files in the desktop version of Word and Excel. The reason that Microsoft is capable of doing this is that they have built the support for the custom URI scheme for all their desktop products and created protocol handler which comes installed with Microsoft Office, capable of handling HREF links marked with their own custom URI scheme.

EXCEL EXAMPLE

Just so the process is understood, this is how the editing of MS Excel file hosted in OneDrive works. The website link points to

<a href="ms-excel:\\remote-server@SSL@443\webdav\test.xlsx">Click to Open Excel File</a>

As you can see, the link doesn’t point to https://onedrive, but it starts with ‘ms-excel:\\’, this is what is called a custom URI Scheme.  The URI generic syntax consists of a hierarchical sequence of five components:

URI = scheme:[//authority]path[?query][#fragment]

We’re interested in this scheme part of the URI only:

Now, when it comes to URI Scheme, we are used to seeing primarily the HTTP and HTTPS schemes, but essentially, this can be set to anything, as long as the OS on the end user’s computer knows what to do with it.

So how does Microsoft capture their ‘ms-excel’ scheme? This can be seen in Windows Registries. I’ve searched for ms-excel in registries and found that whenever Windows comes across ms-excel URI scheme, it points it to

  • C:\Program Files (x86)\Microsoft Office\Root\Office16\protocolhandler.exe “%1”

, which accepts the parameters (link to the document, passed onto it from the website):

Creating a custom browser URI scheme and C# protocol handler client 

Ok, so now that we know how Microsoft does it, we should be able to reproduce this behaviour for PhotoShop or any other files.

We just need to create our own protocol handler client that supports opening and editing of remotely hosted PhotoShop and other documents through WebDAV and we need to define our own unique URI scheme. And that should be fairly simple.

C# protocol handler client

I started with creating a Windows-based protocol handler in C#.

In Visual Studio 2017 (free comunity edition), I’ve created a project called: OpenAnythingFromWeb as a simple console app that’s using Windows .NET framework:

I won’t go to too many details about the C# code, because that would inflate this article, but as you can see, it’s fairly trivial, see the in-line comments to understand it. Just note that this isn’t anything production ready, there is no error handling, etc. It’s just to illustrate a working solution capable of opening any .ext file with a default Windows program:

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;


namespace OpenDefaultProgram
{
    class Program
    {
        [DllImport("shell32.dll", EntryPoint = "FindExecutable")]
        private static extern long FindExecutable(string lpFile, string lpDirectory, StringBuilder lpResult);

        static void Main(string[] args)
        {
            var pathToFile = args[0].Replace("unitydoprocess:", "").Replace("%5C","\\");

            string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\UnityDP\\test" + Path.GetExtension(pathToFile);
            if (!File.Exists(path))
            {
                using (StreamWriter sw = File.CreateText(path)) { }
            }

            //var pathToLocalWPDFile = Directory.GetCurrentDirectory() + "\\wptest.wpd";
            var commandToRun = "\""+ Program.FindExecutable(path) +"\" " + "\"" + pathToFile + "\"";
                        
            Console.Write(Environment.NewLine); Console.Write("Path: " + path);
            Console.Write(Environment.NewLine); Console.Write("Is there a default app for this file? " + Program.HasExecutable(path));
            Console.Write(Environment.NewLine); Console.Write("Command to run: " + commandToRun);  Console.Write(Environment.NewLine);

            Process cmd = new Process();
                cmd.StartInfo.FileName = "cmd.exe";
                cmd.StartInfo.RedirectStandardInput = true;
                cmd.StartInfo.RedirectStandardOutput = true;
                cmd.StartInfo.CreateNoWindow = true;
                cmd.StartInfo.UseShellExecute = false;
                cmd.Start();

                cmd.StandardInput.WriteLine(commandToRun);
                cmd.StandardInput.Flush();
                cmd.StandardInput.Close();
      
            string path2 = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +  "\\UnityDP\\debugging.txt";
            // This text is added only once to the file.
            if (!File.Exists(path2))
            {
                // Create a file to write to.
                using (StreamWriter sw = File.CreateText(path2))
                {
                    sw.WriteLine(commandToRun);
                }
            } else
            {
                using (StreamWriter sw = File.CreateText(path2))
                {
                    sw.WriteLine(commandToRun);
                }
            }


            // }

        }


        public static bool HasExecutable(string path)
        {
            var executable = FindExecutable(path);
            return !string.IsNullOrEmpty(executable);
        }

        private static string FindExecutable(string path)
        {
            var executable = new StringBuilder(1024);
            FindExecutable(path, string.Empty, executable);
            return executable.ToString();
        }
    }
}

Once saved, simply build the solution and Voila!, we now have our own OpenAnythingFromWeb.exe protocol handler.

To use it, of course, we could build a digitally signed installer, and give it to our clients as a downloadable .msi or .exe file.

But we can also simply place this on any machine where we want to handle remote Photoshop editing.

To test if the protocol handler works, let’s copy it into C:\OpenAnythingFromWeb, like this:

 

Registry Entry for custom ‘OpenAnythingFromWeb’ URI Scheme

Now, we need to add the entry into Windows Registries. I’ve made a copy of the ms-excel registry entry and replaced it with my own values.

Like this:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\OpenAnythingFromWeb]
@="URL:OpenAnythingFromWeb Protocol"
"URL Protocol"="OpenAnythingFromWeb"

[HKEY_CLASSES_ROOT\OpenAnythingFromWeb\DefaultIcon]
@="C:\\OpenAnythingFromWeb\\OpenAnythingFromWeb.exe"

[HKEY_CLASSES_ROOT\OpenAnythingFromWeb\shell]

[HKEY_CLASSES_ROOT\OpenAnythingFromWeb\shell\open]

[HKEY_CLASSES_ROOT\OpenAnythingFromWeb\shell\open\command]
@="C:\\OpenAnythingFromWeb\\OpenAnythingFromWeb.exe \"%1\""

Just save the above as an OpenAnythingFromWeb.reg file using notepad:

Run it and import it:

Once done, if you open Regedit, we should see our protocol handler entry:

 

Creating a Web Page

Now we simply need to create a page with HREF link pointing to WebDAV documents using our custom ‘OpenAnythingFromWeb’ URI Scheme.

Here is a sample page I create for editing of the following file types:

  • PhotoShop
  • C# Code .cs files in Visual Studio 2017
  • LibreOffice CALC
  • WordPerfect
  • Excel
  • PDF
<html><body> 
<center>
<h1>Open/Edit remotely hosted files in the Desktop software</h1>
<h4>Implementation of custom browser URI Scheme w/ support for opening and editing of remote documents!</h4>
<br><br>
<br><br>
<li>Edit remote: <a href="OpenAnythingFromWeb:\\www.joe0.com@SSL\webdav\test.psd">PhotoShop</a> document</li>
<br><br>
<li>Edit remote: <a href="OpenAnythingFromWeb:\\www.joe0.com@SSL\webdav\program.cs">C# Code</a> in Visual Studio 2017</li> 
<br><br>
<li>Edit remote: <a href="OpenAnythingFromWeb:\\www.joe0.com@SSL\webdav\librecalc.ods">LibreOffice CALC</a> document</li>
<br><br>
<li>Edit remote: <a href="OpenAnythingFromWeb:\\www.joe0.com@SSL\webdav\wptest.wpd">WordPerfect</a> document</li>
<br><br>
<li>Edit remote: <a href="OpenAnythingFromWeb:\\www.joe0.com@SSL\webdav\excel.xlsx">Excel</a> document</li>
<br><br>
<li>Edit remote: <a href="OpenAnythingFromWeb:\\www.joe0.com@SSL\webdav\test.pdf">PDF</a> document</li>
</center>
</body></html> 
 

I’ve tested it and it all seems to work.

I hope this makes it easier for you to create your own custom protocol handlers in Windows or other operating systems.

That’s it, enjoy!