param( [string]$HostName = "localhost", [UInt16]$Port = 32568, [string]$UserName = $null, [int]$ConnectionWaitSeconds = 15, [switch]$IntegratedSecurity, [switch]$UseArchestrAUser ) $ErrorActionPreference = "Stop" function ConvertTo-PlainText { param([Security.SecureString]$SecureString) $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) try { [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { if ($bstr -ne [IntPtr]::Zero) { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } } function Set-PropertyIfPresent { param( [object]$Object, [string]$Name, [object]$Value ) $property = $Object.GetType().GetProperty($Name) if ($null -ne $property -and $property.CanWrite) { $property.SetValue($Object, $Value, $null) } } function Get-PropertyText { param( [object]$Object, [string]$Name ) if ($null -eq $Object) { return $null } $property = $Object.GetType().GetProperty($Name) if ($null -eq $property -or -not $property.CanRead) { return $null } $value = $property.GetValue($Object, $null) if ($null -eq $value) { return $null } return $value.ToString() } function Get-PropertyValue { param( [object]$Object, [string]$Name ) if ($null -eq $Object) { return $null } $property = $Object.GetType().GetProperty($Name) if ($null -eq $property -or -not $property.CanRead) { return $null } return $property.GetValue($Object, $null) } function Get-UserNameShape { param([string]$Value) if ([string]::IsNullOrWhiteSpace($Value)) { return "empty" } if ($Value.Contains("\")) { return "domain-or-machine-qualified" } if ($Value.Contains("@")) { return "upn" } return "unqualified" } $repoRoot = Split-Path -Parent $PSScriptRoot $dllDir = Join-Path $repoRoot "current" $managedDll = Join-Path $dllDir "aahClientManaged.dll" if (-not (Test-Path -LiteralPath $managedDll)) { throw "Missing aahClientManaged.dll at $managedDll" } if (-not $IntegratedSecurity -and [string]::IsNullOrWhiteSpace($UserName)) { $defaultUser = "{0}\{1}" -f $env:COMPUTERNAME, $env:USERNAME $entered = Read-Host "Historian user [$defaultUser]" if ([string]::IsNullOrWhiteSpace($entered)) { $UserName = $defaultUser } else { $UserName = $entered } } $plainPassword = $null if (-not $IntegratedSecurity) { $securePassword = Read-Host "Historian password" -AsSecureString $plainPassword = ConvertTo-PlainText $securePassword } [AppDomain]::CurrentDomain.add_AssemblyResolve({ param($sender, $eventArgs) $assemblyName = New-Object Reflection.AssemblyName($eventArgs.Name) $candidate = Join-Path $script:dllDir ($assemblyName.Name + ".dll") if (Test-Path -LiteralPath $candidate) { return [Reflection.Assembly]::LoadFrom($candidate) } return $null }) Push-Location $dllDir try { $assembly = [Reflection.Assembly]::LoadFrom($managedDll) $accessType = $assembly.GetType("ArchestrA.HistorianAccess", $true) $argsType = $assembly.GetType("ArchestrA.HistorianConnectionArgs", $true) $statusType = $assembly.GetType("ArchestrA.HistorianConnectionStatus", $true) $errorType = $assembly.GetType("ArchestrA.HistorianAccessError", $true) $connectionType = $assembly.GetType("ArchestrA.HistorianConnectionType", $true) $argsObject = [Activator]::CreateInstance($argsType) Set-PropertyIfPresent $argsObject "ServerName" $HostName Set-PropertyIfPresent $argsObject "TcpPort" $Port Set-PropertyIfPresent $argsObject "ReadOnly" $true Set-PropertyIfPresent $argsObject "IntegratedSecurity" ([bool]$IntegratedSecurity) Set-PropertyIfPresent $argsObject "UseArchestrAUser" ([bool]$UseArchestrAUser) Set-PropertyIfPresent $argsObject "ConnectionType" ([Enum]::Parse($connectionType, "Process")) if (-not $IntegratedSecurity) { Set-PropertyIfPresent $argsObject "UserName" $UserName Set-PropertyIfPresent $argsObject "Password" $plainPassword } $access = [Activator]::CreateInstance($accessType) $errorObject = [Activator]::CreateInstance($errorType) $openParameterTypes = [Type[]]@($argsType, $errorType.MakeByRefType()) $openMethod = $accessType.GetMethod("OpenConnection", $openParameterTypes) if ($null -eq $openMethod) { throw "Could not find HistorianAccess.OpenConnection(HistorianConnectionArgs, HistorianAccessError ByRef)." } $invokeArgs = New-Object "object[]" 2 $invokeArgs[0] = $argsObject $invokeArgs[1] = $errorObject $success = $false $exceptionText = $null try { $success = [bool]$openMethod.Invoke($access, $invokeArgs) $errorObject = $invokeArgs[1] } catch [Reflection.TargetInvocationException] { $inner = $_.Exception.InnerException if ($null -ne $inner) { $exceptionText = "{0}: {1}" -f $inner.GetType().FullName, $inner.Message } else { $exceptionText = $_.Exception.Message } } $connected = $false $pending = $false $statusErrorOccurred = $false $statusError = $null $getStatusMethod = $accessType.GetMethod("GetConnectionStatus", [Type[]]@($statusType.MakeByRefType())) if ($null -ne $getStatusMethod) { $deadline = [DateTime]::UtcNow.AddSeconds([Math]::Max($ConnectionWaitSeconds, 1)) do { $statusObject = [Activator]::CreateInstance($statusType) $statusArgs = New-Object "object[]" 1 $statusArgs[0] = $statusObject [void]$getStatusMethod.Invoke($access, $statusArgs) $statusObject = $statusArgs[0] $connected = [bool](Get-PropertyValue $statusObject "ConnectedToServer") $pending = [bool](Get-PropertyValue $statusObject "Pending") $statusErrorOccurred = [bool](Get-PropertyValue $statusObject "ErrorOccurred") $statusError = Get-PropertyValue $statusObject "Error" if (($connected -and -not $pending) -or $statusErrorOccurred -or (-not $connected -and -not $pending)) { break } Start-Sleep -Milliseconds 250 } while ([DateTime]::UtcNow -lt $deadline) } $result = [ordered]@{ Operation = "aahClientManaged.OpenConnection" HostName = $HostName Port = $Port IntegratedSecurity = [bool]$IntegratedSecurity UseArchestrAUser = [bool]$UseArchestrAUser UserNameShape = Get-UserNameShape $UserName Success = $success ConnectedToServer = $connected ConnectionPending = $pending ConnectionErrorOccurred = $statusErrorOccurred Exception = $exceptionText ErrorType = Get-PropertyText $errorObject "ErrorType" ErrorCode = Get-PropertyText $errorObject "ErrorCode" ErrorDescription = Get-PropertyText $errorObject "ErrorDescription" ErrorServerName = Get-PropertyText $errorObject "ServerName" ConnectionStatusErrorType = Get-PropertyText $statusError "ErrorType" ConnectionStatusErrorCode = Get-PropertyText $statusError "ErrorCode" ConnectionStatusErrorDescription = Get-PropertyText $statusError "ErrorDescription" } $result | ConvertTo-Json -Depth 4 if ($success) { $closeError = [Activator]::CreateInstance($errorType) $closeParameterTypes = [Type[]]@($errorType.MakeByRefType()) $closeMethod = $accessType.GetMethod("CloseConnection", $closeParameterTypes) if ($null -ne $closeMethod) { $closeArgs = New-Object "object[]" 1 $closeArgs[0] = $closeError [void]$closeMethod.Invoke($access, $closeArgs) } } $dispose = $access -as [IDisposable] if ($null -ne $dispose) { $dispose.Dispose() } } finally { if ($null -ne $plainPassword) { $plainPassword = $null } Pop-Location }