IOException: процес не може отримати доступ до файлу "шлях до файлу", оскільки він використовується іншим процесом


172

У мене є якийсь код, і коли він виконується, він кидає IOException, кажучи це

Процес не може отримати доступ до файлу "ім'я файлу", оскільки він використовується іншим процесом

Що це означає, і що я можу з цим зробити?


1
Дивіться це запитання, як знайти інший процес, який використовує файл.
стом

Відповіді:


274

У чому причина?

Повідомлення про помилку є досить зрозумілим: ви намагаєтеся отримати доступ до файлу, і він недоступний, оскільки інший процес (або навіть той самий процес) щось робить з ним (і він не дозволяв жодного спільного доступу).

Налагодження

Це може бути досить легко вирішити (або досить важко зрозуміти), залежно від конкретного сценарію. Давайте подивимось деякі.

Ваш процес єдиний, хто має доступ до цього файлу.
Ви впевнені, що інший процес - це ваш власний процес. Якщо ви знаєте, що ви відкриваєте цей файл в іншій частині вашої програми, то, перш за все, ви повинні перевірити, чи правильно ви закриваєте обробку файлу після кожного використання. Ось приклад коду з цією помилкою:

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

На щастя, FileStreamреалізація IDisposable, тому легко загорнути весь код у usingвиписку:

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

Цей шаблон також гарантуватиме, що файл не залишиться відкритим у випадку винятків (це може бути причиною використання файлу: щось пішло не так, і ніхто не закрив його; див. Приклад у цій публікації ).

Якщо все здається нормальним (ви впевнені, що завжди закриваєте кожен відкритий файл, навіть у випадку винятків) і у вас є кілька робочих потоків, то у вас є два варіанти: переробити код, щоб серіалізувати доступ до файлів (не завжди це можливо і не завжди) бажано) або застосувати повторний шаблон . Це досить поширена схема для операцій вводу / виводу: ви намагаєтесь щось зробити, а в разі помилки ви зачекаєте та спробуйте знову (ви запитували себе, чому, наприклад, Windows Shell потрібен певний час, щоб повідомити, що використовується файл і його не можна видалити?). У C # це досить просто реалізувати (див. Також кращі приклади щодо вводу / виводу диска , мереж та доступу до бази даних ).

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);
    }
}

Зверніть увагу на поширену помилку, яку ми часто спостерігаємо в StackOverflow:

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

У цьому випадку ReadAllText()не вдасться, оскільки файл використовується ( File.Open()у рядку раніше). Заздалегідь відкривати файл - це не тільки непотрібно, але й неправильно. Те ж саме відноситься і до всіх Fileфункцій , які не повертають дескриптор до файлу ви працюєте: File.ReadAllText(), File.WriteAllText(), File.ReadAllLines(), File.WriteAllLines()та інші (наприклад , File.AppendAllXyz()функції) будуть все відкривати і закривати файл самостійно.

Ваш процес не єдиний, хто має доступ до цього файлу
Якщо ваш процес не єдиний, хто має доступ до цього файлу, то взаємодія може бути важче. Шаблон повторних спроб допоможе (якщо файл не повинен бути відкритий ким - небудь ще , але це, то вам необхідно утиліта , як Process Explorer , щоб перевірити , хто робить те , що ).

Способи уникнути

При необхідності, завжди використовувати з допомогою заяви для відкриття файлів. Як було сказано в попередньому пункті, він буде активно допомагати вам уникнути багатьох поширених помилок (див цього поста для прикладу на те , щоб не використовувати його ).

Якщо можливо, спробуйте вирішити, кому належить доступ до певного файлу, і централізуйте доступ за допомогою декількох відомих методів. Наприклад, якщо у вас є файл даних, де ваша програма читає і записує, вам слід встановити вікно всіх кодів вводу-виводу в одному класі. Це полегшить налагодження (адже ви завжди можете поставити там точку перерви і побачити, хто що робить), а також це буде точка синхронізації (якщо потрібно) для багаторазового доступу.

Не забувайте, що операції вводу / виводу завжди можуть вийти з ладу, поширений приклад такий:

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

