Undocumented DOS From Protected-Mode Windows 3

Paul shows how to make undocumented DOS calls from Windows' standard and enhanced modes.


February 01, 1992
URL:http://www.drdobbs.com/windows/undocumented-dos-from-protected-mode-win/184408714

FEB92: UNDOCUMENTED DOS FROM PROTECTED-MODE WINDOWS 3

This article contains the following executables: SFEDIT.ARC

Paul is a software engineer at Claris and is the coauthor of the Turbo C++ DiskTutor (Osborne/McGraw-Hill). He can be reached through the DDJ office.


Under a true protected-mode system, an ill-behaved application cannot corrupt the rest of the system. Windows, however, is not a pure protected-mode environment. Windows lives on top of real-mode MS-DOS. So, how does Windows communicate with DOS, the BIOS, network drivers, and other real mode services that are unaware of protected mode? That--and how you go about making undocumented DOS calls--is the focus of this article.

To examine these concepts, I used Microsoft's QuickC for Windows to write a DLL that provides standard file dialog boxes similar to those found in Windows 3.1. I chose QuickC/Win largely because it is a Windows-hosted environment that provides the essentials of the Windows SDK.

Extending Windows

Hidden within Windows is a DOS Protected-Mode Interface (DPMI) compliant DOS extender. The DOS extender is an "invisible" layer between protected-mode applications and DOS. Protected-mode Windows applications can use the same real-mode services that DOS applications use. The Windows DOS extender accomplishes this by trapping each call to the system. When a Windows program calls DOS, Windows handles the call, switches the processor to real mode, and relays the call to DOS. When the call is complete, the extender code switches back to protected mode and returns control to the application. (See the accompanying text box, "DPMI and the Windows DOS Extender.")

Transparent to the application, the Windows DOS extender must also translate protected-mode buffers to real-mode buffers. For example, to call the DOS Open File function (INT 21h, Function 3Dh), the DOS extender first copies the protected-mode filename to a real-mode addressable buffer before issuing the DOS call. This transparency allows unmodified standard C library functions such as fopen to work in Windows applications.

Standard File Dialog Boxes

As Windows users know, a file dialog box allows the user to select a file from a list of available files, enter a filename directly into an edit box, and to switch drives and directories. Windows' lack of a standardized interface element for selecting files has always been a mystery to me. Unlike the Macintosh, which includes a Standard File Package, each Windows 3.0 application must implement its own version of a file dialog box. (Note that Windows 3.1 will include standard file dialog boxes in COMMDLG.DLL.)

One benefit of standard file dialog boxes is shared code. The Open File and File Save As dialog boxes are common to a large class of applications, so it is natural to put them in a Dynamic Link Library. In a DLL, these dialog boxes are not statically bound to any one application, but are available to any application that wants to provide a user interface for specifying filenames.

SFILEDLG

SFILEDLG is a DLL containing a File Open and a File Save As dialog box. (Because of space considerations, SFILEDLG and other listings are provided electronically. See "Availability" on page 3 for details.) It uses an owner-draw list box to draw bitmaps representing different drive types. The exported function, SFGetFileDlg, is the interface to these dialog boxes. SFileDlgProc, the callback function, processes the Windows messages. When the DLL is loaded, LibMain creates the resources used by the dialog boxes. These resources are discarded when the DLL is removed from the system.

Undocumented DOS Calls

The LibMain initialization routine in SFILEDLG calls GetDriveTypeX to determine the type of drive for each possible drive in the system. The Windows API function GetDriveType recognizes floppy, hard, and network drives. GetDriveTypeX extends GetDriveType by also recognizing RAM, CD-ROM, and SUBST drives. Unfortunately, neither Windows nor DOS provides a one-stop solution to recognize all types of drives. This solution requires a combination of documented and undocumented DOS.

Recall that the Windows DOS extender transparently reflects each DOS call for protected-mode Windows applications. The DOS extender traps many documented system calls. To make undocumented DOS calls, however, Windows applications need to employ their own protected- to real-mode interface.

