Click here to Skip to main content
Click here to Skip to main content

Patching me and patching you

, 1 Jan 2012
Rate this:
Please Sign up or sign in to vote.
This will show you how to generate and apply your own patches using Delphi.

Introduction

We've all seen it before: applications updating software and allowing you to 'rollback' your changes. This article will show you how this can be accomplished in Delphi using the Microsoft PatchAPI interface. The main object (discussed later) should work straight off the bat without you needing to add in anything with regards to the patching options.

Background

I've always wanted to create my own installation packages and all that stuff. I think everyone has. I always thought it was this magical thing that just 'happened' and you had to use pre-built installation programs that you need to install in order to build them. This article does not show you how to make a full fledged application installer builder, but it will give you insight into it with regards to creating patches between your old and new file versions. This article will also show you how to use DLL files as well.

Unlike my previous articles, I am not going to show very much code, because I commented the source code quite well (I think).

So what's going on in the code?

This article is a lot more higher level than my earlier articles, but if you have a background in development, you should get the gist of it Smile | :) . You will notice that there will be two DLLs accompanying the source code, namely mspatcha.dll and mspatchc.dll. You shouldn't need any of these DLLs but I added them for convenience's sake. The former concerns applying the patches while the latter involves creating the patches (more on that later). First things first:

PatchAPI

This file contains the main definition for what the original DLL files actually allow you to do. It defines all the constants, functions, and the actual DLL call definitions to the mentioned DLLs. For more information regarding PatchAPI, please read further here.

uPatcher

This is the wrapper class which I generated in order to take away the strain of generating the patch files and applying said patches. It contains Delphi specific set values, custom events, and even a custom exception class. I did my best to comment as much as I could with this unit, so if anyone would like any other explanations with regard to what is happening, please let me know and I will be happy to assist.

Unit1

This obviously is the main screen of the application. It allows you to add/edit/remove files you want to patch as well as creating/applying patches.

Using the code

The object that you want to use mostly is the TPatcher class. It provides the easiest and most effective way of creating and applying your patch files without needing to work with the PatchAPI class. There are four (custom) events which I created which do the following:

  • TOnPatchProgress: Shows you the progress when you create or apply a patch
  • TOnPatchComplete: Occurs when the patch create or apply is complete
  • TOnPatchFileBegin: Occurs when a file is being patched or created
  • TOnPatchFileEnd: Occurs when the end of a file patch or create is complete

Don't worry, the patcher will work fine if you do not implement these events Smile | :) . In fact, the patcher will work quite fine without you adding in anything more than just patch files that you have added. For your convenience, I have ensured that I implemented all four of the custom events so you can device your own definitions for them. Theoretically, if you create a patch of a file, the patch can actually go both ways. If I am not mistaken, using this article, you can make a file both the old and the new version just by applying the delta the way you see fit. This is how (I think) any installation application works with regards to applying and rolling back a patch.

As a point of interest, I will demonstrate how one of the custom events are defined and implemented:

TOnPatchProgress = procedure(
  ASender : TObject;
  const ACurrentPosition : LongWord;
  const AMaximumPosition : LongWord;
  var ACanContinue : LongBool) of object;

This is the definition for the progress of the creating or patching of a file (obviously). So all you will need to do is create a corresponding definition for the event like this (based on my demo application):

procedure TPatcherDemo.DoPatcherProgress(ASender : TObject;
  const ACurrentPosition : LongWord;
  const AMaximumPosition : LongWord;
  var ACanContinue : LongBool);
var
  LStr : string;
begin
  if AMaximumPosition <> ProgressBar1.Max then
    ProgressBar1.Max := AMaximumPosition;
  if ACurrentPosition <> ProgressBar1.Position then
    ProgressBar1.Position := ACurrentPosition;

  LStr := 'Complete: ' + FormatFloat('#,##0', ACurrentPosition) + 
          ' of ' + FormatFloat('#,##0', AMaximumPosition);
  mmo1.Lines.Add(LStr);
  Application.ProcessMessages;
end;

When you step through the program, you will see that it fires each time something happens when the patch is created/applied.

I also introduce the concept of constant type arrays. All that basically means is that based on a type definition, you can create a constant array based on the type that you defined. So based on that, I create string definitions based on the type definitions like this:

type
  TPatchApplyOption = (
    paoFailIfExact, // = APPLY_OPTION_FAIL_IF_EXACT
    paoFailIfClose, // = APPLY_OPTION_FAIL_IF_CLOSE
    paoTestOnly, // = APPLY_OPTION_TEST_ONLY
    paoValidFlags // = APPLY_OPTION_VALID_FLAGS
    );
  TPatchApplyOptions = set of TPatchApplyOption;
const
  Descriptions_TPatchApplyOption : array[Low(TPatchApplyOption)..High(TPatchApplyOption)] of string = (
    'If the source file and the target are the same, return a failure and don'#39't create the target.',
    'If the source file and the target differ by only rebase and bind 
     information (that is, they have the same normalized signature), 
     return a failure and don'#39't create the target.',
    'Don'#39't create the target.',
    'The logical OR of all valid patch apply flags.'
    );

You will see in the demo application how this actually works, but you will find out quick enough where I use it Smile | :) .

This object also allows you to use different kinds of input options but I only used ANSI (mostly) and Unicode (sometimes). The other option is by handle, which I never tested. So if you feel that you want to use it and find issues, please let me know.

Points of interest

I had a bit of trouble with regards to inserting multiple files into a single patch file. The patcher also allows you to use symbol files and anything that Windows has at its disposal. I just haven't had the chance to implement it all. If anyone wants me to add more functionality to this, please let me know and I will be happy to update this article.

Microsoft (currently) uses two forms of patching interfaces, PatchAPI and MSDelta. The former being from XP down and the latter being from Vista upwards. An issue which I found is, if you read the article regarding patching which I linked at the top of the article, PatchAPI works quite nicely in Delphi 7 (I haven't tried it with the later ones), but MSDelta works quite fine with CBuilder, but apparently they are not interchangeable. I will be posting my CBuilder article regarding MSDelta soon, so keep checking Smile | :) . If anyone can make suggestions regarding building them into either or both, please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

nortee
Software Developer (Senior)
South Africa South Africa
I've been coding for the better part now of about 15 years, starting in the good old Turbo Pascal 7 days, when I made my very first computer game ( my own version of Reversi / Othello) and I was hooked, through to Delphi 2009 (and higher, just didn't use it that much).
 
For the last 3 years or so I have been off and on using C#/ASP.NET (and even more recently Entity Framework and MVC) and I think it is TOTALLY awesome. Having said that though, I must admit that I will always be a Delphi guy at heart. I've dabbled in a few other languages and am always on the lookout to keep in touch with what's new in the software development scene.
 
When I am not coding, I keep myself busy writing short stories[^], partying hard, singing at karaoke (don't judge me!), trying to learn how to play the guitar and generally helping others wherever I can.

Comments and Discussions

 
QuestionHigh level / Low level PinpremiumB. Clay Shannon18-Jul-14 13:34 
AnswerRe: High level / Low level Pinmembernortee23-Jul-14 1:10 
QuestionPatching Pinmemberonurag1926-Jul-12 5:32 
AnswerRe: Patching PinmemberKarl Sanford26-Jul-12 5:58 
AnswerRe: Patching Pinmembernortee26-Jul-12 6:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 1 Jan 2012
Article Copyright 2012 by nortee
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid