WSE 2.0 Progress Bars That Work
Ite's been a while since I posted some amazingly useful, but insanely impossible to find code for you. But, I am still alive and kicking and working on all sorts of interesting projects, so it was only a matter of time until I ran into another Microsoft oversight that needed a workaround. If you have ever tried to implement a progress bar with WSE 2.0 in your application, it probably wasn't a fun experience. In fact, if you were using something like DIME, you probably gave up. Yes, theoretically, there are ways to use the DimeReader / DimeWriter to do the task, but that requires a lot of extra work and doesn't fit nearly as nice and neat into the framework as the auto-generated WSE classes that you are used to. Personally, I like using the WSE auto-generated code, but I was amazed to discover that there is absolutely no way built-in for you to obtain progress information while a large DIME message is being streamed to the server. What is worse is that due to the way that WSE was built, you cannot even plug into WSE to provide this functionality yourself... well, at least that is what everyone was saying. But, as is often said, "Where there is a will, there is a way." Today, ladies and gentlemen, I present you with the way.
1) Derive a class from the WSE generated stub and insert the following code:
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
SoapWebRequest wr = (SoapWebRequest)base.GetWebRequest(uri);
Type t = wr.GetType();
FieldInfo fi = t.GetField("_request", BindingFlags.Instance | BindingFlags.NonPublic);
HttpWebRequest h = (HttpWebRequest)WebRequest.Create(uri);
CustomWebRequest cwr = new CustomWebRequest(h);
cwr.StreamAcquired+=new StreamHandler(cwr_StreamAcquired);
fi.SetValue(wr, cwr); return wr;
}
public event StreamHandler StreamAcquired;
private void cwr_StreamAcquired(object sender, Stream s)2) Add the following class to your solution:
{
if(StreamAcquired != null)
{
StreamAcquired(sender, s);
}
}
public delegate void StreamHandler(object sender, Stream s);
public class CustomWebRequest : WebRequest
{
public CustomWebRequest(WebRequest wrapped)
{
_wrapped = wrapped;
}
WebRequest _wrapped;
public override void Abort()
{
_wrapped.Abort ();
}
public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state) { return _wrapped.BeginGetResponse (callback, state); }
public override string ConnectionGroupName { get { return _wrapped.ConnectionGroupName; } set { _wrapped.ConnectionGroupName = value; } }
public override string ContentType { get { return _wrapped.ContentType; } set { _wrapped.ContentType = value; } }
public override long ContentLength { get { return _wrapped.ContentLength; } set { _wrapped.ContentLength = value; } }
public override IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state) { return _wrapped.BeginGetRequestStream (callback, state); }
public override ICredentials Credentials { get { return _wrapped.Credentials; } set { _wrapped.Credentials = value; } }
public override Stream EndGetRequestStream(IAsyncResult asyncResult) { return _wrapped.EndGetRequestStream (asyncResult); }
public override WebResponse EndGetResponse(IAsyncResult asyncResult) { return _wrapped.EndGetResponse (asyncResult); }
MemoryStream _dummyStream;
public override Stream GetRequestStream()
{
_dummyStream = new MemoryStream();
return _dummyStream;
}
int _bytesWritten = 0;
public int BytesWritten
{
get { return _bytesWritten; }
}
int _totalBytes = 0;
public int TotalBytes { get { return _totalBytes; } }
public override WebResponse GetResponse()
{
byte[] data = _dummyStream.ToArray();
((HttpWebRequest)_wrapped).AllowWriteStreamBuffering = false;
((HttpWebRequest)_wrapped).ContentLength = data.Length;
Stream s = _wrapped.GetRequestStream ();
try
{
if(StreamAcquired != null) StreamAcquired(this, s);
int chunkSize = 4092;
_totalBytes = data.Length;
for(int i = 0; i < data.Length; )
{
int len = Math.Min(chunkSize, data.Length-(i));
s.Write(data, i, len);
i+=len; _bytesWritten+=len;
}
return _wrapped.GetResponse ();
}
finally
{
s.Close();
}
}
public override WebHeaderCollection Headers { get { return _wrapped.Headers; } set { _wrapped.Headers = value; } }
public override string Method { get { return _wrapped.Method; } set { _wrapped.Method = value; } }
public override bool PreAuthenticate { get { return _wrapped.PreAuthenticate; } set { _wrapped.PreAuthenticate = value; } }
public override IWebProxy Proxy { get { return _wrapped.Proxy; } set { _wrapped.Proxy = value; } }
public override Uri RequestUri { get { return _wrapped.RequestUri; } }
public override int Timeout { get { return _wrapped.Timeout; } set { _wrapped.Timeout = value; } }
public event StreamHandler StreamAcquired;
private void cwr_StreamAcquired(object sender, Stream s)
{
if(StreamAcquired != null)
{
StreamAcquired(sender, s);
}
}
}
3) In your calling form / dialog / whatever, attach to the StreamAcquired member of your new class before calling. The sender will contain your "CustomWebRequest" object, which will provide you with progress while the request is being sent to the server. Add some thready goodness and you will have nice updating progress bars to keep your users entertained while they wait.