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;
}