1

I am working on a little project to deploy client applications in my company via GPO (Citrix Receiver and HDX Real Time Engine).

HDX Client can be installed only if Citrix Receiver has been installed beforehand. I am also testing whether HDX is already installed on the machine along with its version. See what I have done so far:

setlocal enabledelayedexpansion

REM Logs Share

set logshare=\\[path_to_logs_share]\


REM Search for Citrix Receiver Client

reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Citrix\PluginPackages\XenAppSuite\ICA_Client

REM If Client has been found - search for HDX Client starting by "Citrix HDX"

if %errorlevel% EQU 0 (

    reg query HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s /v Displayname ^| findstr /c:"Citrix HDX"

    REM If HDX Client has been detected set a variable containing the version of it

    if !errorlevel! EQU 0 (

        for /F "tokens=8" %%a in ('reg query HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s /v Displayname ^| findstr /c:"Citrix HDX"') do set HDX_Version=%%a

        REM If HDX version is greater or eqaul to 2.4

        if %HDX_Version% GEQ 2.4 (
            echo Current version is ok >> %logshare%%ComputerName%.txt
        ) else (
            echo Installation HDX 2.4 in progress >> %logshare%%ComputerName%.txt
        )
    ) else (

        REM In case HDX has not been detected at all - installation begins

        echo Installation HDX 2.4 in progress >> %logshare%%ComputerName%.txt
    )
) else (
REM In case Citrix Client is missing

    echo Client Citrix missing
)

Endlocal

The issue is that testing %errorlevel% twice in a batch script is apparently awkward. I do not know how to work out that problem.

The line output by reg and findstr is for example:

    DisplayName REG_SZ  Citrix HDX RealTime Media Engine 2.4

The version at end of this registry string value must be processed to determine if an already installed Citrix client must be updated or nothing must be done.

Mofi
  • 38,783
  • 14
  • 62
  • 115
Charlypop
  • 116
  • 1
  • 2
  • 13

1 Answers1

1

In general it is better to use if not errorlevel 1 instead of if %errorlevel% EQU 0 or if !errorlevel! EQU 0 because this syntax works really everywhere. if not errorlevel 1 means IF exit code of previous command/application is NOT GREATER OR EQUAL 1 or in other words is LESS THAN 1 or is EQUAL 0 because nearly no command/application exits with a negative value according to the guidelines of Microsoft. This syntax working since MS-DOS inside and outside a command block is explained by help of command IF output on running in a command prompt window if /?.

The redirection operator | must be escaped with ^ only on being used inside set of command FOR. The usage of ^| on standard command line like on on second reg query command line results in getting the vertical bar interpreted as literal character and REG outputs an error message because of too many parameters.

But the main reason for code not working as expected is the line:

if %HDX_Version% GEQ 2.4

There is set HDX_Version=%%a inside the command block starting with ( on first IF line and ending with matching ) in last but one non-empty line which defines this environment variable with string read from Windows registry. The variable reference %HDX_Version% is replaced by Windows command processor on parsing the entire command block before running the first IF. So most likely %HDX_Version% is replaced by nothing and the IF condition executed is if GEQ 2.4 which results in an exit of batch file execution because of a syntax error. It would be necessary to use here also delayed environment variable expansion, i.e. use syntax !HDX_Version! on this IF command line.

However, the code would also not work with if !HDX_Version! GEQ 2.4 because of the comparison operators EQU, NEQ, GEQ, etc. are primary designed for comparing two 32-bit signed integer values. If one of the two arguments strings left and right the operator cannot be successfully converted to a 32-bit signed integer, cmd.exe runs a string comparison and compares the integer value returned by the string comparison function against value 0 on being equal, not equal, greater than, etc. Floating point values containing . are not supported by cmd.exe at all. For more details see answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files.

I suggest the following code for this task:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogShare=\\[path_to_logs_share]\"
if not exist "%LogShare%" set "LogShare=%TEMP%\"
set "MinimumMajorVersion=2"
set "MinimumMinorVersion=4"

set "SoftwareKey=HKEY_LOCAL_MACHINE\SOFTWARE"
if not "%ProgramFiles(x86)%" == "" if not exist %SystemRoot%\Sysnative\cmd.exe set "SoftwareKey=%SoftwareKey%\Wow6432Node"

rem Search for Citrix receiver client.
%SystemRoot%\System32\reg.exe query %SoftwareKey%\Citrix\PluginPackages\XenAppSuite\ICA_Client >nul 2>nul
if errorlevel 1 (
    echo Citrix client is not installed.>>"%LogShare%%ComputerName%.txt"
    goto InstallClient
)

rem Search for HDX client starting by "Citrix HDX" if receiver client was found.
for /F "tokens=8" %%I in ('%SystemRoot%\System32\reg.exe query %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall /s 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"') do set "HDX_Version=%%I" & goto EvaluateVersion

echo HDX version not found under registry key %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall.>>"%LogShare%%ComputerName%.txt"
goto InstallClient

:EvaluateVersion
for /F delims^=.0123456789^ eol^= %%I in ("%HDX_Version%") do (
    echo Determined HDX version string "%HDX_Version%" is of unknown format.>>"%LogShare%%ComputerName%_Error.txt"
    goto EndCitrixCheck
)

