@echo off setlocal EnableExtensions title Linkly Port Change cd /d "%~dp0" :: One-click: self-elevates, then runs the PowerShell embedded below. :: Windows 7 (PowerShell 2.0) through Windows 11. net session >nul 2>&1 if %errorlevel% neq 0 ( echo Requesting Administrator rights... powershell.exe -NoProfile -Command "Start-Process -FilePath '%~f0' -Verb RunAs" if errorlevel 1 ( echo Failed to elevate. Right-click this file and choose "Run as administrator". pause ) exit /b ) set "LINKLY_SELF=%~f0" powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& {$p=$env:LINKLY_SELF;$t=[System.IO.File]::ReadAllText($p);$m=':::'+'LINKLY_PORT_CHANGE_PS'+':::' ;$i=$t.LastIndexOf($m);if($i -lt 0){throw 'Embedded script marker not found.'};Invoke-Expression $t.Substring($i+$m.Length)}" set ERR=%ERRORLEVEL% if %ERR% neq 0 ( echo. echo Script exited with error %ERR%. pause ) exit /b %ERR% :::LINKLY_PORT_CHANGE_PS::: # Compatible with Windows 7 (PowerShell 2.0 / .NET 3.5) through Windows 11. Set-StrictMode -Version 2 $ErrorActionPreference = 'Stop' function Test-Blank { param($Value) if ($null -eq $Value) { return $true } return [string]::IsNullOrEmpty($Value.ToString().Trim()) } function Wait-AnyKey { param([string]$Message = 'Press any key to exit...') Write-Host '' Write-Host $Message try { $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') } catch { Read-Host 'Press Enter to exit' } } function Test-IsAdministrator { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($id) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Write-Step { param([string]$Text) Write-Host '' Write-Host "=== $Text ===" -ForegroundColor Cyan } function Write-Ok { param([string]$Text) Write-Host " [OK] $Text" -ForegroundColor Green } function Write-Warn { param([string]$Text) Write-Host " [WARN] $Text" -ForegroundColor Yellow } function Write-Fail { param([string]$Text) Write-Host " [FAIL] $Text" -ForegroundColor Red } function Convert-RegistryPortToDecimal { param($RawValue) if ($null -eq $RawValue) { return $null } if ($RawValue -is [int] -or $RawValue -is [long]) { return [int]$RawValue } $text = [string]$RawValue $text = $text.Trim() if (Test-Blank $text) { return $null } if ($text -match '^0x') { return [Convert]::ToInt32($text, 16) } $parsed = 0 if ([int]::TryParse($text, [ref]$parsed)) { return $parsed } throw "IP_INTERFACE_PORT value '$text' is not a number." } function Get-ProcessIds { param($ProcessList) $ids = @() foreach ($p in @($ProcessList)) { if ($null -ne $p) { $ids += $p.Id } } return $ids } function Get-CullenHive { $is64Process = [IntPtr]::Size -eq 8 if ($is64Process) { $candidates = @( @{ SubKey = 'SOFTWARE\WOW6432Node\CullenSoftwareDesign'; Label = 'WOW6432Node (32-bit client)'; Display = 'HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\CullenSoftwareDesign' }, @{ SubKey = 'SOFTWARE\CullenSoftwareDesign'; Label = 'native 64-bit'; Display = 'HKEY_LOCAL_MACHINE\SOFTWARE\CullenSoftwareDesign' } ) } else { $candidates = @( @{ SubKey = 'SOFTWARE\CullenSoftwareDesign'; Label = '32-bit SOFTWARE (WOW64 redirected)'; Display = 'HKEY_LOCAL_MACHINE\SOFTWARE\CullenSoftwareDesign' }, @{ SubKey = 'SOFTWARE\WOW6432Node\CullenSoftwareDesign'; Label = 'WOW6432Node'; Display = 'HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\CullenSoftwareDesign' } ) } $hklm = [Microsoft.Win32.Registry]::LocalMachine foreach ($c in $candidates) { $key = $null try { $key = $hklm.OpenSubKey($c.SubKey, $true) } catch { $key = $null } if ($null -eq $key) { continue } $obj = New-Object PSObject $obj | Add-Member NoteProperty Key $key $obj | Add-Member NoteProperty SubKey $c.SubKey $obj | Add-Member NoteProperty Label $c.Label $obj | Add-Member NoteProperty Display $c.Display return $obj } return $null } function Get-InstallDirCandidates { param([string]$FromRegistry) $list = @() if (-not (Test-Blank $FromRegistry)) { $list += $FromRegistry.Trim().TrimEnd([char]'\') } $roots = @( 'C:\PC_EFT', 'C:\PC-EFT', 'C:\PC-EFTPOS', 'C:\Linkly', 'C:\Program Files\PC-EFTPOS', 'C:\Program Files\Linkly', 'C:\Program Files\PC_EFT', 'C:\Program Files\PC-EFT', 'C:\Program Files (x86)\PC-EFTPOS', 'C:\Program Files (x86)\Linkly', 'C:\Program Files (x86)\PC_EFT', 'C:\Program Files (x86)\PC-EFT' ) foreach ($r in $roots) { $list += $r } return $list } function Find-EftClntUi { param([string]$InstallDir) foreach ($dir in (Get-InstallDirCandidates -FromRegistry $InstallDir)) { if (Test-Blank $dir) { continue } $direct = Join-Path $dir 'EftClntUI.exe' if (Test-Path -LiteralPath $direct) { return (Get-Item -LiteralPath $direct).FullName } } foreach ($dir in (Get-InstallDirCandidates -FromRegistry $InstallDir)) { if (-not (Test-Path -LiteralPath $dir)) { continue } $found = Get-ChildItem -LiteralPath $dir -Filter 'EftClntUI.exe' -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.PSIsContainer } | Select-Object -First 1 if ($found) { return $found.FullName } } return $null } function Get-LinklyService { # Services.msc display names only — do not match EFTSRV / EftClnt.exe path # (those can bind to a different Windows service). $wantedDisplay = @('PC-EFTPOS Client', 'Linkly Client') $services = @(Get-WmiObject -Class Win32_Service -ErrorAction Stop) foreach ($want in $wantedDisplay) { foreach ($svc in $services) { if ($null -eq $svc) { continue } $dn = [string]$svc.DisplayName if ($dn.Trim() -eq $want) { return $svc } } } return $null } function Get-ExeDirectoryFromServicePath { param([string]$PathName) if (Test-Blank $PathName) { return $null } $exe = $null if ($PathName -match '^"([^"]+)"') { $exe = $Matches[1] } else { $exe = ($PathName -split '\s+')[0] } if (Test-Blank $exe) { return $null } return Split-Path -Parent $exe } function Stop-ClientGui { $killed = @() $procs = @(Get-WmiObject -Class Win32_Process -Filter "Name = 'EftClntUI.exe'" -ErrorAction SilentlyContinue) foreach ($p in $procs) { if ($null -eq $p) { continue } Write-Host " Stopping GUI PID $($p.ProcessId) ($($p.Name))" $result = $p.Terminate() $code = 0 if ($null -ne $result) { $code = [int]$result.ReturnValue } if ($code -eq 0) { $killed += $p.ProcessId } else { Write-Warn "Terminate returned $code for PID $($p.ProcessId); trying Stop-Process." Stop-Process -Id $p.ProcessId -Force -ErrorAction SilentlyContinue $killed += $p.ProcessId } } $deadline = (Get-Date).AddSeconds(10) do { $still = @(Get-Process -Name 'EftClntUI' -ErrorAction SilentlyContinue) if ($still.Count -eq 0) { break } Start-Sleep -Milliseconds 250 } while ((Get-Date) -lt $deadline) $leftover = @(Get-Process -Name 'EftClntUI' -ErrorAction SilentlyContinue) if ($leftover.Count -gt 0) { $ids = Get-ProcessIds $leftover throw "GUI process still running: $($ids -join ', ')" } return $killed } function Wait-ServiceStatus { param( [string]$Name, [string]$Status, [int]$TimeoutSeconds = 45 ) $deadline = (Get-Date).AddSeconds($TimeoutSeconds) do { $svc = Get-Service -Name $Name -ErrorAction Stop if ($svc.Status -eq $Status) { return $svc } Start-Sleep -Milliseconds 400 } while ((Get-Date) -lt $deadline) $svc = Get-Service -Name $Name -ErrorAction Stop throw "Service '$Name' did not reach $Status (current: $($svc.Status))." } function Restart-LinklyService { param($WmiService) $name = $WmiService.Name $display = $WmiService.DisplayName Write-Host " Restarting Windows service: $display (internal name: $name)" $svc = Get-Service -Name $name -ErrorAction Stop if ($svc.Status -eq 'Running') { Write-Host ' Stopping service...' Stop-Service -Name $name -Force -ErrorAction Stop Wait-ServiceStatus -Name $name -Status Stopped | Out-Null Write-Ok 'Service stopped.' } else { Write-Host " Service is currently $($svc.Status)." } Write-Host ' Starting service...' Start-Service -Name $name -ErrorAction Stop Wait-ServiceStatus -Name $name -Status Running | Out-Null Write-Ok 'Service is running.' } if (-not (Test-IsAdministrator)) { $self = $env:LINKLY_SELF if (Test-Blank $self) { Write-Fail 'Please right-click this file and choose Run as administrator.' Wait-AnyKey exit 1 } Start-Process -FilePath $self -Verb RunAs exit } try { $Host.UI.RawUI.WindowTitle = 'Linkly Port Change' } catch { } Write-Host 'Linkly / PC-EFTPOS - PINPAD PORT = TCPIP' -ForegroundColor White Write-Host ('-' * 52) Write-Host (" Windows " + [Environment]::OSVersion.Version.ToString() + " PowerShell " + $PSVersionTable.PSVersion.ToString()) $hive = $null $exitCode = 0 $missingLinkly = $false try { Write-Step '1. Read IP_INTERFACE_PORT' $hive = Get-CullenHive if ($null -eq $hive) { Write-Fail 'CullenSoftwareDesign was not found under:' Write-Host ' HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\CullenSoftwareDesign' Write-Host ' HKEY_LOCAL_MACHINE\SOFTWARE\CullenSoftwareDesign' Write-Host '' Write-Host 'install linkly first' -ForegroundColor Yellow $missingLinkly = $true $exitCode = 1 } if (-not $missingLinkly) { Write-Ok ("Found " + $hive.Display + " [" + $hive.Label + "]") $clientKey = $null try { $clientKey = $hive.Key.OpenSubKey('EFTCLIENT\CLIENT', $false) } catch { $clientKey = $null } $portSource = 'default (IP_INTERFACE_PORT not present)' $port = 2011 $installDir = $null $rawPort = $null if ($null -eq $clientKey) { Write-Warn 'CLIENT key not found. Using default port 2011.' } else { $valueNames = @($clientKey.GetValueNames()) if ($valueNames -contains 'IP_INTERFACE_PORT') { $rawPort = $clientKey.GetValue('IP_INTERFACE_PORT') $kind = $clientKey.GetValueKind('IP_INTERFACE_PORT') $port = Convert-RegistryPortToDecimal -RawValue $rawPort $portSource = "registry $kind" } else { Write-Warn 'IP_INTERFACE_PORT does not exist. Using default 2011.' } $installDir = [string]$clientKey.GetValue('INSTALL_DIR') $clientKey.Close() } Write-Host '' Write-Host " port number = $port (dec)" -ForegroundColor Green Write-Host " source = $portSource" if ($rawPort -is [int] -or $rawPort -is [long]) { Write-Host (" registry = 0x{0:X}" -f [int]$rawPort) } if (-not (Test-Blank $installDir)) { Write-Host " INSTALL_DIR = $installDir" } Write-Step '2. Set PINPAD PORT = TCPIP' $pinpadKey = $hive.Key.CreateSubKey('EFTCLIENT\PINPAD') if ($null -eq $pinpadKey) { throw "Could not create or open $($hive.Display)\EFTCLIENT\PINPAD" } $existing = $pinpadKey.GetValue('PORT') $pinpadKey.SetValue('PORT', 'TCPIP', [Microsoft.Win32.RegistryValueKind]::String) $pinpadKey.Flush() $written = [string]$pinpadKey.GetValue('PORT') $pinpadKey.Close() if ($written -ne 'TCPIP') { throw "Write verification failed. PORT is '$written'." } $pinpadDisplay = $hive.Display + '\EFTCLIENT\PINPAD' if (-not (Test-Blank $existing)) { Write-Ok "Updated $pinpadDisplay PORT: '$existing' -> 'TCPIP'" } else { Write-Ok "Created $pinpadDisplay PORT = 'TCPIP' (REG_SZ)" } Write-Step '3. Find PC_EFT / Linkly (EftClntUI.exe)' $wmiSvc = $null try { $wmiSvc = Get-LinklyService } catch { Write-Warn $_.Exception.Message } if ($null -ne $wmiSvc -and -not (Test-Blank $wmiSvc.PathName)) { $svcDir = Get-ExeDirectoryFromServicePath -PathName $wmiSvc.PathName if ((-not (Test-Blank $svcDir)) -and (Test-Blank $installDir)) { $installDir = $svcDir } Write-Host " Service binary folder: $svcDir" } $uiExe = Find-EftClntUi -InstallDir $installDir if (Test-Blank $uiExe) { Write-Fail 'EftClntUI.exe was not found in INSTALL_DIR or the known folders:' foreach ($d in (Get-InstallDirCandidates -FromRegistry $installDir)) { Write-Host " $d" } throw 'Cannot restart the GUI without EftClntUI.exe.' } $uiDir = Split-Path -Parent $uiExe Write-Ok "Found $uiExe" Write-Step '4. Kill GUI (EftClntUI.exe) and restart service' $killed = @(Stop-ClientGui) if ($killed.Count -gt 0) { Write-Ok "Stopped GUI process(es): $($killed -join ', ')" } else { Write-Host ' GUI was not running.' } if ($null -eq $wmiSvc) { throw 'Windows service "PC-EFTPOS Client" / "Linkly Client" was not found.' } Restart-LinklyService -WmiService $wmiSvc Write-Step '5. Start EftClntUI.exe' Start-Sleep -Seconds 3 $already = @(Get-Process -Name 'EftClntUI' -ErrorAction SilentlyContinue) if ($already.Count -gt 0) { $ids = Get-ProcessIds $already Write-Ok "GUI already running after service start (PID $($ids -join ', '))." } else { Write-Host " Launching: $uiExe" Start-Process -FilePath $uiExe -WorkingDirectory $uiDir Start-Sleep -Seconds 2 $started = @(Get-Process -Name 'EftClntUI' -ErrorAction SilentlyContinue) if ($started.Count -gt 0) { $ids = Get-ProcessIds $started Write-Ok "GUI started (PID $($ids -join ', '))." } else { Write-Warn 'EftClntUI.exe was launched but the process is not visible yet.' } } Write-Host '' Write-Host 'Done.' -ForegroundColor Green Write-Host " port number = $port (dec)" Write-Host ' PINPAD PORT = TCPIP' } } catch { $exitCode = 1 Write-Host '' Write-Fail $_.Exception.Message if ($null -ne $_.InvocationInfo -and $null -ne $_.InvocationInfo.PositionMessage) { Write-Host $_.InvocationInfo.PositionMessage -ForegroundColor DarkGray } } finally { if ($null -ne $hive -and $null -ne $hive.Key) { try { $hive.Key.Close() } catch { } } } Wait-AnyKey exit $exitCode