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>
35 lines
1.6 KiB
Rust
35 lines
1.6 KiB
Rust
//! `mxaccess-callback` — `INmxSvcCallback` RPC server (the callback exporter).
|
|
//!
|
|
//! M2 wave 3 landed: the [`exporter`] module ports
|
|
//! `src/MxNativeClient/ManagedCallbackExporter.cs` to a tokio-based TCP
|
|
//! server that serves `IRemUnknown` and `INmxSvcCallback` opnums and emits
|
|
//! typed [`exporter::CallbackEvent`]s for diagnostic observation.
|
|
//!
|
|
//! Opnums (verified against `src/MxNativeClient/NmxSvcCallbackMessages.cs:11-12`):
|
|
//! - `3` `DataReceived(bufferSize: i32, dataBuffer: sbyte[bufferSize]) -> hresult`
|
|
//! - `4` `StatusReceived(bufferSize: i32, statusBuffer: sbyte[bufferSize]) -> hresult`
|
|
//!
|
|
//! Plus the `IRemUnknown::RemQueryInterface` handler that completes the
|
|
//! server-side handshake against our exported OBJREF (DoD condition for M2).
|
|
|
|
// `forbid(unsafe_code)` lifted: the F55 / Path A `dcom_sink` module
|
|
// (gated behind `windows-com`) implements an `INmxSvcCallback` COM
|
|
// class that must dereference stub-side buffer pointers in
|
|
// `DataReceivedRaw` / `StatusReceivedRaw`. Each unsafe block carries
|
|
// a SAFETY comment documenting the COM stub's buffer-validity
|
|
// contract.
|
|
#![deny(unsafe_op_in_unsafe_fn)]
|
|
|
|
pub mod exporter;
|
|
|
|
pub use exporter::{CallbackEvent, CallbackExporter, ExporterIdentities, IUNKNOWN_IID};
|
|
|
|
/// Path A — DCOM-managed `INmxSvcCallback` sink. Required because
|
|
/// NmxSvc rejects hand-rolled OBJREFs from [`exporter::CallbackExporter`]
|
|
/// with `RPC_S_SERVER_UNAVAILABLE` (1722) on RegisterEngine2 — see F55.
|
|
#[cfg(all(windows, feature = "windows-com"))]
|
|
pub mod dcom_sink;
|
|
|
|
#[cfg(all(windows, feature = "windows-com"))]
|
|
pub use dcom_sink::{create_dcom_callback_sink_objref, INMX_SVC_CALLBACK_IID};
|