Skip to content
This repository has been archived by the owner on Jul 1, 2020. It is now read-only.

Integrating SimplePatchTool

Süleyman Yasir KULA edited this page May 23, 2019 · 19 revisions

To add patching support to your apps, you can create an instance of SimplePatchTool (SimplePatchToolCore namespace), configure it and then execute it. SimplePatchTool runs in a separate thread in the background, so you will probably want to fetch its progress from time to time to update your UI accordingly.

Creating SimplePatchTool

public SimplePatchTool( string rootPath, string versionInfoURL ): creates a new SimplePatchTool instance

  • rootPath: the path of the application directory (you may use Path.GetDirectoryName(PatchUtils.GetCurrentExecutablePath()), if your executable resides at the root of the application directory)
  • versionInfoURL: download URL of the VersionInfo.info (if you don't have a versionInfoURL yet, see Generating versionInfoURL)

Configuring SimplePatchTool

SimplePatchTool UseRepairPatch( bool canRepairPatch ): sets whether or not repair patch can be used to patch the application (default: true)

SimplePatchTool UseIncrementalPatch( bool canIncrementalPatch ): sets whether or not incremental patches can be used to patch the application (default: true)

SimplePatchTool UseInstallerPatch( bool canInstallerPatch ): sets whether or not installer patch can be used to patch the application (default: true)

SimplePatchTool CheckForMultipleRunningInstances( bool checkForMultipleRunningInstances ): if checkForMultipleRunningInstances is set to true, patcher aborts if there are multiple running instance of the application (default: true)

SimplePatchTool VerifyFilesOnServer( bool verifyFiles ): if verifyFiles is set to true, files on the server will be verified (default: false)

SimplePatchTool UseCustomDownloadHandler( DownloadHandlerFactory factoryFunction ): instructs SimplePatchTool to use a custom download handler. By default, a WebClient based download handler is used but on some platforms (e.g. Unity), WebClient may not support https urls. In such cases, you may want to use a custom download handler implementation that supports https. DownloadHandlerFactory has the following signature: delegate IDownloadHandler DownloadHandlerFactory()

SimplePatchTool UseCustomFreeSpaceCalculator( FreeDiskSpaceCalculator freeSpaceCalculatorFunction ): by default, SimplePatchTool uses the DriveInfo.AvailableFreeSpace property to determine the free space of a drive but on some platforms (e.g. Unity), it may not be supported. In such cases, you may want to use a custom function to calculate the free space of a drive correctly (or, you can use a function that returns long.MaxValue to skip free space check entirely). FreeDiskSpaceCalculator has the following signature: delegate long FreeDiskSpaceCalculator( string drive )

SimplePatchTool UseVersionInfoVerifier( XMLVerifier verifierFunction ): instructs SimplePatchTool to verify the downloaded VersionInfo with the provided function. XMLVerifier has the following signature: delegate bool XMLVerifier( ref string xmlContents ). This function must return true only if the downloaded VersionInfo (xmlContents) is genuine. If you use a custom layer of security that e.g. encrypts the contents of the VersionInfo, you should first decrypt xmlContents. A VersionInfo/PatchInfo verifier is not mandatory and should not be used if you don't sign/encrypt your VersionInfo and/or PatchInfo'es

SimplePatchTool UsePatchInfoVerifier( XMLVerifier verifierFunction ): instructs SimplePatchTool to verify the downloaded PatchInfo'es with the provided function. For example, the following code uses the XMLSigner.VerifyXMLContents function to verify the VersionInfo and PatchInfo files that have been signed with the XMLSigner.SignXMLFile function (or with the Patcher sign_xml or Patcher project_sign_xmls console commands):

patcher.UseVersionInfoVerifier( ( ref string xml ) => XMLSigner.VerifyXMLContents( xml, publicRSAKey ) )
       .UsePatchInfoVerifier( ( ref string xml ) => XMLSigner.VerifyXMLContents( xml, publicRSAKey ) );

SimplePatchTool LogProgress( bool value ): sets whether or not SimplePatchTool should log any IOperationProgress data. This interface has two properties: int Percentage { get; } (between 0 and 100) and string ProgressInfo { get; } (localized description for the progress). Currently, two operations provide progress info: DownloadProgress and FilePatchProgress (default: true)

SimplePatchTool LogToFile( bool value ): sets whether or not SimplePatchTool should write logs and any raised exceptions to a file. This log file will be located inside the application directory with name spt_logs.txt (default: true)

SimplePatchTool SilentMode( bool silent ): sets whether or not SimplePatchTool should run silently (i.e. no logs) (default: false)

Executing SimplePatchTool

bool CheckForUpdates( bool checkVersionOnly = true ): asynchronously checks whether or not app is up-to-date in a separate thread. Returns false, if SimplePatchTool is already checking for updates or applying a patch. If checkVersionOnly is set to true, only the version code of the app (e.g. 1.0.0) is compared against the VersionInfo's version code. Otherwise, hashes and sizes of the files in the application directory are compared against VersionInfo (i.e. integrity check)

bool Run( bool selfPatching ): starts patching the application directory (rootPath) asynchronously in a separate thread. It is not mandatory to check for updates beforehand because this function internally checks for updates, as well. You can perform self patching only if you have a self patcher executable that was distributed with your application. This function returns false, if SimplePatchTool is already running

NOTE: any application can self patch itself, which eliminates the need for a launcher; but it is not recommended for large applications because if user terminates the self patcher executable before it updates all the files in the application directory (which may take some time for large applications), then the application may become corrupt.

bool ApplySelfPatch( string selfPatcherExecutable, string postSelfPatchExecutable = null ): terminates the app and runs the self patcher executable. You must pass the path of the self patcher executable to the selfPatcherExecutable parameter. If you'd like to launch an executable after self patcher executes successfully (e.g. to restart the app after self patching is complete), pass the path of that executable to the postSelfPatchExecutable parameter. This function should only be called if Operation is SelfPatching and Result is Success. Returns false, if something goes wrong

Fetching SimplePatchTool's progress

There are two ways to fetch SimplePatchTool's progress:

  • 1. Calling the following methods and properties manually from time to time

string FetchLog(): fetches the next log that SimplePatchTool has generated. Returns null, if there is no log in the queue

IOperationProgress FetchProgress(): returns an IOperationProgress instance if patcher's progress has changed, null otherwise

IOperationProgress FetchOverallProgress(): returns an IOperationProgress instance if patcher's overall progress has changed, null otherwise. Under normal circumstances, patch will finish when overall progress' Percentage reaches 100

bool IsRunning { get; }: returns true if SimplePatchTool is currently checking for updates or applying a patch

void Cancel(): cancels the current operation

PatchOperation Operation { get; }: returns CheckingForUpdates, if last operation was (or currently running operation is) CheckForUpdates; Patching, if it was/is Run(false); SelfPatching, if it was/is Run(true) and ApplyingSelfPatch, if it was/is ApplySelfPatch

PatchMethod PatchMethod { get; }: returns the patch method that SimplePatchTool is currently using (None, RepairPatch, IncrementalPatch or InstallerPatch)

PatchStage PatchStage { get; }: returns the current stage of the patcher (e.g. CheckingUpdates, DownloadingFiles, DeletingObsoleteFiles and so on)

string NewVersion { get; }: returns the version code of the new version (e.g. 1.2.0) or null, if it isn't determined (i.e. if the patcher didn't/couldn't fetch the VersionInfo yet)

  • 2. Using a SimplePatchTool.IListener object

You can register a listener to SimplePatchTool using the SetListener function. This listener will receive the following callbacks:

void Started(); // SimplePatchTool has started running an operation
void LogReceived( string log );
void ProgressChanged( IOperationProgress progress );
void OverallProgressChanged( IOperationProgress progress );
void PatchStageChanged( PatchStage stage );
void PatchMethodChanged( PatchMethod method );
void VersionInfoFetched( VersionInfo versionInfo ); // Can be used to modify the VersionInfo at runtime (e.g. add ignored paths to it)
void VersionFetched( string currentVersion, string newVersion ); // New version's version code is fetched
void Finished(); // Currently running operation has finished

Though you can implement the SimplePatchTool.IListener interface yourself, be aware that any expensive operations performed in these callbacks will block the patcher's thread. Instead, you are recommended to use the PatcherAsyncListener class which has a dedicated event for each callback and runs these events in a separate thread (if you are using Unity, use the PatcherListener class to receive callbacks on the main thread).

Fetching the result of the operation

PatchResult Result { get; }: returns different values for different Operation's. Its value should be checked after IsRunning returns false:

  • CheckingForUpdates: returns PatchResult.AlreadyUpToDate if app is up-to-date, PatchResult.Success if there is an update for the app, and PatchResult.Failed if there was an error while checking for updates
  • Patching/SelfPatching: returns PatchResult.AlreadyUpToDate if app is already up-to-date, PatchResult.Success if app is updated successfully, and PatchResult.Failed if there was an error while updating the app. In self patching mode, a value of PatchResult.Success means that we are ready to launch the self patcher executable via ApplySelfPatch
  • ApplyingSelfPatch: returns PatchResult.Success if self patcher executable is successfully started (in which case, currently running application will terminate promptly), and PatchResult.Failed if there was an error while trying to start the self patcher executable

PatchFailReason FailReason { get; }: if Result is PatchResult.Failed, this property stores why the patcher has failed (e.g. Cancelled, InsufficientSpace, XmlDeserializeError and so on). You may want to execute special logic for the following cases:

  • RequiresAdminPriviledges: we need admin permissions to update the files in the application directory
  • FilesAreNotUpToDateAfterPatch: after applying the patch, app is somehow still not up-to-date
  • UnderMaintenance_AbortApp: servers are currently under maintenance and users should not be allowed to launch the app (maintenance check requires manual setup)
  • UnderMaintenance_CanLaunchApp: servers are currently under maintenance but users can continue using the app (maintenance check requires manual setup)
  • MultipleRunningInstances: multiple instance of the application are running
  • SelfPatcherNotFound: self patcher executable couldn't be started because it doesn't exist

string FailDetails { get; }: if Result is PatchResult.Failed, returns a localized string that briefly explains why the patcher has failed

Example code: SimplePatchToolConsoleApp.Program.CheckForUpdates and SimplePatchToolConsoleApp.Program.ApplyPatch. Also see the WinForms-based launcher example.

Clone this wiki locally