fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
427 lines
22 KiB
Markdown
427 lines
22 KiB
Markdown
# MxNativeSession API
|
|
|
|
`MxNativeSession` is the first high-level .NET 10 x64 facade over the managed
|
|
NMX protocol work in this repository. It is intended to be the consumer-facing
|
|
entry point while the lower layers remain available for protocol tests and
|
|
diagnostics.
|
|
|
|
## Runtime prerequisites
|
|
|
|
- AVEVA System Platform/NMX installed locally so `NmxSvc.NmxService` can be
|
|
activated.
|
|
- The Galaxy Repository SQL database available to the current Windows session.
|
|
- Managed NTLM runtime credentials supplied through the existing environment
|
|
variable path used by `ManagedNtlmClientContext`.
|
|
- The x64 callback COM registration/type-library setup already documented in
|
|
`DotNet10-Native-Library-Plan.md`.
|
|
|
|
The session facade does not store credentials. It relies on the process
|
|
environment and the already-authenticated Windows/SQL context.
|
|
|
|
## Basic write
|
|
|
|
```csharp
|
|
using MxNativeClient;
|
|
|
|
using var session = MxNativeSession.Open(new MxNativeClientOptions
|
|
{
|
|
LocalEngineId = 0x7fee,
|
|
EngineName = "MxNativeClient.Sample",
|
|
// Optional. Leave null unless a deployment-specific heartbeat cadence is known.
|
|
HeartbeatTicksPerBeat = null,
|
|
});
|
|
|
|
await session.WriteAsync("TestChildObject.TestInt", 123);
|
|
await session.Write2Async(
|
|
"TestChildObject.TestInt",
|
|
124,
|
|
DateTime.UtcNow);
|
|
```
|
|
|
|
`HeartbeatTicksPerBeat` and `HeartbeatMaxMissedTicks` expose
|
|
`INmxService2.SetHeartbeatSendInterval`. A .NET 10 x64 probe validated
|
|
`SetHeartbeatSendInterval(5, 3)` against local NmxSvc. The setting remains
|
|
opt-in because the exact MXAccess heartbeat cadence has not been captured on
|
|
this VM; leaving `HeartbeatTicksPerBeat` null preserves the previously
|
|
validated connection behavior.
|
|
|
|
## Recovery
|
|
|
|
`MxNativeSession.RecoverConnection()` explicitly rebuilds the managed NMX
|
|
service client, re-registers the local engine and callback, reapplies optional
|
|
heartbeat settings, reconnects known publisher engines, and replays current
|
|
normal or buffered subscriptions with their existing correlation IDs. The
|
|
compatibility facade exposes the same operation as
|
|
`MxNativeCompatibilityServer.RecoverConnection(serverHandle)`.
|
|
|
|
`RecoverConnectionAsync(policy, cancellationToken)` adds an explicit retry loop
|
|
around that primitive. `MxNativeRecoveryPolicy.MaxAttempts` defaults to `1`;
|
|
`Delay` defaults to zero. The compatibility facade exposes the same retrying
|
|
operation as `RecoverConnectionAsync(serverHandle, policy, cancellationToken)`.
|
|
|
|
Recovery progress is observable:
|
|
|
|
- `RecoveryAttemptStarted` fires before each reconnect/replay attempt.
|
|
- `RecoveryAttemptFailed` fires after a recoverable attempt failure and reports
|
|
the exception plus whether the retry loop will continue.
|
|
- `RecoveryCompleted` fires after the replacement service has been registered,
|
|
publisher endpoints have been reconnected, subscriptions have been replayed,
|
|
and the session has swapped to the recovered service.
|
|
|
|
The compatibility facade exposes the same events with the server handle added
|
|
to each event payload. A live .NET 10 x64 probe with two allowed attempts and a
|
|
100 ms delay observed one started event, zero failed events, one completed
|
|
event, preserved the existing subscription count, and wrote through the
|
|
recovered session.
|
|
|
|
Callbacks are passed through during the recovery window instead of being
|
|
suppressed or buffered. `MxNativeCallbackEvent`,
|
|
`MxNativeOperationStatusEvent`, `MxNativeReferenceRegistrationEvent`, and
|
|
`MxNativeUnparsedCallbackEvent` include `IsDuringRecovery` so callers can
|
|
separate replay-window events from steady-state events. Compatibility
|
|
`DataChanged`, `BufferedDataChanged`, and `WriteCompleted` payloads carry the
|
|
same marker.
|
|
|
|
`MxNativeClient.Probe --probe-session-recover-multi` subscribes to multiple
|
|
tags, runs explicit recovery, and reports total callbacks plus
|
|
`IsDuringRecovery` counts for data, operation-status, reference-registration,
|
|
and unparsed callback families. The first live run replayed four subscriptions
|
|
and preserved all four, with zero callback events marked as recovery-window
|
|
events on this VM.
|
|
|
|
Adding `--recover-concurrent-writes` starts a separate writer session during
|
|
the same recovery window. A live run wrote `TestChildObject.TestInt` values
|
|
`330`-`334`, preserved all four subscriptions, and observed two data callbacks
|
|
with `IsDuringRecovery=true`. This confirms that callbacks are not quiesced by
|
|
the library during recovery; consumers that require strict post-recovery
|
|
ordering should ignore or queue marked events at their boundary.
|
|
|
|
Automatic background recovery is intentionally not enabled yet. Retrying writes
|
|
inside normal write calls can duplicate writes, so callers should choose where
|
|
recovery belongs in their own operation model. A .NET 10 x64 probe validated the
|
|
current explicit path by subscribing to `TestChildObject.TestInt`, recovering
|
|
the connection, preserving the subscription count, and writing through the
|
|
recovered session.
|
|
|
|
`WriteSecured2Async` is implemented for the observed boolean secured/verified
|
|
payload shape:
|
|
|
|
```csharp
|
|
await session.WriteSecured2Async(
|
|
"TestMachine_001.ProtectedValue",
|
|
true,
|
|
DateTime.Now,
|
|
currentUserId: 1,
|
|
verifierUserId: 0);
|
|
```
|
|
|
|
The encoder emits native command `0x38` and has been live validated from .NET
|
|
10 x64 against the secured and verified boolean tags deployed on this node, as
|
|
well as authenticated bool, int, float, double, string, datetime, and
|
|
scalar-array calls against `TestChildObject` attributes. The handle-based
|
|
compatibility facade also supports `AuthenticateUser` followed by
|
|
`WriteSecured2`, and intentionally does not synthesize `OnWriteComplete` for
|
|
this path because the successful native captures did not show that event.
|
|
`WriteSecuredAsync` remains unsupported because native MXAccess still returns
|
|
`0x80004021` before emitting a value-bearing body in every captured scenario.
|
|
Additional native captures beyond bool and int would improve fixture coverage,
|
|
but the managed encoder is now generic over the timestamped write-body support.
|
|
|
|
## Browse
|
|
|
|
```csharp
|
|
IReadOnlyList<GalaxyTagMetadata> tags =
|
|
await session.BrowseAsync("TestChildObject", "Test%", maxRows: 25);
|
|
```
|
|
|
|
`BrowseAsync` uses Galaxy Repository metadata directly. It does not call the
|
|
x86 LMX resolver and returns the same metadata needed by the managed handle and
|
|
wire encoders.
|
|
|
|
`ResolveAsync` accepts full references in the observed MXAccess forms:
|
|
`Object.Attribute`, `Object.Primitive.Attribute`, and
|
|
`Object.Primitive.Dotted.Attribute` for primitive attributes such as
|
|
`TestMachine_001.TestAlarm001.Alarm.TimeDeadband`. It also recognizes the
|
|
captured literal property form `Object.Attribute.property(buffer)`, resolving it
|
|
to the base attribute handle with property id `0x32`.
|
|
|
|
Each `GalaxyTagMetadata` exposes `IsSupportedValueKind` and
|
|
`TryGetValueKind(out MxValueKind valueKind)`. Use those before write/read
|
|
projection when browsing broad GR metadata. The current wire codec supports the
|
|
live OPC-UA-critical GR types `Boolean`, `Integer`, `Float`, `Double`, `String`,
|
|
`Time`, and array forms captured so far. Live GR inspection also found
|
|
`ElapsedTime` and `InternationalizedString`. Subscribe/read callback decoding now
|
|
handles the observed `ElapsedTime` wire kind `0x07` as `TimeSpan` and the compact
|
|
empty `InternationalizedString`/string payload form `0x05 04 00 00 00` as
|
|
`string.Empty`. They are still reported as unsupported by `TryGetValueKind`
|
|
because they are not core one-to-one data kinds, but write projection now follows
|
|
captured MXAccess caller-variant behavior: `ElapsedTime` values supplied as
|
|
`TimeSpan` or integer project to integer milliseconds/wire kind `0x02`, and
|
|
`InternationalizedString` values project to normal string wire kind `0x05`.
|
|
|
|
`WriteAsync` and `Write2Async` resolve the tag from Galaxy Repository metadata,
|
|
build the managed `MxReferenceHandle`, encode the NMX write body, wrap it in
|
|
the `TransferData` envelope, and call `INmxService2::TransferData` through the
|
|
managed DCOM path.
|
|
|
|
## Subscribe
|
|
|
|
```csharp
|
|
using MxNativeClient;
|
|
|
|
using var session = MxNativeSession.Open();
|
|
|
|
session.CallbackReceived += (_, evt) =>
|
|
{
|
|
Console.WriteLine(
|
|
$"{evt.Record.TimestampUtc:O} status={evt.Status.Category} quality=0x{evt.Record.Quality:X4} value={evt.Record.Value}");
|
|
};
|
|
|
|
session.OperationStatusReceived += (_, evt) =>
|
|
{
|
|
Console.WriteLine(
|
|
$"operation_status=0x{evt.Message.StatusCode:X4} status={evt.Message.Status.Category}");
|
|
};
|
|
|
|
MxNativeSubscription sub = await session.SubscribeAsync("TestChildObject.TestInt");
|
|
await Task.Delay(TimeSpan.FromSeconds(10));
|
|
session.Unsubscribe(sub.CorrelationId);
|
|
```
|
|
|
|
## Read
|
|
|
|
```csharp
|
|
object? value = await session.ReadAsync(
|
|
"TestChildObject.TestInt",
|
|
timeout: TimeSpan.FromSeconds(10));
|
|
```
|
|
|
|
`ReadAsync` is implemented as a transient subscription read. It subscribes,
|
|
waits for the first typed value callback matching that item correlation, then
|
|
unsubscribes.
|
|
|
|
`SubscribeAsync` performs the managed equivalent of the observed MXAccess path:
|
|
|
|
1. Resolve tag metadata from the Galaxy Repository.
|
|
2. Connect the local engine to the tag's owning engine.
|
|
3. Add the local engine as a subscriber.
|
|
4. Send the generated item-control `0x1f` body using the native value handle.
|
|
5. Decode incoming `INmxSvcCallback` bodies with `NmxSubscriptionMessage`.
|
|
|
|
Capture `099-frida-plain-advise-testint` showed public `Advise` and the earlier
|
|
`AdviseSupervisory` scalar path using the same `0x1f` body shape for
|
|
`TestChildObject.TestInt`, so the compatibility wrapper maps both methods onto
|
|
this subscription path.
|
|
|
|
`CallbackReceived` is raised for typed `0x32` subscription-status and `0x33`
|
|
data-update records. Each callback exposes the raw NMX record fields plus a
|
|
first-pass `MxStatus` projection. `MxStatus.DetailText` returns the installed
|
|
English `Lmx.aaDCT` detail text for known LMX/NMX status details, including
|
|
reference, conversion, security, alarm, initializing, and secured/verified
|
|
write conditions. `OperationStatusReceived`
|
|
is raised for both observed operation-status frame forms. The non-length-prefixed
|
|
5-byte status-word form with status word `0x8050` and completion byte `0x00`
|
|
maps to `MxStatus.WriteCompleteOk` and is the captured public
|
|
`OnWriteComplete` success notification. Length-prefixed completion-only frames
|
|
are decoded separately and expose `NmxOperationStatusFormat.CompletionOnly`.
|
|
Captures `089`, `092`, and `093` produced completion byte `0x41` after
|
|
wrong-type string writes, and capture `091` produced completion byte `0x00`
|
|
after double-to-int coercion; MXAccess did not raise `OnWriteComplete` for
|
|
either completion-only form.
|
|
|
|
The managed x64 path now has a proven value-bearing subscription fixture:
|
|
`SubscribeAsync("TestChildObject.ShortDesc")` receives the same compact empty
|
|
string callback observed from x86 MXAccess (`0x32`, wire kind `0x05`, quality
|
|
`0x00C0`). The key handle detail is that value references use property id `10`;
|
|
GR `mx_attribute_category` is metadata and is not the NMX value-handle property
|
|
field.
|
|
|
|
String-array callback handling is intentionally conservative. Captures `100`
|
|
and `101` showed `0x45` string-array callback records whose buffers stopped
|
|
inside the final element, and MXAccess did not raise a public data-change event
|
|
for those runs. The decoder does not fabricate a value for malformed array
|
|
payloads; callers should treat a `null` value with a known array wire kind as an
|
|
incomplete callback until a complete/public string-array callback is captured.
|
|
|
|
`ReferenceRegistrationReceived` is raised for decoded `0x11`
|
|
reference-registration result frames. Those frames are emitted after the `0x10`
|
|
normal/buffered registration request and include the item handle, correlation
|
|
ID, reference text, context, and MX data type.
|
|
|
|
`UnparsedCallbackReceived` is a diagnostic event for callback bodies that are
|
|
not yet part of the high-level API surface. It is currently used to expose
|
|
frames such as the 92-byte operation/status-shaped body seen before scalar
|
|
subscription status callbacks, rather than silently swallowing unknown protocol
|
|
bodies during reverse engineering.
|
|
|
|
## Probe commands
|
|
|
|
The probe now has facade-level entry points:
|
|
|
|
```powershell
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-session-write --tag=TestChildObject.TestInt --value=123 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-session-read --tag=TestChildObject.TestInt --read-timeout-seconds=10 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-session-subscribe --tag=TestChildObject.TestInt --subscribe-hold-seconds=5 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-session-subscribe --tag=TestChildObject.ShortDesc --subscribe-hold-seconds=8 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-compatibility-subscribe --tag=TestChildObject.ShortDesc --subscribe-hold-seconds=8 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-compatibility-subscribe-write --tag=TestChildObject.TestInt --value=793 --subscribe-hold-seconds=8 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-compatibility-subscribe --tag=NoSuchObject_999.NoSuchAttr --subscribe-hold-seconds=2 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-compatibility-subscribe-multi --tag=TestChildObject.ShortDesc --tag=TestChildObject.TestInt --tag=NoSuchObject_999.NoSuchAttr --subscribe-hold-seconds=4 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-compatibility-write-multi --subscribe-hold-seconds=4 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-compatibility-write-unadvised --tag=TestChildObject.TestInt --value=99 --objref-only
|
|
dotnet run --project .\src\MxNativeClient.Probe\MxNativeClient.Probe.csproj -c Release -- --probe-managed-subscribe --tag=TestChildObject.TestInt --subscribe-hold-seconds=5 --send-observed-preadvise-metadata --objref-only
|
|
```
|
|
|
|
These commands require the same runtime managed NTLM environment as the lower
|
|
level managed probes.
|
|
|
|
## MXAccess-style compatibility wrapper
|
|
|
|
`MxNativeCompatibilityServer` provides an MXAccess-like handle model for code
|
|
that expects integer server and item handles:
|
|
|
|
```csharp
|
|
using MxNativeClient;
|
|
|
|
using var mx = new MxNativeCompatibilityServer();
|
|
|
|
mx.DataChanged += (_, evt) =>
|
|
{
|
|
Console.WriteLine(
|
|
$"server={evt.ServerHandle} item={evt.ItemHandle} value={evt.Value} quality=0x{evt.Quality:X4}");
|
|
};
|
|
|
|
mx.WriteCompleted += (_, evt) =>
|
|
{
|
|
Console.WriteLine(
|
|
$"server={evt.ServerHandle} item={evt.ItemHandle} status={evt.Statuses[0].Category}");
|
|
};
|
|
|
|
int server = mx.Register("OpcUaBridge.Native");
|
|
int item = mx.AddItem(server, "TestChildObject.TestInt");
|
|
mx.AdviseSupervisory(server, item);
|
|
mx.Write(server, item, 123);
|
|
mx.UnAdvise(server, item);
|
|
mx.RemoveItem(server, item);
|
|
mx.Unregister(server);
|
|
```
|
|
|
|
The compatibility wrapper maps `Register`, `Unregister`, `AddItem`,
|
|
`RemoveItem`, `Advise`, `AdviseSupervisory`, `UnAdvise`, `Write`, and `Write2`
|
|
onto `MxNativeSession`. `Suspend` and `Activate` mirror observed MXAccess local
|
|
behavior for advised items: unadvised calls throw, `Suspend` returns
|
|
pending/requesting-LMX status, and `Activate` returns ok/requesting-LMX status.
|
|
After `RemoveItem`, the wrapper mirrors native x86 stale-item behavior by
|
|
returning `ArgumentException` with `HResult=0x80070057` for advise, unadvise,
|
|
write, write2, suspend, activate, and repeated remove attempts.
|
|
Invalid server handles and cross-server item handles use the same
|
|
`ArgumentException`/`0x80070057` shape observed from native MXAccess.
|
|
Literal `property(buffer)` items added with `AddItem` are not treated as
|
|
`AddBufferedItem` handles. They follow the captured MXAccess literal-reference
|
|
path: add/advise succeeds, normal write returns without a public write-complete,
|
|
and no public data-change is promoted on the current VM state.
|
|
`AddItem2` resolves the item definition directly first and then retries with the
|
|
supplied context. This covers the captured simple form
|
|
`AddItem2("TestInt", "TestChildObject")` plus dotted relative forms such as
|
|
`AddItem2("Alarm.TimeDeadband", "TestMachine_001.TestAlarm001")` and
|
|
`AddItem2("TestInt.property(buffer)", "TestChildObject")`.
|
|
`AddBufferedItem` and `SetBufferedUpdateInterval` now cover the decoded public
|
|
API path: buffered add creates a handle, the interval is stored as local session
|
|
state, and advising the buffered handle sends the observed NMX `0x10`
|
|
registration for `itemDefinition.property(buffer)` in the supplied item
|
|
context. Headless Ghidra analysis shows native MXAccess routes buffered item
|
|
callbacks through the same `OnDataChange` callback method as normal items, then
|
|
branches to `_ILMXProxyServerEvents2::OnBufferedDataChange` when the item record
|
|
is marked buffered. `MxNativeCompatibilityServer.BufferedDataChanged` mirrors
|
|
that separation for any parsed buffered callback and does not promote buffered
|
|
items through `DataChanged`. Captures against `TestChildObject.TestInt` and
|
|
GR-confirmed historized `TestMachine_001.TestHistoryValue` prove the outbound
|
|
context-bearing registration/result bodies, but this VM has not emitted a live
|
|
`OnBufferedDataChange` payload. True multi-sample buffered payload decoding
|
|
still needs that runtime condition.
|
|
`ArchestrAUserToId` follows the observed MXAccess x86
|
|
behavior on this node: known user GUIDs and an invalid zero GUID all return `1`.
|
|
`AuthenticateUser` follows the same session-local handle model for the observed
|
|
dev-node behavior: MXAccess returned `S_OK` and user ID `1` for both
|
|
`Administrator` and an invalid user name with an empty password. The managed
|
|
compatibility method validates the server handle and user string, returns a
|
|
session-local handle, and does not store or compare password material.
|
|
`WriteCompleted` is raised only for the captured MXAccess-visible 5-byte
|
|
`0x8050/0x00` operation-status frame. Completion-only frames are still available
|
|
through `MxNativeSession.OperationStatusReceived`, but the compatibility wrapper
|
|
does not convert them into `OnWriteComplete` events because x86 MXAccess did not
|
|
fire that event in those captures. Pending write handles are queued per server
|
|
session, so concurrent compatibility sessions do not consume each other's
|
|
completion callbacks. The mixed `--probe-compatibility-write-multi` path
|
|
validates that normal int, internationalized string, literal buffer-property,
|
|
and invalid-reference items in one server session do not leak public data-change
|
|
or write-complete events across item handles; only the invalid reference emits
|
|
the expected configuration-error data-change. `OperationCompleted` keeps the public event
|
|
shape, but no trigger has been modeled yet because the capture set contains no
|
|
`mx.event.operation-complete` events.
|
|
`DataChanged` is also suppressed when the lower-level decoder reports a known
|
|
wire kind with a `null` value, which is how malformed/incomplete callback
|
|
payloads such as the observed string-array records from captures `100` and
|
|
`101` are represented.
|
|
`GalaxyRepositoryUserResolver` is separate metadata support: it resolves
|
|
`dbo.user_profile` rows, profile IDs, default security group, InTouch access
|
|
level, and decoded role names from the GR role blob. `AddItem2` currently
|
|
combines context and item text only for simple relative references; the real
|
|
MXAccess context behavior still needs captures. Unsupported MXAccess methods
|
|
throw `NotSupportedException` with the missing capture/protocol reason.
|
|
|
|
For lower-level use, `GalaxyRepositoryUserResolver` exposes:
|
|
|
|
```csharp
|
|
var users = new GalaxyRepositoryUserResolver();
|
|
GalaxyUserProfile profile = await users.ResolveByNameAsync("Administrator");
|
|
int profileId = await users.ResolveUserProfileIdByGuidAsync(profile.UserGuid);
|
|
IReadOnlyList<string> roles = profile.Roles;
|
|
```
|
|
|
|
Security-enabled `AuthenticateUser` password verification remains intentionally
|
|
out of scope until successful and failed captures from a security-enabled Galaxy
|
|
show whether MXAccess delegates to OS/domain auth, GR hashes, or another token
|
|
provider.
|
|
|
|
## Current limitations
|
|
|
|
- Managed NTLM live probes now validate .NET 10 x64 activation, RemQI,
|
|
`GetPartnerVersion`, direct `WriteAsync` transport, callback delivery, and
|
|
unsubscribe cleanup with unique local engine IDs.
|
|
- Value-bearing managed `OnDataChange` is proven for
|
|
`TestChildObject.ShortDesc` after matching native advise transfer kind and
|
|
value-handle property id. The lower-level session exposes that callback, but
|
|
the MXAccess-style compatibility wrapper suppresses the empty
|
|
`InternationalizedString` public `DataChanged` event because fresh x86
|
|
MXAccess capture `108` also raises no public event for `ShortDesc`. Fresh
|
|
current-state x86 MXAccess captures also raise no public `TestInt`
|
|
data-change for subscribe-only or subscribe-then-write, matching the managed
|
|
status-only `TestInt` callbacks on this VM. Broader scalar/array parity still
|
|
needs live validation against a runtime state that emits public values.
|
|
Replaying the captured
|
|
adapter-visible `0x17` metadata body is accepted by `NmxSvc` and produces
|
|
decoded `0x40` metadata-response plus `0x32` metadata-status callbacks, but
|
|
the response still contains an internal base-object error and does not appear
|
|
to be the gate for the `ShortDesc` value callback.
|
|
- `ReadAsync` currently uses a transient subscription. A lower-latency direct
|
|
read path can be added if a distinct NMX request/response read body is found;
|
|
tags with no initial value can still time out after receiving only status
|
|
callbacks.
|
|
- String-array callbacks are partially modeled from the element format, but the
|
|
available Frida string-array callback line is truncated before all values.
|
|
- Error/failure callback bodies still need more captures for exact
|
|
`MXSTATUS_PROXY[]` parity.
|
|
- `ElapsedTime` and `InternationalizedString` writes are value-projected from
|
|
captures `095` and `096`. The managed `ShortDesc` write body now matches
|
|
capture `096` byte-for-byte and returns the same completion-only `0xef` as
|
|
native MXAccess on this node, while `TestInt` returns completion-only `0x00`
|
|
and a status-only update callback. Public write-complete semantics for these
|
|
completion-only frames still need more captures.
|
|
- `MxNativeCompatibilityServer` provides source-level migration help but is not
|
|
a binary COM replacement for `ArchestrA.MxAccess`.
|
|
- For invalid references, the compatibility wrapper intentionally differs from
|
|
strict `MxNativeSession`: it returns an item handle and raises the observed
|
|
public configuration-error data-change (`null`, quality `0`, detail `6`) on
|
|
advise, matching x86 MXAccess captures.
|