IOException: il processo non può accedere al 'percorso del file' perché è utilizzato da un altro processo


172

Ho un po 'di codice e quando viene eseguito, genera un IOException, dicendo quello

Il processo non può accedere al file "nome file" perché è utilizzato da un altro processo

Cosa significa questo e cosa posso fare al riguardo?


1
Vedi questa domanda per come trovare l'altro processo che sta usando il file.
stomy

Risposte:


274

Qual è la causa?

Il messaggio di errore è abbastanza chiaro: stai tentando di accedere a un file e non è accessibile perché un altro processo (o anche lo stesso processo) sta facendo qualcosa con esso (e non ha consentito alcuna condivisione).

Debug

Potrebbe essere abbastanza facile da risolvere (o piuttosto difficile da capire), a seconda del tuo scenario specifico. Vediamo alcuni.

Il tuo processo è l'unico ad accedere a quel file.
Sei sicuro che l' altro processo sia il tuo processo. Se sai che apri quel file in un'altra parte del tuo programma, prima di tutto devi controllare che chiudi correttamente l'handle del file dopo ogni utilizzo. Ecco un esempio di codice con questo errore:

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

Fortunatamente FileStreamimplementa IDisposable, quindi è facile racchiudere tutto il codice all'interno di usingun'istruzione:

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

Questo modello assicurerà anche che il file non venga lasciato aperto in caso di eccezioni (potrebbe essere la ragione per cui il file è in uso: qualcosa è andato storto e nessuno lo ha chiuso; vedere questo post per un esempio).

Se tutto sembra a posto (sei sicuro di chiudere sempre tutti i file che apri, anche in caso di eccezioni) e hai più thread funzionanti, allora hai due opzioni: rielaborare il codice per serializzare l'accesso ai file (non sempre fattibile e non sempre voluto) o applicare un modello di ripetizione . È un modello abbastanza comune per le operazioni di I / O: provi a fare qualcosa e in caso di errore attendi e riprovi (ti sei chiesto perché, ad esempio, Windows Shell impiega del tempo per informarti che un file è in uso e non può essere cancellato?). In C # è abbastanza facile da implementare (vedi anche esempi migliori su I / O del disco , rete e accesso al database ).

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

Nota un errore comune che vediamo molto spesso su StackOverflow:

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

In questo caso ReadAllText()fallirà perché il file è in uso ( File.Open()nella riga precedente). Aprire il file in anticipo non è solo non necessario ma anche sbagliato. Lo stesso vale per tutte le Filefunzioni che non restituiscono un handle per il file che si sta lavorando con: File.ReadAllText(), File.WriteAllText(), File.ReadAllLines(), File.WriteAllLines()e altri (comeFile.AppendAllXyz() funzioni) saranno tutti aprire e chiudere il file da soli.

Il tuo processo non è il solo ad accedere a quel file
Se il tuo processo non è il solo ad accedere a quel file, allora l'interazione può essere più difficile. Un modello di tentativi aiuterà (se il file non deve essere aperto da nessun altro ma lo è, allora è necessario un programma di utilità come Process Explorer per verificare chi sta facendo cosa ).

Modi da evitare

Se applicabile, utilizzare sempre usando istruzioni per aprire i file. Come detto nel paragrafo precedente, ti aiuterà attivamente a evitare molti errori comuni (vedi questo post per un esempio su come non usarlo ).

Se possibile, prova a decidere chi possiede l'accesso a un file specifico e centralizza l'accesso attraverso alcuni metodi noti. Se, ad esempio, si dispone di un file di dati in cui il programma legge e scrive, è necessario racchiudere tutto il codice I / O all'interno di una singola classe. Renderà più facile il debug (perché puoi sempre mettere un punto di interruzione lì e vedere chi sta facendo cosa) e inoltre sarà un punto di sincronizzazione (se richiesto) per l'accesso multiplo.

Non dimenticare che le operazioni di I / O possono sempre fallire, un esempio comune è questo:

if (File.Exists(path))
    File.Delete(path);

Se qualcuno elimina il file dopo File.Exists()ma prima File.Delete(), lo lancerà IOExceptionin un posto dove potresti sentirti erroneamente al sicuro.

Ogni volta che è possibile, applica un modello di tentativi e, se lo stai utilizzando FileSystemWatcher, considera di rimandare l'azione (perché riceverai una notifica, ma un'applicazione potrebbe continuare a funzionare esclusivamente con quel file).

