Archive

Archive for the ‘Code’ Category

PayPalMerchantSDK, integrate paypal into your mvc 4 website

September 20th, 2013 2 comments

The following outlines some of the things that I have learned whilst trying to integrate a PayPal payment gateway into the MVC 4.0 website.

Installation

After much reading I determined that I easiest way was to use the PayPaylMerchantSDK.
This is implemented using the PayPal classic API which is the only API available in Australia @ the time of writing.
The easiest way to add this to your application is to use NuGet in Visual Studio (Tools ->> Library Package Manager ->> Manage NuGet Packages for Visual Studio) and search for PayPal.
Click on the PayPalMerchantSDK and choose Install. This will download the dependencies including log4net and the “PayPal Core SDK for .Net”

Code Samples

You should download and use the “Merchant” solution in the .net code samples that can be found here.
Classic API .Net code samples

App.Config

The code samples have an App.Config file that contains the base configuration for working with the API.
It contains the log4net and PayPal configuration section. You should copy these configuration sections into your web.config file.

Access the paypal settings section in the web.config

When coding I want to access some of the PayPal settings that I just copied into my web.config file, for example “paypalUrl” could be used when attempting to send a request for express checkout.

To access the paypal configuration settings that are in the web.config use the following code:

  string url = PayPal.Manager.ConfigManager.Instance.GetProperties()["paypalUrl"];

IPN Testing

IPN Simulator

I thought that it was dangerous opening port 80 through the firewall to get to my development server, so I was clever and used an unusual port number. This worked fine for most things but the IPN testing would not work with my special port number.

You have to use port 80 to allow the IPN test to work.

Core .SDK

The old way of implementing the IPN callback has been integrated into the PayPal .Core SDK.
IPNMessage.cs

This can now be implemented in 3 lines of code.

Put this inside your controller page

public ActionResult IPN()
{
	byte[] bytes = Request.BinaryRead(Request.ContentLength);

	PayPal.IPNMessage message = new PayPal.IPNMessage(bytes);
	if (message.Validate())
	{
		// message returned VERIFIED
	} 
	else 
	{
		// There was a problem
	}
	
	return null;
}

Note I then created a PayPalTransaction Class to simplify access to the PayPal.IPNMessage object.

It looked something like this.

