How to asynchronously write CD/DVD using IMAPI? - c#

I have been told to write a software to burn a CD synchronously/asynchronously as per user choice. I am using IMAPIv2 with C# for the project, and it does not provide the functionality explicitly to write the data asynchronously.
In order to design the functionality, I have researched online resources, but in vain.
Can someone explain what Synchronous/Asynchronous I/O is, in terms of burning an image on a disc?
Any help is appreciated.

IMAPI does not provide in-build class/method to write data asynchronously. But it is designed a way that it is possible with any technology that supports asynchronous programming. The one you are using (C# as you mentioned in comments) does support it.
IMAPI exposes interfaces those report status for progress and actions. All you need to do is use the threading to run the activity asynchronously; this will free up your UI and you can perform other activities. Then, you may subscribe for the events those will report the status to you.
Refer this project on CodeProject which uses BackgroundWorker for the same:
Multithreading
Burning or formatting media can take some time, so we do not want to perform these actions on the main UI thread. I use the BackgroundWorker class to handle the multithreading of these lengthy tasks. The BackgroundWorker class allows you to set values within the thread and then call the ReportProgress method which fires a ProgressChanged event in the calling thread. When you are finished with your worker thread, it fires the RunWorkerCompleted event to notify the calling thread that it is finished.
Following are DoWork and Update events:
private void backgroundBurnWorker_DoWork(object sender, DoWorkEventArgs e)
{
MsftDiscRecorder2 discRecorder = null;
MsftDiscFormat2Data discFormatData = null;
try
{
//
// Create and initialize the IDiscRecorder2 object
//
discRecorder = new MsftDiscRecorder2();
var burnData = (BurnData)e.Argument;
discRecorder.InitializeDiscRecorder(burnData.uniqueRecorderId);
//
// Create and initialize the IDiscFormat2Data
//
discFormatData = new MsftDiscFormat2Data
{
Recorder = discRecorder,
ClientName = ClientName,
ForceMediaToBeClosed = _closeMedia
};
//
// Set the verification level
//
var burnVerification = (IBurnVerification)discFormatData;
burnVerification.BurnVerificationLevel = _verificationLevel;
//
// Check if media is blank, (for RW media)
//
object[] multisessionInterfaces = null;
if (!discFormatData.MediaHeuristicallyBlank)
{
multisessionInterfaces = discFormatData.MultisessionInterfaces;
}
//
// Create the file system
//
IStream fileSystem;
if (!CreateMediaFileSystem(discRecorder, multisessionInterfaces, out fileSystem))
{
e.Result = -1;
return;
}
//
// add the Update event handler
//
discFormatData.Update += discFormatData_Update;
//
// Write the data here
//
try
{
discFormatData.Write(fileSystem);
e.Result = 0;
}
catch (COMException ex)
{
e.Result = ex.ErrorCode;
MessageBox.Show(ex.Message, "IDiscFormat2Data.Write failed",
MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
finally
{
if (fileSystem != null)
{
Marshal.FinalReleaseComObject(fileSystem);
}
}
//
// remove the Update event handler
//
discFormatData.Update -= discFormatData_Update;
if (_ejectMedia)
{
discRecorder.EjectMedia();
}
}
catch (COMException exception)
{
//
// If anything happens during the format, show the message
//
MessageBox.Show(exception.Message);
e.Result = exception.ErrorCode;
}
finally
{
if (discRecorder != null)
{
Marshal.ReleaseComObject(discRecorder);
}
if (discFormatData != null)
{
Marshal.ReleaseComObject(discFormatData);
}
}
}
void discFormatData_Update([In, MarshalAs(UnmanagedType.IDispatch)] object sender,
[In, MarshalAs(UnmanagedType.IDispatch)] objectprogress)
{
//
// Check if we've cancelled
//
if (backgroundBurnWorker.CancellationPending)
{
var format2Data = (IDiscFormat2Data)sender;
format2Data.CancelWrite();
return;
}
var eventArgs = (IDiscFormat2DataEventArgs)progress;
_burnData.task = BURN_MEDIA_TASK.BURN_MEDIA_TASK_WRITING;
// IDiscFormat2DataEventArgs Interface
_burnData.elapsedTime = eventArgs.ElapsedTime;
_burnData.remainingTime = eventArgs.RemainingTime;
_burnData.totalTime = eventArgs.TotalTime;
// IWriteEngine2EventArgs Interface
_burnData.currentAction = eventArgs.CurrentAction;
_burnData.startLba = eventArgs.StartLba;
_burnData.sectorCount = eventArgs.SectorCount;
_burnData.lastReadLba = eventArgs.LastReadLba;
_burnData.lastWrittenLba = eventArgs.LastWrittenLba;
_burnData.totalSystemBuffer = eventArgs.TotalSystemBuffer;
_burnData.usedSystemBuffer = eventArgs.UsedSystemBuffer;
_burnData.freeSystemBuffer = eventArgs.FreeSystemBuffer;
//
// Report back to the UI
//
backgroundBurnWorker.ReportProgress(0, _burnData);
}

Related

Windows Service is Running but not executing code

We have windows service which is running fine untill any exceptions occured in the process.
It contains two Threads (GenerateInvoice and GenerateReport).
These threads are getting blocked and results in DeadLock like situation mostly when there is high CPU usage on our DataBase server.
We have done some changes in code to handle such situations like added while condition below code but still it is not working.
Below is the OnStart() method of service:
protected override void OnStart(string[] args)
{
try
{
log.Debug("Starting Invoice Generation Service");
_thread = new Thread(new ThreadStart((new GenerateInvoice()).Process));
_thread.IsBackground = true;
_thread.Start();
_reportThread = new Thread(new ThreadStart((new GenerateReport()).Process));
_reportThread.IsBackground = true;
_reportThread.Start();
}
catch (Exception ex)
{
log.Error("Error in Invoice Generation Service:", ex);
}
}
Here is the processing code of first thread: GenerateInvoice
public void Process()
{
while (isProcessActive)
{
try
{
DBBilling obj = new DBBilling();
DataTable dtInvoiceID = obj.readData(#"SELECT * FROM (SELECT ird.BillByType, ird.InvoiceID, ir.BeginDate, ir.EndDate, ir.SendToQB, ir.SendEmail,
i.ARAccountID, i.ARAccountHotelID, i.invoiceNumber,i.[STATUS],UPDATETIME,row_number() over (PARTITION BY ird.INVOICEID ORDER BY UPDATETIME DESC) AS row_number
FROM Invoices i JOIN InvoicesRunRequestDetails ird ON ird.InvoiceID=i.InvoiceID
JOIN InvoicesRunRequest ir ON ird.RequestID = ir.RequestID
Where i.[STATUS] = 'PENDING') AS rows
WHERE ROW_NUMBER=1 ORDER BY UPDATETIME");
processCounter = 0;
#region process
if (dtInvoiceID != null && dtInvoiceID.Rows.Count > 0)
{
//some code here..
}
#endregion
}
catch (Exception ex) //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016
{
log.ErrorFormat("Generate Invoice -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex);
if(DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains))
{
processCounter++;
if (processCounter >= 1) //Need to change to 25 after Problem Solve
{
isProcessActive = false;
log.ErrorFormat("Generate Invoice -> Process -> RunInvoice Service exiting loop"); //From here control is not going back
}
else
System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec
}
}
}
}
Processing of Second Thread i.e. GenerateReport code:
public void Process()
{
AppSettingsReader ar = new AppSettingsReader();
string constr = (string)ar.GetValue("BillingDB", typeof(string));
SqlConnection con = new SqlConnection(constr);
while (isProcessActive)
{
try
{
DBBilling obj = new DBBilling();
DataTable dtReportRunID = obj.readData(#"SELECT ReportRunID,MonYear, BeginDate, EndDate FROM ReportRunRequest
Where [STATUS] = 'PENDING' ORDER BY ReportRunID");
processCounter = 0;
if (dtReportRunID != null && dtReportRunID.Rows.Count > 0)
{
//some code here..
}
}
catch (Exception ex) //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016
{
log.ErrorFormat("Generate Report -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex);
if (DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains))
{
processCounter++;
if (processCounter >= 1) //Need to change to 25 after Problem Solve
{
isProcessActive = false;
log.ErrorFormat("Generate Report -> Process -> RunInvoice Service Exiting loop"); //From here control is not going back
}
else
System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec
}
}
}
}
What possible solution to avoid such conditions?
The way to avoid it is to either lock every access to a global variable, or not to use global variables.
here is one obvious example
DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains)
dbConnTimeoutErrorMessage is a static field that is being used from two different threads and I assume is not thread safe, surround access to it with a
lock(locObj)
{
// access to dbConnTimeoutErrorMessage
}
I am gonna go ahead and guess that log is also a global variable. Perhaps maybe even isProcessActive or processCounter.
I am guessing there is more in those comments - make sure your code is threadsafe before using it with two different threads.
I doubt locking access to what I said will fix your problem, but I guess your lack of threadsafe programming in these is a symptom to not using lock when it is needed. The secret is to lock every access to a global context, and just that.
What i suggest is to use Timer instead of infinite loop and as mentioned earlier in other answere you need some kind of synchronization. First of all, you need to implement your variables which used in different threads as follows (i don't know exactly definitions of your variables, but main idea is to use volatile keyword in your case):
public static volatile bool isProcessActive;
public static volatile int proccessCounter;
volatile keyword switches off the compiler optimizations for using variable in one thread. It means that your variables now are thread safe.
Next you need to use neither System.Threading.Timer or System.Timers.Timer. I will use in my example second one.
public sealed class GenerateInvoice :
{
protected const int timerInterval = 1000; // define here interval between ticks
protected Timer timer = new Timer(timerInterval); // creating timer
public GenerateInvoice()
{
timer.Elapsed += Timer_Elapsed;
}
public void Start()
{
timer.Start();
}
public void Stop()
{
timer.Stop();
}
public void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
DBBilling obj = new DBBilling();
DataTable dtInvoiceID = obj.readData(#"SELECT * FROM (SELECT ird.BillByType, ird.InvoiceID, ir.BeginDate, ir.EndDate, ir.SendToQB, ir.SendEmail,
i.ARAccountID, i.ARAccountHotelID, i.invoiceNumber,i.[STATUS],UPDATETIME,row_number() over (PARTITION BY ird.INVOICEID ORDER BY UPDATETIME DESC) AS row_number
FROM Invoices i JOIN InvoicesRunRequestDetails ird ON ird.InvoiceID=i.InvoiceID
JOIN InvoicesRunRequest ir ON ird.RequestID = ir.RequestID
Where i.[STATUS] = 'PENDING') AS rows
WHERE ROW_NUMBER=1 ORDER BY UPDATETIME");
processCounter = 0;
#region process
if (dtInvoiceID != null && dtInvoiceID.Rows.Count > 0)
{
//some code here..
}
#endregion
}
catch (Exception ex) //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016
{
log.ErrorFormat("Generate Invoice -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex);
if(DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains))
{
processCounter++;
if (processCounter >= 1) //Need to change to 25 after Problem Solve
{
isProcessActive = false;
// supposing that log is a reference type and one of the solutions can be using lock
// in that case only one thread at the moment will call log.ErrorFormat
// but better to make synchronization stuff unside logger
lock (log)
log.ErrorFormat("Generate Invoice -> Process -> RunInvoice Service exiting loop"); //From here control is not going back
}
else
// if you need here some kind of execution sleep
// here you can stop timer, change it interval and run again
// it's better than use Thread.Sleep
// System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec
}
}
}
}
Use the same approach for the GenerateReport to make Timer-based.
And, finally, you need to change your OnStart and OnStop methods something like so:
protected GenerateInvoice generateInvoice;
protected GenerateReport generateReport;
protected override void OnStart(string[] args)
{
// all exception handling should be inside class
log.Debug("Starting Invoice Generation Service");
generateInvoice = new GenerateInvoice();
generateInvoice.Start();
generateReport = new GenerateReport();
generateReport.Start();
}
protected override void OnStop()
{
generateInvoice.Stop();
generateReport.Stop();
}

Getting the C# BackgroundWorker process to invoke Pings

Read through most (all?) of the answered questions regarding the C# BackgroundWorker but none seemed to apply to this situation. If I missed one, please point me in that direction!
Anyway, I having troubles getting the Ping process to run as a background process. I made a simple form application to send pings and report back. That worked fine but it would only results results to the user after the pings were complete -- thus the need to a background process. I am somewhat new to C# and was unfamiliar with the particulars of BackgroundWorker. However found a helpful walkthrough from Microsoft here: http://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx
I am now attempting to get the same process to apply to a System.Net.NetworkInformation object instead of a System.IO.StreamReader object. I think I am really close (read: I can get the app to build and run) but I consistently get an error at runtime (see below).
This is the Microsoft code for their sample app. It works like a champ:
The method in MainForm.cs that calls the Words.cs class referenced in the walkthrough
void backgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
Words WC = (Words)e.Argument;
WC.CountWords(worker, e);
}
The relevant method in the 'Words.cs' class
public void CountWords(
System.ComponentModel.BackgroundWorker worker,
System.ComponentModel.DoWorkEventArgs e)
{
// Initialize the variables.
CurrentState state = new CurrentState();
string line = "";
int elapsedTime = 20;
DateTime lastReportDateTime = DateTime.Now;
if (CompareString == null ||
CompareString == System.String.Empty)
{
throw new Exception("CompareString not specified.");
}
// Open a new stream.
using (System.IO.StreamReader myStream = new System.IO.StreamReader(SourceFile))
{
// Process lines while there are lines remaining in the file.
while (!myStream.EndOfStream)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
line = myStream.ReadLine();
WordCount += CountInString(line, CompareString);
LinesCounted += 1;
// Raise an event so the form can monitor progress.
int compare = DateTime.Compare(
DateTime.Now, lastReportDateTime.AddMilliseconds(elapsedTime));
if (compare > 0)
{
state.LinesCounted = LinesCounted;
state.WordsMatched = WordCount;
worker.ReportProgress(0, state);
lastReportDateTime = DateTime.Now;
}
}
// Uncomment for testing.
System.Threading.Thread.Sleep(5);
}
// Report the final count values.
state.LinesCounted = LinesCounted;
state.WordsMatched = WordCount;
worker.ReportProgress(0, state);
}
}
When I try a similar process (sending a Ping instead of a reading a file) I get this error:
Error: Object reference not set to an instance of an object.
Details: System.Collections.ListDictionaryInternal //This is defined in the MyApp namespace as: using System.Collections
Source: MyApp
StackTrack: at MyApp.MainForm.Bw01DoWork(Object sender, DoWorkEventArgs e) in
[path]\MainForm.cs:line 152
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
Target: Void Bw01DoWork(System.Object, System.ComponentModel.DoWorkEventArgs)
Here is my method. Line 152 referenced in the error is the very last line of the last method in MainForm.cs (the var names are different, but you get the idea):
void Bw01DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
PTResults PR = (PTResults)e.Argument;
PR.SendPings(worker, e); // Line 152
}
And the relevant portion of the PTResults.cs class:
using (Ping newPing = new Ping())
{
PingReply reply = newPing.Send([Target Site],[Timeout]);
if(reply.Status == IPStatus.Success)
{
state.PingOK = true;
}
else if(reply.Status == IPStatus.TimedOut)
{
state.PingOK = false;
state.PingUpdateState = " Timed Out";
}
else if(reply.Status != IPStatus.Success)
{
state.PingOK = false;
state.PingUpdateState = " FAILED";
}
else
{
state.PingOK = false;
state.PingUpdateState = " UNKNOWN";
}
worker.ReportProgress(0, state.PingOK);
}
I am thinking the System.Net.NetworkInformation.Ping component cannot be invoked the same way System.IO.StreamReader is. Thoughts?
I doubt it makes a difference but FWIW I am coding in SharpDevelop on a Windows 8.1 system.
Take a look at the Ping SendAsync, you may be able to eliminate most of your code - just call PingAsync, and handle the result being sure to dispatch it to the UI thread and then re-queue another call.
http://msdn.microsoft.com/en-us/library/ms144961(v=vs.110).aspx

