INT 21h AH=3Eh ends the use of a handle. For a file that was written, DOS first writes the data still sitting in its buffers to the disk, then updates the directory entry: the new length, and the date and time of the last write (or the stamp set with INT 21h AH=57h). Until that happens the directory may still show the old size, and a crash or a reset loses the end of the file.

The handle number becomes free, and the next open or create can hand it out again. If several handles refer to the same open file (copies made with INT 21h AH=45h, or handles a child program inherited), closing one drops only that handle; the file stays open through the others.

prints three lines: "first close: ok", "second close: error 06" and "reopened: the same handle number"
MOV AH, 3Ch ; DOS: create TEST.TXT
XOR CX, CX ; CX = 0: normal attributes
MOV DX, OFFSET FNAME
INT 21h
CALL CHECK ; stop if CF = 1
MOV BX, AX ; BX = the handle
MOV DX, OFFSET M1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV AH, 3Eh ; DOS: close the handle in BX
INT 21h
CALL REPORT
MOV DX, OFFSET M2
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV AH, 3Eh ; the same number again
INT 21h
CALL REPORT ; CF = 1, AX = 06h
MOV AX, 3D00h ; open the file again
MOV DX, OFFSET FNAME
INT 21h
CALL CHECK
MOV DX, OFFSET SAME1
CMP AX, BX ; the number that was just freed?
JE SHOW1
MOV DX, OFFSET OTHER1
SHOW1:
MOV BX, AX ; BX = the new handle
MOV AH, 09h
INT 21h
MOV AH, 3Eh ; close the handle
INT 21h
MOV AH, 41h ; delete TEST.TXT
MOV DX, OFFSET FNAME
INT 21h
FNAME DB "TEST.TXT", 0
M1 DB "first close: $"
M2 DB "second close: $"
SAME1 DB "reopened: the same handle number", 13, 10, "$"
OTHER1 DB "reopened: a different handle number", 13, 10, "$"
CRLF DB 13, 10, "$"
CHECK PROC NEAR ; CF = 1: print the error code in AL and exit
JNC CHECK1
PUSH AX
MOV DX, OFFSET FAILMSG
MOV AH, 09h
INT 21h
POP AX
CALL PHEXB
MOV AX, 4C01h ; exit with code 1
INT 21h
CHECK1:
RET
CHECK ENDP
FAILMSG DB "failed: error $"
REPORT PROC NEAR ; CF = 0: "ok"; CF = 1: "error" and AL
JC REPORT1
MOV DX, OFFSET OKMSG
MOV AH, 09h
INT 21h
RET
REPORT1:
PUSH AX
MOV DX, OFFSET ERRMSG
MOV AH, 09h
INT 21h
POP AX
CALL PHEXB ; the error code
MOV DX, OFFSET CRLF
MOV AH, 09h
INT 21h
RET
REPORT ENDP
OKMSG DB "ok", 13, 10, "$"
ERRMSG DB "error $"
PHEXB PROC NEAR ; print AL as two hex digits
PUSH CX
PUSH DX
MOV DH, AL ; keep the byte
MOV CL, 4
SHR AL, CL ; the high digit first
MOV CX, 2
PHEXB1:
AND AL, 0Fh
ADD AL, "0"
CMP AL, "9"
JBE PHEXB2
ADD AL, 7 ; 10..15 -> "A".."F"
PHEXB2:
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h
MOV AL, DH ; then the low digit
LOOP PHEXB1
POP DX
POP CX
RET
PHEXB ENDP

The second close fails with 06h, invalid handle: the number no longer refers to anything. The same error comes back for a number that was never opened and for anything above 19.

Standard handles

Handles 0 to 4 can be closed like any other. Closing handle 1 cuts the program off from standard output (INT 21h AH=09h then prints nothing), and the next file opened gets number 1, so whatever the program prints goes into that file. That is one way to redirect output from inside a program; INT 21h AH=45h and 46h do it with a way back.

When a program ends with INT 21h AH=4Ch, DOS closes every handle it still has, so files are not left half-written. Relying on that is poor style, and nothing closes them if the program hangs.

In DOSBox-X a file on a host folder is a host file; the host is sure to see its final contents, length and timestamp once the last handle to it is closed.