INT 21h AH=45h returns a new handle number that refers to the same open file as BX. It is not a second open. DOS keeps one entry per open file in its system file table, with the file pointer, the access mode and the buffers, and both handles point to that entry. A read, a write or a seek through either handle moves the pointer for both.

What the copy is good for: closing it. INT 21h AH=3Eh on the copy writes out DOS's buffers and updates the directory entry, while the original stays open at its position. Before DOS 3.3 brought a commit call (68h), duplicate-and-close was the way to make sure the data had reached the disk.

prints two lines: "position seen through the copy: 3" and "after closing the copy, size: 5"
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 AH, 45h ; DOS: duplicate the handle in BX
INT 21h
CALL CHECK
MOV DI, AX ; DI = the copy
MOV AH, 40h ; write "ABC" through the original
MOV CX, 3
MOV DX, OFFSET ABC
INT 21h
CALL CHECK
PUSH BX
MOV BX, DI ; ask the copy where the pointer is
MOV AX, 4201h ; seek 0 from the current position
XOR CX, CX
XOR DX, DX
INT 21h
CALL CHECK
MOV SI, AX ; SI = the position, seen through the copy
MOV AH, 3Eh ; close the copy: DOS flushes the file
INT 21h
POP BX
MOV AH, 40h ; the original still works: write "DE"
MOV CX, 2
MOV DX, OFFSET ABC+3
INT 21h
CALL CHECK
MOV AX, 4202h ; the size: seek 0 from the end
XOR CX, CX
XOR DX, DX
INT 21h
CALL CHECK
MOV DI, AX ; DI = the size
MOV AH, 3Eh ; close the handle
INT 21h
MOV AH, 41h ; delete TEST.TXT
MOV DX, OFFSET FNAME
INT 21h
MOV DX, OFFSET M1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV AX, SI
CALL PDEC
MOV DX, OFFSET CRLF
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV DX, OFFSET M2
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV AX, DI
CALL PDEC
MOV DX, OFFSET CRLF
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
FNAME DB "TEST.TXT", 0
ABC DB "ABCDE"
M1 DB "position seen through the copy: $"
M2 DB "after closing the copy, size: $"
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 $"
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
PDEC PROC NEAR ; print AX in decimal
PUSH BX
PUSH CX
PUSH DX
MOV BX, 10
XOR CX, CX ; CX = digits pushed
PDEC1:
XOR DX, DX
DIV BX ; AX = AX / 10, DX = the last digit
PUSH DX
INC CX
OR AX, AX
JNZ PDEC1
PDEC2:
POP DX ; the digits come back first to last
ADD DL, "0"
MOV AH, 02h
INT 21h
LOOP PDEC2
POP DX
POP CX
POP BX
RET
PDEC ENDP

Redirection

The other use is saving a standard handle. Function 46h, force duplicate, makes handle CX refer to the same open file as BX, closing whatever CX was before. COMMAND.COM runs PROG > FILE with the pair: it opens the file, keeps a copy of handle 1 with 45h, points handle 1 at the file with 46h, and when the program returns puts the saved copy back with 46h and closes it. The INT 21h AH=44h article does the same inside one program.

Errors: 04h when all 20 handles of the program are in use, 06h when BX is not an open handle. Handles a child program inherits through EXEC are shared the same way, file pointer included, unless the file was opened with bit 7 of the access mode set (INT 21h AH=3Dh).