Якщо хтось видалить файл після, File.Exists()але раніше File.Delete(), він перекине IOExceptionмісце, де ви можете помилково почувати себе в безпеці.

Коли це можливо, застосуйте шаблон повторного спроби , і якщо ви використовуєте FileSystemWatcher, розглянути можливість відстрочки дії (оскільки ви отримаєте сповіщення, але програма все ще може працювати виключно з цим файлом).

Розширені сценарії
Це не завжди так просто, тому вам може знадобитися поділитися доступом з кимось іншим. Якщо, наприклад, ви читаєте з початку і пишете до кінця, у вас є як мінімум два варіанти.

1) поділитися тим самим FileStreamз належними функціями синхронізації (оскільки це не є безпечним для потоків ). Дивіться це і це повідомлення для прикладу.

2) використовуйте FileShareперерахування, щоб доручити ОС дозволити іншим процесам (або іншим частинам вашого власного процесу) одночасно отримувати доступ до одного файлу.

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

У цьому прикладі я показав, як відкрити файл для запису та поділитися для читання; зауважте, що при читанні та написанні перекриттів це призводить до невизначених або недійсних даних. Це ситуація, яку треба впоратися під час читання. Також зауважте, що це не робить доступ до streamбезпечного потоку, тому цей об'єкт не може бути спільним для кількох потоків, якщо доступ якось синхронізовано (див. Попередні посилання). Доступні й інші варіанти спільного використання, і вони відкривають складніші сценарії. Будь ласка, зверніться до MSDN для отримання більш детальної інформації.

Загалом N процесів можуть читати з одного файлу всі разом, але тільки один повинен писати, в контрольованому сценарії ви навіть можете включати одночасні записи, але це не може бути узагальнено в кількох абзацах тексту всередині цієї відповіді.

Чи можна розблокувати файл, який використовується іншим процесом? Це не завжди безпечно і не так просто, але так, можливо .


Я не знаю, що з моїм кодом не так, я використовую блоки, але все ж є помилка, яка каже, що файл використовується, коли я намагаюся його видалити.
Джамшайд Камран

Ні, насправді у мене є керування таймером, що робить це, крім таймера, якщо я знову викликаю функцію, це призведе до виключення. в основному я розшифровую файл в інший текстовий файл, після цього видаляючи новостворений файл ,, який видаляє виняток при видаленні!
Джамшайд Камран

3
@ جمشیدکامران Навіщо створювати файл із вмістом у коді та видаляти його? Створюється файл спочатку дивно, а потім. Оскільки ви не розмістили код, ми не знаємо, що ви робите. Але коли ви створюєте свій файл, якщо ви це робите File.Create(path), вам слід додати його .Close()в кінці, перш ніж писати в нього. Окрім цього, існують підводні камені, крім usingвисловлювань для написання файлів та видалення після них. Ви повинні розмістити код у своєму запитанні щодо того, як ви створюєте та видаляєте файл. Але, ймовірно, відповідає одному із згаданих вище.
vapcguy

Мій код використовує, Directory.SetCreationTimeUTC()але він виходить з ладу, коли відкривається Провідник файлів, стверджуючи, що до каталогу доступний інший процес. Як мені впоратися з цією ситуацією?
Кайл Делані

1
@KyleDelaney Я б сказав, що вам потрібно почекати, поки папка не буде закрита, якщо це не кілька секунд, тоді все швидко стане складним (збережіть фонову чергу з очікуваними операціями? Оглядач файлової системи? Опитування?) Ви, можливо, захочете опублікуйте запитання з додатковими подробицями для тимчасової відповіді
Adriano Repetti

29

За допомогою FileShare виправлено мою проблему відкриття файлу, навіть якщо він відкритий іншим процесом.

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

9

Під час завантаження зображення виникла проблема, не вдалося її видалити, і знайшли рішення. gl hf

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

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

//later...

File.Delete(filePath); //now works

2
Якщо ви видаляєте файл, вам потрібно спочатку розпорядитися об’єктом зображення.
шазія

1
Приємно, я шукав це рішення. У мене також були проблеми з функцією Image.FromFile.
scion

1
@thescion :) np
Хадсон

