'Classes/System/IO/FileStreamImpl.ab Namespace System Namespace IO Namespace Detail Sub ThrowIfFilePointerFunctionFailed(dwLow As DWord) If dwLow = INVALID_FILE_SIZE Then Dim error = GetLastError() If error <> NO_ERROR Then Detail.ThrowWinIOException("FileStream: Failed to position or file size function.", error) End If End If End Sub /*! @brief シーク系関数の集合 @date 2008/08/21 @auther Egtra */ Class SeekFunctions Public Abstract Function CanSeek() As Boolean Abstract Function Seek(s As FileStream, offset As Int64, origin As SeekOrigin) As Int64 Abstract Sub Position(s As FileStream, value As QWord) Abstract Function Position(s As FileStream) As QWord Abstract Function Length(s As FileStream) As QWord Abstract Sub SetLength(s As FileStream, value As QWord) End Class /*! @brief シークできない実装 @date 2008/08/21 @auther Egtra */ Class Unseekable Inherits SeekFunctions Public Override Function CanSeek() As Boolean Return False End Function Override Function Seek(s As FileStream, offset As Int64, origin As SeekOrigin) As Int64 Throw New NotSupportedException("FileStream.Seek") End Function Override Sub Position(s As FileStream, vvalue As QWord) Throw New NotSupportedException("FileStream.Position") End Sub Override Function Position(s As FileStream) As QWord Throw New NotSupportedException("FileStream.Position") End Function Override Function Length(s As FileStream) As QWord Throw New NotSupportedException("FileStream.Length") End Function Override Sub SetLength(s As FileStream, value As QWord) Throw New NotSupportedException("FileStream.SetLength") End Sub End Class /*! @brief シークできる実装 @date 2008/08/21 @auther Egtra */ Class Seekable Inherits SeekFunctions Public Override Function CanSeek() As Boolean Return True End Function Override Function Seek(s As FileStream, offset As Int64, origin As SeekOrigin) As Int64 Dim seek = VarPtr(offset) As *LARGE_INTEGER Dim ret = SetFilePointer(s.Handle, seek->LowPart As Long, VarPtr(seek->HighPart), origin As DWord) If ret = INVALID_SET_FILE_POINTER Then Dim error = GetLastError() If error = ERROR_NEGATIVE_SEEK Then Throw New ArgumentException("FileStream.Seek: Cannot seek to negative offset.") ElseIf error <> NO_ERROR Then Detail.ThrowWinIOException("FileStream.Seek: Failed to seek.", error) End If End If seek->LowPart = ret Seek = offset End Function Override Sub Position(s As FileStream, value As QWord) Dim position = VarPtr(value) As *LARGE_INTEGER With position[0] Dim ret = SetFilePointer(s.Handle, .LowPart As Long, VarPtr(.HighPart), FILE_BEGIN) ThrowIfFilePointerFunctionFailed(ret) End With End Sub Override Function Position(s As FileStream) As QWord Dim position = VarPtr(Position) As *LARGE_INTEGER With position[0] .LowPart = SetFilePointer(s.Handle, .LowPart As Long, VarPtr(.HighPart), FILE_CURRENT) ThrowIfFilePointerFunctionFailed(.LowPart) End With End Function Override Function Length(s As FileStream) As QWord Dim length = VarPtr(Length) As *ULARGE_INTEGER length->LowPart = GetFileSize(s.Handle, VarPtr(length->HighPart)) ThrowIfFilePointerFunctionFailed(length->LowPart) End Function Override Sub SetLength(s As FileStream, value As QWord) If s.CanWrite() Then Dim current = s.Position() Position(s, value) Dim ret = SetEndOfFile(s.Handle) If ret = FALSE Then Detail.ThrowWinLastErrorIOException("FileStream.SetLength failed") End If Position(s, current) End If End Sub End Class /*! @brief シークできる実装(重複IO用) @date 2008/08/21 @auther Egtra */ Class OverlappedSeekable Inherits Seekable Public Override Function Seek(s As FileStream, offset As Int64, origin As SeekOrigin) As Int64 Seek = Super.Seek(s, offset, origin) offset = Position(s) End Function Override Sub Position(s As FileStream, value As QWord) Super.Position(s, value) offset = value End Sub Override Function Position(s As FileStream) As QWord Position = offset End Function Private offset As QWord End Class '--------------------------------------- /*! @brief 読み取り関数の集合 @date 2008/08/21 @auther Egtra */ Class ReadFunctions Public Abstract Function CanRead() As Boolean Abstract Function Read(s As FileStream, buffer As *Byte, offset As Long, count As Long) As Long Abstract Function BeginRead(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult Abstract Function EndRead(s As FileStream, asyncResult As System.IAsyncResult) As Long End Class /*! @brief 読み取りできない実装 @date 2008/08/21 @auther Egtra */ Class Unreadable Inherits ReadFunctions Public Override Function CanRead() As Boolean Return False End Function Override Function Read(s As FileStream, buffer As *Byte, offset As Long, count As Long) As Long Throw New NotSupportedException("FileStream.Read") End Function Override Function BeginRead(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult Throw New NotSupportedException("FileStream.BeginRead") End Function Override Function EndRead(s As FileStream, asyncResult As System.IAsyncResult) As Long Throw New NotSupportedException("FileStream.EndRead") End Function End Class /*! @brief 読み取りできる実装 @date 2008/08/21 @auther Egtra */ Class Readable Inherits ReadFunctions Public Override Function CanRead() As Boolean Return True End Function Override Function Read(s As FileStream, buffer As *Byte, offset As Long, count As Long) As Long Dim readBytes As DWord Dim ret = ReadFile(s.Handle, VarPtr(buffer[offset]), count As DWord, VarPtr(readBytes), ByVal NULL) If ret = FALSE Then Detail.ThrowWinLastErrorIOException("FileStream.Read: Failed to read.") End If Read = readBytes As Long End Function Override Function BeginRead(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult BeginRead = ThreadPoolIo(ToMBStr("ReadFile"), s, buffer, offset, count, callback, state) End Function Override Function EndRead(s As FileStream, asyncResult As System.IAsyncResult) As Long Dim ar = asyncResult As Detail.IAsyncStreamResult EndRead = ar.WaitAndGetResult End Function End Class /*! @brief 読み取りできる実装(重複IO用) @date 2008/08/21 @auther Egtra */ Class OverlappedReadable Inherits Readable Public Override Function Read(s As FileStream, buffer As *Byte, offset As Long, count As Long) As Long Read = EndRead(s, BeginRead(s, buffer, offset, count, Nothing, Nothing)) End Function Override Function BeginRead(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult BeginRead = BeginOverlappedIo(ToMBStr("ReadFile"), s, buffer, offset, count, callback, state) If s.CanSeek Then s.Seek(count, SeekOrigin.Current) End If End Function End Class '--------------------------------------- /*! @brief 書き込み関数の集合 @date 2008/08/21 @auther Egtra */ Class WriteFunctions Public Abstract Function CanWrite() As Boolean Abstract Sub Write(s As FileStream, buffer As *Byte, offset As Long, count As Long) Abstract Function BeginWrite(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult Abstract Sub EndWrite(s As FileStream, asyncResult As System.IAsyncResult) End Class /*! @brief 書き込みできない実装 @date 2008/08/21 @auther Egtra */ Class Unwritable Inherits WriteFunctions Public Override Function CanWrite() As Boolean Return False End Function Override Sub Write(s As FileStream, buffer As *Byte, offset As Long, count As Long) Throw New NotSupportedException("FileStream.Write") End Sub Override Function BeginWrite(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult Throw New NotSupportedException("FileStream.BeginWrite") End Function Override Sub EndWrite(s As FileStream, asyncResult As System.IAsyncResult) Throw New NotSupportedException("FileStream.EndWrite") End Sub End Class /*! @brief 書き込みできる実装 @date 2008/08/21 @auther Egtra */ Class Writable Inherits WriteFunctions Public Override Function CanWrite() As Boolean Return True End Function Override Sub Write(s As FileStream, buffer As *Byte, offset As Long, count As Long) Dim readBytes As DWord Dim ret = WriteFile(s.Handle, VarPtr(buffer[offset]), count As DWord, VarPtr(readBytes), ByVal NULL) If ret = FALSE Then Detail.ThrowWinLastErrorIOException("FileStream.Write: Failed to write.") End If End Sub Override Function BeginWrite(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult BeginWrite = ThreadPoolIo(ToMBStr("WriteFile"), s, buffer, offset, count, callback, state) End Function Override Sub EndWrite(s As FileStream, asyncResult As System.IAsyncResult) Dim ar = asyncResult As Detail.IAsyncStreamResult ar.WaitAndGetResult() End Sub End Class /*! @brief 書き込みできる実装(重複IO用) @date 2008/08/21 @auther Egtra */ Class OverlappedWritable Inherits Writable Public Override Sub Write(s As FileStream, buffer As *Byte, offset As Long, count As Long) EndWrite(s, BeginWrite(s, buffer, offset, count, Nothing, Nothing)) End Sub Override Function BeginWrite(s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult BeginWrite = BeginOverlappedIo(ToMBStr("WriteFile"), s, buffer, offset, count, callback, state) If s.CanSeek Then s.Seek(count, SeekOrigin.Current) End If End Function End Class '--------------------------------------- /*! @brief 重複IO実行開始 @param[in] fn "ReadFile"か"WriteFile"または引数に互換性のあるkernel32の関数名 @param[in] s 対象のストリーム @param[in,out] buffer 入出力先 @param[in] offset buffer内で入出力を開始する位置 @param[in] count 読み書きするバイト数 @param[in] callback 完了したときに呼ばれるデリゲート @param[in] state IAsyncResultへの追加引数 @return EndRead/Writeへの引き換えとなるIAsyncResult @date 2008/08/21 @auther Egtra */ Function BeginOverlappedIo(fn As PCSTR, s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As Detail.AsyncStreamResult Dim ioFile = GetProcAddress(GetModuleHandle("kernel32.dll"), fn) As Detail.PIOFile BeginOverlappedIo = New Detail.AsyncStreamResult(s.Handle, state) Dim pov = BeginOverlappedIo.PtrToOverlapped Dim position = s.Position pov->Offset = LODWORD(position) pov->OffsetHigh = HIDWORD(position) position += count If ioFile(s.Handle, buffer + offset, count, 0, pov) Then BeginOverlappedIo.SetCompletedSynchronously() Else Dim lastError = GetLastError() If lastError = ERROR_IO_PENDING Then If Not ActiveBasic.IsNothing(callback) Then Dim d = New Detail.CallbackData d.Callback = callback d.Result = BeginOverlappedIo Threading.ThreadPool.RegisterWaitForSingleObject(BeginOverlappedIo.AsyncWaitHandle, AddressOf(CallAsyncCallbackB), d) End If Exit Function ElseIf lastError = ERROR_HANDLE_EOF Then BeginOverlappedIo.SetCompletedSynchronously() BeginOverlappedIo.Handle = 0 BeginOverlappedIo.Result = 0 Else Detail.ThrowWinIOException("AsyncFileStream", lastError) End If End If If Not ActiveBasic.IsNothing(callback) Then Dim d = New Detail.CallbackData d.Callback = callback d.Result = BeginOverlappedIo Threading.ThreadPool.QueueUserWorkItem(AddressOf(CallAsyncCallback), d) End If End Function /*! @brief BeginOverlappedIoのコールバック補助 @return EndRead/Writeへの引き換えとなるIAsyncResult @date 2008/08/21 @auther Egtra */ Sub CallAsyncCallbackB(o As Object, b As Boolean) CallAsyncCallback(o) End Sub /*! @brief BeginOverlappedIoのコールバック補助 @return EndRead/Writeへの引き換えとなるIAsyncResult @date 2008/08/21 @auther Egtra */ Sub CallAsyncCallback(o As Object) Dim d = o As Detail.CallbackData Dim c = d.Callback c(d.Result) End Sub /*! @brief Stream用IAsyncResult拡張 @date 2008/08/21 @auther Egtra */ Interface IAsyncStreamResult Inherits IAsyncResult /*! @brief 入出力完了まで待機する @return 読み書きしたバイト数 */ Function WaitAndGetResult() As DWord End Interface /*! @brief IAsyncStreamResult実装用基底クラス @date 2008/08/21 @auther Egtra 2つの派生クラス、AsyncStreamResultとThreadPoolIoResultの共通部分 */ Class AsyncStreamResultImplBase Implements IAsyncStreamResult Protected Sub AsyncStreamResultImplBase(state As Object) Imports System.Threading wait = New EventWaitHandle(False, EventResetMode.ManualReset) s = state End Sub Public Virtual Function AsyncWaitHandle() As Threading.WaitHandle AsyncWaitHandle = wait End Function Virtual Function CompletedSynchronously() As Boolean End Function Virtual Function IsCompleted() As Boolean IsCompleted = AsyncWaitHandle.WaitOne(0) End Function Virtual Function AsyncState() As Object AsyncState = s End Function Virtual Function WaitAndGetResult() As DWord End Function Function AsyncEventWaitHandle() As Threading.EventWaitHandle AsyncEventWaitHandle = wait End Function Private wait As Threading.EventWaitHandle s As Object End Class /*! @brief 重複IO用IAsyncStreamResult実装 @date 2008/08/21 @auther Egtra */ Class AsyncStreamResult Inherits AsyncStreamResultImplBase Public Sub AsyncStreamResult(handle As HANDLE, state As Object) AsyncStreamResultImplBase(state) h = handle cs = False overlapped.hEvent = AsyncWaitHandle.Handle End Sub Override Function CompletedSynchronously() As Boolean CompletedSynchronously = cs End Function Sub SetCompletedSynchronously() cs = True wait.Set() End Sub Override Function WaitAndGetResult() As DWord If h <> 0 Then If GetOverlappedResult(h, overlapped, result, TRUE) = FALSE Then Detail.ThrowWinLastErrorIOException("FileStream.EndRead/Write") End If h = 0 End If WaitAndGetResult = result End Function Function PtrToOverlapped() As *OVERLAPPED PtrToOverlapped = VarPtr(overlapped) End Function Sub Handle(handle As HANDLE) h = handle End Sub '同期IO用 Sub Result(res As DWord) result = res End Sub Private overlapped As OVERLAPPED h As HANDLE result As DWord cs As Boolean End Class /*! @brief ThreadPoolIo用IAsyncStreamResult実装 @date 2008/08/21 @auther Egtra ThreadPoolIo関数も参照 */ Class ThreadPoolIoResult Inherits AsyncStreamResultImplBase Public Sub ThreadPoolIoResult(state As Object) AsyncStreamResultImplBase(state) End Sub Override Function CompletedSynchronously() As Boolean CompletedSynchronously = False End Function Override Function WaitAndGetResult() As DWord AsyncWaitHandle.WaitOne() If Not ActiveBasic.IsNothing(ex) Then Throw ex End If WaitAndGetResult = Result End Function Sub SetException(e As Exception) ex = e End Sub Result As DWord Private ex As Exception End Class /*! @brief Stream用同期IAsyncStreamResult実装 @date 2008/08/21 @auther Egtra StreamでのBeginRead/Writeの実装は、常にBegin内部で同期的に処理するというもの。これはそれ用の実装。 */ Class SyncStreamResultImpl Implements IAsyncStreamResult Public Sub SyncStreamResultImpl(result As DWord, state As Object) Imports System.Threading wait = New EventWaitHandle(False, EventResetMode.ManualReset) s = state End Sub Virtual Function AsyncWaitHandle() As Threading.WaitHandle Imports System.Threading If ActiveBasic.IsNothing(wait) Then wait = New EventWaitHandle(True, EventResetMode.ManualReset) End If AsyncWaitHandle = wait End Function Virtual Function CompletedSynchronously() As Boolean CompletedSynchronously = True End Function Virtual Function IsCompleted() As Boolean IsCompleted = True End Function Virtual Function AsyncState() As Object AsyncState = s End Function Virtual Function WaitAndGetResult() As DWord WaitAndGetResult = res End Function Function AsyncEventWaitHandle() As Threading.EventWaitHandle AsyncEventWaitHandle = wait End Function Private s As Object res As DWord Static wait = Nothing As Threading.EventWaitHandle End Class /*! @brief ReadFile/WriteFileのプロトタイプに一致する関数へのポインタ @date 2008/08/21 @auther Egtra */ TypeDef PIOFile = *Function(h As HANDLE, buf As VoidPtr, bytes As DWord, result As *DWord, ov As *OVERLAPPED) As BOOL /*! @brief BeginOverlappedIoとCallAsyncCallbackとの間で受け渡されるデータの集まり @date 2008/08/21 @auther Egtra */ Class CallbackData Public Callback As AsyncCallback Result As IAsyncResult End Class '--------------------------------------- /*! @brief スレッドプールによる非同期IO @date 2008/08/21 @param[in] fn "ReadFile"か"WriteFile"または引数に互換性のあるkernel32の関数名 @param[in] s 対象のストリーム @param[in,out] buffer 入出力先 @param[in] offset buffer内で入出力を開始する位置 @param[in] count 読み書きするバイト数 @param[in] callback 完了したときに呼ばれるデリゲート @param[in] state IAsyncResultへの追加引数 @return EndRead/Writeへの引き換えとなるIAsyncResult @auther Egtra OVERLAPPED指定無しで開いたファイルの非同期関数用 */ Function ThreadPoolIo(ioType As PCSTR, s As FileStream, buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult Dim result = New ThreadPoolIoResult(state) ThreadPoolIo = result Dim d = New ThreadPoolIoData With d .IoType = ioType .Handle = s.Handle .Result = result .Buffer = buffer + offset .Size = count As DWord End With Threading.ThreadPool.QueueUserWorkItem(AddressOf(ThreadIo), d) End Function /*! @brief ThreadPoolIo補助で実際に入出力関数を呼ぶ @date 2008/08/21 @auther Egtra */ Sub ThreadIo(o As Object) Dim d = o As ThreadPoolIoData With d Dim ioFile = GetProcAddress(GetModuleHandle("kernel32.dll"), .IoType) As Detail.PIOFile If ioFile(.Handle, .Buffer, .Size, VarPtr(.Result.Result), 0) = FALSE Then .Result.SetException(Detail.GetWinLastErrorIOException("FileStream.BeginRead/Write")) End If d.Result.AsyncEventWaitHandle.Set() If Not ActiveBasic.IsNothing(.Callback) Then Dim c = .Callback c(.Result) End If End With End Sub /*! @brief ThreadPoolIoとThreadIoとの間で受け渡すデータの集まり @date 2008/08/21 @auther Egtra */ Class ThreadPoolIoData Public IoType As PCSTR Handle As HANDLE Result As ThreadPoolIoResult Callback As AsyncCallback Buffer As *Byte Size As DWord End Class End Namespace End Namespace End Namespace