public class PayPalTransaction
    {
        
        #region Constants

        private const string KEY_ADDRESSSTATE = "address_state";
        private const string KEY_TXNID = "txn_id";
        private const string KEY_LASTNAME = "last_name";
        private const string KEY_CURRENCY = "mc_currency";
        private const string KEY_PAYERSTATUS = "payer_status";
        private const string KEY_ADDRESSSTATUS = "address_status";
        private const string KEY_TAX = "tax";
        private const string KEY_INVOICE = "invoice";
        private const string KEY_PAYEREMAIL = "payer_email";
        private const string KEY_FIRSTNAME = "first_name";
        private const string KEY_BUSINESS = "business";
        private const string KEY_VERIFYSIGN = "verify_sign";
        private const string KEY_PAYERID = "payer_id";
        private const string KEY_PAYMENTDATE = "payment_date";
        private const string KEY_PAYMENTSTATUS = "payment_status";
        private const string KEY_RECEIVEREMAIL = "receiver_email";
        private const string KEY_PAYMENTTYPE = "payment_type";
        private const string KEY_ADDRESSNAME = "address_name";
        private const string KEY_ADDRESSSTREET = "address_street";
        private const string KEY_ADDRESSZIP = "address_zip";
        private const string KEY_ADDRESSCITY = "address_city";
        private const string KEY_ADDRESSCOUNTRY = "address_country";
        private const string KEY_ADDRESSCOUNTRYCODE = "address_country_code";
        private const string KEY_SHIPPING = "mc_shipping";
        private const string KEY_ITEMNUMBER1 = "item_number1";
        private const string KEY_SHIPPING1 = "mc_shipping1";
        private const string KEY_ITEMNAME1 = "item_name1";
        private const string KEY_HANDLING1 = "mc_handling1";
        private const string KEY_GROSS1 = "mc_gross1";
        private const string KEY_GROSS = "mc_gross";
        private const string KEY_FEE = "mc_fee";
        private const string KEY_RESIDENCECOUNTRY = "residence_country";
        private const string KEY_NOTIFYVERSION = "notify_version";
        private const string KEY_RECEIVERID = "receiver_id";
        private const string KEY_HANDLING = "mc_handling";
        private const string KEY_TXNTYPE = "txn_type";
        private const string KEY_CUSTOM = "custom";
        private const string KEY_TESTIPN = "test_ipn";

        private const string KEY_IPNTRACKID = "ipn_track_id";
        private const string KEY_PROTECTION_ELIGIBILITY = "protection_eligibility";

        private const string KEY_QUANTITY = "quantity";
        private const string KEY_RESEND = "resend";
        private const string KEY_NOTIFY_VERSION = "notify_version";
        
        
        #endregion 

        #region Constructor

        public PayPalTransaction()
        {
        }

        public PayPalTransaction(PayPal.IPNMessage message)
        {
            this.TransactionId = message.IpnValue(KEY_TXNID);
            this.TransactionType = message.IpnValue(KEY_TXNTYPE);
            this.PayerId = message.IpnValue(KEY_PAYERID);
            this.Currency = message.IpnValue(KEY_CURRENCY);
            this.Custom = message.IpnValue(KEY_CUSTOM);
            this.HandlingAmount = GetAsDecimal(message, KEY_HANDLING);
            this.FirstName = message.IpnValue(KEY_FIRSTNAME);
            this.IpnTrackId = message.IpnValue(KEY_IPNTRACKID);
            this.LastName = message.IpnValue(KEY_LASTNAME);
            this.PayerStatus = message.IpnValue(KEY_PAYERSTATUS);
            this.PaymentDate = GetAsDate(message, KEY_PAYMENTDATE);
            this.PaymentStatus = message.IpnValue(KEY_PAYMENTSTATUS);
            this.ProtectionEligibility = message.IpnValue(KEY_PROTECTION_ELIGIBILITY);
            this.ShippingAmount = GetAsDecimal(message, KEY_SHIPPING);
            this.GrossAmount = GetAsDecimal(message, KEY_GROSS);
            this.FeeAmount = GetAsDecimal(message, KEY_FEE);

            this.Quantity = GetAsInt(message, KEY_QUANTITY);
            this.Resend = GetAsBool(message, KEY_RESEND);
            this.PaymentType = message.IpnValue(KEY_PAYMENTTYPE);
            this.NotifyVersion = message.IpnValue(KEY_NOTIFY_VERSION);
            this.PayerEmail = message.IpnValue(KEY_PAYEREMAIL);
            this.TaxAmount = GetAsDecimal(message, KEY_TAX);
            this.ResidenceCountry = message.IpnValue(KEY_RESIDENCECOUNTRY);

            this.ItemNumber1 = message.IpnValue(KEY_ITEMNUMBER1);
            this.ItemName1 = message.IpnValue(KEY_ITEMNAME1);
            this.ShippingAmount1 = GetAsDecimal(message, KEY_SHIPPING1);
            this.HandlingAmount1 = GetAsDecimal(message, KEY_HANDLING1);
            this.GrossAmount1 = GetAsDecimal(message, KEY_GROSS1);

            this.AddressCity = message.IpnValue(KEY_ADDRESSCITY);
            this.AddressCountryCode = message.IpnValue(KEY_ADDRESSCOUNTRYCODE);
            this.AddressCountry = message.IpnValue(KEY_ADDRESSCOUNTRY);
            this.AddressName = message.IpnValue(KEY_ADDRESSNAME);
            this.AddressStreet = message.IpnValue(KEY_ADDRESSSTREET);
            this.AddressZip = message.IpnValue(KEY_ADDRESSZIP);

        }

        #endregion

        #region Private Helpers

        private int GetAsInt(PayPal.IPNMessage m, string key)
        {
            int value = Convert.ToInt32(m.IpnValue(key));
            return value;
        }

        private bool GetAsBool(PayPal.IPNMessage m, string key)
        {
            bool bln = false;
            string value = m.IpnValue(key);
            if (value != null)
            {
                bln = (value.ToLower().Trim() == "true");
            }
            return bln;
        }

        private decimal GetAsDecimal(PayPal.IPNMessage m, string key)
        {
            decimal value = Convert.ToDecimal(m.IpnValue(key));
            return value;
        }

        private DateTime GetAsDate(PayPal.IPNMessage m, string key)
        {
            string value = m.IpnValue(key);
            value = HttpUtility.HtmlDecode(value);

            int iPos = value.LastIndexOf(" ");
            string timezone = value.Substring(iPos+1);
            TimeZoneInfo tzSource = null;
            
            value = value.Substring(0, iPos);

            switch (timezone)
            {
                case "PST":
                case "PDT":
                    tzSource = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
                    break;
            }
            
            DateTime date = Convert.ToDateTime(value);
            date = TimeZoneInfo.ConvertTimeToUtc(date, tzSource);
            date = TimeZoneInfo.ConvertTimeFromUtc(date, TimeZoneInfo.Local);

            return date;
        }

        #endregion

        #region Properties

        public int Id { get; set; }

        /// <summary>
        /// The transaction Id of the PayPal Transaction
        /// </summary>
        public string TransactionId { get; private set; }

        public string TransactionType { get; private set; }

        public string PayerId { get; private set; }

        public string IpnTrackId { get; private set; }

        public string Currency { get; private set; }

        public string Custom { get; private set; }

        public string FirstName { get; private set; }

        public decimal HandlingAmount { get; private set; }

        public string LastName { get; private set; }

        public string ProtectionEligibility { get; private set; }

        public string PayerStatus { get; private set; }

        public DateTime PaymentDate { get; private set; }

        public string PaymentStatus { get; private set; }

        public int Quantity { get; private set; }

        public bool Resend { get; private set; }

        public string PaymentType { get; private set; }

        public string NotifyVersion { get; private set; }

        public string PayerEmail { get; private set; }

        public string ResidenceCountry { get; private set; }

        public string AddressName { get; private set; }
        
        public string AddressStreet { get; private set; }
        
        public string AddressZip { get; private set; }
        
        public string AddressCity { get; private set; }
        
        public string AddressCountry { get; private set; }

        public string AddressCountryCode { get; private set; }

        public decimal ShippingAmount { get; private set; }

        public decimal GrossAmount { get; private set; }

        public decimal FeeAmount { get; private set; }

        public decimal TaxAmount { get; private set; }

        public decimal ShippingAmount1 { get; private set; }

        public decimal GrossAmount1 { get; private set; }

        public decimal HandlingAmount1 { get; private set; }

        public string ItemNumber1 { get; private set; }

        public string ItemName1 { get; private set; }
    
        #endregion

        #region Virtual Properites

        public virtual bool IsComplete
        {
            get
            {
                bool value = (this.PaymentStatus.ToLower().Trim() == "completed");
                return value;
            }
        }
        #endregion

    }

