2fc327a8d5
Replace the hand-rolled CallbackExporter (TCP listener + custom
OBJREF) with a real `windows-rs` `#[implement]` COM class for
INmxSvcCallback, marshalled via CoMarshalInterface. NmxSvc validates
the callback OBJREF by calling IObjectExporter::ResolveOxid against
the local RPCSS at 127.0.0.1:135; hand-rolled OXIDs aren't registered
there, which is why RegisterEngine2 returned RPC_S_SERVER_UNAVAILABLE
(1722) on every live attempt. CoMarshalInterface registers the OXID
with RPCSS automatically, so the SCM-side resolution succeeds.
Mirrors MxNativeSession.CreateRegisteredService (cs:624), which is
the .NET reference's working path:
ComObjRefProvider.MarshalInterfaceObjRef(callback,
INmxSvcCallback, DifferentMachine)
Layout:
- mxaccess-callback::dcom_sink — INmxSvcCallback + DcomCallbackSink
+ create_dcom_callback_sink_objref. Forwards inbound calls into
the same CallbackEvent::CallbackInvoked { opnum, body } shape the
legacy exporter produces, so callback_router stays path-agnostic.
- Session::from_nmx_client — branched on `windows-com`. Real DCOM
sink when on; legacy CallbackExporter when off (kept for unit
tests that run against an in-process fake NMX peer).
- SessionInner.dcom_sink_holder: Option<IUnknownHolder> — keeps the
COM ref alive for the session's lifetime; shutdown_nmx drops it.
- mxaccess-rpc + mxaccess-callback: windows-rs 0.59 → 0.62. The 0.59
#[implement] macro generates code that doesn't compile under
edition 2024; 0.62 is fixed.
Live result: cargo test -p mxaccess-compat --features
live-windows-com --test lmx_write_complete_live -- --ignored
--nocapture passes end-to-end. RegisterEngine2 OK, write
round-trips, OnWriteComplete fires with the captured MxStatus shape.
Unblocks F49 step 5; F55 marked Resolved in design/followups.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
2.0 KiB
TOML
51 lines
2.0 KiB
TOML
[package]
|
|
name = "mxaccess-callback"
|
|
description = "TCP listener + RPC server for INmxSvcCallback and IRemUnknown (the callback exporter)."
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
rust-version.workspace = true
|
|
authors.workspace = true
|
|
|
|
[dependencies]
|
|
mxaccess-rpc = { path = "../mxaccess-rpc", version = "0.0.0" }
|
|
mxaccess-codec = { path = "../mxaccess-codec", version = "0.0.0" }
|
|
tokio = { workspace = true }
|
|
tracing = { workspace = true }
|
|
rand = "0.8"
|
|
|
|
# F55 / Path A — DCOM-managed callback sink.
|
|
# `windows-com` enables `dcom_sink.rs` which implements
|
|
# `INmxSvcCallback` as a real COM class via `windows-rs` `#[implement]`.
|
|
# The marshalled OBJREF passes NmxSvc's SCM-side OXID resolution
|
|
# where the hand-rolled `exporter.rs` approach fails. Default build
|
|
# stays slim — the windows crate is only pulled in when the consumer
|
|
# enables `windows-com`. Propagates through to
|
|
# `mxaccess-rpc/windows-com` so the OBJREF marshaller is available.
|
|
windows = { version = "0.62", features = [
|
|
"Win32_Foundation",
|
|
"Win32_System_Com",
|
|
"Win32_System_Com_Marshal",
|
|
"Win32_System_Com_StructuredStorage",
|
|
"Win32_System_Memory",
|
|
], optional = true }
|
|
# windows-rs's `#[interface]` and `#[implement]` macros expand to
|
|
# absolute `::windows_core::*` paths, so the consumer must depend on
|
|
# `windows-core` directly (the `windows` crate's re-export at
|
|
# `windows::core` doesn't satisfy the macro's path resolution).
|
|
# Pin to the same 0.62 line as the `windows` dep above so the
|
|
# `IUnknown` / `IUnknown_Vtbl` types resolve to the same crate
|
|
# version that `mxaccess-rpc::com_objref_provider::IUnknownHolder`
|
|
# wraps — version skew between the two would surface as "expected
|
|
# IUnknown, found IUnknown" type errors at the
|
|
# `IUnknownHolder::from_iunknown` boundary.
|
|
windows-core = { version = "0.62", optional = true }
|
|
|
|
[features]
|
|
default = []
|
|
windows-com = ["dep:windows", "dep:windows-core", "mxaccess-rpc/windows-com"]
|
|
|
|
[lints]
|
|
workspace = true
|