Scenari avanzati
Non è sempre così facile, quindi potrebbe essere necessario condividere l'accesso con qualcun altro. Se, ad esempio, stai leggendo dall'inizio e scrivendo fino alla fine, hai almeno due opzioni.

1) condividere lo stesso FileStreamcon le funzioni di sincronizzazione appropriate (perché non è thread-safe ). Vedi questo e questo post per un esempio.

2) utilizzare l' FileShareenumerazione per indicare al sistema operativo di consentire ad altri processi (o altre parti del proprio processo) di accedere allo stesso file contemporaneamente.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

In questo esempio ho mostrato come aprire un file per la scrittura e condividere per la lettura; si noti che durante la lettura e la scrittura di sovrapposizioni, si ottengono dati indefiniti o non validi. È una situazione che deve essere gestita durante la lettura. Si noti inoltre che ciò non rende l'accesso al streamthread sicuro, quindi questo oggetto non può essere condiviso con più thread a meno che l'accesso non sia sincronizzato in qualche modo (vedere i collegamenti precedenti). Sono disponibili altre opzioni di condivisione che aprono scenari più complessi. Fare riferimento a MSDN per maggiori dettagli.

In generale, N processi possono leggere tutti dallo stesso file tutti insieme ma solo uno dovrebbe scrivere, in uno scenario controllato puoi persino abilitare scritture simultanee ma questo non può essere generalizzato in pochi paragrafi di testo all'interno di questa risposta.

È possibile sbloccare un file utilizzato da un altro processo? Non è sempre sicuro e non è così facile ma sì, è possibile .


Non so cosa c'è di sbagliato nel mio codice, utilizzo i blocchi, ma c'è ancora un errore che dice che il file è in uso quando provo a eliminarlo.
Jamshaid Kamran,

No, in realtà ho un controllo timer che lo fa, oltre al timer, se chiamo di nuovo la funzione genererà un'eccezione. fondamentalmente sto decifrando un file in un altro file di testo, dopo aver eliminato il file appena creato, che genera l'eccezione alla cancellazione!
Jamshaid Kamran,

3
@ جمشیدکامران Perché creare un file con i contenuti presenti nel codice, quindi eliminarlo? Sembra strano creare il file in primo luogo, quindi. Dal momento che non hai postato il codice, non sappiamo cosa stai facendo. Ma quando crei il tuo file, se lo fai con File.Create(path), dovresti aggiungerlo .Close()alla fine prima di scriverlo. Ci sono insidie ​​del genere, oltre alle usingistruzioni per la scrittura dei file e la loro eliminazione. Dovresti pubblicare il codice nella tua domanda per come stai creando ed eliminando il tuo file. Ma probabilmente si allinea con qualcosa di cui sopra.
vapcguy,

Il mio codice usa Directory.SetCreationTimeUTC()ma non riesce quando File Explorer è aperto, sostenendo che la directory è accessibile da un altro processo. Come dovrei gestire questa situazione?
Kyle Delaney,

1
@KyleDelaney Direi che devi aspettare fino alla chiusura della cartella, se non si tratta di qualche secondo, tutto diventerà rapidamente complesso (mantieni una coda in background con operazioni in sospeso? Controllo del file system? Polling?) Potresti voler pubblica una domanda con maggiori dettagli per una risposta ad hoc
Adriano Repetti,

29

L'uso di FileShare ha risolto il mio problema di apertura del file anche se è stato aperto da un altro processo.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}

9

Si è verificato un problema durante il caricamento di un'immagine e non è stato possibile eliminarla e ha trovato una soluzione. gl hf

//C# .NET
var image = Image.FromFile(filePath);

image.Dispose(); // this removes all resources

//later...

File.Delete(filePath); //now works

2
Se si sta eliminando un file, è necessario prima eliminare l'oggetto immagine.
Shazia,

1
Bello, ho cercato questa soluzione. Ho avuto anche un problema con la funzione Image.FromFile.
il rampollo

1
@thescion :) np
Hudson

1
Questo ha funzionato perfettamente per me e ha risolto il mio esatto problema. Volevo elaborare una cartella di file Tiff, convertirli in flussi di byte [] e inviarli a un servizio, quindi spostare la cartella dei file Tiff in una cartella di archivio "Elaborata". Image.FromFile posiziona un blocco sul file Tiff e non lo rilascia tempestivamente, quindi quando andavo a spostare i file Tiff e la cartella contenente, ottenevo l'errore "utilizzato da un altro processo", poiché i blocchi erano ancora a posto. Effettuare il. Rilascio subito dopo aver ottenuto i byte del file Tiff risolto totalmente questo problema di file bloccato.
Developer63