Links

  • Code Samples
  • .Net Merchant Samples Just click to download the entire thing as a zip file
    • VN:F [1.9.22_1171]
      Rating: 9.3/10 (3 votes cast)

Categories: c#, Code Tags:

What is the best unique computer identifier?

June 28th, 2012 2 comments

I have been trying to find out what is the best way to uniquely identify a computer for licensing purposes.

Here are some suggestions.

Computer SID

In a previous post “Read computer sid on 64-bit machine” I show how to get the Product ID of the operating system as the unique identifier.

Cons
This seemed great and I even used it for a licensing tool until I installed the software on multiple virtual machines!
The VM’s were cloned from the same base image and guess what? They all had the same Product ID, which meant that every computer passed in the same ID.

Network Card Mac address and other hardware items

These do provide a level of uniqueness but there are a few problems:

Cons

  1. Most computers these days have many network cards, USB wi-fi etc, so which card do you choose to get the MAC address from?
  2. Unless it’s in a laptop or onboard, NIC’s can easily be replaced.

What is the best unique computer identifier

Thinking logically you want to use an identifier that is likely to change the least so CPU’s, NIC’s, Hard Drives etc etc should all be rejected.

The one component that generally doesn’t change very often is the motherboard. The motherboard has a UUID (Univerally Unique Identifier), that can be read from the bios.

