clsAsyncUnbuffWriter.vb 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. Imports System.IO
  2. Imports System.Runtime.InteropServices
  3. Public Class clsAsyncUnbuffWriter
  4. '''' We need the page size for best performance - so we use GetSystemInfo and dwPageSize
  5. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  6. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  7. Public Class clsSystemInfo
  8. private Class WinApi
  9. <DllImport("kernel32.dll")> _
  10. Public Shared Sub GetSystemInfo(<MarshalAs(UnmanagedType.Struct)> ByRef lpSystemInfo As SYSTEM_INFO)
  11. End Sub
  12. <StructLayout(LayoutKind.Sequential)> _
  13. Public Structure SYSTEM_INFO
  14. Friend uProcessorInfo As _PROCESSOR_INFO_UNION
  15. Public dwPageSize As UInteger
  16. Public lpMinimumApplicationAddress As IntPtr
  17. Public lpMaximumApplicationAddress As IntPtr
  18. Public dwActiveProcessorMask As IntPtr
  19. Public dwNumberOfProcessors As UInteger
  20. Public dwProcessorType As UInteger
  21. Public dwAllocationGranularity As UInteger
  22. Public dwProcessorLevel As UShort
  23. Public dwProcessorRevision As UShort
  24. End Structure
  25. <StructLayout(LayoutKind.Explicit)> _
  26. Public Structure _PROCESSOR_INFO_UNION
  27. <FieldOffset(0)> _
  28. Friend dwOemId As UInteger
  29. <FieldOffset(0)> _
  30. Friend wProcessorArchitecture As UShort
  31. <FieldOffset(2)> _
  32. Friend wReserved As UShort
  33. End Structure
  34. End Class
  35. Public Shared Function GetPageSize() As Integer
  36. Dim sysinfo As New WinApi.SYSTEM_INFO()
  37. WinApi.GetSystemInfo(sysinfo)
  38. Return CInt(sysinfo.dwPageSize)
  39. End Function
  40. End Class
  41. Private target As FileStream
  42. Private inputBuffer As MemoryStream
  43. Private bufferSize As Integer
  44. Private running As Boolean
  45. Private writing As Boolean
  46. Private readWait As Threading.ManualResetEvent
  47. Private writeWait As Threading.ManualResetEvent
  48. Private finishedWriting As Threading.ManualResetEvent
  49. Private totalWritten As Int64
  50. Private writeTimer As Stopwatch
  51. Public Function GetTotalBytesWritten() As Int64
  52. Return totalWritten
  53. End Function
  54. Public Function IsRunning() As Boolean
  55. Return running
  56. End Function
  57. Public Sub Close()
  58. writing = False
  59. writeWait .Set
  60. finishedWriting.WaitOne
  61. readWait .Set
  62. End Sub
  63. Public Function GetActiveMiliseconds() As Int64
  64. Try
  65. Return writeTimer.ElapsedMilliseconds
  66. Catch ex As Exception
  67. Return 0
  68. End Try
  69. End Function
  70. Public Shared Function GetPageSize() As Integer
  71. Return clsSystemInfo.GetPageSize
  72. End Function
  73. Public Sub new(ByVal dest As String, _
  74. Optional ByVal unbuffered As Boolean = False, _
  75. Optional ByVal _bufferSize As Integer = (1024 * 1024), _
  76. Optional ByVal setLength As Int64 = 0)
  77. bufferSize = _bufferSize
  78. Dim options As FileOptions = FileOptions.SequentialScan
  79. If unbuffered then options = FileOptions.WriteThrough or FileOptions.SequentialScan
  80. readWait = New Threading.ManualResetEvent(False)
  81. writeWait = New Threading.ManualResetEvent(False)
  82. finishedWriting = New Threading.ManualResetEvent(False)
  83. readWait .Set
  84. writeWait .Reset
  85. finishedWriting .Reset
  86. target = New FileStream(dest, _
  87. FileMode.Create, FileAccess.Write, FileShare.None, GetPageSize, options)
  88. If setLength > 0 then target.SetLength(setLength)
  89. totalWritten = 0
  90. inputBuffer = New MemoryStream(bufferSize)
  91. running = True
  92. writing = True
  93. writeTimer = New Stopwatch
  94. Dim asyncWriter As New Threading.Thread(AddressOf WriteThread)
  95. With asyncWriter
  96. .Priority = Threading.ThreadPriority.Lowest
  97. .IsBackground = True
  98. .Name = "AsyncCopy writer"
  99. .Start()
  100. End With
  101. End Sub
  102. Public Function Write(ByVal someBytes() As Byte, numToWrite As Integer) As Boolean
  103. If Not running then Return False
  104. If numToWrite < 1 then Return False
  105. If numToWrite > inputBuffer.Capacity then
  106. Throw New Exception("clsAsyncUnbuffWriter: someBytes() can not be larger then buffer capacity")
  107. End If
  108. If (inputBuffer.Length + numToWrite) > inputBuffer.Capacity then
  109. If inputBuffer.Length > 0 then
  110. readWait .Reset
  111. writeWait .Set
  112. readWait .WaitOne
  113. If Not running then Return False
  114. inputBuffer.Write(someBytes, 0, numToWrite)
  115. End If
  116. Else
  117. inputBuffer.Write(someBytes, 0, numToWrite)
  118. End If
  119. Return True
  120. End Function
  121. Private Sub WriteThread()
  122. Dim bytesThisTime As Int32 = 0
  123. Dim internalBuffer(bufferSize) As byte
  124. writeTimer .Stop
  125. writeTimer .Reset
  126. writeTimer .Start
  127. Do
  128. writeWait .WaitOne
  129. writeWait .Reset
  130. bytesThisTime = CInt(inputBuffer.Length)
  131. Buffer.BlockCopy(inputBuffer.GetBuffer, 0, internalBuffer, 0, bytesThisTime)
  132. inputBuffer .SetLength(0)
  133. readWait .Set()
  134. target.Write(internalBuffer, 0, bytesThisTime)
  135. totalWritten += bytesThisTime
  136. Loop While writing
  137. ' Flush inputBuffer
  138. If inputBuffer.Length > 0 then
  139. bytesThisTime = CInt(inputBuffer.Length)
  140. Buffer.BlockCopy(inputBuffer.GetBuffer, 0, internalBuffer, 0, bytesThisTime)
  141. target.Write(internalBuffer, 0, bytesThisTime)
  142. totalWritten += bytesThisTime
  143. End If
  144. running = False
  145. writeTimer .Stop
  146. finishedWriting .Set
  147. Try
  148. target .Close
  149. target .Dispose
  150. Catch ex As Exception
  151. End Try
  152. inputBuffer .Close()
  153. inputBuffer .Dispose()
  154. inputBuffer = Nothing
  155. internalBuffer = Nothing
  156. target = Nothing
  157. GC.GetTotalMemory(True)
  158. End Sub
  159. End Class