_MAKING UNDOCUMENTED DOS CALLS FROM WINDOWS 3_ by Paul Chui [LISTING ONE] #define NOCOMM #include #include #include #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; }