/*++ Copyright (c) 1991-2000, Microsoft Corporation All rights reserved. Module Name: init.c Abstract: This file contains the initialization code for the NLS APIs. External Routines found in this file: NlsDllInitialize Revision History: 05-31-91 JulieB Created. --*/ // // Include Files. // #include "nls.h" #include "nlssafe.h" #include "stdio.h" // // Global Variables. // HANDLE hModule; // handle to module RTL_CRITICAL_SECTION gcsTblPtrs; // critical section for tbl ptrs UINT gAnsiCodePage; // Ansi code page value UINT gOemCodePage; // OEM code page value UINT gMacCodePage; // MAC code page value LCID gSystemLocale; // system locale value LANGID gSystemInstallLang; // system's original install language PLOC_HASH gpSysLocHashN; // ptr to system loc hash node PLOC_HASH gpInvLocHashN; // ptr to invariant loc hash node PCP_HASH gpACPHashN; // ptr to ACP hash node PCP_HASH gpOEMCPHashN; // ptr to OEMCP hash node PCP_HASH gpMACCPHashN; // ptr to MACCP hash node HANDLE hCodePageKey; // handle to System\Nls\CodePage key HANDLE hLocaleKey; // handle to System\Nls\Locale key HANDLE hAltSortsKey; // handle to Locale\Alternate Sorts key HANDLE hLangGroupsKey; // handle to System\Nls\Language Groups key PNLS_USER_INFO pNlsUserInfo; // ptr to the user info cache PNLS_USER_INFO pServerNlsUserInfo; // ptr to the user info cache in the csrss.exe. NLS_USER_INFO gProcUserInfo; // The user info cache for the process. // The values in this cached are updated from the content of the // server side cache (pNlsRegUserInfo in csrss.exe). // Every time when we read the cache, we will check a version count (ulCacheUpdateCount // field in NLS_USER_INFO) against the server-side cache (pNlsRegUserInfo) to // see if the process cache is still valid. The ulCacheUpdateCount will be incremented // and sync'ed in the server-side when SetLocaleInfo() happens or intl registry key changes. RTL_CRITICAL_SECTION gcsNlsProcessCache; // critical section for nls process cache // // Forward Declarations. // ULONG NlsServerInitialize(void); ULONG NlsProcessInitialize(void); void InitKoreanWeights(void); //-------------------------------------------------------------------------// // EXTERNAL ROUTINES // //-------------------------------------------------------------------------// //////////////////////////////////////////////////////////////////////////// // // NlsDllInitialize // // DLL Entry initialization procedure for NLSAPI. This is called by // the base dll initialization. // // 05-31-91 JulieB Created. //////////////////////////////////////////////////////////////////////////// BOOLEAN NlsDllInitialize( IN PVOID hMod, ULONG Reason, IN PBASE_STATIC_SERVER_DATA pBaseStaticServerData) { if (Reason == DLL_PROCESS_ATTACH) { ULONG rc; // // Save module handle for use later. // hModule = (HANDLE)hMod; // // Initialize the cached user info pointer. // pServerNlsUserInfo = &(pBaseStaticServerData->NlsUserInfo); pNlsUserInfo = &gProcUserInfo; // // Process attaching, so initialize tables. // rc = NlsServerInitialize(); if (rc) { KdPrint(("NLSAPI: Could NOT initialize Server - %lx.\n", rc)); return (FALSE); } rc = NlsProcessInitialize(); if (rc) { KdPrint(("NLSAPI: Could NOT initialize Process - %lx.\n", rc)); return (FALSE); } } // // Return success. // return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // NlsThreadCleanup // // Cleanup for thread resources when it terminates. // // 03-30-99 SamerA Created. //////////////////////////////////////////////////////////////////////////// VOID NlsThreadCleanup( VOID) { if (NtCurrentTeb()->NlsCache) { CLOSE_REG_KEY( ((PNLS_LOCAL_CACHE)NtCurrentTeb()->NlsCache)->CurrentUserKeyHandle ); RtlFreeHeap( RtlProcessHeap(), 0, NtCurrentTeb()->NlsCache ); } } //-------------------------------------------------------------------------// // INTERNAL ROUTINES // //-------------------------------------------------------------------------// //////////////////////////////////////////////////////////////////////////// // // NlsServerInitialize // // Server initialization procedure for NLSAPI. This is the ONE-TIME // initialization code for the NLSAPI DLL. It simply does the calls // to NtCreateSection for the code pages that are currently found in the // system. // // 05-31-91 JulieB Created. //////////////////////////////////////////////////////////////////////////// ULONG NlsServerInitialize(void) { HANDLE hSec = (HANDLE)0; // section handle ULONG rc; PPEB Peb; #ifndef DOSWIN32 PIMAGE_NT_HEADERS NtHeaders; #endif Peb = NtCurrentPeb(); // // MultiUser NT (Hydra). SesssionId = 0 is the console CSRSS. // If this is NOT the first server process, then just return success, // since we only want to create the object directories once. // if (Peb->SessionId != 0) { return (NO_ERROR); } #ifndef DOSWIN32 // // This is to avoid being initialized again when NTSD dynlinks to // a server to get at its debugger extensions. // NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress); if (NtHeaders && (NtHeaders->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)) { return (NO_ERROR); } #endif // // Create the NLS object directory. // // Must create a separate directory off the root in order to have // CreateSection access on the fly. // if (rc = CreateNlsObjectDirectory()) { return (rc); } // // The ACP, OEMCP, and Default Language files are already created // at boot time. The pointers to the files are stored in the PEB. // // Create the section for the following data files: // UNICODE // LOCALE // CTYPE // SORTKEY // SORT TABLES // // All other data files will have the sections created only as they // are needed. // if ((!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_UNICODE, 0, &hSec))) || (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_LOCALE, 0, &hSec))) || (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_CTYPE, 0, &hSec))) || (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_SORTKEY, 0, &hSec))) || (!NT_SUCCESS(rc = CsrBasepNlsCreateSection( NLS_CREATE_SECTION_SORTTBLS, 0, &hSec)))) { return (rc); } // // Return success. // return (NO_ERROR); } //////////////////////////////////////////////////////////////////////////// // // NlsProcessInitialize // // Process initialization procedure for NLS API. This routine sets up all // of the tables so that they are accessable from the current process. If // it is unable to allocate the appropriate memory or memory map the // appropriate files, an error is returned. // // 05-31-91 JulieB Created. //////////////////////////////////////////////////////////////////////////// ULONG NlsProcessInitialize(void) { ULONG rc; LPWORD pBaseAddr; // ptr to base address of section LCID UserLocale; // user locale id PLOC_HASH pUserLocHashN; // ptr to user locale hash node PPEB Peb; Peb = NtCurrentPeb(); // // Initialize the critical section that protects the NLS cache for // this process. // if ((rc = RtlInitializeCriticalSection(&gcsNlsProcessCache)) != ERROR_SUCCESS) { return (rc); } // // Initialize the table pointers critical section. // Enter the critical section to set up the tables. // if ((rc = RtlInitializeCriticalSectionAndSpinCount(&gcsTblPtrs, 4000)) != ERROR_SUCCESS) { return (rc); } RtlEnterCriticalSection(&gcsTblPtrs); // // Allocate initial tables. // if (rc = AllocTables()) { KdPrint(("AllocTables failed, rc %lx\n", rc)); RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // Initialize the handles to the various registry keys to NULL. // hCodePageKey = NULL; hLocaleKey = NULL; hAltSortsKey = NULL; hLangGroupsKey = NULL; // // Get the ANSI code page value. // Create the hash node for the ACP. // Insert the hash node into the global CP hash table. // // At this point, the ACP table has already been mapped into // the process, so get the pointer from the PEB. // pBaseAddr = Peb->AnsiCodePageData; gAnsiCodePage = ((PCP_TABLE)(pBaseAddr + CP_HEADER))->CodePage; if (rc = MakeCPHashNode( gAnsiCodePage, pBaseAddr, &gpACPHashN, FALSE, NULL )) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // Get the OEM code page value. // Create the hash node for the OEMCP. // Insert the hash node into the global CP hash table. // // At this point, the OEMCP table has already been mapped into // the process, so get the pointer from the PEB. // pBaseAddr = Peb->OemCodePageData; gOemCodePage = ((PCP_TABLE)(pBaseAddr + CP_HEADER))->CodePage; if (gOemCodePage != gAnsiCodePage) { // // Oem code page is different than the Ansi code page, so // need to create and store the new hash node. // if (rc = MakeCPHashNode( gOemCodePage, pBaseAddr, &gpOEMCPHashN, FALSE, NULL )) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } } else { // // Oem code page is the same as the Ansi code page, so set // the oem cp hash node to be the same as the ansi cp hash node. // gpOEMCPHashN = gpACPHashN; } // // Initialize the MAC code page values to 0. // These values will be set the first time they are requested for use. // gMacCodePage = 0; gpMACCPHashN = NULL; // // Open and Map a View of the Section for UNICODE.NLS. // Save the pointers to the table information in the table ptrs // structure. // if (rc = GetUnicodeFileInfo()) { KdPrint(("GetUnicodeFileInfo failed, rc %lx\n", rc)); RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // Cache the system locale value. // rc = NtQueryDefaultLocale(FALSE, &gSystemLocale); if (!NT_SUCCESS(rc)) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // Store the user locale value. // UserLocale = pNlsUserInfo->UserLocaleId; if (UserLocale == 0) { UserLocale = gSystemLocale; } // // Initialize the system install language to zero. This will only // be retrieved on an as need basis. // gSystemInstallLang = 0; // // Open and Map a View of the Section for LOCALE.NLS. // Create and insert the hash node into the global Locale hash table // for the system default locale. // if (rc = GetLocaleFileInfo( gSystemLocale, &gpSysLocHashN, TRUE )) { // // Change the system locale to be the default (English). // if (GetLocaleFileInfo( MAKELCID(NLS_DEFAULT_LANGID, SORT_DEFAULT), &gpSysLocHashN, TRUE )) { KdPrint(("Couldn't do English\n")); RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } else { // // Registry is corrupt, but allow the English default to // work. Need to reset the system default. // gSystemLocale = MAKELCID(NLS_DEFAULT_LANGID, SORT_DEFAULT); KdPrint(("NLSAPI: Registry is corrupt - Using Default Locale.\n")); } } // // If the user default locale is different from the system default // locale, then create and insert the hash node into the global // Locale hash table for the user default locale. // // NOTE: The System Default Locale Hash Node should be // created before this call. // if (UserLocale != gSystemLocale) { if (rc = GetLocaleFileInfo( UserLocale, &pUserLocHashN, TRUE )) { // // Change the user locale to be equal to the system default. // UserLocale = gSystemLocale; KdPrint(("NLSAPI: Registry is corrupt - User Locale Now Equals System Locale.\n")); } } // // Create and insert the hash node into the global Locale hash // table for the invariant locale. // if (rc = GetLocaleFileInfo( LOCALE_INVARIANT, &gpInvLocHashN, TRUE )) { KdPrint(("NLSAPI: Registry is corrupt - Invariant Locale Cannot Be Initialized.\n")); } // // Open and Map a View of the Section for SORTKEY.NLS. // Save the pointers to the semaphore dword and the default sortkey // table in the table ptrs structure. // if (rc = GetDefaultSortkeyFileInfo()) { KdPrint(("NLSAPI: Initialization, GetDefaultSortkeyFileInfo failed with rc %lx.\n", rc)); // RtlLeaveCriticalSection(&gcsTblPtrs); // return (rc); } // // Open and Map a View of the Section for SORTTBLS.NLS. // Save the pointers to the sort table information in the // table ptrs structure. // if (rc = GetDefaultSortTablesFileInfo()) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // Get the language information portion of the system locale. // // NOTE: GetDefaultSortkeyFileInfo and GetDefaultSortTablesFileInfo // should be called before this so that the default sorting // tables are already initialized at the time of the call. // if (rc = GetLanguageFileInfo( gSystemLocale, &gpSysLocHashN, FALSE, 0 )) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // Get the language information portion of the invariant locale. We // use the default locale (US English). // if (rc = GetLanguageFileInfo( MAKELCID(NLS_DEFAULT_LANGID, SORT_DEFAULT), &gpInvLocHashN, FALSE, 0 )) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } // // If the user default is different from the system default, // get the language information portion of the user default locale. // // NOTE: GetDefaultSortkeyFileInfo and GetDefaultSortTablesFileInfo // should be called before this so that the default sorting // tables are already initialized at the time of the call. // if (gSystemLocale != UserLocale) { if (rc = MakeLangHashNode( UserLocale, NULL, &pUserLocHashN, FALSE )) { RtlLeaveCriticalSection(&gcsTblPtrs); return (rc); } } // // Initialize the Korean SMWeight values. // InitKoreanWeights(); // // Leave the critical section. // RtlLeaveCriticalSection(&gcsTblPtrs); RtlEnterCriticalSection(&gcsNlsProcessCache); // // Fill up the cache from the cache in csrss.exe so that we have a fresh copy of cache // values. We will retrieve a copy of ulCacheUpdateCount so that we can check // if the cache values are no longer valid after they are retrieved here. // // Enter a critical section gcsNlsProcessCache to provide synchronization between // threads since we are updating the process-wide cache. // if (!NT_SUCCESS(CsrBasepNlsGetUserInfo(pNlsUserInfo, sizeof(NLS_USER_INFO)))) { RtlLeaveCriticalSection(&gcsNlsProcessCache); return (ERROR_INVALID_FUNCTION); } RtlLeaveCriticalSection(&gcsNlsProcessCache); // // Return success. // return (NO_ERROR); } //////////////////////////////////////////////////////////////////////////// // // InitKoreanWeights // // Creates the SMWeight array with the IDEOGRAPH script member sorting // before all other script members. // // NOTE: This function assumes we're in a critical section. // // 05-31-91 JulieB Created. //////////////////////////////////////////////////////////////////////////// void InitKoreanWeights() { DWORD ctr; // loop counter BYTE NewScript; // new script to store LPBYTE pSMWeight = pTblPtrs->SMWeight; // ptr to script member weights PMULTI_WT pMulti; // ptr to multi weight // // Set the 0 to FIRST_SCRIPT of script structure to its default // value. // RtlZeroMemory(pSMWeight, NUM_SM); for (ctr = 1; ctr < FIRST_SCRIPT; ctr++) { pSMWeight[ctr] = (BYTE)ctr; } // // Save the order in the SMWeight array. // NewScript = FIRST_SCRIPT; pSMWeight[IDEOGRAPH] = NewScript; NewScript++; // // See if the script is part of a multiple weights script. // pMulti = pTblPtrs->pMultiWeight; for (ctr = pTblPtrs->NumMultiWeight; ctr > 0; ctr--, pMulti++) { if (pMulti->FirstSM == IDEOGRAPH) { // // Part of multiple weight, so must move entire range // by setting each value in range to NewScript and // then incrementing NewScript. // // NOTE: May use 'ctr' here since it ALWAYS breaks // out of outer for loop. // for (ctr = 1; ctr < pMulti->NumSM; ctr++) { pSMWeight[IDEOGRAPH + ctr] = NewScript; NewScript++; } break; } } // // Must set each script member that has not yet been reset to its // new order. // // The default ordering is to assign: // Order = Script Member Value // // Therefore, can simply set each zero entry in order to the end // of the array to the next 'NewScript' value. // for (ctr = FIRST_SCRIPT; ctr < NUM_SM; ctr++) { // // If it's a zero value, set it to the next sorting order value. // if (pSMWeight[ctr] == 0) { pSMWeight[ctr] = NewScript; NewScript++; } } }