Force log file with Inno Setup versions before 5.2.0

From ISXKB

(Difference between revisions)
Jump to: navigation, search
(changed link to zip from image to media with size)
Line 54: Line 54:
</pre>
</pre>
-
You will have to download and extract 'StartHelper.exe' into your setup's working directory first: [[Image:StartHelper.zip|StartHelper.zip]]. The source code for the program, or most of it, can be found further down.
+
You will have to download and extract 'StartHelper.exe' into your setup's working directory first: [[Media:StartHelper.zip|StartHelper.zip 16KB]]. The source code for the program, or most of it, can be found further down.
Line 212: Line 212:
-
Source code of StartHelper.exe ([[Image:StartHelper.zip|StartHelper.zip]]):
+
Source code of StartHelper.exe ([[Media:StartHelper.zip|StartHelper.zip 16KB]]):
<pre>
<pre>

Revision as of 09:37, 5 February 2007

Installers created with Inno Setup support the command line parameters /LOG and /LOG="filename" to trace issues during the setup process.

Unfortuately users or customers are not always aware of how to enter these parameters. Even after explained on the phone or by email, they finally struggle or fail to send the log file back to the developer.

There's a way to make sure that a log file is always created and put into the application's directory. The follwing example copies the installation log file in a sub folder 'Logs' underneath the application's installation folder. It uses two functions, ForceLogFile () and MoveLogFile () that need to be called from within InitializeSetup () and DeInitializeSetup (), and additionally a helper application to invoke the setup again with /LOG=.

The solution is written so that it can be added to any script as easily as possible. The variables are stored in registry values and are deleted later again, so there's no need to change the tree to reflect your company and application's name, if you want to save some time implementing the solution. Admittingly, many people would suggest to store the log file's name in a global variable, but that would requrire some more changes to the main script.

Here's a suggestion on how to implement it:

Create a file called 'ForceLogFile.pas' or something similiar in the setup working directory and copy and paste both functions into it. This way you can easily reuse ForceLogFile.pas again for other scripts.

In your Inno Setup script, before InitializeSetup () and DeInitializeSetup (), include this line into the [Code] section.

#include "ForceLogFile.pas"


In the variables section of InitializeSetup () the 'Result' variable is required:

var
 Result : Boolean;

As the first operation in InitializeSetup () use this line (where 'MyAppInstall' should be replaced with something more meaningful, but without a file name extension):

Result := ForceLogFile ('MyAppInstall'); if (Result = FALSE) then Abort;

As the last operation in DeInitializeSetup () this line needs to be inserted:

MoveLogFile ('Logs', TRUE);

An empty string here (instead of 'Logs') would not copy the log file into a sub folder, but directly into the application's directory:

MoveLogFile ('', TRUE);

The Boolean parameter tells the function whether a date string should be added to the file name.


In the [Files] section, add the following line:

Source: StartHelper.exe; Flags: dontcopy

You will have to download and extract 'StartHelper.exe' into your setup's working directory first: StartHelper.zip 16KB. The source code for the program, or most of it, can be found further down.


Content of 'ForceLogFile.pas' (copy and paste the code into that file):

function ForceLogFile (sMyLogFile : String): Boolean;
// Don't use a file name extension for sMyLogFile here!
//  It will be .log.
var
	Errorcode	: Integer;
	sLanguage	: String;
	sMLogFileName	: String;
	sCMDPars	: String;
begin
	//MsgBox (GetCmdTail, mbInformation, MB_OK);
	if Pos ('/LOG=', Uppercase (GetCmdTail)) = 0 then
	begin
		sLanguage := '';
		// Retrieve the language. We don't want the language
		//	dialog again in our new setup instance.
		if Pos ('/LANG=', Uppercase (GetCmdTail)) = 0 then
		begin
			sLanguage := ' /LANG=' + ActiveLanguage;
		end;
		sMLogFileName := GenerateUniqueName (ExpandConstant ('{%TMP}'), '.log');
		//sBatName := GenerateUniqueName (ExpandConstant ('{%TMP}'), '.cmd')
		ExtractTemporaryFile ('StartHelper.exe');
		
		// Use the registry instead of global variables. That makes the functions
		//  more portable.
		RegWriteStringValue (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars',
			'sMLogFileName', sMLogFileName);
		RegWriteStringValue (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars',
			'sMyLogFile', sMyLogFile);
		sCMDPars := '"' + ExpandConstant ('{srcexe}')+ '"' + ' ' +
			GetCmdTail + ' "/LOG=' + sMLogFileName + '"' + sLanguage;
		//MsgBox (sCMDPars, mbInformation, MB_OK);
		if Exec (Expandconstant ('{tmp}\StartHelper.exe'),
			sCMDPars,                 {The parameters}
			ExpandConstant ('{src}'), {Working dir.}
			SW_HIDE, ewNoWait, Errorcode) then
		begin
		end else
		begin
			if ErrorCode <> 0 then
			begin
				msgbox ('Error: ' + IntToStr (ErrorCode), mbInformation, MB_OK);
			end;
		end;
		Sleep (100);
		Abort;
		Result := FALSE; // If invoked in the wrong place.
	end else
	begin
		Result := TRUE;
	end;
end;