To make undocumented DOS calls, GetDriveTypeX uses the DPMI Simulate Real-Mode Interrupt facility (INT 31h, Function 0300h), The function DPMI_RealModeInt in Listing One (page 102) works similar to the int86 function supplied by C libraries. The comments in the header of DPMI_RealModeInt document the protocol for DPMI's Simulate Real-Mode Interrupt facility.

More Details.

CD-ROM Drives

The CD-ROM extensions, MSCDEX, are documented extensions of DOS. These extensions are translated by the Windows DOS extender, so there is no need to provide a real-mode translation. To detect a CD-ROM drive, isCDROM calls INT 2Fh, Function 15OBh. The CX register specifies the drive number, which is 0 for drive A:, 1 for drive B:, 2 for drive C:, and so on. If a CD-ROM drive is detected, the function returns ABABh in register BX.

SUBST

The DOS command SUBST is a TSR that associates a drive letter with a directory. Issuing the DOS command SUBST W: C:\WINDOWS maps all requests for drive W: to the C:\WINDOWS directory. Drives created by SUBST can be recognized using the Current Directory Structure (CDS), an undocumented DOS structure. (See the textbox entitled "The DOS Lists of Lists.") This structure contains a bit flag that indicates whether the drive is a SUBST drive.

In Listing One, isSubstDrive calls the function GetCDS to obtain a real-mode pointer to the CDS array. GetCDS, calls the undocumented DOS function INT 21h, Function 5200h (Get DOS List of lists). Because INT 21h, Function 5200h is not serviced by the Windows DOS extender a Windows application must never call it directly. GetCDS uses DMI_RealModeInt as the interface to this undocumented DOS function. On return, the register pair ES:BX holds a real-mode address of the lists of lists. This address must be converted to a protected-mode address with PMODE_ADDR. PMODE_ADDR takes a selector created by the Windows API function AllocSelector, a real-mode address, and returns a protected-mode address.

From the protected-mode address of the List of Lists, GetCDS finds the real-mode address of the CDS for the specified drive. Before this address can be used, isSubstDrive calls on PMODE_ADDR again to convert it to a protected-mode address. Finally, the flag field in the CDS is checked to determine if the drive is a SUBST drive.

RAM Drives

Like SUBST drives, there is no official method for detecting RAM drives. The function isRamDrive uses the assumption that RAM drives have only one File Allocation Table (FAT) instead of the usual two. If a drive is not recognized by any of the above drive types, GetDriveTypeX tries isRamDrive to see if it has only one FAT. The CDS contains a pointer to the drive's Drive Parameter Block (DPB), which holds the number of FATs used by the drive. Like the List of Lists, the CDS is an internal DOS structure and thus contains real-mode pointers. Again, a conversion to protected-mode pointer using PMODE-ADDR is required before the DPB can be dereferenced. (Note: The RAM drive detection method used by the Windows File Manager appears to check the disk volume label for the string "MS-RAMDRIVE.")

True Pathnames

Network and SUBST drives are aliases to another device or pathname. LibMain calls GetCanonicalPath to resolve the true paths of each drive. GetCanonicalPath calls the undocumented DOS INT 21h Function 60h for this purpose. DPMI_RealModeInt is called to invoke INT 21h Function 60h, which asks for a pointer to a relative path string in DS:SI and returns the canonical fully qualified string in ES:DI. These pointers must be real-mode addressable. GetCanonicalPath uses the Windows API function GlobalDOSAlloc to create this buffer. GlobalDOSAlloc returns a protected-mode selector in the high word and a real-mode segment in the low word of a buffer in the first megabyte of RAM. This buffer is used in GetCanonicalPath to transfer area for the real-mode interrupt. The transfer buffer is returned to the precious real-mode memory pool using GlobalDOSFree.

Real-Mode Buffers