How to coordinate 3 threads executing (UI done with DevExpress)

Here is my problem:
I have a Wcf service on a server that has two methods:
Synchronize() and GetExecutionState().
I also have a client that is to call those operations.
The Synchronize() executes lots of things and in several steps so at each step i update a local (inside the wcf) variable and i publish this variable via the GetExecutionState() operation The operation can fail, can take eons to execute and so on.
So, when i consume this service on the client, say on somebutton_click() event,
what i want to happen is this:
show infinite progressbar (main thread, UI);
start a thread to run Synchronize();
start another thread to keep reading GetExecutionState() every x minutes and in the event of a connection failure (which is the main reason i need this last thread) cancel the whole thing.
I dont know lot about threading, but so far i have implemented 1 and 2. Can someone help me with 3?
I am using devExpress and here is my relevant code.
SERVER PART:
public class SyncServerService :ISyncServer {
protected CsNo Node;
protected SyncState State;
public SyncServerService() {
State = SyncState.None;
Node = null;
}
public SyncState OperationState() {
return State;
}
public void PutComputerName(string value) {
var man = new CsNoManager();
Node = man.GetByMachineName(value);
}
public bool CanSync() {
var man = new ViewSyncLogManager();
var log = man.GetByMachineName(Node.MachineName);
return !log[0].IsInSync;
}
public CommandExecutionResponse Synchronize() {
CommandExecutionResponse res = null;
var logManager = new CsLogSyncManager();
var log = logManager.GetByNode(Node.IDNo);
State=SyncState.Syncing;
//step 1
State = SyncState.State2;
//...step n
State = SyncState.SomeOtherState;
//somewhere along the path create the res object
return res;
}
}
I read somewhere that with WCF, i can call operations both sync and async, so i dont think i have to mess with the server part regarding my requirements.
Client part:
On the button click that starts the process:
private void cmdSync_Click(object sender, EventArgs e) {
pgbSync.Properties.Stopped = false;
backgroundWorker1.RunWorkerAsync();
}
pgbSync is of type MarqueeProgressBar, a DevExpress progressbar that has infinite loop
backgroundWorker1 is of type System.ComponentModel.BackgroundWorker...supposedly runs tasks on the background.
It's start and finish methods are:
START:
private void StartSync(object sender, DoWorkEventArgs e) {
try {
//setting up wcf link properties
var manager = new CsConfiguracaoManager();
var address = manager.SyncAppServiceAddress();
var binding = new NetTcpBinding {
Security = new NetTcpSecurity() { Mode = SecurityMode.None },
CloseTimeout = new TimeSpan(0, 0, 30, 0, 0),
OpenTimeout = new TimeSpan(0, 0, 30, 0, 0),
ReceiveTimeout = new TimeSpan(0, 0, 30, 0, 0),
SendTimeout = new TimeSpan(0, 0, 30, 0, 0)
};
var factory = new ChannelFactory<ISyncServer>(binding, new EndpointAddress(address));
var proxy = factory.CreateChannel();
proxy.PutComputerName(PcName);
//checking if i can sync first
if (proxy.CanSync() == true) {
ExecutionResponse = proxy.Sync();
}
else {
//set up messages to show errors
}
}
catch (DataException dataErr) {
//set up appropriate messages
}
catch (EndpointNotFoundException err) {
//set up appropriate messages
}
catch (Exception masterErr) {
//set up appropriate messages
}
}
FINISH:
private void FinishSync(object sender, RunWorkerCompletedEventArgs e) {
pgbSync.Properties.Stopped = true;
//process ExecutionResponse object from FinishSync
}
This all runs, but if i disconnect the network after the process has started (we are anticipating lots of communication issues) the process will only throw an exception after the 30 minutes set in the service have passed.
Thats why i want to introduce a 3rd step where i check every x minutes for the output of OperationState.
If i cannot read it, i abort the operation, if it has a null or err state i also stop.
Any ideas?
I am note sure if you need a 3rd thread for this.
Have you tried System.Timers.Timer to have an event generated every x seconds and doing your checks?
If that event does not occur you can go with a BackgroundWorker which will have a loop with Thread.Sleep(250) and cancelation checks.

