I have a "work list style" application where the user selects a zip file from a list, then clicks a button to unzip it to a local folder.
Here is an extract from the code used:
ZipFile zip = Ionic.Zip.ZipFile.Read(sourcePackage);
zip.ExtractAll(destination);
zip.Dispose();
Everything works fine the first time but if the user tries to unzip the same file again (even after unzipping a few others), it goes too quick and all that is created in the destination folder is what looks like a temp file (e.g. 'x2hiex0z.pj0').
It's as if Ionic.Zip.ZipFile.Read is creating a cache of previously unzipped file names.
If so, how do I clear it so that I can force it to unzip the file again?
I gues you get some kind of a "File Exist"-Exception
Try:
OpenFileDialog open = new OpenFileDialog();
open.Filter = "zip Datei (.zip)|*.zip";
open.RestoreDirectory = true;
if (open.ShowDialog() == DialogResult.OK)
{
try
{
ZipFile zip = Ionic.Zip.ZipFile.Read(open.FileName);
zip.ExtractAll(".\\");
zip.Dispose();
}
catch (ZipException zex)
{
MessageBox.Show(zex.Message);
}
}
or with a Thread:
private void open()
{
OpenFileDialog open = new OpenFileDialog();
open.Filter = "zip Datei (.zip)|*.zip";
open.RestoreDirectory = true;
if (open.ShowDialog() == DialogResult.OK)
{
Thread t1 = new Thread
(delegate()
{
try
{
using (ZipFile zip = Ionic.Zip.ZipFile.Read(open.FileName))
{
zip.ExtractProgress += zip_ExtractProgress;
zip.ExtractAll(".\\", ExtractExistingFileAction.OverwriteSilently);
}
}
catch (ZipException zex)
{
error(zex.Message);
}
});
t1.IsBackground = true;
t1.Start();
}
}
private void zip_ExtractProgress(object sender, ExtractProgressEventArgs args)
{
update(args.TotalBytesToTransfer, args.BytesTransferred);
}
private void update(long ueTotal, long done)
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() => { update(ueTotal, done); }));
}
else
{
if (ueTotal > 0)
{
double prz = (100d / ueTotal) * done;
lblProz.Text = prz.ToString("###.##");
}
}
}
Related
I wrote program that listens on a directory (GUI - WPF). When the new file show up in this directory is sent to the printer. The problem occurs when I try to save a large file to this directory. I have to wait until the file is closed, and then send it to the printer. I have a function that checks if the file is open. But when I use it in the whole GUI hangs. How do I use this function asynchronously?
protected void newfile(object fscreated, FileSystemEventArgs Eventocc)
{
try
{
string CreatedFileName = Eventocc.Name;
FileInfo createdFile = new FileInfo(CreatedFileName);
string extension = createdFile.Extension;
string eventoccured = Eventocc.ChangeType.ToString();
fsLastRaised = DateTime.Now;
this.Dispatcher.Invoke((Action)(() =>
{
String file = "";
file = watchingFolder + "\\" + CreatedFileName;
//printing
this.Dispatcher.Invoke((Action)(() =>
{
FileInfo info = new FileInfo(file);
while (!IsFileReady(info)) { }
var t = new Thread(() => printFile(file, extension)); //send to printer
t.Start();
}));
}));
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error");
}
}
IsFileReady function:
public static bool IsFileReady(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
And printfile
public void printFile(string filepath, string ext)
{
ProcessStartInfo info = new ProcessStartInfo();
info.Verb = "print";
info.FileName = filepath;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
Process p = new Process();
p.StartInfo = info;
p.Start();
p.WaitForInputIdle();
System.Threading.Thread.Sleep(3000);
if (false == p.CloseMainWindow())
p.Kill();
}
}
How can I correct this code to work with large files without hangs up?
EDIT:
For check new file I use FileSystemWatcher
private void start(object sender, RoutedEventArgs e)
{
if (watchingFolder == null)
{
}
else
{
fs = new FileSystemWatcher(watchingFolder, "*.*");
fs.EnableRaisingEvents = true;
fs.IncludeSubdirectories = true;
fs.Created += new FileSystemEventHandler(newfile);
btnSatrt.IsEnabled = false;
btnStop.IsEnabled = true;
}
}
You're executing while (!IsFileReady(info)) { } through Dispatcher.Invoke, that executes the code on the UI thread so it will block for sure the app.
You aren't interacting at all with the UI, so the correct approach is to execute it asynchronously, via Tasks and awaits or with a background thread via the ThreadPool and not using at all Dispatcher.Invoke.
Try to execute all code in the newfile event handler on a background thread by starting a new task:
protected async void newfile(object fscreated, FileSystemEventArgs Eventocc)
{
try
{
await Task.Run(() =>
{
string CreatedFileName = Eventocc.Name;
FileInfo createdFile = new FileInfo(CreatedFileName);
string extension = createdFile.Extension;
string eventoccured = Eventocc.ChangeType.ToString();
fsLastRaised = DateTime.Now;
string file = watchingFolder + "\\" + CreatedFileName;
FileInfo info = new FileInfo(file);
while (!IsFileReady(info)) { }
printFile(file, extension);
});
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error");
}
}
Use a BackgroundWorker instead of Dispatcher.Invoke.
I'm working on a web installer and one of the things I have currently is
void MoveFiles()
{
lbldlstatus.Text = "Moving Files";
string InstallDirectory = Directory.GetCurrentDirectory() + "/DoxramosRepack-master";
DirectoryInfo d = new DirectoryInfo(InstallDirectory);
foreach(var file in d.GetFiles("*"))
{
try
{
if (File.Exists(file.Name)) {
File.Delete(file.Name);
}
Directory.Move(file.FullName, file.Name);
Cleanup();
}
catch(Exception e)
{
MessageBox.Show(e.ToString());
lbldlstatus.Text = "Repack Installation Failed";
}
}
}
void Cleanup()
{
lbldlstatus.Text = "Cleaning Up Files";
try
{
if (File.Exists("Repack.zip"))
{
File.Delete("Repack.zip");
}
if(Directory.Exists("DoxramosRepack-master"))
{
Directory.Delete("DoxramosRepack-master");
}
lbldlstatus.Text = "Repack Installed Successfully";
}
When I get to Cleanup() I have a System.IO.IOException.
Process cannot access the file Repack.zip because it being used by
another process.
The full code runs
Download->Extract->Move->Cleanup.
I'm not sure what process is being used, but I'm looking to find a way for each process to wait for the previous to finish before starting.
According to extract code below
void Extract()
{
string zipPath = #"Repack.zip";
string extractPath = #".";
try
{
using (ZipFile unzip = ZipFile.Read(zipPath))
{
unzip.ExtractAll(extractPath);
lbldlstatus.Text = "Extracting Files";
MoveFiles();
}
}
catch (ZipException e)
{
MessageBox.Show(e.ToString());
lbldlstatus.Text = "Repack Installation Failed";
}
}
You are calling the move files before you are finish with the zip file. Seeing as the move file method is responsible for calling the clean up function then you should make sure that the zip file is already disposed of before trying to delete it.
void Extract()
{
string zipPath = #"Repack.zip";
string extractPath = #".";
try
{
using (ZipFile unzip = ZipFile.Read(zipPath))
{
unzip.ExtractAll(extractPath);
lbldlstatus.Text = "Extracting Files";
}
MoveFiles();
}
catch (ZipException e)
{
MessageBox.Show(e.ToString());
lbldlstatus.Text = "Repack Installation Failed";
}
}
Clean up should also be called after everything has been moved. Currently the example code is calling it repeatedly in the for loop.
The code which you pasted on pastebin is different from what you have posted here. The code in pastebin never calls cleanup.
Anyways the problem is because you are calling MoveFiles() from within the using block here:
using (ZipFile unzip = ZipFile.Read(zipPath))
{
unzip.ExtractAll(extractPath);
lbldlstatus.Text = "Extracting Files";
MoveFiles();
}
Move it outside the using block.
I'm making a WPF application.
I'm using WebClient to download files. I have a list of the name of the files that should be downloaded from a current path. I use an foreach to iterate through each name and then download each file sequency. The name of the file i get from a torrent file which i decode.
public class DownloadGameFile
{
private DownloadTorrentFile DLTorrent;
//List of file that already exist
private List<string> ExistFile = new List<string>();
DirectoryInfo fileInfo;
private volatile bool _completed;
private string savePath = #"C:\Program Files (x86)\program\Client\package\downloads\";
public DownloadGameFile()
{
DLTorrent = new DownloadTorrentFile();
fileInfo = new DirectoryInfo(savePath);
}
public bool StartDownload(int torrentId)
{
try
{
DLTorrent.DecodeTorrent(torrentId);
//File info from a Directory
FileInfo[] files = fileInfo.GetFiles();
foreach (FileInfo i in files)
{
Console.WriteLine("Files exit ");
if (DLTorrent.GameInfomation[i.Name] != i.Length)
{
i.Delete();
}
else
{
Console.WriteLine("add files ");
ExistFile.Add(i.Name);
}
}
//Make a list which file not downloaded yet
var res = DLTorrent.GameInfomation.Keys.Except(ExistFile);
foreach (var x in res)
{
Console.WriteLine(x);
}
foreach (var x in res)
{
DownloadProtocol("http://cdn.path.com/rental/" + torrentId + "/" + x, savePath + x);
}
return true;
}
catch
{
return false;
}
}
public void DownloadProtocol(string address, string location)
{
WebClient client = new WebClient();
Uri Uri = new Uri(address);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
client.DownloadFileAsync(Uri, location);
}
private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
// Displays the operation identifier, and the transfer progress.
Console.WriteLine("{0} downloaded {1} of {2} bytes. {3} % complete...",
(string)e.UserState,
e.BytesReceived,
e.TotalBytesToReceive,
e.ProgressPercentage);
}
private void Completed(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled == true)
{
Console.WriteLine("Download has been canceled.");
}
else
{
Console.WriteLine("Download completed!");
}
}
This code work fine in an Console app with a current thread blocker. But when I use the same code in an WPF app It doesn't. I'm using a button to execute the StartDownload() function, but when I do that it start downloading all the files at the same time. Example the first file get 3% done and then it switch to another file and so on. I really don't know why this isn't working.
Have you considered not using .DownloadFileAsync? You can try .DownloadFile and start DownloadProtocol() with a single background thread. Although I think you'll have to rethink your DownloadProgress output.
I do something very similar within a winform.
I have the following code:
Open File Code
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Open File";
ofd.FileName = "";
ofd.Filter = "Rich Text Files (*.rtf)|*.rtf|Text Document (*.txt)|*.txt|Microsoft Word Document (*.doc)|*.doc|Hypertext Markup Language Document (*.html)|*.html"; StreamReader sr = null;
if (ofd.ShowDialog() != DialogResult.Yes) return;
{
NewFile();
}
try
{
sr = new StreamReader(ofd.FileName);
this.Text = string.Format("{0} - Basic Word Processor", Path.GetFileName(ofd.FileName));
richTextBoxPrintCtrl1.Text = ofd.FileName;
richTextBoxPrintCtrl1.Text = sr.ReadToEnd();
filepath = ofd.FileName;
richTextBoxPrintCtrl1.LoadFile(fileName, RichTextBoxStreamType.RichText);
}
catch
{
}
finally
{
if (sr != null) sr.Close();
}
New File Code
if (richTextBoxPrintCtrl1.Modified)
{
DialogResult r = MessageBox.Show(this, "Save Current Document?", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Yes) SaveFile();
if (r == DialogResult.Cancel) return;
}
this.Text = string.Format("Untitled - Basic Word Processor");
richTextBoxPrintCtrl1.Text = "";
filepath = null;
}
}
SaveFileAs Code
SaveFileDialog sfdSaveFile = new SaveFileDialog();
sfdSaveFile.Title = "Save File";
sfdSaveFile.FileName = "Untitled";
sfdSaveFile.Filter = "Rich Text Files (*.rtf)|*.rtf|Text Document (*.txt)|*.txt|Microsoft Word Document (*.doc)|*.doc|Hypertext Markup Language Document (*.html)|*.html";
if (sfdSaveFile.ShowDialog() == DialogResult.OK)
try
{
filepath = sfdSaveFile.FileName;
SaveFile();
this.Text = string.Format("{0} - Basic Word Processor", Path.GetFileName(sfdSaveFile.FileName));
}
catch (Exception exc)
{
}
SaveFile Code
if (filepath == null)
{
SaveFileAs();
return;
}
StreamWriter sw = new StreamWriter(filepath);
//StreamWriter stwrite = null;
try
{
sw.WriteLine(richTextBoxPrintCtrl1.Text);
richTextBoxPrintCtrl1.Modified = false;
sw.Close();
}
catch (Exception e)
{
MessageBox.Show("Failed to save file. \n" + e.Message);
}
finally
{
if (sw != null) sw.Close();
}
Currently, the program skips the NewFile event (even if the text has been modified). How can I make it so that when I click "Open", it asks me if I would like to save (if the text is modified). Then if I click cancel, it returns me to the form?
Sorry. I'm really new to programming so this is all a learning curve.
Okay, I think I see what's going on here. First off I don't believe return; works the way you think it does.
if (ofd.ShowDialog() != DialogResult.Yes) return;
{
NewFile();
}
You have a return; call that happens if the show dialog is not yes. The { newFile() } code doesn't need braces around it. So those lines are really:
if (ofd.ShowDialog() != DialogResult.Yes) return;
NewFile();
Now, given your requirement, NewFile is called WAY too late in the game anyway. You want that to happen before you ask them what to open; just like most other windows programs work.
But, there's another issue. Your return statement in the NewFile method is simply returning from NewFile. It's not telling the previous method to bail out.
So the NewFile method needs a return type to indicate whether to allow the calling method to go forward or not.
And, looking at your save file you have a return method there too. What's with all of the return; calls?
Which brings us back to how do we fix this?
Answer: rewrite the whole thing. Starting with the following method:
private Boolean CanClear() {
Boolean result = false;
if (richTextBoxPrintCtrl1.Modified)
{
DialogResult r = MessageBox.Show(this, "Save Current Document?", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Yes) {
SaveFile();
result = true;
}
} else {
result = true;
}
return result;
}
Now, in your Open and New file methods do the following (assuming these are the method headers)
protected void OpenFile(...) {
if (!CanClear()) return;
.... now execute the code to load the open dialog and the selected file.
}
protected void NewFile(...) {
if (!CanClear()) return;
this.Text = string.Format("Untitled - Basic Word Processor");
richTextBoxPrintCtrl1.Text = "";
filepath = null;
}
The problem is here:
if (ofd.ShowDialog() != DialogResult.Yes) return;
{
NewFile();
}
remove that return. But, as #Chris says, you should ask whether to save the current file or not before the user selects the new file to open.
I have a ListBox which I put some files, if the file is not AVI I automatically converts it but I want when the files converting message will write on a label that the files are now converted to another format, i know i need use Dispatcher in order to update the UI thread but i use now Winform instead of WPF, and i need help with this.
BTW i cannot use Task because i am using .Net 3.5
private void btnAdd_Click(object sender, EventArgs e)
{
System.IO.Stream myStream;
OpenFileDialog thisDialog = new OpenFileDialog();
thisDialog.InitialDirectory = "c:\\";
thisDialog.Filter = "All files (*.*)|*.*";
thisDialog.FilterIndex = 1;
thisDialog.RestoreDirectory = false;
thisDialog.Multiselect = true; // Allow the user to select multiple files
thisDialog.Title = "Please Select Source File";
thisDialog.FileName = lastPath;
List<string> list = new List<string>();
if (thisDialog.ShowDialog() == DialogResult.OK)
{
foreach (String file in thisDialog.FileNames)
{
try
{
if ((myStream = thisDialog.OpenFile()) != null)
{
using (myStream)
{
listBoxFiles.Items.Add(file);
lastPath = file;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
for (int i = 0; i < listBoxFiles.Items.Count; i++)
{
string path = (string)listBoxFiles.Items[i];
FileInfo fileInfo = new FileInfo(path);
if (fileInfo.Extension != ".AVI")
{
listToRemove.Add(path);
}
}
(new System.Threading.Thread(sendFilesToConvertToPcap)).Start();
foreach (string file in listToRemove) //remove all non .AVI files from listbox
{
listBoxFiles.Items.Remove(file);
}
}
}
this function need to change the Label:
public void sendFilesToConvertToPcap()
{
if (listToRemove.Count == 0) // nothing to do
{
return;
}
lblStatus2.Content = "Convert file to .AVI...";
foreach (String file in listToRemove)
{
FileInfo fileInfo = new FileInfo(file);
myClass = new (class who convert the files)(fileInfo);
String newFileName = myClass.mNewFileName;
listBoxFiles.Items.Add(myClass._newFileName);
}
lblStatus2.Content = "Finished...";
}
From your question, it seems that you'd like to convert several files. You may want to consider using the BackgroundWorker class and overwrite the DoWork and ProgressChanged events as described in this article. You can update the label and other controls in the ProgressChanged event.
public void sendFilesToConvertToPcap()
{
.....
....
this.Invoke((MethodInvoker)delegate {
lblStatus2.Text = "Convert file to .AVI..."; });
....
}
This is a very common requirement for long-running processes. If you don't explicitly call methods on a separate thread, they will automatically run on the main (see: UI) thread and cause your UI to hand (as I suspect you already know).
http://www.dotnetperls.com/backgroundworker
Here is an old, but excellent link with an example on how to use the background worker to handle the threadpool for you.
This way, you can just create a worker to manage running your conversion process on a separate thread, and I believe there is even an example for creating a process bar.