Files
histsdk/docs/reverse-engineering/wcf-historian-info.md
T
Joseph Doherty fbd839077b R1.4 GetHistorianInfo: bounded out on 2020 WCF (named-value-only, no struct)
Captured the native HistorianAccess.GetHistorianInfo(out HistorianInfo, out err)
and decoded the wire: over 2020 WCF, GETHI is a named-value query whose only
working key is "HistorianVersion" (response ~30 bytes = the version string).
Probed 7 storage-mode key names -> all ok=False/err. The 518-byte HISTORIAN_INFO
struct + EventStorageMode@514 is the 2023R2 HCAL-native/gRPC model (confirmed
from the decompiled 2023R2 source); on 2020 the native client derives the mode
outside the WCF wire.

Version is already exposed (ProbeAsync/GetRuntimeParameterAsync), so no hollow
GetHistorianInfoAsync is shipped (same disposition as R1.3 timezone). This
completes the reachable 2020-WCF M1 read surface; remaining M1 = config writes
(gated on explicit request) or gRPC/2023R2-only items.

RE aids kept: harness `historian-info` scenario, Capture-HistorianInfo.ps1,
decode-historian-info-capture.py, and StringHandleProbeDiagnosticTests
.GETHI_CandidateInfoNames (asserts the named-value-only finding; gated).
Docs: wcf-historian-info.md (new) + roadmap/matrix/wall-doc updates. 230 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
2026-06-20 23:42:27 -04:00

4.1 KiB

GetHistorianInfo over 2020 WCF — GETHI is named-value-only (HCAL R1.4)

Status: ⚠ Bounded out on 2020 WCF (2026-06-20). GetHistorianInfoAsync is not shipped: the one field that motivates it — EventStorageMode — is not on the 2020 WCF wire. The version field that GETHI does return over WCF is already exposed (ProbeAsync, GetRuntimeParameterAsync("HistorianVersion")), so there is nothing new to ship here without a 2023 R2 gRPC server. This parallels R1.3 (GetServerTimeZone), which is likewise 2023R2-only.

What the capture showed

scripts/Capture-HistorianInfo.ps1 drives the native HistorianAccess.GetHistorianInfo(out HistorianInfo, out error) through the instrumented (instrument-wcf-{write,read}message) current/aahClientManaged.dll. The native call succeeds and returns EventStorageMode = Blocks, ServerVersion = 20,0,000,000, no error.

But the wire tells a different story (scripts/decode-historian-info-capture.py):

  • The only GETHI op on the wire is aa/Stat/GETHI(handle, pRequestBuff) with pRequestBuff = 53 67 02 00 (sig 0x6753 + version 2) + uint charCount(16) + UTF-16 "HistorianVersion" — i.e. the named-value request, identical to the GETRP/version shape.
  • Its response pResponseBuff is ~30 bytes: uint charCount(12) + UTF-16 "20,0,000,000" (+ a 02 00 01 00 trailer). Just the version — not a 518-byte struct.
  • The post-GETHI ops in the same capture are Hist/UpdC3 + a run of Stat/GetSystemParameter (AllowOriginals, HistorianPartner, HistorianVersion, MaxCyclicStorageTimeout, RealTimeWindow, FutureTimeThreshold, AllowRenameTags). None carries a storage-mode value. So the native wrapper's EventStorageMode is derived by the C++ HCAL outside the WCF wire, not fetched over it.

Probe: does GETHI expose storage mode under any name?

StringHandleProbeDiagnosticTests.GETHI_CandidateInfoNames_AgainstLocalHistorian (gated on HISTORIAN_HOST=localhost) issues GETHI for HistorianVersion plus seven storage-mode name guesses. Result on the live 2020 server:

GETHI parameter name result
HistorianVersion ok=True, respLen=32 (version)
EventStorageMode, EventStorageType, StorageType, HistorianEventStorageMode, EventStorage, StorageMode, HistorianInfo ok=False, errLen=5, empty

So GETHI on 2020 WCF is a strict named-value lookup with exactly one known-good key (HistorianVersion). There is no storage-mode key, no full-struct request.

Why the 518-byte struct doesn't apply here

The 2023 R2 decompiled ArchestrA.HistorianAccess.GetHistorianInfo (analysis folder) allocates a 518-byte HISTORIAN_INFO struct, pre-inits int32 @514 to -1, calls native HCAL (vtable+648) which fills it, then reads version (UTF-16 @0) + EventStorageMode (@514: -1=Unsupported, 0=Database, else=Blocks). That is the HCAL-native / 2023R2 gRPC front-door model (StatusService.GetHistorianInfo returns bytes btHistorianInfo). On 2020 WCF that struct is never marshaled across the wire — only the version named-value is. The native client's EventStorageMode therefore comes from C++-internal state the managed WCF replay cannot observe or reproduce.

Conclusion / where it lands

  • 2020 WCF: GetHistorianInfoAsync would add nothing over existing surface (version only) and could not report a real EventStorageMode — so it is intentionally not shipped (no hollow Unsupported-returning API; project discipline: don't ship misleading behavior).
  • 2023 R2 gRPC: Status.GetHistorianInfo returns the full 518-byte btHistorianInfo; decode version@0 + EventStorageMode@514 there. Build + verify against a live 2023 R2 server. The HistorianInfo / HistorianEventStorageMode public types should land alongside that path.

Tooling kept as RE aids

  • tools/AVEVA.Historian.NativeTraceHarness historian-info scenario (drives the native call).
  • scripts/Capture-HistorianInfo.ps1 + scripts/decode-historian-info-capture.py.
  • StringHandleProbeDiagnosticTests.GETHI_CandidateInfoNames_AgainstLocalHistorian (locks the named-value-only finding; gated).