Windows Event Log

I am developing an app to capture event logs (security) from multiple Windows systems. I have a handler to EntryWritten. I am able to map most fields from the Event Viewer to the EntryWrittenEventArgs entry in .net. However, I cannot seem to find the mappings for the Level, OpCode and Task Category fields which show up in Event Viewer. Any ideas on how I get this in vb.net or c#? Thanks
The EventLog class in the System.Diagnostics namespace does not contain fields for Level, OpCode or Task. There is, however, the EventRecord class in the System.Diagnostics.Eventing.Reader namespace which is capable of returning those fields. Note that this namespace is mainly used for retrieving event logs from a remote machine. Even though you could use it to get logs on the local machine as well, it opens a local pipe to the system, which makes it slower than the EventLog class. If you really need to access those fields though, this is how this class is generally used:
private void LoadEventLogs()
{
List<EventRecord> eventLogs = new List<EventRecord>();
EventLogSession session = new EventLogSession();
foreach (string logName in session.GetLogNames())
{
EventLogQuery query = new EventLogQuery(logName, PathType.LogName);
query.TolerateQueryErrors = true;
query.Session = session;
EventLogWatcher logWatcher = new EventLogWatcher(query);
logWatcher.EventRecordWritten +=
new EventHandler<EventRecordWrittenEventArgs>(LogWatcher_EventRecordWritten);
try
{
logWatcher.Enabled = true;
}
catch (EventLogException) { }
// This is how you'd read the logs
//using (EventLogReader reader = new EventLogReader(query))
//{
// for (EventRecord eventInstance = reader.ReadEvent(); eventInstance != null; eventInstance = reader.ReadEvent())
// {
// eventLogs.Add(eventInstance);
// }
//}
}
}
And the LogWatcher_EventRecordWritten event handler:
private void LogWatcher_EventRecordWritten(object sender, EventRecordWrittenEventArgs e)
{
var level = e.EventRecord.Level;
var task = e.EventRecord.TaskDisplayName;
var opCode = e.EventRecord.OpcodeDisplayName;
// Other properties
}
Note that I wrapped the logWatcher.Enabled = true; statement in a try-catch block, because not all sources allow entry-written listeners (security should work fine). The commented-out section shows you an example of reading all the logs, if you need it.