This value is unique in both physical computers and Virtual Machines.

The following code sample is a c# example of how to read the UUID from the motherboard BIOS using the System.Management.ManagementClass to read the Win32_ComputerSystemProduct

using System.Management;
...
...
public static string UUID
{
    get
    {
        string uuid = string.Empty;

        ManagementClass mc = new ManagementClass("Win32_ComputerSystemProduct");
        ManagementObjectCollection moc = mc.GetInstances();

        foreach (ManagementObject mo in moc)
        {
            uuid = mo.Properties["UUID"].Value.ToString();
            break;
        }

        return uuid;
    }
}

Note for a full reference of all the Win32 Management classes go here:
MSDN Win32 Classes

VN:F [1.9.22_1171]
Rating: 9.8/10 (10 votes cast)

Categories: c#, Code Tags:

How to read computer SID on 64-bit machine

June 26th, 2012 1 comment

To get a unique identifier for a computer the recommended key is “ProductId” that is accessible from the following registry key – HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion.

This is the value you see as “Product ID” on the properties screen of your computer.

To access this key can be a little problematic if you are attempting to read the “ProductId” sub-key on a 64-bit machine from a 32-bit application.

This is due to Registry Reflection or WOW64 as it is sometimes called.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384235%28v=vs.85%29.aspx

In short the 64-bit OS shows 2 logical views of certain portions of the registry on WOW64, one for 32-bit and one for 64-bit. In the case of the “ProductId” key it explicitly exists in the 64-bit view so the following code will fail to find the key, as when running inside the 32-bit application it accesses the 32-bit view of the registry and fails to find the key.

The following code will return an empty string

public static string ComputerSID
{
    get
    {
        string sid = string.Empty;

        string SIDKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
        RegistryKey key = Registry.LocalMachine.OpenSubKey(SIDKey);

        object keyValue = key.GetValue("ProductId");

        if (keyValue != null)
        {
            sid = keyValue.ToString();
        }

        key = null;
        keyValue = null;

        return sid;
    }
}

To make this work you have to explicitly request c# to open the 64-bit view of the registry.
The following code sample will work.

public static string ComputerSID
{
    get
    {
        string sid = string.Empty;
        string SIDKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";

        RegistryKey baseKey = null;
        if (Environment.Is64BitOperatingSystem)
        {
            baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
        }
        else
        {
            baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default);
        }

        RegistryKey key = baseKey.OpenSubKey(SIDKey);

        object keyValue = key.GetValue("ProductId");

        if (keyValue != null)
        {
            sid = keyValue.ToString();
        }

        key = null;
        keyValue = null;

        return sid;
    }
}

Cheers
John

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: c#, Code Tags:

Linq for Entity Join Query – Entity Framework

May 22nd, 2012 No comments

The Problem

You have a parent/child table structure in your database that looks like the table structure outlined below.

You have used the EntityFramework or Linq to SQL to build your data access layer.

Now you want to write a method using linq as follows:

public List<Child> GetChildren(string parentName)

The database

Create Table Parent (
  Id int identity(1,1) NOT NULL,
  ParentName varchar(50) NOT NULL,
  CONSTRAINT PK_Parent Primary Key Clustered (
    Id Asc
  )
);

Create Table Child (
   Id int identity(1,1) NOT NULL,
   ParentId int NOT NULL,
   ChildName varchar(50) NOT NULL,
   CONSTRAINT PK_Child Primary Key Clustered (
    Id Asc
  )
)

