Hello,

In this post, I would like to present the definition of a custom Uniform Resource Identifier (URI) in order to execute a program in Windows on client side.

In a URL the portion with the HTTP://,FTP://, …etc are called URI Schemes. So, in some cases, it may be desirable to invoke another application to handle a custom Uniform Resource Identifier (URI) scheme. To register the existing application as an URI pluggable protocol handler and associate it with the custom URI scheme. Once the application has successfully launched, it can use command-line parameters to retrieve the URI that launched it. These settings apply to pluggable protocol handlers launched from within Windows Internet Explorer and from Windows Explorer using the Run… command (Windows logo key+R).

So, it is possible to register your own through the registry via the adding of a new key, along with the appropriate subkeys and values, to HKEY_CLASSES_ROOT. The root key must match the URI scheme that is being added.

HKEY_CLASSES_ROOT/
your-protocol-name/
your-protocol-name/(Default) = URL:your-protocol-name Protocol
your-protocol-name/URL Protocol =
your-protocol-name/DefaultIcon/
your-protocol-name/DefaultIcon/(Default) = Executable.exe,1
your-protocol-name/shell/
your-protocol-name/shell/(Default) =
your-protocol-name/shell/open/
your-protocol-name/shell/open/(Default) =
your-protocol-name/shell/open/command/
your-protocol-name/shell/open/command/(Default) = "%ENV_VARIABLE%PathToExecutable/Executable.exe" "%1"

The URL Protocol string value indicates that this key declares a custom pluggable protocol handler. Without this key, the handler application will not launch. The value should be an empty string.
When a user clicks a link containing your custom URI scheme, Windows Internet Explorer launches the pluggable protocol handler registered for that URI scheme. If the specified open command specified in the registry contains a %1 parameter, Internet Explorer passes the URI to the registered pluggable protocol handler application
Importante Note IE must be the default browser on CLIENT side.

