Python detection code for InnoSetup

Robert Olson olson at mcs.anl.gov
Mon Sep 29 13:19:00 CDT 2003


and a different version that uses enumeration of the pythoncore key to find 
all of 'em.

--bob

At 10:31 AM 9/29/2003 -0500, Robert Olson wrote:
>Sending this off for future reference since it doesn't belong in the .iss 
>I'm working on at the moment.
>
>--bob
>
-------------- next part --------------
var
  PythonPaths, PythonNames, PythonKeyNames : TArrayOfString;
  PythonPathVals: TArrayOfString;
  MultiplePythonInstalls: Boolean;

// External functions:
// InnoSetup 2 versions:
//function RegOpenKeyEx(RootKey : Integer;subkey : string;options: integer;sec:integer;valu:integer): integer; external 'advapi32' name 'RegOpenKeyExA'; stdcall;
//function RegCloseKey(valu:integer): integer; external 'advapi32' name 'RegCloseKey'; stdcall;
//function RegEnumKeyEx(hKey: Integer;dwIndex: Integer; lpName : Pchar; lpcbName: PChar;lpReserved: Integer;lpClass: Pchar;lpcbClass: Integer;lpFTLWT: Integer): Integer; external 'advapi32' name 'RegEnumKeyExA'; stdcall;
// InnoSetup 3 versions:
function RegOpenKeyEx(RootKey : Integer;SubKey: String;Options: Integer;Sec: Integer; Valu: Integer): Integer; external 'RegOpenKeyExA at advapi32.dll stdcall';
function RegCloseKey(Valu: Integer): Integer; External 'RegCloseKey at advapi32.dll stdcall';
function RegEnumKeyEx(hKey: Integer;dwIndex: Integer; lpName : Pchar; lpcbName: PChar;lpReserved: Integer;lpClass: Pchar;lpcbClass: Integer;lpFTLWT: Integer): Integer; external 'RegEnumKeyExA at advapi32.dll stdcall';
function RegQueryInfoKey(hKey: Integer;lpClass: Integer; var lpcClass: Integer; lpReserved: Integer; var lpcSubKeys: Integer; var lpcMaxSubkeyLen: Integer; var lpcMaxClassLen: Integer; var lpcValues: Integer; var lpcMaxValueNameLen: Integer; var lpcMaxValueLen: Integer; var lpcbSecurityDescriptor: Integer; lpftLastWriteTime: Integer): Integer; external 'RegQueryInfoKeyA at advapi32.dll stdcall';
// End external functions

// From winerror.h/windows.pas:
Const KEY_QUERY_VALUE = $1;
Const KEY_ENUMERATE_SUB_KEYS = $8;
Const ERROR_SUCCESS = 0;
Const ERROR_MORE_DATA = 234;
Const ERROR_NO_MORE_ITEMS = 259;

//**********************************************************
// What this is all about:
// Created by Ton Huisman, Aug 2002 ath at ath.myweb.nl
// Freeware, no claims, no f(l)ames, use at your own risk, test before releasing to public!!
//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyExists: Query for the existence of some Keyvalue in the registry
// -> Function RegKeyExists(RootKey : Integer; SubKey : String) : Boolean;
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//   SubKey  : The full path to the queried key
// Output: Boolean: True if found, False if not found.
// Queried by trying to open the key for read access, True if successful
// Uses: RegOpenKeyEx, CastPtrToInt, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
// RegOpenKey: Open a Keyvalue in the registry
// -> Function RegOpenKey(RootKey : Integer; SubKey : String;Sam: Integer) : Integer;
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//   SubKey  : The full path to the queried key
//   Sam   : The Security Acces Mask, If 0 then KEY_QUERY_VALUE
// Output: Integer (HKey): Handle for registry key, 0 if not Ok
// Uses: RegOpenKeyEx
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyNames: Query for a string of all SubKeys of a SubKey in the registry
// -> Function RegKeyNames(RootKey: Integer; SubKey : String) : String;
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//   SubKey  : The full path to the queried key
// Output: String: Comma-separated list of all subkeys found
// Uses: RegOpenKey, CastIntToPtrStr, RegEnumKeyEx, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////

//**********************************************************
// Supporting function

procedure MsgShow(c:string);
begin
  MsgBox(c,mbConfirmation,MB_OK);
end;


Function CastPtrToInt(PtrStr: String) : Integer;
Begin
	Result := Ord(StrGet(PtrStr,1)) + (Ord(StrGet(PtrStr,2)) * 256) + (Ord(StrGet(PtrStr,3))*256*256) + (Ord(StrGet(PtrStr,4))*256*256*256);
End; { CastPtrToInt }

