Files
histsdk/docs/reverse-engineering/wcf-status-localhost.md
T
Joseph Doherty 4da5287d01 R1.2 GetRuntimeParameter + string-handle wall RESOLVED (handle-format bug)
Execute HCAL roadmap R1.2 (GetRuntimeParameterAsync) end-to-end, and in doing so
discover that the "string-handle wall" blocking R1.1/R1.4/R1.5/R1.6 was a handle
FORMAT bug, not a missing native session/filter registration.

R1.2 (shipped, live-verified):
- Captured native GetRuntimeParameter -> WCF op aa/Stat/GETRP (string-handle op,
  GETHI's shape), via scripts/Capture-RuntimeParam.ps1 + instrument-wcf-{write,read}message.
- HistorianRuntimeParameterProtocol serializes pRequestBuff (54 67 01 00 + uint
  nameCount + per-name uint charCount + UTF-16) and parses pResponseBuff (version +
  uint resultCount + CRetVariant 0x43 VT_BSTR + uint16 len + uint16 charCount + UTF-16).
- IStatusServiceContract2.GetRuntimeParameter (GETRP) op; HistorianWcfStatusClient
  passes the Open2 storage-session GUID as the string handle, UPPERCASE.
- Public HistorianClient.GetRuntimeParameterAsync(name) via the dialect.
- Golden WcfRuntimeParameterProtocolTests + gated live test; returns HistorianVersion.

String-handle wall RESOLVED (proven, public APIs deferred):
- The Open2 storage GUID works as the string handle when sent UPPERCASE
  (ToString("D").ToUpperInvariant()); earlier "blocked" probes used lowercase.
- Live-probed GETHI (R1.4) -> returns data; ExeC (R1.1) -> Retr.GetV prime -> ExeC ->
  GetR returns a BinaryFormatter-serialized .NET DataTable. Gated
  StringHandleProbeDiagnosticTests + scripts/Capture-ExecSql.ps1 + exec-sql harness scenario.
- Docs flipped: wcf-string-handle-wall.md RESOLVED banner; roadmap R1.1/R1.4 reachable,
  R1.5/R1.6 likely; wcf-status-localhost.md GETRP section.
- R1.1/R1.4 public APIs NOT shipped: ExeC needs a GetR paging loop + a BinaryFormatter-
  stream parser (BinaryFormatter is removed from .NET 10); GETHI full-info struct needs
  its own capture.

223 unit tests pass; gated live tests green against the local 2020 Historian.

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

3.8 KiB

WCF Status Evidence

Commands:

dotnet run --no-build --project tools\AVEVA.Historian.ReverseEngineering -- wcf-status localhost 32568
dotnet run --no-build --project tools\AVEVA.Historian.ReverseEngineering -- wcf-status localhost 32568 Version

Confirmed:

  • The local status endpoint is net.tcp://localhost:32568/Stat.
  • IStatusServiceContract2 is a static WCF contract named Stat in namespace aa. The managed definitions now include GetSystemParameter, GETHI, PNGS, and PNGP.
  • GetInterfaceVersion returns code 0, version 0 on the local 2020 install.
  • The decompiled CStatusConnectionWCF.GetServerTime implementation is a WCF-path stub that returns success without calling the Stat service. The managed direct call likewise returns code 0 with size 0 and no buffer.

Observed sanitized localhost results:

  • GetSystemTimeZoneName(handle: 0) returns code 4 and no value.
  • IsDBCaseSensitive(handle: 0) returns code 4.
  • GetSystemParameter(handle: 0, "Version") returns false with no error buffer.

Re-tested 2026-06-20 with a real authenticated client handle (full Open2 auth chain), not handle: 0:

  • GetSystemParameter(handle, "HistorianVersion") → real version string (works; shipped as GetSystemParameterAsync).
  • GetSystemTimeZoneName(handle) → return code 0x00000000 (success) but an empty value string. Same channel/handle that makes GetSystemParameter return real data, so this is the op's own behavior, not an auth/marshalling gap. GetSystemTimeZoneName is a member of the GetServerTime stub family: the 2020 WCF path returns success without producing a value (the native client computes the zone locally). It only becomes a real round-trip on the 2023 R2 gRPC front door (Status.GetSystemTimeZoneName), which is absent on this box.

Interpretation:

  • Stat endpoint routing is confirmed, but status operations that require a real client handle are not usable until managed session open is solved.
  • GetServerTime should not be promoted into the public SDK as a real server time call from this WCF path; native evidence shows it is a no-op stub here.
  • GetServerTimeZoneAsync (roadmap R1.3) is NOT a trivial WCF op on 2020 — it is a stub returning empty. Do not ship it over the 2020 WCF transport. Deliver it only against a live 2023 R2 gRPC server. Reclassified in docs/plans/hcal-roadmap.md.

GETRP / GetRuntimeParameter (roadmap R1.2) — DONE, live-verified 2026-06-20

Captured the native HistorianAccess.GetRuntimeParameter(List<string>, out List<object>) WCF traffic with scripts/Capture-RuntimeParam.ps1 (instrument-wcf-{write,read}message). Findings:

  • The WCF op is aa/Stat/GETRPbool GETRP(string handle, byte[] pRequestBuff, out byte[] pResponseBuff, out byte[] errorBuffer), i.e. the same string-handle + request/response-buffer shape as GETHI, not the simple GetSystemParameter(uint, string) shape the roadmap originally assumed.
  • The string handle is the Open2 storage-session GUID (the value ParseOpenConnectionResponse reads from outBuff[5..21]), sent UPPERCASE, dash-separated, no braces (ToString("D").ToUpperInvariant()).
  • Unlike GETHI (which the earlier probe found blocked), GETRP succeeds from the pure-managed client with that handle: GetRuntimeParameter("HistorianVersion")20,0,000,000.
  • pRequestBuff = 54 67 01 00 (sig+version) + uint nameCount + per name(uint charCount + UTF-16LE). pResponseBuff = version(1) + uint resultCount + CRetVariant(0x43 VT_BSTR + uint16 payloadLen + uint16 charCount + UTF-16LE).

Shipped as HistorianClient.GetRuntimeParameterAsync(name). See HistorianRuntimeParameterProtocol, golden WcfRuntimeParameterProtocolTests, and the handle-format lead in wcf-string-handle-wall.md §Update (retry GETHI/ExeC uppercased).