Как освободить файл от процесса?
Существует некий imageList, в который загружаю файлы следующим образом:
// LOAD PHOTO
// get all photos as file and save it in imageListPhoto
int vPhotoSize = 0;
int[] indexDataFacePhoto = new int[400800];
foreach (int i in usersData.readId)
{
// 1. if you already have a user photo with id equal to i, then =>
// 2. => use for listView
// 3. else download nessesary photo and =>
// 4. => use again
Device.DisableDevice(axFP_CLOCK, m_nMachineNum);
string pathToPhoto = photoDir + "\\" + i.ToString() + ".jpg";
// 1.
if (System.IO.File.Exists(pathToPhoto))
{
// 2.
this.imageListPhoto.Images.Add(Image.FromFile(pathToPhoto));
continue;
}
// 3.
IntPtr ptrIndexFacePhoto = Marshal.AllocHGlobal(indexDataFacePhoto.Length);
bool bRet = axFP_CLOCK.GetEnrollPhotoCS(m_nMachineNum, i, ref vPhotoSize, ptrIndexFacePhoto);
if (bRet)
{
byte[] mbytCurEnrollData = new byte[vPhotoSize];
Marshal.Copy(ptrIndexFacePhoto, mbytCurEnrollData, 0, vPhotoSize);
System.IO.File.WriteAllBytes(pathToPhoto, mbytCurEnrollData);
}
Marshal.FreeHGlobal(ptrIndexFacePhoto);
// 4.
this.imageListPhoto.Images.Add(Image.FromFile(pathToPhoto));
Device.EnableDevice(axFP_CLOCK, m_nMachineNum);
}
Данный imageList используется в ListViewItem (если нужен код, дайте знать).
После каждого второго вызова формы с данным кодом, любое следующее действие очищает память:
Собственно вопрос, как сразу очистить используемую память? Пытался удалять папку с файлами при закрытии формы, но ругается, что файл занят другим процессом. Despose тоже не помог. Строчка Marshal.FreeHGlobal(ptrIndexFacePhoto); тоже не сработала.
Ответы (2 шт):
Когда изображение создаётся напрямую из файла:
Image.FromFile(pathToPhoto)
оно удерживает хендл на ресурс (файл).
Распространённым способом решения проблемы является следующий:
var image = Image.FromFile(pathToPhoto);
this.imageListPhoto.Images.Add((Image)image.Clone());
image.Dispose();
Здесь первоначальное изображение, удерживающее хендл, сразу диспозится (можно обернуть в using). Таким образом файл освобождается.
А в ImageList добавляется копия изображения. Она с файлом не связана.
Занятая память != используемая память. Сборщик мусора освободит память тогда, когда посчитает нужным, обычно это происходит не сразу после удаления ресурсов в памяти, а через какое-то время. На объемах в несколько мегабайт вам не стоит об этом беспокоиться вообще. Но код можно оптимизировать, и поправить проблему с занятыми файлами.
Чтобы избежать лишних аллокаций неуправляемой памяти, AllocHGlobal можно использовать один раз. Чтобы избежать лишних аллокаций массивов, можно так же создать один буфферный массив за пределами цикла.
const int bufferSize = 1024 * 1024; // 1 МБ
IntPtr ptr = Marshal.AllocHGlobal(bufferSize);
try
{
using var ms = new MemoryStream(bufferSize);
Device.DisableDevice(axFP_CLOCK, m_nMachineNum);
foreach (int i in usersData.readId)
{
ms.Position = 0;
string pathToPhoto = Path.Combine(photoDir, $"{i}.jpg");
try // не используйте File.Exists, он не гарантирует, что через миллисекунду после проверки этот файл останется на месте, то есть проверка будет успешной, а при чтении файла будет исключение, проще сразу обработать его
{
ms.SetLength(0);
using var fs = File.OpenRead(pathToPhoto);
fs.CopyTo(ms);
}
catch (FileNotFoundException) { } // Файл не найден - залогируйте, если надо
int photoSize = 0; // нет смысла выносить отсюда переменную вовне, если она там не нужна
if (ms.Position == 0 && axFP_CLOCK.GetEnrollPhotoCS(m_nMachineNum, i, ref photoSize, ptr))
{
ms.SetLength(photoSize);
Marshal.Copy(ptr, ms.GetBuffer(), 0, photoSize);
using var fs = File.Create(pathToPhoto);
ms.CopyTo(fs);
}
ms.Position = 0;
imageListPhoto.Images.Add(Image.FromStream(ms));
}
Device.EnableDevice(axFP_CLOCK, m_nMachineNum);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
Теперь должно памяти поменьше кушать.
Ну и определитесь, а стоит ли это все на диск писать?