windows-nt/Source/XPSP1/NT/ds/dns/dnslib/string.c

1348 lines
29 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995-2001 Microsoft Corporation
Module Name:
string.c
Abstract:
Domain Name System (DNS) Library
DNS string routines.
Author:
Jim Gilroy (jamesg) October 1995
Revision History:
jamesg Jan 1997 UTF-8, Unicode conversions
--*/
#include "local.h"
PSTR
Dns_CreateStringCopy(
IN PCHAR pchString,
IN DWORD cchString
)
/*++
Routine Description:
Create copy of string.
Arguments:
pchString -- ptr to string to copy
cchString -- length of string, if unknown; if NOT given, then pchString
MUST be NULL terminated
Return Value:
Ptr to string copy, if successful
NULL on failure.
--*/
{
LPSTR pstringNew;
DNSDBG( TRACE, ( "Dns_CreateStringCopy()\n" ));
if ( !pchString )
{
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
// determine string length, if not given
if ( !cchString )
{
cchString = strlen( pchString );
}
// allocate memory
pstringNew = (LPSTR) ALLOCATE_HEAP( cchString+1 );
if ( !pstringNew )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return( NULL );
}
// copy and NULL terminate
RtlCopyMemory(
pstringNew,
pchString,
cchString );
pstringNew[cchString] = 0;
return( pstringNew );
}
DWORD
Dns_GetBufferLengthForStringCopy(
IN PCHAR pchString,
IN DWORD cchString,
IN DNS_CHARSET CharSetIn,
IN DNS_CHARSET CharSetOut
)
/*++
Routine Description:
Determing length required for copy of string.
Arguments:
pchString -- ptr to string to get buffer length for
cchString -- length of string, if known;
- if CharSetIn is unicode, then this is length in wide characters
- if NOT given, then pchString MUST be NULL terminated
CharSetIn -- incoming character set
CharSetOut -- result character set
Return Value:
Buffer length (bytes) required for string, includes space for terminating NULL.
Zero on invalid\unconvertible string. GetLastError() set to ERROR_INVALID_DATA.
--*/
{
INT length;
DNSDBG( TRACE, ( "Dns_GetBufferLengthForStringCopy()\n" ));
if ( !pchString )
{
SetLastError( ERROR_INVALID_PARAMETER );
return( 0 );
}
//
// incoming Unicode
//
if ( CharSetIn == DnsCharSetUnicode )
{
if ( !cchString )
{
cchString = (WORD) wcslen( (PWCHAR)pchString );
}
// unicode to unicode
if ( CharSetOut == DnsCharSetUnicode )
{
return( (cchString+1) * 2 );
}
// unicode to UTF8
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// for unicode-UTF8 there's no invalid string possible
else if ( CharSetOut == DnsCharSetUtf8 )
{
#if 0
length = WideCharToMultiByte(
CP_UTF8,
0, // no flags
(PWCHAR) pchString,
(INT) cchString,
NULL,
0, // call determines required buffer length
NULL,
NULL );
#endif
length = Dns_UnicodeToUtf8(
(PWCHAR) pchString,
(INT) cchString,
NULL,
0
);
ASSERT( length != 0 || cchString == 0 );
return( length + 1 );
}
// unicode to ANSI
// - some chars will NOT convert
else if ( CharSetOut == DnsCharSetAnsi )
{
length = WideCharToMultiByte(
CP_ACP,
0, // no flags
(PWCHAR) pchString,
(INT) cchString,
NULL,
0, // call determines required buffer length
NULL,
NULL
);
if ( length == 0 && cchString != 0 )
{
goto Failed;
}
return( length + 1 );
}
// bad CharSetOut drops to Failed
}
//
// incoming UTF8
//
else if ( CharSetIn == DnsCharSetUtf8 )
{
if ( !cchString )
{
cchString = strlen( pchString );
}
// UTF8 to UTF8
if ( CharSetOut == DnsCharSetUtf8 )
{
return( cchString + 1 );
}
// UTF8 to unicode
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// for UTF8 string can be invalid, catch and return error
else if ( CharSetOut == DnsCharSetUnicode )
{
#if 0
length = MultiByteToWideChar(
CP_UTF8,
0, // no flags
pchString,
(INT) cchString,
NULL,
0 // call determines required buffer length
);
#endif
length = Dns_Utf8ToUnicode(
pchString,
(INT) cchString,
NULL,
0
);
if ( length == 0 && cchString != 0 )
{
ASSERT( GetLastError() == ERROR_INVALID_DATA );
return( 0 );
}
return( (length+1)*2 );
}
// UTF8 to ANSI
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetAnsi )
{
return Dns_Utf8ToAnsi(
pchString,
cchString,
NULL,
0 );
}
// bad CharSetOut drops to Failed
}
//
// incoming ANSI
//
else if ( CharSetIn == DnsCharSetAnsi )
{
if ( !cchString )
{
cchString = strlen( pchString );
}
// ANSI to ANSI
if ( CharSetOut == DnsCharSetAnsi )
{
return( cchString + 1 );
}
// ANSI to unicode
// - should always succeed
else if ( CharSetOut == DnsCharSetUnicode )
{
length = MultiByteToWideChar(
CP_ACP,
0, // no flags
pchString,
(INT) cchString,
NULL,
0 // call determines required buffer length
);
if ( length == 0 && cchString )
{
ASSERT( FALSE );
ASSERT( GetLastError() == ERROR_INVALID_DATA );
goto Failed;
}
return( (length+1) * 2 );
}
// ANSI to UTF8
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetUtf8 )
{
return Dns_AnsiToUtf8(
pchString,
cchString,
NULL,
0 );
}
// bad CharSetOut drops to Failed
}
// all unhandled cases are failures
Failed:
DNSDBG( ANY, (
"ERROR: Dns_GetBufferLengthForStringCopy() failed!\n"
"\tpchString = %p (%*s)\n"
"\tcchString = %d\n"
"\tCharSetIn = %d\n"
"\tCharSetOut = %d\n",
pchString,
cchString, pchString,
cchString,
CharSetIn,
CharSetOut ));
SetLastError( ERROR_INVALID_DATA );
return( 0 );
}
DWORD
Dns_StringCopy(
OUT PBYTE pBuffer,
IN OUT PDWORD pdwBufLength,
IN PCHAR pchString,
IN DWORD cchString,
IN DNS_CHARSET CharSetIn,
IN DNS_CHARSET CharSetOut
)
/*++
Routine Description:
Create copy of DNS string.
Arguments:
pBuffer -- buffer to copy to
pdwBufLength -- ptr to length of buffer in bytes;
if NULL, buffer MUST have adequate length
if exists, then copy only completed if *pdwBufLength is adequate
to hold converted result
pchString -- ptr to string to copy
cchString -- length of string, if known;
- if CharSetIn is unicode, then this is length in wide characters
- if NOT given, then pchString MUST be NULL terminated
CharSetIn -- incoming character set
CharSetOut -- result character set
Return Value:
Count of bytes written to buffer (includes terminating NULL).
Zero on error. GetLastError() for status.
--*/
{
INT length;
DWORD bufLength;
DNSDBG( TRACE, ( "Dns_StringCopy()\n" ));
DNSDBG( STRING, (
"Dns_StringCopy()\n"
"\tpBuffer = %p\n"
"\tpdwBufLen = %p\n"
"\tbuf length = %d\n"
"\tpchString = %p\n"
"\tcchString = %d\n"
"\tCharSetIn = %d\n"
"\tCharSetOut = %d\n",
pBuffer,
pdwBufLength,
pdwBufLength ? *pdwBufLength : 0,
pchString,
cchString,
CharSetIn,
CharSetOut ));
if ( !pchString )
{
DNS_ASSERT( FALSE );
SetLastError( ERROR_INVALID_PARAMETER );
return( 0 );
}
//
// find string length
// do this here so don't do it twice if must calculate required buffer length
//
if ( cchString == 0 )
{
if ( CharSetIn == DnsCharSetUnicode )
{
cchString = (WORD) wcslen( (PWCHAR)pchString );
}
else
{
cchString = strlen( pchString );
}
}
//
// verify adequate buffer length
//
// DCR_PERF: ideally make direct copy to buffer and fail if
// over length, rather than effectively having to convert
// twice
//
if ( pdwBufLength )
{
bufLength = Dns_GetBufferLengthForStringCopy(
pchString,
cchString,
CharSetIn,
CharSetOut );
if ( bufLength == 0 )
{
SetLastError( ERROR_INVALID_DATA );
*pdwBufLength = 0;
return( 0 );
}
if ( bufLength > *pdwBufLength )
{
SetLastError( ERROR_MORE_DATA );
*pdwBufLength = bufLength;
return( 0 );
}
*pdwBufLength = bufLength;
}
//
// incoming unicode string
//
if ( CharSetIn == DnsCharSetUnicode )
{
// unicode to unicode straight copy
// - correct for length in wide characters
if ( CharSetOut == DnsCharSetUnicode )
{
((PWORD)pBuffer)[ cchString ] = 0;
cchString *= 2;
RtlCopyMemory(
pBuffer,
pchString,
cchString );
return( cchString+2 );
}
// unicode => UTF8
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// for unicode-UTF8 there's no invalid string possible
else if ( CharSetOut == DnsCharSetUtf8 )
{
#if 0
length = WideCharToMultiByte(
CP_UTF8,
0, // no flags
(PWCHAR) pchString,
(INT) cchString,
pBuffer,
MAXWORD, // assuming adequate length
NULL,
NULL );
#endif
length = Dns_UnicodeToUtf8(
(LPWSTR) pchString,
cchString,
pBuffer,
MAXWORD // assuming adequate length
);
ASSERT( length != 0 || cchString == 0 );
pBuffer[ length ] = 0;
return( length + 1 );
}
// unicode => ANSI
// - this conversion can fail
else if ( CharSetOut == DnsCharSetAnsi )
{
length = WideCharToMultiByte(
CP_ACP,
0, // no flags
(PWCHAR) pchString,
(INT) cchString,
pBuffer,
MAXWORD, // assuming adequate length
NULL,
NULL );
if ( length == 0 && cchString != 0 )
{
goto Failed;
}
pBuffer[ length ] = 0;
return( length + 1 );
}
// bad CharSetOut drops to Failed
}
//
// incoming UTF8
//
if ( CharSetIn == DnsCharSetUtf8 )
{
// UTF8 to UTF8 straight copy
if ( CharSetOut == DnsCharSetUtf8 )
{
memcpy(
pBuffer,
pchString,
cchString );
pBuffer[cchString] = 0;
return( cchString + 1 );
}
// UTF8 to unicode conversion
//
// use private unicode\utf8 conversion functions
// - superior to public ones (faster, more robust)
// - Win95 does not support CP_UTF8
//
// UTF8 strings can be invalid, and since sending in "infinite"
// buffer, this is only possible error
else if ( CharSetOut == DnsCharSetUnicode )
{
#if 0
length = MultiByteToWideChar(
CP_UTF8,
0, // no flags
(PCHAR) pchString,
(INT) cchString,
(PWCHAR) pBuffer,
MAXWORD // assuming adequate length
);
#endif
length = Dns_Utf8ToUnicode(
pchString,
cchString,
(LPWSTR) pBuffer,
MAXWORD
);
if ( length == 0 && cchString != 0 )
{
ASSERT( GetLastError() == ERROR_INVALID_DATA );
goto Failed;
}
((PWORD)pBuffer)[length] = 0;
return( (length+1) * 2 );
}
// UTF8 to ANSI
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetAnsi )
{
length = Dns_Utf8ToAnsi(
pchString,
cchString,
pBuffer,
MAXWORD );
if ( length == 0 )
{
goto Failed;
}
return( length );
}
// bad CharSetOut drops to Failed
}
//
// incoming ANSI
//
if ( CharSetIn == DnsCharSetAnsi )
{
// ANSI to ANSI straight copy
if ( CharSetOut == DnsCharSetAnsi )
{
memcpy(
pBuffer,
pchString,
cchString );
pBuffer[cchString] = 0;
return( cchString + 1 );
}
// ANSI to unicode conversion
// - ANSI to unicode should not fail
else if ( CharSetOut == DnsCharSetUnicode )
{
length = MultiByteToWideChar(
CP_ACP,
0, // no flags
(PCHAR) pchString,
(INT) cchString,
(PWCHAR) pBuffer,
MAXWORD // assuming adequate length
);
if ( length == 0 && cchString )
{
ASSERT( FALSE );
ASSERT( GetLastError() == ERROR_INVALID_DATA );
goto Failed;
}
((PWORD)pBuffer)[length] = 0;
return( (length+1) * 2 );
}
// ANSI to UTF8
// - note, result length here is actually buffer length
else if ( CharSetOut == DnsCharSetUtf8 )
{
length = Dns_AnsiToUtf8(
pchString,
cchString,
pBuffer,
MAXWORD );
if ( length == 0 )
{
goto Failed;
}
return( length );
}
// bad CharSetOut drops to Failed
}
// all unhandled cases are failures
Failed:
DNSDBG( ANY, (
"ERROR: Dns_StringCopy() failed!\n"
"\tpBuffer = %p\n"
"\tpdwBufLen = %p\n"
"\tbuf length = %d\n"
"\tpchString = %p (%*s)\n"
"\tcchString = %d\n"
"\tCharSetIn = %d\n"
"\tCharSetOut = %d\n",
pBuffer,
pdwBufLength,
pdwBufLength ? *pdwBufLength : 0,
pchString,
cchString, pchString,
cchString,
CharSetIn,
CharSetOut ));
SetLastError( ERROR_INVALID_DATA );
return( 0 );
}
PVOID
Dns_StringCopyAllocate(
IN PCHAR pchString,
IN DWORD cchString,
IN DNS_CHARSET CharSetIn,
IN DNS_CHARSET CharSetOut
)
/*++
Routine Description:
Create copy of DNS string
Arguments:
pchString -- ptr to string to copy
cchString -- length of string, if known;
- if CharSetIn, then this is length in wide characters
- if NOT given, then pchString MUST be NULL terminated
CharSetIn -- flag indicates incoming string is unicode
CharSetOut -- flag indicates copy will be in unicode format
Return Value:
Ptr to string copy, if successful
NULL on failure.
--*/
{
PCHAR pnew;
DWORD length;
DNSDBG( TRACE, ( "Dns_StringCopyAllocate()\n" ));
DNSDBG( STRING, (
"Dns_StringCopyAllocate( %.*s )\n"
"\tpchString = %p\n"
"\tcchString = %d\n"
"\tUnicodeIn = %d\n"
"\tUnicodeOut = %d\n",
cchString,
pchString,
pchString,
cchString,
CharSetIn,
CharSetOut ));
if ( !pchString )
{
DNS_ASSERT( FALSE );
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
//
// determine incoming string length
// do this explicitly to avoid doing string length operations twice
//
if ( !cchString )
{
if ( CharSetIn == DnsCharSetUnicode )
{
cchString = (WORD) wcslen( (PWCHAR)pchString );
}
else
{
cchString = strlen( pchString );
}
}
//
// determine required buffer length and allocate
//
length = Dns_GetBufferLengthForStringCopy(
pchString,
cchString,
CharSetIn,
CharSetOut );
if ( length == 0 )
{
ASSERT( CharSetIn && CharSetOut && GetLastError() == ERROR_INVALID_DATA );
SetLastError( ERROR_INVALID_DATA );
return( NULL );
}
pnew = (PVOID) ALLOCATE_HEAP( length );
if ( !pnew )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return( NULL );
}
//
// copy \ convert string
// - can fail if conversion not valid
// (ex. bogus UTF8 string, or attempting
// conversion from ANSI to UTF8)
//
if ( ! Dns_StringCopy(
pnew,
NULL,
pchString,
cchString,
CharSetIn,
CharSetOut ) )
{
FREE_HEAP( pnew );
return( NULL );
}
return( pnew );
}
//
// Simple create string copy utilities.
//
PSTR
Dns_CreateStringCopy_A(
IN PCSTR pszString
)
/*++
Routine Description:
Create copy of string.
Simple wrapper to handle
- sizing
- memory allocation
- copy of string
Arguments:
pszString -- ptr to string to copy
Return Value:
Ptr to string copy, if successful
NULL on failure.
--*/
{
PSTR pnew;
DWORD length;
DNSDBG( TRACE, ( "Dns_CreateStringCopy_A( %s )\n", pszString ));
if ( !pszString )
{
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
// determine string length, if not given
length = strlen( pszString ) + 1;
// allocate memory
pnew = (LPSTR) ALLOCATE_HEAP( length );
if ( !pnew )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return( NULL );
}
// copy and NULL terminate
RtlCopyMemory(
pnew,
pszString,
length );
return( pnew );
}
PWSTR
Dns_CreateStringCopy_W(
IN PCWSTR pwsString
)
{
PWSTR pnew;
DWORD length;
DNSDBG( TRACE, ( "Dns_CreateStringCopy_W( %S )\n", pwsString ));
if ( !pwsString )
{
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
// allocate memory
length = (wcslen( pwsString ) + 1) * sizeof(WCHAR);
pnew = (PWSTR) ALLOCATE_HEAP( length );
if ( !pnew )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return( NULL );
}
// copy and NULL terminate
RtlCopyMemory(
pnew,
pwsString,
length );
return( pnew );
}
PWSTR
Dns_CreateConcatenatedString_W(
IN PCWSTR * pStringArray
)
/*++
Routine Description:
Create concatenated string.
Arguments:
pStringArray -- array of string pointers to concat
NULL pointer terminates array
Return Value:
Ptr to concantenated string copy, if successful
NULL on failure.
--*/
{
PWSTR pnew;
PCWSTR pwstr;
DWORD length;
DWORD iter;
DNSDBG( TRACE, ( "Dns_CreateConcatenatedString_W()\n" ));
if ( !pStringArray )
{
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
//
// loop determining required length
//
length = 1;
iter = 0;
while ( pwstr = pStringArray[iter++] )
{
length += wcslen( pwstr );
}
//
// allocate
//
pnew = (PWSTR) ALLOCATE_HEAP( length*sizeof(WCHAR) );
if ( !pnew )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return NULL;
}
//
// write concatented string
//
pnew[0] = 0;
iter = 0;
while ( pwstr = pStringArray[iter++] )
{
wcscat( pnew, pwstr );
}
DNSDBG( TRACE, ( "Concatented string = %S\n", pnew ));
return pnew;
}
//
// MULTI_SZ routines
//
DWORD
MultiSz_Length_A(
IN PCSTR pmszString
)
/*++
Routine Description:
Determine length (size) of MULTI_SZ string.
Arguments:
pmszString -- ptr to string to size
Return Value:
Size of MULTI_SZ string (in bytes).
Includes terminating double NULL.
--*/
{
PSTR pnext;
DWORD lengthTotal = 0;
DWORD length;
DNSDBG( TRACE, ( "MultiSz_Length_A( %s )\n", pmszString ));
//
// loop until read at end of strings
//
// when we reach the end, we'll be pointing at the second
// zero in the double null terminator; strlen() will return
// zero, and we'll add that to our count as 1 and exit
//
pnext = (PSTR) pmszString;
while ( pnext )
{
length = strlen( pnext ) + 1;
lengthTotal += length;
if ( length == 1 )
{
break;
}
pnext += length;
}
return lengthTotal;
}
PSTR
MultiSz_NextString_A(
IN PCSTR pmszString
)
/*++
Routine Description:
Find next string in MULTI_SZ string
Arguments:
pmszString -- ptr to multi string
Return Value:
Next string in MULTI_SZ string.
NULL if no strings left.
--*/
{
PSTR pnext;
DWORD length;
DNSDBG( TRACE, ( "MultiSz_NextString_A( %s )\n", pmszString ));
//
// find next string in multi-string
// - find length of current string
// - hop over it (inc. null)
// - if pointing at terminating double-null return
// NULL to signal end
//
pnext = (PSTR) pmszString;
if ( !pnext )
{
return NULL;
}
length = strlen( pnext );
if ( length == 0 )
{
DNSDBG( ANY, (
"ERROR: MultiSz_Next(%p) called on terminator!\n",
pmszString ));
return NULL;
}
pnext += length + 1;
if ( *pnext == 0 )
{
return NULL;
}
return pnext;
}
PSTR
MultiSz_Copy_A(
IN PCSTR pmszString
)
/*++
Routine Description:
Create copy of MULTI_SZ string.
Simple wrapper to handle
- sizing
- memory allocation
- copy of string
Arguments:
pmszString -- ptr to string to copy
Return Value:
Ptr to string copy, if successful
NULL on failure.
--*/
{
PSTR pnew;
DWORD length;
DNSDBG( TRACE, ( "MultiSz_Copy_A( %s )\n", pmszString ));
if ( !pmszString )
{
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
// determine string length, if not given
length = MultiSz_Length_A( pmszString );
// allocate memory
pnew = (LPSTR) ALLOCATE_HEAP( length );
if ( !pnew )
{
SetLastError( DNS_ERROR_NO_MEMORY );
return( NULL );
}
// copy and NULL terminate
RtlCopyMemory(
pnew,
pmszString,
length );
return( pnew );
}
//
// Random
//
INT
wcsicmp_ThatWorks(
IN PWSTR pString1,
IN PWSTR pString2
)
/*++
Routine Description:
A version of wcsicmp that actually works.
This is just a wrapped on CompareStringW, to hide all the detail
and give an interface identical to wcsicmp().
It uses US English to standardize the comparison.
Arguments:
pString1 -- first string; must be NULL terminated
pString2 -- first second; must be NULL terminated
Return Value:
-1 -- if string 1 less than string 2
0 -- strings are equal
1 -- if string 1 greater than string 2
--*/
{
INT result;
//
// compare
// - case conversion done in default DNS locale -- US English
// this locale correctly matches most non-locale sensitive
// upper-lower characters
//
result = CompareStringW(
DNS_DEFAULT_LOCALE,
NORM_IGNORECASE,
pString1,
(-1), // NULL terminated
pString2,
(-1) // NULL terminated
);
if ( result == CSTR_EQUAL )
{
result = 0;
}
else if ( result == CSTR_LESS_THAN )
{
result = -1;
}
else // greater than or error
{
result = 1;
}
return( result );
}
LPWSTR
Dns_GetResourceString(
IN DWORD dwStringId,
IN OUT LPWSTR pwszBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Loads a string (defined in dnsmsg.mc) from current module
Arguments:
dwStringId -- The ID of the string to be fetched
Return Value:
DCR: kill off eyal function
DEVNOTE: don't understand the value of this return
-- it's essentially a BOOL, we already know what the ptr is
it's the buffer passed in
-- ptr to next byte is useful in continuous write situation
(ugly and useless in others)
-- better would just be the same return as LoadString, so we
both get the success\failure indication and also know
how many bytes forward we must push our buffer ptr if
we want to write more
Error: NULL
Success: a pointer to the loaded string
--*/
{
LPWSTR pStr = NULL;
DWORD status;
HANDLE hMod;
DNSDBG( TRACE, (
"Dns_GetStringResource()\n" ));
// Get module handle-- No need to close handle, it is just a ptr w/o increment on ref count.
hMod = GetModuleHandle( NULL );
if ( !hMod )
{
ASSERT( hMod );
return NULL;
}
status = LoadStringW(
hMod,
dwStringId,
pwszBuffer,
cbBuffer );
if ( status != 0 )
{
pStr = pwszBuffer;
}
ELSE
{
// LoadString returns # of bytes loaded, convert to error.
status = GetLastError();
DNSDBG( TRACE, (
"Error <%lu>: Failed to load string %d\n",
status, dwStringId ));
ASSERT ( FALSE );
}
DNSDBG( TRACE, (
"Exit <0x%p> Dns_GetStringResource\n",
pStr ));
return pStr;
}
//
// End string.c
//