Figure 1   RtlInitAnsiString

void RtlInitAnsiString( RTL_STRING * pRtlString, PSTR psz )
{
    pRtlString->len = 0;
    pRtlString->maxLen = 0;
    pRtlString->pBuffer = psz;

    if ( psz )
    {
        DWORD cChars = strlen( psz ) + 1;   // This strlen is done inline
        pRtlString->maxLen = cChars;
        pRtlString->len = cChars - 1;
    }
}


Figure 2   RtlInitUnicodeString


void RtlInitUnicodeString( RTL_STRING * pRtlString, PWSTR pwsz )
{
    pRtlString->len = 0;
    pRtlString->maxLen = 0;
    pRtlString->pBuffer = pwsz;

    if ( pwsz )
    {
        DWORD cChars = wcslen( pwsz ) + 1;  // This wcslen is done inline
        pRtlString->maxLen = cChars;
        pRtlString->len = cChars - 1;
    }
}


Figure 3   RtlUnicodeStringToAnsiString


 DWORD RtlUnicodeStringToAnsiString( RTL_STRING * pAnsiRtlString,
                                     RTL_STRING * pUniRtlString, 
                                     BOOL fAlloc )
 {
     DWORD size;
     DWORD retCode;
     DWORD retValue;
 
     retValue = ERROR_SUCCESS;   // Assume this function will succeed
 
     //
     // Figure out how big the Unicode string is
     //              
     if ( NlsMbCodePageTag )
         size = RtlxUnicodeStringToAnsiSize( pUniRtlString ); 
     else
         size = (pUniRtlString->len + 2) / 2;
 
     //=========================================================================
     // Bail out if the string is > 64KB
     //=========================================================================
     if ( size > 0xFFFF )
         return STATUS_INVALID_PARAMETER_2;
 
     pAnsiRtlString->len = size-1;
 
     if ( fAlloc )   // Allocate memory for the destination ANSI string
     {               // (if requested)
         pAnsiRtlString->maxLen = size;
         
         pAnsiRtlString->pBuffer = NtdllpAllocateStringRoutine( size );
 
         if ( !pAnsiRtlString->pBuffer )
             return STATUS_NO_MEMORY;
     }
     else          // Use the existing buffer from the ANSI RTL_STRING passed in
     {
                   // Sanity check to make sure the destination is big enough
         WORD maxLen = pAnsiRtlString->maxLen;
         
         if ( maxLen <= size )
         {
             if ( 0 == maxLen )
                 return STATUS_BUFFER_OVERFLOW;
                 
             maxLen--;
             retValue = STATUS_BUFFER_OVERFLOW;
             pAnsiRtlString->len = maxLen;
         }
     }
     
     //=========================================================
     // Do the actual Unicode to Multibyte (8bit) translation
     //=========================================================
     retCode = RtlUnicodeToMultiByteN(   pAnsiRtlString->pBuffer,
                                         pAnsiRtlString->len,
                                         &size,
                                         pUniRtlString->pBuffer,
                                         pUniRtlString->len);
     
     if ( FAILED(retCode) )
     {
         if ( fAlloc )
             NtdllpFreeStringRoutine( pAnsiRtlString->pBuffer );
 
         return retCode; 
     }
 
         
     pAnsiRtlString->pBuffer[ size ] = 0;
 
     return retValue;
 }


