我正在寫使用FileSystemWatcher
監視更改爲指定目錄下的程序,當收到OnCreated或事件調用onChanged,它會將這些創建/修改過的文件到指定的目錄。起初我遇到的問題是,OnChanged/OnCreated事件可以發送兩次(如果需要處理500MB文件,則不可接受),但是我解決了這個問題,並且我真的被阻止,正在獲取以下內容IOException
: 該進程無法訪問文件'C:\ Where \ Photosmarks \ bookmarks(11).html',因爲它正在被另一個進程使用。C#無法打開文件進行讀取
因此,防止複製它應該將所有文件的程序。 因此,正如我所提到的,當用戶使用此程序時,他/她指定了受監視的目錄,當用戶在該目錄中複製/創建/更改文件時,程序應該獲取OnCreated/OnChanged事件,然後將該文件複製到其他幾個目錄。 上述錯誤在所有情況下都會發生,如果用戶複製了幾個文件,這些文件需要覆蓋被監控文件夾中的其他文件或者複製多個文件的大量文件,甚至有時在受監控目錄中複製一個文件。 整個程序很大,所以我發送了最重要的部分。 OnCreated:
private void OnCreated(object source, FileSystemEventArgs e) {
AddLogEntry(e.FullPath, "created", "");
// Update last access data if it's file so the same file doesn't
// get processed twice because of sending another event.
if (fileType(e.FullPath) == 2) {
lastPath = e.FullPath;
lastTime = DateTime.Now;
}
// serves no purpose now, it will be remove soon
string fileName = GetFileName(e.FullPath);
// copies file from source to few other directories
Copy(e.FullPath, fileName);
Console.WriteLine("OnCreated: " + e.FullPath);
}
調用onChanged:
private void OnChanged(object source, FileSystemEventArgs e) {
// is it directory
if (fileType(e.FullPath) == 1)
return; // don't mind directory changes itself
// Only if enough time has passed or if it's some other file
// because two events can be generated
int timeDiff = ((TimeSpan)(DateTime.Now - lastTime)).Seconds;
if ((timeDiff < minSecsDiff) && (e.FullPath.Equals(lastPath))) {
Console.WriteLine("-- skipped -- {0}, timediff: {1}", e.FullPath, timeDiff);
return;
}
// Update last access data for above to work
lastPath = e.FullPath;
lastTime = DateTime.Now;
// Only if size is changed, the rest will handle other handlers
if (e.ChangeType == WatcherChangeTypes.Changed) {
AddLogEntry(e.FullPath, "changed", "");
string fileName = GetFileName(e.FullPath);
Copy(e.FullPath, fileName);
Console.WriteLine("OnChanged: " + e.FullPath);
}
}
的fileType:
private int fileType(string path) {
if (Directory.Exists(path))
return 1; // directory
else if (File.Exists(path))
return 2; // file
else
return 0;
}
複製:
private void Copy(string srcPath, string fileName) {
foreach (string dstDirectoy in paths) {
string eventType = "copied";
string error = "noerror";
string path = "";
string dirPortion = "";
// in case directory needs to be made
if (srcPath.Length > fsw.Path.Length) {
path = srcPath.Substring(fsw.Path.Length,
srcPath.Length - fsw.Path.Length);
int pos = path.LastIndexOf('\\');
if (pos != -1)
dirPortion = path.Substring(0, pos);
}
if (fileType(srcPath) == 1) {
try {
Directory.CreateDirectory(dstDirectoy + path);
//Directory.CreateDirectory(dstDirectoy + fileName);
eventType = "created";
} catch (IOException e) {
eventType = "error";
error = e.Message;
}
} else {
try {
if (!overwriteFile && File.Exists(dstDirectoy + path))
continue;
// create new dir anyway even if it exists just to be sure
Directory.CreateDirectory(dstDirectoy + dirPortion);
// copy file from where event occured to all specified directories
using (FileStream fsin = new FileStream(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (FileStream fsout = new FileStream(dstDirectoy + path, FileMode.Create, FileAccess.Write)) {
byte[] buffer = new byte[32768];
int bytesRead = -1;
while ((bytesRead = fsin.Read(buffer, 0, buffer.Length)) > 0)
fsout.Write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
if ((e is IOException) && (overwriteFile == false)) {
eventType = "skipped";
} else {
eventType = "error";
error = e.Message;
// attempt to find and kill the process locking the file.
// failed, miserably
System.Diagnostics.Process tool = new System.Diagnostics.Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = "\"" + srcPath + "\"";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern)) {
System.Diagnostics.Process.GetProcessById(int.Parse(match.Value)).Kill();
}
Console.WriteLine("ERROR: {0}: [ {1} ]", e.Message, srcPath);
}
}
}
AddLogEntry(dstDirectoy + path, eventType, error);
}
}
我在我的程序檢查無處不在,每當我用一些文件我在using
塊中使用它,所以即使將事件寫入日誌(由於可能存在過多的代碼,所以我省略了該類的內容)不會鎖定該文件,也就是說不應該這樣做,因爲所有操作都使用using
語句塊。
我根本不知道誰是從鎖定用戶通過Windows或別的東西,如果不是我的節目「複製」過程中的文件。
現在我有兩個可能的「解決方案」(我不能說他們是乾淨的解決方案,因爲它們是黑客,因此不理想)。由於可能的問題是與fileType
方法(還有什麼可以鎖定的文件?)我試了一下改變這一點,以模擬「堵,直到準備到打開」操作:
的fileType:
private int fileType(string path) {
FileStream fs = null;
int ret = 0;
bool run = true;
if (Directory.Exists(path))
ret = 1;
else {
while (run) {
try {
fs = new FileStream(path, FileMode.Open);
ret = 2;
run = false;
} catch (IOException) {
} finally {
if (fs != null) {
fs.Close();
fs.Dispose();
}
}
}
}
return ret;
}
儘管我可以告訴(測試),但它的功能還是很強大的,更不用說其他缺陷了。
我可以嘗試的其他「解決方案」(我沒有測試它)在fileType()
方法的末尾某處使用GC.Collect()
。也許更糟糕的「解決方案」比以前更糟糕。
有人能認罪告訴我,究竟什麼是鎖定的文件,防止它打開,我該如何解決呢?我錯過了什麼?
在此先感謝。
非常感謝響應速度快,但我不需要寫權限,我需要_read_許可所以我可以從中讀取並複製到其他目的地。 – Maks 2010-06-16 21:17:48
@Maks:當你可以寫文件時,你也可以閱讀它。如果您永遠無法寫信,例如因爲該文件夾是受保護的,您也可以簡單地嘗試以只讀方式打開文件。 – 2010-06-16 21:31:51
我的意思是說,如果它是Windows進程(複製)而不是寫入權限(因爲它沒有在事件發生時完成),它可能「更容易」(比寫入權限更可能)獲取讀取權限:) – Maks 2010-06-17 13:22:06