SFILEDLG demonstrates two cases of a real- to protected-mode buffer transfer. These solutions can be applied more generally to accessing any absolute real-mode address such as TSRs, network areas, BIOS data area, and so on. In GetDriveTypeX, the protected-mode application must access existing buffers (DOS List of Lists, Current Directory Structure, Drive Parameter Block) created by real-mode programs. GetDriveTypeX calls AllocSelector and SetSelectorBase to map these buffers into the protected-mode address space. In Get CanonicalPath, the interrupt asks the calling routine for a buffer to fill. GlobalDOSAlloc provides the real-mode addressable buffer and a selector the protected-mode program uses to access it.

Also, the file dialog boxes in SFILEDLG keeps track of the current filename and file type. Typically, this data would be kept in static or global variables. However, precautions must be taken with static and global data in DLLs. At most, one instance of a DLL is loaded by Windows, even if many applications are using it. Consequently, global and static data are stored in a DLL's single data segment, which is shared among all applications that call it.

To associate a separate data buffer with each instance of a file dialog box, SFILEDLG uses window properties. A property list contains a list of words, indexed by strings. Each window may have its own property list. Unlike window extra bytes, properties may be assigned to a window even if that window was not registered by the application. SFILEDLG allocates a global SFDlg structure containing the dialog state. The handle to this structure is then placed in the dialog box's property list.

Using SFILEDLG

SFEDIT (provided electronically) is a text editor that uses SFILEDLG.DLL for selecting filenames for opening and saving. The application itself is not particularly interesting; the editor is a multiline EDIT control which provides no additional functionality over Windows' own NOTEPAD. But SFEDIT shows how easily SFIELDLG.DLL can be incorporated into any application needing file dialog boxes.

More Details.

Also provided electronically is SFILEDLG.MAK, a QuickC for Windows 1.0 project file for making SFILEDLG.DLL and the bitmap resource files referenced in SFILEDLG.DLL. QuickC/Win will automatically create an import library, SFILEDLG.LIB, which is used by SFEDIT.MAK and linked with the application SFEDIT.EXE. (With minor modifications, SFILEDLG.DLL and SFEDIT.EXE can be created with Microsoft C or Borland C++.)

A Word About Tools

QuickC for Windows offers a less painful entry into the world of Windows development than Microsoft C 6.0 and the Windows Software Development Kit. QuickC/Win offers an integrated editor, compiler, and debugger in a completely Windows-hosted environment. This is undoubtedly a more convenient environment than the DOS-hosted Microsoft C 6.x Programmer's WorkBench. Furthermore, QuickC/Win is packaged with the essential elements of the Windows SDK. A dialog editor, image editor, and CASE tool is included. Code-View for Windows is not included, but I found the integrated Windows-hosted debugger to be generally very capable. However, when debugging user-interface code, QuickC/Win has a tendency to grab input focus from the application being debugged. Two other SDK utilities that I missed in QuickC/Win are SPY and HEAPWALKER, which Microsoft is now selling separately.

QuickC/Win is an evolutionary tool for Windows software development. It is not Visual Basic for C programmers and it doesn't compile C++ code. Microsoft has revised its QuickC for DOS compiler and packaged it with existing SDK tools and documentation. It's a good package, and will entice many users to delete Microsoft C 6.0 and the Windows SDK from their hard drives.

References

Petzold, Charles. Programming Windows. Redmond, Wash.: Microsoft Press, 1990.

Schulman, Andrew, Raymond J. Michels, Jim Kyle, Tim Paterson, David Maxey, and Ralf Brown. Undocumented DOS. Reading, Mass.: Addison-Wesley, 1990.

DOS Protected Mode Interface Specification, Santa Clara, Calif.: Intel, 1991.

Windows INT 21h and NetBios Support for DPMI, Redmond, Wash.: Microsoft Press, 1990.

DPMI and the Windows DOS Extender

The DOS Protected-Mode Interface (DPMI) allows protected-mode programs to access DOS by specifying a set of low-level functions that manage extended memory and call real-mode programs. The services provided by DPMI are not transparent as in the case of DOS extenders. In fact, DPMI is not principally designed to be called by applications. The DPMI "client" is intended to be DOS extenders, such as the one in Windows. The DOS extender is responsible for translating protected-mode system calls for the application.

