Flush File Data Buffers
|
|
Thread rating:  |
Michael D. Ober - 29 Oct 2003 20:55 GMT How can I flush the file buffers from a file opened with the VB open statement? Direct API calls are ok.
open "foo.txt" for output as 1 print #1, "This is a line"
' The next line is what I need flush #1
' more print statements close #1
Thanks, Mike.
Tom Esh - 29 Oct 2003 21:04 GMT >How can I flush the file buffers from a file opened with the VB open >statement? Direct API calls are ok. [quoted text clipped - 7 lines] >' more print statements >close #1 See the Reset method.
-Tom MVP - Visual Basic (please post replies to the newsgroup)
Michael D. Ober - 29 Oct 2003 21:27 GMT Can it be done without closing the files?
Mike.
> >How can I flush the file buffers from a file opened with the VB open > >statement? Direct API calls are ok. [quoted text clipped - 13 lines] > MVP - Visual Basic > (please post replies to the newsgroup) Tom Esh - 29 Oct 2003 22:31 GMT >Can it be done without closing the files? Have a look at the FlushFileBuffers Api function. You would have to first open the file with Api methods to get a handle (VB and Api file handles are not interchangeable), but I don't see anything that says it won't work if multiple handles are in use.
-Tom MVP - Visual Basic (please post replies to the newsgroup)
Tony Proctor - 30 Oct 2003 09:55 GMT FlushFileBuffers will not flush any data held by the VB run-time's file support. It is highly likely that this will hold buffers for record blocking/de-blocking.
As Adam says, later, you need to use the WINAPI functions throughout, possibly with a VB wrapper around them.
Depending on what the OP is trying to achieve, it's also worth looking at the FILE_FLAG_WRITE_THROUGH flag to CreateFile(). This guarantees that each individual WriteFile() operation is pushed "through" the file cache until it safely reaches the disk. Hence, no need to worry about calling FlushFileBuffers().
Tony Proctor
> >Can it be done without closing the files? > [quoted text clipped - 6 lines] > MVP - Visual Basic > (please post replies to the newsgroup) Tony Proctor - 30 Oct 2003 13:58 GMT Sorry, I meant to refer to the reply from "Frank Adam", not "Adam"
Tony
> FlushFileBuffers will not flush any data held by the VB run-time's file > support. It is highly likely that this will hold buffers for record [quoted text clipped - 21 lines] > > MVP - Visual Basic > > (please post replies to the newsgroup) Frank Adam - 29 Oct 2003 23:08 GMT >Can it be done without closing the files? Yes... You do need FlushFileBuffers(), but that requires that you use native win32 file i/o. VB doesn't give us a valid handle to our files, which truely sucks, considering that it does use CreateFile to open files, but thems are the damn rules. :(
If this is paramount for you, then you should consider writing a wrapper for the CraeteFile, WriteFile and ReadFile APIs, and then you can pass the handle returned by CreateFile to FlushFileBuffers. Note that this is simply a memory flush,it does not flush the disk cache itself to disk physical, but the H/D cache is pretty efficient.
ps: IIRC, the MSDN has a VB example on using WriteFile from VB, but it's not rocket science if you can't find it.
 Signature Regards, Frank
J French - 30 Oct 2003 12:46 GMT Frank has given you a pretty comprehensive answer
Another approach is to Close and then re-open the File - I strongly recommend doing this if one is *extends* a file and is not going to write to it again within say 1 second
It might be an idea to post what you are really trying to do, it looks suspiciously as if you are trying to write to a log file that can be read by another App
>How can I flush the file buffers from a file opened with the VB open >statement? Direct API calls are ok. [quoted text clipped - 10 lines] >Thanks, >Mike. Michael D. Ober - 30 Oct 2003 15:09 GMT I am indeed attempting to write a log file. VB gives you the option of setting the record length to 1, but that generates an incredible amount of overhead. Being able to flush the file after each non-blank line is critical so if a program crashes, I can start isolating the crash location using log file entries. By the way, the logfile is a class, so I have encapsulated the inner workings. Reading from another app during the current apps processing isn't a requirement, but it would be nice.
Mike.
> Frank has given you a pretty comprehensive answer > [quoted text clipped - 20 lines] > >Thanks, > >Mike. Tony Proctor - 30 Oct 2003 15:13 GMT It sounds like the FILE_FLAG_WRITE_THROUGH is just what you need then Michael
Tony Proctor
> I am indeed attempting to write a log file. VB gives you the option of > setting the record length to 1, but that generates an incredible amount of [quoted text clipped - 30 lines] > > >Thanks, > > >Mike. Larry Serflaten - 30 Oct 2003 15:19 GMT > I am indeed attempting to write a log file. VB gives you the option of > setting the record length to 1, but that generates an incredible amount of [quoted text clipped - 3 lines] > encapsulated the inner workings. Reading from another app during the > current apps processing isn't a requirement, but it would be nice. Closing the file after each write would protect you from program crashes.
LFS
J French - 30 Oct 2003 15:26 GMT >I am indeed attempting to write a log file. VB gives you the option of >setting the record length to 1, but that generates an incredible amount of [quoted text clipped - 3 lines] >encapsulated the inner workings. Reading from another app during the >current apps processing isn't a requirement, but it would be nice. Right, gottit
You need to open the file for Append
Another method is to pre-format the file and open it in Binary mode, keeping track of your 'logical' end of file.
The major problem is not so much flushing the file buffers, as updating the Directory entry after a file has been extended.
I would look at the Open for Append - unless you are logging dozens of lines per second - in which case I would have another App doing the logging - say an AX EXE - or even something 'listening' on a Windows Hook
Actually I reckon that is the best idea by far
Don@home.com - 30 Oct 2003 16:07 GMT >I am indeed attempting to write a log file. VB gives you the option of >setting the record length to 1, but that generates an incredible amount of [quoted text clipped - 5 lines] > >Mike. Is this NOT the Case....
Open File.... Print #.... Close #.... At this point the file no longer belongs to you but it belongs to Windows <?¿?>
Here is an example to play with (if you are interested) I have tried others but the results are the same... The Temp.Txt file always has a list from 0 to 4...
Can someone Not make it work..
Option Explicit
Private lLp As Long
Private Sub FileHandler() Dim lFF As Long lFF = FreeFile If lLp > 0 Then Open "d:\temp\temp.txt" For Append As #lFF Print #lFF, CStr(lLp) Close #lFF Else Open "d:\temp\temp.txt" For Output As #lFF Close #lFF Open "d:\temp\temp.txt" For Append As #lFF Print #lFF, CStr(lLp) Close #lFF End If End Sub
Private Sub Form_Click() For lLp = 0 To 10 If lLp = 5 Then lLp = 1 / 0 'Cause it to Crash... Else FileHandler End If Next lLp End Sub
Have a good day...
Don
Michael D. Ober - 30 Oct 2003 16:48 GMT First, thanks to all who replied. The CreateFile API was the kicker. I have successfully used in C and C++, but had been unsuccessful in VB. I chose not to use the FILE_FLAG_WRITE_THROUGH flag on the open because there is no need to flush empty lines. In my original request for assistance, I said that API calls were ok. As you can see, I was already using the GetLocalTime API call, and as a C programmer, I'm comfortable with them. My previous attemts to use CreateFile failed because I didn't have the function prototype correct. I have left the VB open/write/close statement in for comparison.
Thanks, Mike Ober.
'============================ logfile.cls Option Explicit Option Compare Text
'Source: i:\mis_stuff\vbcollections\library\logfile.cls 'Documentation: i:\mis_stuff\vbcollections\library\Library Specifications.doc#Objects\LogFile" '8 Jun 01 mdo Initial documentation '30 Oct 03 mdo Converted to use Win32 API CreateFile/WriteFile/FlushFileBuffers/CloseHandle with ' the assistance of several "Netizins" ' Now each non-blank line is flushed when it is written. Blank lines are buffered.
Private BaseFile As String Private FileName As String Private FileNum As Long Private Opened As Date Public UseVersion As Boolean
Private Type SYSTEMTIME wYear As Integer wMonth As Integer wDayOfWeek As Integer wDay As Integer wHour As Integer wMinute As Integer wSecond As Integer wMilliseconds As Integer End Type Private Declare Sub GetLocalTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
Private Const FILE_SHARE_READ = &H1 Private Const FILE_SHARE_WRITE = &H2 Private Const OPEN_ALWAYS = 4 Private Const GENERIC_READ = &H80000000 Private Const GENERIC_WRITE = &H40000000 Private Const INVALID_HANDLE_VALUE = -1
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As Long Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hFile As Long) As Long Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Sub WriteLog(Optional ByVal Message As String = "") Dim tm As SYSTEMTIME Dim written As Long
On Error GoTo Reopen If FileNum <> INVALID_HANDLE_VALUE Then 'If FileNum <> 0 Then Reopen If Message <> "" Then GetLocalTime tm Message = Format(tm.wYear, "0000") & Format(tm.wMonth, "00") & Format(tm.wDay, "00") & " " & _ Format(tm.wHour, "00") & "." & Format(tm.wMinute, "00") & "." & Format(tm.wSecond, "00") & "." & Format(tm.wMilliseconds, "000") & _ vbTab & Message End If retry: Message = Message & vbNewLine WriteFile FileNum, ByVal Message, Len(Message), written, vbNullString If Message <> vbNewLine Then FlushFileBuffers FileNum 'Print #FileNum, Message End If Exit Sub
Reopen: FileNum = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_ALWAYS, 0, 0) 'Open FileName For Append As FileNum Resume retry End Sub
Private Sub Class_Initialize() FileNum = INVALID_HANDLE_VALUE 'FileNum = 0 FileName = "" UseVersion = True End Sub
Private Sub Class_Terminate() WriteLog "Log File closing by object going out of Scope" 'If FileNum <> 0 Then Close #FileNum If FileNum <> INVALID_HANDLE_VALUE Then CloseHandle FileNum End Sub
Public Property Get Name() As String Name = FileName End Property Public Property Let Name(FileBase As String) Dim os As New OSInterface
' If no name is given, close existing log file and exit If FileBase = "" Then 'If FileNum <> 0 Then If FileNum <> INVALID_HANDLE_VALUE Then WriteLog "Log file closing; no new logfile being opened" CloseHandle FileNum FileNum = INVALID_HANDLE_VALUE 'Close #FileNum 'FileNum = 0 End If Exit Property End If
' Generate logfile name from FileBase Opened = Date If InStr(FileBase, "\") = 0 Then BaseFile = Environ$("LOGDIR") If BaseFile = "" Then BaseFile = "c:" BaseFile = TrailSlash(BaseFile) Else BaseFile = Left$(FileBase, InStrRev(FileBase, "\") - 1) FileBase = Mid$(FileBase, InStrRev(FileBase, "\") + 1) End If
' Ensure directory exists for new logfile os.CreateDirectory BaseFile
If UseVersion Then If InStr(FileBase, " v.") = 0 Then FileBase = FileBase & " v." & App.Major & "." & App.Minor & "." & App.Revision End If
BaseFile = TrailSlash(BaseFile) + FileBase + " - " + UCase(os.UserName) + " on " + UCase(os.ComputerName) + " - " FileName = BaseFile + Format$(Opened, "YYYYMMDD") + ".LOG"
' Can only write to one log file at a time If FileNum <> INVALID_HANDLE_VALUE Then 'If FileNum <> 0 Then WriteLog "Log file closing; new logfile '" & FileName & "' being opened" CloseHandle FileNum 'Close #FileNum End If
' Now open new logfile 'FileNum = FreeFile(1) 'Open FileName For Append As #FileNum FileNum = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_ALWAYS, 0, 0) WriteLog "Log File " + FileName + " opened" End Property
Public Sub Reopen(Optional force As Boolean = False) If Date <> Opened Then Opened = Date WriteLog "Day Change Closure" 'Close #FileNum 'FileNum = FreeFile(1) 'Open FileName For Output As #FileNum CloseHandle FileNum FileName = BaseFile + Format$(Opened, "YYYYMMDD") + ".LOG" FileNum = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_ALWAYS, 0, 0) WriteLog "Log File " + FileName + " opened by date change" ElseIf force Then WriteLog "Forced Reopen of Logfile" 'Close #FileNum 'FileNum = FreeFile(1) 'Open FileName For Output As #FileNum CloseHandle FileNum FileNum = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_ALWAYS, 0, 0) WriteLog "Reopen complete" End If End Sub
|
|
|