From a7cc2fe73a170592a97ca1d23bbe63f1f07e461e Mon Sep 17 00:00:00 2001 From: Bill Stewart Date: Thu, 10 Jun 2021 13:51:42 -0600 Subject: [PATCH] 1.0.1 --- README.md | 60 ++++++++++----------- UninsIS.iss | 61 ++++++++++----------- UninsIS.pp | 22 ++++---- UninsIS.rc | 8 +-- history.md | 10 ++++ wsISPackage.pp | 144 ++++++++++++++++++++++++------------------------- wsUtilFile.pp | 132 +++++++++++++++++++++++---------------------- wsUtilReg.pp | 134 ++++++++++++++++++++++++--------------------- 8 files changed, 290 insertions(+), 281 deletions(-) diff --git a/README.md b/README.md index b7194ee..89ee6c0 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ https://github.com/Bill-Stewart/UninsIS/releases/ Inno Setup is a powerful, freely available tool for building installers for the Windows OS platform. -The Inno Setup philosophy for upgrading software is, put simply, install the new version "on top" of the old version. Files will be upgraded automatically according to version numbering rules (where selected/applicable), etc. However, there are some limitations: +The Inno Setup philosophy for upgrading or downgrading software is, put simply, install the new version "on top" of the old version. Files will be overwritten or retained according to version numbering rules (where selected/applicable), etc. However, there are some limitations: * If a new version of an application makes obsolete any files installed by a previous version, the obsolete files remain on the target system after upgrading. This can be mitigated in simple use cases by using Inno Setup's `[InstallDelete]` section or custom code, but this has the potential to be awkward, unwieldy, and error-prone for larger setup projects. * The above problem can also apply to registry entries: Suppose an older version of an application stores configuration data using the Windows registry but a newer version uses text-based configuration files. Without custom code, the obsolete registry entries remain on the target system after an upgrade. -* Downgrading an application is only possible by uninstalling a newer version and then installing an older version. +* Unless file versioning is handled carefully, downgrading can result in a corrupted application due to mismatched file versions. Depending on your needs, it may be preferable to uninstall an existing installed version first. UninsIS.dll provides the following capabilities: @@ -70,40 +70,34 @@ For example, you can use the UninsIS.dll functions to automatically uninstall an ``` // Wrapper for UninsIS.dll IsISPackageInstalled() function // Returns true if package is detected as installed, or false otherwise - function IsISPackageInstalled(): boolean; - begin - result := DLLIsISPackageInstalled( - 'Your_Appid_Here', // AppId - DWORD(Is64BitInstallMode()), // Is64BitInstallMode - DWORD(IsAdminInstallMode()) // IsAdminInstallMode - ) = 1; - end; + function IsISPackageInstalled(): Boolean; + begin + result := DLLIsISPackageInstalled('Your_Appid_Here', // AppId + DWORD(Is64BitInstallMode()), // Is64BitInstallMode + DWORD(IsAdminInstallMode())) = 1; // IsAdminInstallMode + end; // Wrapper for UninsIS.dll CompareISPackageVersion() function // Returns: // < 0 if version we are installing is < installed version // 0 if version we are installing is = installed version // > 0 if version we are installing is > installed version - function CompareISPackageVersion(): longint; - begin - result := DLLCompareISPackageVersion( - 'Your_AppId_Here', // AppId - 'Your_AppVersion_Here', // InstallingVersion - DWORD(Is64BitInstallMode()), // Is64BitInstallMode - DWORD(IsAdminInstallMode()) // IsAdminInstallMode - ); - end; + function CompareISPackageVersion(): LongInt; + begin + result := DLLCompareISPackageVersion('Your_AppId_Here', // AppId + 'Your_AppVersion_Here', // InstallingVersion + DWORD(Is64BitInstallMode()), // Is64BitInstallMode + DWORD(IsAdminInstallMode())); // IsAdminInstallMode + end; // Wrapper for UninsIS.dll UninstallISPackage() function // Returns 0 for success, non-zero for failure function UninstallISPackage(): DWORD; - begin - result := DLLUninstallISPackage( - 'Your_AppId_Here', // AppId - DWORD(Is64BitInstallMode()), // Is64BitInstallMode - DWORD(IsAdminInstallMode()) // IsAdminInstallMode - ); - end; + begin + result := DLLUninstallISPackage('Your_AppId_Here', // AppId + DWORD(Is64BitInstallMode()), // Is64BitInstallMode + DWORD(IsAdminInstallMode())); // IsAdminInstallMode + end; ``` In the above code, replace: @@ -116,12 +110,12 @@ For example, you can use the UninsIS.dll functions to automatically uninstall an 4. In the `[Code]` section after the wrapper functions, add or update the `PrepareToInstall()` event function to use the wrapper functions: ``` - function PrepareToInstall(var NeedsRestart: boolean): string; - begin + function PrepareToInstall(var NeedsRestart: Boolean): string; + begin result := ''; if IsISPackageInstalled() and (CompareISPackageVersion() <> 0) then UninstallISPackage(); - end; + end; ``` Change the comparison with the `CompareISPackageVersion()` function to suit your needs: @@ -175,7 +169,7 @@ DWORD IsISPackageInstalled( Pascal: ``` -function IsISPackageInstalled(AppId: pwidechar; +function IsISPackageInstalled(AppId: PWideChar; Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; ``` @@ -240,8 +234,8 @@ DWORD CompareISPackageVersion( Pascal: ``` -function CompareISPackageVersion(AppId, InstallingVersion: pwidechar; - Is64BitInstallMode, IsAdminInstallMode: DWORD): longint; +function CompareISPackageVersion(AppId, InstallingVersion: PWideChar; + Is64BitInstallMode, IsAdminInstallMode: DWORD): LongInt; ``` ### Parameters @@ -305,7 +299,7 @@ DWORD UninstallISPackage( Pascal: ``` -function UninstallISPackage(AppId: pwidechar; +function UninstallISPackage(AppId: PWideChar; Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; ``` diff --git a/UninsIS.iss b/UninsIS.iss index abbc418..74d04db 100644 --- a/UninsIS.iss +++ b/UninsIS.iss @@ -21,7 +21,7 @@ #endif #define AppGUID "{9F49B8E7-BAB8-40DB-A106-316CCCCE0823}" -#define AppVersion "1.0.0.0" +#define AppVersion "1.0.1.0" [Setup] AppId={{#AppGUID} @@ -47,75 +47,68 @@ Source: "README.md"; DestDir: "{app}" [Code] // Import IsISPackageInstalled() function from UninsIS.dll at setup time -function DLLIsISPackageInstalled(AppId: string; - Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; +function DLLIsISPackageInstalled(AppId: string; Is64BitInstallMode, + IsAdminInstallMode: DWORD): DWORD; external 'IsISPackageInstalled@files:UninsIS.dll stdcall setuponly'; // Import CompareISPackageVersion() function from UninsIS.dll at setup time function DLLCompareISPackageVersion(AppId, InstallingVersion: string; - Is64BitInstallMode, IsAdminInstallMode: DWORD): longint; + Is64BitInstallMode, IsAdminInstallMode: DWORD): LongInt; external 'CompareISPackageVersion@files:UninsIS.dll stdcall setuponly'; // Import UninstallISPackage() function from UninsIS.dll at setup time -function DLLUninstallISPackage(AppId: string; - Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; +function DLLUninstallISPackage(AppId: string; Is64BitInstallMode, + IsAdminInstallMode: DWORD): DWORD; external 'UninstallISPackage@files:UninsIS.dll stdcall setuponly'; - // Wrapper for UninsIS.dll IsISPackageInstalled() function // Returns true if package is detected as installed, or false otherwise -function IsISPackageInstalled(): boolean; - begin - result := DLLIsISPackageInstalled( - '{#AppGUID}', // AppId - DWORD(Is64BitInstallMode()), // Is64BitInstallMode - DWORD(IsAdminInstallMode()) // IsAdminInstallMode - ) = 1; +function IsISPackageInstalled(): Boolean; +begin + result := DLLIsISPackageInstalled('{#AppGUID}', // AppId + DWORD(Is64BitInstallMode()), // Is64BitInstallMode + DWORD(IsAdminInstallMode())) = 1; // IsAdminInstallMode if result then Log('UninsIS.dll - Package detected as installed') else Log('UninsIS.dll - Package not detected as installed'); - end; +end; // Wrapper for UninsIS.dll CompareISPackageVersion() function // Returns: // < 0 if version we are installing is < installed version // 0 if version we are installing is = installed version // > 0 if version we are installing is > installed version -function CompareISPackageVersion(): longint; - begin - result := DLLCompareISPackageVersion( - '{#AppGUID}', // AppId - '{#AppVersion}', // InstallingVersion - DWORD(Is64BitInstallMode()), // Is64BitInstallMode - DWORD(IsAdminInstallMode()) // IsAdminInstallMode - ); +function CompareISPackageVersion(): LongInt; +begin + result := DLLCompareISPackageVersion('{#AppGUID}', // AppId + '{#AppVersion}', // InstallingVersion + DWORD(Is64BitInstallMode()), // Is64BitInstallMode + DWORD(IsAdminInstallMode())); // IsAdminInstallMode if result < 0 then Log('UninsIS.dll - This version {#AppVersion} older than installed version') else if result = 0 then Log('UninsIS.dll - This version {#AppVersion} same as installed version') else Log('UninsIS.dll - This version {#AppVersion} newer than installed version'); - end; +end; // Wrapper for UninsIS.dll UninstallISPackage() function // Returns 0 for success, non-zero for failure function UninstallISPackage(): DWORD; - begin - result := DLLUninstallISPackage( - '{#AppGUID}', // AppId - DWORD(Is64BitInstallMode()), // Is64BitInstallMode - DWORD(IsAdminInstallMode()) // IsAdminInstallMode - ); +begin + result := DLLUninstallISPackage('{#AppGUID}', // AppId + DWORD(Is64BitInstallMode()), // Is64BitInstallMode + DWORD(IsAdminInstallMode())); // IsAdminInstallMode if result = 0 then Log('UninsIS.dll - Installed package uninstall completed successfully') else Log('UninsIS.dll - installed package uninstall did not complete successfully'); - end; +end; -function PrepareToInstall(var NeedsRestart: boolean): string; - begin +function PrepareToInstall(var NeedsRestart: Boolean): string; +begin result := ''; // If package installed, uninstall it automatically if the version we are // installing does not match the installed version; If you want to @@ -124,4 +117,4 @@ function PrepareToInstall(var NeedsRestart: boolean): string; // ...when upgrading: change <> to > if IsISPackageInstalled() and (CompareISPackageVersion() <> 0) then UninstallISPackage(); - end; +end; diff --git a/UninsIS.pp b/UninsIS.pp index 188fc9c..fe8171d 100644 --- a/UninsIS.pp +++ b/UninsIS.pp @@ -19,29 +19,29 @@ {$H+} {$R *.res} -library - UninsIS; +library UninsIS; uses wsISPackage; -function IsISPackageInstalled(AppId: pwidechar; Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; stdcall; - begin +function IsISPackageInstalled(AppId: PWideChar; Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; stdcall; +begin InnoSetupPackage.Init(AppId, Is64BitInstallMode <> 0, IsAdminInstallMode <> 0); result := InnoSetupPackage.IsInstalled(); - end; +end; -function CompareISPackageVersion(AppId, InstallingVersion: pwidechar; Is64BitInstallMode, IsAdminInstallMode: DWORD): longint; stdcall; - begin +function CompareISPackageVersion(AppId, InstallingVersion: PWideChar; Is64BitInstallMode, IsAdminInstallMode: DWORD): LongInt; +stdcall; +begin InnoSetupPackage.Init(AppId, Is64BitInstallMode <> 0, IsAdminInstallMode <> 0); result := InnoSetupPackage.CompareVersion(InstallingVersion); - end; +end; -function UninstallISPackage(AppId: pwidechar; Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; stdcall; - begin +function UninstallISPackage(AppId: PWideChar; Is64BitInstallMode, IsAdminInstallMode: DWORD): DWORD; stdcall; +begin InnoSetupPackage.Init(AppId, Is64BitInstallMode <> 0, IsAdminInstallMode <> 0); result := InnoSetupPackage.Uninstall(); - end; +end; exports IsISPackageInstalled, diff --git a/UninsIS.rc b/UninsIS.rc index 6d54911..89dbf80 100644 --- a/UninsIS.rc +++ b/UninsIS.rc @@ -1,6 +1,6 @@ 1 VERSIONINFO -FILEVERSION 1,0,0,0 -PRODUCTVERSION 1,0,0,0 +FILEVERSION 1,0,1,0 +PRODUCTVERSION 1,0,1,0 FILEOS 0x4 FILETYPE 1 { @@ -10,12 +10,12 @@ FILETYPE 1 { VALUE "CompanyName", "" VALUE "FileDescription", "UninsIS.dll" - VALUE "FileVersion", "1.0.0.0" + VALUE "FileVersion", "1.0.1.0" VALUE "InternalName", "UninsIS.dll" VALUE "LegalCopyright", "Copyright 2021 by Bill Stewart (bstewart at iname.com)" VALUE "OriginalFilename", "UninsIS.dll" VALUE "ProductName", "" - VALUE "ProductVersion", "1.0.0.0" + VALUE "ProductVersion", "1.0.1.0" } } diff --git a/history.md b/history.md index 22d6a6d..414c3f4 100644 --- a/history.md +++ b/history.md @@ -1,5 +1,15 @@ # UninsIS.dll Version History +## 1.0.1 (10 Jun 2021) + +* Update code formatting. + +* String-read from registry updated to avoid potential (but very low probability) buffer overflow error. + +* Compile using FPC 3.2.2. + +* Minor tweaks. + ## 1.0.0 (19 Mar 2021) * Initial version. diff --git a/wsISPackage.pp b/wsISPackage.pp index 24f8d82..e566a1a 100644 --- a/wsISPackage.pp +++ b/wsISPackage.pp @@ -18,33 +18,32 @@ {$MODE OBJFPC} {$H+} -unit - wsISPackage; +unit wsISPackage; interface uses - windows; + Windows; const - ERROR_UNKNOWN_PRODUCT = 1605; + ERROR_UNKNOWN_PRODUCT = 1605; ERROR_BAD_CONFIGURATION = 1610; type TInnoSetupPackage = class - private - MyIs64Bit, MyIsAdmin: boolean; - MyAppId, MySubKeyName: unicodestring; - MyRootKey: HKEY; - public - constructor Create(); - procedure Init(const AppId: unicodestring; const Is64BitInstallMode, IsAdminInstallMode: boolean); - function IsInstalled(): DWORD; - function Version(): unicodestring; - function CompareVersion(const InstallingVersion: unicodestring): longint; - function Uninstall(): DWORD; - destructor Destroy(); override; - end; //class + private + MyIs64Bit, MyIsAdmin: Boolean; + MyAppId, MySubKeyName: UnicodeString; + MyRootKey: HKEY; + public + constructor Create(); + procedure Init(const AppId: UnicodeString; const Is64BitInstallMode, IsAdminInstallMode: Boolean); + function IsInstalled(): DWORD; + function Version(): UnicodeString; + function CompareVersion(const InstallingVersion: UnicodeString): LongInt; + function Uninstall(): DWORD; + destructor Destroy(); override; + end; //class var InnoSetupPackage: TInnoSetupPackage; @@ -56,133 +55,134 @@ implementation wsUtilReg; // Returns S as a longint; if conversion fails, returns Def -function StrToIntDef(const S: unicodestring; const Def: longint): longint; - var - Code: word; - begin +function StrToIntDef(const S: UnicodeString; const Def: LongInt): LongInt; +var + Code: Word; +begin Val(S, result, Code); - if Code > 0 then result := Def; - end; + if Code > 0 then + result := Def; +end; // Compares two version strings 'a[.b[.c[.d]]]' // Returns: // < 0 if V1 < V2 // 0 if V1 = V2 // > 0 if V1 > V2 -function CompareVersionStrings(V1, V2: unicodestring): longint; - var - P, N1, N2: longint; - begin +function CompareVersionStrings(V1, V2: UnicodeString): LongInt; +var + P, N1, N2: LongInt; +begin result := 0; while (result = 0) and ((V1 <> '') or (V2 <> '')) do - begin + begin P := Pos('.', V1); if P > 0 then - begin + begin N1 := StrToIntDef(Copy(V1, 1, P - 1), 0); Delete(V1, 1, P); - end + end else if V1 <> '' then - begin + begin N1 := StrToIntDef(V1, 0); V1 := ''; - end + end else N1 := 0; P := Pos('.', V2); if P > 0 then - begin + begin N2 := StrToIntDef(Copy(V2, 1, P - 1), 0); Delete(V2, 1, P); - end + end else if V2 <> '' then - begin + begin N2 := StrToIntDef(V2, 0); V2 := ''; - end + end else N2 := 0; if N1 < N2 then result := -1 else if N1 > N2 then result := 1; - end; end; +end; constructor TInnoSetupPackage.Create(); - begin +begin MyIs64Bit := false; MyIsAdmin := false; MyAppId := ''; MySubKeyName := ''; MyRootKey := 0; - end; +end; -procedure TInnoSetupPackage.Init(const AppId: unicodestring; const Is64BitInstallMode, IsAdminInstallMode: boolean); - begin +procedure TInnoSetupPackage.Init(const AppId: UnicodeString; const Is64BitInstallMode, IsAdminInstallMode: Boolean); +begin if AppId = '' then exit(); MyAppId := AppId; MyIs64Bit := Is64BitInstallMode; MyIsAdmin := IsAdminInstallMode; if MyIs64Bit then - begin + begin if MyIsAdmin then MyRootKey := HKEY_LOCAL_MACHINE_64 else MyRootKey := HKEY_CURRENT_USER_64; - end + end else - begin + begin if MyIsAdmin then MyRootKey := HKEY_LOCAL_MACHINE_32 else MyRootKey := HKEY_CURRENT_USER_32; - end; - MySubKeyName := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + MyAppId + '_is1'; end; + MySubKeyName := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + MyAppId + '_is1'; +end; function TInnoSetupPackage.IsInstalled(): DWORD; - begin +begin // Must call Init() first if MyAppId = '' then exit(ERROR_INVALID_PARAMETER) else - begin + begin if RegKeyExists(MyRootKey, MySubKeyName) then result := 1 else result := 0; - end; end; +end; -function TInnoSetupPackage.Version(): unicodestring; - var - DisplayVersion: unicodestring; - begin +function TInnoSetupPackage.Version(): UnicodeString; +var + DisplayVersion: UnicodeString; +begin result := ''; if myAppId <> '' then - begin + begin if RegQueryStringValue(MyRootKey, MySubKeyName, 'DisplayVersion', DisplayVersion) and (DisplayVersion <> '') then result := DisplayVersion; - end; end; +end; -function TInnoSetupPackage.CompareVersion(const InstallingVersion: unicodestring): longint; - var - CurrentVersion: unicodestring; - begin +function TInnoSetupPackage.CompareVersion(const InstallingVersion: UnicodeString): LongInt; +var + CurrentVersion: UnicodeString; +begin result := 0; CurrentVersion := Version(); if (CurrentVersion <> '') and (InstallingVersion <> '') then result := CompareVersionStrings(InstallingVersion, CurrentVersion); - end; +end; function TInnoSetupPackage.Uninstall(): DWORD; - var - UninstallString, UninstallerFileName: unicodestring; - P, ProcessExitCode: DWORD; - begin +var + UninstallString, UninstallerFileName: UnicodeString; + P, ProcessExitCode: DWORD; +begin // Must call Init() first if MyAppId = '' then exit(ERROR_INVALID_PARAMETER); @@ -196,37 +196,37 @@ function TInnoSetupPackage.Uninstall(): DWORD; // Remove '"' characters P := Pos('"', UninstallerFileName); while P > 0 do - begin + begin Delete(UninstallerFileName, P, 1); P := Pos('"', UninstallerFileName); - end; + end; if not FileExists(UninstallerFileName) then exit(ERROR_BAD_CONFIGURATION); // Run uninstaller executable and wait until it closes UninstallString := '"' + UninstallerFileName + '" /SILENT /SUPPRESSMSGBOXES /NORESTART'; result := StartProcess(UninstallString, ProcessExitCode); if result = 0 then - begin + begin if ProcessExitCode = 0 then // Wait for uninstaller executable to be deleted while FileExists(UninstallerFileName) do Sleep(100); result := ProcessExitCode; - end; end; +end; destructor TInnoSetupPackage.Destroy(); - begin - end; +begin +end; initialization begin - InnoSetupPackage := TInnoSetupPackage.Create(); + InnoSetupPackage := TInnoSetupPackage.Create(); end; finalization begin - InnoSetupPackage.Destroy(); + InnoSetupPackage.Destroy(); end; end. diff --git a/wsUtilFile.pp b/wsUtilFile.pp index 1fcab20..17f6efd 100644 --- a/wsUtilFile.pp +++ b/wsUtilFile.pp @@ -18,124 +18,126 @@ {$MODE OBJFPC} {$H+} -unit - wsUtilFile; +unit wsUtilFile; interface -function FileExists(const FileName: unicodestring): boolean; +function FileExists(const FileName: UnicodeString): Boolean; -function StartProcess(const CommandLine: unicodestring; var ProcessExitCode: DWORD): DWORD; +function StartProcess(const CommandLine: UnicodeString; var ProcessExitCode: DWORD): DWORD; implementation uses - windows; + Windows; const INVALID_FILE_ATTRIBUTES = DWORD(-1); var - PerformWow64FsRedirection: boolean; - Wow64FsRedirectionOldValue: pointer; + PerformWow64FsRedirection: Boolean; + Wow64FsRedirectionOldValue: Pointer; procedure ToggleWow64FsRedirection(); - begin +begin if PerformWow64FsRedirection then - begin + begin if not Assigned(Wow64FsRedirectionOldValue) then - begin + begin if not Wow64DisableWow64FsRedirection(@Wow64FsRedirectionOldValue) then Wow64FsRedirectionOldValue := nil; - end + end else - begin + begin if Wow64RevertWow64FsRedirection(Wow64FsRedirectionOldValue) then Wow64FsRedirectionOldValue := nil; - end; end; end; +end; -function IsProcessWoW64(): boolean; - type - TIsWow64Process = function(hProcess: HANDLE; var Wow64Process: BOOL): BOOL; stdcall; - var - Kernel32: HMODULE; - IsWow64Process: TIsWow64Process; - ProcessHandle: HANDLE; - IsWoW64: BOOL; - begin +function IsProcessWoW64(): Boolean; +type + TIsWow64Process = function(hProcess: HANDLE; var Wow64Process: BOOL): BOOL; stdcall; +var + Kernel32: HMODULE; + IsWow64Process: TIsWow64Process; + ProcessHandle: HANDLE; + IsWoW64: BOOL; +begin result := false; - Kernel32 := GetModuleHandle('kernel32'); - IsWow64Process := TIsWow64Process(GetProcAddress(Kernel32, 'IsWow64Process')); + Kernel32 := GetModuleHandle('kernel32'); // LPCSTR lpModuleName + IsWow64Process := TIsWow64Process(GetProcAddress(Kernel32, // HMODULE hModule + 'IsWow64Process')); // LPCSTR lpProcName if Assigned(IsWow64Process) then - begin + begin ProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION, // DWORD dwDesiredAccess - true, // BOOL bInheritHandle - GetCurrentProcessId()); // DWORD dwProcessId + true, // BOOL bInheritHandle + GetCurrentProcessId()); // DWORD dwProcessId if ProcessHandle <> 0 then - begin - if IsWow64Process(ProcessHandle, IsWoW64) then + begin + if IsWow64Process(ProcessHandle, // HANDLE hProcess + IsWoW64) then // PBOOL Wow64Process result := IsWoW64; - CloseHandle(ProcessHandle); - end; + CloseHandle(ProcessHandle); // HANDLE hObject end; end; +end; -function FileExists(const FileName: unicodestring): boolean; - var - Attrs: DWORD; - begin +function FileExists(const FileName: UnicodeString): Boolean; +var + Attrs: DWORD; +begin ToggleWow64FsRedirection(); - Attrs := GetFileAttributesW(pwidechar(FileName)); + Attrs := GetFileAttributesW(PWideChar(FileName)); // LPCWSTR lpFileName ToggleWow64FsRedirection(); - result := (Attrs <> INVALID_FILE_ATTRIBUTES) and - ((Attrs and FILE_ATTRIBUTE_DIRECTORY) = 0); - end; + result := (Attrs <> INVALID_FILE_ATTRIBUTES) and ((Attrs and FILE_ATTRIBUTE_DIRECTORY) = 0); +end; -function StartProcess(const CommandLine: unicodestring; var ProcessExitCode: DWORD): DWORD; - var - StartInfo: STARTUPINFOW; - ProcInfo: PROCESS_INFORMATION; - OK: boolean; - begin +function StartProcess(const CommandLine: UnicodeString; var ProcessExitCode: DWORD): DWORD; +var + StartInfo: STARTUPINFOW; + ProcInfo: PROCESS_INFORMATION; + OK: Boolean; +begin result := 0; FillChar(StartInfo, SizeOf(StartInfo), 0); StartInfo.cb := SizeOf(StartInfo); FillChar(ProcInfo, SizeOf(ProcInfo), 0); ToggleWow64FsRedirection(); - OK := CreateProcessW(nil, // LPCWSTR lpApplicationName - pwidechar(CommandLine), // LPWSTR lpCommandLine - nil, // LPSECURITY_ATTRIBUTES lpProcessAttributes - nil, // LPSECURITY_ATTRIBUTES lpThreadAttributes - true, // BOOL bInheritHandles - CREATE_UNICODE_ENVIRONMENT, // DWORD dwCreationFlags - nil, // LPVOID lpEnvironment - nil, // LPCWSTR lpCurrentDirectory - StartInfo, // LPSTARTUPINFOW lpStartupInfo - ProcInfo); // LPPROCESS_INFORMATION lpProcessInformation + OK := CreateProcessW(nil, // LPCWSTR lpApplicationName + PWideChar(CommandLine), // LPWSTR lpCommandLine + nil, // LPSECURITY_ATTRIBUTES lpProcessAttributes + nil, // LPSECURITY_ATTRIBUTES lpThreadAttributes + true, // BOOL bInheritHandles + CREATE_UNICODE_ENVIRONMENT, // DWORD dwCreationFlags + nil, // LPVOID lpEnvironment + nil, // LPCWSTR lpCurrentDirectory + StartInfo, // LPSTARTUPINFOW lpStartupInfo + ProcInfo); // LPPROCESS_INFORMATION lpProcessInformation ToggleWow64FsRedirection(); if OK then + begin + if WaitForSingleObject(ProcInfo.hProcess, // HANDLE hHandle + INFINITE) <> WAIT_FAILED then // DWORD dwMilliseconds begin - if WaitForSingleObject(ProcInfo.hProcess, INFINITE) <> WAIT_FAILED then - begin - if not GetExitCodeProcess(ProcInfo.hProcess, ProcessExitCode) then + if not GetExitCodeProcess(ProcInfo.hProcess, // HANDLE hprocess + ProcessExitCode) then // LPDWORD lpexitCode result := GetLastError(); - end + end else result := GetLastError(); - CloseHandle(ProcInfo.hThread); - CloseHandle(ProcInfo.hProcess); - end + CloseHandle(ProcInfo.hThread); // HANDLE hObject + CloseHandle(ProcInfo.hProcess); // HANDLE hObject + end else result := GetLastError(); - end; +end; procedure InitializeUnit(); - begin +begin PerformWow64FsRedirection := IsProcessWoW64(); Wow64FsRedirectionOldValue := nil; - end; +end; initialization InitializeUnit(); diff --git a/wsUtilReg.pp b/wsUtilReg.pp index 4dffede..f4ad42e 100644 --- a/wsUtilReg.pp +++ b/wsUtilReg.pp @@ -18,115 +18,125 @@ {$MODE OBJFPC} {$H+} -unit - wsUtilReg; +unit wsUtilReg; interface uses - windows; + Windows; const - HKEY_CURRENT_USER_64 = $80000101; - HKEY_CURRENT_USER_32 = $80000201; + HKEY_CURRENT_USER_64 = $80000101; + HKEY_CURRENT_USER_32 = $80000201; HKEY_LOCAL_MACHINE_64 = $80000102; HKEY_LOCAL_MACHINE_32 = $80000202; // NOTE: The RootKey parameter in the below functions can also be -// HKEY_LOCAL_MACHINE_64 (works from 32-bit process) or HKEY_LOCAL_MACHINE_32 +// HKEY_LOCAL_MACHINE_64 or HKEY_LOCAL_MACHINE_32 (these work from 32-bit +// processes) // Returns true if the specified registry subkey exists or false otherwise -function RegKeyExists(RootKey: HKEY; - const SubKeyName: unicodestring): boolean; +function RegKeyExists(RootKey: HKEY; const SubKeyName: UnicodeString): Boolean; // Returns the ValueName value from the specified key and subkey into // ResultStr; returns true for success or false for failure -function RegQueryStringValue(RootKey: HKEY; - const SubKeyName, ValueName: unicodestring; - var ResultStr: unicodestring): boolean; +function RegQueryStringValue(RootKey: HKEY; const SubKeyName, ValueName: UnicodeString; + var ResultStr: UnicodeString): Boolean; implementation // Updates RootKey and AccessFlags appropriately if using _32 or _64 RootKey procedure UpdateRootKeyAndFlags(var RootKey: HKEY; var AccessFlags: REGSAM); - begin +begin if (RootKey and KEY_WOW64_32KEY) <> 0 then - begin + begin RootKey := RootKey and (not KEY_WOW64_32KEY); AccessFlags := AccessFlags or KEY_WOW64_32KEY; - end + end else if (RootKey and KEY_WOW64_64KEY) <> 0 then - begin + begin RootKey := RootKey and (not KEY_WOW64_64KEY); AccessFlags := AccessFlags or KEY_WOW64_64KEY; - end; end; +end; -function RegKeyExists(RootKey: HKEY; - const SubKeyName: unicodestring): boolean; - var - AccessFlags: REGSAM; - hkHandle: HANDLE; - begin +function RegKeyExists(RootKey: HKEY; const SubKeyName: UnicodeString): Boolean; +var + AccessFlags: REGSAM; + hkHandle: HANDLE; +begin AccessFlags := KEY_READ; UpdateRootKeyAndFlags(RootKey, AccessFlags); - result := RegOpenKeyExW(RootKey, // HKEY hKey - pwidechar(SubKeyName), // LPCSTR lpSubKey - 0, // DWORD ulOptions - AccessFlags, // REGSAM samDesired - hkHandle) = 0; // PHKEY phkResult + result := RegOpenKeyExW(RootKey, // HKEY hKey + PWideChar(SubKeyName), // LPCSTR lpSubKey + 0, // DWORD ulOptions + AccessFlags, // REGSAM samDesired + hkHandle) = 0; // PHKEY phkResult if result then RegCloseKey(hkHandle); - end; - -function RegQueryStringValue(RootKey: HKEY; - const SubKeyName, ValueName: unicodestring; - var ResultStr: unicodestring): boolean; - var - AccessFlags: REGSAM; - hkHandle: HKEY; - ValueType, ValueSize: DWORD; - pData: pointer; - begin +end; + +function RegQueryStringValue(RootKey: HKEY; const SubKeyName, ValueName: UnicodeString; + var ResultStr: UnicodeString): Boolean; +var + AccessFlags: REGSAM; + hkHandle: HKEY; + ValueType, ValueSize, BufSize: DWORD; + pData, pBuf: Pointer; +begin AccessFlags := KEY_READ; UpdateRootKeyAndFlags(RootKey, AccessFlags); - result := RegOpenKeyExW(RootKey, // HKEY hKey - pwidechar(SubKeyName), // LPCSTR lpSubKey - 0, // DWORD ulOptions - AccessFlags, // REGSAM samDesired - hkHandle) = 0; // PHKEY phkResult + result := RegOpenKeyExW(RootKey, // HKEY hKey + PWideChar(SubKeyName), // LPCSTR lpSubKey + 0, // DWORD ulOptions + AccessFlags, // REGSAM samDesired + hkHandle) = 0; // PHKEY phkResult if result then - begin + begin // First call: Get value size - result := RegQueryValueExW(hkHandle, // HKEY hKey - pwidechar(ValueName), // LPCSTR lpValueName - nil, // LPDWORD lpReserved - @ValueType, // LPDWORD lpType - nil, // LPBYTE lpData - @ValueSize) = 0; // LPDWORD lpcbData + result := RegQueryValueExW(hkHandle, // HKEY hKey + PWideChar(ValueName), // LPCSTR lpValueName + nil, // LPDWORD lpReserved + @ValueType, // LPDWORD lpType + nil, // LPBYTE lpData + @ValueSize) = 0; // LPDWORD lpcbData if result then - begin + begin // Must be REG_SZ or REG_EXPAND_SZ if (ValueType = REG_SZ) or (ValueType = REG_EXPAND_SZ) then - begin + begin GetMem(pData, ValueSize); // Second call: Get value data - result := RegQueryValueExW(hkHandle, // HKEY hKey - pwidechar(ValueName), // LPCSTR lpValueName - nil, // LPDWORD lpReserved - @ValueType, // LPDWORD lpType - pData, // LPBYTE lpData - @ValueSize) = 0; // LPDWORD lpcbData + result := RegQueryValueExW(hkHandle, // HKEY hKey + PWideChar(ValueName), // LPCSTR lpValueName + nil, // LPDWORD lpReserved + @ValueType, // LPDWORD lpType + pData, // LPBYTE lpData + @ValueSize) = 0; // LPDWORD lpcbData if result then - ResultStr := pwidechar(pData); + begin + // Last char is null + if PWideChar(pData)[(ValueSize div SizeOf(WideChar)) - 1] = #0 then + ResultStr := PWideChar(pData) + else + begin + // Last char not null: Return as null-terminated string + BufSize := ValueSize + SizeOf(WideChar); + GetMem(pBuf, BufSize); + FillChar(pBuf^, BufSize, 0); + Move(pData^, pBuf^, ValueSize); + ResultStr := PWideChar(pBuf); + FreeMem(pBuf, BufSize); + end; + end; FreeMem(pData, ValueSize); - end + end else result := false; - end; - RegCloseKey(hkHandle); end; + RegCloseKey(hkHandle); end; +end; begin end.