1
Це блискуче спрацювало для мене і вирішило мою точну проблему. Я хотів обробити папку файлів Tiff, перетворити їх у потоки байтів [] та надіслати службі, а потім перемістити папку файлів Tiff у папку архіву "Оброблений". Image.FromFile розміщує замок на файлі Tiff, і не випускає його своєчасно, тому коли я пішов переміщувати файли Tiff та папку, що містив, я отримав би помилку "використовується іншим процесом", оскільки блоки все ще були на місці. Виконання .Release відразу після отримання байтів файлу Tiff повністю усунула цю проблему із заблокованим файлом.
Developer63

4

Я отримав цю помилку, оскільки я робив File.Move до шляху до файлу без імені файлу, потрібно вказати повний шлях у пункті призначення.


І не тільки без імені файлу. Незаконне ім'я (призначення) файлу - у моєму випадку "... \ файл". - зробить цю саму дурну помилку і вкаже вас у неправильний бік на півдня!
Нік Вестгейт

3

Помилка вказує на те, що інший процес намагається отримати доступ до файлу. Можливо, ви або хтось інший відкриває його, поки ви намагаєтесь написати це. "Прочитати" або "Копіювати" зазвичай це не викликає, але запис на нього або виклик видалення на ньому буде.

Існує кілька основних речей, яких можна цього уникнути, як згадували інші відповіді:

  1. В FileStreamопераціях розміщуйте його в usingблоці з FileShare.ReadWriteрежимом доступу.

    Наприклад:

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

    Зауважте, що FileAccess.ReadWriteце неможливо, якщо ви використовуєте FileMode.Append.

  2. Я зіткнувся з цією проблемою, коли я використовував вхідний потік для виконання, File.SaveAsколи файл використовувався. У моєму випадку я виявив, що мені взагалі не потрібно було зберігати його назад у файловій системі, тому я в кінцевому підсумку просто видалив це, але, мабуть, я міг би спробувати створити FileStream у usingвиписці із FileAccess.ReadWrite, як і код вище.

  3. Збереження даних як іншого файлу та повернення до видалення старого, коли виявлено, що він більше не використовується, а потім перейменування того, що успішно збережено, на ім'я оригіналу. Тестування файлу, який ви використовуєте, здійснюється через

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

    рядок у моєму коді нижче, і це можна зробити в службі Windows, в циклі, якщо у вас є певний файл, який ви хочете регулярно переглядати та видаляти, коли ви хочете його замінити. Якщо у вас не завжди є один і той же файл, може бути оновлений текстовий файл або таблиця баз даних, що служба завжди перевіряє назви файлів, а потім виконує цю перевірку на процеси та згодом виконує вбивання процесу та видалення на ньому, як я описую у наступному варіанті. Зверніть увагу, що вам потрібно буде ім'я користувача та пароль облікового запису, які мають права адміністратора на даному комп'ютері, звичайно, для виконання процесів видалення та закінчення.

  4. Якщо ви не знаєте, чи буде використовуватись файл, коли ви намагаєтеся зберегти його, ви можете закрити всі процеси, які могли б його використовувати, як Word, якщо це документ Word, перед збереженням.

    Якщо він локальний, ви можете зробити це:

    ProcessHandler.localProcessKill("winword.exe");

    Якщо він віддалений, ви можете зробити це:

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

    де txtUserNameзнаходиться у формі DOMAIN\user.

  5. Скажімо, ви не знаєте ім'я процесу, яке блокує файл. Потім ви можете зробити це:

    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);
    }

    Зауважте, що це fileповинен бути шлях до UNC: \\computer\share\yourdoc.docxдля того, Processщоб зрозуміти, на якому комп'ютері він працює, та p.MachineNameбути дійсним.

    Нижче наведено клас, який ці функції використовують, для чого потрібно додати посилання на System.Management. Код був спочатку написаний Еріком Дж . :

    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

Як вказували інші відповіді в цій темі, для вирішення цієї помилки потрібно уважно ознайомитися з кодом, щоб зрозуміти, де файл заблокований.

У моєму випадку я надсилав файл у вигляді вкладення електронної пошти перед тим, як виконати операцію переміщення.