Obviously I would have foreign keys between parent and child.. you get the idea, this is your basic run of the mill one to many, parent child relationship.

In SQL

To get a list of all the children in sql your query would look something like:

select c.*
from Parent p 
    inner join Child c on c.ParentId = p.Id
Where p.ParentName = @searchString -- where @searchString is a variable 

The Method

I looked around the web to find a simple example of how to do this and I could not find one, that explained how to do an easy join syntax between two tables.

After much trial and error I came up with this:


public List<Child> GetChildren(string parentName)
{
    List<Child> children = null;

    using (FrameworkDb dbContext = new FrameworkDb())
    {
        children = (from c in dbContext.Children
                    join p in dbContext.Parent.Where(p => p.ParentName == parentName)
                    on new { PID = c.ParentId } equals new { PID = p.Id }
                    select c).ToList();
    }

    return children;
}

VN:F [1.9.22_1171]
Rating: 8.2/10 (6 votes cast)

Categories: c#, Code Tags:

PowerShell get “Return Value” from StoredProcedure ExecuteNonQuery

February 27th, 2012 1 comment

The scenario is that from PowerShell you want to do the following:

1. Execute a stored Procedure
2. Check the Return Code from the stored procedure call.
3. Perform conditional logic based upon whether the stored procedure succeeded or failed

