Переписываю библиотеку скачивалки. Для форума код немного сократил, но суть такая:
public int ContentToStream(Stream stream, int bufferSize,
IProgress<long> progressReporter, CancellationToken cancellationToken)
{
long bytesTransfered = 0L;
byte[] buf = new byte[bufferSize];
do
{
int bytesRead = ContentData.Read(buf, 0, buf.Length);
if (bytesRead <= 0)
{
break;
}
stream.Write(buf, 0, bytesRead);
bytesTransfered += bytesRead;
progressReporter?.Report(bytesTransfered);
}
while (!cancellationToken.IsCancellationRequested);
if (cancellationToken.IsCancellationRequested)
{
return FileDownloader.DOWNLOAD_ERROR_CANCELED_BY_USER;
}
else if (ContentLength >= 0L && bytesTransfered != ContentLength)
{
return FileDownloader.DOWNLOAD_ERROR_INCOMPLETE_DATA_READ;
}
return 200;
}
public sealed class FileDownloader
{
public string Url { get; set; }
public const int DOWNLOAD_ERROR_URL_NOT_DEFINED = -1;
public const int DOWNLOAD_ERROR_INVALID_URL = -2;
public const int DOWNLOAD_ERROR_CANCELED_BY_USER = -3;
public const int DOWNLOAD_ERROR_INCOMPLETE_DATA_READ = -4;
public const int DOWNLOAD_ERROR_RANGE = -5;
public const int DOWNLOAD_ERROR_ZERO_LENGTH_CONTENT = -6;
public const int DOWNLOAD_ERROR_INSUFFICIENT_DISK_SPACE = -7;
public const int DOWNLOAD_ERROR_DRIVE_NOT_READY = -8;
public const int DOWNLOAD_ERROR_NULL_CONTENT = -9;
public delegate void ConnectingDelegate(object sender, string url);
public delegate void ConnectedDelegate(object sender, string url, HttpRequestResult requestResult, ref int errorCode);
public delegate void WorkStartedDelegate(object sender, long contentLength);
public delegate void WorkProgressDelegate(object sender, long bytesTransfered, long contentLength);
public delegate void WorkFinishedDelegate(object sender, long bytesTransfered, long contentLength, int errorCode);
public ConnectingDelegate Connecting;
public ConnectedDelegate Connected;
public WorkStartedDelegate WorkStarted;
public WorkProgressDelegate WorkProgress;
public WorkFinishedDelegate WorkFinished;
public int Download(Stream stream)
{
Connecting?.Invoke(this, Url);
HttpRequestResult requestResult = HttpRequestSender.Send("GET", Url, null, Headers);
long size = requestResult.WebContent.ContentLength;
WorkStarted?.Invoke(this, size);
Progress<long> progress = new Progress<long>();
progress.ProgressChanged += (s, n) =>
{
WorkProgress?.Invoke(this, n, size);
};
IProgress<long> reporter = progress;
try
{
LastErrorCode = requestResult.WebContent.ContentToStream(
stream, 99999, reporter, default);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
LastErrorMessage = ex.Message;
LastErrorCode = ex.HResult;
return LastErrorCode;
}
requestResult.Dispose();
WorkFinished?.Invoke(this, _bytesTransfered, size, LastErrorCode);
return LastErrorCode;
}
downloader.WorkProgress += (s, bytesTransfered, contentLength) =>
{
if (contentLength > 0L)
{
double percent = 100.0 / contentLength * bytesTransfered;
progressBar1.Value = (int)percent;
string percentFormatted = string.Format("{0:F3}", percent);
lblDownloadingProgress.Text = $"Скачано {bytesTransfered} из {contentLength} ({percentFormatted}%)";
}
else
{
lblDownloadingProgress.Text = $"Скачано {bytesTransfered} из <Неизвестно>";
}
Application.DoEvents();
};
Если использую IProgress
, то на строчке lblDownloadingProgress.Text = $"Скачано {bytesTransfered} из {contentLength} ({percentFormatted}%)";
через какое-то время возникает StackOverflow
. При этом, данные на форме вообще не обновляются, но файл скачивается правильно. Если поставить точку останова на строчку Application.DoEvents()
, то дебаггер падает на неё только через какое-то время, а не сразу. И после этого через несколько итераций StackOverflow
. Всё это выполняется в одном потоке с формой.
Наверное, нифига не понятно. Задавайте вопросы