Thanks everyone who helped with tips.
I've just found a simple solution for this problem.
What I did was to implement a CheckAlive
function that is called by the server and is executed on the client side using a callback. When all the clients are gone I can finish the server.
internal class BasicClassFactory<T> : IClassFactory where T : new()
{
private readonly List<T> _instances = new();
private readonly object lockObj = new();
public void CreateInstance(
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
ref Guid riid,
out IntPtr ppvObject
)
{
Type interfaceType = GetValidatedInterfaceType(typeof(T), ref riid, pUnkOuter);
object obj = new T();
if (pUnkOuter != null)
obj = CreateAggregatedObject(pUnkOuter, obj);
ppvObject = GetObjectAsInterface(obj, interfaceType);
lock (lockObj)
{
//Each client gets a new instance of the COM object,
//so I keep a list of them for isAlive check purposes.
_instances.Add((T)obj);
}
}
public void CheckAlive(Func<T, bool> checkFunc)
{
lock (lockObj)
{
//Check if any instance is still alive, by calling
//a callback that is executed on the client side.
//If your object implements IDisposable you should
//call dispose as well.
for (int i = _instances.Count - 1; i >= 0; i--)
{
try
{
if (!checkFunc(_instances[i]))
{
_instances.Remove(_instances[i]);
(_instances[i] as IDisposable)?.Dispose();
}
}
catch (Exception)
{
//if I get an error, it means the client is not alive
//and the memory got corrupted.
_instances.Remove(_instances[i]);
(_instances[i] as IDisposable)?.Dispose();
}
}
}
}
public int AliveCount
{
get
{
lock (lockObj)
{
return _instances.Count;
}
}
}
}
My local server keeps running by a waiter, this way:
public void Run()
{
_waiter = new ManualResetEvent(false);
_waiter.WaitOne();
}
So, all I need to do is to have a Timer checking every X seconds if there is any client alive, if not I can release the waiter and the server closes itself automatically.
var timer = new Timer
(
_ =>
{
classFactory.CheckAlive(checAliveFunc);
if (classFactory.AliveCount == 0)
_waiter?.Set();
},
null,
TimeSpan.FromSeconds(20), //Runs for the first time after 20 seconds
TimeSpan.FromSeconds(5) //Check every 5 seconds
);