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 !
Setup
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";
Then add we check if a Registry Key exists for this value :
RegistryKey key = Registry.ClassesRoot.OpenSubKey(customProtocol);
if (key == null){
...
}
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");
key.Close();
}
Now to explain it a bit.
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.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.key.SetValue("URL Protocol", string.Empty);
This line sets a value named "URL Protocol" within the same subkey.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.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.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" />
</requestedPrivileges>
This will make the application ask the user for admin the privilege's that we need to create the key.
GUI
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; }
}
And within the MainWindow.xaml file add a textblock to display the string:
<TextBlock x:Name="argumentsTextBlock" Margin="10" />
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)
{
base.OnStartup(e);
RegisterProtocol();
MainWindow mainWindow = new MainWindow();
if (e.Args.Length > 0)
{
string arguments = string.Join(", ", e.Args);
mainWindow.ArgumentsText = "Arguments: " + arguments;
}
else
{
mainWindow.ArgumentsText = "No arguments provided.";
}
mainWindow.Show();
}
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 -
This is what we expect - there are no parameters passed when clicking the EXE.
Web
Next we're going to create a simple HTML file to simulate opening our protocol from the web.
<!DOCTYPE html>
<html>
<head>
<title>Custom Protocol Test</title>
</head>
<body>
<p>Click the link below to test your custom protocol:</p>
<a href="CustomProtocol://myargument">Test Custom Protocol</a>
</body>
</html>
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:
Perfect. Click Open and our application should open as expected.
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:
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 ☕ )
Top comments (7)
Great article!
Personally instead of always running the application as admin I would do something like this to ask the user only when needed:
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
Absolutely right Thomas.
Awesome, thank you very much
What if application is already running is it still possible to open with parameter and pass to existing application instance?
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 !
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.