Таким чином файл заблокувався на пару секунд, поки клієнт SMTP не закінчив надсилати електронну пошту.

Я прийняв рішення: спочатку перемістити файл, а потім надіслати електронний лист. Це вирішило для мене проблему.

Іншим можливим рішенням, як раніше вказував Хадсон, було б знешкодження об'єкта після використання.

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
            }
} 

Якщо файл використовується, File.Move()він не працюватиме і дає таку ж помилку. Якщо ви просто додаєте файл до електронної пошти, я не думаю, що він помиляється під час використання під час Attachments.Add()операції, оскільки це лише операція копіювання. Якщо це було з якоїсь причини, ви можете скопіювати його в каталог Temp, приєднати копію та видалити скопійований файл згодом. Але я не думаю, що якщо ОП хоче змінити файл і використовувати це, то таке рішення (на яке ви не показали код, тільки прикріплена частина) буде працювати. .Dispose()завжди хороша ідея, але не актуальна тут, якщо файл не відкрито в попередній оп.
vapcguy

1

У мене був такий сценарій, який спричинив ту саму помилку:

  • Завантажте файли на сервер
  • Потім позбудьтесь старих файлів після їх завантаження

Більшість файлів мали невеликий розмір, однак деякі були великі, і тому спроба видалити ці файли призвела до помилки доступу до файлу .

Знайти це було непросто, однак рішення було таким же простим, як і очікування "виконання завдання для завершення виконання":

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

-1

Мій нижче код вирішує цю проблему, але я пропоную, перш за все, вам потрібно зрозуміти, що викликає цю проблему, і спробувати рішення, яке ви можете знайти, змінивши код

Я можу дати інший спосіб вирішити цю проблему, але краще рішення - перевірити структуру кодування та спробувати проаналізувати, що робить це, якщо ви не знайдете жодного рішення, то можете перейти з цим кодом нижче

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
Кілька проблем з цим кодом: 1) якщо вам потрібно зателефонувати, GC.*()то, мабуть, у вас є інші проблеми з вашим кодом. 2) Повідомлення локалізовано та неміцне , використовуйте замість нього HRESULT. 3) Ви можете спати Task.Delay()(і десять секунд якимось чином надмірне у багатьох випадках). 4) У вас немає умови виходу: цей код може висіти forver. 5) Тут вам точно не потрібно goto. 6) Ловити, Exceptionяк правило, погана ідея, в цьому випадку також тому, що ... 6) Якщо щось інше трапиться, то ви проковтнете помилку.
Адріано Репетті

@AdrianoRepetti мій перший коментар сказав, що не використовуйте цей код, спробуйте знайти інше рішення, це буде останнім варіантом, і, як ніколи, якщо ця помилка трапиться, код припиняється, але якщо ви обробляєте цю помилку, система зачекає, щоб звільнити файл і після цього він запуститься працюючи, і я не стикаюся з іншими проблемами з GC. * під час використання цього коду. У мене вже є робоча система з цим кодом, тому я розмістив повідомлення, хтось може бути корисним.
Еш

Я б сказав, щоб не використовувати цей код ніколи (через точки, про які я згадував вище), і я б не вважав, що це працює в системі prod, але це лише мій POV. Не соромтеся погоджуватися!
Адріано Репетті

1) Я використовував службу Windows, де я спожив це, і я використав GC. * До цього я не стикаюся з жодною проблемою з цим2) Так, HRESULT може бути кращим варіантом з точки зору кодування standard3) Залежно від ситуації, яку ви можете дати часу, щойно редагував це до 5 секунд 4) у будь-якому випадку краще систематично почекати колись, щоб звільнити ресурс, а не зупинити систему з помилкою 5) так, я згоден з вами обробка виключень - це не завжди гарна ідея, але якщо фрагмент коду невеликий, і ви знаєте що тут я можу впоратися з винятком, тоді це рішення є варіантом для вас.
Еш

Дивіться також: docs.microsoft.com/en-us/archive/blogs/ricom/… . ТИЛЬКИ (і я наголошую ТІЛЬКИ) випадок, який мені потрібно було використовувати GC.Collect(), був у роботі з деякими об'єктами COM.
Адріано Репетті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.