/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WKERNEL.C
 *  WOW32 16-bit Kernel API support
 *
 *  History:
 *  Created 07-Mar-1991 by Jeff Parsons (jeffpar)
--*/


#include "precomp.h"
#pragma hdrstop

MODNAME(wkernel.c);


ULONG FASTCALL WK32WritePrivateProfileString(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ pszSection;
    PSZ pszKey;
    PSZ pszValue;
    PSZ pszFilename;
    register PWRITEPRIVATEPROFILESTRING16 parg16;
    BOOL fIsWinIni;
    CHAR szLowercase[MAX_PATH];

    GETARGPTR(pFrame, sizeof(WRITEPRIVATEPROFILESTRING16), parg16);
    GETPSZPTR(parg16->f1, pszSection);
    GETPSZPTR(parg16->f2, pszKey);
    GETPSZPTR(parg16->f3, pszValue);
    GETPSZPTR(parg16->f4, pszFilename);

    UpdateDosCurrentDirectory(DIR_DOS_TO_NT);

    strcpy(szLowercase, pszFilename);
    WOW32_strlwr(szLowercase);

    fIsWinIni = IS_WIN_INI(szLowercase);

    // Trying to install or change default printer to fax printer?
    if (fIsWinIni &&
        pszSection &&
        pszKey &&
        pszValue &&
        !WOW32_stricmp(pszSection, szDevices) &&
        IsFaxPrinterWriteProfileString(pszSection, pszKey, pszValue)) {

        ul = TRUE;
        goto Done;
    }

    ul = GETBOOL16( WritePrivateProfileString(
             pszSection,
             pszKey,
             pszValue,
             pszFilename
             ));

    if( ul != 0 &&
        fIsWinIni &&
        IS_EMBEDDING_SECTION( pszSection ) &&
        pszKey != NULL &&
        pszValue != NULL ) {

        UpdateClassesRootSubKey( pszKey, pszValue);
    }

Done:
    FREEPSZPTR(pszSection);
    FREEPSZPTR(pszKey);
    FREEPSZPTR(pszValue);
    FREEPSZPTR(pszFilename);
    FREEARGPTR(parg16);

    return ul;
}


ULONG FASTCALL WK32WriteProfileString(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ   pszSection;
    PSZ   pszKey;
    PSZ   pszValue;
    register PWRITEPROFILESTRING16 parg16;

    GETARGPTR(pFrame, sizeof(WRITEPROFILESTRING16), parg16);
    GETPSZPTR(parg16->f1, pszSection);
    GETPSZPTR(parg16->f2, pszKey);
    GETPSZPTR(parg16->f3, pszValue);

    // For WinFax Lite install hack. Bug #126489  See wow32fax.c
    if(!gbWinFaxHack) { 

        // Trying to install or change default printer to fax printer?
        if (pszSection &&
            pszKey &&
            pszValue &&
            !WOW32_stricmp(pszSection, szDevices) &&
            IsFaxPrinterWriteProfileString(pszSection, pszKey, pszValue)) {
    
            ul = TRUE;
            goto Done;
        }
 
    } else {
        IsFaxPrinterWriteProfileString(pszSection, pszKey, pszValue);
    }

    ul = GETBOOL16( WriteProfileString(
             pszSection,
             pszKey,
             pszValue
             ));

    if( ( ul != 0 ) &&
        IS_EMBEDDING_SECTION( pszSection ) &&
        ( pszKey != NULL ) &&
        ( pszValue != NULL ) ) {
        UpdateClassesRootSubKey( pszKey, pszValue);
    }

Done:
    FREEPSZPTR(pszSection);
    FREEPSZPTR(pszKey);
    FREEPSZPTR(pszValue);
    FREEARGPTR(parg16);
    return ul;
}