The following will show you how to:

    1. Create a PowerShell function to execute a stored procedure.
    2. Call this stored procedure and extract the return code into an integer variable
    3. Apply conditional logic based upon the return code

    I struggled with getting this to work and did not find any exact examples that explained it exactly as I wanted.

    Environment Setup

    1. SQL Server / Express – I am running SQL Sever 2008 R2 Express on the local host
    2. Create an empty Database called “TestDB”
    3. PowerShell configured with Remote-Signed Execution Policy. If you don’t know how to do this go here Set PowerShell Execution Policy

    StoredProcedure

    Create a stored procedure as follows on your TestDB

    
    IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[prcTest]') AND type in (N'P', N'PC'))
    DROP PROCEDURE [dbo].[prcTest]
    GO
    
    Create Procedure prcTest 
    	@value int
    as
    begin
    	Declare @return float
    	
    	BEGIN TRY
    	
    		-- could return divide by 0 error if value = 0 
    		set @return = 1 / @value;
    	
    		Return 1 -- success
    	
    	END TRY
    	BEGIN CATCH
    		-- will return 0 when there has been a divide by zero error
    		return 0
    	END CATCH
    end
    
    

    This procedure will accept a single integer parameter and divide by it. If zero is passed in the procedure will fail with a divide by zero error and return the return code of 0.
    Any other number will succeed.

    1 = Success and 0 = Error

    (NOTE: I specifically reversed the normal codes returned from Stored Procedures because if there is a connection problem and the following powershell script cannot execute it will return 0 which needs to be treated as a failure, so 1 must be the success code)

    PowerShellScript

    Create a file called test.ps1 in your c:\Scripts directory.

    You can run this from the PowerShell ISE or you can create a .bat file and past this line in it:
    [bat]
    PowerShell.exe -noexit c:\scripts\test.ps1
    [/bat]

    The Power Shell Script is as follows:

    function ConnectionString()
    {
        return "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDB;Data Source=.\SQLExpress";
    }
    
    function executeStoredProcedure($value)
    {
        
        $connection = ConnectionString;
        $query = "prcTest";
        
        $sqlConnection = new-object System.Data.SqlClient.SqlConnection $connection
        $sqlConnection.Open() 
        
        $sqlCmd = new-object System.Data.SqlClient.SqlCommand("$query", $sqlConnection) 
    
        $sqlCmd.CommandType = [System.Data.CommandType]"StoredProcedure" 
        
        $sqlCmd.Parameters.AddWithValue("@value", $value)
    
        $sqlCmd.Parameters.Add("@ReturnValue", [System.Data.SqlDbType]"Int") 
        $sqlCmd.Parameters["@ReturnValue"].Direction = [System.Data.ParameterDirection]"ReturnValue" 
    
    
        $sqlCmd.ExecuteNonQuery() | out-null
        $sqlConnection.Close() 
    
        [int]$sqlCmd.Parameters["@ReturnValue"].Value
        
    }
    
    # Test 1 - Should fail
    #======================================
    Write-Host "====================="
    Write-Host "TEST 1"
    Write-Host "====================="
    $out = executeStoredProcedure(0)
    Write-Host $out
    $returnValue = $out[2]
    Write-Host $returnValue
    
    
    if($returnValue -eq $null -or $returnValue -eq 0)
    {
        Write-Host "An Error Occured"
    } 
    else 
    {
        Write-Host "Success"
    }
    
    # Test 2 success
    # ---------------
    # removed the Write-Host lines 
    #======================================
    Write-Host "====================="
    Write-Host "TEST 2"
    Write-Host "====================="
    $out = executeStoredProcedure(1)
    $returnValue = $out[2]
    
    if($returnValue -eq $null -or $returnValue -eq 0)
    {
        Write-Host "An Error occurred"
    } 
    else 
    {
        Write-Host "Success"
    }
    

    I will talk you through a few of the interesting points of above.

    ConnectionString Fuction
    I do this so I can have my connection string centralised in my script and that way if
    I need to change the SQL Server credentials I am using I only need to make the change in one place

    [int]$sqlCmd.Parameters[“@ReturnValue”].Value
    This gets the “ReturnValue” from the stored procedure and then casts it to an integer.

    Note: I did try code like;

    $rc = [int]$sqlCmd.Parameters["@ReturnValue"].Value
    return $rc
    

    But it made no difference to the output of the function.

    $returnValue = $out[2]
    I will confess I don’t now a great deal about PowerShell but I have worked through this problem to get a working solution.
    If you execute it and look at the output from the “Write-Host $out” you will see it appears to be an array type object and it prints as

    @value @ReturnValue 1
    

    I figured this is a zero based array so I just accessed the value in position 3 i.e. $out[2], which is already of type “int” due to the cast in the function.

    Conditional Logic
    So now I have the integer return value in a variable $returnValue I can use it in my conditional logic. If the value was null or == 0 then it must be in error else success.

    Implementing this means you can execute a stored procedure and if it fails , you can cancel any further processing in the Script.

    Nice 😉

    Update – Oct 2012

    I recently had to use this code again and I found that the best solution is to have the stored procedure return “non-zero” values, eg 1 for success, -1 for failure.
    The issue is that the return code from the “executeStoredProcedure” call will be zero in certain catastrophic conditions. For example, break your connection string on purpose and the stored procedure will never get executed and the return code from “executeStoredProcedure” is zero.

    By using non-zero return codes you now have 3 possible states, 1 – success, -1 failure, 0 – catastrophic failure.
    I extended my code to use “blat.exe” to send admin emails on return code zero so that if my powershell script failed to run someone is notified.

    VN:F [1.9.22_1171]
    Rating: 10.0/10 (1 vote cast)

Categories: Code Tags:

Set PowerShell ExecutionPolicy

February 27th, 2012 No comments

When trying to run a PowerShell script you may get the following error:

“scriptname.ps1 cannot be loaded because the execution of scripts is disabled on this system”

You need to do the following to set the executionPolicy.

1. Run Powershell as Administrator
2. get-exeuctionPolicy
3. set-executionPolicy RemoteSigned
4. Enter Y
5. Check by running get-exeuctionPolicy
 

 

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: Code Tags:

Enable Windows PowerShell ISE (Integrated Scripting Environment)

February 27th, 2012 No comments

On a Windows Server 2008 box you may be wondering where the Windows PowerShell ISE is.

