Saya memiliki beberapa kode dan ketika dijalankan, ia melempar IOException
, mengatakan itu
Proses tidak dapat mengakses file 'nama file' karena sedang digunakan oleh proses lain
Apa artinya ini, dan apa yang bisa saya lakukan?
Saya memiliki beberapa kode dan ketika dijalankan, ia melempar IOException
, mengatakan itu
Proses tidak dapat mengakses file 'nama file' karena sedang digunakan oleh proses lain
Apa artinya ini, dan apa yang bisa saya lakukan?
Jawaban:
Pesan kesalahannya cukup jelas: Anda mencoba mengakses file, dan itu tidak dapat diakses karena proses lain (atau bahkan proses yang sama) sedang melakukan sesuatu dengannya (dan tidak memungkinkan berbagi apa pun).
Mungkin sangat mudah untuk dipecahkan (atau cukup sulit untuk dipahami), tergantung pada skenario spesifik Anda. Mari kita lihat.
Proses Anda adalah satu-satunya untuk mengakses file itu.
Anda yakin proses lainnya adalah proses Anda sendiri. Jika Anda tahu Anda membuka file itu di bagian lain dari program Anda, maka pertama-tama Anda harus memeriksa apakah Anda benar-benar menutup pegangan file setelah setiap kali digunakan. Berikut adalah contoh kode dengan bug ini:
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
Untungnya FileStream
implementasinya IDisposable
, jadi mudah untuk membungkus semua kode Anda di dalam using
pernyataan:
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
Pola ini juga akan memastikan bahwa file tidak akan dibiarkan terbuka jika ada pengecualian (mungkin itu alasan file digunakan: ada yang tidak beres, dan tidak ada yang menutupnya; lihat posting ini sebagai contoh).
Jika semuanya tampak baik-baik saja (Anda yakin Anda selalu menutup setiap file yang Anda buka, bahkan dalam kasus pengecualian) dan Anda memiliki beberapa utas yang berfungsi, maka Anda memiliki dua opsi: pengerjaan ulang kode Anda untuk membuat serial akses file (tidak selalu bisa dilakukan dan tidak selalu ingin) atau menerapkan pola coba lagi . Ini adalah pola yang cukup umum untuk operasi I / O: Anda mencoba melakukan sesuatu dan jika terjadi kesalahan Anda menunggu dan mencoba lagi (apakah Anda bertanya pada diri sendiri mengapa, misalnya, Windows Shell membutuhkan waktu untuk memberi tahu Anda bahwa file sedang digunakan dan tidak bisa dihapus?). Di C # ini cukup mudah untuk diterapkan (lihat juga contoh yang lebih baik tentang disk I / O , jaringan dan akses basis data ).
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);
}
}
Harap perhatikan kesalahan umum yang sering kita lihat di StackOverflow:
var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);
Dalam hal ini ReadAllText()
akan gagal karena file sedang digunakan ( File.Open()
di baris sebelumnya). Untuk membuka file sebelumnya tidak hanya tidak perlu tetapi juga salah. Hal yang sama berlaku untuk semua File
fungsi yang tidak mengembalikan pegangan ke file Anda bekerja dengan: File.ReadAllText()
, File.WriteAllText()
, File.ReadAllLines()
, File.WriteAllLines()
dan lain-lain (seperti File.AppendAllXyz()
fungsi) semua akan membuka dan menutup file sendiri.
Proses Anda bukan satu-satunya untuk mengakses file itu.
Jika proses Anda bukan satu-satunya untuk mengakses file itu, maka interaksi bisa lebih sulit. Sebuah pola coba lagi akan membantu (jika file tersebut tidak harus terbuka oleh orang lain tetapi, maka Anda memerlukan utilitas seperti Process Explorer untuk memeriksa siapa yang melakukan apa ).
Jika berlaku, selalu gunakan menggunakan pernyataan untuk membuka file. Seperti yang disebutkan pada paragraf sebelumnya, ini akan secara aktif membantu Anda menghindari banyak kesalahan umum (lihat posting ini untuk contoh tentang bagaimana tidak menggunakannya ).
Jika memungkinkan, cobalah untuk memutuskan siapa yang memiliki akses ke file tertentu dan memusatkan akses melalui beberapa metode terkenal. Jika, misalnya, Anda memiliki file data di mana program Anda membaca dan menulis, maka Anda harus memasukkan semua kode I / O di dalam satu kelas. Ini akan membuat debug lebih mudah (karena Anda selalu dapat meletakkan breakpoint di sana dan melihat siapa yang melakukan apa) dan juga itu akan menjadi titik sinkronisasi (jika diperlukan) untuk beberapa akses.
Jangan lupa operasi I / O selalu bisa gagal, contoh umum adalah ini:
if (File.Exists(path))
File.Delete(path);
Jika seseorang menghapus file setelah File.Exists()
tetapi sebelumnya File.Delete()
, maka itu akan melempar IOException
di tempat di mana Anda salah merasa aman.
Kapan pun memungkinkan, terapkan pola coba lagi , dan jika Anda menggunakan FileSystemWatcher
, pertimbangkan untuk menunda tindakan (karena Anda akan diberi tahu, tetapi aplikasi mungkin masih bekerja secara eksklusif dengan file itu).
Skenario lanjutan
Ini tidak selalu mudah, jadi Anda mungkin perlu berbagi akses dengan orang lain. Jika, misalnya, Anda membaca dari awal dan menulis hingga akhir, Anda memiliki setidaknya dua opsi.
1) berbagi hal yang sama FileStream
dengan fungsi sinkronisasi yang tepat (karena tidak aman untuk thread ). Lihat ini dan posting ini sebagai contoh.
2) menggunakan FileShare
enumerasi untuk menginstruksikan OS untuk memungkinkan proses lain (atau bagian lain dari proses Anda sendiri) untuk mengakses file yang sama secara bersamaan.
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}
Dalam contoh ini saya menunjukkan cara membuka file untuk ditulis dan dibagikan untuk dibaca; harap dicatat bahwa ketika membaca dan menulis tumpang tindih, itu menghasilkan data yang tidak terdefinisi atau tidak valid. Ini adalah situasi yang harus ditangani ketika membaca. Perhatikan juga bahwa ini tidak membuat akses ke stream
thread-safe, jadi objek ini tidak dapat dibagi dengan banyak utas kecuali jika akses entah bagaimana disinkronkan (lihat tautan sebelumnya). Opsi berbagi lainnya tersedia, dan mereka membuka skenario yang lebih kompleks. Silakan merujuk ke MSDN untuk detail lebih lanjut.
Secara umum N proses dapat membaca dari file yang sama secara bersamaan, tetapi hanya satu yang harus menulis, dalam skenario terkontrol Anda bahkan dapat mengaktifkan tulisan bersamaan tetapi ini tidak dapat digeneralisasi dalam beberapa paragraf teks di dalam jawaban ini.
Apakah mungkin untuk membuka kunci file yang digunakan oleh proses lain? Itu tidak selalu aman dan tidak begitu mudah tetapi ya, itu mungkin .
File.Create(path)
, Anda harus menambahkan .Close()
di akhir itu sebelum Anda menulisnya. Ada jebakan seperti itu, selain using
pernyataan untuk menulis file, kemudian menghapusnya. Anda harus memposting kode dalam pertanyaan Anda untuk bagaimana Anda membuat & menghapus file Anda. Tetapi mungkin sejalan dengan sesuatu yang disebutkan di atas.
Directory.SetCreationTimeUTC()
tetapi gagal ketika File Explorer terbuka, mengklaim direktori sedang diakses oleh proses lain. Bagaimana saya harus menangani situasi ini?
Menggunakan FileShare memperbaiki masalah saya membuka file bahkan jika dibuka oleh proses lain.
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Mengalami masalah saat mengunggah gambar dan tidak dapat menghapusnya dan menemukan solusinya. gl hf
//C# .NET
var image = Image.FromFile(filePath);
image.Dispose(); // this removes all resources
//later...
File.Delete(filePath); //now works
Saya mendapat kesalahan ini karena saya sedang melakukan File.Move ke path file tanpa nama file, perlu menentukan path lengkap di tujuan.
Kesalahan menunjukkan proses lain sedang mencoba mengakses file. Mungkin Anda atau orang lain membukanya saat Anda mencoba menulisnya. "Baca" atau "Salin" biasanya tidak menyebabkan ini, tetapi menulis ke sana atau memanggil delete akan membuatnya.
Ada beberapa hal mendasar untuk menghindari ini, seperti jawaban lain telah disebutkan:
Dalam FileStream
operasi, letakkan di using
blok dengan FileShare.ReadWrite
mode akses.
Sebagai contoh:
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Catatan yang FileAccess.ReadWrite
tidak mungkin jika Anda gunakan FileMode.Append
.
Saya mengalami masalah ini ketika saya menggunakan aliran input untuk melakukan File.SaveAs
ketika file sedang digunakan. Dalam kasus saya yang saya temukan, saya sebenarnya tidak perlu menyimpannya kembali ke sistem file sama sekali, jadi saya akhirnya hanya menghapus itu, tapi saya mungkin bisa mencoba membuat FileStream dalam sebuah using
pernyataan dengan FileAccess.ReadWrite
, seperti kode atas.
Menyimpan data Anda sebagai file yang berbeda dan kembali untuk menghapus yang lama ketika ternyata tidak lagi digunakan, kemudian mengganti nama yang berhasil disimpan dengan nama yang asli adalah pilihan. Bagaimana Anda menguji file yang sedang digunakan dicapai melalui
List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);
baris dalam kode saya di bawah ini, dan dapat dilakukan dalam layanan Windows, dalam satu lingkaran, jika Anda memiliki file tertentu yang ingin Anda tonton dan hapus secara teratur ketika Anda ingin menggantinya. Jika Anda tidak selalu memiliki file yang sama, file teks atau tabel database dapat diperbarui bahwa layanan selalu memeriksa nama file, dan kemudian melakukan itu memeriksa proses & selanjutnya melakukan proses membunuh dan menghapusnya, seperti yang saya jelaskan di opsi selanjutnya. Perhatikan bahwa Anda akan memerlukan nama pengguna dan kata sandi akun yang memiliki hak Admin di komputer yang diberikan, tentu saja, untuk melakukan penghapusan dan proses akhir.
Ketika Anda tidak tahu apakah suatu file akan digunakan ketika Anda mencoba untuk menyimpannya, Anda bisa menutup semua proses yang bisa menggunakannya, seperti Word, jika itu dokumen Word, di depan save.
Jika lokal, Anda dapat melakukan ini:
ProcessHandler.localProcessKill("winword.exe");
Jika jarak jauh, Anda dapat melakukan ini:
ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");
di mana txtUserName
dalam bentuk DOMAIN\user
.
Katakanlah Anda tidak tahu nama proses yang mengunci file. Kemudian, Anda dapat melakukan ini:
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);
}
Catatan yang file
harus menjadi jalur UNC: \\computer\share\yourdoc.docx
agar Process
dapat mengetahui komputer apa yang aktif dan p.MachineName
valid.
Di bawah ini adalah kelas yang digunakan fungsi-fungsi ini, yang membutuhkan penambahan referensi System.Management
. Kode ini awalnya ditulis oleh 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;
}
}
Seperti yang ditunjukkan oleh jawaban lain di utas ini, untuk menyelesaikan kesalahan ini Anda perlu memeriksa kode dengan hati-hati, untuk memahami di mana file terkunci.
Dalam kasus saya, saya mengirim file sebagai lampiran email sebelum melakukan operasi pemindahan.
Jadi file terkunci selama beberapa detik sampai klien SMTP selesai mengirim email.
Solusi yang saya adopsi adalah memindahkan file terlebih dahulu, dan kemudian mengirim email. Ini memecahkan masalah bagi saya.
Solusi lain yang mungkin, seperti yang ditunjukkan sebelumnya oleh Hudson, adalah membuang benda setelah digunakan.
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()
tidak akan berfungsi & memberikan kesalahan yang sama. Jika hanya menambahkan file ke email saya tidak berpikir itu kesalahan saat digunakan selama Attachments.Add()
operasi karena ini hanya operasi salin. Jika itu dilakukan karena alasan tertentu Anda bisa menyalinnya ke direktori Temp, lampirkan salinannya, & hapus file yang disalin sesudahnya. Tapi saya tidak berpikir, jika OP ingin memodifikasi file & menggunakannya, solusi semacam ini (yang Anda tidak menunjukkan kode, hanya bagian yang dilampirkan) akan berfungsi. .Dispose()
selalu merupakan ide yang baik, tetapi tidak relevan di sini kecuali file dibuka di op sebelumnya.
Saya memiliki skenario berikut yang menyebabkan kesalahan yang sama:
Sebagian besar file berukuran kecil, namun ada pula yang berukuran besar, sehingga berusaha menghapusnya menyebabkan tidak dapat mengakses kesalahan file .
Itu tidak mudah ditemukan, namun, solusinya sesederhana Menunggu "untuk tugas untuk menyelesaikan eksekusi":
using (var wc = new WebClient())
{
var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
tskResult.Wait();
}
Kode saya di bawah ini menyelesaikan masalah ini, tetapi saya sarankan Pertama-tama Anda perlu memahami apa yang menyebabkan masalah ini dan mencoba solusi yang dapat Anda temukan dengan mengubah kode
Saya dapat memberikan cara lain untuk mengatasi masalah ini, tetapi solusi yang lebih baik adalah dengan memeriksa struktur pengkodean Anda dan mencoba menganalisis apa yang menyebabkan hal ini terjadi, jika Anda tidak menemukan solusi apa pun, Anda dapat menggunakan kode ini di bawah ini.
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;
}
}
GC.*()
maka Anda mungkin memiliki masalah lain dengan kode Anda. 2) Pesan dilokalkan dan rapuh , gunakan HRESULT sebagai gantinya. 3) Anda mungkin ingin tidur dengan Task.Delay()
(dan sepuluh detik entah bagaimana berlebihan dalam banyak kasus). 4) Anda tidak memiliki kondisi keluar: kode ini bisa hang forver. 5) Anda pasti tidak perlu di goto
sini. 6) Menangkap Exception
biasanya merupakan ide yang buruk, dalam hal ini juga karena ... 6) Jika ada hal lain terjadi maka Anda menelan kesalahan.
GC.Collect()
adalah ketika berurusan dengan beberapa objek COM.