/// <summary> /// Pass the 'inStream' through exiftool and write the output to the 'outStream'. /// </summary> private void WriteTemporaryFile(Stream file, Stream input) { // get a buffer var buffer = BufferCache.Get(); // read from the input stream int count = input.Read(buffer, 0, Global.BufferSizeLocal); // write the buffer to the file file.Write(buffer, 0, count); // reset the buffer BufferCache.Set(buffer); // is this the final buffer? if (count < Global.BufferSizeLocal) { // yes, dispose of the file stream file.Dispose(); var process = _coordinator.Process.TakeItem(); process.StandardInput.BaseStream.Write(_bytesRemoveMetadata, 0, _bytesRemoveMetadata.Length); byte[] pathBytes = System.Text.Encoding.ASCII.GetBytes(Path); process.StandardInput.BaseStream.Write(pathBytes, 0, pathBytes.Length); process.StandardInput.BaseStream.Write(_coordinator.BytesExecute, 0, _coordinator.BytesExecute.Length); process.StandardInput.BaseStream.Flush(); _coordinator.Process.Release(); // add this task to run with the coordinators process _coordinator.Add(this); } else { // continue writing the temporary file ManagerUpdate.Control.AddSingle(WriteTemporaryFile, file, input); } }
/// <summary> /// Send the specified string to the client. /// </summary> public unsafe void Send(string str, HttpSendOptions options = null, IAction onSent = null) { Log.Info("Sending string '" + str + "' to client " + this + "."); // is the string empty? skip if (string.IsNullOrEmpty(str)) { return; } _lock.Take(); // process send options ProcessOptions(options); _onSent = onSent; var buffer = BufferCache.Get(str.Length + str.Length); int count; // get pointers to the string and the send byte buffer fixed(char *src = str) fixed(byte *dst = &buffer[0]) { // get the bytes that the string represents count = _encoder.GetBytes(src, str.Length, dst, buffer.Length, true); } SendBytes(buffer, 0, count); BufferCache.Set(buffer); }
/// <summary> /// Creates a new AsyncSocket. You must call Start() after creating the AsyncSocket /// in order to begin receive data. /// </summary> internal AsyncUdpSocket(Socket socket, IPEndPoint localEndpoint, Action <IPEndPoint, byte[], int> onReceive, Action <Exception> onError) { Socket = socket; OnReceive = onReceive; OnError = onError; LocalEndPoint = localEndpoint; RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); TargetEndPoint = RemoteEndPoint; _sendLock = new Lock(); _readLock = new Lock(); _enqueueBuffer = new BufferQueue(); _sendQueue = new Shared <ArrayRig <QueuedBuffer> >(new ArrayRig <QueuedBuffer>()); _syncLock = new LockReadWrite(); _onReceive = new ActionSequence(); _receiveBuffer = BufferCache.Get(); _receivedChunks = new Dictionary <int, ChunkedGram>(); _disposing = false; }
//----------------------------------// /// <summary> /// Get a byte collection of the specified string representation of a /// hexidecimal number. /// </summary> public static byte[] ToBytes(this string str) { // is the string the correct length? if (str.Length == 0 || str.Length % 2 != 0) { return(new byte[0]); } byte[] buffer = BufferCache.Get(); int length = str.Length / 2; char c; for (int bx = 0, sx = 0; bx < length; ++bx, ++sx) { // convert first half of byte c = str[sx]; buffer[bx] = (byte)((c > Chars.n9 ? (c > Chars.Z ? (c - Chars.a + 10) : (c - Chars.A + 10)) : (c - Chars.n0)) << 4); // convert second half of byte c = str[++sx]; buffer[bx] |= (byte)(c > Chars.n9 ? (c > Chars.Z ? (c - Chars.a + 10) : (c - Chars.A + 10)) : (c - Chars.n0)); } return(buffer); }
/// <summary> /// Allocate an event arg for a receive operation /// </summary> public static SocketAsyncEventArgs AllocateForReceive(EventHandler <SocketAsyncEventArgs> ioCompletedHandler) { SocketAsyncEventArgs result; if (!_eventArgsReceive.Dequeue(out result)) { result = new SocketAsyncEventArgs(); result.SetBuffer(BufferCache.Get(), 0, Global.BufferSizeLocal); } result.Completed += ioCompletedHandler; return(result); }
/// <summary> /// Prepare the specified number of bytes. This ensures that at least the specified /// number of bytes are sent to the 'Next' method each call. /// </summary> protected void BufferCount(int length) { if (length == 0) { _buffer = false; } else { _buffer = true; _bufferTarget = length; if (_bufferBytes == null || _bufferBytes.Length < _bufferTarget) { _bufferBytes = BufferCache.Get(length); } } }
/// <summary> /// Append the specified bytes to the next read. /// </summary> protected void Buffer(byte[] bytes, int offset, int length) { _buffer = true; // does the buffer have room for the specified bytes? if (length > _bufferTarget - _bufferIndex) { // no, resize the prepared byte array _bufferBytes = BufferCache.Get(_bufferTarget + length); } // copy the byte buffer into the prepared collection Micron.CopyMemory(bytes, offset, _bufferBytes, _bufferIndex, length); // increment the index _bufferIndex += length; }
/// <summary> /// Send the specified element to the client. /// </summary> public unsafe void Send(Element element, bool buildStyle = true, HttpSendOptions options = null, IAction onSent = null) { Log.Info("Sending element '" + element + "' to client " + this + "."); if (buildStyle) { // build the style var style = element.FindChild("style"); style.ContentString = element.BuildCss(); element.EncodeContent = false; } // build the element into a string string str = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" + element.Build(); _lock.Take(); // have the options been specified? yes, process them if (options == null || options.ContentType == null) { Headers[HttpResponseHeader.ContentType] = "text/html; charset=UTF-8"; } ProcessOptions(options); _onSent = onSent; // resize buffer as required var buffer = BufferCache.Get(str.Length + str.Length); // get pointers to the string and the send byte buffer int count; fixed(char *src = str) fixed(byte *dst = &buffer[0]) { // get the bytes that the string represents count = _encoder.GetBytes(src, str.Length, dst, buffer.Length, true); } // send the bytes SendBytes(buffer, 0, count); BufferCache.Set(buffer); }
/// <summary> /// Buffer processed data and send to the receive callback. /// </summary> private void ProcessReceivedData() { // get a buffer to dequeue into var buffer = BufferCache.Get(); try { // dequeue a buffer from the pending received data int count = _receivedBuffer.TakeItem().Dequeue(buffer, 0, Global.BufferSizeLocal); _receivedBuffer.Release(); if (count == 0) { // allocate the buffer cache once more BufferCache.Set(buffer); } else { // remove //Log.Debug(new string(System.Text.Encoding.UTF8.GetChars(buffer, 0, count))); // run the callback action _onReceive.AddRun(ActionSet.New(OnReceive, buffer, count)); if (count == Global.BufferSizeLocal) { ManagerUpdate.Control.AddSingle(ProcessReceivedData); return; } } // no bytes to receive CompleteReceive(); } catch (SocketException ex) { _readLock.Release(); BufferCache.Set(buffer); if (ex.SocketErrorCode == SocketError.ConnectionReset) { return; } ProcessError(ex); } catch (Exception ex) { _readLock.Release(); BufferCache.Set(buffer); ProcessError(ex); } }
/// <summary> /// Send the specified string to the client. /// </summary> public unsafe void Send(string str, TransportOptions options = null, IAction onSent = null) { // is disposed? if (_disposed) { Log.Warning("Cannot send from disposed connection."); return; } // reset the timeout if (_timeoutTimer != null) { _timeoutTimer.Reset(_timeoutMilliseconds); } // is the string empty? skip if (string.IsNullOrEmpty(str)) { return; } _lock.Take(); // process send options ProcessOptions(options); _onSent = onSent; var buffer = BufferCache.Get(str.Length + str.Length); int count; // get pointers to the string and the send byte buffer fixed(char *src = str) fixed(byte *dst = &buffer[0]) { // get the bytes that the string represents count = _encoder.GetBytes(src, str.Length, dst, buffer.Length, true); } // send the byte buffer SendBytes(buffer, 0, count); // cache the buffer BufferCache.Set(buffer); }
/// <summary> /// Send a buffer of bytes to the client. Releases the lock. /// </summary> protected void SendBytes(byte[] bytes, int offset, int count) { try { // yes, does the buffer need to be compressed? if (_header.Compression != DecompressionMethods.None) { // yes, compress the bytes bytes = StreamHelper.Compress(_header.Compression, System.IO.Compression.CompressionLevel.Optimal, bytes, offset, ref count); offset = 0; } // are the bytes to be encrypted? if (_header.EncryptionPassword != null) { // yes, encrypt them byte[] buffer = BufferCache.Get(count); Buffer.BlockCopy(bytes, offset, buffer, 0, count); bytes = Crypto.EncryptWithPassword(buffer, _header.EncryptionPassword); // send headers with the content length EnqueueHeader(bytes.Length); // send the bytes AsyncSocket.Enqueue(bytes, 0, bytes.Length); AsyncSocket.Send(_onSent); } else { // send headers with the content length EnqueueHeader(count); // send the bytes AsyncSocket.Enqueue(bytes, offset, count); AsyncSocket.Send(_onSent); } } finally { _onSent = null; _lock.Release(); } }
/// <summary> /// Initialize a file extractor with a collection of file paths, the extraction instance and the /// encoding of the file. /// </summary> public FileExtractor(ArrayRig <string> files, Extract extractor = null, Encoding encoding = null) { _files = new Queue <string>(); // add the files to the queue foreach (string file in files) { _files.Enqueue(file); } // persist the extractor Extractor = extractor; Decoder = (encoding ?? Encoding.UTF8).GetDecoder(); // initialize the buffers _buffer = BufferCache.Get(); _chars = new char[(encoding ?? Encoding.UTF8).GetMaxCharCount(Global.BufferSizeLocal)]; _timeout = new Timer(Timeout, Run, false); _lock = new Lock(); }
/// <summary> /// Pass the 'inStream' through exiftool and write the output to the 'outStream'. /// </summary> private void ReadTemporaryFile(Stream file) { // get a buffer byte[] buffer = BufferCache.Get(); // read from the stream int count = file.Read(buffer, 0, Global.BufferSizeLocal); // write the buffer to the output stream Output.Write(buffer, 0, count); // is this the final buffer? if (count < Global.BufferSizeLocal) { // yes, dispose of the file stream file.Dispose(); // reset the buffer BufferCache.Set(buffer); Success = true; // run callback OnComplete.Run(); // should the file be removed? if (_removeTempFile) { ManagerResources.RemoveFile(Path); } } else { // no, reset the buffer BufferCache.Set(buffer); // add a task to clear the metadata ManagerUpdate.Control.AddSingle(ReadTemporaryFile, file); } }
//----------------------------------// /// <summary> /// Initialize a new buffer queue. /// </summary> public BufferQueue() { _buffer = BufferCache.Get(); }
//-------------------------------------------// /// <summary> /// Start sending data to the socket. Make sure slimLock is entered in read mode before calling this, /// and that disposing is not true. Ensure an error handler is implemented which forwards to ProcessError. /// </summary> private void StartSend() { if (_disposing) { _sendLock.Release(); return; } byte[] buffer = BufferCache.Get(); // iterate while there are more bytes to be sent for (;;) { // get data without removing it from the buffer and set buffer on socket int count; _sendBuffer.Take(); if (_currentCallbackAction == null) { count = _sendBuffer.Item.Dequeue(buffer, 0, Global.BufferSizeLocal); if (count == 0) { _sendLock.Release(); // more data to send? yes, take the lock and return positive if (_sendBuffer.Item.Length > 0 && _sendLock.TryTake) { count = _sendBuffer.Item.Dequeue(buffer, 0, Global.BufferSizeLocal); } else { _sendBuffer.Release(); BufferCache.Set(buffer); return; } } } else { _callbackLock.Take(); count = _sendBuffer.Item.Dequeue(buffer, 0, Global.BufferSizeLocal); if (count == 0) { _sendLock.Release(); // more data to send? yes, take the lock and return positive if (_sendBuffer.Item.Length > 0 && _sendLock.TryTake) { count = _sendBuffer.Item.Dequeue(buffer, 0, Global.BufferSizeLocal); } else { _sendBuffer.Release(); _callbackLock.Release(); BufferCache.Set(buffer); return; } } _currentCallbackIndex -= count; _callbackLock.Release(); } _sendBuffer.Release(); _sendSocketArgs.SetBuffer(buffer, 0, count); if (_disposing) { _sendLock.Release(); return; } if (Socket.Connected) { // sendAsync returns true if the I/O operation is pending. An event will be raised upon completion. // returns false if the I/O operation completed synchronously if (Socket.SendAsync(_sendSocketArgs)) { // an event is going to be raised to OnIOComplete_Send, so let's exit right now because we're done return; } // send -- is there more data to send now? no, break iteration if (!CompleteSend(_sendSocketArgs)) { return; } } else { _sendLock.Release(); BufferCache.Set(buffer); if (_disposing) { return; } ProcessError("Socket was disconnected mid-send."); return; } } // in case loop is ever broken //_syncLock.ExitReadLock(); }
/// <summary> /// On socket data being received. /// </summary> private unsafe void OnReceiveSocketData(IAsyncResult ar) { int count = 0; try { // end the receive count = Socket.EndReceiveFrom(ar, ref _receiveEndPoint); } catch (Exception ex) { // release the read lock _syncLock.ReleaseRead(); ProcessError(ex); return; } if (count > 0) { // is the received buffer chunked? byte flag = _receiveBuffer[0]; if (flag < 60 || flag >= 188) { count -= ChunkHeaderSize; int chunkId; int chunkIndex; int chunkCount; // sanity check for correct number of bytes received if (count < 0) { _syncLock.ReleaseRead(); ProcessError("Socket didn't receive enough data for chunked information."); return; } // yes, read the chunk details fixed(byte *intP = &_receiveBuffer[1]) { chunkId = *(int *)intP; } fixed(byte *intP = &_receiveBuffer[5]) { chunkIndex = *(int *)intP; } fixed(byte *intP = &_receiveBuffer[9]) { chunkCount = *(int *)intP; } // sanity check for the chunk data being valid if (chunkIndex >= chunkCount) { _syncLock.ReleaseRead(); ProcessError("Socket received invalid chunk index and count information."); return; } // write ChunkedGram chunkedGram; if (_receivedChunks.TryGetValue(chunkId, out chunkedGram)) { chunkedGram.Length += count; chunkedGram.Chunks.Insert(Teple.New(count, _receiveBuffer), chunkIndex); // have all chunks been added? if (chunkedGram.Chunks.Count == chunkCount) { // yes, remove from the collection _receivedChunks.Remove(chunkId); // create a byte buffer for the entire message byte[] result = new byte[chunkedGram.Length]; int index = 0; foreach (var chunk in chunkedGram.Chunks) { int length = chunk.ArgA; Micron.CopyMemory(chunk.ArgB, ChunkHeaderSize, result, index, length); index += length; } // reference the endpoint from which the data was received IPEndPoint endpoint = (IPEndPoint)_receiveEndPoint; // run the callback _onReceive.AddRun(ActionSet.New(OnReceive, endpoint, result, chunkedGram.Length)); } else { // no, create a new receive buffer _receiveBuffer = BufferCache.Get(); } } else { chunkedGram = new ChunkedGram { Chunks = new ArrayRig <Teple <int, byte[]> >(chunkCount), Timestamp = Time.Timestamp }; _receivedChunks.Add(chunkId, chunkedGram); chunkedGram.Chunks.Add(Teple.New(count, _receiveBuffer)); chunkedGram.Length += count; // create a new receive buffer _receiveBuffer = BufferCache.Get(); } } else { // no, copy the received buffer --count; byte[] buffer = BufferCache.Get(count); Micron.CopyMemory(_receiveBuffer, 1, buffer, 0, count); // reference the endpoint from which the data was received IPEndPoint endpoint = (IPEndPoint)_receiveEndPoint; // run the callback _onReceive.AddRun(ActionSet.New(OnReceive, endpoint, buffer, count)); } } if (_receivedChunks.Count > 0) { // check for any chunked data timeouts ArrayRig <int> toRemove = null; foreach (var chunkedGram in _receivedChunks) { if (Time.Timestamp - chunkedGram.Value.Timestamp > ChunkedGramTimeout) { if (toRemove == null) { toRemove = new ArrayRig <int>(); } toRemove.Add(chunkedGram.Key); } } if (toRemove != null) { foreach (var chunkId in toRemove) { ChunkedGram chunked; if (_receivedChunks.TryGetValue(chunkId, out chunked)) { _receivedChunks.Remove(chunkId); chunked.Chunks.Dispose(); } } } } // release the read lock _syncLock.ReleaseRead(); // create the endpoint for receiving data _receiveEndPoint = new IPEndPoint(RemoteEndPoint.Address, RemoteEndPoint.Port); try { // start receiving again Socket.BeginReceiveFrom(_receiveBuffer, 0, Global.BufferSizeLocal, SocketFlags.None, ref _receiveEndPoint, OnReceiveSocketData, null); } catch (Exception ex) { ProcessError(ex); return; } }
//-------------------------------------------// /// <summary> /// Start sending data to the socket. Make sure slimLock is entered in read mode before calling this, /// and that disposing is not true. Ensure an error handler is implemented which forwards to ProcessError. /// </summary> private void StartSend() { if (_disposing) { _sendLock.Release(); return; } // get a buffer used to send data byte[] buffer = BufferCache.Get(MaxDatagramSize); // iterate while there are more bytes to be sent for (;;) { // get data without removing it from the buffer and set buffer on socket int count = TryGetBuffer(buffer); // any bytes in the buffer? if (count == 0) { _sendLock.Release(); // more data to send? yes, take the lock and return positive if (_enqueueBuffer.Length == 0 || !_sendLock.TryTake || (count = TryGetBuffer(buffer)) == 0) { BufferCache.Set(buffer); return; } } //Log.Debug("Sending buffer '"+count+"' : " + buffer.ToString(0, count, ',')); var args = new SocketAsyncEventArgs(); args.Completed += OnSocketSend; args.SendPacketsFlags = TransmitFileOptions.UseSystemThread; // set the remote endpoint of the data to be sent args.RemoteEndPoint = TargetEndPoint; // set the data args.SetBuffer(buffer, 0, count); if (_disposing) { _sendLock.Release(); args.Dispose(); return; } if (Socket.SendToAsync(args)) { // an event is going to be raised to OnIOComplete_Send, so let's exit right now because we're done return; } // send -- is there more data to send now? no, break iteration if (!CompleteSend(args)) { return; } } // in case loop is ever broken //_syncLock.ExitReadLock(); }
//----------------------------------// protected Interpret() { _bufferCapacity = Global.BufferSizeLocal; _bufferBytes = BufferCache.Get(_bufferCapacity); }
/// <summary> /// Run the medadata removal process. /// </summary> public void Run() { var stream = new ByteBuffer(new MemoryStream()); bool found = false; if (_coordinator.Buffer != null) { while (_coordinator.BufferIndex < _coordinator.BufferCount) { var bit = _coordinator.Buffer[_coordinator.BufferIndex]; stream.Write(bit); ++_coordinator.BufferIndex; if (_coordinator.ReadySearch.Next(bit)) { found = true; break; } } if (_coordinator.BufferIndex == _coordinator.BufferCount) { _coordinator.Buffer = null; } } if (!found) { // read the {ready} flag from the exiftool var buffer = BufferCache.Get(); var process = _coordinator.Process.TakeItem(); int count = process.StandardOutput.BaseStream.Read(buffer, 0, Global.BufferSizeLocal); // while there are bytes in the process standard output while (count > 0) { int index = 0; while (index < count) { // check the buffer for the {ready} flag var bit = buffer[index]; stream.Write(bit); ++index; if (_coordinator.ReadySearch.Next(bit)) { // the flag has been found - are there bytes in the buffer? if (index == count) { BufferCache.Set(buffer); } else { // yes, remember the buffer _coordinator.Buffer = buffer; _coordinator.BufferIndex = index; _coordinator.BufferCount = count; } found = true; break; } } if (found) { break; } count = process.StandardOutput.BaseStream.Read(buffer, 0, Global.BufferSizeLocal); } _coordinator.Process.Release(); } // should the file be removed? if (_removeTempFile) { ManagerResources.RemoveFile(Path); } // reset the stream position stream.Position = 0; // create the metadata dictionary for the callback Metadata = new Dictionary <MetaKey, string>(); // while there are more bytes to read while (stream.Position < stream.WriteEnd) { string key; try { // read the key of the key value pair key = stream.ReadString(Chars.Colon); } catch { // ignore and break break; } // trim the key key = key.TrimSpace(); // no, read the value of the key-value pair var value = stream.ReadString(Chars.NewLine).TrimSpace(); if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) { Log.Debug("Missing metadata '" + key + "' : '" + value + "'."); continue; } // parse the key and assign key-value pair MetaKey metaKey; if (MetaKeys.Map.TryGetValue(key, out metaKey)) { // determine how to parse the metadata value switch (metaKey) { case MetaKey.FileSize: { // determine the scale of the file size var splitFileSize = value.Split(value.IndexOf(Chars.Space)); switch (splitFileSize.ArgB) { case "bytes": Metadata[metaKey] = splitFileSize.ArgA; break; case "MB": Metadata[metaKey] = ((int)(splitFileSize.ArgA.ToDouble() * Global.Megabyte)).ToString(); break; case "kB": Metadata[metaKey] = ((int)(splitFileSize.ArgA.ToDouble() * Global.Kilobyte)).ToString(); break; case "GB": Metadata[metaKey] = ((int)(splitFileSize.ArgA.ToDouble() * Global.Gigabyte)).ToString(); break; default: Log.Error("Unknown file size scale of media '" + value + "'."); break; } } break; case MetaKey.Duration: { // check for (approx) suffix int index = value.IndexOf(Chars.Space); if (index != -1) { value = value.Substring(0, index); } var splitDuration = value.Split(Chars.Colon); // determine the type of duration if (splitDuration.Length == 1) { Metadata[metaKey] = ((int)(splitDuration[0].ToDouble() * 1000)).ToString(); } else { // parse each component int time = splitDuration[0].ToInt32() * 60 * 60 * 1000; time += splitDuration[1].ToInt32() * 60 * 1000; time += splitDuration[2].ToInt32() * 1000; // assign the time value Metadata[metaKey] = time.ToString(); } } break; case MetaKey.Bitrate: { // determine the scale of the file size var splitBitrate = value.Split(value.IndexOf(Chars.Space)); switch (splitBitrate.ArgB) { case "bps": Metadata[metaKey] = splitBitrate.ArgA; break; case "kbps": Metadata[metaKey] = ((int)(splitBitrate.ArgA.ToDouble() * Global.Kilobyte)).ToString(); break; case "Mbps": Metadata[metaKey] = ((int)(splitBitrate.ArgA.ToDouble() * Global.Megabyte)).ToString(); break; default: Log.Warning("Unknown bitrate scale of media '" + value + "'."); break; } } break; default: // assign the mime type directly Metadata[metaKey] = value; break; } } else { Log.Warning("Unrecognized metadata key '" + key + " : " + value + "'."); } } // dispose of the byte stream stream.Close(); // run callback OnComplete.Run(); }
/// <summary> /// Run the medadata removal process. /// </summary> public void Run() { bool found = false; if (_coordinator.Buffer != null) { while (_coordinator.BufferIndex < _coordinator.BufferCount) { if (_coordinator.ReadySearch.Next(_coordinator.Buffer[_coordinator.BufferIndex])) { ++_coordinator.BufferIndex; found = true; break; } ++_coordinator.BufferIndex; } if (_coordinator.BufferIndex == _coordinator.BufferCount) { _coordinator.Buffer = null; } } if (!found) { // read the {ready} flag from the exiftool var buffer = BufferCache.Get(); var process = _coordinator.Process.TakeItem(); int count = process.StandardOutput.BaseStream.Read(buffer, 0, Global.BufferSizeLocal); // while there are bytes in the process standard output while (count > 0) { int index = 0; while (index < count) { // check the buffer for the {ready} flag if (_coordinator.ReadySearch.Next(buffer[index])) { ++index; // are there bytes in the buffer? if (index == count) { // no, persist the buffer BufferCache.Set(buffer); } else { // yes, remember the buffer and position _coordinator.Buffer = buffer; _coordinator.BufferIndex = index; _coordinator.BufferCount = count; } found = true; break; } ++index; } if (found) { break; } count = process.StandardOutput.BaseStream.Read(buffer, 0, Global.BufferSizeLocal); } _coordinator.Process.Release(); } // should the file be read? if (Output == null) { // no, run callback OnComplete.Run(); // should the file be removed? if (_removeTempFile) { ManagerResources.RemoveFile(Path); } return; } FileStream fileStream; try { fileStream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (IOException ex) { // if the file is still being used if (ex.HResult == -2147024864) { // try twice more ManagerUpdate.Iterant.AddSingle(TryReadTemporaryFile, 0); return; } throw; } // get a stream to the clean file ManagerUpdate.Control.AddSingle(ReadTemporaryFile, fileStream); }