INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

Databases and tables

How to repair a corrupted DBF header? by dbMark
Posted: 26 May 14 (Edited 9 Aug 14)

It's important to note that this function only repairs an invalid table header record count, it does not resolve any other issues in the header or table records.

FAQ184-3162: How to read a DBF Header and get info about it and FAQ184-4353: DBF table structure give background on the format of a DBF table's header. In my experience corrupted tables are rare but usually encountered after a partially completed APPEND operation, probably in a multi-user or networked environment. What that means is that VFP appended the record(s) but for whatever reason failed to update the header record with the new record count. So the next time a user tries to open the table then error #2091 is generated and the user sees the message: Table "name" has become corrupted. The table will need to be repaired before using again.

While the message can be bypassed by changing the setting of SET("TABLEVALIDATE") from the normal 3 to 0, that only bypasses the error, the user can open the table but the table header is still "corrupted", that is, incorrect.

When I first wrote it I used FILETOSTR() and STRTOFILE() but while simple this does read and rewrite the entire table. It's best to make as minimal an impact as possible, especially with large tables so I wanted to update only the 4 bytes holding the record count. I instead used the low level file functions such as FOPEN, FSEEK, FREAD, FWRITE and FCLOSE that are present in all versions of Visual FoxPro.

CODE

FUNCTION FixRecCount( pTable )
LOCAL cTable, objErr, nErrNo, cErrMsg, nTmp, nTmpCount, nHandle, nAscValue, nSuccess
LOCAL nTabLen, cHdrRead, nCurCount, nHdrLen, nRecLen, nRecCount, cHdrWrite
DO CASE
CASE !EMPTY(pTable) AND FILE(pTable)
	cTable = pTable
CASE !EMPTY(pTable) AND FILE(pTable + ".dbf")
	cTable = pTable + ".dbf"
OTHERWISE
	cTable = GETFILE("DBF")
ENDCASE

nErrNo = 0
cErrMsg = ""
SET TABLEVALIDATE TO 3  && normal table validation
IF FILE(cTable)

	TRY
		USE (cTable) IN 0 ALIAS _OPENTEST SHARED
	CATCH TO objErr
		nErrNo = objErr.ErrorNo
		cErrMsg = objErr.Message
		RELEASE objErr
	ENDTRY
	IF NOT EMPTY(SELECT("_OPENTEST"))
		USE IN "_OPENTEST"
	ENDIF

	IF nErrNo = 2091 OR "corrupted" $ cErrMsg

		nHandle = FOPEN(cTable, 2)
		IF nHandle <> 0
			nTabLen = FSEEK(nHandle, 0, 2) && go to EOF to determine file size
			IF nTabLen <> 0
				nTmp = FSEEK(nHandle, 0, 0) && go to BOF()
				* We only need data from the first 12 bytes of the header
				cHdrRead = FREAD(nHandle, 12)

				nCurCount = ASC(SUBSTR(cHdrRead,5,1)) * (256^0) + ASC(SUBSTR(cHdrRead,6,1)) * (256^1) + ASC(SUBSTR(cHdrRead,7,1)) * (256^2) + ASC(SUBSTR(cHdrRead,8,1)) * (256^3)
				nHdrLen = ASC(SUBSTR(cHdrRead,9,1)) * (256^0) + ASC(SUBSTR(cHdrRead,10,1)) * (256^1) && Header length
				nRecLen = ASC(SUBSTR(cHdrRead,11,1)) * (256^0) + ASC(SUBSTR(cHdrRead,12,1)) * (256^1) && Record Length
				nRecCount = (nTabLen - (nHdrLen + 1)) / nRecLen

				nAscValue = INT(nRecCount/(256^3))
				nTmpCount = MOD(nRecCount, 256^3)
				cHdrWrite = CHR(nAscValue)

				nAscValue = INT(nTmpCount/(256^2))
				nTmpCount = MOD(nTmpCount, 256^2)
				cHdrWrite = CHR(nAscValue) + cHdrWrite

				nAscValue = INT(nTmpCount/(256^1))
				nTmpCount = MOD(nTmpCount, 256^1)
				cHdrWrite = CHR(nAscValue) + cHdrWrite

				nAscValue = INT(nTmpCount)
				cHdrWrite = CHR(nAscValue) + cHdrWrite

				IF nCurCount <> nRecCount
					MESSAGEBOX("Table record count ("+LTRIM(STR(nCurCount))+") will be updated to "+LTRIM(STR(nRecCount))+".", 48, "Record count incorrect")
					nTmp = FSEEK(nHandle, 4, 0) && BOF() + 4
					nSuccess = FWRITE(nHandle, cHdrWrite, 12)
					MESSAGEBOX("Table record count was " + IIF(EMPTY(nSuccess), "NOT ", "") + "updated to "+LTRIM(STR(nRecCount))+".", 48, "Write results")
				ELSE
					MESSAGEBOX("Table record count ("+LTRIM(STR(nCurCount))+") is correct.", 48, "No action needed")
				ENDIF

			ELSE
				MESSAGEBOX("Cannot low-level open table.", 48, "Unable to continue.")
			ENDIF
			nTmp = FCLOSE(nHandle)
		ELSE
			MESSAGEBOX("File has zero size or unable to properly open table", 64, "Table not open")
		ENDIF
	ELSE
		MESSAGEBOX("No table validation errors", 64, "No issues detected")
	ENDIF
ELSE
	MESSAGEBOX("No table selected", 64, "Nothing to do")
ENDIF
RETURN 

Back to Microsoft: Visual FoxPro FAQ Index
Back to Microsoft: Visual FoxPro Forum

My Archive

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close