Figure 4   RtlUnicodeToMultiByteN


 DWORD RtlUnicodeToMultiByteN(   LPSTR pDest, DWORD cDestLen,
                                 DWORD * pSize,
                                 LPWSTR pSrc, DWORD cSrcLen )
 {
     if ( 0 == NlsMbCodePageTag )
     {
         cSrcLen >> 1;   // Convert byte count to characters
         if ( cSrcLen >= cDestLen )
             cSrcLen = cDestLen;
     
         if ( pSize )
             *pSize = cSrcLen;
             
         DWORD charsThisTime = cSrcLen & 0xF;
                 
         PBYTE pSrcCurr = pSrc + charsThisTime*2;
         PBYTE pDestCurr = pDest + charsThisTime;
         
         if ( charsThisTime < 0xF )
         {
             // JMP table to that transfers to the appropriate spot in the
             // else clause code below
         }
         else
         {
           do    // Loop through the string, translating 16 chars at a time
           {
             pSrcCurr  += 0x20;  // Point to the end of the next Src region
             pDestCur += 0x10;   // point to the end of the next Dest region
             
             // Translate 16 characters at a time.  Code is an unrolled loop
             // to eliminate cache flushing EIP transfers
             pDestCurr[-0x10]= (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x10]];
             pDestCurr[-0xF] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0xF] ];
             pDestCurr[-0xE] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0xE] ];
             pDestCurr[-0xD] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0xD] ];
             pDestCurr[-0xC] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0xC] ];
             pDestCurr[-0xB] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0xB] ];
             pDestCurr[-0xA] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0xA] ];
             pDestCurr[-0x9] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x9] ];
             pDestCurr[-0x8] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x8] ];
             pDestCurr[-0x7] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x7] ];
             pDestCurr[-0x6] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x6] ];
             pDestCurr[-0x5] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x5] ];
             pDestCurr[-0x4] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x4] ];
             pDestCurr[-0x3] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x3] ];
             pDestCurr[-0x2] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x2] ];
             pDestCurr[-0x1] = (BYTE)NlsUnicodeDataToAnsiData[pSrcCurr[-0x1] ];
                                                              
             cSrcLen -= charsThisTime;
             charsTimeTime = 10;             
           } while ( cSrcLen != 0 )
         }
     }
     else    // NlsMbCodePageTag is non-zero
     {
         cSrcLen >> 1;   // Convert byte count to characters
 
         if ( cSrcLen )  // Anything to convert?
         {
             pDestCurr = pDest;
                 
             while ( cDestLen )  // While not at the end of the string...
             {
                 srcChar = *(PWORD)pSrc;
                 destChar = CodePage[ srcChar ]; // Do the character translation
                 pSrc += 2;
                 
                 if ( HIBYTE(destChar) )     // Check to see if the high BYTE is
                 {                           // set.  If so, it's a MultiByte
                     cDestLen--;             // character that takes 2 BYTEs
                     if ( cDestLen < 2 )
                         break;
                                                 
                     *pDestCurr = HIBYTE(destChar);
                     pDestCurr++;
                 }
                 
                 *pDestCurr = LOWBYTE(destChar);     // Copy the low BYTE to the
                                                     // destination string
                 cDestLen--;
                 pDestCurr++;        // Prepare for the next character
                 cSrcLen--;
                 if ( cSrcLen == 0 )
                     break;
             }
         }
 
         if ( pSize )    // If this param is non-zero, fill in as requested
             *pSize =  pDestCurr - pSrc;
     }
     
     return 0;
 }


Figure 5   BasepXXXStringToYYYString


 DWORD
 Basep8BitStringToUnicodeString( RTL_STRING * pUniRtlString, 
                                 RTL_STRING * pAnsiRtlString,
                                 BOOL fAlloc )
 {
     if ( BasepFileApisAreOem )
     {
         return RtlOemStringToUnicodeString( pUniRtlString,
                                             pAnsiRtlString,
                                             fAlloc );
     }
     else
     {
         return RtlAnsiStringToUnicodeString(pUniRtlString,
                                             pAnsiRtlString,
                                             fAlloc );
     }
 }
 
 DWORD
 BasepUnicodeStringTo8BitString( RTL_STRING * pAnsiRtlString,
                                 RTL_STRING * pUniRtlString, 
                                 BOOL fAlloc )
 {
     if ( BasepFileApisAreOem )
     {
         return RtlUnicodeStringToOemString( pAnsiRtlString,
                                             pUniRtlString
                                             fAlloc );
     }
     else
     {
         return RtlUnicodeStringToAnsiString(pAnsiRtlString,
                                             pUniRtlString
                                             fAlloc );
     }
 }


Figure 6   String Freeing Routines


 BOOL RtlFreeAnsiString( PRTL_STRING pRtlString )
 {
     if ( pRtlString->pBuffer )
         return pfnNtdllpFreeStringRoutine( pRtlString->pBuffer )
     
     return 0;
 }
 
 BOOL RtlFreeUnicodeString( PRTL_STRING pRtlString )
 {
     PVOID pTemp = pRtlString;
     
     if ( pTemp )
         return pfnNtdllpFreeStringRoutine( ptemp )
     
     return 0;
 }
 
 _NtdllpFreeStringRoutine( PVOID p )
 {   
     return RtlFreeHeap( GetProcessHeap(), 0, p );
 }