function MoveLogFile (SubDir : String; bDate : Boolean): Boolean;
// Moves the log file to the application's directory.
//	SubDir names a directory below {app}, for instance 'Logs'.
var
	sSubF			: String;
	sAppDir			: String;
	sBatName		: String;
	sDate			: String;
	sAppLogFileName	: String;
	sMyLogFile		: String;
	sMLogFileName	: String;
	Errorcode		: Integer;
begin
	try
		sAppDir := ExpandConstant ('{app}');
	except
		sAppDir := '';
	end;
	if sAppDir <> '' then
	begin
		if SubDir <> '' then
		begin
			sSubF := '\' + SubDir;
		end else
		begin
			sSubF := '';
		end;
		RegQueryStringValue (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars',
			'sMLogFileName', sMLogFileName);
		RegDeleteValue (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars',
			'sMLogFileName');
		RegQueryStringValue (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars',
			'sMyLogFile', sMyLogFile);
		RegDeleteValue (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars',
			'sMyLogFile');
		RegDeleteKeyIfEmpty (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\Vars');
		RegDeleteKeyIfEmpty (HKEY_CURRENT_USER, 'Software\MyBrandNewApplication\');
		if DirExists (sAppDir + sSubF) then
		begin
			if bDate then
				sDate := GetDateTimeString ('yyyy-mm-dd', '-', ':')
			else
				sDate := '';
			sAppLogFileName := sAppDir + sSubF + '\' + sMyLogFile + sDate + '.log';
			// Create a batch file that can add the newly created log file.
			sBatName := GenerateUniqueName (ExpandConstant ('{%TMP}'), '.cmd')
			SaveStringToFile (sBatName,
				'PING -n 5 127.0.0.1' +
				Chr (13) + Chr (10),
				FALSE);
			SaveStringToFile (sBatName,
				'TYPE "' + sMLogFileName + '">>"' + sAppLogFileName + '"' +
				Chr (13) + Chr (10),
				TRUE);
			if GetWindowsVersion () >= $05000893 then
			begin
				// Windows 2000 and higher. Let's delete the file.
				SaveStringToFile (sBatName,
					'DEL /F/Q "' + sMLogFileName + '"' +
					Chr (13) + Chr (10),
					TRUE);
				SaveStringToFile (sBatName,
					'DEL /F/Q "' + sBatName + '"' +
					Chr (13) + Chr (10),
					TRUE);
			end;
			SaveStringToFile (sBatName,
				'Exit' +
				Chr (13) + Chr (10),
				TRUE);
			Exec (ExpandConstant ('{sys}\CMD.EXE '),
				' /C ' +
				'"' + sBatName + '"',
				ExpandConstant ('{tmp}'),
				SW_HIDE, ewNoWait, Errorcode);
			if FileOrDirExists (sMLogFileName) then
			begin
				try
					RestartReplace (sMLogFileName, '');
				except
				end;
				try
					RestartReplace (sBatName, '');
				except
				end;
			end;
			Result := TRUE;
		end;
	end else
	begin
		Result := FALSE;
	end;
end;

Bare in mind that this solution only works on systems with Windows NT or higher.


Source code of StartHelper.exe (StartHelper.zip 16KB):

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <crtdbg.h>
#include "GetWindowsErrorText.h"

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	STARTUPINFO	si;
	PROCESS_INFORMATION pi;
	TCHAR		tcPars [10000];
	TCHAR		tcError [1000];
	DWORD		dwError;
	int			argc;
	size_t		stlen;

	if (__argc > 1)
	{
		argc = 3;
		tcPars [0] = '\0';
		stlen = 10000;
		while (argc <= __argc)
		{
			if (argc > 3)
			{
				strncat (tcPars, " ", stlen);
				tcPars [10000 - 1] = '\0';
				stlen--;
			}
			if ((strchr (__argv [argc - 1], ' ')) && (!strchr (__argv [argc - 1], '\"')))
			{
				stlen--;
				strncat (tcPars, "\"", stlen);
				tcPars [10000 - 1] = '\0';
			}
			stlen -= strlen (__argv [argc - 1]);
			strncat (tcPars, __argv [argc - 1], stlen);
			tcPars [10000 - 1] = '\0';
			if ((strchr (__argv [argc - 1], ' ')) && (!strchr (__argv [argc - 1], '\"')))
			{
				stlen--;
				strncat (tcPars, "\"", stlen);
				tcPars [10000 - 1] = '\0';
			}
			argc++;
		}
		ZeroMemory (&si, sizeof (STARTUPINFO));
		ZeroMemory (&pi, sizeof (PROCESS_INFORMATION));
		if (CreateProcess (__argv [1],
			tcPars, NULL, NULL, FALSE, 0, NULL, NULL /*DefaultDir*/, &si, &pi) == 0)
		{
			dwError = GetLastError ();
			_snprintf (tcError, 1000, "CreateProcess () failed. Error %d: %s.", dwError, GetWindowsErrorText (dwError));
			tcError [1000 - 1] = '\0';
			MessageBox (NULL, tcError, "Error StartHelper.exe", MB_OK | MB_ICONSTOP);
		};
	} else
		MessageBox (NULL, "Usage:\n\nStartHelper [executable file] [args]", "StartHelper", MB_OK | MB_ICONINFORMATION);
};
Personal tools
Ads: