Web Content Driven App (Part 1): How to detect that your application is installed via JavaScript

OK, so you have written a cool app that is driven by content on the web, something like special RSS feeds.

How do you make the deploy and start of the application as seamless as possible for the user? You want basically a 1 click-install, run experience from a website.

Step 1.: Navigate to the webpage
Step 2.:  Click On a Link
Step 3.a :
If the application is not yet installed for the user the link offers a download of an exe, when running this it will install the application and then run the application for the specified web content.

Step 3.b :
If the  application is not installed the link would just launch the application on that particular content.

There are a couple of problems to tackle:

1.  How do you decide that your application is already installed from the webpage?

2. How do you let the downloaded exe know (that runs later and independent of its source of origin) which web content it should consume.

3. How do you launch a the installed application from an html page.

At least these are the ones you may start with.

Let’s deal with the first one first:

Obviously we do not want to use any intrusive solution so we  do not want to use flash, applets, Silverlight, especially any embedded technology means extra pre-requisites. What we need is a simple few lines of JavaScript returns true if the app is installed and false if not.

After going through the net you’ll find a bunch possibilities. The one I like best is when the application installs a new URI scheme and then the JavaScript tries to open a new window. The JavaScript then tries to catch the window’s blur event, if it manages to catch it it closes it immediately, and reports success. Otherwise catching exception on the window open means that the URI scheme is not supported. My main problem with this apart from the trial and error part, is that it uses a piece of functionality (opening a new window) for something it has never been meant to. (Not to mention what pop-up blockers may cause to this approach.)

So realizing that the only way for an application to signal its presence to The Browsers (IE, Chrome, FF etc…) is if it installs a browser plugin. The list of plugins then can be iterated in JavaScript and if our plugin is found in that list it is safe to assume that our application is already installed. At least it would be true for all most browsers except IE where this list is not available. On IE you need to do the same with an ActiveX object.

SO here’s a step by step guide on how to do this the easiest way I found:

  1. Create a Basic FireBreath plugin let’s call it AppDetectPlugin for now. There’s a perfect tutorial on how to do it.

    The good bit about the FireBreath implementaiton is that it already provides you a plugin for all Mozilla and WebKit based browsers and an ActiveX for  IE, and all these can be installed using one simple command line.

  2. Implement some basic function on your new plugin let’s say SecretCode(); this method will verify the identity of your plugin.[optional]

  3. Make sure installing your npAppDetectPlugin.dll in your setup project.

    Make sure you are deploying the dll in the setup project, then you can use similar code to install and uninstall it in a custom installer. (Don’t forget to hook the custom action up in the setup project with the proper arguments: /TargetDir=”[TARGETDIR]\”)

    private void AppCustomInstaller_AfterInstall(object sender, InstallEventArgs e)
            {
                try
                {
                    string targetdir = this.Context.Parameters["TargetDir"];
                    if (String.IsNullOrWhiteSpace(targetdir)) return;
                    //registering plugin
                    string dllPath = Path.Combine(targetdir, "npAppDetectPlugin.dll");
                    if (File.Exists(dllPath))
                    {
                        ProcessStartInfo startInfo = new ProcessStartInfo("RegSvr32.exe", "/s " + "\"" + dllPath + "\"");
    
                        startInfo.UseShellExecute = true;
                        startInfo.CreateNoWindow = true;
                        startInfo.Verb = "runas";
                        Process process = new Process() { StartInfo = startInfo, };
                        process.Start();
                    }
                }
                catch (Exception ex)
                {
    
                    //MessageBox.Show(ex.ToString());
                }
    
            }

    private void AppCustomInstaller_BeforeUninstall(object sender, InstallEventArgs e)
            {
                try
                {
                    string targetdir = this.Context.Parameters["TargetDir"];
                    if (String.IsNullOrWhiteSpace(targetdir)) return;
                    targetdir = targetdir.TrimEnd().TrimEnd('\\');
                    string dllPath = Path.Combine(targetdir, "npAppDetectPlugin.dll");
                    if (File.Exists(dllPath))
                    {
                        ProcessStartInfo startInfo = new ProcessStartInfo("RegSvr32.exe", "/u /s " + "\"" + dllPath + "\"");
    
                        startInfo.UseShellExecute = true;
                        startInfo.CreateNoWindow = true;
                        startInfo.Verb = "runas";
                        Process process = new Process() { StartInfo = startInfo, };
                        process.Start();
                    }
                }
                catch (Exception ex)
                {
                    //MessageBox.Show(ex.ToString());
                }
            }
  4. Write JavaSscript that detects your plugin

    function AppDetected() 
    {
        if (jQuery.browser["msie"]) 
        {
    
            return DetectActiveX();
    
        } else 
        {
    
            return DetectPlugin();
        }
        return false;
    }
    
    function DetectActiveX() {
        try {
            if (IsIE9()) {
            try{
                var ao = new ActiveXObject("Company.AppDetectPlugin");
                if (ao != null || ao != "undefined") {
                    var x = ao.SecretCode();
                    return x == "SecretCode";
                }
                }
                catch (e)
                {}
                return false;
            }  
    
            var obj = document.createElement("object").type = "application/x-appdetectplugin";
            var retbool = obj != null || obj != "undefined";
    
            return obj != null || obj != "undefined";
    
        }
        catch (e)
        { 
        }
        return false;
    }
    
    function IsIE9() {
        return getInternetExplorerVersion() >= 9.0;
    }
    
    function getInternetExplorerVersion()
    // Returns the version of Internet Explorer or a -1
    // (indicating the use of another browser).
    {
        var rv = -1; // Return value assumes failure.
        if (navigator.appName == 'Microsoft Internet Explorer') {
            var ua = navigator.userAgent;
            var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
            if (re.exec(ua) != null)
                rv = parseFloat(RegExp.$1);
        }
        return rv;
    }
    
    function DetectPlugin() {
        try {
            if (navigator.plugins && navigator.plugins.length > 0) {
                for (var i = 0; i < navigator.plugins.length; i++) {
                    var plugin = navigator.plugins[i];
                    var pluginName = plugin.name;
                    if (pluginName.indexOf("App Detect Plugin") > -1) { return true }
                }
            }
        } catch (e)
        { }
        return false;
    }
    For IE we actually try to instantiate the ActiveX object and call the SecretCode() to verify it’s identity. This may be an overkill though.
    For non IE browsers it is enough to iterate  through the plugins array.
Advertisements

~ by baloghp on July 4, 2012.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: