Tuesday, 17 April 2012

Dealing with User Account Control (UAC)

While testing the implementation of my previous blog, Installshield Automation Using c#, a build error number -6017 occurred. This error states that COM information cannot be extracted from the COM server in the project. The only reason this could happen is because I ran the IS project without administrative privileges.

So this brought me to my new blog topic ‘UAC’.

With the arrival of Vista, windows 7 and Server 2008 and having developed applications that run on the those platforms, one thing that I have consistently dealt with is, permissions to resources. Reason being the new technology namely, UAC, which was introduced by Microsoft on the latest windows releases. The main purpose of this feature is to protect the OS by running applications with reduced privileges.

UAC has two dialogs; A blue one which indicates that the application is trusted and signed.


A yellow dialog that show that your application is not digitally signed and it is not fully trusted.


User Account Control prevent low privilege applications from doing the following :
  1. Perform a window handle validation of higher process privilege.
  2. SendMessage or PostMessage to higher privilege application windows. These Application Programming Interfaces (APIs) return success but silently drop the window message.
  3. Use thread hooks to attach to a higher privilege process.
  4. Use Journal hooks to monitor a higher privilege process.
  5. Perform DLL injection to a higher privilege process.
src: http://msdn.microsoft.com/en-us/library/aa905330.aspx


In my development, how do I deal UAC it?
There are several methods  that can be used:
1.     Including a UAC manifest that will cause the application to request administrative privileges at start up:

a.    To add a manifest file in VS2008, just right click on your solution then from the menu choose add>new Item. Now from the new Item dialog box select the “Application Manifest File”. Edit the file as follows.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level=" requireAdministrator”    uiAccess="false"/>
        </requestedPrivileges>
       </security>
  </trustInfo>
</assembly>

b.     Under VS2010 it is a little different. Right click the Project and select Properties. Select the "Application" tab and then click "View Windows Settings". This opens the manifest, and then you can make the changes you need. VS2008 procedure works too.

     2. The second method is to isolate the part of your code that requires elevated privileges into an application that uses a UAC manifest to require administrator privileges. Your application does not need to run as admin, when these privileges are required you should invoke the external application. Here is some code prototype you could use.



    using System.Security.Permissions;
    using System.Diagnostics;

            ProcessStartInfo processInfo = new ProcessStartInfo();
            processInfo.Verb = "runas";
            processInfo.FileName = [Add filename here];
            Process.Start(processInfo);

     If you would like your application to behave differently depending on if the current user has admin rights, you can use the code below;

        WindowsIdentity identity = WindowsIdentity.GetCurrent();
        if (identity != null)
        {
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }

The code will return true if administrative and false otherwise.



Friday, 13 April 2012

InstallShield Automation Using c#


For the last few days I did a quick search for a solution to automate my nightly Installshield MSI builds . After going through a few code snippets developed in VBS etc, I decided to create my own using C# which I would like to share.

To start with you need to create a C# project then add a reference for Installshield Automation Library, can be locate at C:\Program Files\InstallShield\[Version]\. Depending on the IS version you are using, the Library name may vary.

It would be more present if Installshield provided a factory to serve up the different automation interface versions since this already presents a challenge of the code being version dependent. In my solution here am using IS 2011 whose reference appears as ISWiAuto17.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ISWiAuto17;


namespace xxx
{
    public delegate void LogStatus(String smessage);
    public delegate void logprogress(int lIncrement);

    class InstallShieldAuto
    {
        private string sProjectFileName;
        private string sISExFilename;
        private string sBuildLocation;
        private string sBuildNumber;


        public event LogStatus log;
        public event logprogress progress;

        public InstallShield() {}

        public InstallShield(string projectName, string isexFileName, string buildLocation, string buildNumber)
        {
            sProjectFileName = projectName;
            sISExFilename = isexFileName;
            sBuildLocation = buildLocation;
            sBuildNumber = buildNumber;
        }


        /// <summary>
        /// Holds the location where the ISM project
        /// </summary>
        public string ProjectFile{
            get{
                return sProjectFileName;

            }
            set {
                sProjectFileName = value;
           
            }
        }
        /// <summary>
        /// Holds the Install Shield Executable file location
        /// </summary>
        public string ISExecutableFile
        {
            get
            {
                return sISExFilename;

            }
            set
            {
                sISExFilename = value;

            }
        }

        /// <summary>
        /// Holds the release location
        /// </summary>
        public string BuildLocation
        {
            get
            {
                return sBuildLocation;

            }
            set
            {
                sBuildLocation = value;

            }
        }
        /// <summary>
        /// Holds the build number
        /// </summary>
        public string BuildNumber
        {
            get
            {
                return sBuildNumber;

            }
            set
            {
                sBuildNumber = value;

            }
        }


        /// <summary>
        /// execute the build process
        /// </summary>
        public void BuildProject()
        {
            ISWiProductConfigs oProdConfigs;
            ISWiProductConfig oProdConfig;
            ISWiRelease oRelease= new ISWiRelease();
            ISWiProject oISWiProj = new ISWiProject();
            try
            {

                //Create IS object
                oISWiProj.OpenProject(sProjectFileName, false);
                oISWiProj.ProductVersion = sBuildNumber;

                oProdConfigs = oISWiProj.ISWiProductConfigs;
                oProdConfig = oProdConfigs[oProdConfigs.Count];


                oRelease = oProdConfig.ISWiReleases[1];

                oRelease.ProgressIncrement += new __ISWiRelease_ProgressIncrementEventHandler(release_ProgressIncrement);
                oRelease.StatusMessage += new __ISWiRelease_StatusMessageEventHandler(this.release_StatusMessages);

                oRelease.BuildLocation = sBuildLocation;
                oRelease.Build();

                 oISWiProj.SaveProject();

                 oISWiProj.CloseProject();
            }
            catch
            {
                log("Build Failed...");
            }
            finally {
                oRelease.ProgressIncrement -= new __ISWiRelease_ProgressIncrementEventHandler(release_ProgressIncrement);
                oRelease.StatusMessage -= new __ISWiRelease_StatusMessageEventHandler(this.release_StatusMessages);
            }
        }
        private void release_ProgressIncrement(int lIncrement, ref bool pbCancel)
        {
            progress(lIncrement);
        }
        private void release_StatusMessages(string sMessage, ref bool pbCancel)
        {
            log(sMessage);
        }
    }
}


As you can see there is nothing much of the code is self-explanatory, Let’s expound on some of the major area in the  Buildproject() method.
1.    Declare and open your project
                        oISWiProj.OpenProject(sProjectFileName, false);

2.    Retrive all the available configurations and pick the right configuration, in this case I have used the last one.
oProdConfigs= oISWiProj.ISWiProductConfigs;
          oProdConfig = oProdConfigs[oProdConfigs.Count];

3.    From the selected configuration, you will have a collection of  releases, pick the release want by providing the index. In my case index one [1] is Release 1.
oRelease = oProdConfig.ISWiReleases[1];

4.    As the build progresses, the process provides status messages and the progress value through events.  
                oRelease.ProgressIncrement += new __ISWiRelease_ProgressIncrementEventHandler(release_ProgressIncrement);
                oRelease.StatusMessage += new __ISWiRelease_StatusMessageEventHandler(this.release_StatusMessages);


5.    Build the release then save ad close the project.
oRelease.Build();
oISWiProj.SaveProject();
              oISWiProj.CloseProject();

One thing that I still haven’t figured out is how to use the names to pick out he configuration and the release from the collection. Once I have that ready I will post my finding but for now I will make do with the indexes