You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
765 lines
20 KiB
765 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
geo.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the system APIs that provide geographical information.
|
|
|
|
Private APIs found in this file:
|
|
GetIS0639
|
|
GetGeoLCID
|
|
|
|
External Routines found in this file:
|
|
GetGeoInfoW
|
|
GetUserGeoID
|
|
SetUserGeoID
|
|
EnumSystemGeoID
|
|
|
|
Revision History:
|
|
|
|
11-20-99 WeiWu Created
|
|
03-07-00 lguindon Began Geo API port
|
|
|
|
--*/
|
|
|
|
|
|
|
|
//
|
|
// Include Files.
|
|
//
|
|
|
|
#include "nls.h"
|
|
#include "nlssafe.h"
|
|
|
|
|
|
|
|
|
|
//
|
|
// Global Variables.
|
|
//
|
|
|
|
PGEOTABLEHDR gpGeoTableHdr = NULL;
|
|
PGEOINFO gpGeoInfo = NULL;
|
|
PGEOLCID gpGeoLCID = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetGeoLCID
|
|
//
|
|
// Returns the Locale ID associated with the Language Identifier and
|
|
// Geographical Identifier. This routine scans the mapping table for the
|
|
// corresponding combination. If nothing is found, the function returns
|
|
// 0 as the Locale Identifier.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LCID GetGeoLCID(
|
|
GEOID GeoId,
|
|
LANGID LangId)
|
|
{
|
|
int ctr1, ctr2;
|
|
|
|
if (pTblPtrs->pGeoInfo == NULL)
|
|
{
|
|
if (GetGeoFileInfo())
|
|
{
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search for GEOID.
|
|
//
|
|
// Note: We can have more then one Language ID for one GEOID.
|
|
//
|
|
for (ctr1 = 0; ctr1 < pTblPtrs->nGeoLCID; ctr1++)
|
|
{
|
|
if (GeoId == pTblPtrs->pGeoLCID[ctr1].GeoId)
|
|
{
|
|
//
|
|
// Search for Language ID
|
|
//
|
|
for (ctr2 = ctr1;
|
|
ctr2 < pTblPtrs->nGeoLCID && pTblPtrs->pGeoLCID[ctr2].GeoId == GeoId;
|
|
ctr2++)
|
|
{
|
|
if (pTblPtrs->pGeoLCID[ctr2].LangId == LangId)
|
|
{
|
|
return (pTblPtrs->pGeoLCID[ctr2].lcid);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Nothing found, return zero
|
|
//
|
|
return ((LCID)0);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// EXTERNAL API ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetGeoInfoW
|
|
//
|
|
// Retrieves information about a geographical location on earth. The
|
|
// required size is the number of characters. If cchData is zero, the
|
|
// function returns the number of characters needed to copy to caller's
|
|
// buffer. Otherwise, the function returns the number of characters copied
|
|
// to caller's buffer if caller provided proper lpGeoData and cchData.
|
|
// The function returns zero in the case of failure.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int WINAPI GetGeoInfoW(
|
|
GEOID GeoId,
|
|
DWORD GeoType,
|
|
LPWSTR lpGeoData,
|
|
int cchData,
|
|
LANGID LangId)
|
|
{
|
|
int ctr1, ctr2, ctr3;
|
|
int Length = 0;
|
|
LPWSTR pString = NULL;
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE] = {0};
|
|
LCID Locale;
|
|
LANGID DefaultLangId;
|
|
PLOC_HASH pHashN;
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - count is negative
|
|
// - NULL data pointer AND count is not zero
|
|
// - invalid lang id
|
|
//
|
|
// NOTE: Invalid geo id is checked in the binary search below.
|
|
// Invalid type is checked in the switch statement below.
|
|
//
|
|
Locale = MAKELCID(LangId, SORT_DEFAULT);
|
|
VALIDATE_LOCALE(Locale, pHashN, FALSE);
|
|
if ((cchData < 0) ||
|
|
((lpGeoData == NULL) && (cchData > 0)) ||
|
|
(pHashN == NULL))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Check if the section is mapped into memory.
|
|
//
|
|
if (pTblPtrs->pGeoInfo == NULL)
|
|
{
|
|
if (GetGeoFileInfo())
|
|
{
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if we are dealing with an invalid geoid.
|
|
//
|
|
if (GeoId == GEOID_NOT_AVAILABLE)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Initialize variables for the binary search.
|
|
//
|
|
ctr1 = 0;
|
|
ctr2 = pTblPtrs->nGeoInfo - 1;
|
|
ctr3 = ctr2 >> 1;
|
|
|
|
//
|
|
// Binary search GEO data.
|
|
//
|
|
while (ctr1 <= ctr2)
|
|
{
|
|
if (GeoId == pTblPtrs->pGeoInfo[ctr3].GeoId)
|
|
{
|
|
//
|
|
// Jump out of the loop.
|
|
//
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (GeoId < pTblPtrs->pGeoInfo[ctr3].GeoId)
|
|
{
|
|
ctr2 = ctr3 - 1;
|
|
}
|
|
else
|
|
{
|
|
ctr1 = ctr3 + 1;
|
|
}
|
|
ctr3 = (ctr1 + ctr2) >> 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if we have found the requested element.
|
|
//
|
|
if (ctr1 > ctr2)
|
|
{
|
|
//
|
|
// Could not find the Geo ID.
|
|
//
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Get the appropriate information based on the requested GeoType.
|
|
//
|
|
switch (GeoType)
|
|
{
|
|
case ( GEO_NATION ) :
|
|
{
|
|
if (pTblPtrs->pGeoInfo[ctr3].GeoClass == GEOCLASS_NATION)
|
|
{
|
|
NlsConvertIntegerToString(
|
|
(UINT)(pTblPtrs->pGeoInfo[ctr3].GeoId),
|
|
10,
|
|
0,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE );
|
|
pString = pTemp;
|
|
}
|
|
break;
|
|
}
|
|
case ( GEO_LATITUDE ) :
|
|
{
|
|
pString = pTblPtrs->pGeoInfo[ctr3].szLatitude;
|
|
break;
|
|
}
|
|
case ( GEO_LONGITUDE ) :
|
|
{
|
|
pString = pTblPtrs->pGeoInfo[ctr3].szLongitude;
|
|
break;
|
|
}
|
|
case ( GEO_ISO2 ) :
|
|
{
|
|
pString = pTblPtrs->pGeoInfo[ctr3].szISO3166Abbrev2;
|
|
break;
|
|
}
|
|
case ( GEO_ISO3 ) :
|
|
{
|
|
pString = pTblPtrs->pGeoInfo[ctr3].szISO3166Abbrev3;
|
|
break;
|
|
}
|
|
case ( GEO_RFC1766 ) :
|
|
{
|
|
//
|
|
// Check if it's a valid LANGID. If not, get the default.
|
|
//
|
|
if (LangId == 0)
|
|
{
|
|
LangId = GetUserDefaultLangID();
|
|
}
|
|
|
|
//
|
|
// Make the corresponding LCID.
|
|
//
|
|
Locale = MAKELCID(LangId, SORT_DEFAULT);
|
|
|
|
//
|
|
// Get IS0639 value associated with the LANGID.
|
|
//
|
|
if (!GetLocaleInfoW( Locale,
|
|
LOCALE_SISO639LANGNAME,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE ))
|
|
{
|
|
//
|
|
// Try the Primary Language Identifier.
|
|
//
|
|
DefaultLangId = MAKELANGID(PRIMARYLANGID(LangId), SUBLANG_DEFAULT);
|
|
if (DefaultLangId != LangId)
|
|
{
|
|
Locale = MAKELCID(DefaultLangId, SORT_DEFAULT);
|
|
GetLocaleInfoW( Locale,
|
|
LOCALE_SISO639LANGNAME,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE );
|
|
}
|
|
}
|
|
|
|
if (pTemp[0] != 0)
|
|
{
|
|
//
|
|
// Construct the name to fit the form xx-yy where
|
|
// xx is ISO639_1 name associated with the LANGID
|
|
// and yy is the ISO3166 name 2 char abreviation.
|
|
//
|
|
if( FAILED(StringCchCatW(pTemp, ARRAYSIZE(pTemp), L"-")) ||
|
|
FAILED(StringCchCatW(pTemp, ARRAYSIZE(pTemp), pTblPtrs->pGeoInfo[ctr3].szISO3166Abbrev2)))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return(0);
|
|
}
|
|
|
|
_wcslwr(pTemp);
|
|
|
|
pString = pTemp;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( GEO_LCID ) :
|
|
{
|
|
//
|
|
// Check if the we have a valid LANGID. If not, retrieve
|
|
// the default one.
|
|
//
|
|
if (LangId == 0)
|
|
{
|
|
LangId = GetUserDefaultLangID();
|
|
}
|
|
|
|
//
|
|
// Try to get a valid LCID from the GEOID and the LANGID.
|
|
//
|
|
if ((Locale = GetGeoLCID(GeoId, LangId)) == 0)
|
|
{
|
|
//
|
|
// Try the Primary Language Identifier.
|
|
//
|
|
DefaultLangId = MAKELANGID(PRIMARYLANGID(LangId), SUBLANG_DEFAULT);
|
|
if (DefaultLangId != LangId)
|
|
{
|
|
Locale = GetGeoLCID(GeoId, DefaultLangId);
|
|
}
|
|
|
|
//
|
|
// Check if the Locale returned is valid.
|
|
//
|
|
if (Locale == 0)
|
|
{
|
|
//
|
|
// Nothing found, make something with the LangId.
|
|
// If Language ID already contains a sub-language,
|
|
// we'll use it directly.
|
|
//
|
|
if (SUBLANGID(LangId) != 0)
|
|
{
|
|
Locale = MAKELCID(LangId, SORT_DEFAULT);
|
|
}
|
|
else
|
|
{
|
|
Locale = MAKELCID(MAKELANGID(LangId, SUBLANG_DEFAULT), SORT_DEFAULT);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert the value found into a string.
|
|
//
|
|
if (Locale != 0)
|
|
{
|
|
NlsConvertIntegerToString( Locale,
|
|
16,
|
|
8,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE );
|
|
pString = pTemp;
|
|
}
|
|
break;
|
|
}
|
|
case ( GEO_FRIENDLYNAME ) :
|
|
{
|
|
Length = GetStringTableEntry( GeoId,
|
|
LangId,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_GEO_FRIENDLY_NAME );
|
|
if (Length == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
pString = pTemp;
|
|
break;
|
|
}
|
|
case ( GEO_OFFICIALNAME ) :
|
|
{
|
|
Length = GetStringTableEntry( GeoId,
|
|
LangId,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_GEO_OFFICIAL_NAME );
|
|
if (Length == 0)
|
|
{
|
|
//
|
|
// If the official name is not there, fall back on
|
|
// the friendly name.
|
|
//
|
|
Length = GetStringTableEntry( GeoId,
|
|
LangId,
|
|
pTemp,
|
|
MAX_REG_VAL_SIZE,
|
|
RC_GEO_FRIENDLY_NAME );
|
|
if (Length == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
}
|
|
pString = pTemp;
|
|
break;
|
|
}
|
|
case ( GEO_TIMEZONES ) :
|
|
{
|
|
// Not implemented
|
|
break;
|
|
}
|
|
case ( GEO_OFFICIALLANGUAGES ) :
|
|
{
|
|
// Not implemented
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure the pointer is valid. If not, return failure.
|
|
//
|
|
if (pString == NULL)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Get the length (in characters) of the string to copy.
|
|
//
|
|
if (Length == 0)
|
|
{
|
|
Length = NlsStrLenW(pString);
|
|
}
|
|
|
|
//
|
|
// Add one for null termination. All strings should be null
|
|
// terminated.
|
|
//
|
|
Length++;
|
|
|
|
//
|
|
// Check cchData for size of given buffer.
|
|
//
|
|
if (cchData == 0)
|
|
{
|
|
//
|
|
// If cchData is 0, then we can't use lpGeoData. In this
|
|
// case, we simply want to return the length (in characters) of
|
|
// the string to be copied.
|
|
//
|
|
return (Length);
|
|
}
|
|
else if (cchData < Length)
|
|
{
|
|
//
|
|
// The buffer is too small for the string, so return an error
|
|
// and zero bytes written.
|
|
//
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Copy the string to lpGeoData and null terminate it.
|
|
// Return the number of characters copied.
|
|
//
|
|
wcsncpy(lpGeoData, pString, Length - 1);
|
|
lpGeoData[Length - 1] = 0;
|
|
return (Length);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumSystemGeoID
|
|
//
|
|
// Enumerates the GEOIDs that are available on the system. This function
|
|
// returns TRUE if it succeeds, FALSE if it fails.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI EnumSystemGeoID(
|
|
GEOCLASS GeoClass,
|
|
GEOID ParentGeoId,
|
|
GEO_ENUMPROC lpGeoEnumProc)
|
|
{
|
|
int ctr1;
|
|
|
|
if ((lpGeoEnumProc == NULL) ||
|
|
(0 != ParentGeoId) )
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (GeoClass != GEOCLASS_NATION)
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (pTblPtrs->pGeoInfo == NULL)
|
|
{
|
|
if (GetGeoFileInfo())
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
for (ctr1 = 0; ctr1 < pTblPtrs->nGeoInfo; ctr1++)
|
|
{
|
|
if (pTblPtrs->pGeoInfo[ctr1].GeoClass == GeoClass)
|
|
{
|
|
if (!lpGeoEnumProc(pTblPtrs->pGeoInfo[ctr1].GeoId))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUserGeoID
|
|
//
|
|
// Retrieves information about the geographical location of the user.
|
|
// This function returns a valid GEOID or the value GEOID_NOT_AVAILABLE.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
GEOID WINAPI GetUserGeoID(
|
|
GEOCLASS GeoClass)
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
|
|
BYTE buffer[MAX_KEY_VALUE_FULLINFO]; // buffer
|
|
HANDLE hKey = NULL; // handle to geo key
|
|
WCHAR wszGeoRegStr[48]; // ptr to class key
|
|
GEOID GeoId = GEOID_NOT_AVAILABLE; // GEOID to default
|
|
UNICODE_STRING ObUnicodeStr; // registry data value string
|
|
|
|
switch (GeoClass)
|
|
{
|
|
case ( GEOCLASS_NATION ) :
|
|
{
|
|
if(FAILED(StringCchCopyW(wszGeoRegStr, ARRAYSIZE(wszGeoRegStr), GEO_REG_NATION)))
|
|
{
|
|
//
|
|
// Failure should in theory be impossible, but if we ignore the
|
|
// return value, PREfast will complain.
|
|
//
|
|
return(GEOID_NOT_AVAILABLE);
|
|
}
|
|
break;
|
|
}
|
|
case ( GEOCLASS_REGION ) :
|
|
{
|
|
if(FAILED(StringCchCopyW(wszGeoRegStr, ARRAYSIZE(wszGeoRegStr), GEO_REG_REGION)))
|
|
{
|
|
//
|
|
// Failure should in theory be impossible, but if we ignore the
|
|
// return value, PREfast will complain.
|
|
//
|
|
return(GEOID_NOT_AVAILABLE);
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
return (GeoId);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the Control Panel International registry key.
|
|
//
|
|
OPEN_GEO_KEY(hKey, GEOID_NOT_AVAILABLE, KEY_READ);
|
|
|
|
//
|
|
// Query the registry value.
|
|
//
|
|
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)buffer;
|
|
if (QueryRegValue( hKey,
|
|
wszGeoRegStr,
|
|
&pKeyValueFull,
|
|
MAX_KEY_VALUE_FULLINFO,
|
|
NULL ) == NO_ERROR)
|
|
{
|
|
//
|
|
// Convert the string to a value.
|
|
//
|
|
GeoId = _wtol(GET_VALUE_DATA_PTR(pKeyValueFull));
|
|
}
|
|
|
|
//
|
|
// Close the registry key.
|
|
//
|
|
CLOSE_REG_KEY(hKey);
|
|
|
|
//
|
|
// Return the Geo Id.
|
|
//
|
|
return (GeoId);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetUserGeoID
|
|
//
|
|
// Sets information about the geographical location of the user. This
|
|
// function returns TRUE if it succeeds, FALSE if it fails.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL WINAPI SetUserGeoID(
|
|
GEOID GeoId)
|
|
{
|
|
int ctr1, ctr2, ctr3;
|
|
WCHAR wszRegStr[MAX_REG_VAL_SIZE];
|
|
HANDLE hKey = NULL;
|
|
BOOL bRet = FALSE;
|
|
WCHAR wszBuffer[MAX_REG_VAL_SIZE] = {0};
|
|
|
|
if (pTblPtrs->pGeoInfo == NULL)
|
|
{
|
|
if (GetGeoFileInfo())
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
ctr1 = 0;
|
|
ctr2 = pTblPtrs->nGeoInfo - 1;
|
|
ctr3 = ctr2 >> 1;
|
|
|
|
//
|
|
// Binary searching the GEOID's GEOCLASS type.
|
|
//
|
|
while (ctr1 <= ctr2)
|
|
{
|
|
if (GeoId == pTblPtrs->pGeoInfo[ctr3].GeoId)
|
|
{
|
|
switch (pTblPtrs->pGeoInfo[ctr3].GeoClass)
|
|
{
|
|
case ( GEOCLASS_NATION ) :
|
|
{
|
|
if(FAILED(StringCchCopyW(wszRegStr, ARRAYSIZE(wszRegStr), GEO_REG_NATION)))
|
|
{
|
|
//
|
|
// Failure should in theory be impossible, but if we ignore the
|
|
// return value, PREfast will complain.
|
|
//
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
}
|
|
case ( GEOCLASS_REGION ) :
|
|
{
|
|
if(FAILED(StringCchCopyW(wszRegStr, ARRAYSIZE(wszRegStr), GEO_REG_REGION)))
|
|
{
|
|
//
|
|
// Failure should in theory be impossible, but if we ignore the
|
|
// return value, PREfast will complain.
|
|
//
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (GeoId < pTblPtrs->pGeoInfo[ctr3].GeoId)
|
|
{
|
|
ctr2 = ctr3 - 1;
|
|
}
|
|
else
|
|
{
|
|
ctr1 = ctr3 + 1;
|
|
}
|
|
ctr3 = (ctr1 + ctr2) >> 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not a valid GEOID or available GEOID if we can't find it in our
|
|
// GEO table.
|
|
//
|
|
if (ctr1 > ctr2)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// If the registry key does not exist, create a new one.
|
|
//
|
|
if (CreateRegKey( &hKey,
|
|
NULL,
|
|
GEO_REG_KEY,
|
|
KEY_READ | KEY_WRITE ) != NO_ERROR)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Convert to decimal string.
|
|
//
|
|
NlsConvertIntegerToString((UINT)GeoId, 10, 0, wszBuffer, MAX_REG_VAL_SIZE);
|
|
|
|
//
|
|
// Set the new GEOID value.
|
|
//
|
|
if (SetRegValue( hKey,
|
|
wszRegStr,
|
|
wszBuffer,
|
|
(NlsStrLenW(wszBuffer) + 1) * sizeof(WCHAR) ) == NO_ERROR)
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
|
|
//
|
|
// Close the registry key.
|
|
//
|
|
CLOSE_REG_KEY(hKey);
|
|
|
|
//
|
|
// Return the result.
|
|
//
|
|
return (bRet);
|
|
}
|