// Supporting function
// Cast Integer to PointerString
Function CastIntToPtrStr(Inte: Cardinal) : String;
Var n : Integer;
r : Cardinal;
t : Cardinal;
Begin
Result := StringOfChar(#0,4);
r := Inte;
n := 4;
If r >= (256*256*256) then
Begin
t := r / (256*256*256);
StrSet(Chr(t),n,Result);
r := r - (t * (256*256*256));
End;
n := n - 1;
If r >= (256*256) then
Begin
t := r / (256*256);
StrSet(Chr(t),n,Result);
r := r - (t * (256*256));
End;
n := n - 1;
If r >= (256) then
Begin
t := r / (256);
StrSet(Chr(t),n,Result);
r := r - (t * (256));
End;
n := n - 1;
If r > 0 then
Begin
t := r;
StrSet(Chr(t),n,Result);
r := r - t;
End;
End; { CastIntToPtrStr }

//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyExists: Query for the existence of some Keyvalue in the registry
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//   SubKey  : The full path to the queried key
// Output: Boolean: True if found, False if not found.
// Queried by trying to open the key for read access, True if successful
// Uses: RegOpenKeyEx, CastPtrToInt, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////
Function RegKeyExists(RootKey : Integer; SubKey : String) : Boolean;
Var l : Integer;
ls : String;
hKey : Integer;
Begin
	ls := StringOfChar(#0,4);
	l := CastStringToInteger(ls);
	Result := (RegOpenKeyEx(RootKey,SubKey,0,KEY_QUERY_VALUE,l) = 0);
	hKey := CastPtrToInt(ls);
	RegCloseKey(hKey);
End; { RegKeyExists }

//////////////////////////////////////////////////////////////////////////////////////////////
// RegOpenKey: Open a Keyvalue in the registry
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//   SubKey  : The full path to the queried key
//   Sam   : The Security Acces Mask, If 0 then KEY_QUERY_VALUE
// Output: Integer (HKey): Handle for registry key, 0 if not Ok
// Uses: RegOpenKeyEx
//////////////////////////////////////////////////////////////////////////////////////////////
Function RegOpenKey(RootKey : Integer; SubKey : String;Sam: Integer) : Integer;
Var l : Integer;
ls : String;
Begin
	ls := StringOfChar(#0,4);
	l := CastStringToInteger(ls);
	If Sam = 0 then Sam := KEY_QUERY_VALUE;
	Result := 0;
	If (RegOpenKeyEx(RootKey,SubKey,0,Sam,l) = 0) then
		Result := CastPtrToInt(ls);
End; { RegOpenKey }

//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyNames: Query for a string of all SubKeys of a SubKey in the registry
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//   SubKey  : The full path to the queried key
// Output: String: Comma-separated list of all subkeys found
// Uses: RegOpenKey, CastIntToPtrStr, RegEnumKeyEx, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////

Const Key_Separator = ',';// Default to comma, change as needed, could be #9 or ';'?
Const Key_MaxLength = 255;// Usually this (255) is way to large, 64 could do niceley, but just be safe. Longer keys than this length will be ignored!

Function RegKeyNames(RootKey: Integer; SubKey : String) : String;
Var hKey : Integer;// HKey is not directly supported...
	wdIndex : Integer;
	s : String;
	ss : Integer;
	st : String;
	r : Integer;
Begin
	wdIndex := 0;
	Result := '';
	s := StringOfChar(#0,Key_MaxLength);
	ss := Key_MaxLength;
	st := CastIntToPtrStr(ss);
	hKey := RegOpenKey(RootKey,SubKey,KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS);
	If hKey <> 0 then
	Begin
		ss := Key_MaxLength;
		st := CastIntToPtrStr(ss);
		r := RegEnumKeyEx(hKey,wdIndex,s,st,0,'',0,0);
		MsgBox('got ' + InttoStr(r), mbInformation, MB_OK);
		While ((r = ERROR_SUCCESS) or (r = ERROR_MORE_DATA)) do
		Begin
			ss := CastPtrToInt(st);
			If ss > 0 then begin
				MsgBox(s, mbInformation, MB_OK);
				Result := Result + Copy(s,1,ss) + Key_Separator;
			end;
			wdIndex := wdIndex + 1;
			ss := Key_MaxLength;
			st := CastIntToPtrStr(ss);
			r := RegEnumKeyEx(hKey,wdIndex,s,st,0,'',0,0);
		End;
		If Result <> '' then
			Result := Copy(Result,1,Length(Result) - 1);// remove extra separator ;-)
		RegCloseKey(hKey);
	End;
End; { RegKeyNames }

Function RegKeyNamesArray(RootKey: Integer; SubKey : String) : TArrayOfString;
Var hKey : Integer;	// HKey is not directly supported...

	SubKeys 			: Integer;
	MaxSubkeyLen		: Integer;
	ActSubkeyLen		: Integer;

	ClassBufferSize		: Integer;
	MaxClassLen		: Integer;
	NumValues		: Integer;
	MaxValueNameLen	: Integer;
	MaxValueLen		: Integer;
	SecDescrLen		: Integer;

	SubkeyName, st		: String;
	ii				: Integer;
	jj				: Integer;
	Flag				: Integer;

	Res : TArrayOfString;
	r, l : Integer;

Begin
	SubKeys := 0;
	MaxSubkeyLen := 0;
	ClassBufferSize := 0;
	MaxClassLen := 0;
	NumValues := 0;
	MaxValuenameLen := 0;
	MaxValueLen := 0;
	SecDescrLen := 0;
	Flag := 0;

	SetArrayLength(Res,0);

	hKey := RegOpenKey(RootKey,SubKey,KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS);
	If hKey <> 0 then
	Begin
		// String zum Empfang der Subkey Anzahl vorbereiten
		SubKeys := 0;
		MaxSubkeyLen := 0;
		r := RegQueryInfoKey(hKey,0,ClassBufferSize,0,SubKeys,MaxSubkeyLen,MaxClassLen,NumValues,MaxValuenameLen,MaxValueLen,SecDescrLen,0);

		// MsgShow('RegQueryInfoKey: SubKeys = ' + IntToStr(SubKeys) + ' MaxSubkeylen = ' + IntToStr(MaxSubkeyLen));

		SetArrayLength(Res,SubKeys);

		for ii:=0 to (SubKeys-1) do
		begin
			ActSubkeyLen := MaxSubkeyLen + 1;
			SubkeyName := StringOfChar(#0,ActSubkeyLen);
			st := CastIntToPtrStr(ActSubkeyLen);
			r := RegEnumKeyEx(hKey,ii,SubkeyName,st,0,'',0,0);
			l := CastPtrToInt(st)
			Res[ii] := Copy(SubkeyName, 0, l);
			// MsgShow('RegEnumKeyEx returns SubkeyName = ' + Res[ii] + ' len is ' + IntToStr(l));
		end;
	End;

	Result := Res;
End; { RegKeyNamesArray }

function InitializeSetup(): Boolean;
var
	numPythons, i : Integer;
	base, val : String;
begin

	PythonKeyNames := RegKeyNamesArray(HKLM, 'Software\Python\PythonCore');

	numPythons := GetArrayLength(PythonKeyNames);
	//MsgShow('numpys ' + IntToStr(numPythons));
	if numPythons > 1 then
		MultiplePythonInstalls := true
	else
		MultiplePythonInstalls := false;


	SetArrayLength(PythonPaths, numPythons);
	SetArrayLength(PythonNames, numPythons);
	SetArrayLength(PythonPathVals, numPythons);

	for i := 0 to numPythons - 1 do
	begin
		base := 'Software\Python\PythonCore\' + PythonKeyNames[i] + '\InstallPath'
		// MsgShow('base is <' + base+ '>');

		RegQueryStringValue(HKLM, base, '', val)
		PythonPaths[i] := val
		RegQueryStringValue(HKLM, base + '\InstallGroup', '', val)
		PythonNames[i] := val
	end

	{ Default to the latest one. }
	PythonPathVals[numPythons - 1] := '1';

	Result := true;
end;


function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
var
  I, CurSubPage: Integer;
  Nextt, NextOk: Boolean;
  pythonPath: String;
begin
    if (not BackClicked and (CurPage = wpSelectDir)) or (BackClicked and (CurPage = wpSelectProgramGroup)) then begin
		{ Insert a custom wizard page between two non custom pages }
		{ First open the custom wizard page }
		ScriptDlgPageOpen();
		{ Set some captions }
		ScriptDlgPageSetCaption('Choose python version.');
		ScriptDlgPageSetSubCaption1('Multiple versions of Python are installed on this system.');
		ScriptDlgPageSetSubCaption2('Select the version you wish to use for the Access Grid Toolkit, and click Next.');


		Nextt := InputOptionArray(PythonNames, PythonPathVals, True, False);

		for i := 0 to GetArrayLength(PythonPathVals) - 1 do begin
			if (PythonPathVals[i] = '1') then begin
				pythonPath := PythonPaths[i];
				break
			end;
		end;

		MsgBox(pythonPath, mbInformation, MB_OK);

		if not BackClicked then
			Result := Nextt
		else
			Result := not Nextt;
		ScriptDlgPageClose(not Result);
    end	else begin
		Result := true
	end;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  if MultiplePythonInstalls then
	  Result := ScriptDlgPages(CurPage, False)
  else
	Result := True
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  if MultiplePythonInstalls then
	  Result := ScriptDlgPages(CurPage, True)
  else
	Result := True
end;


More information about the ag-dev mailing list