Posts: 1,956
Threads: 65
Joined: Jun 2003
I was running a Quickbasic utility program that I wrote back in 1999. Under certain conditions the program needs to do:
SHELL "DIR filespec > tempfile"
Then it reads the tempfile and selects all the filenames only, not directory names, headings, etc.
Suddenly it's not working. After much scouting I discovered that a DIR under Windows XP produces a listing much different than the original DOS 6.22 that I was using at the time I wrote the program.
I don't know exactly when Windows changed to the new DIR format, but I did some testing and came up with a DIR command which will work for DOS 6.22 and Windows XP. The secret is using the /B switch which produces a "bare" listing of only the filenames (including the subdirectory names), and combining this with the /A-D which eliminates the subdirectory names, to get only the filenames. The new SHELL becomes:
SHELL "DIR filespec /B /A-D > tempfile"
A minor additional change in my program now picks up the filenames left-justified in each record of the tempfile.
So, if you've got an old program that does a DIR to get filenames, take another look at it if you're running a newer Windows.
*****
Posts: 544
Threads: 27
Joined: Jan 2005
Moneo:
Very good information! But, why not add a "sort by name" switch also? Then, your new code line would be:
Code: SHELL "DIR filespec /B/O /A-D > tempfile"
And, for those who may not know how, to get all the data on any command.com command, use the /? switch, as well as the "show a page and pause" switch, /p, as in:
dir/?/p
Ralph, using QuickBASIC 4.5 and Windows XP Home Edition and Service Pack 2, with HP LaserJet 4L printer.
Posts: 1,956
Threads: 65
Joined: Jun 2003
Thanks for the good feedback, Ralph.
While I was testing using Windows XP, I discovered that it ALWAYS sorts the filenames by default.
Although it's a good idea, I wouldn't put in any more switches for sorting unless I really needed the file to be in that sequence.
Your other general suggestions about /? and /p are well taken.
*****
Posts: 544
Threads: 27
Joined: Jan 2005
Live and learn!
I tried the dir command with the switches, but without the /o, and, you are right, in my Win XP HE, the directories are shown sorted by name...However! When I tried this on 3 1/2" floppy disks, I had to use the /o switch to get the files sorted by name. More mysteries? Seems as if there is no standard way of saying something, without somebody finding an exception!
Ralph, using QuickBASIC 4.5 and Windows XP Home Edition and Service Pack 2, with HP LaserJet 4L printer.
Posts: 1,752
Threads: 21
Joined: Jun 2002
Relying on the command interpreter to do your dirty work is just asking for trouble imo...a DOS can have a different output for "DIR" and still be 100% "MS-DOS compatible", such as IBM PC-DOS. Additionally, command shells such as 4DOS change the behavior and output of built-in commands like DIR.
It's much better to use the DOS interrupt (21h) to do things like this; that's why it's there. It's faster, guaranteed to work, and requires no temporary files.
Posts: 6,419
Threads: 74
Joined: Mar 2002
Quote:Relying on the command interpreter to do your dirty work is just asking for trouble imo...a DOS can have a different output for "DIR" and still be 100% "MS-DOS compatible", such as IBM PC-DOS. Additionally, command shells such as 4DOS change the behavior and output of built-in commands like DIR.
It's much better to use the DOS interrupt (21h) to do things like this; that's why it's there. It's faster, guaranteed to work, and requires no temporary files.
I swear I was gonna post exactly this :o
http://spike.scu.edu.au/~barry/interrupts.html
Posts: 1,956
Threads: 65
Joined: Jun 2003
Plasma and Nathan,
The DOS interrupt 21h thingy sounds good, but for someone who has never used it, would you please post the equivalent code to perform the same as: DIR filespec /B /A-D
and be able to retrieve the DIR list of filenames.
Thanks in advance.
*****
Posts: 1,752
Threads: 21
Joined: Jun 2002
Code: ' DIR$ replacement by Plasma
' QuickBASIC 4.x version
' Remember to start QB with the /L switch: QB /L
DEFINT A-Z
'$INCLUDE: 'QB.BI'
DECLARE FUNCTION DIR2$ (FileSpec$, Attr)
CONST ANYDIR = &H10
CONST ANYFILE = &H27
' Examples:
' To check for the existence of a file:
' IF LEN(DIR2$("C:\AUTOEXEC.BAT", ANYFILE)) THEN
' PRINT "The file exists"
' ELSE
' PRINT "The file does not exist"
' END IF
' To check for the existence of a directory:
' IF LEN(DIR2$("C:\WINDOWS", ANYDIR)) THEN
' PRINT "The directory exists"
' ELSE
' PRINT "The directory does not exist"
' END IF
' To generate a list of files:
'
' n$ = DIR2$("C:\*.*", ANYFILE)
' DO WHILE LEN(n$)
' PRINT n$
' n$ = DIR2$("", ANYFILE)
' LOOP
' To generate a list of directories:
'
' n$ = DIR2$("C:\*.*", ANYDIR)
' DO WHILE LEN(n$)
' PRINT n$
' n$ = DIR2$("", ANYDIR)
' LOOP
FUNCTION DIR2$ (FileSpec$, Attr) STATIC
'==========================================================================
' DIR$ replacement by Plasma (1.1)
'--------------------------------------------------------------------------
' Settings for Attr: (may be combined)
'
' &H40 Device
' &H20 Archive
' &H10 Directory
' &H8 Volume Label
' &H4 System File
' &H2 Hidden File
' &H1 Read-Only File
'==========================================================================
DIM DTA AS STRING * 44
FileSpecZ$ = FileSpec$ + CHR$(0)
DO
DIM Regs AS RegTypeX
Regs.ax = &H1A00
Regs.ds = VARSEG(DTA)
Regs.dx = VARPTR(DTA)
InterruptX &H21, Regs, Regs
IF FileSpecZ$ <> CHR$(0) THEN
Regs.ax = &H4E00
Regs.cx = Attr
Regs.ds = VARSEG(FileSpecZ$)
Regs.dx = SADD(FileSpecZ$)
ELSE
Regs.ax = &H4F00
END IF
InterruptX &H21, Regs, Regs
IF Regs.flags AND 1 THEN
DIR2$ = ""
EXIT FUNCTION
ELSE
RealAttr = ASC(MID$(DTA, 22, 1))
IF RealAttr AND Attr THEN
IF Attr > &H27 AND NOT RealAttr AND &H10 THEN
FileSpecZ$ = CHR$(0)
ELSE
Null = INSTR(31, DTA, CHR$(0))
DIR2$ = MID$(DTA, 31, Null - 31)
EXIT FUNCTION
END IF
ELSE
FileSpecZ$ = CHR$(0)
END IF
END IF
LOOP
END FUNCTION
Code: ' DIR$ replacement by Plasma
' QBASIC version
DEFINT A-Z
DECLARE FUNCTION DIR2$ (FileSpec$, Attr)
DECLARE SUB IntX (IntNum, InReg AS ANY, OutReg AS ANY)
TYPE RegTypeX
ax AS INTEGER
bx AS INTEGER
cx AS INTEGER
dx AS INTEGER
bp AS INTEGER
si AS INTEGER
di AS INTEGER
flags AS INTEGER
ds AS INTEGER
es AS INTEGER
END TYPE
CONST ANYDIR = &H10
CONST ANYFILE = &H27
' Examples:
' To check for the existence of a file:
' IF LEN(DIR2$("C:\AUTOEXEC.BAT", ANYFILE)) THEN
' PRINT "The file exists"
' ELSE
' PRINT "The file does not exist"
' END IF
' To check for the existence of a directory:
' IF LEN(DIR2$("C:\WINDOWS", ANYDIR)) THEN
' PRINT "The directory exists"
' ELSE
' PRINT "The directory does not exist"
' END IF
' To generate a list of files:
'
' n$ = DIR2$("C:\*.*", ANYFILE)
' DO WHILE LEN(n$)
' PRINT n$
' n$ = DIR2$("", ANYFILE)
' LOOP
' To generate a list of directories:
'
' n$ = DIR2$("C:\*.*", ANYDIR)
' DO WHILE LEN(n$)
' PRINT n$
' n$ = DIR2$("", ANYDIR)
' LOOP
FUNCTION DIR2$ (FileSpec$, Attr) STATIC
'==========================================================================
' DIR$ replacement by Plasma (1.1)
'--------------------------------------------------------------------------
' Settings for Attr: (may be combined)
'
' &H40 Device
' &H20 Archive
' &H10 Directory
' &H8 Volume Label
' &H4 System File
' &H2 Hidden File
' &H1 Read-Only File
'==========================================================================
DIM DTA AS STRING * 44
FileSpecZ$ = FileSpec$ + CHR$(0)
DO
DIM Regs AS RegTypeX
Regs.ax = &H1A00
Regs.ds = VARSEG(DTA)
Regs.dx = VARPTR(DTA)
IntX &H21, Regs, Regs
IF FileSpecZ$ <> CHR$(0) THEN
Regs.ax = &H4E00
Regs.cx = Attr
Regs.ds = VARSEG(FileSpecZ$)
Regs.dx = SADD(FileSpecZ$)
ELSE
Regs.ax = &H4F00
END IF
IntX &H21, Regs, Regs
IF Regs.flags AND 1 THEN
DIR2$ = ""
EXIT FUNCTION
ELSE
RealAttr = ASC(MID$(DTA, 22, 1))
IF RealAttr AND Attr THEN
IF Attr > &H27 AND NOT RealAttr AND &H10 THEN
FileSpecZ$ = CHR$(0)
ELSE
Null = INSTR(31, DTA, CHR$(0))
DIR2$ = MID$(DTA, 31, Null - 31)
EXIT FUNCTION
END IF
ELSE
FileSpecZ$ = CHR$(0)
END IF
END IF
LOOP
END FUNCTION
SUB IntX (IntNum, InReg AS RegTypeX, OutReg AS RegTypeX)
'==========================================================================
' IntX 1.0 by Plasma (4/29/2004)
' Use interrupts without CALL INTERRUPT or CALL ABSOLUTE
'==========================================================================
STATIC Version, VarptrSeg&, VarptrOff, IntAsm(), IntOff
IF VarptrSeg& = 0 THEN
DefSeg& = VARSEG(DefSeg$)
VarptrSeg& = DefSeg& - &H400
DO WHILE VarptrSeg& > 0
DEF SEG = VarptrSeg&
FOR i = &H9 TO &H3FF3
IF PEEK(i) = &H87 AND PEEK(i + 1) = &HDA AND PEEK(i + 2) = &H8B THEN
IF PEEK(i + 3) = &H46 AND PEEK(i + 4) = &H6 AND PEEK(i + 5) = &H88 THEN
IF PEEK(i + 6) = &H7 AND PEEK(i + 7) = &H8B AND PEEK(i + 8) = &H46 THEN
IF PEEK(i + 9) = &H8 AND PEEK(i + 10) = &H89 AND PEEK(i + 11) = &H47 THEN
IF PEEK(i + 12) = &H1 AND PEEK(i + 13) = &H92 THEN
VarptrOff = i - 9
Version = 45
EXIT DO
END IF
END IF
END IF
END IF
ELSEIF PEEK(i) = &HB9 AND PEEK(i + 1) = &H3 AND PEEK(i + 2) = &H0 THEN
IF PEEK(i + 6) = &H8D AND PEEK(i + 7) = &H76 AND PEEK(i + 8) = &H6 THEN
IF PEEK(i + 9) = &HA4 AND PEEK(i + 10) = &H46 AND PEEK(i + 11) = &HA5 THEN
VarptrOff = i + 12
Version = 71
EXIT DO
END IF
END IF
END IF
NEXT
VarptrSeg& = VarptrSeg& - &H3F0
LOOP
IF i = &H3FF4 THEN
VarptrSeg& = 0
EXIT SUB
END IF
IntAsm$ = "8E1F1E8B060E0050A100008B1E02008B0E04008B1606008B2E08008B360A"
IntAsm$ = IntAsm$ + "008B3E0C008E0612008E1E10009DCD001E53506658665B9C66"
IntAsm$ = IntAsm$ + "C1CB108EDB66C1EB10891E1000A3000066C1E8108906020089"
IntAsm$ = IntAsm$ + "0E040089160600892E080089360A00893E0C005889060E008C"
IntAsm$ = IntAsm$ + "0612009D071F61"
IF Version = 45 THEN
IntAsm$ = "5589E5601E069C8B5E08" + IntAsm$ + "5D31C0CA0400"
IntOff = 55
ELSE
IntAsm$ = "601E069C4F4F268B1D" + IntAsm$ + "5F5E5DCA0400"
IntOff = 54
END IF
DIM IntAsm(LEN(IntAsm$) / 4)
DEF SEG = VARSEG(IntAsm(0))
FOR i = 1 TO LEN(IntAsm$) STEP 2
POKE (i - 1) / 2, VAL("&H" + MID$(IntAsm$, i, 2))
NEXT
END IF
DEF SEG = VarptrSeg&
POKE VarptrOff + 0, &HEA
POKE VarptrOff + 1, 0
POKE VarptrOff + 2, 0
POKE VarptrOff + 3, VARSEG(IntAsm(0)) AND &HFF
POKE VarptrOff + 4, (VARSEG(IntAsm(0)) AND &HFF00&) \ &H100
DEF SEG = VARSEG(IntAsm(0))
POKE IntOff, IntNum
DIM Regs(10)
Regs(0) = InReg.ax
Regs(1) = InReg.bx
Regs(2) = InReg.cx
Regs(3) = InReg.dx
Regs(4) = InReg.bp
Regs(5) = InReg.si
Regs(6) = InReg.di
Regs(7) = InReg.flags
Regs(8) = InReg.ds
Regs(9) = InReg.es
RegSeg = VARSEG(Regs(0))
rc$ = VARPTR$(RegSeg)
OutReg.ax = Regs(0)
OutReg.bx = Regs(1)
OutReg.cx = Regs(2)
OutReg.dx = Regs(3)
OutReg.bp = Regs(4)
OutReg.si = Regs(5)
OutReg.di = Regs(6)
OutReg.flags = Regs(7)
OutReg.ds = Regs(8)
OutReg.es = Regs(9)
DEF SEG = VarptrSeg&
IF Version = 45 THEN
POKE VarptrOff + 0, &H55
POKE VarptrOff + 1, &H8B
POKE VarptrOff + 2, &HEC
POKE VarptrOff + 3, &HBB
POKE VarptrOff + 4, &H3
ELSE
POKE VarptrOff + 0, &H5F
POKE VarptrOff + 1, &H5E
POKE VarptrOff + 2, &H5D
POKE VarptrOff + 3, &HCA
POKE VarptrOff + 4, &H4
END IF
END SUB
Posts: 1,956
Threads: 65
Joined: Jun 2003
Wow, Plasma, thanks.
It looks complicated. I'll have to study it more.
BTW, where does the list of filenames go?
*****
Posts: 1,752
Threads: 21
Joined: Jun 2002
It's not really as bad as it looks. The QBasic version is more complicated because there is no call interrupt. (I could have just used call absolute, but I made that version for somebody at qbasic.com who didn't want to use call absolute or something...so basically IntX is just a call interrupt hack.)
Anyway, it works by setting the DTA to a local buffer, and then calling FindFirst and FindNext with the appropriate filename and attributes. This is how nearly all "professional" DOS applications generate their directory listings.
The list of filenames/directories isn't stored anywhere; it's generated by DOS as you request the next entry. If you want to store the list somewhere, you can always stick it in an array.
|