Files
histsdk/docs/reverse-engineering/wcf-string-handle-wall.md
T
Joseph Doherty 84ec175f76 docs: map the 2020 WCF string-handle wall (R1.4 GETHI blocked)
Probed R1.4 GetHistorianInfo (GETHI) live against the local 2020 server.
GETHI returns native error type 4 / code 1 for the exact native request shape
across 5 handle formats (storage GUID, context GUID, uint decimal/X8/0x-hex)
even with Stat.GetV ×2 priming. Its result is discarded (TryRun) in the only
place it's used, so it was never actually verified to return data managed-side.

This confirms a structural boundary on the 2020 WCF surface: ops taking a uint
client handle work (the proven read/browse/metadata/status/event surface);
ops taking a string GUID handle (ExeC, QTB, QTG, GETHI, GetTepByNm, ...) are
blocked behind an unmapped native session/filter registration. Every remaining
M1 *read* item (R1.1/R1.4/R1.5/R1.6) is string-handle -> all gated on that one
RE target. Reachable uint-handle items: R1.7 event filters, R1.8/R1.9 summary
modes.

New: docs/reverse-engineering/wcf-string-handle-wall.md (full dichotomy + table).
Roadmap R1.4/R1.5/R1.6 struck through; reachable items re-pointed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 15:32:15 -04:00

59 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# The 2020 WCF string-handle wall (2026-06-20)
Live-probing the local **Historian 2020** (WCF, port 32568) for HCAL roadmap M1
surfaced a clean structural boundary on what the pure-managed client can call. It
explains why R1.1/R1.4/R1.5 all fail and identifies the single RE target that
unblocks the rest of the M1 read surface.
## The dichotomy
Retrieval/Status/History ops split by the **type of their first (handle) parameter**:
| Handle type | Examples | Status on 2020 WCF |
|---|---|---|
| **`uint` client handle** (Open2 output) | `StartQuery2`, `GetNextQueryResultBuffer2`, `IsOriginalAllowed`, `GetTagInfosFromName`/`GetTagInfoFromName` (GetTgByNm), `GetSystemParameter`, `StartEventQuery`, `GetNextEventQueryResultBuffer`, `RegisterTags2`, `EnsureTags2`, `UpdateClientStatus3` | ✅ **work** — the proven read/browse/metadata/status-param/event/write surface |
| **`string` GUID handle** | `ExecuteSqlCommand` (ExeC), `StartTagQuery` (QTB), `QueryTag` (QTG), `GetHistorianInfo` (GETHI), `GetTagExtendedPropertiesFromName` (GetTepByNm), `GetTagInfosFromName2` (GetTgByNm2), `GetTagidsByTagnameAndSource` | ⛔ **blocked** — native error type 4, code **51 (InvalidParameter)** or **1 (Failure)** |
## Evidence (this probe + prior notes)
- **ExeC** → type 4 / code 51 for every handle variant (storageGuid, contextGuid).
Matches `implementation-status.md` ~982 / ~1404 ("StartTagQuery depends on earlier
native session/filter registration … do not wire through guessed calls").
- **GETHI** (`HistorianVersion` param query — the *exact* native request shape from
`BuildGetHistorianInfoRequest`, with `Stat.GetV ×2` priming) → type 4 / code **1**
for all five handle formats tried: storage-session GUID, context GUID, uint as
decimal, uint as `X8` hex, uint as `0x`-hex. In the only place GETHI is used (the
event-priming chain) its result is wrapped in `TryRun` and **discarded**, so there
was never evidence it actually returns data from the managed client.
- **GetTepByNm / QTB / QTG / GetTgByNm2** all take a `string handle` → same family.
## Why
The string-handle ops are keyed off a **native-side session/filter registration**
that the C++ client performs but the managed replay does not reproduce. The uint
client handle is the Open2 session token the server already trusts; the string GUID
handle indexes a *different* per-service registration table that stays empty unless
the native priming is replicated faithfully. `Stat.GetV ×2` alone is insufficient.
## Consequence for the roadmap
Every remaining **M1 read** item is a string-handle op:
- R1.1 `ExecuteSqlCommandAsync` (ExeC) — blocked
- R1.4 `GetHistorianInfoAsync` (GETHI) — blocked
- R1.5 extended-property read (GetTepByNm) — blocked (string handle, confirmed)
- R1.6 localized-property read — same family
So **M1 read-surface completion on 2020 WCF is gated entirely behind one RE target:
the native session/filter registration for string-handle ops.** Reverse-engineer it
once and the whole family unlocks. Until then, the alternatives are:
1. **RE the registration** — instrument the native `CRetrievalConnectionWCF` /
`CStatusConnectionWCF` priming between Open2 and the first successful string-handle
call (capture-tier; the highest-leverage single RE task for M1).
2. **2023 R2 gRPC server** — these ops are first-class on the gRPC front door, where
the handle/envelope differs and the registration wall may not apply.
Do **not** ship any string-handle op via guessed calls (project discipline:
"leave them throwing until evidence supports an implementation").