84ec175f76
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>
59 lines
3.5 KiB
Markdown
59 lines
3.5 KiB
Markdown
# 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").
|