How to end thread in win CF

Windows mobile 5; compact framework and relative newbie to c# and threads.
I want to download large files (several meg) from my own website; being GPRS this could take a while. I want to show a progress bar, and allow an option to cancel the download.
I've got a class called FileDownload and create an instance of it; give it a url and save location then:
MyFileDownLoader.Changed += new FileDownLoader.ChangedEventHandler(InvokeProgressBar);
BGDownload = new Thread(new ThreadStart(MyFileDownLoader.DownloadFile));
BGDownload.Start();
So I create an event handler for updates to progress bar and start the thread. This works fine.
I've got a cancel button which reads:
MyFileDownLoader.Changed -= InvokeProgressBar;
MyFileDownLoader.Cancel();
BGDownload.Join();
lblPercentage.Text = CurPercentage + " Cancelled"; // CurPercentage is a string
lblPercentage.Refresh();
btnUpdate.Enabled = true;
In the FileDownload class the key parts are:
public void Cancel()
{
CancelRequest = true;
}
In method Download file:
...
success = false;
try {
//loop until no data is returned
while ((bytesRead = responseStream.Read(buffer, 0, maxRead)) > 0)
{
_totalBytesRead += bytesRead;
BytesChanged(_totalBytesRead);
fileStream.Write(buffer, 0, bytesRead);
if (CancelRequest)
break;
}
if (!CancelRequest)
success = true;
}
catch
{
success = false;
// other error handling code
}
finally
{
if (null != responseStream)
responseStream.Close();
if (null != response)
response.Close();
if (null != fileStream)
fileStream.Close();
}
// if part of the file was written and the transfer failed, delete the partial file
if (!success && File.Exists(destination))
File.Delete(destination);
The code i'm using for the download is based on http://spitzkoff.com/craig/?p=24
The problem i've got is when I cancel, the download stops immediately, however it can take up to 5 seconds or so for the join process to complete. This is evidenced by lblPercentage.Text being updated after the join.
If I then try and download again, sometimes it works and sometimes I get a nullreference exception (still trying to track that down).
I think i'm doing something wrong in my approach to cancelling the thread.
Am i ?
public void Cancel()
{
CancelRequest = true;
}
I suppose you should add thread-safe to this action.
public void Cancel()
{
lock (this)
{
CancelRequest = true;
}
}
Hope this help!

Categories