ULONG FASTCALL WK32GetProfileString(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ pszSection;
    PSZ pszKey;
    PSZ pszDefault;
    PSZ pszReturnBuffer;
    UINT cchMax;
#ifdef FE_SB
    PSZ pszTmp = NULL;
#endif // FE_SB
    register PGETPROFILESTRING16 parg16;

    GETARGPTR(pFrame, sizeof(GETPROFILESTRING16), parg16);
    GETPSZPTR(parg16->f1, pszSection);
    GETPSZPTR(parg16->f2, pszKey);
    GETPSZPTR(parg16->f3, pszDefault);
    ALLOCVDMPTR(parg16->f4, parg16->f5, pszReturnBuffer);
    cchMax = INT32(parg16->f5);
#ifdef FE_SB
    //
    // For those applications that expect sLongDate contains
    // windows 3.1J picture format string.
    //
    if (GetSystemDefaultLangID() == 0x411 &&
            !lstrcmpi(pszSection, "intl") && !lstrcmpi(pszKey, "sLongDate")) {

        pszTmp = pszKey;
        pszKey = "sLongDate16";
    }
#endif // FE_SB

    if (IS_EMBEDDING_SECTION( pszSection ) &&
        !WasSectionRecentlyUpdated() ) {
        if( pszKey == NULL ) {
            UpdateEmbeddingAllKeys();
        } else {
            UpdateEmbeddingKey( pszKey );
        }
        SetLastTimeUpdated();

    } else if (pszSection &&
               pszKey &&
               !WOW32_stricmp(pszSection, szDevices) &&
               IsFaxPrinterSupportedDevice(pszKey)) {

        ul = GETINT16(GetFaxPrinterProfileString(pszSection, pszKey, pszDefault, pszReturnBuffer, cchMax));
        goto FlushAndReturn;
    }

    ul = GETINT16(GetProfileString(
             pszSection,
             pszKey,
             pszDefault,
             pszReturnBuffer,
             cchMax));


    //
    // Win3.1/Win95 compatibility:  Zap the first trailing blank in pszDefault
    // with null, but only if the default string was returned.  To detect
    // the default string being returned we need to ignore trailing blanks.
    //
    // This code is duplicated in thunks for GetProfileString and
    // GetPrivateProfileString, update both if you make changes.
    //

    if ( pszDefault && pszKey )  {

        int  n, nLenDef;

        //
        // Is the default the same as the returned string up to any NULLs?
        //
        nLenDef = 0;
        n=0;
        while (
            (pszDefault[nLenDef] == pszReturnBuffer[n]) &&
            pszReturnBuffer[n]
            ) {
            
            n++;
            nLenDef++;
        }
        
        //
        // Did we get out of the loop because we're at the end of the returned string?
        //
        if ( '\0' != pszReturnBuffer[n] ) {
            //
            // No.  The strings are materially different - fall out.
            //
        }
        else {
            //
            // Ok, the strings are identical to the end of the returned string.
            // Is the default string spaces out to the end?
            //
            while ( ' ' == pszDefault[nLenDef] ) {
                nLenDef++;
            }
            
            //
            // The last thing was not a space.  If it was a NULL, then the app
            // passed in a string with trailing NULLs as default.  (Otherwise
            // the two strings are materially different and we do nothing.)
            //
            if ( '\0' == pszDefault[nLenDef] ) {
                
                char szBuf[4];  // Some random, small number of chars to get.
                                // If the string is long, we'll get only 3 chars
                                // (and NULL), but so what? - we only need to know if
                                // we got a default last time...
                
                //
                // The returned string is the same as the default string
                // without trailing blanks, but this might be coincidence,
                // so see if a call with empty pszDefault returns anything.
                // If it does, we don't zap because the default isn't
                // being used.
                //

                if (0 == GetProfileString(pszSection, pszKey, "", szBuf, sizeof(szBuf))) {

                    //
                    // Zap first trailing blank in pszDefault with null.
                    //

                    pszDefault[ul] = 0;
                    FLUSHVDMPTR(parg16->f3 + ul, 1, pszDefault + ul);
                }
            }
        }
    }


FlushAndReturn:
#ifdef FE_SB
    if ( pszTmp )
        pszKey = pszTmp;

    if (CURRENTPTD()->dwWOWCompatFlagsFE & WOWCF_FE_USEUPPER) { // for WinWrite
        if (pszSection && !lstrcmpi(pszSection, "windows") &&
             pszKey && !lstrcmpi(pszKey, "device")) {
            CharUpper(pszReturnBuffer);
        }
        if (pszSection && !lstrcmpi(pszSection, "devices") && pszKey) {
            CharUpper(pszReturnBuffer);
        }
    }
#endif // FE_SB
    FLUSHVDMPTR(parg16->f4, (ul + (pszSection && pszKey) ? 1 : 2), pszReturnBuffer);
    FREEPSZPTR(pszSection);
    FREEPSZPTR(pszKey);
    FREEPSZPTR(pszDefault);
    FREEVDMPTR(pszReturnBuffer);
    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WK32GetPrivateProfileString(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ pszSection;
    PSZ pszKey;
    PSZ pszDefault;
    PSZ pszReturnBuffer;
    PSZ pszFilename;
    UINT cchMax;
#ifdef FE_SB
    PSZ pszTmp = NULL;
#endif // FE_SB
    register PGETPRIVATEPROFILESTRING16 parg16;
    CHAR szLowercase[MAX_PATH];

    GETARGPTR(pFrame, sizeof(GETPRIVATEPROFILESTRING16), parg16);
    GETPSZPTR(parg16->f1, pszSection);
    GETPSZPTR(parg16->f2, pszKey);
    GETPSZPTR(parg16->f3, pszDefault);
    ALLOCVDMPTR(parg16->f4, parg16->f5, pszReturnBuffer);
    GETPSZPTR(parg16->f6, pszFilename);

    // PC3270 (Personal communications): while installing this app it calls
    // GetPrivateProfileString (sectionname, NULL, defaultbuffer, returnbuffer,
    // cch = 0, filename). On win31 this call returns relevant data in return
    // buffer and corresponding size as return value. On NT, since the
    // buffersize(cch) is '0' no data is copied into the return buffer and
    // return value is zero which makes this app abort installation.
    //
    // So restricted compatibility:
    //   if above is the case set
    //      cch = 64k - offset of returnbuffer;
    //
    // A safer 'cch' would be
    //      cch = GlobalSize(selector of returnbuffer) -
    //                                (offset of returnbuffer);
    //                                                           - nanduri

    if (!(cchMax = INT32(parg16->f5))) {
        if (pszKey == (PSZ)NULL) {
            if (pszReturnBuffer != (PSZ)NULL) {
                 cchMax = 0xffff - (LOW16(parg16->f4));
            }
        }
    }

    UpdateDosCurrentDirectory(DIR_DOS_TO_NT);

    strcpy(szLowercase, pszFilename);
    WOW32_strlwr(szLowercase);

    if (IS_WIN_INI( szLowercase )) {
#ifdef FE_SB
        //
        // For those applications that expect sLongDate contains
        // windows 3.1J picture format string.
        //
        if (GetSystemDefaultLangID() == 0x411 &&
            lstrcmpi( pszSection, "intl") == 0 &&
            lstrcmpi( pszKey, "sLongDate") == 0) {
            pszTmp = pszKey;
            pszKey = "sLongDate16";
        }
#endif // FE_SB
        if (IS_EMBEDDING_SECTION( pszSection ) &&
            !WasSectionRecentlyUpdated() ) {
            if( pszKey == NULL ) {
                UpdateEmbeddingAllKeys();
            } else {
                UpdateEmbeddingKey( pszKey );
            }
            SetLastTimeUpdated();

        } else if (pszSection &&
                   pszKey &&
                   !WOW32_stricmp(pszSection, szDevices) &&
                   IsFaxPrinterSupportedDevice(pszKey)) {

            ul = GETINT16(GetFaxPrinterProfileString(pszSection, pszKey, pszDefault, pszReturnBuffer, cchMax));
            goto FlushAndReturn;
        }
    }

    ul = GETUINT16(GetPrivateProfileString(
        pszSection,
        pszKey,
        pszDefault,
        pszReturnBuffer,
        cchMax,
        pszFilename));

    
    // start comaptibility hacks
    
    
    //
    // Win3.1/Win95 compatibility:  Zap the first trailing blank in pszDefault
    // with null, but only if the default string was returned.  To detect
    // the default string being returned we need to ignore trailing blanks.
    //
    // This code is duplicated in thunks for GetProfileString and
    // GetPrivateProfileString, update both if you make changes.
    //

    if ( pszDefault && pszKey )  {

        int  n, nLenDef;

        //
        // Is the default the same as the returned string up to any NULLs?
        //
        nLenDef = 0;
        n=0;
        while (
            (pszDefault[nLenDef] == pszReturnBuffer[n]) &&
            pszReturnBuffer[n]
            ) {
            
            n++;
            nLenDef++;
        }
        
        //
        // Did we get out of the loop because we're at the end of the returned string?
        //
        if ( '\0' != pszReturnBuffer[n] ) {
            //
            // No.  The strings are materially different - fall out.
            //
        }
        else {
            //
            // Ok, the strings are identical to the end of the returned string.
            // Is the default string spaces out to the end?
            //
            while ( ' ' == pszDefault[nLenDef] ) {
                nLenDef++;
            }
            
            //
            // The last thing was not a space.  If it was a NULL, then the app
            // passed in a string with trailing NULLs as default.  (Otherwise
            // the two strings are materially different and we do nothing.)
            //
            if ( '\0' == pszDefault[nLenDef] ) {
                
                char szBuf[4];  // Some random, small number of chars to get.
                                // If the string is long, we'll get only 3 chars
                                // (and NULL), but so what? - we only need to know if
                                // we got a default last time...
                
                //
                // The returned string is the same as the default string
                // without trailing blanks, but this might be coincidence,
                // so see if a call with empty pszDefault returns anything.
                // If it does, we don't zap because the default isn't
                // being used.
                //
                if (0 == GetProfileString(pszSection, pszKey, "", szBuf, sizeof(szBuf))) {

                    //
                    // Zap first trailing blank in pszDefault with null.
                    //

                    pszDefault[ul] = 0;
                    FLUSHVDMPTR(parg16->f3 + ul, 1, pszDefault + ul);
                }
            }
        }
    }

    
    if( CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_SAYITSNOTTHERE ) {
    
        // CrossTalk 2.2 gets hung in a loop while trying to match a printer in
        // their xtalk.ini file with a printer name in the PrintDlg listbox.  
        // There is a bug in their code for handling this that gets exposed by
        // the fact that NT PrintDlg listboxes do not include the port name as
        // Win3.1 & Win'95 do.  We avoid the buggy code altogether with this 
        // hack by telling them that the preferred printer isn't stored in 
        // xtalk.ini. See bug #43168  a-craigj        
        // Maximizer 5.0 has similar problem. it tries to parse 
        // print driver location however, allocates too small buffer. See
        // Whistler bug 288491
         
        if(!WOW32_stricmp(pszSection, "Printer")) {
           if((WOW32_strstr(szLowercase, "xtalk.ini")   && !WOW32_stricmp(pszKey, "Device")) ||  // CrossTalk 2.2
              (WOW32_strstr(szLowercase, "bclwdde.ini") && !WOW32_stricmp(pszKey, "Driver")) ){   // Maximizer 5             
              strcpy(pszReturnBuffer, pszDefault);
              ul = strlen(pszReturnBuffer);
           }
        }

        // WHISTLER RAID BUG # 379253  05/06/2001 - alexsm
        // National Geographic Explorer needed the oppisite of the above. The app 
        // was checking for a QTWVideo string, which contained a path to a quicktime
        // driver. Without the key, the app wouldn't play video. It's not really a
        // SAYITSNOTTHERE, more of a SAYITISTHERE, but this saves on compat bits.
        if(!WOW32_stricmp(pszSection, "mci")) {
            if((WOW32_strstr(szLowercase, "system.ini") && !WOW32_stricmp(pszKey, "QTWVideo"))) {
                CHAR szMCIQTW[] = "\\system\\mciqtw.drv";
                CHAR szQTWVideo[MAX_PATH + sizeof(szMCIQTW) / sizeof(CHAR)];          // make sure there's room for the new string (max_path + pszMCIQTW)

                GetSystemWindowsDirectory(szQTWVideo, MAX_PATH);
                strcat(szQTWVideo, szMCIQTW);
                strncpy(pszReturnBuffer, szQTWVideo, cchMax - 1);
                *(pszReturnBuffer + (cchMax - 1))  = '\0';
                ul = strlen(szQTWVideo);
            }
        }

    }

FlushAndReturn:
#ifdef FE_SB
    if ( pszTmp )
        pszKey = pszTmp;
#endif // FE_SB
    if (ul) {
        FLUSHVDMPTR(parg16->f4, (ul + (pszSection && pszKey) ? 1 : 2), pszReturnBuffer);
        LOGDEBUG(8,("GetPrivateProfileString returns '%s'\n", pszReturnBuffer));
    }

#ifdef DEBUG

    //
    // Check for bad return on retrieving entire section by walking
    // the section making sure it's full of null-terminated strings
    // with an extra null at the end.  Also ensure that this all fits
    // within the buffer.
    //

    if (!pszKey) {
        PSZ psz;

        //
        // We don't want to complain if the poorly-formed buffer was the one
        // passed in as pszDefault by the caller.
        //

        // Although the api docs clearly state that pszDefault should never
        // be null but win3.1 is nice enough to still deal with this. Delphi is
        // passing pszDefault as NULL and this following code causes an
        // assertion in WOW. So added the pszDefault check first.
        //
        // sudeepb 11-Sep-1995


        if (!pszDefault || WOW32_strcmp(pszReturnBuffer, pszDefault)) {

            psz = pszReturnBuffer;

            while (psz < (pszReturnBuffer + ul + 2) && *psz) {
                psz += strlen(psz) + 1;
            }

            WOW32ASSERTMSGF(
                psz < (pszReturnBuffer + ul + 2),
                ("GetPrivateProfileString of entire section returns poorly formed buffer.\n"
                 "pszReturnBuffer = %p, return value = %d\n",
                 pszReturnBuffer,
                 ul
                 ));
        }
    }

#endif // DEBUG

    FREEPSZPTR(pszSection);
    FREEPSZPTR(pszKey);
    FREEPSZPTR(pszDefault);
    FREEVDMPTR(pszReturnBuffer);
    FREEPSZPTR(pszFilename);
    FREEARGPTR(parg16);
    RETURN(ul);
}




ULONG FASTCALL WK32GetProfileInt(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    PSZ psz2;
    register PGETPROFILEINT16 parg16;

    GETARGPTR(pFrame, sizeof(GETPROFILEINT16), parg16);
    GETPSZPTR(parg16->f1, psz1);
    GETPSZPTR(parg16->f2, psz2);

    ul = GETWORD16(GetProfileInt(
    psz1,
    psz2,
    INT32(parg16->f3)
    ));

    //
    // In HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics, there
    // are a bunch of values that define the screen appearance. You can
    // watch these values get updated when you go into the display control
    // panel applet and change the "appearance scheme", or any of the
    // individual elements. The win95 shell is different than win31 in that it
    // sticks "twips" values in there instead of pixels. These are calculated
    // with the following formula:
    //
    //  twips = - pixels * 72 * 20 / cyPixelsPerInch
    //
    //  pixels = -twips * cyPixelsPerInch / (72*20)
    //
    // So if the value is negative, it is in twips, otherwise it in pixels.
    // The idea is that these values are device independent. NT is
    // different than win95 in that we provide an Ini file mapping to this
    // section of the registry where win95 does not. Now, when the Lotus
    // Freelance Graphics 2.1 tutorial runs, it mucks around with the look
    // of the screen, and it changes the border width of window frames by
    // using SystemParametersInfo(). When it tries to restore it, it uses
    // GetProfileInt("Windows", "BorderWidth", <default>), which on win31
    // returns pixels, on win95 returns the default (no ini mapping), and
    // on NT returns TWIPS. Since this negative number is interpreted as
    // a huge UINT, then the window frames become huge. What this code
    // below will do is translate the number back to pixels.   [neilsa]
    //

    if ((CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_PIXELMETRICS) &&
        !WOW32_stricmp(psz1, "Windows") &&
        !WOW32_stricmp(psz2, "BorderWidth") &&
        ((INT)ul < 0)) {

        HDC hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
        if ( !hDC ) {
            ul = (ULONG) (-(INT)ul * GetDeviceCaps(hDC, LOGPIXELSY)/(72*20));
            DeleteDC(hDC);
        }
    }

    FREEPSZPTR(psz1);
    FREEPSZPTR(psz2);
    FREEARGPTR(parg16);
    RETURN(ul);
}



ULONG FASTCALL WK32GetPrivateProfileInt(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    PSZ psz2;
    PSZ psz4;
    register PGETPRIVATEPROFILEINT16 parg16;

    GETARGPTR(pFrame, sizeof(GETPRIVATEPROFILEINT16), parg16);
    GETPSZPTR(parg16->f1, psz1);
    GETPSZPTR(parg16->f2, psz2);
    GETPSZPTR(parg16->f4, psz4);

    UpdateDosCurrentDirectory(DIR_DOS_TO_NT);
    
    ul = GETWORD16(GetPrivateProfileInt(
    psz1,
    psz2,
    INT32(parg16->f3),
    psz4
    ));

    // jarbats
    // sierra's setup faults overwriting video/sound buffer
    // limits. by returning videospeed as non-existent
    // we force it to use the default videospeed path,not the optimized
    // "bad" path code.
    // this is matching with setup.exe in the registry which is not specific
    // enough. post-beta2 enhance matching process to match version resources


    if( (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_SAYITSNOTTHERE) &&
        !WOW32_stricmp(psz4, "sierra.ini") &&
        !WOW32_stricmp(psz1, "Config") &&
        !WOW32_stricmp(psz2, "VideoSpeed")
        ) {                   
         ul = parg16->f3;                   
    }

    FREEPSZPTR(psz1);
    FREEPSZPTR(psz2);
    FREEPSZPTR(psz4);
    FREEARGPTR(parg16);
    RETURN(ul);
}



ULONG FASTCALL WK32GetModuleFileName(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    register PGETMODULEFILENAME16 parg16;
    HANDLE hT;

    GETARGPTR(pFrame, sizeof(GETMODULEFILENAME16), parg16);
    ALLOCVDMPTR(parg16->f2, parg16->f3, psz2);
    //
    // ShellExecute DDE returns (HINST)33 when DDE is used
    // to satisfy a request.  This looks like a task alias
    // to ISTASKALIAS but it's not.
    //

    if ( ISTASKALIAS(parg16->f1) && 33 != parg16->f1) {
        ul = GetHtaskAliasProcessName(parg16->f1,psz2,INT32(parg16->f3));
    } else {
        hT = (parg16->f1 && (33 != parg16->f1))
                 ? (HMODULE32(parg16->f1))
                 : GetModuleHandle(NULL) ;
        ul = GETINT16(GetModuleFileName(hT, psz2, INT32(parg16->f3)));
    }

    FLUSHVDMPTR(parg16->f2, strlen(psz2)+1, psz2);
    FREEVDMPTR(psz2);
    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WK32WOWFreeResource(PVDMFRAME pFrame)
{
    ULONG ul;
    register PWOWFREERESOURCE16 parg16;

    GETARGPTR(pFrame, sizeof(*parg16), parg16);

    ul = GETBOOL16(FreeResource(
                       HCURSOR32(parg16->f1)
                       ));

    FREEARGPTR(parg16);
    RETURN(ul);
}



ULONG FASTCALL WK32GetDriveType(PVDMFRAME pFrame)
{
    ULONG ul;
    CHAR    RootPathName[] = "?:\\";
    register PGETDRIVETYPE16 parg16;

    GETARGPTR(pFrame, sizeof(GETDRIVETYPE16), parg16);

    // Form Root path
    RootPathName[0] = (CHAR)('A'+ parg16->f1);

    ul = GetDriveType (RootPathName);
    // bugbug  - temporariy fixed, should be removed when base changes
    // its return value for non-exist drives
    // Windows 3.0 sdk manaul said this api should return 1
    // if the drive doesn't exist. Windows 3.1 sdk manual said
    // this api should return 0 if it failed. Windows 3.1 winfile.exe
    // expects 0 for noexisting drives. The NT WIN32 API uses
    // 3.0 convention. Therefore, we reset the value to zero
    // if it is 1.
    if (ul <= 1)
        ul = 0;

    // DRIVE_CDROM and DRIVE_RAMDISK are not supported under Win 3.1
    if ( ul == DRIVE_CDROM ) {
        ul = DRIVE_REMOTE;
    }
    if ( ul == DRIVE_RAMDISK ) {
        ul = DRIVE_FIXED;
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}

/* WK32TermsrvGetWindowsDir - Front end to TermsrvGetWindowDirectory.
 *
 *
 * Entry - pszPath   - Pointer to return buffer for path (ascii)
 *         usPathLen - Size of path buffer (bytes)
 *
 * Exit
 *     SUCCESS
 *       True
 *
 *     FAILURE
 *       False
 *
 */
ULONG FASTCALL WK32TermsrvGetWindowsDir(PVDMFRAME pFrame)
{
    PTERMSRVGETWINDIR16 parg16;
    PSTR    psz;
    NTSTATUS Status = 0;
    USHORT   usPathLen;

    //
    // Get arguments.
    //
    GETARGPTR(pFrame, sizeof(TERMSRVGETWINDIR16), parg16);
    psz = SEGPTR(FETCHWORD(parg16->pszPathSegment),
                 FETCHWORD(parg16->pszPathOffset));
    usPathLen = FETCHWORD(parg16->usPathLen);
    FREEARGPTR(parg16);

    Status = GetWindowsDirectoryA(psz, usPathLen);

    // If this is a long path name then get the short one
    // Otherwise it will return the same path

    GetShortPathNameA(psz, psz, (DWORD)Status);

    // Get the real size.
    Status = lstrlen(psz);

    return(NT_SUCCESS(Status));
}