Install Manager Solution

•August 13, 2012 • Leave a Comment

Examine the solution

IMBootstrapper

This is the bootstrapper project. I started off using the the setup sample in the Windows SDK and extended it to include the .Net FW 4 net installer.

InstallerGUI and InstallerGUI2

These two projects are two samples of a WPF UI based installer. InstallerGUI is a wizard based UI and it is using the wizard control from WPF Extended Toolkit. The InstallerGUI2 is a custom UI to demonstrate MSI install and uninstall.

Models

The Models Project contains some diagrams bout the solution.

SampleApplication

This is the application the MSI will install.

ApplicationSetup

This is the project that creates the MSI file that installs the SampleApplication.

Embedding relationships:

The Bootstrapper project must embed the WPF installer. When it finds that all pre-requisites are installed it will extract the WPF installer to a temp location and start it.

The WPF installer must embed the MSI. It will extract it to a temp location and manage its execution internally.

To effectively manage the embedding relationships among project outputs the solution contains a Drop folder where these project outputs are copied by a postbuild event. In addition these items are added on solution level under the Drop solution folder. These items are added “As Link” to individual projects pointing to their Drop folder copy.

To prevent the XCOPY errors of the DropReadonly.vbs makes sure that even if checked in these files are re-writable on build.

Advertisements

MSI InstallManager Library

•August 13, 2012 • Leave a Comment

I have created a codeplex project from a tool I needed a while back the InstallManager library.

MSI Install Manager is an API that allows you to install an MSI within a .NET application. You can create a WPF install experience and drive the execution of the MSI within.
(In fact you can use command line, Winforms, or any other .NET capable client )

The project contains

  • a reference implementation of a .NET 4.0 wrapper over the native MSI API. For more information about the Windows Installer API see MSDN.
  • a reference implementation of a bootstrapper project that ensures that .NET FW 4.0 is installed as pre-requisite.
  • a sample WPF application that shall be deployed by the msi file.
  • sample msi setup project that deploys the sample application
  •  2 sample WPF installer applications that can be extracted and run by the bootstrapper, These extract and govern the execution of the MSI.

Running the original msi:

Installing via sample WPF UI:

Download get the InstallManager Source

You can get the latest source code from http://msiinstallmanager.codeplex.com/

I’m going to gather all the related links about the project here:

Pareto is right again.

•August 3, 2012 • Leave a Comment

After being busted on a toggle switch, I started to cover edge scenarios for my WP7.1 project.

Stuff like:

  1. Is the Location Services enabled
  2. Is there an active network connection
  3. Is there a WIFI connection.

Turns out the best solution for all of these is a different user experience, nobody wants their users to wait for the timeout events, even if it is covered up by a nice busy indicator, etc…

Turns out the three new corner case needs at least as much work as the normal use case and definitely more than the development of just the main functionality.

The ratio is probably not 80-20, but it is getting closer.

Free TFS online

•August 3, 2012 • Leave a Comment

In case anyone was wondering how to bring the “enterprise” feeling to home projects, Microsoft has launched https://tfspreview.com/ a good while ago.

I have subscribed a good while back and it is still free, no bugs on my project, good performance.

MS will ask for a fee later when the preview phase is over, but the pricing model is still under construction.

Win phone app busted!

•July 23, 2012 • Leave a Comment

My new win phone app got rejected from the windows market place because there is no option to turn off the functionality based on geo-location.

My problem is that the whole app is based on geo-location. So if I turn it off the app is kinda useless.

Well anyway, I have to go ahead and implement this “feature” or ask for exemption.

Well.. haven’t decided yet…

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

•July 4, 2012 • Leave a Comment

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.

MVVM reloaded

•May 28, 2012 • Leave a Comment

I have just noticed a discussion on a small article at InfoQ: http://www.infoq.com/news/2011/12/mvvm-frameworks-net
Jonathan Allen lists a couple of signs that your MVVM design may have gone wrong. Here are the points briefly:

1. You have a model and a view-model with the same name.
2. You have a view and a view-model with the same name.
3. You have no code behind.
4. View-models are listening to property changed notifications

I don’t want to defend or argue about these points, rather it got me realized that I also had some concepts wrong at the start.

Graphics is a designer issue.
XAML separates the design and development processes completely. Isn’t this that would bring tremendous advantages after all. The dream is that Graphic Designers work on XAML and developers work on code. First of all, Graphic designers did not learn the new tool, language. The reason is that the concepts of graphic designs are so different from the concepts of application UI that designers can’t work properly in this environment. Layers for example are in every GD tool, yet they are missing as a concept from XAML, HTML etc… (You can workaround it, but that need dev skills 🙂 ).

No Code behind
UI related logic such as how often a blinky icon blinks or when the tutorial animation kicks in should go into the XAML code behind in my personal opinion. When I have a list box, and the items do animations depending on their type and some random timing, calculating when an animation should start and starting it should nicely fit into the code behind of the control. Obvoiusly if you need this behaviour more than one time you should create a proper Custom Control, but for just one instance it may be an overkill.
I did think at some point that the fingerprint of a good architecture is that there’s no code behind the XAML. Even though XAML is capable of much, UI logic is best described and maintained as usually logic is described and maintained, that is, in code, but not deeper than the XAML code behind. To put this very simply references to controls should only exist in XAML code behind. By delegating control references to ViewModels via commanding may be leaky nothing guarantees that the lifetime of the View and the ViewModel is the same, in reality the ViewModel is likely to hang around after the view is not visible on the screen.

Not to mention that the ViewModel should be View agnostic, so should never refer to a Button, or a Combobox.

View-ViewModel: 1-1
Usually a developer starts to create a UserControl (XControl) and with the same length of breath they create its ViewModel (XControlViewModel).

If we accept that the ViewModel should be View agnostic, the relationship should be at least *-1 and the name of the ViewModel should indicate that. So are we better of with the concept of EmployeeViewModel that reflects the Model type rather than the UI type.

No, not really. ViewModels should be more like facets of usage on the Model. I think it is a new conceptual layer that groups model elements and functional elements of related use cases together. I should really give you a good example at this point. So let’s take the example of the Model class Employee, you can have an EmployeeDetailViewModel where every aspects of the employees can be changed and viewed, and you could have an EmployeeSummaryViewModel where only main attributes can be changed, but also contains information of how this employee compares to other employees. I admit even in this scenario you probably end up with an EmployeeDetailControl, and an EmployeeSummaryControl, but the ViewModels should not be exclusive to them.