mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
Add external public IPC host/client and plugin SDK
Introduce a new LanMountainDesktop.Shared.IPC project implementing a public IPC host and client (LanMountainDesktopIpcClient, PublicIpcHostService), IPC constants and routed notify IDs, DTOs and DI helpers for registering public services. Update Plugin SDK to allow plugins to contribute public IPC services and registrations, add related descriptors/records and extension helpers. Migrate Launcher/App to use the new public IPC for startup/loading notifications and wiring (including TryConnect helper), switch LoadingStateReporter to use the external notification publisher, and add host-side public services (app info, shell control, plugin catalog). Include integration tests and spec/checklist/docs for the external IPC public API.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
using dotnetCampus.Ipc.CompilerServices.Attributes;
|
||||
|
||||
namespace LanMountainDesktop.Shared.IPC.Abstractions.Services;
|
||||
|
||||
[IpcPublic(IgnoresIpcException = true)]
|
||||
public interface IPublicAppInfoService
|
||||
{
|
||||
PublicAppInfoSnapshot GetAppInfo();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using dotnetCampus.Ipc.CompilerServices.Attributes;
|
||||
|
||||
namespace LanMountainDesktop.Shared.IPC.Abstractions.Services;
|
||||
|
||||
[IpcPublic(IgnoresIpcException = true)]
|
||||
public interface IPublicPluginCatalogService
|
||||
{
|
||||
PublicIpcCatalogSnapshot GetCatalog();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using dotnetCampus.Ipc.CompilerServices.Attributes;
|
||||
|
||||
namespace LanMountainDesktop.Shared.IPC.Abstractions.Services;
|
||||
|
||||
[IpcPublic(IgnoresIpcException = true)]
|
||||
public interface IPublicShellControlService
|
||||
{
|
||||
Task<bool> ActivateMainWindowAsync();
|
||||
|
||||
Task<bool> OpenSettingsAsync(string? pageTag = null);
|
||||
|
||||
Task<bool> RestartAsync();
|
||||
|
||||
Task<bool> ExitAsync();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace LanMountainDesktop.Shared.IPC.DependencyInjection;
|
||||
|
||||
public sealed record PublicIpcServiceRegistration(
|
||||
Type ContractType,
|
||||
Func<IServiceProvider, object> ImplementationFactory,
|
||||
string? ObjectId,
|
||||
string? PluginId,
|
||||
string[] NotifyIds);
|
||||
@@ -0,0 +1,83 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace LanMountainDesktop.Shared.IPC.DependencyInjection;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddLanMountainDesktopIpcHost(
|
||||
this IServiceCollection services,
|
||||
string? pipeName = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
services.AddSingleton(provider =>
|
||||
{
|
||||
var host = new PublicIpcHostService(pipeName ?? IpcConstants.DefaultPipeName);
|
||||
foreach (var registration in provider.GetServices<PublicIpcServiceRegistration>())
|
||||
{
|
||||
var implementation = registration.ImplementationFactory(provider);
|
||||
host.RegisterPublicService(
|
||||
registration.ContractType,
|
||||
implementation,
|
||||
registration.ObjectId,
|
||||
registration.PluginId,
|
||||
registration.NotifyIds);
|
||||
}
|
||||
|
||||
host.Start();
|
||||
return host;
|
||||
});
|
||||
|
||||
services.AddSingleton<IExternalIpcNotificationPublisher>(provider =>
|
||||
provider.GetRequiredService<PublicIpcHostService>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddPublicIpcService<TContract, TImplementation>(
|
||||
this IServiceCollection services,
|
||||
string? objectId = null,
|
||||
string? pluginId = null,
|
||||
params string[] notifyIds)
|
||||
where TContract : class
|
||||
where TImplementation : class, TContract
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
EnsureSingletonRegistration<TContract, TImplementation>(services);
|
||||
|
||||
if (!services.Any(descriptor =>
|
||||
descriptor.ServiceType == typeof(PublicIpcServiceRegistration) &&
|
||||
descriptor.ImplementationInstance is PublicIpcServiceRegistration existing &&
|
||||
existing.ContractType == typeof(TContract) &&
|
||||
string.Equals(existing.ObjectId, objectId, StringComparison.Ordinal)))
|
||||
{
|
||||
services.AddSingleton(new PublicIpcServiceRegistration(
|
||||
typeof(TContract),
|
||||
provider => provider.GetRequiredService<TContract>(),
|
||||
objectId,
|
||||
pluginId,
|
||||
notifyIds ?? []));
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void EnsureSingletonRegistration<TContract, TImplementation>(IServiceCollection services)
|
||||
where TContract : class
|
||||
where TImplementation : class, TContract
|
||||
{
|
||||
var descriptor = services.LastOrDefault(item => item.ServiceType == typeof(TContract));
|
||||
if (descriptor is null)
|
||||
{
|
||||
services.AddSingleton<TContract, TImplementation>();
|
||||
return;
|
||||
}
|
||||
|
||||
if (descriptor.Lifetime != ServiceLifetime.Singleton)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Public IPC contract '{typeof(TContract).FullName}' must be registered as Singleton.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public interface IExternalIpcNotificationPublisher
|
||||
{
|
||||
Task NotifyAsync<TPayload>(string notifyId, TPayload payload, CancellationToken cancellationToken = default)
|
||||
where TPayload : class;
|
||||
}
|
||||
14
LanMountainDesktop.Shared.IPC/IpcConstants.cs
Normal file
14
LanMountainDesktop.Shared.IPC/IpcConstants.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public static class IpcConstants
|
||||
{
|
||||
public const string DefaultPipeName = "LanMountainDesktop.IPC.v1.Server";
|
||||
|
||||
public const string ProtocolVersion = "external-ipc-public-api.v1";
|
||||
|
||||
public static class Routes
|
||||
{
|
||||
public const string SessionGetInfo = "lanmountain.session.get-info";
|
||||
public const string CatalogGet = "lanmountain.catalog.get";
|
||||
}
|
||||
}
|
||||
8
LanMountainDesktop.Shared.IPC/IpcRoutedNotifyIds.cs
Normal file
8
LanMountainDesktop.Shared.IPC/IpcRoutedNotifyIds.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public static class IpcRoutedNotifyIds
|
||||
{
|
||||
public const string CatalogChanged = "lanmountain.catalog.changed";
|
||||
public const string LauncherStartupProgress = "lanmountain.launcher.startup-progress";
|
||||
public const string LauncherLoadingState = "lanmountain.launcher.loading-state";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>1.0.0</Version>
|
||||
<PackageId>LanMountainDesktop.Shared.IPC</PackageId>
|
||||
<IsPackable>true</IsPackable>
|
||||
<Authors>LanMountainDesktop</Authors>
|
||||
<Description>Public IPC abstractions and host/client infrastructure for LanMountainDesktop, backed by dotnetCampus.Ipc.</Description>
|
||||
<PackageTags>LanMountainDesktop;IPC;dotnetCampus.Ipc;Integration</PackageTags>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/wwiinnddyy/LanMountainDesktop</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>LGPL-3.0-or-later</PackageLicenseExpression>
|
||||
<Copyright>Copyright (c) LanMountainDesktop Contributors</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dotnetCampus.Ipc" Version="2.0.0-alpha434" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
96
LanMountainDesktop.Shared.IPC/LanMountainDesktopIpcClient.cs
Normal file
96
LanMountainDesktop.Shared.IPC/LanMountainDesktopIpcClient.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using dotnetCampus.Ipc.CompilerServices.GeneratedProxies;
|
||||
using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;
|
||||
using dotnetCampus.Ipc.Pipes;
|
||||
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed class LanMountainDesktopIpcClient : IDisposable
|
||||
{
|
||||
private bool _started;
|
||||
|
||||
public LanMountainDesktopIpcClient(string? clientPipeName = null)
|
||||
{
|
||||
Provider = string.IsNullOrWhiteSpace(clientPipeName)
|
||||
? new IpcProvider()
|
||||
: new IpcProvider(clientPipeName);
|
||||
RoutedProvider = new JsonIpcDirectRoutedProvider(Provider);
|
||||
}
|
||||
|
||||
public IpcProvider Provider { get; }
|
||||
|
||||
public JsonIpcDirectRoutedProvider RoutedProvider { get; }
|
||||
|
||||
public PeerProxy? Peer { get; private set; }
|
||||
|
||||
public bool IsConnected => Peer is not null && Peer.IsConnectedFinished;
|
||||
|
||||
public async Task ConnectAsync(string pipeName = IpcConstants.DefaultPipeName)
|
||||
{
|
||||
EnsureStarted();
|
||||
Peer = await Provider.GetAndConnectToPeerAsync(pipeName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void RegisterNotifyHandler<TPayload>(string notifyId, Action<TPayload> handler)
|
||||
where TPayload : class
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(notifyId);
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
RoutedProvider.AddNotifyHandler(notifyId, handler);
|
||||
}
|
||||
|
||||
public void RegisterNotifyHandler<TPayload>(string notifyId, Func<TPayload, Task> handler)
|
||||
where TPayload : class
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(notifyId);
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
RoutedProvider.AddNotifyHandler(notifyId, handler);
|
||||
}
|
||||
|
||||
public TContract CreateProxy<TContract>(string? objectId = null)
|
||||
where TContract : class
|
||||
{
|
||||
var peer = Peer ?? throw new InvalidOperationException("IPC client is not connected.");
|
||||
return Provider.CreateIpcProxy<TContract>(peer, objectId);
|
||||
}
|
||||
|
||||
public async Task<PublicIpcCatalogSnapshot?> GetCatalogAsync()
|
||||
{
|
||||
var client = await GetRoutedClientAsync().ConfigureAwait(false);
|
||||
return await client.GetResponseAsync<PublicIpcCatalogSnapshot>(IpcConstants.Routes.CatalogGet)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<PublicIpcSessionInfo?> GetSessionInfoAsync()
|
||||
{
|
||||
var client = await GetRoutedClientAsync().ConfigureAwait(false);
|
||||
return await client.GetResponseAsync<PublicIpcSessionInfo>(IpcConstants.Routes.SessionGetInfo)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<JsonIpcDirectRoutedClientProxy> GetRoutedClientAsync()
|
||||
{
|
||||
if (Peer is null)
|
||||
{
|
||||
throw new InvalidOperationException("IPC client is not connected.");
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
return new JsonIpcDirectRoutedClientProxy(Peer);
|
||||
}
|
||||
|
||||
private void EnsureStarted()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RoutedProvider.StartServer();
|
||||
_started = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Provider.Dispose();
|
||||
}
|
||||
}
|
||||
9
LanMountainDesktop.Shared.IPC/PublicAppInfoSnapshot.cs
Normal file
9
LanMountainDesktop.Shared.IPC/PublicAppInfoSnapshot.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed record PublicAppInfoSnapshot(
|
||||
string ApplicationName,
|
||||
string Version,
|
||||
string Codename,
|
||||
string PipeName,
|
||||
int ProcessId,
|
||||
DateTimeOffset StartedAt);
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed record PublicIpcCatalogSnapshot(
|
||||
PublicIpcServiceDescriptor[] Services,
|
||||
PublicPluginDescriptor[] Plugins,
|
||||
DateTimeOffset Timestamp);
|
||||
219
LanMountainDesktop.Shared.IPC/PublicIpcHostService.cs
Normal file
219
LanMountainDesktop.Shared.IPC/PublicIpcHostService.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System.Reflection;
|
||||
using System.Collections.Concurrent;
|
||||
using dotnetCampus.Ipc.Context;
|
||||
using dotnetCampus.Ipc.CompilerServices.GeneratedProxies;
|
||||
using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;
|
||||
using dotnetCampus.Ipc.Pipes;
|
||||
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed class PublicIpcHostService : IDisposable, IExternalIpcNotificationPublisher
|
||||
{
|
||||
private static readonly MethodInfo CreateIpcJointMethod = typeof(GeneratedIpcFactory)
|
||||
.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.Single(method =>
|
||||
method.Name == nameof(GeneratedIpcFactory.CreateIpcJoint) &&
|
||||
method.IsGenericMethodDefinition &&
|
||||
method.GetParameters().Length == 3);
|
||||
|
||||
private readonly Dictionary<(Type ContractType, string ObjectId), PublicServiceEntry> _services = new();
|
||||
private readonly ConcurrentDictionary<string, PeerProxy> _connectedPeers = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _gate = new();
|
||||
private bool _started;
|
||||
|
||||
public PublicIpcHostService(string pipeName = IpcConstants.DefaultPipeName)
|
||||
{
|
||||
PipeName = pipeName;
|
||||
StartedAt = DateTimeOffset.UtcNow;
|
||||
Provider = new IpcProvider(pipeName);
|
||||
RoutedProvider = new JsonIpcDirectRoutedProvider(Provider);
|
||||
}
|
||||
|
||||
public string PipeName { get; }
|
||||
|
||||
public DateTimeOffset StartedAt { get; }
|
||||
|
||||
public IpcProvider Provider { get; }
|
||||
|
||||
public JsonIpcDirectRoutedProvider RoutedProvider { get; }
|
||||
|
||||
public Func<IReadOnlyList<PublicPluginDescriptor>> PluginDescriptorProvider { get; set; } =
|
||||
static () => Array.Empty<PublicPluginDescriptor>();
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RoutedProvider.AddRequestHandler(IpcConstants.Routes.SessionGetInfo, () => BuildSessionInfo());
|
||||
RoutedProvider.AddRequestHandler(IpcConstants.Routes.CatalogGet, () => GetCatalogSnapshot());
|
||||
Provider.PeerConnected += OnPeerConnected;
|
||||
RoutedProvider.StartServer();
|
||||
_started = true;
|
||||
}
|
||||
|
||||
public void RegisterPublicService<TContract>(
|
||||
TContract implementation,
|
||||
string? objectId = null,
|
||||
string? pluginId = null,
|
||||
params string[] notifyIds)
|
||||
where TContract : class
|
||||
{
|
||||
RegisterPublicService(typeof(TContract), implementation, objectId, pluginId, notifyIds);
|
||||
}
|
||||
|
||||
public void RegisterPublicService(
|
||||
Type contractType,
|
||||
object implementation,
|
||||
string? objectId = null,
|
||||
string? pluginId = null,
|
||||
IEnumerable<string>? notifyIds = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(contractType);
|
||||
ArgumentNullException.ThrowIfNull(implementation);
|
||||
|
||||
var normalizedObjectId = objectId ?? string.Empty;
|
||||
var normalizedNotifyIds = notifyIds?
|
||||
.Where(id => !string.IsNullOrWhiteSpace(id))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray() ?? [];
|
||||
|
||||
lock (_gate)
|
||||
{
|
||||
if (_services.ContainsKey((contractType, normalizedObjectId)))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Public IPC contract '{contractType.FullName}' with object id '{normalizedObjectId}' is already registered.");
|
||||
}
|
||||
|
||||
CreateIpcJointMethod
|
||||
.MakeGenericMethod(contractType)
|
||||
.Invoke(null, [Provider, implementation, string.IsNullOrEmpty(normalizedObjectId) ? null : normalizedObjectId]);
|
||||
|
||||
_services[(contractType, normalizedObjectId)] = new PublicServiceEntry(
|
||||
contractType,
|
||||
implementation,
|
||||
string.IsNullOrEmpty(normalizedObjectId) ? null : normalizedObjectId,
|
||||
pluginId,
|
||||
normalizedNotifyIds);
|
||||
}
|
||||
|
||||
if (_started)
|
||||
{
|
||||
_ = NotifyCatalogChangedAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public PublicIpcCatalogSnapshot GetCatalogSnapshot()
|
||||
{
|
||||
PublicIpcServiceDescriptor[] services;
|
||||
lock (_gate)
|
||||
{
|
||||
services = _services.Values
|
||||
.Select(entry => new PublicIpcServiceDescriptor(
|
||||
entry.ContractType.FullName ?? entry.ContractType.Name,
|
||||
entry.ContractType.Assembly.GetName().Name ?? string.Empty,
|
||||
entry.ContractType.AssemblyQualifiedName,
|
||||
entry.ObjectId,
|
||||
entry.PluginId,
|
||||
string.IsNullOrWhiteSpace(entry.PluginId),
|
||||
entry.NotifyIds))
|
||||
.OrderBy(entry => entry.PluginId ?? string.Empty, StringComparer.OrdinalIgnoreCase)
|
||||
.ThenBy(entry => entry.ContractTypeName, StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
var plugins = PluginDescriptorProvider()?.ToArray() ?? Array.Empty<PublicPluginDescriptor>();
|
||||
return new PublicIpcCatalogSnapshot(services, plugins, DateTimeOffset.UtcNow);
|
||||
}
|
||||
|
||||
public Task PublishStartupProgressAsync(
|
||||
LanMountainDesktop.Shared.Contracts.Launcher.StartupProgressMessage message,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(message);
|
||||
return NotifyAsync(IpcRoutedNotifyIds.LauncherStartupProgress, message, cancellationToken);
|
||||
}
|
||||
|
||||
public Task PublishLoadingStateAsync(
|
||||
LanMountainDesktop.Shared.Contracts.Launcher.LoadingStateMessage message,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(message);
|
||||
return NotifyAsync(IpcRoutedNotifyIds.LauncherLoadingState, message, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync<TPayload>(string notifyId, TPayload payload, CancellationToken cancellationToken = default)
|
||||
where TPayload : class
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(notifyId);
|
||||
ArgumentNullException.ThrowIfNull(payload);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
foreach (var peer in _connectedPeers.Values)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var client = new JsonIpcDirectRoutedClientProxy(peer);
|
||||
await client.NotifyAsync(notifyId, payload).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Keep notification fan-out best-effort. Broken peers are cleaned by dotnetCampus.Ipc.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task NotifyCatalogChangedAsync()
|
||||
{
|
||||
return NotifyAsync(IpcRoutedNotifyIds.CatalogChanged, GetCatalogSnapshot());
|
||||
}
|
||||
|
||||
private PublicIpcSessionInfo BuildSessionInfo()
|
||||
{
|
||||
return new PublicIpcSessionInfo(
|
||||
PipeName,
|
||||
IpcConstants.ProtocolVersion,
|
||||
[
|
||||
IpcConstants.Routes.SessionGetInfo,
|
||||
IpcConstants.Routes.CatalogGet,
|
||||
IpcRoutedNotifyIds.CatalogChanged,
|
||||
IpcRoutedNotifyIds.LauncherStartupProgress,
|
||||
IpcRoutedNotifyIds.LauncherLoadingState
|
||||
],
|
||||
StartedAt);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Provider.PeerConnected -= OnPeerConnected;
|
||||
Provider.Dispose();
|
||||
}
|
||||
|
||||
private void OnPeerConnected(object? sender, PeerConnectedArgs e)
|
||||
{
|
||||
var peer = e.Peer;
|
||||
_connectedPeers[peer.PeerName] = peer;
|
||||
peer.PeerConnectionBroken -= OnPeerConnectionBroken;
|
||||
peer.PeerConnectionBroken += OnPeerConnectionBroken;
|
||||
}
|
||||
|
||||
private void OnPeerConnectionBroken(object? sender, IPeerConnectionBrokenArgs e)
|
||||
{
|
||||
if (sender is PeerProxy peer)
|
||||
{
|
||||
_connectedPeers.TryRemove(peer.PeerName, out _);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record PublicServiceEntry(
|
||||
Type ContractType,
|
||||
object Implementation,
|
||||
string? ObjectId,
|
||||
string? PluginId,
|
||||
string[] NotifyIds);
|
||||
}
|
||||
10
LanMountainDesktop.Shared.IPC/PublicIpcServiceDescriptor.cs
Normal file
10
LanMountainDesktop.Shared.IPC/PublicIpcServiceDescriptor.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed record PublicIpcServiceDescriptor(
|
||||
string ContractTypeName,
|
||||
string ContractAssemblyName,
|
||||
string? ContractAssemblyQualifiedName,
|
||||
string? ObjectId,
|
||||
string? PluginId,
|
||||
bool IsBuiltIn,
|
||||
string[] NotifyIds);
|
||||
7
LanMountainDesktop.Shared.IPC/PublicIpcSessionInfo.cs
Normal file
7
LanMountainDesktop.Shared.IPC/PublicIpcSessionInfo.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed record PublicIpcSessionInfo(
|
||||
string PipeName,
|
||||
string ProtocolVersion,
|
||||
string[] Capabilities,
|
||||
DateTimeOffset StartedAt);
|
||||
8
LanMountainDesktop.Shared.IPC/PublicPluginDescriptor.cs
Normal file
8
LanMountainDesktop.Shared.IPC/PublicPluginDescriptor.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace LanMountainDesktop.Shared.IPC;
|
||||
|
||||
public sealed record PublicPluginDescriptor(
|
||||
string PluginId,
|
||||
string DisplayName,
|
||||
string? Version,
|
||||
bool IsLoaded,
|
||||
bool IsEnabled);
|
||||
3
LanMountainDesktop.Shared.IPC/README.md
Normal file
3
LanMountainDesktop.Shared.IPC/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# LanMountainDesktop.Shared.IPC
|
||||
|
||||
Public IPC abstractions and host/client helpers for LanMountainDesktop.
|
||||
Reference in New Issue
Block a user