4

Ho ricevuto questo errore perché stavo facendo File. Sposta in un percorso di file senza un nome di file, è necessario specificare il percorso completo nella destinazione.


E non solo senza un nome di file. Un nome di file (destinazione) illegale - nel mio caso "... \ file". - darà lo stesso stupido errore e ti indicherà la direzione sbagliata per mezza giornata!
Nick Westgate,

3

L'errore indica che un altro processo sta tentando di accedere al file. Forse tu o qualcun altro lo avete aperto mentre state tentando di scrivergli. "Leggi" o "Copia" di solito non causano questo, ma scrivere su di esso o chiamare eliminarlo su di esso.

Ci sono alcune cose basilari da evitare, come hanno già detto altre risposte:

  1. Nelle FileStreamoperazioni, posizionalo in un usingblocco con aFileShare.ReadWrite modalità di accesso.

    Per esempio:

    using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
    {
    }

    Si noti che FileAccess.ReadWritenon è possibile se si utilizza FileMode.Append.

  2. Mi sono imbattuto in questo problema quando stavo usando un flusso di input per fare un File.SaveAsquando il file era in uso. Nel mio caso ho scoperto che in realtà non avevo bisogno di salvarlo nuovamente nel file system, quindi ho finito per rimuoverlo, ma probabilmente avrei potuto provare a creare un FileStream in una usingdichiarazione con FileAccess.ReadWrite, molto simile al codice sopra.

  3. Salvare i dati come file diverso e tornare indietro per eliminare quello vecchio quando non viene più utilizzato, quindi è possibile rinominare quello che è stato salvato correttamente con il nome di quello originale. Il modo in cui si esegue il test del file in uso viene realizzato tramite

    List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);

    riga nel mio codice di seguito, e potrebbe essere eseguita in un servizio Windows, su un ciclo, se si dispone di un determinato file che si desidera guardare ed eliminare regolarmente quando si desidera sostituirlo. Se non si dispone sempre dello stesso file, è possibile aggiornare un file di testo o una tabella di database che il servizio controlla sempre i nomi dei file, quindi esegue quel controllo per i processi e successivamente esegue il processo uccide ed elimina su di esso, come descrivo nella prossima opzione. Tieni presente che avrai bisogno di un nome utente e di una password con privilegi di amministratore sul computer dato, ovviamente, per eseguire la cancellazione e la fine dei processi.

  4. Quando non sai se un file sarà in uso quando stai cercando di salvarlo, puoi chiudere tutti i processi che potrebbero usarlo, come Word, se si tratta di un documento Word, prima del salvataggio.

    Se è locale, puoi farlo:

    ProcessHandler.localProcessKill("winword.exe");

    Se è remoto, puoi farlo:

    ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");

    dove txtUserNameè sotto forma di DOMAIN\user.

  5. Diciamo che non conosci il nome del processo che sta bloccando il file. Quindi, puoi farlo:

    List<Process> lstProcs = new List<Process>();
    lstProcs = ProcessHandler.WhoIsLocking(file);
    
    foreach (Process p in lstProcs)
    {
        if (p.MachineName == ".")
            ProcessHandler.localProcessKill(p.ProcessName);
        else
            ProcessHandler.remoteProcessKill(p.MachineName, txtUserName, txtPassword, p.ProcessName);
    }

    Nota che filedeve essere il percorso UNC: \\computer\share\yourdoc.docxper Processcapire quale computer è acceso ed p.MachineNameessere valido.

    Di seguito è riportata la classe utilizzata da queste funzioni, che richiede l'aggiunta di un riferimento System.Management. Il codice è stato originariamente scritto da Eric J .:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Management;
    
    namespace MyProject
    {
        public static class ProcessHandler
        {
            [StructLayout(LayoutKind.Sequential)]
            struct RM_UNIQUE_PROCESS
            {
                public int dwProcessId;
                public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
            }
    
            const int RmRebootReasonNone = 0;
            const int CCH_RM_MAX_APP_NAME = 255;
            const int CCH_RM_MAX_SVC_NAME = 63;
    
            enum RM_APP_TYPE
            {
                RmUnknownApp = 0,
                RmMainWindow = 1,
                RmOtherWindow = 2,
                RmService = 3,
                RmExplorer = 4,
                RmConsole = 5,
                RmCritical = 1000
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            struct RM_PROCESS_INFO
            {
                public RM_UNIQUE_PROCESS Process;
    
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
                public string strAppName;
    
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
                public string strServiceShortName;
    
                public RM_APP_TYPE ApplicationType;
                public uint AppStatus;
                public uint TSSessionId;
                [MarshalAs(UnmanagedType.Bool)]
                public bool bRestartable;
            }
    
            [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
            static extern int RmRegisterResources(uint pSessionHandle,
                                                UInt32 nFiles,
                                                string[] rgsFilenames,
                                                UInt32 nApplications,
                                                [In] RM_UNIQUE_PROCESS[] rgApplications,
                                                UInt32 nServices,
                                                string[] rgsServiceNames);
    
            [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
            static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
    
            [DllImport("rstrtmgr.dll")]
            static extern int RmEndSession(uint pSessionHandle);
    
            [DllImport("rstrtmgr.dll")]
            static extern int RmGetList(uint dwSessionHandle,
                                        out uint pnProcInfoNeeded,
                                        ref uint pnProcInfo,
                                        [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                                        ref uint lpdwRebootReasons);
    
            /// <summary>
            /// Find out what process(es) have a lock on the specified file.
            /// </summary>
            /// <param name="path">Path of the file.</param>
            /// <returns>Processes locking the file</returns>
            /// <remarks>See also:
            /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
            /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
            /// 
            /// </remarks>
            static public List<Process> WhoIsLocking(string path)
            {
                uint handle;
                string key = Guid.NewGuid().ToString();
                List<Process> processes = new List<Process>();
    
                int res = RmStartSession(out handle, 0, key);
                if (res != 0) throw new Exception("Could not begin restart session.  Unable to determine file locker.");
    
                try
                {
                    const int ERROR_MORE_DATA = 234;
                    uint pnProcInfoNeeded = 0,
                        pnProcInfo = 0,
                        lpdwRebootReasons = RmRebootReasonNone;
    
                    string[] resources = new string[] { path }; // Just checking on one resource.
    
                    res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
    
                    if (res != 0) throw new Exception("Could not register resource.");
    
                    //Note: there's a race condition here -- the first call to RmGetList() returns
                    //      the total number of process. However, when we call RmGetList() again to get
                    //      the actual processes this number may have increased.
                    res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
    
                    if (res == ERROR_MORE_DATA)
                    {
                        // Create an array to store the process results
                        RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                        pnProcInfo = pnProcInfoNeeded;
    
                        // Get the list
                        res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                        if (res == 0)
                        {
                            processes = new List<Process>((int)pnProcInfo);
    
                            // Enumerate all of the results and add them to the 
                            // list to be returned
                            for (int i = 0; i < pnProcInfo; i++)
                            {
                                try
                                {
                                    processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                                }
                                // catch the error -- in case the process is no longer running
                                catch (ArgumentException) { }
                            }
                        }
                        else throw new Exception("Could not list processes locking resource.");
                    }
                    else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
                }
                finally
                {
                    RmEndSession(handle);
                }
    
                return processes;
            }
    
            public static void remoteProcessKill(string computerName, string userName, string pword, string processName)
            {
                var connectoptions = new ConnectionOptions();
                connectoptions.Username = userName;
                connectoptions.Password = pword;
    
                ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions);
    
                // WMI query
                var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'");
    
                using (var searcher = new ManagementObjectSearcher(scope, query))
                {
                    foreach (ManagementObject process in searcher.Get()) 
                    {
                        process.InvokeMethod("Terminate", null);
                        process.Dispose();
                    }
                }            
            }
    
            public static void localProcessKill(string processName)
            {
                foreach (Process p in Process.GetProcessesByName(processName))
                {
                    p.Kill();
                }
            }
    
            [DllImport("kernel32.dll")]
            public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags);
    
            public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;
    
        }
    }

2

Come hanno indicato altre risposte in questo thread, per risolvere questo errore è necessario ispezionare attentamente il codice, per capire dove viene bloccato il file.

Nel mio caso, stavo inviando il file come allegato e-mail prima di eseguire l'operazione di spostamento.

Quindi il file è stato bloccato per un paio di secondi fino a quando il client SMTP ha finito di inviare l'e-mail.

La soluzione che ho adottato è stata quella di spostare prima il file e quindi inviare l'e-mail. Questo ha risolto il problema per me.

Un'altra possibile soluzione, come sottolineato in precedenza da Hudson, sarebbe stata quella di disporre l'oggetto dopo l'uso.

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
} 

Se un file è in uso File.Move()non funzionerà e darà lo stesso errore. Se sto semplicemente aggiungendo un file a un messaggio di posta elettronica, non penso che si verifichi un errore durante l'utilizzo durante l' Attachments.Add()operazione perché si tratta solo di un'operazione di copia. In caso contrario, è possibile copiarlo in una directory Temp, allegare la copia ed eliminare il file copiato in seguito. Ma non penso, se l'OP vuole modificare un file e usarlo, che questo tipo di soluzione (di cui non hai mostrato il codice, solo la parte allegata) funzionerebbe. .Dispose()è sempre una buona idea, ma non è rilevante qui a meno che il file non venga aperto in un'operazione precedente.
Vapcguy,

1

Ho avuto il seguente scenario che stava causando lo stesso errore:

  • Carica file sul server
  • Quindi eliminare i vecchi file dopo che sono stati caricati

La maggior parte dei file era di piccole dimensioni, tuttavia alcuni erano di grandi dimensioni, quindi il tentativo di eliminarli ha comportato l' accesso al file errore di .

Non è stato facile trovare, tuttavia, la soluzione era semplice come Aspettando "che l'attività completasse l'esecuzione":

using (var wc = new WebClient())
{
   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 
}

-1

Il mio codice qui sotto risolve questo problema, ma suggerisco prima di tutto che devi capire cosa sta causando questo problema e provare la soluzione che puoi trovare cambiando il codice

Posso dare un altro modo per risolvere questo problema, ma la soluzione migliore è controllare la struttura della codifica e provare ad analizzare ciò che lo rende possibile, se non trovi alcuna soluzione puoi andare con questo codice qui sotto

try{
Start:
///Put your file access code here


}catch (Exception ex)
 {
//by anyway you need to handle this error with below code
   if (ex.Message.StartsWith("The process cannot access the file"))
    {
         //Wait for 5 seconds to free that file and then start execution again
         Thread.Sleep(5000);
         goto Start;
    }
 }

1
Alcuni problemi con questo codice: 1) se devi chiamare GC.*(), probabilmente hai altri problemi con il tuo codice. 2) Il messaggio è localizzato e fragile , utilizzare invece HRESULT. 3) Potresti voler dormire con Task.Delay()(e dieci secondi è in qualche modo eccessivo in molti casi). 4) Non hai una condizione di uscita: questo codice potrebbe essere sospeso per sempre. 5) Sicuramente non è necessario gotoqui. 6) Catturare di Exceptionsolito è una cattiva idea, anche in questo caso perché ... 6) Se succede qualcos'altro, stai deglutendo l'errore.
Adriano Repetti