Example : Step by step

  • Go to Start then in Find type regedit -> it should open Registry editor:
  • Click Right Mouse on HKEY_CLASSES_ROOT then New -> Key:
  • In the Key give the lowercase name (testhuseyin) corresponding and matching with the URI Scheme (in my case it will be testhuseyin://mydata)
  • Click Right Mouse on testhuseyin -> then New -> String Value and add URL protocol without value.

  • Create hierarchy like testhuseyin -> shell -> open -> command via (Right Mouse New -> Key)
  • Create a new key DefaultIcon under testhuseyin via (Right Mouse New -> Key)
  • Inside the root key testhuseyin modify the value of entry (Default) to the protocol URL:Testhuseyin Protocol

  • Inside the key testhuseyin/DefaultIcon/, modify the value of (Default) to the name of .exe which to be launched: testhuseyin.exe,1 The “,1” corresponds to the parameter’s data.
  • Inside the key testhuseyin/shell/open/command/, modify the value of (Default) to the path of .exe which to be launched “c:\testing\testhuseyin.exe” “%1”. The “%1” corresponds to the parameter’s data.
  • Result should be:
    testhuseyin/
    testhuseyin/(Default) = URL:Testhuseyin Protocol
    testhuseyin/URL Protocol =
    testhuseyin/DefaultIcon/
    testhuseyin/DefaultIcon/(Default) = testhuseyin.exe,1
    testhuseyin/shell/
    testhuseyin/shell/(Default) =
    testhuseyin/shell/open/
    testhuseyin/shell/open/(Default) =
    testhuseyin/shell/open/command/
    testhuseyin/shell/open/command/(Default) = "c:\testing\testhuseyin.exe" "%1"
    
  • To test if it works go to Internet Explorer (not Chrome or Firefox) and enter testhuseyin:test_1_test_2_hello_everybody:

    Without application helper, the below message will be displayed:

    If, you test with an unconfigured protocol, for example testhuseyin123, you will not obtain the same behaviour : testhuseyin123:test_1_test_2_hello_everybody.

  • So, create a new console application Testhuseyin:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    
    namespace Testhuseyin
    {
        class Program
        {
            static void Main(string[] args)
            {
            }
        }
    }
    

    Modify the Main method simply to write into output the passed parameter:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Testhuseyin
    {
        class Program
        {
            static void Main(string[] args)
            {
                //TEST HUSEYIN
                if (args != null && args.Length > 0)
                    Console.WriteLine(args[0]);
                Console.ReadKey();
    
            }
        }
    }
    

    Build the solution:

    1>------ Build started: Project: Testhuseyin, Configuration: Debug Any CPU ------
    1>  Testhuseyin -> C:\Workspaces\MS_Visual_Studio_Projects\Testhuseyin\Testhuseyin\bin\Debug\Testhuseyin.exe
    ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
    

    Copy/paste the executable into the protocol’s configured folder:

  • Try again to test if it works go to Internet Explorer (not Chrome or Firefox) and enter testhuseyin:test_1_test_2_hello_everybody:

  • Try again to test if it works via Start Menu/Run and enter testhuseyin:JAVABLOG.LU_huseyin:

Advanced : Use environment variable

  • Create user environment variable in order to store the path of executable of application helper : my_huo_user_variable = C:\testing\
    For example, this variable could be modified every time the boot of standalone application.

    Check the value of this variable:

  • Inside the key testhuseyin/shell/open/command/, modify the value of (Default) in order to use this environment variable: “%my_huo_user_variable%testhuseyin.exe” “%1”.
  • In order to use environment variable, you need to change the type of testhuseyin/shell/open/command/(Default) from default type REG_SZ to REG_EXPAND_SZ. However, it is possible to do via regedit GUI. You can use command line reg.exe to do this.Example 1 : C:\WINDOWS\system32>reg.exe add “HKCR\testhuseyin\shell\open\command” /ve /t REG_EXPAND_SZ /d “\”%my_huo_user_variable%testhuseyin.exe\” \”%1\”” /f
    The operation completed successfully.

    Example 2 : C:\WINDOWS\system32>reg.exe add “HKCR\testhuseyin\shell\open\command” /ve /t REG_EXPAND_SZ /d “\”^%my_huo_user_variable^%testhuseyin.exe\” \”%1\”” /f
    The operation completed successfully.

  • Final result should be:
    testhuseyin/
    testhuseyin/(Default) = URL:Testhuseyin Protocol
    testhuseyin/URL Protocol =
    testhuseyin/DefaultIcon/
    testhuseyin/DefaultIcon/(Default) = testhuseyin.exe,1
    testhuseyin/shell/
    testhuseyin/shell/(Default) =
    testhuseyin/shell/open/
    testhuseyin/shell/open/(Default) =
    testhuseyin/shell/open/command/
    testhuseyin/shell/open/command/(Default) = "%my_huo_user_variable%testhuseyin.exe" "%1"
    
  • Test again:

Advanced : Use creation script

  • Open notepad and save the below script to testhuseyin2.reg and execute it by clicking on it in explorer.
    REGEDIT4
    
    
    [HKEY_CLASSES_ROOT\testhuseyin2]
    @="URL:Testhuseyin2 Protocol"
    "URL Protocol"=""
    
    [HKEY_CLASSES_ROOT\testhuseyin2\DefaultIcon]
    @="testhuseyin.exe,1"
    
    [HKEY_CLASSES_ROOT\testhuseyin2\shell]
    
    [HKEY_CLASSES_ROOT\testhuseyin2\shell\open]
    
    [HKEY_CLASSES_ROOT\testhuseyin2\shell\open\command]
    @="\"C:\\testing\\testhuseyin.exe\" \"%1\""
    

  • Final result should be:
    testhuseyin2/
    testhuseyin2/(Default) = URL:Testhuseyin2 Protocol
    testhuseyin2/URL Protocol =
    testhuseyin2/DefaultIcon/
    testhuseyin2/DefaultIcon/(Default) = testhuseyin.exe,1
    testhuseyin2/shell/
    testhuseyin2/shell/(Default) =
    testhuseyin2/shell/open/
    testhuseyin2/shell/open/(Default) =
    testhuseyin2/shell/open/command/
    testhuseyin2/shell/open/command/(Default) = "C:\\testing\\testhuseyin.exe" "%1"
    
  • Test again:

Important : URI links in mails

Some messaging providers (ex GOOGLE) delete the URI links (ex: <a href=”testhuseyin://mydata”>My URI link</a>) in the mails and allow only the http:// and https:// links (and NOTES links notes://). More, the javascript code in the email cannot be executed (CODE visible via the “Original Show” action, take the HTML and check the page “https://www.w3schools.com/js/tryit.asp?filename=tryjs_alert” ). The FORMs towards a URI are present but disabled (anti hack / fishing) (CODE visible via the action “Original Show” take the HTML and check in the page “https://www.w3schools.com/js/tryit. asp? filename = tryjs_alert “).

In our example: we use the URI testhuseyin in order to execute an action in an local application. So, the solution will be the replacement of URI link by HTTP link URL to a simple HTML page.


<html>
<head>

</head>
<body style=’font-family:Arial;font-size:10pt;’>

<script type=’text/javascript’>

// https://stackoverflow.com/questions/979975/how-to-get-the-value-from-the-get-parameters
function parse_query_string(query) {
var vars = query.split(“&”);
var query_string = {};
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split(“=”);
var key = decodeURIComponent(pair[0]);
var value = decodeURIComponent(pair[1]);
// If first entry with this name
if (typeof query_string[key] === “undefined”) {
query_string[key] = decodeURIComponent(value);
// If second entry with this name
} else if (typeof query_string[key] === “string”) {
var arr = [query_string[key], decodeURIComponent(value)];
query_string[key] = arr;
// If third or later entry with this name
} else {
query_string[key].push(decodeURIComponent(value));
}
}
return query_string;
}

// https://stackoverflow.com/questions/51033111/how-to-display-result-of-javascript-as-html-link
// defining a function to create a link node, however this isn’t neccessary,
// you could just hard code the logic above.
// I wouldn’t recommend setting innerHtml in lieu of making a text node however.
function createLinkNode(url, parent) {
const linkTextNode = document.createTextNode(url);
const linkNode = document.createElement(‘a’);
linkNode.href = url;
//linkNode.target = ‘_blank’;
linkNode.target = ‘_self’;
linkNode.appendChild(linkTextNode);
parent.appendChild(linkNode);
}

function getBaseURL() {
var getUrl = window.location;
var baseUrl = getUrl.pathname; //getUrl .protocol + “//” + getUrl.host + “/” + getUrl.pathname.split(‘/’)[1];
return baseUrl;
}

window.onload = function() {
var query = window.location.search.substring(1);

if(query){
var qs = parse_query_string(query);

// https://stackoverflow.com/questions/246801/how-can-you-encode-a-string-to-base64-in-javascript
// var str = btoa(‘testhuseyin://MyAction?param1=val1&param2=val2&param3=val3’);
//alert(str);
var str = qs.q;
var str2 = atob(str);
//alert(str2);

//SOLUTION 1 :OK just there is no closing of the opened page
// https://www.w3schools.com/jsref/met_win_open.asp
// similar behavior as clicking on a link
window.open(str2, ‘_self’, ”);
//window.location.href = str2;
//window.open(”, ‘_self’, ”);
//window.close();

//SOLUTION 2 : NOK AJAX ERROR BLOCKING URIs (need to configure CHROME..etc.)
/* var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
window.open(”, ‘_self’, ”);
window.close();
}
};
xhttp.open(“GET”, str2, true);
xhttp.send();
*/

//SOLUTION 3 : NOK USE WORKER/THREAD JAVASCRIPT ERROR URI BLOCKING

// TEST
/* open(str2, ‘_self’);
window.open(”, ‘_parent’, ”).close()

var h = 1231;
var mywindow = mywindow.open(”, ‘_self’, ”);
mywindow = window.open(str2, ‘_self’, ”);
var i = 1231;
mywindow.opener = self;
mywindow.close(”,’_self’,”);
mywindow.close();
*/

}//end-if

/* HUO : links for testing */
{
// H1 text
var h = document.createElement(“H1”) // Create a <h1> element
var t = document.createTextNode(“testhuseyin://MyAction”); // Create a text node
h.appendChild(t); // Append the text to <h1>
document.body.appendChild(h);
// LINK A HREF
var str = btoa(‘testhuseyin://MyAction?param1=val1&param2=val2&param3=val3′);
//alert(str);
const link = getBaseURL() + “?q=”+str; //window.location + “?q=”+str;
createLinkNode(link, document.body);
}

}//end-window.onload

</script>

<h1>
testhuseyin : You can close this page…
</h1>
<!–
<a target=”_self” href=”C:/Users/myuser/Desktop/convertBase64AndRedirectToURI.html?q=dGVzdGh1c2V5aW46Ly9NeUFjdGlvbj9wYXJhbTE9dmFsMSZwYXJhbTI9dmFsMiZwYXJhbTM9dmFsMw==”>
C:/Users/myuser/Desktop/convertBase64AndRedirectToURI.html?q=dGVzdGh1c2V5aW46Ly9NeUFjdGlvbj9wYXJhbTE9dmFsMSZwYXJhbTI9dmFsMiZwYXJhbTM9dmFsMw==
</a>

<a href=”testhuseyin://MyAction?param1=val1&param2=val2&param3=val3″>My Document</a>

<form action=’testhuseyin://MyAction’>
<input type = ‘hidden’ name=’param1′ value=’val1’/>
<input type = ‘hidden’ name=’param2′ value=’val2’/>
<input type = ‘hidden’ name=’param3′ value=’val3’/>
<input type =’submit’ value=’Go’/>
</form>
–>

</body></html>


  • Replacement of the URI links or FORM by an HTTP link to the previous HTML page with the “q” parameter containing the BASE64 value of the URI:
    
    

    <a href=”testhuseyin://MyAction?param1=val1&param2=val2&param3=val3″>My Document</a>

    <form action=’testhuseyin://MyAction’>
    <input type = ‘hidden’ name=’param1′ value=’val1’/>
    <input type = ‘hidden’ name=’param2′ value=’val2’/>
    <input type = ‘hidden’ name=’param3′ value=’val3’/>
    <input type =’submit’ value=’Go’/>
    </form>

    testhuseyin://MyAction?param1=val1&param2=val2&param3=val3

    https://myserver/mycontext/convertBase64AndRedirectToURI.html?q=dGVzdGh1c2V5aW46Ly9NeUFjdGlvbj9wYXJhbTE9dmFsMSZwYXJhbTI9dmFsMiZwYXJhbTM9dmFsMw==

    TESTS base64 : https://www.base64encode.net/ and https://www.base64decode.org/

    
    

That’s all!!!

Huseyin OZVEREN