Generally, Windows will translate any software interrupts that are register based (that is, don't pass pointers, stack parameters, and use segment registers). The reason is that, in protected-mode Windows, segment registers are "selectors", which contain offsets into the Local Descriptor Table (LDT). The actual linear address of the data segment is stored in the LDT.

Most DOS functions work under Windows 3.0; even those that use pointers or segment registers. Windows provides the necessary protected-mode pointer translations for DOS functions. However, the Windows DOS extender either does not support or supports in limited fashion the DOS interrupts shown in Figure 1.

Figure 1: The Windows DOS Extender either does not support or supports in limited fashion these DOS interrupts.


  INT       Description
  -------------------------------------

  20h       Terminate Program
  25h       Absolute Disk Read
  26h       Absolute Disk Write
  27h       Terminate And Stay Resident

  INT 21h functions not supported
  Function  Description
  -------------------------------------

  00h       Terminate Process
  0Fh       Open File with FCB
  10h       Close File with FCB
  14h       Sequential Read
  15h       Sequential Write
  16h       Create File with FCB
  21h       Random Read
  22h       Random Write
  23h       Get File Size
  24h       Set Relative Record
  27h       Random Block Read
  28h       Random block Write

  INT 21h functions supported with restrictions
  Function  Description
  -----------------------------------------------------------------------

  21h, 35h  Set and Get Interrupt Vector
             Software interrupts issued in real mode are not reflected to
             the protected mode interrupt handlers, except for INT 23h,
             INT 24h, and INT 1Ch.
  44h       IOCTL
             Subfunctions 02h, 03h, 04h,, and 05h, used to receive data
             from and send data to a device; the transfer buffer must be
             less than 4K unless it is below the 1-Mbyte boundary.
             Subfunction OCh minor code 4Ah, 4Ch, 4Dh, 6Ah, and 6Bh; the
             code-page functions are not supported.
  38h, 65h  Get Country Data and Extended Country
             DWORD pointers returned by these functions are real-mode
             addresses.

Unlike extenders, which try to be fully DOS compatible, Windows does not translate many undocumented DOS functions. If necessary, a combination of direct DPMI calls and Windows segment functions may be used. Windows 3.0 supports version 0.9 of the DPMI specification. (The DPMI 1.0 specification can be obtained from the Intel Literature Center, P.O. Box 58065, Santa Clara, CA 95052, or by calling for the Intel Reference Literature Packet # JP 26, 800-548-4725.)

Microsoft recommends that Windows applications not call any DPMI functions other than those listed in Figure 2. The Windows kernel contains a number of functions that may be used instead of calling DPMI directly. Not all of these functions are documented. Some of the more useful ones, along with their equivalent DPMI functions, are listed in Figure 3.

Figure 2: DPMI functions that may be safely called by a Windows application

  DPMI (INT 31h)
  Functions      Description
  -------------------------------------------------------------

  0200h          Get Real-Mode Interrupt Vector
  0201h          Set Real-Mode Interrupt Vector
  0300h          Simulate Real-Mode Interrupt
  0301h          Call Real-Mode Procedure with Far Return Frame
  0302h          Call Real-Mode Procedure with IRET Frame
  0303h          Allocate Real-Mode Call-Back Address
  0304h          Free Real-Mode Call-Back Address

Figure 3: Subset of DPMI functions supported within the Windows API

  DMPI (INT 31h)
  Functions      Decription                  Windows API Function
  ---------------------------------------------------------------

  0000h          Allocate Local Descriptor   AllocSelector
  0001h          Free Local Descriptor       FreeSelector
  0006h          Get Segment Base Address    GetSelectorBase
  0007h          Set Segment Base Address    SetSelectorBase
  0008h          Set Segment Limit           SetSelectorLimit
  0100h          Allocate DOS Memory Block   GlobalDOSAlloc
  0101h          Free DOS Memory Block       GlobalDOSFree

The DOS List of Lists

MS-DOS maintains a list of system variables near the beginning of its kernel's data segment. This undocumented area, coined the "DOS List of Lists," is a gateway for accessing many other undocumented structures of DOS. Among other secrets, the List of Lists contains: the address of the first memory control block; information on the BUFFERS and LASTDRIVE commands in CONFIG.SYS; and the address of the System File Tables, which maintain the state of open files in the system. A pointer to the List of Lists can be obtained by calling INT 21h, Function 52h. The address of the List of Lists is returned in the ES:BX register pair.

Among the undocumented structures in the List of Lists is the address for the Current Directory Structure (CDS), located at offset 23. The CDS was introduced as part of the network enhancements made in DOS 3.0. In addition to working with network files, the CDS is used for manipulating foreign file systems. There is one CDS for each possible drive on the system. For example, if LASDRIVE = Z is set in the CONFIG.SYS file,then DOS will maintain an array of 26 Current Directory Structures. The size of each CDS varies depending on the version of DOS. In DOS 3.x, the size of the CDS is 81 bytes. From DOS 4.0 and up, the size of the CDS is 88 bytes. But this is an undocumented internal data structure, so the size and the contents of the CDS are not guaranteed.

--P.C.


_MAKING UNDOCUMENTED DOS CALLS FROM WINDOWS 3_
by Paul Chui


[LISTING ONE]


#define NOCOMM
#include <windows.h>
#include <dos.h>
#include <memory.h>
#include "getdrvx.h"

#define CDS_SUBST (0x1000)

#define DOS3_CDS_SIZE  81
#define DOS4_CDS_SIZE  88
#define CDS_PATH       0x00
#define CDS_FLAGS      0x43
#define CDS_DPB        0x45

#define DPB_FATS       8

#define MK_FP(seg,ofs)  ((void far *) (((unsigned long)(seg) << 16) | (unsigned)(ofs)))
#define _DS             GetDS()

/* Documented Windows functions not declared in WINDOWS.H */
DWORD FAR PASCAL GlobalDosAlloc(DWORD dwBytes);
WORD  FAR PASCAL GlobalDosFree(WORD wSelector);

/* Undocumented Windows functions */
VOID FAR PASCAL SetSelectorBase(WORD wSelector, DWORD dwBase);
VOID FAR PASCAL SetSelectorLimit(WORD wSelector, DWORD dwLimit);

/* Global Variables */
WORD _wSelector;                // a data selector

/* Types */
// DOS list of lists structure (for DOS 3.1 or better)
typedef struct {
    BYTE x[22];                 // (don't care)
    BYTE far* cds;
    BYTE xx[7];                 // (don't care)
    BYTE lastdrive;
    // ... the rest of DOS List of Lists
} DOSLISTS;

// DPMI real-mode call structure
typedef struct {
    DWORD edi, esi, ebp, reserved, ebx, edx, ecx, eax;
    WORD  flags, es, ds, fs, gs, ip, cs, sp, ss;
} REALMODECALL;


int GetDosVersionMajor(void)
{
    int     dosver;

    _asm    mov ah, 0x30;
    _asm    int 0x21;
    _asm    mov dosver, ax;

    return (BYTE)dosver;
}

unsigned GetDS(void)
{
    _asm    mov ax, ds;
    return;                 // return value in AX
}


/*************************************************************************
    BOOL DPMI_RealModeInt(int intno, REALMODECALL far* r)

    PURPOSE: DPMI simulate real-mode interrupt function.  The
    following is an excerpt from the DPMI Specification:

    PARAMETERS:
        int   intno             real-mode interrupt to simulate
        REALMODECALL far* r

    RETURNS: TRUE if interrupt is successful, FALSE on error

    NOTES: The following is an excerpt from INTEL's DPMI specs:

      To Call

           AX = 0300h
           BL = Interrupt number
           BH = Flags
                Bit 0  = 1 resets the interrupt controller and A20
                line
                Other flags reserved and must be 0
           CX =  Number of  words to  copy from  protected mode to
                real-mode stack
           ES:(E)DI = Selector:Offset of real-mode call structure

      Returns

           If function was successful:
           Carry flag is clear.
           ES:(E)DI =  Selector:Offset of modified real-mode call
                structure

           If function was not successful:
           Carry flag is set.
*************************************************************************/
BOOL DPMI_RealModeInt(int intno, REALMODECALL far* r)
{
    intno &= 0x00FF;        // reset high byte (flags)

    _asm {
        push    di
        mov     bx, intno   // flags | real-mode interrupt to call
        mov     cx, 0       // don't copy anything from protected mode stack
        les     di, r       // es:di -> real-mode call structure
        mov     ax, 0x0300  // DPMI simulate real-mode interrupt function
        int     0x31
        pop     di
        jc      error
    }
    return  TRUE;

error:
    return  FALSE;
}

/**************************************************************************
    BYTE far* PMODE_ADDR(WORD wSel, BYTE far* RMODE_ADDR)

    PURPOSE: Get the Current Directory Structure

    PARAMETERS:
        WORD wSel               A valid selector
        BYTE far* RMODE_ADDR    A real mode address

    RETURNS: a protected-mode address of RMODE_ADDR

    NOTES: The base address of selector wSel is set to the segment of
    RMODE_ADDR.
**************************************************************************/
BYTE far* PMODE_ADDR(WORD wSel, BYTE far* RMODE_ADDR)
{
    SetSelectorBase(wSel, (DWORD) FP_SEG(RMODE_ADDR) << 4);
    return  MK_FP(wSel, FP_OFF(RMODE_ADDR));
}

/**************************************************************************
    BYTE far* GetCDS(int nDrive)

    PURPOSE: Get the Current Directory Structure

    PARAMETERS: int nDrive          The drive to retrieve

    RETURNS: a real-mode pointer to the Current Directory Structure of
    drive nDrive.
***************************************************************************/
BYTE far* GetCDS(int nDrive)
{
    DOSLISTS far*   doslists;
    REALMODECALL    r;

    // Get DOS list of lists (INT 21h, Function 52h)
    _fmemset(&r, 0, sizeof(REALMODECALL));
    r.eax = 0x5200;
    if (!DPMI_RealModeInt(0x21, &r))
        return  NULL;

    // Pointer to DOS list of lists is returned in ES:BX
    doslists = (DOSLISTS far*)
        PMODE_ADDR( _wSelector, MK_FP(r.es, LOWORD(r.ebx)) );

    if (GetDosVersionMajor() < 4)
       return   doslists->cds + (nDrive * DOS3_CDS_SIZE);
    else
       return   doslists->cds + (nDrive * DOS4_CDS_SIZE);
}

/*************************************************************************
    BOOL isCDROM(int nDrive)

    PURPOSE: Tests if nDrive is a CD ROM drive

    PARAMETERS: int nDrive          The drive to test

    RETURNS: TRUE if nDrive is a CD ROM
 **************************************************************************/
BOOL isCDROM(int nDrive)
{
    WORD     saveAX, saveBX;

    _asm     mov cx, nDrive;
    _asm     mov ax, 0x150B;
    _asm     int 0x2F;
    _asm     mov saveAX, ax;
    _asm     mov saveBX, bx;

    return   saveBX == 0xADAD && saveAX != 0;
}


/**************************************************************************
    BOOL isSubstDrive(int nDrive)

    PURPOSE: Tests for drives created using the DOS SUBST command

    PARAMETERS: int nDrive          The drive to test

    RETURNS: TRUE if nDrive is a SUBST drive
***************************************************************************/
BOOL isSubstDrive(int nDrive)
{
    BYTE far*   cds;        // Current Directory Structure
    WORD        cds_flags;

    cds = PMODE_ADDR(_wSelector, GetCDS(nDrive));
    cds_flags = *(WORD far*)(cds+CDS_FLAGS);
    if (cds_flags &  (CDS_SUBST))
        return  TRUE;

    return  FALSE;
}

/**************************************************************************
    BOOL isRamDrive(int nDrive)

    PURPOSE: Tests for Ram drives

    PARAMETERS: int nDrive          The drive to test

    RETURNS: TRUE if nDrive is a ram drive

    NOTES: This function tests to see if the drive has only one FAT.  It
    is assumed that if this is true, then the drive must be a RAM drive.
***************************************************************************/
BOOL isRamDrive(int nDrive)
{
    BYTE far*   cds;    // Current Directory Structure
    BYTE far*   dpb;    // Drive Parameter Block

    cds = PMODE_ADDR(_wSelector, GetCDS(nDrive));

    dpb = *(BYTE far* far*)(cds+CDS_DPB);
    dpb = PMODE_ADDR(_wSelector, dpb);

    if (*(dpb+DPB_FATS) == 1)
        return  TRUE;

    return  FALSE;
}

/***************************************************************************
    WORD FAR PASCAL GetDriveTypeX(int nDrive)

    PURPOSE: Determines drive type

    PARAMETERS:
        int   nDrive            The drive number. 0 = A:, 1 = B:,
                                2 = C:, 3 = D:, ...
    RETURNS:
        DRIVE_UNKNOWN           Unknown drive type
        DRIVE_NOTEXIST          Drive does not exist
        DRIVE_REMOVE            Removable (floppy) drive
        DRIVE_FIXED             Fixed (hard) drive
        DRIVE_REMOTE            Remote (network) drive
        DRIVE_CDROM             CD ROM drive
        DRIVE_RAM               Ram drive
        DRIVE_SUBST             SUBST drive
***************************************************************************/
WORD FAR PASCAL GetDriveTypeX(int nDrive)
{
    WORD    wDriveType;

    // Get a new selector, using the current DS as the prototype
    _wSelector = AllocSelector(_DS);
    SetSelectorLimit(_wSelector, 0xFFFF);

    wDriveType = GetDriveType(nDrive);

    if (isCDROM(nDrive))
        wDriveType = DRIVE_CDROM;
    else
    if (wDriveType != DRIVE_REMOTE && isSubstDrive(nDrive))
        wDriveType = DRIVE_SUBST;
    else
    if (isRamDrive(nDrive))
        wDriveType = DRIVE_RAM;

    FreeSelector(_wSelector);
    return  wDriveType;
}

/***************************************************************************
    BOOL FAR PASCAL GetCanonicalPath(LPSTR lpszRelPath, LPSTR lpszTruePath)

    PURPOSE: Resolve path string to canonical path string

    PARAMETERS:
        LPSTR   lpszRelPath     Relative path string or directory name
        LPSTR   lpszTruePath    Destination for canonical fully qualified
                                path
    RETURNS:
        TRUE if successful
***************************************************************************/
BOOL FAR PASCAL GetCanonicalPath(LPSTR lpszRelPath, LPSTR lpszTruePath)
{
    BOOL        retval;
    DWORD       dw;
    WORD        wSelector;
    WORD        wSegment;
    LPSTR       lpszDosBuf;
    REALMODECALL    r;

    dw = GlobalDosAlloc(128);
    if (dw == NULL)
        return FALSE;

    wSelector = LOWORD(dw);
    wSegment  = HIWORD(dw);

    lpszDosBuf = MK_FP(wSelector, 0);
    _fmemcpy(lpszDosBuf, lpszRelPath, 128);

    r.eax = 0x6000;
    r.ds  = wSegment;
    r.esi = 0;
    r.es  = wSegment;
    r.edi = 0;

    retval = DPMI_RealModeInt(0x21, &r);

    _fmemcpy(lpszTruePath, lpszDosBuf, 128);
    GlobalDosFree(wSelector);

    return  retval;
}


Copyright © 1992, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.