@AdrianoRepetti il ​​mio primo commento ha detto di non usare questo codice prova a trovare altra soluzione questa sarà l'ultima opzione e comunque se questo errore si verifica il codice viene fermato ma se stai gestendo questo sistema di errore aspetterà di liberare il file e dopodiché inizierà funzionante e non ho altri problemi con GC. * Durante l'utilizzo di questo codice. Ho già un sistema funzionante con questo codice, quindi ho pubblicato può essere utile per qualcuno.
Ash,

Direi di non usare mai questo codice (a causa dei punti che ho menzionato sopra) e non lo considererei funzionante in un sistema prod ma è solo il mio POV. Sentiti libero di non essere d'accordo!
Adriano Repetti

1) Ho usato il servizio Windows dove ho consumato questo e ho usato GC. * Fino a questo non ho alcun problema con questo2) Sì HRESULT può essere l'opzione migliore in termini di standard di codifica3) Dipende dalla situazione che puoi dedicare al tempo che ho appena modificato a 5 secondi 4) comunque è meglio attendere qualche tempo per liberare risorse invece di arrestare il sistema con errore 5) sì sono d'accordo con te la gestione delle eccezioni non è sempre una buona idea ma se lo snippet di codice è piccolo e sai che qui posso gestire le eccezioni, questa soluzione è un'opzione per te.
Ash,

Vedi anche: docs.microsoft.com/en-us/archive/blogs/ricom/… . Il caso SOLO (e sottolineo SOLO) che dovevo usare GC.Collect()è stato quando ho a che fare con alcuni oggetti COM.
Adriano Repetti
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.