Figure 7   GetModuleHandleA


 WINBASEAPI HMODULE WINAPI GetModuleHandleA( LPCSTR lpModuleName )
 {
     if ( 0 == lpModuleName )    // Special case, where hModule = 0 returns
     {                           // HMODULE of the process EXE
         return pPEB->exeBaseAddress;
     }
     else
     {
         RTL_STRING rtlAnsiString;
 
         // Create an ANSI string from the input null-terminated string      
         RtlInitAnsiString( &rtlAnsiString, lpModuleName );
 
         // Convert to Unicode, using the function that takes into account
         // ANSI vs. OEM file APIs.
         DWORD retCode;
         retCode = Basep8BitStringToUnicodeString(pTEB->staticUnicodeRTL_STRING, 
                                                  &rtlAnsiString, FALSE );
 
         if ( FAILED(retCode) )  // Make sure the translation worked!
         {
             if ( retCode == STATUS_BUFFER_OVERFLOW )
                 SetLastError( ERROR_FILENAME_EXCED_RANGE );
             else
                 BaseSetLastNTError( retCode );  // Same as GetLastError
 
             return 0;
         }
         else    // Finally... Call the Unicode version of the function
             return GetModuleHandleW( rtlAnsiString->pBuffer );
     }
 }


Figure 8   SetComputerNameA


 WINBASEAPI BOOL WINAPI SetComputerNameA( LPCSTR lpComputerName )
 {
     RTL_STRING rtlUniString;
     RTL_STRING rtlAnsiString;
     DWORD retCode;
 
     // Create the RTL_STRING string from the input null-terminated string
     RtlInitAnsiString( &rtlAnsiString, lpComputerName );
 
     // Convert the ANSI string to Unicode.  Note the TRUE argument, which
     // indicates that the memory for the Unicode string should be allocated
     retCode = RtlAnsiStringToUnicodeString( &rtlAnsiString,
                                             &rtlUniString, TRUE );
     if ( FAILED(retCode) )
     {
         BaseSetLastNTError( retCode );  // See GetLastError()
 
         return FALSE;
     }
     
     // Call the Unicode version of the API
     retCode = SetComputerNameW( &rtlUniString.pBuffer );    
 
     RtlFreeUnicodeString( &rtlUniString );
 
     return (BOOL)retCode;
 }


Figure 9   GetCurrentDirectoryA


 WINBASEAPI DWORD WINAPI GetCurrentDirectoryA( DWORD nBufferLength, 
                                              LPSTR lpBuffer )
 {
     RTL_STRING rtlAnsiString;
     WORD cbLen;
     DWORD retCode;
 
     // Get the Unicode version of the current directory, stored in the
     // per-thread static Unicode buffer     
     cbLen = RtlGetCurrentDirectory_U( pTEB->staticUnicodeRTL_STRING.maxLen,
                                       pTEB->staticUnicodeRTL_STRING.pBuffer );
 
     pTEB->staticUnicodeRTL_STRING.len = cbLen;
     
     if ( nBufferLength > (cbLen/2) )    // Will it fit into the output buffer?
     {
         rtlAnsiString->maxLen = nBufferLength + 1;
         rtlAnsiString->pBuffer = lpBuffer;
 
         retCode =BasepUnicodeStringTo8BitString(&rtlAnsiString,
                                                 &pTEB->staticUnicodeRTL_STRING,
                                                 FALSE);
         if ( FAILED(retCode) )
         {
             BaseSetLastNTError( retCode );  // See GetLastError
             return 0;
         }
 
         return rtlAnsiString.len;   // Success case - return chars copied
     }
 
     return cbLen + 1;   // Failure case - return size of buffer needed
 }


