Все объекты являются неуправляемыми за исключением некоторых типов.
Принцип наследования:
public interface IVlcObject // Главный обобщенный интерфейс
{
void Release();
void DeleteLater(object sender, ref IVlcObject obj);
}
Наследником интерфейса является абстрактный класс, который является в свою очередь обобщенным классом для всех остальных неуправляемых объектов:
internal abstract class VlcObject<TDeleter> : SafeHandleZeroOrMinusOneIsInvalid, IVlcObject where TDeleter : Delegate
{
private readonly VlcLibrary _libVlc;
private readonly VlcLibrary _libVlcCore;
protected readonly TDeleter Deleter;
protected VlcObject() : base(true)
{
_libVlcCore = WinApi.LoadLibrary(Factory.LibVlcCorePath);
_libVlc = WinApi.LoadLibrary(Factory.LibVlcPath);
Deleter = ResolveVlc<TDeleter>();
}
protected override bool ReleaseHandle()
{
_libVlc.Close();
_libVlcCore.Close();
bool result = _libVlc.IsClosed && _libVlcCore.IsClosed;
return result;
}
internal static bool CheckAttr<TDelegate>(out string procName) where TDelegate : Delegate
{
Type typeData = typeof(TDelegate);
object[] attributes = typeData.GetCustomAttributes(typeof(CFuncAttribute), false);
if (attributes.Length <= 0)
{
procName = string.Empty;
return false;
}
procName = ((CFuncAttribute)attributes[0])?.Function;
return true;
}
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
protected TDelegate ResolveVlc<TDelegate>() where TDelegate : Delegate
{
if (CheckAttr<TDelegate>(out string name))
{
return ResolveVlc<TDelegate>(name);
}
throw new InvalidOperationException("Delegate with param type cannot be used for extract procedure name! Please see another overload.");
}
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
protected TDelegate ResolveVlcCore<TDelegate>() where TDelegate : Delegate
{
Factory.Log();
if (CheckAttr<TDelegate>(out string name))
{
return ResolveVlcCore<TDelegate>(name);
}
throw new InvalidOperationException("Delegate with param type cannot be used for extract procedure name! Please see another overload.");
}
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
protected TDelegate ResolveVlc<TDelegate>(string name) where TDelegate : Delegate
{
return _libVlc.Resolve<TDelegate>(name);
}
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
protected TDelegate ResolveVlcCore<TDelegate>(string name) where TDelegate : Delegate
{
return _libVlcCore.Resolve<TDelegate>(name);
}
public void Release()
{
Close();
}
public void DeleteLater(object sender, ref IVlcObject obj)
{
((VlcInstance) obj).VlcObjects.Remove(DangerousGetHandle());
((VlcInstance) obj).DoDeleteLater -= DeleteLater;
Release();
}
}
Далее наследники перегружают метод ReleaseHandle
и сами производят вызов TDeleter
с Handle
'om которым владеют, далее приведу один из классов, который бы как мне хотелось при освобождении уничтожал все объекты которые ссылались на него, но не были освобождены, а те объекты которые ссылались на данный объект, и были уничтожены самостоятельно, вызывали бы какой-то метод и убирали бы себя из списка объектов подлежащих утилизации при уничтожении объекта на который сослались.
Т.е. хотелось бы реализовать что-то вроде DeleteLater
в Qt
.
Как мне показалось то возможно вот такое решение будет нормальным, но может есть какие-то другие способы реализовать все это дело, да так что бы например не было проблем с много-поточным обращением.
Ниже приведен интерфейс который возвращается вместо самого объекта для работы с ним:
public interface IVlcInstance :
IVlcObject // используем обобщенный интерфейс
{
...
}
Наследник данного интерфейса, а так же объект который может создавать другие объекты:
internal delegate void RemoveSelfOnDestroy(object sender, ref IVlcObject obj);
internal class VlcInstance : VlcObject<libvlc_release>, IVlcInstance
{
protected internal readonly Dictionary<IntPtr, IVlcObject> VlcObjects;
internal event RemoveSelfOnDestroy DoDeleteLater;
protected VlcInstance()
{
Factory.Log();
VlcObjects = new Dictionary<IntPtr, IVlcObject>();
}
public LibVlcExitHandler ExitHandler
{
set => SetExitHandler(value);
}
/// <inheritdoc />
public IVlcMediaPlayer CreatePlayer()
{
VlcMediaPlayer player = ResolveVlc<libvlc_media_player_new>().Invoke(DangerousGetHandle());
if (!CheckInvalidAndRelease(ref player))
{
return player;
}
VlcObjects.Add(player.DangerousGetHandle(), player);
DoDeleteLater += player.DeleteLater;
return player;
}
public IVlcMedia OpenMedia(Uri mrl)
{
VlcMedia media = mrl.IsLoopback
? ResolveVlc<libvlc_media_new_path>().Invoke(DangerousGetHandle(), mrl.ToString())
: ResolveVlc<libvlc_media_new_location>().Invoke(DangerousGetHandle(), mrl.ToString());
if (!CheckInvalidAndRelease(ref media))
{
return media;
}
VlcObjects.Add(media.DangerousGetHandle(), media);
DoDeleteLater += media.DeleteLater;
return media;
}
public IVlcMedia OpenMedia(string path)
{
VlcMedia media = ResolveVlc<libvlc_media_new_path>().Invoke(DangerousGetHandle(), path);
if (!CheckInvalidAndRelease(ref media)) return media;
VlcObjects.Add(media.DangerousGetHandle(), media);
DoDeleteLater += media.DeleteLater;
return media;
}
public IVlcMedia OpenMedia(FileInfo file)
{
VlcMedia media = ResolveVlc<libvlc_media_new_path>().Invoke(DangerousGetHandle(), file.FullName);
if (!CheckInvalidAndRelease(ref media)) return media;
VlcObjects.Add(media.DangerousGetHandle(), media);
DoDeleteLater += media.DeleteLater;
return media;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool CheckInvalidAndRelease<TVlcObject>(ref TVlcObject obj) where TVlcObject : IVlcObject
{
if (!((SafeHandleZeroOrMinusOneIsInvalid) (IVlcObject) obj).IsInvalid) return true;
obj.Release();
obj = default;
return false;
}
protected override bool ReleaseHandle()
{
IVlcObject self = this;
OnDeleteLater(ref self);
Deleter.Invoke(DangerousGetHandle());
return base.ReleaseHandle();
}
private void SetExitHandler(LibVlcExitHandler handler)
{
ResolveVlc<libvlc_set_exit_handler>().Invoke(DangerousGetHandle(), handler, IntPtr.Zero);
}
protected virtual void OnDeleteLater(ref IVlcObject obj)
{
DoDeleteLater?.Invoke(this, ref obj);
}
}
Таким образом все связанные объекты уничтожаются, но может есть более удобный способ такое провернуть?
А вы уверены, что ваша задача - именно уничтожить все объекты перед выгрузкой библиотеки, а не отложить выгрузку библиотеки до момента уничтожения всех объектов?
Второе делается куда более просто при помощи DangerousAddRef
/DangerousRelease
:
abstract class VlcObject : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeHandle parent;
public void SetParent(SafeHandle newParent) {
bool success = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
if (parent != null) parent.DangerousRelease();
if (newParent != null) newParent.DangerousAddRef(ref success);
} finally {
if (success)
parent = newParent;
else
parent = null;
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected virtual bool ReleaseHandle() {
if (parent != null) parent.DangerousRelease();
parent = null;
}
}
// ...
public IVlcMediaPlayer CreatePlayer() {
VlcMediaPlayer player = ResolveVlc<libvlc_media_player_new>().Invoke(this);
player.SetParent(this);
return player;
}
По моему, ничего более интересного придумать нельзя.
Разве что заменить метод, который вызывает порожденный объект у родителя, что бы обозначить, что произошло удаление на событие
.
Т.е Родитель
создает дочерний
объект и перед отправкой этого объекта наружу подписывается на какое-нибудь событие удаления.
Если дочерний
объект удаляется, то вызывается перед смертью событие и родитель
удаляет у себя ссылку на этот объект.
По моему, так более красивее и естественно будет выглядеть. И порожденному объекту в этом случае не нужно будет хранить ссылку на родителя, что бы вызвать метод у родителя, что бы уведомить об удалении.
В первом листинге, вроде, вы что-то делаете похожее на это, так как я вижу удаление из словаря объектов, однако, во втором листинге, где вы хотите дать такое поведение, я не вижу этого.
P.S Код смотрел бегло.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Просто как заблокировать клавиатуру и мышь? Уже смотрел эту страницу http://wwwcyberforum
Имеется связка "Компьютер - GPRS модем - счетчик стандарта ГОСТ IEC 61107—2011"GPRS модем подключен к COM порту