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

Last change on this file since 391 was 391, checked in by イグトランス (egtra), 16 years ago

FileStream非同期読み書きの修正、例外処理の追加。

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