for /F "tokens=1,2 delims=." %%I in ("%HDX_Version%") do (
    if %%I LSS %MinimumMajorVersion% (
        echo Determined HDX version %HDX_Version% is too low.>>"%LogShare%%ComputerName%.txt"
        goto InstallClient
    )
    if %%I EQU %MinimumMajorVersion% (
        if "%%J" == "" (
            if not %MinimumMinorVersion% == 0 (
                echo Determined HDX version %HDX_Version% has no minor version number.>>"%LogShare%%ComputerName%.txt"
                goto InstallClient
            )
        ) else if %%J LSS %MinimumMinorVersion% (
            echo Determined HDX version %HDX_Version% is too low.>>"%LogShare%%ComputerName%.txt"
            goto InstallClient
        )
    )
    echo Determined HDX version %HDX_Version% is okay.>>"%LogShare%%ComputerName%.txt"
    goto EndCitrixCheck
)
echo Determined HDX version string "%HDX_Version%" is of unknown format.>>"%LogShare%%ComputerName%_Error.txt"
goto EndCitrixCheck

:InstallClient
echo Installation of HDX in progress ...>>"%LogShare%%ComputerName%.txt"

rem Add here the command lines to install the Citrix client.

:EndCitrixCheck
if "%TEMP%\" == "%LogShare%" del "%LogShare%%ComputerName%.txt"
endlocal

The first and last IF condition in this code are just for making it possible for everyone reading this code to run it without an error message.

This batch code works even on Windows XP although this is most likely not a requirement for this task.

The Windows x86 on Windows x64 emulation must be taken into account on accessing registry keys under HKEY_LOCAL_MACHINE\SOFTWARE according to the Microsoft articles:

The environment variable SoftwareKey is defined first with standard registry key HKEY_LOCAL_MACHINE\SOFTWARE. This is the right key for 32-bit Windows and batch file being executed in 32-bit environment by the x86 versions of cmd.exe and reg.exe executed from %SystemRoot%\SysWOW64. But it is necessary to append \Wow6432Node to access the right key on batch file being executed by x64 version of cmd.exe starting x64 version of reg.exe stored both in %SystemRoot%\System32 on 64-bit Windows.

The rewritten code avoids definition/modification of an environment variable in a command block referenced in same command block. Therefore delayed environment variable expansion is not needed by this code which solves first main problem.

Let us look on the long command line:

for /F "tokens=8" %%I in ('%SystemRoot%\System32\reg.exe query %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall /s 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"') do set "HDX_Version=%%I" & goto EvaluateVersion

FOR executes in a separate command process started with cmd.exe /C and the string in the round brackets between the two ' in background for example the command line:

C:\Windows\System32\reg.exe query HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s 2>nul | C:\Windows\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"

REG outputs everything of uninstall registry key for x86 applications to handle STDOUT. An error message output to handle STDERR would be redirected with 2>nul to device NUL to suppress it. REG should not output an error message in this case. It is possible to use additionally /v DisplayName in addition to /s on Windows Vista and later Windows versions to get output by REG just all values with name DisplayName. The output of REG is redirected with | to handle STDIN of command FINDSTR.

FINDSTR searches in every line case-insensitive with a regular expression for a string starting anywhere in line with DisplayName, having 0 or more characters and the string Citrix HDX. The usage of /R /C:"DisplayName.*Citrix HDX" instead of just "DisplayName.*Citrix HDX" is necessary as otherwise FINDSTR would run a regular expression find searching for DisplayName and 0 or more characters and the string Citrix OR the string HDX anywhere in a line which is not wanted here. FINDSTR outputs hopefully always just the line with the string value of interest to handle STDOUT of separate command process.

Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with reg and findstr in a separate command process started in background.

FOR captures the output written to STDOUT of started command process and processes it line by line with ignoring empty lines and by default also lines starting with a semicolon which both do not occur here. Other lines are split up into substrings (tokens) using normal space and horizontal tab as delimiters and assigns just eight space/tab delimited substring to specified loop variable I because of option tokens=8. FOR runs never the command SET if there is no line with at least eight space/tab separated strings. The string assigned to loop variable I is assigned as is to environment variable HDX_Version and the batch file execution continues on the line below label EvaluateVersion.

The second FOR validates if the string assigned to HDX_Version consists of only one or more dots/digits. FOR outputs an error message to an error file instead of standard text file in case of the string assigned to HDX_Version contains any other character than .0123456789 including a ; at beginning of the string. The execution of the batch file continues at end of batch file as this error condition cannot be handled automatically. It could be that the displayed string changed since writing this batch file which should be at least detected and reported by the batch file.

Otherwise on HDX version being most likely of format major.minor one more FOR is used to split up the version into two strings which are integers for evaluation with the integer comparators of command IF. The minor version number must not exist except the major version number is equal the minimum major version number. The missing minimum minor version number is in this case interpreted as 0 and so an installation/update of Citrix client is necessary, too.

It is very unlikely, but nevertheless possible that the string assigned to environment variable HDX_Version consist of only one or more . in which case the third FOR does not execute any command line in command block. This results also in writing an error message into the error file and a jump to end of batch file.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • echo /?
  • endlocal /?
  • goto /?
  • if /?
  • reg /?
  • reg query /?
  • rem /?
  • set /?
  • setlocal /?
Mofi
  • 38,783
  • 14
  • 62
  • 115
  • 1
    Man you've done so much! Thank you very much for all of this! I have not had the opportunity to give it a try unfortunately but I just wanted you to know that I am so grateful for that job. I shall invite you at home for a coffee! :) – Charlypop Dec 12 '18 at 21:11