source: trunk/ab5.0/ablib/src/Classes/System/IO/FileStream.ab@ 560

Last change on this file since 560 was 560, checked in by dai, 16 years ago

#183への対応。コンストラクタ、デストラクタが直接呼び出された場合はエラーとして扱うようにした。
(64bit版は後ほどコミットします)

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