DEV Community

Cover image for Creating a Custom URL Protocol in .NET
Jamie Mc Manus
Jamie Mc Manus

Posted on

Creating a Custom URL Protocol in .NET

You open a Teams link and it prompts you to open the Teams Desktop Application, and you sit back and wonder how this works.

Well wonder no more ! This process is possible because of URL protocols - similar to how Windows sets the default web browser or the default email app. You have probably seen it in actions a few times from apps like Teams or Zoom. Windows has a few of these built in, but we can create our own custom protocols too.

You can create these by manually adding a Reg Key but we'll do this via .NET.

We'll create a simple app, and a custom protocol that will open this app when a user clicks on a URL with the protocol.

Lets go !


Lets start with creating a desktop application - for simplicity I've chosen a WPF app. Open Visual Studio with Administrator privilege's ( I'll explain later ) and create a new WPF project.

App Startup

In the App.xaml.cs file create a new method called RegisterProtocol() . We'll run this on startup to create our protocol.

We'll need to do check if the protocol exists, and if not then create it.

We set the name of our protocol and assign to a variable.

string customProtocol = "CustomProtocol";
Enter fullscreen mode Exit fullscreen mode

Then add we check if a Registry Key exists for this value :

RegistryKey key = Registry.ClassesRoot.OpenSubKey(customProtocol);
if (key == null){
Enter fullscreen mode Exit fullscreen mode

If the key is null then we will have to create one.

if (key == null) {
    key = Registry.ClassesRoot.CreateSubKey(customProtocol);
    key.SetValue(string.Empty, "URL: " + customProtocol);
    key.SetValue("URL Protocol", string.Empty);

    key = key.CreateSubKey(@"shell\open\command");
    key.SetValue(string.Empty, applicationPath + " " + "%1");    
Enter fullscreen mode Exit fullscreen mode

Now to explain it a bit.

  1. key = Registry.ClassesRoot.CreateSubKey(customProtocol);
    This line creates a new subkey in the HKEY_CLASSES_ROOT registry. This is where file associations and protocol handlers are registered.

  2. key.SetValue(string.Empty, "URL: " + customProtocol);
    This line sets a value for the subkey we just created. The value's name is an empty string (indicating the default value for the key), and the value's data is a string that specifies the type of URL and the custom protocol name.

  3. key.SetValue("URL Protocol", string.Empty);
    This line sets a value named "URL Protocol" within the same subkey.

  4. key = key.CreateSubKey(@"shell\open\command");
    This line creates a new subkey within our key. The subkey is named "shell\open\command". This is where you specify the command to be executed when a URL matching our protocol is opened.

  5. key.SetValue(string.Empty, applicationPath + " " + "%1");
    This line sets the default value for the shell command key we just created. The data is the filepath to the EXE for our application, and %1 represents any parameters sent.

  6. key.Close();
    We simply close the key to finish.

We now have one last thing before the key can be registered. We have to make sure the application has the correct privilege's to edit the registry.

In your app.manifest file ( add one if you dont have one )

Add the below:

<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
    <requestedExecutionLevel level="requireAdministrator" />
Enter fullscreen mode Exit fullscreen mode

This will make the application ask the user for admin the privilege's that we need to create the key.


Next up we'll edit the application to display any arguments that are passed to the app when opened via the custom protocol.
We don't need to do this part, but it will show you that any parameters you sent are indeed working !

In your MainWindow.xaml.cs add the below string to display the arguments passed in.

public string ArgumentsText
        get { return argumentsTextBlock.Text; }
        set { argumentsTextBlock.Text = value; }
Enter fullscreen mode Exit fullscreen mode

And within the MainWindow.xaml file add a textblock to display the string:

<TextBlock x:Name="argumentsTextBlock" Margin="10" />
Enter fullscreen mode Exit fullscreen mode

Next we need to edit the app.xaml.cs to pass the arguments into the MainWindow:

Edit the OnStartup like so:

protected override void OnStartup(StartupEventArgs e)

        MainWindow mainWindow = new MainWindow();
        if (e.Args.Length > 0)
           string arguments = string.Join(", ", e.Args);
           mainWindow.ArgumentsText = "Arguments: " + arguments;
            mainWindow.ArgumentsText = "No arguments provided.";

Enter fullscreen mode Exit fullscreen mode

We first call our RegisterProtocol method and then check for any arguments passed in on startup before manually creating our main window.

Now you can publish the application and click the EXE to run it.
You should see something like the below image -
Default App Open

This is what we expect - there are no parameters passed when clicking the EXE.


Next we're going to create a simple HTML file to simulate opening our protocol from the web.

<!DOCTYPE html>
    <title>Custom Protocol Test</title>
    <p>Click the link below to test your custom protocol:</p>

    <a href="CustomProtocol://myargument">Test Custom Protocol</a> 

Enter fullscreen mode Exit fullscreen mode

Like I said - pretty simple. The value for the href is the name of your protocol followed by '://' along with any parameters you want to pass - I am passing in the string "myargument" .

In Action

Lets open up the file in browser and try it out.

Click on the link and you should be greeted with a prompt like the below:

Protocol Prompt

Perfect. Click Open and our application should open as expected.

App Displaying our parameter

Finishing Up

There we have it - its not a glamourous example but who said programming would be eh ?

You can see the sample application here:

GitHub logo JamieMcManus / CustomProtocolAppExample

Sample Project for a Registering a Custom URL Protocol

Got any improvements ? Then feel free to comment below !

And if you're feeling generous you can buy me a coffee with the link below ( and yes its all for coffee, I drink a copious amount of it while writing ☕ )

Buy Me A Coffee

Top comments (7)

peyman profile image

Great article!

Personally instead of always running the application as admin I would do something like this to ask the user only when needed:

var appPath = Process.GetCurrentProcess().MainModule.FileName;
ExecuteElevated(appPath, "--register-url-protocol");

private void ExecuteElevated(string fileName, string arguments)
    using var process = new Process();
    process.StartInfo = new ProcessStartInfo
        FileName = fileName,
        Arguments = arguments,
        Verb = "runas",

Enter fullscreen mode Exit fullscreen mode
// App.xaml.cs
private void Application_Startup(object sender, StartupEventArgs e)
    if (e.Args.Contains("--register-url-protocol"))
        // TODO: Register the url protocol.
Enter fullscreen mode Exit fullscreen mode
<!-- App.xaml -->
Enter fullscreen mode Exit fullscreen mode
artydev profile image

Awesome, thank you very much

thomaslevesque profile image
Thomas Levesque

No need for admin privileges if you register the protocol for the current user only.
Just write to HKEY_CURRENT_USER\Software\Classes, instead of HKEY_CLASSES_ROOT

jamiemcmanus profile image
Jamie Mc Manus

Absolutely right Thomas.

deexter profile image

What if application is already running is it still possible to open with parameter and pass to existing application instance?

jamiemcmanus profile image
Jamie Mc Manus

Great question ! It should be possible -
You could use an Inter-Process Communication such as named pipes or WM_COPYDATA to pass parameters to the open application .

I haven't had a chance to test this yet though, hopefully I will get a chance to test it and update the article !

janseris profile image

This does not work when run from cmd or from Win+R. How to fix that?
It only works from browser and from explorer.exe address bar.
And no, adding quotes "" around executable path and %1 which is suggested in official Microsoft documentation does not help.