Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 16
  1. #1

    [c#] Application.Exit() cancellazione oggetti!!

    Salve ragazzi, ho creato una classe con le directshow.net e ho un problema... praticamente la classe si occupa di prendere il video dalla webcam. In poche parole una volta istanziato l'oggetto, dichiaro handle e l'indice del device che deve usare e mi mostra il video.

    Il problema è questo: Il video rimane giustamente aperto finché non lo chiudo con un commando (che è presente anche nel distruttore della classe).

    Se premo un bottone che scatena "Application.Exit()" (ovvero chiude tutti i form ed esce dal loop costringendo Application.Run() a tornare). L'oggetto della webcam non viene distrutto causando errore.

    Se avvio il commando di chiusura tramite altro bottone, posso chiudere e tutto va bene, ma se la webcam è ancora aperta, non fa in tempo a chiamare il distruttore dell'oggetto e fa errore!

    Infatti ho dovuto fare un "walkaround":

    Nel form dove richiamo la classe ho dovuto mettere un evento "onClosing" in modo che forzo il form in chiusura (quando viene premuto Application.Exit) a richiamare la funzione che dovrebbe in teoria richiamare il distruttore..

    in poche parole:
    Se non utilizzo manualmente il distruttore, questo non viene richiamato.

    Il distruttore è dichiaro in modo semplice:
    codice:
    ~webcam()
    {    
         this.CloseInterfaces();
    }
    L'unico modo per costringerlo ad eseguire this.CloseInterfaces() è richiamare dal form un evento OnClosing e forzarlo da lì. Qualche suggerimento?

    codice:
    namespace Jarvis 
    { 
        class webcam 
        {
            
             public string DeviceName, DevicePath; 
    
             ~webcam() 
             { 
                 this.CloseInterfaces(); 
             }
    
    
    ...................
    
    
            public void CloseInterfaces()
            {
                // Stop previewing data
                if (this.mediaControl != null)
                    this.mediaControl.StopWhenReady();
    
                // Stop receiving events
                if (this.mediaEventEx != null)
                    this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero);
    
                // Relinquish ownership (IMPORTANT!) of the video window.
                // Failing to call put_Owner can lead to assert failures within
                // the video renderer, as it still assumes that it has a valid
                // parent window.
                if (this.videoWindow != null)
                {
                    this.videoWindow.put_Visible(OABool.False);
                    this.videoWindow.put_Owner(IntPtr.Zero);
                }
    
                // Remove filter graph from the running object table
                if (rot != null)
                {
                    rot.Dispose();
                    rot = null;
                }
            }
        }
    }

  2. #2
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    970

    Re: [c#] Application.Exit() cancellazione oggetti!!

    Andando per gradi.

    Originariamente inviato da Bloodxyz
    Salve ragazzi, ho creato una classe con le directshow.net e ho un problema... praticamente la classe si occupa di prendere il video dalla webcam. In poche parole una volta istanziato l'oggetto, dichiaro handle e l'indice del device che deve usare e mi mostra il video.
    Fino a qua ci siamo.

    Il problema è questo: Il video rimane giustamente aperto finché non lo chiudo con un commando (che è presente anche nel distruttore della classe).
    Già qui inizio a non capire. Se hai creato una classe che gestisce l'acquisizione o la preview che sia il distruttore come lo chiami tu sarà presente solo nella classe, ovvero verrà richiamato tramite il metodo .Dispose() della classe in questione. Che vuol dire quel 'che è presente anche nel distruttore della classe' ?

    Se premo un bottone che scatena "Application.Exit()" (ovvero chiude tutti i form ed esce dal loop costringendo Application.Run() a tornare). L'oggetto della webcam non viene distrutto causando errore.
    Ovviamente prima di chiudere l'applicazione dovrai fare il .Dispose() della classe, no?
    Sbagliare è umano, perseverare è diabolico.

  3. #3
    Per ora non c'è la funzione Dispose() dentro la classe, avevo provato prima ma senza risultati positivi.

    Il Dispose, se presente la funzione, non avviene automaticamente all'interno della classe quando chiudo l'applicazione? Come in c++ quando dichiaro con ~ il distruttore, viene eseguito anche se non lo richiamo manualmente.

    Il distruttore che intendo è quella porzione:
    codice:
    ~mysql { CloseInterfaces(); }
    Ho provato anche mettendo:
    codice:
    class mysql: IDisposable
    {
         public void Dispose()
         {
             CloseInterfaces();
         }
    
    
    }
    ma non viene eseguito automaticamente quando chiudo, la funzione Dispose() non viene chiamata..

    quando chiudo l'applicazione (il bottone stà su un'altro form), il programma esegue "Application.Exit();" ho provato anche con

    codice:
    Dispose();
    Application.Exit();
    oppure (tentativi disperati)

    codice:
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Application.Exit();
    ma nulla.

  4. #4
    Forse mi sto sbagliando io.. mi son riletto la documentazione sull'utilizzo del Dispose e del Finalize.

    Allora, il finalize ( ~nomeclass ) viene richiamato dal GC quando l'oggetto termina il suo ciclo vitale o è nullo... quindi nel mio caso, l'oggetto della classe webcam viene istanziato al costruttore del form e lo tiene aperto (altrimenti lo streaming mi si chiude) quando lancio la funzione per catturare lo streaming.

    Quindi penso che essendo un oggetto ancora valido (a streaming aperto) il garbage collector non mi esegue il finalize..è per quello?

    Il metodo dispose non ho ben capito, deve venir richiamato manualmente e si occupa di una sorta di catena per cancellare risorse gestite e non gestite.

    Se si esegue Dispose manualmente, bisogna invocare il suppressfinalize, altrimenti si rischia di invocare la chiusura due volte..

    Questo è piu o meno quello che ho capito..

  5. #5
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    970
    l Dispose, se presente la funzione, non avviene automaticamente all'interno della classe quando chiudo l'applicazione?
    No, se la classe espone il metodo .Dispose() deve essere chiamato manualmente (a parte C# vedi nota alla fine), quello che viene chiamato automaticamente dalla GC è il metodo .Finalize() se dichiarato utilizzando l'Ovverrides e Protected e questo accade quando impostiamo la nostra classe a Nothing oppure quando esce dall'ambito di visibilità.

    Per cui se implementiamo il metodo .Dispose() e il metodo .Finalize() ed in entrami i metodi rilasciamo le risorse potrebbe succedere che le risorse vengano rilasciate due volte, una quando chiamiamo il metodo .Dispose() manualmente e una quando la GC invoca il metodo .Finalize(). Proprio per questo e buona norma integrare nel metodo .Dispose() il metodo .GC.SuppressFinalize che appunta evita che le risorse vengano rilasciate due volte.

    Mi sembra che solo con C# utilizzando l'istruzione Using la classe invoca automaticamente il metodo .Dispose().
    Ad esempio a memoria:

    codice:
     public partial class Form1 : Form
        {
    
            Class1 MiaClasse;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                 using (MiaClasse= new Class1())
                {
                    //Codice di MiaClasse
                }
            }
            }
    Sbagliare è umano, perseverare è diabolico.

  6. #6
    Ho capito, se devo dirti la verità sono un po confuso, perchè è la prima volta che affronto questo tipo di linguaggi, ho iniziato a capire qualcosa col c++ e mi son dovuto trasportare di fretta sul c#..

    Questa è una classe che mi serve (scopiazzata dal file di esempio delle directshow.net, ma lì veniva inglobata direttamente dentro un form) per catturare l'immagine da una webcam.

    Come parametri fornisco l'handle del controllo (esempio panel.handle) e un indice che corrisponde alla webcam rilevata da dsdevice.

    Allora io per adesso ce l'ho così:

    File webcam.cs (file di classe separato)
    codice:
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System;
    using System.Diagnostics;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using System.Runtime.InteropServices.ComTypes;
    using DirectShowLib;
    
    namespace Jarvis
    {
        internal class webcam: IDisposable
        {
            public void Dispose()
            {
                GC.SuppressFinalize(this);
                CloseInterfaces();
            }
    
            ~webcam()
            {
                CloseInterfaces();
            }
    
            public string DeviceName, DevicePath;
            // Application-defined message to notify app of filtergraph events
            public const int WM_GRAPHNOTIFY = 0x8000 + 1;
    
            IVideoWindow videoWindow = null;
            IMediaControl mediaControl = null;
            IMediaEventEx mediaEventEx = null;
            IGraphBuilder graphBuilder = null;
            ICaptureGraphBuilder2 captureGraphBuilder = null;
            DsROTEntry rot = null;
            
    
            public void CaptureVideo(IntPtr customhandle, int index_device)
            {
                int hr = 0;
                IBaseFilter sourceFilter = null;
    
                try
                {
                    // Get DirectShow interfaces
                    GetInterfaces();
    
                    // Attach the filter graph to the capture graph
                    hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder);
                    DsError.ThrowExceptionForHR(hr);
    
                    // Use the system device enumerator and class enumerator to find
                    // a video capture/preview device, such as a desktop USB video camera.
                    sourceFilter = FindCaptureDevice(index_device);
    
                    // Add Capture filter to our graph.
                    hr = this.graphBuilder.AddFilter(sourceFilter, "Video Capture");
                    DsError.ThrowExceptionForHR(hr);
    
                    // Render the preview pin on the video capture filter
                    // Use this instead of this.graphBuilder.RenderFile
                    hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, null, null);
                    DsError.ThrowExceptionForHR(hr);
    
                    // Now that the filter has been added to the graph and we have
                    // rendered its stream, we can release this reference to the filter.
                    //
    
                    // Set video window style and position
                    SetupVideoWindow(customhandle);
    
                    // Add our graph to the running object table, which will allow
                    // the GraphEdit application to "spy" on our graph
                    rot = new DsROTEntry(this.graphBuilder);
    
                    // Start previewing video data
                    hr = this.mediaControl.Run();
                    DsError.ThrowExceptionForHR(hr);
    
                }
                catch
                {
                    MessageBox.Show("An unrecoverable error has occurred.");
                }
            }
    
            private void GetInterfaces()
            {
                // An exception is thrown if cast fail
                this.graphBuilder = (IGraphBuilder)new FilterGraph();
                this.captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                this.mediaControl = (IMediaControl)this.graphBuilder;
                this.videoWindow = (IVideoWindow)this.graphBuilder;
                this.mediaEventEx = (IMediaEventEx)this.graphBuilder;
            }
    
            private IBaseFilter FindCaptureDevice(int index_device)
            {
                DsDevice[] devices;
                object source;
    
                // Get all video input devices
                devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
    
                // Take the first device
                DsDevice device = (DsDevice)devices[index_device];
                DeviceName = device.Name;
                DevicePath = device.DevicePath;
    
                // Bind Moniker to a filter object
                Guid iid = typeof(IBaseFilter).GUID;
                device.Mon.BindToObject(null, null, ref iid, out source);
    
                // An exception is thrown if cast fail
                return (IBaseFilter)source;
            }
    
            public void SetupVideoWindow(IntPtr customhandle)
            {
                int hr = 0;
    
                // Set the video window to be a child of the main window
                hr = this.videoWindow.put_Owner(customhandle);
                DsError.ThrowExceptionForHR(hr);
    
                hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
                DsError.ThrowExceptionForHR(hr);
    
                // Resize the video preview window to match owner window size
                if (this.videoWindow != null)
                {
                    this.videoWindow.SetWindowPosition(0, 0, 200, 200);
                }
    
                // Make the video window visible, now that it is properly positioned
                hr = this.videoWindow.put_Visible(OABool.True);
                DsError.ThrowExceptionForHR(hr);
            }
    
            public void CloseInterfaces()
            {
                // Stop previewing data
                if (this.mediaControl != null)
                    this.mediaControl.StopWhenReady();
    
                // Stop receiving events
                if (this.mediaEventEx != null)
                    this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero);
    
                // Relinquish ownership (IMPORTANT!) of the video window.
                // Failing to call put_Owner can lead to assert failures within
                // the video renderer, as it still assumes that it has a valid
                // parent window.
                if (this.videoWindow != null)
                {
                    this.videoWindow.put_Visible(OABool.False);
                    this.videoWindow.put_Owner(IntPtr.Zero);
                }
    
                // Remove filter graph from the running object table
                if (rot != null)
                {
                    rot.Dispose();
                }
    
                //aggiunto da me
                //Marshal.ReleaseComObject(sourceFilter);
            }
    
        }
    }
    Per adesso se mi date una mano a capire, che cosa ho combinato.. vi direi grazie mille!!

    Dubbi:
    - Cosa mi cambia tra indicare private, public o internal prima del nomeclasse? Solitamente non metto mai nulla lascio solo "class nomeclasse { .. "

    - In questo modo ovviamente per distruggere l'oggetto, chiamo "Dispose()" all'interno del dispose del form, in modo che quando chiude il form mi distrugge anche le risorse della classe, volendo potrei comunque metterlo sull'evento "onClosing" del form.

    - Ci sono sicuramente parti superflue di codice, perché dall'esempio ho cercato di eliminare parti del codice per semplificare questa classe..

    Questa è la versione originale dell'esempio da cui ho copiato

  7. #7
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    970
    Originariamente inviato da Bloodxyz
    Dubbi:
    - Cosa mi cambia tra indicare private, public o internal prima del nomeclasse? Solitamente non metto mai nulla lascio solo "class nomeclasse { .. "
    Cambi l'ambito di visibilità della classe (di default e Friend)

    Tornando al tuo problema originale, che ancora non sono riuscito a mettere a fuoco, non mi sembra così difficile.
    Intercetti la chiusura della Form, chiami il metodo .Dispose() della classe o come detto in precedenza esponi il metodo .Finalize() in cui liberi le risorse e quando imposti la classe a Null tale metodo verrà chiamato in automatico.

    Esempio:

    codice:
    Public Class Form1
        Dim MiaClasse As Classe1
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            MiaClasse = New Classe1
            MiaClasse.Work()
            MiaClasse = Nothing
        End Sub
    
        Class Classe1
            Implements IDisposable
    
            Sub Work()
                'Some work
            End Sub
    
            Private disposedValue As Boolean = False
            Protected Overridable Sub Dispose(ByVal disposing As Boolean)
                If Not Me.disposedValue Then
                    If disposing Then
    
                    End If
    
                End If
                Me.disposedValue = True
            End Sub
    
            Public Sub Dispose() Implements IDisposable.Dispose
                Dispose(True)
                GC.SuppressFinalize(Me)
            End Sub
    
            Protected Overrides Sub Finalize()
                Debug.WriteLine("Oggetto distrutto")
            End Sub
        End Class
    
    End Class
    Sbagliare è umano, perseverare è diabolico.

  8. #8
    quindi sarebbe corretto scrivere (preso da msdn):
    codice:
    public void Dispose() 
    {
        Dispose(true);
        GC.SuppressFinalize(this); 
    }
    
    protected virtual void Dispose(bool disposing) 
    {
        if (disposing) 
        {
            // Libera risorse gestite
        }
    
          // Libera risorse non gestite
    
    }
    
    // Use C# destructor syntax for finalization code.
    ~nomeclasse()
    {
        Dispose (false);
    }
    In modo che..

    Se viene eseguito Dispose(), avvia l'altro dispose (quello protected), e blocca il finalize per questo oggetto tramite GC.SuppressFinalize().

    Se viene eseguito il finalize "~nomeclasse()" dal Garbage, avvia Dispose(false) in modo da rilasciare soltanto le risorse non gestite

  9. #9
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    970
    Yes.

    E' lo stesso codice che si autoImplementa quando in VB.NET scrivi sotto il nome della classe IDisposable, ovvero:

    codice:
    Class test
            Implements IDisposable
    
            Private disposedValue As Boolean = False        ' To detect redundant calls
    
            ' IDisposable
            Protected Overridable Sub Dispose(ByVal disposing As Boolean)
                If Not Me.disposedValue Then
                    If disposing Then
                        ' TODO: free other state (managed objects).
                    End If
    
                    ' TODO: free your own state (unmanaged objects).
                    ' TODO: set large fields to null.
                End If
                Me.disposedValue = True
            End Sub
    
    #Region " IDisposable Support "
            ' This code added by Visual Basic to correctly implement the disposable pattern.
            Public Sub Dispose() Implements IDisposable.Dispose
                ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
                Dispose(True)
                GC.SuppressFinalize(Me)
            End Sub
    #End Region
    
        End Class
    Sbagliare è umano, perseverare è diabolico.

  10. #10
    Quindi questo codice dovrebbe autoeseguirsi (il dispose ) quando chiudo l'applicazione giusto?

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.