To Enable

  1. Bring up the Server Manager (Right-Click on “Computer”, choose manage
  2. Move to the Features Tab
  3. Choose “Add Features”
  4. Click “Windows PowerShell Integrated Scripting Environment”
     
     
     

     

  5. Click Next
  6. Click Install

The IDE is now installed and can be found under:
– All Programs
– Accessories
– Windows PowerShell

Windows Power Shell ISE Menu Location

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: Code Tags:

Bootstrapper Manifest Generator (2008) – How To Guide v2

March 3rd, 2011 14 comments

I wrote the original Boostrapper Manifest Generator (BMG) blog post a few years ago.

It is one of the most heavily visited pages on my site so I thought I should revisit it, and given that the BMG is so buggy it could be useful to have some screen shots.

I will provide 2 concrete examples of how to use the BMG to create a bootstrapper pre-requisite check.

All this is being done on a Windows 7 32-bit (Virtual) machine, with visual studio 2010 installed.

I have also downloaded and installed the BMG2008 which can be downloaded from here

The application that I am going to bootstrapper is a small test application called “GuidGenerator”, can you guess what it does?

I will be using a standard setup deployment project to create the installer.

Here goes..

Setup BMG 2008

  1. Download the BMG2008 from here
  2. Install the application and note the path that it is installed to as it does not create a short cut, (C:Program FilesMicrosoftBootstrapper Manifest Generator for VS2008), and create your own desktop shortcut
  3. Run the BMG
  4. Click “Tools :: Edit Machine Paths”
  5. You may get a message about the SDK not being installed etc etc. Get past the error message and you will need to set the path to your boot strap directory. Do a search on your file system for “bootstrapper” and you will find a folder somewhere like this (note the SDK path)
    C:Program FilesMicrosoft SDKsWindowsv7.0ABootstrapper
    Enter that into the path and choose OK.

    You are now ready to create a bootstrapper

    Note: This bootstrapper folder is important – read the tips and tricks at the end.

Example 1 – .Net Framework 4.0 Pre-requisite Check

  1. Download the DotNet framework full install from Microsoft here, and save to a location that you can find, mine is @ c:DownloadsdotNetFx40_Full_x86_x64.exe
  2. Run the BMG
  3. Click “File New”
  4. Choose “Package Manifest”, choose OK
  5. Choose “Add Install File”, (button in the top left)

  6. Browse to the .Net Framework that you downloaded earlier and click OK
  7. Display Name: enter a name for the bootstrapper, this is the name of the pre-requisite check that will show up in visual studio. I will call mine “My .net Framework 4.0 Check”
  8. Click back on the “Package” option which is the root of the tree menu on the left and set the Product Name to DotNet4Check – this becomes the name of the folder that the package is created in.
    Note: You have to do it in this order as if you set the package name before adding a file it crashes the BMG.
  9. Click into the product code field and it will default with the “Product Name” just set, (otherwise this can throw a warning if left blank).
  10. System Checks
      You want to set up a system check that fails if the condition is not met, or alternatively by-pass the install if a condition is met.
      If it fails the check then it will prompt the user that they need to install the pre-requisite.

      For the .Net Framework I have chosen to do a simple registry check for the .Net Framework version.

    • Switch to the system checks tab
    • Click on the registry check (2nd icon in the list)
    • Property for Result: DotNet4RegCheck (note: this is the name of the check that is used later in the “Install Conditions” tab
    • Enter the following registry key: HKLMSoftwareMicrosoftNET Framework SetupNDPv4Full
    • Registry Value: Version
  11. Install Conditions
    • Switch to the install conditions tab. This is where we define when our installer should be run.
    • Type: ByPassIf
    • Property: DotNet4RegCheck – (This is the name entered in the “Property for Result” field in the system check
    • Comparison: Ver >=
    • Value: 4.0.30319
  12. Exit Codes
    Set the following: (Note: I am cheating as I am reverse engineering the .Net bootstrapper that comes with VS2010 to get these exit codes

      0 – Success
      3010 – Success with reboot

    Leave all the other options with their defaults

  13. Build: Now we can build the package, (button top right)
  14. Results, note that in my case it succeeded with no errors, and the path contains the folder name that I entered in the product name field.

  15. Copy: Go to the output directory and copy the “DotNet4Check” folder to “C:Program FilesMicrosoft SDKsWindowsv7.0ABootstrapperPackages”
  16. Setup Project
    1. My project looks like this:

    2. Right Click on the Setup Project and choose properites
    3. On the properties window Click the “Pre-Requisites” button.
    4. Deselect anything that is selected that you do not require and choose your check.

    5. In this case I am going to say – “Download prerequisites from the same location as my application”
    6. Build the setup solution in release mode.
    7. Copy the release directory somewhere to a machine missing the pre-requisites. The release directory should look something like this, because we chose to set the download from the same location.

    8. Run the installer and you will see this…

Example 2 – .Net Framework 4.0 Pre-requisite Check – web download

In this example we want to supply a web download version of the installer so you don’t have to distribute the requisite files with your installer.

This will be identical to the previous example with these changes:

  1. After adding the file you get the Properties tab. Enter the URL from the tips below in the “HomeSite URL” setting.
    I used – http://go.microsoft.com/fwlink/?linkid=182805 for the .Net 4.0 Framework. (remember you must right-click and choose paste).

  2. When setting up the package pre-requisites choose to “Download Pre-requisites from the package vendors website”.

Repeat all the other steps and when you choose “install” from the bootstrapper prompt you should get a message that it is downloading.

Tips and Tricks

  • The “Bootstrapper” directory is your friend (on my machine “C:Program FilesMicrosoft SDKsWindowsv7.0ABootstrapper”).
    If you have access to a VS2010 installed machine and go to the bootstrapper directory and into the Packages folder and you will find bootstrappers for 19 different things, including .Net 3.5/4.0 and Client installation versions.
    I suspect if you took a copy of the packages folder back to your older VS2005/2008 environment you would get the pre-requisites.
  • If you don’t intend to set up exit conditions don’t switch to the “Exit Conditions tab”. if you do it will put an empty condition in that throws a warning at build time. The only way to get rid of it is to enter 0 / Success.
  • I suspect for things like .Net you want the user to be able to download the .exe directly. Here are the URL’s provided by the .Net bootstrappers installed with VS2010.

    These are the small web installer based versions

    • .Net Framework 3.5 SP1: http://go.microsoft.com/fwlink/?linkid=118076 download
    • .Net Framework 3.5 Client: http://go.microsoft.com/fwlink/?LinkId=119637 download
    • .Net Framework 4.0: http://go.microsoft.com/fwlink/?linkid=182805 download
    • .Net Framework 4.0 Client: http://go.microsoft.com/fwlink/?linkid=182804 download
  • My web install failed because on my Install I happened to click into the “Value” field and it put the following “N/A for ValueExists and ValueNotExists”. This created an “unexpected error” on install.
    I went into the product.xml file and set value=”” and it worked.
VN:F [1.9.22_1171]
Rating: 10.0/10 (4 votes cast)

Categories: c#, Code Tags:

List<object> Find with delegate

March 11th, 2008 No comments

Thought this was really cool.

I have a generic List of objects, for example:

List<User> users = GetAllUsers();

I want to find a user by username. Usually you would loop through looking for the item with the matching username and return the user that was found.

or you could do this:

User loggedOnUser = users.Find(delegate(User u) { return u.Username == "jbloggs"; });

Much less code, and you could match on any property of the object that you wanted.

Cheers

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: c#, Code Tags:

Dynamically creating user controls in code behind

February 27th, 2008 No comments

If you have a user control that you wish to dynamically add to your page the following will not work;

UserControl control = new UserControl()

The problem is that the control has not been successfully created and all the internal controls that make up the user control, like text boxes etc, will be null.

The way to create the control is with the “LoadControl()” statement.

UserControl control = LoadControl("~/controls/UserControl.ascx") as UserControl;

and now it will work.

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: c#, Code Tags: ,