URL protocol handler
It's fairly common to find web pages link to resources using non-http protocols. An example is the mailto:
protocol:
<a href="mailto:webmaster@example.com">Web Master</a>
You might want your application to become the handler for a particular protocol well-known protocol (if you are an email application, for example) or even create your own custom protocol to handle links that navigate to particular places or states in your application. This is particularly useful when sharing some content with contacts and a common scenario many applications need to implement.
Implementation
- Electron
- PWA
- WebView2
To register a custom protocol in your Electron application you can use the
setAsDefaultProtocolClient
method of the app
module.
const { app } = require('electron');
app.whenReady().then(() => {
app.setAsDefaultProtocolClient('mycustomprotocol');
});
Once registered, your application will be opened anytime a user interacts with a link
similar to mycustomprotocol://custom/path
. How the application access the URI is
different depending on the platform.
For macOS is via the open-url
event
const { app } = require('electron');
app.on('open-url', (event, url) => {
// Your code handling here
});
On Windows the URI is passed as part of the arguments (process.argv
) when starting the
application. Because the position could change, it is recommended to iterate to find the
right one. An optimistic implementation would be:
const protocolArg = process.argv.find((arg) => arg.startsWith('mycustomprotocol://'));
if(!protocolArg){
return;
}
// Your code handling here
caution
Depending on the platform there might be some extra steps to do (like add the protocol to
the app's info.plist
on macOS). Make sure to read the official documentation.
Contrary to the PWA case, there are no limitations on the name of the protocol, thus
why the name is mycustomprotocol
and not web+mycustomprotocol
.
If you have Fiddle installed, you can run a full example by clicking the following button:
Open in FiddleWhen building a PWA there are 2 ways to register the applications as a protocol handler:
The first one is specifying the protocol_handlers
property in the Web app manifest of the PWA:
{
"name": "My PWA",
"orientation": "any",
"icons": [
{
"src": "icon.png",
"sizes": "512x512",
"type": "image/png"
}
],
//...
"protocol_handlers": [
{
"protocol": "web+mycustomprotocol",
"url": "index.html?custom=%s"
}
]
}
Or you can do it programatically via JavaScript calling
navigator.registerProtocolHandler
:
document.addEventListener('DOMContentLoaded', (event) => {
navigator.registerProtocolHandler('web+mycustomprotocol', 'index.html?custom=%s');
})
The value of the protocol can be one of the following 2 options:
- One of the safelisted schemes
- Or begin with
web+
followed by at least one or more lowercase ASCII letters
You can learn more in this article in web.dev.
caution
This feature is currently being tested in Chromium browsers. Websites can opt-in via origin trials
As mentioned in its page, WebView2 is available on Windows platforms and can be used from C# on a WPF or WinForms application, or in a Win32 with C++.
caution
While the macOS version of WebView2 is in development, there is no preview available. That said, registering the application to handle a custom protocol will require calling to OS APIs the same way it is done on Windows.
To handle the On a WPF application the URL is passed as part of the parameters when invoking it so you have to look for it:
class Program
{
[STAThread]
static void Main(string[] args)
{
if (args != null && args.Length > 0)
{
if (args[0].StartsWith("mycustomprotocol://"))
{
//handle URI activation
}
App application = new App();
application.InitializeComponent();
application.Run();
}
}
If you are using it on a Win32 application with C++, then your could will look similar to:
// Parameters are assumed to be valid
HRESULT GetPropVariantForUrlAndTime
(PCWSTR pszUrl, const FILETIME &ftLastModified, PROPVARIANT **ppPropValue)
{
*ppPropValue = NULL;
// Allocate the propvariant pointer.
size_t const cbAlloc = sizeof(**ppPropValue);
*ppPropValue = (PROPVARIANT *)CoTaskMemAlloc(cbAlloc));
HRESULT hr = *ppPropValue ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
PropVariantInit(*ppPropValue); // Zero init the value
// Now allocate enough memory for 2 nested PropVariants.
// PKEY_Search_UrlToIndexWithModificationTime is an array of two PROPVARIANTs.
PROPVARIANT *pVector = (PROPVARIANT *)CoTaskMemAlloc(sizeof(*pVector) * 2);
hr = pVector ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
// Set the container PROPVARIANT to be a vector of two PROPVARIANTS.
(*ppPropValue)->vt = VT_VARIANT | VT_VECTOR;
(*ppPropValue)->capropvar.cElems = 2;
(*ppPropValue)->capropvar.pElems = pVector;
PWSTR pszUrlAlloc;
hr = SHStrDup(pszUrl, &pszUrlAlloc);
if (SUCCEEDED(hr))
{
// Now fill the array of PROPVARIANTS.
// Put the pointer to the URL into the vector.
(*ppPropValue)->capropvar.pElems[0].vt = VT_LPWSTR;
(*ppPropValue)->capropvar.pElems[0].pwszVal = pszUrlAlloc;
// Put the FILETIME into vector.
(*ppPropValue)->capropvar.pElems[1].vt = VT_FILETIME;
(*ppPropValue)->capropvar.pElems[1].filetime = ftLastModified;
}
else
{
CoTaskMemFree(pVector);
}
}
if (FAILED(hr))
{
CoTaskMemFree(*ppPropValue);
*ppPropValue = NULL;
}
}
return S_OK;
}
info
Depending on how your application is packaged and distributed (i.e.: MSIX, squirrel, etc.) you might have to do some extra steps like modifying the registry or a manifest.
You can read more in Installing and Registering Protocol Handlers.