source: trunk/Include/Classes/System/IO/FileStream.ab@ 474

Last change on this file since 474 was 474, checked in by OverTaker, 16 years ago

FileStreamクラスでFileMode.Appendが指定されたとき、ファイルの位置が末尾に移動していない不具合を修正。

File size: 12.9 KB
RevLine 
[271]1Namespace System
2Namespace IO
[256]3
[260]4/* ほんとはmiscに入れるかかファイルを分けたほうがいいかもしれないが一先ず実装 */
[256]5Enum FileOptions
[391]6 None = 0
7 Asynchronous = FILE_FLAG_OVERLAPPED
8 DeleteOnClose = FILE_FLAG_DELETE_ON_CLOSE
9 Encrypted = FILE_ATTRIBUTE_ENCRYPTED
10 RandomAccess = FILE_FLAG_RANDOM_ACCESS
11 SequentialScan = FILE_FLAG_SEQUENTIAL_SCAN
12 WriteThrough = FILE_FLAG_WRITE_THROUGH
[256]13End Enum
14
[105]15Class FileStream
[256]16 Inherits Stream
17
18 handle As HANDLE
19
20 /*
[260]21 ファイルハンドルからこれらを取得できれば、これらは入らないが
[256]22 今のところは不明なので自前で実装するしかない
23 */
24 filePath As String
25 fileMode As DWord
26 fileAccess As DWord
27 fileShare As DWord
28 fileOptions As DWord
[432]29 ownsHandle As Boolean
[388]30
31 offset As QWord 'オーバーラップドIO用
[256]32
33Public
[260]34 /* コンストラクタ.NETと同じように実装は難しい、一先ず動くものを実装したが変更が必要だと思う */
[256]35 Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare, options As FileOptions)
[435]36 If ActiveBasic.IsNothing(path) Then
37 Throw New ArgumentNullException("path")
38 ElseIf path.Length = 0 Then
39 Throw New ArgumentException
40 End If
41
42
[432]43 Dim ac = access As DWord
44 Dim sh = share As DWord
45 Dim mo = mode As DWord
46 Dim op = options As DWord
[426]47' If (Environment.OSVersion.Platform As DWord) <> (PlatformID.Win32NT As DWord) Then 'ToDo: なぜかアクセス違反になる
[391]48 op And= Not FILE_FLAG_OVERLAPPED
[426]49' End If
[256]50
[391]51 This.handle=CreateFile(ToTCStr(path),ac,sh,ByVal NULL,mo,op,0)
[260]52 If This.handle=INVALID_HANDLE_VALUE Then
[256]53 'エラー処理
54 'Throw ArgumentException
55 'Throw IOException
56 'Throw System.IO.FileNotFoundException
[260]57 This.handle=0
[435]58 Detail.ThrowWinLastErrorIOException("Failed to open/create file.")
[256]59 Exit Sub
60 End If
61
[336]62 This.filePath = path
63 This.fileMode = mo
64 This.fileAccess = ac
65 This.fileShare = sh
66 This.fileOptions = op
[388]67 This.offset = 0
[432]68 This.ownsHandle = True
[474]69
70 If FileMode.Append = This.fileMode Then
71 Seek(0, SeekOrigin.End)
72 End If
[256]73 End Sub
[260]74 Sub FileStream(path As String, mode As FileMode, access As FileAccess, share As FileShare)
[336]75 This.FileStream(path,mode,access,share,FileOptions.None)
[260]76 End Sub
77 Sub FileStream(path As String, mode As FileMode, access As FileAccess)
[336]78 This.FileStream(path,mode,access,FileShare.None,FileOptions.None)
[260]79 End Sub
[256]80 Sub FileStream(path As String, mode As FileMode)
[260]81 Dim access As FileAccess
[256]82 Select Case mode
83 Case FileMode.Append
[260]84 access=FileAccess.Write
[256]85 Case FileMode.Create
[260]86 access=FileAccess.ReadWrite
[256]87 Case FileMode.CreateNew
[260]88 access=FileAccess.ReadWrite
[256]89 Case FileMode.Open
[260]90 access=FileAccess.ReadWrite
[256]91 Case FileMode.OpenOrCreate
[260]92 access=FileAccess.ReadWrite
[256]93 Case FileMode.Truncate
[260]94 access=FileAccess.Write
[256]95 End Select
[336]96 This.FileStream(path,mode,access,FileShare.None,FileOptions.None)
[256]97 End Sub
[432]98 /*
99 @date 2008/02/26
100 @auther Egtra
101 '不要になったら削除すること
102 */
103 Sub FileStream(h As HANDLE, access As FileAccess, owns As Boolean)
104 handle = h
105 fileAccess = access As DWord
106 ownsHandle = owns
107 End Sub
108
[256]109Public
[391]110 /*!
111 @brief ファイルが読み込みに対応しているかを返す
112 */
[256]113 Override Function CanRead() As Boolean
[336]114 If This.fileAccess And GENERIC_READ Then
[256]115 Return True
116 Else
117 Return False
118 End If
119 End Function
120
[391]121 /*!
122 @brief ファイルがシークに対応しているかを返す
123 */
[256]124 Override Function CanSeek() As Boolean
[336]125 If GetFileType(This.handle)=FILE_TYPE_DISK Then
126 Return True
127 Else
128 Return False
129 End If
[256]130 End Function
131
[336]132' Override Function CanTimeout() As Boolean
133' /* ファイルがタイムアウトに対応しているかを返す */
134' Return False /*今のところ対応していないのでFalse*/
135' End Function*/
[256]136
[391]137 /*!
138 @brief ファイルが書き込みに対応しているかを返す
139 */
[256]140 Override Function CanWrite() As Boolean
[336]141 If This.fileAccess And GENERIC_WRITE Then
[256]142 Return True
143 Else
144 Return False
145 End If
146 End Function
147
148 /*Handle*/
149
[391]150 /*!
151 @brief ファイルが非同期操作に対応しているかを返す
152 */
[256]153 Function IsAsync() As Boolean
[388]154 If This.fileOptions And FILE_FLAG_OVERLAPPED /*FileOptions.Asynchronous*/ Then
[256]155 Return True
156 Else
157 Return False
158 End If
159 End Function
160
161 Override Function Length() As Int64
[391]162 disposedCheck()
[336]163 If This.CanSeek() Then
[391]164 Dim length = VarPtr(Length) As *ULARGE_INTEGER
165 length->LowPart = GetFileSize(This.handle, VarPtr(length->HighPart))
166 If LODWORD(Length) = INVALID_FILE_SIZE Then
167 Dim error = GetLastError()
168 If error <> NO_ERROR Then
169' Detail.ThrowWinIOException("FileStream.Read: Failed to read.", error)
170 End If
171 End If
172
173 If Length < 0 Then
174 Debug 'Throw OverflowException
175 End If
[336]176 End If
[256]177 End Function
178
179 Function Name() As String
[388]180 Return This.filePath
[256]181 End Function
182
183 Override Sub Position(value As Int64)
[391]184 disposedCheck()
[336]185 If This.CanSeek() Then
186 If This.IsAsync() Then
[388]187 offset = value As QWord
[336]188 Else
189 Dim position As LARGE_INTEGER
190 position.LowPart=LODWORD(value)
191 position.HighPart=HIDWORD(value)
192 SetFilePointer(This.handle,position.LowPart,VarPtr(position.HighPart) As *DWord,FILE_BEGIN)
193 End If
[256]194 End If
195 End Sub
196 Override Function Position() As Int64
[391]197 disposedCheck()
[336]198 If This.CanSeek() Then
199 If This.IsAsync() Then
[388]200 Return offset As Int64
[336]201 Else
202 Dim position As LARGE_INTEGER
203 ZeroMemory(VarPtr(position),SizeOf(LARGE_INTEGER))
204 position.LowPart=SetFilePointer(This.handle,position.LowPart,VarPtr(position.HighPart) As *DWord,FILE_CURRENT)
[388]205 Return MAKEQWORD(position.LowPart,position.HighPart) As Int64
[336]206 End If
207 End If
[256]208 End Function
209
[336]210/* Override Sub ReadTimeout(value As Long)
[256]211 'TODO
212 End Sub
213 Override Function ReadTimeout() As Long
214 'TODO
[336]215 End Function*/
[256]216
217 /* Safe~Handle系の実装は要相談!! */
218/* Function SafeFileHandle() As SafeFileHandle
219 End Function*/
220
221 Override Sub WriteTimeout(value As Long)
222 'TODO
223 End Sub
224 Override Function WriteTimeout() As Long
225 'TODO
226 End Function
227
228
229Public
[348]230 Override Function BeginRead(buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult
[336]231 If This.IsAsync() Then
232 Else
233 Read(buffer,offset,count)
234 End If
[256]235 End Function
236
[348]237 Override Function BeginWrite(buffer As *Byte, offset As Long, count As Long, callback As AsyncCallback, state As Object) As System.IAsyncResult
[336]238 If This.IsAsync() Then
239 Else
240 Write(buffer,offset,count)
241 End If
[256]242 End Function
243
244/* CreateObjRef*/
[348]245
[388]246 Override Function EndRead(asyncResult As System.IAsyncResult) As Long
[256]247 'TODO
[339]248 End Function
[256]249
[388]250 Override Sub EndWrite(asyncResult As System.IAsyncResult)
[256]251 'TODO
[349]252 End Sub
[256]253
254/* Equals*/
255
256 Override Sub Flush()
[391]257 disposedCheck()
258 Dim ret = FlushFileBuffers(This.handle)
259 If ret = FALSE Then
260' Detail.ThrowWinLastErrorIOException("FileStream.Read: Failed to read.")
261 End If
[256]262 End Sub
263
264/* Function GetAccessControl() As FileSecurity
265 FileSecurityの実装がまだできてない。
266 End Function*/
267
268/* GetLifetimeService*/
269
270/* Override Function GetType() As TypeInfo
271 Return Super.GetType()
272 End Function*/
273
274/* InitializeLifetimeService*/
275
276 Sub Lock(position As Int64, length As Int64)
[391]277 disposedCheck()
[388]278 If position < 0 Then
279 Throw New ArgumentOutOfRangeException("FileStream.Lock: An argument is negative value.", New System.Int64(position), "position")
280 ElseIf length < 0 Then
281 Throw New ArgumentOutOfRangeException("FileStream.Lock: An argument is negative value.", New System.Int64(length), "length")
282 End If
283 LockFile(handle, LODWORD(position As QWord), HIDWORD(position As QWord),
284 LODWORD(length As QWord), HIDWORD(length As QWord))
[256]285 End Sub
286
[426]287 Override Function Read(buffer As *Byte, offset As Long, count As Long) As Long
[391]288 disposedCheck()
289 If buffer = 0 Then
[426]290 Throw New ArgumentNullException("FileStream.Read: An argument is null value.", "buffer")
[391]291 ElseIf Not This.CanRead() Then
[426]292 Throw New NotSupportedException("FileStream.Read: This stream is not readable.")
[391]293 End If
294
295 Dim ret As BOOL
296 Dim readBytes As DWord
297 If This.IsAsync() Then
298 Dim overlapped As OVERLAPPED
299 SetQWord(VarPtr(overlapped.Offset), offset)
300 overlapped.hEvent = CreateEvent(0, TRUE, FALSE, 0)
301 If overlapped.hEvent = 0 Then
[426]302 Throw New OutOfMemoryException("FileStream.Read: Failed to create an event object.")
[391]303 End If
304 Try
305 ret = ReadFile(This.handle, VarPtr(buffer[offset]), count, 0, overlapped)
[388]306 If ret = FALSE Then
[391]307 Dim error = GetLastError()
308 If error <> ERROR_IO_PENDING Then
[426]309 Detail.ThrowWinIOException("FileStream.Read: Failed to read.", error)
[388]310 End If
311 End If
[391]312 ret = GetOverlappedResult(This.handle, overlapped, readBytes, TRUE)
313 If ret = FALSE Then
[426]314 Detail.ThrowWinLastErrorIOException("FileStream.Read: Failed to read.")
[391]315 End If
[388]316 offset += Read
[391]317 Finally
318 CloseHandle(overlapped.hEvent)
319 End Try
320 Else
321 ret = ReadFile(This.handle,VarPtr(buffer[offset]),count,VarPtr(readBytes),ByVal NULL)
322 If ret = FALSE Then
[426]323 Detail.ThrowWinLastErrorIOException("FileStream.Read: Failed to read.")
[336]324 End If
[256]325 End If
[391]326 Read = readBytes As Long
[256]327 End Function
328
[391]329 /*!
330 @brief ストリームの現在位置を移動させる。
331 @param[in] offset originからの移動量
332 @param[in] origin 移動の基準位置
333 @return 移動後の新しい現在位置
334 @exception DisposedException 既にストリームが閉じられている場合
335 @exception ArgumentException 移動後の位置が負の位置(ファイル先頭より手前)になる場合
336 @exception IOException その他エラーが発生した場合
337 */
338 Override Function Seek(offset As Int64, origin As SeekOrigin) As Int64
339 disposedCheck()
[336]340 If This.CanSeek() Then
341 If This.IsAsync() Then
342 Select Case origin
343 Case SeekOrigin.Begin
[388]344 This.offset = offset
[336]345 Case SeekOrigin.Current
[388]346 This.offset += offset
[336]347 Case SeekOrigin.End
[388]348 This.offset = This.Length + offset
[336]349 End Select
[391]350 Seek = This.offset As Int64
351 If Seek < 0 Then
352' Throw ArgumentException("FileStream.Seek: Cannot seek to negative offset.")
353 End If
[336]354 Else
[391]355 Dim seek = VarPtr(offset) As *ULARGE_INTEGER
356 Dim ret = SetFilePointer(This.handle, seek->LowPart, VarPtr(seek->HighPart), origin As DWord)
357 If ret = INVALID_SET_FILE_POINTER Then
358 Dim error = GetLastError()
359 If error = ERROR_NEGATIVE_SEEK Then
360' Throw ArgumentException("FileStream.Seek: Cannot seek to negative offset.")
361 ElseIf error <> NO_ERROR Then
362' Throw Detail.ThrowWinIOException("FileStream.Seek: Failed to seek.", error)
363 End If
364 End If
365 seek->LowPart = ret
366 Seek = offset
[336]367 End If
[256]368 End If
369 End Function
370
371/* Sub SetAccessControl(fileSecurity As FileSecurity)
372 FileSecurityの実装がまだできてない。
373 End Sub*/
374
375 Override Sub SetLength(value As Int64)
[391]376 disposedCheck()
[336]377 If This.CanWrite() and This.CanSeek() Then
378 If This.IsAsync() Then
379 Else
380 Dim current = This.Position()
381 This.Position(value)
[391]382 Dim ret = SetEndOfFile(This.handle)
383 If ret = FALSE Then
384 Detail.ThrowWinLastErrorIOException("FileStream.Read: Failed to read.")
385 End If
386 Position = current
[336]387 End If
[256]388 End If
389 End Sub
390
391/* Synchronized*/
392
393 Override Function ToString() As String
[336]394 Return This.Name()
[256]395 End Function
396
397 Sub Unlock(position As Int64, length As Int64)
[391]398 disposedCheck()
[388]399 If position < 0 Then
400 Throw New ArgumentOutOfRangeException("FileStream.Lock: An argument is negative value.", New System.Int64(position), "position")
401 ElseIf length < 0 Then
402 Throw New ArgumentOutOfRangeException("FileStream.Lock: An argument is negative value.", New System.Int64(length), "length")
403 End If
[391]404 Dim ret = UnlockFile(handle, LODWORD(position As QWord), HIDWORD(position As QWord),
[388]405 LODWORD(length As QWord), HIDWORD(length As QWord))
[391]406 If ret = FALSE Then
407 Detail.ThrowWinLastErrorIOException("FileStream.Read: Failed to read.")
408 End If
[256]409 End Sub
410
[337]411 Override Sub Write(buffer As *Byte, offset As Long, count As Long)
[391]412 disposedCheck()
[336]413 If This.CanWrite() Then
[388]414 Dim writeBytes As DWord
[336]415 If This.IsAsync() Then
[388]416 Dim overlapped As OVERLAPPED
417 SetQWord(VarPtr(overlapped.Offset), offset)
[391]418 overlapped.hEvent = CreateEvent(0, TRUE, FALSE, 0)
[388]419 Dim ret = WriteFile(This.handle, VarPtr(buffer[offset]), count, 0, overlapped)
[391]420 If ret <> FALSE Or GetLastError() = ERROR_IO_PENDING Then
421 GetOverlappedResult(This.handle, overlapped, writeBytes, TRUE)
[388]422 End If
423 offset += writeBytes
[391]424 CloseHandle(overlapped.hEvent)
[336]425 Else
[388]426 WriteFile(This.handle, VarPtr(buffer[offset]), count, VarPtr(writeBytes), ByVal NULL)
[336]427 End If
[256]428 End If
429 End Sub
430
431Protected
[432]432 Override Sub Dispose(disposing As Boolean)
433 If handle <> 0 Then
434 Flush()
[439]435 CloseHandle(InterlockedExchangePointer(ByVal VarPtr(handle), NULL))
[432]436 End If
437 End Sub
438
[262]439 Override Function CreateWaitHandle() As System.Threading.WaitHandle
[437]440 Return New System.Threading.AutoResetEvent(False)
[256]441 End Function
442
[336]443Private
[391]444 Sub disposedCheck()
445 If handle = 0 Then
446' Throw ObjectDisposedException("FileStream: This stream has closed.")
447 End If
448 End Sub
449
[105]450End Class
[271]451
452
453End Namespace
454End Namespace
Note: See TracBrowser for help on using the repository browser.