06030dd1ef
The .proto contract and MxCommandKind already defined Write, Write2,
WriteSecured, and WriteSecured2, but the worker's MxAccessCommandExecutor
had no case for any of them — every write kind fell through to
CreateInvalidRequestReply ("Unsupported MXAccess command kind Write").
Implement all four:
- VariantConverter.ConvertToComValue projects an MxValue into a
COM-marshalable object (scalars, arrays, null) — the inverse of the
existing COM-to-MxValue projection.
- IMxAccessServer / MxAccessComServer gain Write/Write2/WriteSecured/
WriteSecured2, routed to ILMXProxyServer / ILMXProxyServer4.
- MxAccessSession and MxAccessCommandExecutor add the four write paths,
following the existing ExecuteAdvise pattern; the reply is a plain OK
reply and the outcome surfaces later as an OnWriteComplete event.
Verified live: a Write now returns PROTOCOL_STATUS_CODE_OK and produces
an OnWriteComplete event where it previously returned InvalidRequest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
235 lines
6.9 KiB
C#
235 lines
6.9 KiB
C#
using System;
|
|
using ArchestrA.MxAccess;
|
|
|
|
namespace MxGateway.Worker.MxAccess;
|
|
|
|
/// <summary>
|
|
/// Adapter exposing MXAccess COM object methods through the <see cref="IMxAccessServer"/>
|
|
/// interface.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The supplied object must implement the typed MXAccess COM interface contract.
|
|
/// In production it is the <c>LMXProxyServerClass</c> RCW, which implements
|
|
/// <see cref="ILMXProxyServer"/> / <see cref="ILMXProxyServer3"/> /
|
|
/// <see cref="ILMXProxyServer4"/>. Tests substitute a typed fake that
|
|
/// implements <see cref="IMxAccessServer"/> directly. The earlier late-bound
|
|
/// <c>Type.InvokeMember</c> reflection fallback was removed: it bypassed the
|
|
/// typed interface contract, boxed value-type handles on every call, and only
|
|
/// ever served test doubles — a typed fake is the supported test seam now.
|
|
/// </remarks>
|
|
public sealed class MxAccessComServer : IMxAccessServer
|
|
{
|
|
private readonly object mxAccessComObject;
|
|
|
|
/// <summary>
|
|
/// Initializes the adapter with the MXAccess COM object.
|
|
/// </summary>
|
|
/// <param name="mxAccessComObject">
|
|
/// MXAccess COM object instance. Must implement either the typed
|
|
/// <see cref="ILMXProxyServer"/> COM interface family (production) or
|
|
/// <see cref="IMxAccessServer"/> directly (test fakes).
|
|
/// </param>
|
|
public MxAccessComServer(object mxAccessComObject)
|
|
{
|
|
this.mxAccessComObject = mxAccessComObject ?? throw new ArgumentNullException(nameof(mxAccessComObject));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int Register(string clientName)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
return typedFake.Register(clientName);
|
|
}
|
|
|
|
return AsProxyServer().Register(clientName);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Unregister(int serverHandle)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.Unregister(serverHandle);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer().Unregister(serverHandle);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int AddItem(
|
|
int serverHandle,
|
|
string itemDefinition)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
return typedFake.AddItem(serverHandle, itemDefinition);
|
|
}
|
|
|
|
return AsProxyServer().AddItem(serverHandle, itemDefinition);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int AddItem2(
|
|
int serverHandle,
|
|
string itemDefinition,
|
|
string itemContext)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
return typedFake.AddItem2(serverHandle, itemDefinition, itemContext);
|
|
}
|
|
|
|
return AsProxyServer3().AddItem2(serverHandle, itemDefinition, itemContext);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void RemoveItem(
|
|
int serverHandle,
|
|
int itemHandle)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.RemoveItem(serverHandle, itemHandle);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer().RemoveItem(serverHandle, itemHandle);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Advise(
|
|
int serverHandle,
|
|
int itemHandle)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.Advise(serverHandle, itemHandle);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer().Advise(serverHandle, itemHandle);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void UnAdvise(
|
|
int serverHandle,
|
|
int itemHandle)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.UnAdvise(serverHandle, itemHandle);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer().UnAdvise(serverHandle, itemHandle);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void AdviseSupervisory(
|
|
int serverHandle,
|
|
int itemHandle)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.AdviseSupervisory(serverHandle, itemHandle);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer4().AdviseSupervisory(serverHandle, itemHandle);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Write(
|
|
int serverHandle,
|
|
int itemHandle,
|
|
object? value,
|
|
int userId)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.Write(serverHandle, itemHandle, value, userId);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer().Write(serverHandle, itemHandle, value!, userId);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Write2(
|
|
int serverHandle,
|
|
int itemHandle,
|
|
object? value,
|
|
object? timestamp,
|
|
int userId)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.Write2(serverHandle, itemHandle, value, timestamp, userId);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer4().Write2(serverHandle, itemHandle, value!, timestamp!, userId);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void WriteSecured(
|
|
int serverHandle,
|
|
int itemHandle,
|
|
int currentUserId,
|
|
int verifierUserId,
|
|
object? value)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer().WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value!);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void WriteSecured2(
|
|
int serverHandle,
|
|
int itemHandle,
|
|
int currentUserId,
|
|
int verifierUserId,
|
|
object? value,
|
|
object? timestamp)
|
|
{
|
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
|
{
|
|
typedFake.WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value, timestamp);
|
|
return;
|
|
}
|
|
|
|
AsProxyServer4().WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value!, timestamp!);
|
|
}
|
|
|
|
private ILMXProxyServer AsProxyServer()
|
|
{
|
|
return mxAccessComObject as ILMXProxyServer
|
|
?? throw new InvalidOperationException(
|
|
$"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
|
|
+ $"{nameof(ILMXProxyServer)} or {nameof(IMxAccessServer)}.");
|
|
}
|
|
|
|
private ILMXProxyServer3 AsProxyServer3()
|
|
{
|
|
return mxAccessComObject as ILMXProxyServer3
|
|
?? throw new InvalidOperationException(
|
|
$"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
|
|
+ $"{nameof(ILMXProxyServer3)} or {nameof(IMxAccessServer)}.");
|
|
}
|
|
|
|
private ILMXProxyServer4 AsProxyServer4()
|
|
{
|
|
return mxAccessComObject as ILMXProxyServer4
|
|
?? throw new InvalidOperationException(
|
|
$"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
|
|
+ $"{nameof(ILMXProxyServer4)} or {nameof(IMxAccessServer)}.");
|
|
}
|
|
}
|