ID Number: Q68100
3.x 4.00 4.01
MS-DOS
Problem:
I have an application that needs to know where it was loaded from
and executed by MS-DOS. Specifically, it needs to know the complete
pathname of where the program actually resides in order to be able
to access associated program data files stored in the same directory
as the program.
Response:
The section 4.3, "The MS-DOS Program Segment," in the "Microsoft
MS-DOS Programmer's Reference," includes a brief explanation about a
data structure area that MS-DOS sets up in addition to the copy of the
parent's environment variables whenever it loads and executes a
program. The reference also states that programs can use this area to
determine where a program was loaded. Note that support for this
additional information added to the program's environment block began
with MS-DOS version 3.00.
This data structure includes the MS-DOS default supplied fully
qualified pathname of the program that owns the environment. A fully
qualified pathname (or filename) consists of the complete path from
the root directory, prefixed by a logical drive and followed by the
program name and extension. Thus, by locating this MS-DOS supplied
pathname string, a program can know where it was loaded and executed
from. Locating this fully qualified pathname is a simple task from
assembly language, but is even simpler for a Microsoft C language
program, since the C run-time places this fully qualified pathname in
string ARGV[0]. See the programming samples included below for the
exact methodology.
More Information:
When MS-DOS executes a program, it copies the parent's environment and
places the segment address of this copy at offset +2cH in the program
segment prefix (PSP). The environment is a series of ASCII strings,
each of which is terminated by a zero byte. The set of strings that
composes the environment itself is terminated by another zero byte.
Following the end of the environment is a set of initial arguments
that MS-DOS passes to a program. This set of arguments, or the data
structure, consists of a word count of the number of strings that
follow this environment and the set of ASCIIZ strings. This count is
normally one (but can be more), since MS-DOS also supplies the fully
qualified pathname of the program or process owning this environment
as the first ASCIIZ string in this data structure. Note that this
structure should not be confused with the unformatted parameter area
at offset +80H within the PSP area, which contains all the characters
typed after the command invocation on the MS-DOS command line.
C and Assembly Language Coding Examples
---------------------------------------
----------------------- ARGV.C ---------------------------------
/* This test program will display all program invocation */
/* DOS command line arguments. Note: C run-time ARGV[0] */
/* is the FULLY QUALIFIED PATHNAME. */
# include <stdio.h>
# include <stdlib.h>
main(int argc, char **argv)
{
int i=0;
while (i < argc)
printf("%s\n",argv[i++]);
}
----------------------- FINDP.ASM --------------------------------
TITLE TEST: DISPLAY FULLY QUALIFIED PATHNAME
Comment |
This test program only displays the FULLY QUALIFIED PATHNAME
which follows the program's environment string area.
Assemble source with MASM 5.1+ and LINK to .EXE format.
|
DOSSEG
.MODEL SMALL
.DATA
.CODE
START:
;ES=DS=PSP Segment. Get environment segment from PSP.
mov ax,es:[2ch]
mov es,ax
mov ds,ax ;set DS=ES
;Scan for end environment strings (find two null bytes)
;Use SCASB because don't know if will find even or odd aligned.
xor al,al ;looking for first null byte
xor di,di ;offset zero for environment seg
mov cx,08000h ;32K bytes max environment size
cld
scan_bytes:
repnz scasb ;scan for first zero byte
cmp byte ptr [di],0 ;Found first, test next byte
;for zero too.
jnz scan_bytes ;not zero, continue scan
;Display "fully qualified pathname" which follows end environment
;and is terminated by a null byte
add di,3 ;Adjust DI to point to beginning of
mov si,di ;ASCIIZ string by skipping over null
;byte+word count preceding string.
;display string via BIOS INT 10H, TTY
get_byte:
cld
lodsb
or al,al ;test for null terminating byte
jz done
mov ah,0eh ;BIOS INT 10H, TTY
int 10h
jmp short get_byte
done:
mov ax,4c00h ;MS-DOS INT 21H terminate program
int 21h
END START