Figure 10   GetModuleFileNameA


 WINBASEAPI DWORD WINAPI GetModuleFileNameA( HMODULE hModule, LPSTR lpFilename,
                                             DWORD nSize )
 {
     RTL_STRING rtlUniString;
     RTL_STRING rtlAnsiString;
 
     // Allocate memory for a Unicode string to pass to GetModuleFileNameW
     rtlUniString.pBuffer = RtlAllocateHeap( GetProcessHeap(),
                                             g_HeapFlags, nSize * 2 );
     
     if ( 0 == rtlUniString.pBuffer )
     {
         BaseSetLastNTError( STATUS_NO_MEMORY )
         return 0;
     }
 
     DWORD cChars;
             
     cChars = GetModuleFileNameW( hModule, rtlUniString.pBuffer, nSize );
 
     // Fix up the remaining Unicode RTL_STRING fields
     rtlUniString.maxLen = cChars * 2;
     rtlUniString.len = cChars * 2;
     rtlUniString.maxLen += 2;       // account for NULL terminator
 
     if ( cChars )
     {   
         DWORD retCode;
         
         retCode = BasepUnicodeStringTo8BitString(   &rtlAnsiString,
                                                     &rtlUniString, TRUE );
         if ( FAILED(retCode) )
         {
             BaseSetLastNTError( retCode );
             
             RtlFreeUnicodeString( &rtlUniString );
             
             return 0;
         }
         
         memmove( lpFilename, rtlAnsiString.pBuffer, retCode+1 );
         
         RtlFreeAnsiString( &rtlAnsiString );
     }
 
     RtlFreeUnicodeString( &rtlUniString );
 
     return retCode;     
 }


Figure 11   AnsiUniTiming.cpp


 //==========================================
 // Matt Pietrek
 // Microsoft Systems Journal, December 1997
 // FILE: AnsiUniTiming.CPP
 // To compile: CL /O2 AnsiUniTiming.CPP
 //==========================================
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <stdio.h>
 
 #define LOOP_ITERATIONS 50000
 
 int main()
 {
     LARGE_INTEGER tBefore, tAfter, i64PerfFrequency;
     float tAnsi, tUnicode;
     char szAnsiBuffer[ MAX_PATH ];
     WCHAR wszUnicodeBuffer[ MAX_PATH ];
     unsigned i;
 
     // Figure out how often the performance counter increments
     QueryPerformanceFrequency( &i64PerfFrequency );
 
     // Set this thread's priority as high as reasonably possible to prevent
     // timeslice interruptions
     SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
     
     // ======================== ANSI Portion =================================
 
     Sleep( 0 ); // Try to start on a fresh timeslice
 
     // Get performance counter before the ANSI API loop
     QueryPerformanceCounter( &tBefore );
 
     // ANSI API loop    
     for ( i = 0; i < LOOP_ITERATIONS; i++ )
     {
         HMODULE hMod = GetModuleHandleA( "KERNEL32.DLL" );
 
         GetModuleFileNameA( hMod, szAnsiBuffer, sizeof(szAnsiBuffer) );
         
         GetCurrentDirectoryA( sizeof(szAnsiBuffer), szAnsiBuffer );
     }
 
     // Get performance counter after the ANSI API loop
     QueryPerformanceCounter( &tAfter );
 
     // "QuadPart" is a 64 bit integer (__int64).  VC++ supports them!
     tAnsi = tAfter.QuadPart - tBefore.QuadPart;
     tAnsi = tAnsi / i64PerfFrequency.QuadPart;
             
     printf( "ANSI version took %.4f seconds\n", tAnsi );
 
     // ====================== Unicode Portion =================================
 
     Sleep( 0 ); // Try to start on a fresh timeslice
         
     // Get performance counter before the Unicode API loop
     QueryPerformanceCounter( &tBefore );
     
     // unicode API loop 
     for ( i = 0; i < LOOP_ITERATIONS; i++ )
     {
         HMODULE hMod = GetModuleHandleW( L"KERNEL32.DLL" );
 
         GetModuleFileNameW( hMod, wszUnicodeBuffer, sizeof(wszUnicodeBuffer) );
         
         GetCurrentDirectoryW( sizeof(wszUnicodeBuffer), wszUnicodeBuffer );
     }
 
     // Get performance counter after the Unicode API loop
     QueryPerformanceCounter( &tAfter );
 
     tUnicode = tAfter.QuadPart - tBefore.QuadPart;
     tUnicode = tUnicode / i64PerfFrequency.QuadPart;
             
     printf( "Unicode version took %.4f seconds", tUnicode );
             
     return 0;
 }