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.
7171 lines
208 KiB
7171 lines
208 KiB
/*++
|
|
|
|
Copyright (c) 1994-2000, Microsoft Corporation All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
util.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the utility functions used by the Regional
|
|
Options applet.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
|
|
//
|
|
// Include Files.
|
|
//
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <shlwapi.h>
|
|
#include "intl.h"
|
|
#include <tchar.h>
|
|
#include <windowsx.h>
|
|
#include <userenv.h>
|
|
#include <regstr.h>
|
|
#include "intlhlp.h"
|
|
#include "maxvals.h"
|
|
#include "winnlsp.h"
|
|
#include <lmcons.h>
|
|
#include "intlmsg.h"
|
|
|
|
#define STRSAFE_LIB
|
|
#include <strsafe.h>
|
|
|
|
//
|
|
// Global Variables.
|
|
//
|
|
|
|
#ifdef UNICODE
|
|
#define NUM_CURRENCY_SYMBOLS 2
|
|
LPWSTR pCurrencySymbols[] =
|
|
{
|
|
L"$",
|
|
L"\x20ac"
|
|
};
|
|
#endif
|
|
|
|
#define NUM_DATE_SEPARATORS 3
|
|
LPTSTR pDateSeparators[] =
|
|
{
|
|
TEXT("/"),
|
|
TEXT("-"),
|
|
TEXT(".")
|
|
};
|
|
|
|
#define NUM_NEG_NUMBER_FORMATS 5
|
|
LPTSTR pNegNumberFormats[] =
|
|
{
|
|
TEXT("(1.1)"),
|
|
TEXT("-1.1"),
|
|
TEXT("- 1.1"),
|
|
TEXT("1.1-"),
|
|
TEXT("1.1 -")
|
|
};
|
|
|
|
#define NUM_POS_CURRENCY_FORMATS 4
|
|
LPTSTR pPosCurrencyFormats[] =
|
|
{
|
|
TEXT("¤1.1"),
|
|
TEXT("1.1¤"),
|
|
TEXT("¤ 1.1"),
|
|
TEXT("1.1 ¤")
|
|
};
|
|
|
|
#define NUM_NEG_CURRENCY_FORMATS 16
|
|
LPTSTR pNegCurrencyFormats[] =
|
|
{
|
|
TEXT("(¤1.1)"),
|
|
TEXT("-¤1.1"),
|
|
TEXT("¤-1.1"),
|
|
TEXT("¤1.1-"),
|
|
TEXT("(1.1¤)"),
|
|
TEXT("-1.1¤"),
|
|
TEXT("1.1-¤"),
|
|
TEXT("1.1¤-"),
|
|
TEXT("-1.1 ¤"),
|
|
TEXT("-¤ 1.1"),
|
|
TEXT("1.1 ¤-"),
|
|
TEXT("¤ 1.1-"),
|
|
TEXT("¤ -1.1"),
|
|
TEXT("1.1- ¤"),
|
|
TEXT("(¤ 1.1)"),
|
|
TEXT("(1.1 ¤)")
|
|
};
|
|
|
|
#define NUM_AM_SYMBOLS 1
|
|
LPTSTR pAMSymbols[] =
|
|
{
|
|
TEXT("AM")
|
|
};
|
|
|
|
#define NUM_PM_SYMBOLS 1
|
|
LPTSTR pPMSymbols[] =
|
|
{
|
|
TEXT("PM")
|
|
};
|
|
|
|
|
|
const TCHAR c_szEventSourceName[] = TEXT("Regional and Language Options");
|
|
const TCHAR c_szEventRegistryPath[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\Regional and Language Options");
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_StrToLong
|
|
//
|
|
// Returns the long integer value stored in the string. Since these
|
|
// values are coming back form the NLS API as ordinal values, do not
|
|
// worry about double byte characters.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LONG Intl_StrToLong(
|
|
LPTSTR szNum)
|
|
{
|
|
LONG Rtn_Val = 0;
|
|
|
|
while (*szNum)
|
|
{
|
|
Rtn_Val = (Rtn_Val * 10) + (*szNum - CHAR_ZERO);
|
|
szNum++;
|
|
}
|
|
return (Rtn_Val);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_FileExists
|
|
//
|
|
// Determines if the file exists and is accessible.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_FileExists(
|
|
LPTSTR pFileName)
|
|
{
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE FindHandle;
|
|
BOOL bRet;
|
|
UINT OldMode;
|
|
|
|
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
FindHandle = FindFirstFile(pFileName, &FindData);
|
|
if (FindHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
FindClose(FindHandle);
|
|
bRet = TRUE;
|
|
}
|
|
|
|
SetErrorMode(OldMode);
|
|
|
|
return (bRet);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// TransNum
|
|
//
|
|
// Converts a number string to a dword value (in hex).
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD TransNum(
|
|
LPTSTR lpsz)
|
|
{
|
|
DWORD dw = 0L;
|
|
TCHAR c;
|
|
|
|
while (*lpsz)
|
|
{
|
|
c = *lpsz++;
|
|
|
|
if (c >= TEXT('A') && c <= TEXT('F'))
|
|
{
|
|
c -= TEXT('A') - 0xa;
|
|
}
|
|
else if (c >= TEXT('0') && c <= TEXT('9'))
|
|
{
|
|
c -= TEXT('0');
|
|
}
|
|
else if (c >= TEXT('a') && c <= TEXT('f'))
|
|
{
|
|
c -= TEXT('a') - 0xa;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
dw *= 0x10;
|
|
dw += c;
|
|
}
|
|
return (dw);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Item_Has_Digits
|
|
//
|
|
// Return true if the combo box specified by item in the property sheet
|
|
// specified by the dialog handle contains any digits.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Item_Has_Digits(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
BOOL Allow_Empty)
|
|
{
|
|
TCHAR szBuf[SIZE_128];
|
|
LPTSTR lpszBuf = szBuf;
|
|
HWND hCtrl = GetDlgItem(hDlg, nItemId);
|
|
int dwIndex = ComboBox_GetCurSel(hCtrl);
|
|
|
|
//
|
|
// If there is no selection, get whatever is in the edit box.
|
|
//
|
|
if (dwIndex == CB_ERR)
|
|
{
|
|
dwIndex = GetDlgItemText(hDlg, nItemId, szBuf, SIZE_128);
|
|
if (dwIndex)
|
|
{
|
|
//
|
|
// Get text succeeded.
|
|
//
|
|
szBuf[dwIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get text failed.
|
|
//
|
|
dwIndex = CB_ERR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ComboBox_GetLBText(hCtrl, dwIndex, szBuf);
|
|
}
|
|
|
|
if (dwIndex != CB_ERR)
|
|
{
|
|
while (*lpszBuf)
|
|
{
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*lpszBuf))
|
|
{
|
|
//
|
|
// Skip 2 bytes in the array.
|
|
//
|
|
lpszBuf += 2;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ((*lpszBuf >= CHAR_ZERO) && (*lpszBuf <= CHAR_NINE))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
lpszBuf++;
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// The data retrieval failed.
|
|
// If !Allow_Empty, just return TRUE.
|
|
//
|
|
return (!Allow_Empty);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Item_Has_Digits_Or_Invalid_Chars
|
|
//
|
|
// Return true if the combo box specified by item in the property sheet
|
|
// specified by the dialog handle contains any digits or any of the
|
|
// given invalid characters.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Item_Has_Digits_Or_Invalid_Chars(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
BOOL Allow_Empty,
|
|
LPTSTR pInvalid)
|
|
{
|
|
TCHAR szBuf[SIZE_128];
|
|
LPTSTR lpszBuf = szBuf;
|
|
HWND hCtrl = GetDlgItem(hDlg, nItemId);
|
|
int dwIndex = ComboBox_GetCurSel(hCtrl);
|
|
|
|
//
|
|
// If there is no selection, get whatever is in the edit box.
|
|
//
|
|
if (dwIndex == CB_ERR)
|
|
{
|
|
dwIndex = GetDlgItemText(hDlg, nItemId, szBuf, SIZE_128);
|
|
if (dwIndex)
|
|
{
|
|
//
|
|
// Get text succeeded.
|
|
//
|
|
szBuf[dwIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get text failed.
|
|
//
|
|
dwIndex = CB_ERR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwIndex = ComboBox_GetLBText(hCtrl, dwIndex, szBuf);
|
|
}
|
|
|
|
if (dwIndex != CB_ERR)
|
|
{
|
|
while (*lpszBuf)
|
|
{
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*lpszBuf))
|
|
{
|
|
//
|
|
// Skip 2 bytes in the array.
|
|
//
|
|
lpszBuf += 2;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ( ((*lpszBuf >= CHAR_ZERO) && (*lpszBuf <= CHAR_NINE)) ||
|
|
(_tcschr(pInvalid, *lpszBuf)) )
|
|
{
|
|
return (TRUE);
|
|
}
|
|
lpszBuf++;
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// The data retrieval failed.
|
|
// If !Allow_Empty, just return TRUE.
|
|
//
|
|
return (!Allow_Empty);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Item_Check_Invalid_Chars
|
|
//
|
|
// Return true if the input string contains any characters that are not in
|
|
// lpCkChars or in the string contained in the check id control combo box.
|
|
// If there is an invalid character and the character is contained in
|
|
// lpChgCase, change the invalid character's case so that it will be a
|
|
// vaild character.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Item_Check_Invalid_Chars(
|
|
HWND hDlg,
|
|
LPTSTR lpszBuf,
|
|
LPTSTR lpCkChars,
|
|
int nCkIdStr,
|
|
BOOL Allow_Empty,
|
|
LPTSTR lpChgCase,
|
|
int nItemId)
|
|
{
|
|
TCHAR szCkBuf[SIZE_128];
|
|
LPTSTR lpCCaseChar;
|
|
LPTSTR lpszSaveBuf = lpszBuf;
|
|
int nCkBufLen;
|
|
BOOL bInQuote = FALSE;
|
|
BOOL UpdateEditTest = FALSE;
|
|
HWND hCtrl = GetDlgItem(hDlg, nCkIdStr);
|
|
DWORD dwIndex = ComboBox_GetCurSel(hCtrl);
|
|
BOOL TextFromEditBox = (ComboBox_GetCurSel(GetDlgItem(hDlg, nItemId)) == CB_ERR);
|
|
|
|
if (!lpszBuf)
|
|
{
|
|
return (!Allow_Empty);
|
|
}
|
|
|
|
if (dwIndex != CB_ERR)
|
|
{
|
|
nCkBufLen = ComboBox_GetLBText(hCtrl, dwIndex, szCkBuf);
|
|
if (nCkBufLen == CB_ERR)
|
|
{
|
|
nCkBufLen = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No selection, so pull the string from the edit portion.
|
|
//
|
|
nCkBufLen = GetDlgItemText(hDlg, nCkIdStr, szCkBuf, SIZE_128);
|
|
szCkBuf[nCkBufLen] = 0;
|
|
}
|
|
|
|
while (*lpszBuf)
|
|
{
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*lpszBuf))
|
|
{
|
|
//
|
|
// If the the text is in the midst of a quote, skip it.
|
|
// Otherwise, if there is a string from the check ID to
|
|
// compare, determine if the current string is equal to the
|
|
// string in the combo box. If it is not equal, return true
|
|
// (there are invalid characters). Otherwise, skip the entire
|
|
// length of the "check" combo box's string in lpszBuf.
|
|
//
|
|
if (bInQuote)
|
|
{
|
|
lpszBuf += 2;
|
|
}
|
|
else if (nCkBufLen &&
|
|
lstrlen(lpszBuf) >= nCkBufLen)
|
|
{
|
|
if (CompareString( UserLocaleID,
|
|
0,
|
|
szCkBuf,
|
|
nCkBufLen,
|
|
lpszBuf,
|
|
nCkBufLen ) != CSTR_EQUAL)
|
|
{
|
|
//
|
|
// Invalid DB character.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
lpszBuf += nCkBufLen;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (bInQuote)
|
|
{
|
|
bInQuote = (*lpszBuf != CHAR_QUOTE);
|
|
lpszBuf++;
|
|
}
|
|
else if (_tcschr(lpCkChars, *lpszBuf))
|
|
{
|
|
lpszBuf++;
|
|
}
|
|
else if (TextFromEditBox &&
|
|
(lpCCaseChar = _tcschr(lpChgCase, *lpszBuf), lpCCaseChar))
|
|
{
|
|
*lpszBuf = lpCkChars[lpCCaseChar - lpChgCase];
|
|
UpdateEditTest = TRUE;
|
|
lpszBuf++;
|
|
}
|
|
else if (*lpszBuf == CHAR_QUOTE)
|
|
{
|
|
lpszBuf++;
|
|
bInQuote = TRUE;
|
|
}
|
|
else if ( (nCkBufLen) &&
|
|
(lstrlen(lpszBuf) >= nCkBufLen) &&
|
|
(CompareString( UserLocaleID,
|
|
0,
|
|
szCkBuf,
|
|
nCkBufLen,
|
|
lpszBuf,
|
|
nCkBufLen ) == CSTR_EQUAL) )
|
|
{
|
|
lpszBuf += nCkBufLen;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid character.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parsing passed.
|
|
// If the edit text changed, update edit box only if returning true.
|
|
//
|
|
if (!bInQuote && UpdateEditTest)
|
|
{
|
|
return (!SetDlgItemText(hDlg, nItemId, lpszSaveBuf));
|
|
}
|
|
|
|
//
|
|
// If there are unmatched quotes return TRUE. Otherwise, return FALSE.
|
|
//
|
|
if (bInQuote)
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// No_Numerals_Error
|
|
//
|
|
// Display the no numerals allowed in "some control" error.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void No_Numerals_Error(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
int iStrId)
|
|
{
|
|
TCHAR szBuf[SIZE_300];
|
|
TCHAR szBuf2[SIZE_128];
|
|
TCHAR szErrorMessage[SIZE_300+SIZE_128];
|
|
|
|
LoadString(hInstance, IDS_LOCALE_NO_NUMS_IN, szBuf, SIZE_300);
|
|
LoadString(hInstance, iStrId, szBuf2, SIZE_128);
|
|
//wsprintf(szErrorMessage, szBuf, szBuf2);
|
|
if(FAILED(StringCchPrintf(szErrorMessage, SIZE_300+SIZE_128, szBuf, szBuf2)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
MessageBox(hDlg, szErrorMessage, NULL, MB_OK | MB_ICONINFORMATION);
|
|
SetFocus(GetDlgItem(hDlg, nItemId));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Invalid_Chars_Error
|
|
//
|
|
// Display the invalid chars in "some style" error.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Invalid_Chars_Error(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
int iStrId)
|
|
{
|
|
TCHAR szBuf[SIZE_300];
|
|
TCHAR szBuf2[SIZE_128];
|
|
TCHAR szErrorMessage[SIZE_300+SIZE_128];
|
|
|
|
LoadString(hInstance, IDS_LOCALE_STYLE_ERR, szBuf, SIZE_300);
|
|
LoadString(hInstance, iStrId, szBuf2, SIZE_128);
|
|
//wsprintf(szErrorMessage, szBuf, szBuf2);
|
|
if(FAILED(StringCchPrintf(szErrorMessage, SIZE_300+SIZE_128, szBuf, szBuf2)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
MessageBox(hDlg, szErrorMessage, NULL, MB_OK | MB_ICONINFORMATION);
|
|
SetFocus(GetDlgItem(hDlg, nItemId));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Localize_Combobox_Styles
|
|
//
|
|
// Transform either all date or time style, as indicated by LCType, in
|
|
// the indicated combobox from a value that the NLS will provide to a
|
|
// localized value.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Localize_Combobox_Styles(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
LCTYPE LCType)
|
|
{
|
|
BOOL bInQuote = FALSE;
|
|
BOOL Map_Char = TRUE;
|
|
TCHAR szBuf1[SIZE_128];
|
|
TCHAR szBuf2[SIZE_128];
|
|
LPTSTR lpszInBuf = szBuf1;
|
|
LPTSTR lpszOutBuf = szBuf2;
|
|
HWND hCtrl = GetDlgItem(hDlg, nItemId);
|
|
DWORD ItemCnt = ComboBox_GetCount(hCtrl);
|
|
DWORD Position = 0;
|
|
DWORD dwIndex;
|
|
|
|
if (!Styles_Localized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (Position < ItemCnt)
|
|
{
|
|
//
|
|
// Could check character count with CB_GETLBTEXTLEN to make sure
|
|
// that the item text will fit in 128, but max values for these
|
|
// items is 79 chars.
|
|
//
|
|
dwIndex = ComboBox_GetLBText(hCtrl, Position, szBuf1);
|
|
if (dwIndex != CB_ERR)
|
|
{
|
|
lpszInBuf = szBuf1;
|
|
lpszOutBuf = szBuf2;
|
|
while (*lpszInBuf)
|
|
{
|
|
Map_Char = TRUE;
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*lpszInBuf))
|
|
{
|
|
//
|
|
// Copy any double byte character straight through.
|
|
//
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (*lpszInBuf == CHAR_QUOTE)
|
|
{
|
|
bInQuote = !bInQuote;
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
}
|
|
else
|
|
{
|
|
if (!bInQuote)
|
|
{
|
|
if (LCType == LOCALE_STIMEFORMAT ||
|
|
LCType == LOCALE_SLONGDATE)
|
|
{
|
|
Map_Char = FALSE;
|
|
if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("H"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStyleH[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStyleH))
|
|
{
|
|
*lpszOutBuf++ = szStyleH[1];
|
|
}
|
|
#endif
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("h"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStyleh[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStyleh))
|
|
{
|
|
*lpszOutBuf++ = szStyleh[1];
|
|
}
|
|
#endif
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("m"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStylem[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStylem))
|
|
{
|
|
*lpszOutBuf++ = szStylem[1];
|
|
}
|
|
#endif
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("s"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStyles[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStyles))
|
|
{
|
|
*lpszOutBuf++ = szStyles[1];
|
|
}
|
|
#endif
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("t"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStylet[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStylet))
|
|
{
|
|
*lpszOutBuf++ = szStylet[1];
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Map_Char = TRUE;
|
|
}
|
|
}
|
|
if (LCType == LOCALE_SSHORTDATE ||
|
|
(LCType == LOCALE_SLONGDATE && Map_Char))
|
|
{
|
|
Map_Char = FALSE;
|
|
if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("d"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStyled[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStyled))
|
|
{
|
|
*lpszOutBuf++ = szStyled[1];
|
|
}
|
|
#endif
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("M"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStyleM[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStyleM))
|
|
{
|
|
*lpszOutBuf++ = szStyleM[1];
|
|
}
|
|
#endif
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
1,
|
|
TEXT("y"),
|
|
1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = szStyley[0];
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*szStyley))
|
|
{
|
|
*lpszOutBuf++ = szStyley[1];
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Map_Char = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Map_Char)
|
|
{
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
}
|
|
else
|
|
{
|
|
lpszInBuf++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Append null to localized string.
|
|
//
|
|
*lpszOutBuf = 0;
|
|
|
|
ComboBox_DeleteString(hCtrl, Position);
|
|
ComboBox_InsertString(hCtrl, Position, szBuf2);
|
|
}
|
|
Position++;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NLSize_Style
|
|
//
|
|
// Transform either date or time style, as indicated by LCType, from a
|
|
// localized value to one that the NLS API will recognize.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL NLSize_Style(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
LPTSTR lpszOutBuf,
|
|
LCTYPE LCType)
|
|
{
|
|
BOOL bInQuote = FALSE;
|
|
BOOL Map_Char = TRUE;
|
|
TCHAR szBuf[SIZE_128];
|
|
LPTSTR lpszInBuf = szBuf;
|
|
LPTSTR lpNLSChars1;
|
|
LPTSTR lpNLSChars2;
|
|
HWND hCtrl = GetDlgItem(hDlg, nItemId);
|
|
DWORD dwIndex = ComboBox_GetCurSel(hCtrl);
|
|
BOOL TextFromEditBox = dwIndex == CB_ERR;
|
|
int Cmp_Size;
|
|
#ifndef UNICODE
|
|
BOOL Is_Dbl = FALSE;
|
|
#endif
|
|
|
|
//
|
|
// If there is no selection, get whatever is in the edit box.
|
|
//
|
|
if (TextFromEditBox)
|
|
{
|
|
dwIndex = GetDlgItemText(hDlg, nItemId, szBuf, SIZE_128);
|
|
if (dwIndex)
|
|
{
|
|
//
|
|
// Get text succeeded.
|
|
//
|
|
szBuf[dwIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get text failed.
|
|
//
|
|
dwIndex = (DWORD)CB_ERR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwIndex = ComboBox_GetLBText(hCtrl, dwIndex, szBuf);
|
|
}
|
|
|
|
if (!Styles_Localized)
|
|
{
|
|
//lstrcpy(lpszOutBuf, lpszInBuf);
|
|
// The string is either the long date, the short date, or
|
|
// the long time -- all of them are the same max. length.
|
|
if(FAILED(StringCchCopy(lpszOutBuf, MAX_SLONGDATE, lpszInBuf)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
switch (LCType)
|
|
{
|
|
case ( LOCALE_STIMEFORMAT ) :
|
|
{
|
|
lpNLSChars1 = szTLetters;
|
|
lpNLSChars2 = szTCaseSwap;
|
|
break;
|
|
}
|
|
case ( LOCALE_SLONGDATE ) :
|
|
{
|
|
lpNLSChars1 = szLDLetters;
|
|
lpNLSChars2 = szLDCaseSwap;
|
|
break;
|
|
}
|
|
case ( LOCALE_SSHORTDATE ) :
|
|
{
|
|
lpNLSChars1 = szSDLetters;
|
|
lpNLSChars2 = szSDCaseSwap;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (*lpszInBuf)
|
|
{
|
|
Map_Char = TRUE;
|
|
#ifdef UNICODE
|
|
Cmp_Size = 1;
|
|
#else
|
|
Is_Dbl = IsDBCSLeadByte(*lpszInBuf);
|
|
Cmp_Size = Is_Dbl ? 2 : 1;
|
|
#endif
|
|
|
|
if (*lpszInBuf == CHAR_QUOTE)
|
|
{
|
|
bInQuote = !bInQuote;
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
}
|
|
else
|
|
{
|
|
if (!bInQuote)
|
|
{
|
|
if (LCType == LOCALE_STIMEFORMAT || LCType == LOCALE_SLONGDATE)
|
|
{
|
|
Map_Char = FALSE;
|
|
if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStyleH,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_CAP_H;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStyleh,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_SML_H;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStylem,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_SML_M;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStyles,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_SML_S;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStylet,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_SML_T;
|
|
}
|
|
else
|
|
{
|
|
Map_Char = TRUE;
|
|
}
|
|
}
|
|
if (LCType == LOCALE_SSHORTDATE ||
|
|
(LCType == LOCALE_SLONGDATE && Map_Char))
|
|
{
|
|
Map_Char = FALSE;
|
|
if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStyled,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_SML_D;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStyleM,
|
|
-1) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_CAP_M;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
szStyley,
|
|
-1 ) == CSTR_EQUAL)
|
|
{
|
|
*lpszOutBuf++ = CHAR_SML_Y;
|
|
}
|
|
else if (CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
TEXT("g"),
|
|
-1) == CSTR_EQUAL)
|
|
{
|
|
//
|
|
// g is not localized, but it's legal.
|
|
//
|
|
*lpszOutBuf++ = CHAR_SML_G;
|
|
}
|
|
else
|
|
{
|
|
Map_Char = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Map_Char)
|
|
{
|
|
//
|
|
// Just copy chars in quotes or chars that are not
|
|
// recognized. Leave the char checking to the other
|
|
// function. However, do check for NLS standard chars
|
|
// that were not supposed to be here due to localization.
|
|
//
|
|
if ( !bInQuote &&
|
|
#ifndef UNICODE
|
|
!Is_Dbl &&
|
|
#endif
|
|
(CompareString( UserLocaleID,
|
|
0,
|
|
lpszInBuf,
|
|
Cmp_Size,
|
|
TEXT(" "),
|
|
-1 ) != CSTR_EQUAL) &&
|
|
( _tcschr(lpNLSChars1, *lpszInBuf) ||
|
|
_tcschr(lpNLSChars2, *lpszInBuf) ) )
|
|
{
|
|
return (TRUE);
|
|
}
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
#ifndef UNICODE
|
|
if (Is_Dbl)
|
|
{
|
|
//
|
|
// Copy 2nd byte.
|
|
//
|
|
*lpszOutBuf++ = *lpszInBuf++;
|
|
}
|
|
#endif
|
|
}
|
|
#ifndef UNICODE
|
|
else if (Is_Dbl)
|
|
{
|
|
lpszInBuf += 2;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
lpszInBuf++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Append null to localized string.
|
|
//
|
|
*lpszOutBuf = 0;
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
#ifndef WINNT
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SDate3_1_Compatibility
|
|
//
|
|
// There is a requirement to keep windows 3.1 compatibility in the
|
|
// registry (win.ini). Only allow 1 or 2 'M's, 1 or 2 'd's, and
|
|
// 2 or 4 'y's. The remainder of the date style is compatible.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SDate3_1_Compatibility(
|
|
LPTSTR lpszBuf,
|
|
int Buf_Size)
|
|
{
|
|
BOOL bInQuote = FALSE;
|
|
int Index, Del_Cnt;
|
|
int Len = lstrlen(lpszBuf);
|
|
int MCnt = 0; // running total of Ms
|
|
int dCnt = 0; // running total of ds
|
|
int yCnt = 0; // running total of ys
|
|
|
|
while (*lpszBuf)
|
|
{
|
|
#ifndef UNICODE
|
|
if (IsDBCSLeadByte(*lpszBuf))
|
|
{
|
|
lpszBuf += 2;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (bInQuote)
|
|
{
|
|
bInQuote = (*lpszBuf != CHAR_QUOTE);
|
|
lpszBuf++;
|
|
}
|
|
else if (*lpszBuf == CHAR_CAP_M)
|
|
{
|
|
if (MCnt++ < 2)
|
|
{
|
|
lpszBuf++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// At least 1 extra M. Move all of the chars, including
|
|
// null, up by Del_Cnt.
|
|
//
|
|
Del_Cnt = 1;
|
|
Index = 1;
|
|
while (lpszBuf[Index++] == CHAR_CAP_M)
|
|
{
|
|
Del_Cnt++;
|
|
}
|
|
for (Index = 0; Index <= Len - Del_Cnt + 1; Index++)
|
|
{
|
|
lpszBuf[Index] = lpszBuf[Index + Del_Cnt];
|
|
}
|
|
Len -= Del_Cnt;
|
|
}
|
|
}
|
|
else if (*lpszBuf == CHAR_SML_D)
|
|
{
|
|
if (dCnt++ < 2)
|
|
{
|
|
lpszBuf++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// At least 1 extra d. Move all of the chars, including
|
|
// null, up by Del_Cnt.
|
|
//
|
|
Del_Cnt = 1;
|
|
Index = 1;
|
|
while (lpszBuf[Index++] == CHAR_SML_D)
|
|
{
|
|
Del_Cnt++;
|
|
}
|
|
for (Index = 0; Index <= Len - Del_Cnt + 1; Index++)
|
|
{
|
|
lpszBuf[Index] = lpszBuf[Index + Del_Cnt];
|
|
}
|
|
Len -= Del_Cnt;
|
|
}
|
|
}
|
|
else if (*lpszBuf == CHAR_SML_Y)
|
|
{
|
|
if (yCnt == 0 || yCnt == 2)
|
|
{
|
|
if (lpszBuf[1] == CHAR_SML_Y)
|
|
{
|
|
lpszBuf += 2;
|
|
yCnt += 2;
|
|
}
|
|
else if (Len < Buf_Size - 1)
|
|
{
|
|
//
|
|
// Odd # of ys & room for one more.
|
|
// Move the remaining text down by 1 (the y will
|
|
// be copied).
|
|
//
|
|
// Use Del_Cnt for unparsed string length.
|
|
//
|
|
Del_Cnt = lstrlen(lpszBuf);
|
|
for (Index = Del_Cnt + 1; Index > 0; Index--)
|
|
{
|
|
lpszBuf[Index] = lpszBuf[Index - 1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No room, move all of the chars, including null,
|
|
// up by 1.
|
|
//
|
|
for (Index = 0; Index <= Len; Index++)
|
|
{
|
|
lpszBuf[Index] = lpszBuf[Index + 1];
|
|
}
|
|
Len--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// At least 1 extra y. Move all of the chars, including
|
|
// null, up by Del_Cnt.
|
|
//
|
|
Del_Cnt = 1;
|
|
Index = 1;
|
|
while (lpszBuf[Index++] == CHAR_SML_Y)
|
|
{
|
|
Del_Cnt++;
|
|
}
|
|
for (Index = 0; Index <= Len - Del_Cnt + 1; Index++)
|
|
{
|
|
lpszBuf[Index] = lpszBuf[Index + Del_Cnt];
|
|
}
|
|
Len -= Del_Cnt;
|
|
}
|
|
}
|
|
else if (*lpszBuf == CHAR_QUOTE)
|
|
{
|
|
lpszBuf++;
|
|
bInQuote = TRUE;
|
|
}
|
|
else
|
|
{
|
|
lpszBuf++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Locale_Values
|
|
//
|
|
// Set_Locale_Values is called for each LCType that has either been
|
|
// directly modified via a user change, or indirectly modified by the user
|
|
// changing the regional locale setting. When a dialog handle is available,
|
|
// Set_Locale_Values will pull the new value of the LCType from the
|
|
// appropriate list box (this is a direct change), register it in the
|
|
// locale database, and then update the registry string. If no dialog
|
|
// handle is available, it will simply update the registry string based on
|
|
// the locale registry. If the registration succeeds, return true.
|
|
// Otherwise, return false.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Set_Locale_Values(
|
|
HWND hDlg,
|
|
LCTYPE LCType,
|
|
int nItemId,
|
|
LPTSTR lpIniStr,
|
|
BOOL bValue,
|
|
int Ordinal_Offset,
|
|
LPTSTR Append_Str,
|
|
LPTSTR NLS_Str)
|
|
{
|
|
DWORD dwIndex;
|
|
BOOL bSuccess = TRUE;
|
|
int cchBuf = SIZE_128 + 1;
|
|
TCHAR szBuf[SIZE_128 + 1];
|
|
LPTSTR pBuf = szBuf;
|
|
HWND hCtrl;
|
|
|
|
if (NLS_Str)
|
|
{
|
|
//
|
|
// Use a non-localized string.
|
|
//
|
|
//lstrcpy(pBuf, NLS_Str);
|
|
if(FAILED(StringCchCopy(pBuf, cchBuf, NLS_Str)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
bSuccess = SetLocaleInfo(UserLocaleID, LCType, pBuf);
|
|
}
|
|
else if (hDlg)
|
|
{
|
|
//
|
|
// Get the new value from the list box.
|
|
//
|
|
hCtrl = GetDlgItem(hDlg, nItemId);
|
|
dwIndex = ComboBox_GetCurSel(hCtrl);
|
|
|
|
//
|
|
// If there is no selection, get whatever is in the edit box.
|
|
//
|
|
if (dwIndex == CB_ERR)
|
|
{
|
|
dwIndex = GetDlgItemText(hDlg, nItemId, pBuf, SIZE_128);
|
|
if (dwIndex)
|
|
{
|
|
//
|
|
// Get text succeeded.
|
|
//
|
|
pBuf[dwIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get text failed.
|
|
// Allow the AM/PM symbols to be set as empty strings.
|
|
// Otherwise, fail.
|
|
//
|
|
if ((LCType == LOCALE_S1159) || (LCType == LOCALE_S2359))
|
|
{
|
|
pBuf[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
bSuccess = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if (bValue)
|
|
{
|
|
//
|
|
// Need string representation of ordinal locale value.
|
|
//
|
|
if (nItemId == IDC_CALENDAR_TYPE)
|
|
{
|
|
dwIndex = (DWORD)ComboBox_GetItemData(hCtrl, dwIndex);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Ordinal_Offset is required since calendar is 1 based,
|
|
// not 0 based.
|
|
//
|
|
dwIndex += Ordinal_Offset;
|
|
}
|
|
|
|
//
|
|
// Special case the grouping string.
|
|
//
|
|
if (nItemId == IDC_NUM_DIGITS_GROUP)
|
|
{
|
|
switch (dwIndex)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//lstrcpy(pBuf, TEXT("0"));
|
|
if(FAILED(StringCchCopy(pBuf, cchBuf, TEXT("0"))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
break;
|
|
}
|
|
case ( 1 ) :
|
|
{
|
|
//lstrcpy(pBuf, TEXT("3"));
|
|
if(FAILED(StringCchCopy(pBuf, cchBuf, TEXT("3"))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
break;
|
|
}
|
|
case ( 2 ) :
|
|
{
|
|
//lstrcpy(pBuf, TEXT("3;2"));
|
|
if(FAILED(StringCchCopy(pBuf, cchBuf, TEXT("3;2"))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
break;
|
|
}
|
|
case ( 3 ) :
|
|
{
|
|
//wsprintf( pBuf,
|
|
// TEXT("%d"),
|
|
// ComboBox_GetItemData(hCtrl, dwIndex) );
|
|
if(FAILED(StringCchPrintf( pBuf,
|
|
cchBuf,
|
|
TEXT("%d"),
|
|
ComboBox_GetItemData(hCtrl, dwIndex))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (dwIndex < cInt_Str)
|
|
{
|
|
//lstrcpy(pBuf, aInt_Str[dwIndex]);
|
|
if(FAILED(StringCchCopy(pBuf, cchBuf, aInt_Str[dwIndex])))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//wsprintf(pBuf, TEXT("%d"), dwIndex);
|
|
if(FAILED(StringCchPrintf(pBuf, cchBuf, TEXT("%d"), dwIndex)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get actual value of locale data.
|
|
//
|
|
bSuccess = (ComboBox_GetLBText(hCtrl, dwIndex, pBuf) != CB_ERR);
|
|
}
|
|
|
|
if (bSuccess)
|
|
{
|
|
//
|
|
// If edit text, index value or selection text succeeds...
|
|
//
|
|
if (Append_Str)
|
|
{
|
|
//lstrcat(pBuf, Append_Str);
|
|
if(FAILED(StringCchCat(pBuf, cchBuf, Append_Str)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If this is sNativeDigits, the LPK is installed, and the
|
|
// first char is 0x206f (nominal digit shapes), then do not
|
|
// store the first char in the registry.
|
|
//
|
|
if ((LCType == LOCALE_SNATIVEDIGITS) &&
|
|
(bLPKInstalled) &&
|
|
(pBuf[0] == TEXT('\x206f')))
|
|
{
|
|
pBuf++;
|
|
}
|
|
bSuccess = SetLocaleInfo( UserLocaleID, LCType, pBuf );
|
|
}
|
|
}
|
|
|
|
if (lpIniStr && bSuccess)
|
|
{
|
|
//
|
|
// Set the registry string to the string that is stored in the list
|
|
// box. If there is no dialog handle, get the required string
|
|
// locale value from the NLS function. Write the associated string
|
|
// into the registry.
|
|
//
|
|
if (!hDlg && !NLS_Str)
|
|
{
|
|
GetLocaleInfo( UserLocaleID,
|
|
LCType | LOCALE_NOUSEROVERRIDE,
|
|
pBuf,
|
|
SIZE_128 );
|
|
}
|
|
|
|
#ifndef WINNT
|
|
//
|
|
// There is a requirement to keep windows 3.1 compatibility in the
|
|
// win.ini. There are some win32 short date formats that are
|
|
// incompatible with exisiting win 3.1 apps... modify these styles.
|
|
//
|
|
if (LCType == LOCALE_SSHORTDATE)
|
|
{
|
|
SDate3_1_Compatibility(pBuf, SIZE_128);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check the value whether it is empty or not.
|
|
//
|
|
switch (LCType)
|
|
{
|
|
case ( LOCALE_STHOUSAND ) :
|
|
case ( LOCALE_SDECIMAL ) :
|
|
case ( LOCALE_SDATE ) :
|
|
case ( LOCALE_STIME ) :
|
|
case ( LOCALE_SLIST ) :
|
|
{
|
|
CheckEmptyString(pBuf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the locale information in the registry.
|
|
//
|
|
// NOTE: We want to use SetLocaleInfo if possible so that the
|
|
// NLS cache is updated right away. Otherwise, we'll
|
|
// simply use WriteProfileString.
|
|
//
|
|
if (!SetLocaleInfo(UserLocaleID, LCType, pBuf))
|
|
{
|
|
WriteProfileString(szIntl, lpIniStr, pBuf);
|
|
}
|
|
}
|
|
else if (!bSuccess)
|
|
{
|
|
LoadString(hInstance, IDS_LOCALE_SET_ERROR, szBuf, SIZE_128);
|
|
MessageBox(hDlg, szBuf, NULL, MB_OK | MB_ICONINFORMATION);
|
|
SetFocus(GetDlgItem(hDlg, nItemId));
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_List_Values
|
|
//
|
|
// Set_List_Values is called several times for each drop down list which is
|
|
// populated via an enum function. The first call to this function should
|
|
// be with a valid dialog handle, valid dialog item ID, and null string
|
|
// value. If the function is not already in use, it will clear the list box
|
|
// and store the handle and id information for the subsequent calls to this
|
|
// function that will be made by the enumeration function. The calls from
|
|
// the enumeration function will add the specified string values to the
|
|
// list box. When the enumeration function is complete, this function
|
|
// should be called with a null dialog handle, the valid dialog item id,
|
|
// and a null string value. This will clear all of the state information,
|
|
// including the lock flag.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Set_List_Values(
|
|
HWND hDlg,
|
|
int nItemId,
|
|
LPTSTR lpValueString)
|
|
{
|
|
static BOOL bLock, bString;
|
|
static HWND hDialog;
|
|
static int nDItemId, nID;
|
|
|
|
if (!lpValueString)
|
|
{
|
|
//
|
|
// Clear the lock if there is no dialog handle and the item IDs
|
|
// match.
|
|
//
|
|
if (bLock && !hDlg && (nItemId == nDItemId))
|
|
{
|
|
if (nItemId != IDC_CALENDAR_TYPE)
|
|
{
|
|
hDialog = 0;
|
|
nDItemId = 0;
|
|
bLock = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (bString)
|
|
{
|
|
hDialog = 0;
|
|
nDItemId = 0;
|
|
bLock = FALSE;
|
|
bString = FALSE;
|
|
}
|
|
else
|
|
{
|
|
nID = 0;
|
|
bString = TRUE;
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Return false, for failure, if the function is locked or if the
|
|
// handle or ID parameters are null.
|
|
//
|
|
if (bLock || !hDlg || !nItemId)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Prepare for subsequent calls to populate the list box.
|
|
//
|
|
bLock = TRUE;
|
|
hDialog = hDlg;
|
|
nDItemId = nItemId;
|
|
}
|
|
else if (bLock && hDialog && nDItemId)
|
|
{
|
|
//
|
|
// Add the string to the list box.
|
|
//
|
|
if (!bString)
|
|
{
|
|
ComboBox_InsertString( GetDlgItem(hDialog, nDItemId),
|
|
-1,
|
|
lpValueString );
|
|
}
|
|
else
|
|
{
|
|
ComboBox_SetItemData( GetDlgItem(hDialog, nDItemId),
|
|
nID++,
|
|
Intl_StrToLong(lpValueString) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DropDown_Use_Locale_Values
|
|
//
|
|
// Get the user locale value for the locale type specifier. Add it to
|
|
// the list box and make this value the current selection. If the user
|
|
// locale value for the locale type is different than the system value,
|
|
// add the system value to the list box. If the user default is different
|
|
// than the user override, add the user default.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DropDown_Use_Locale_Values(
|
|
HWND hDlg,
|
|
LCTYPE LCType,
|
|
int nItemId)
|
|
{
|
|
TCHAR szBuf[SIZE_128];
|
|
TCHAR szCmpBuf1[SIZE_128];
|
|
TCHAR szCmpBuf2[SIZE_128];
|
|
HWND hCtrl = GetDlgItem(hDlg, nItemId);
|
|
int ctr;
|
|
|
|
if (GetLocaleInfo(UserLocaleID, LCType, szBuf, SIZE_128))
|
|
{
|
|
ComboBox_SetCurSel(hCtrl, ComboBox_InsertString(hCtrl, -1, szBuf));
|
|
|
|
//
|
|
// If the system setting is different, add it to the list box.
|
|
//
|
|
if (GetLocaleInfo( SysLocaleID,
|
|
LCType | LOCALE_NOUSEROVERRIDE,
|
|
szCmpBuf1,
|
|
SIZE_128 ))
|
|
{
|
|
if (CompareString( UserLocaleID,
|
|
0,
|
|
szCmpBuf1,
|
|
-1,
|
|
szBuf,
|
|
-1 ) != CSTR_EQUAL)
|
|
{
|
|
ComboBox_InsertString(hCtrl, -1, szCmpBuf1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the default user locale setting is different than the user
|
|
// overridden setting and different than the system setting, add
|
|
// it to the list box.
|
|
//
|
|
if (GetLocaleInfo( UserLocaleID,
|
|
LCType | LOCALE_NOUSEROVERRIDE,
|
|
szCmpBuf2,
|
|
SIZE_128 ))
|
|
{
|
|
if (CompareString(UserLocaleID, 0, szCmpBuf2, -1, szBuf, -1) != CSTR_EQUAL &&
|
|
CompareString(UserLocaleID, 0, szCmpBuf2, -1, szCmpBuf1, -1) != CSTR_EQUAL)
|
|
{
|
|
ComboBox_InsertString(hCtrl, -1, szCmpBuf2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Failed to get user value, try for system value. If system value
|
|
// fails, display a message box indicating that there was a locale
|
|
// problem.
|
|
//
|
|
if (GetLocaleInfo( SysLocaleID,
|
|
LCType | LOCALE_NOUSEROVERRIDE,
|
|
szBuf,
|
|
SIZE_128 ))
|
|
{
|
|
ComboBox_SetCurSel(hCtrl, ComboBox_InsertString(hCtrl, -1, szBuf));
|
|
}
|
|
else
|
|
{
|
|
MessageBox(hDlg, szLocaleGetError, NULL, MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If it's the date separator, then we want slash, dot, and dash in
|
|
// the list in addition to the user and system settings (if different).
|
|
//
|
|
if (LCType == LOCALE_SDATE)
|
|
{
|
|
for (ctr = 0; ctr < NUM_DATE_SEPARATORS; ctr++)
|
|
{
|
|
if (ComboBox_FindStringExact( hCtrl,
|
|
-1,
|
|
pDateSeparators[ctr] ) == CB_ERR)
|
|
{
|
|
ComboBox_InsertString(hCtrl, -1, pDateSeparators[ctr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If it's the AM symbol, then we want AM in the list in addition
|
|
// to the user and system settings (if different).
|
|
//
|
|
if (LCType == LOCALE_S1159)
|
|
{
|
|
for (ctr = 0; ctr < NUM_AM_SYMBOLS; ctr++)
|
|
{
|
|
if (ComboBox_FindStringExact( hCtrl,
|
|
-1,
|
|
pAMSymbols[ctr] ) == CB_ERR)
|
|
{
|
|
ComboBox_InsertString(hCtrl, -1, pAMSymbols[ctr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If it's the PM symbol, then we want PM in the list in addition
|
|
// to the user and system settings (if different).
|
|
//
|
|
if (LCType == LOCALE_S2359)
|
|
{
|
|
for (ctr = 0; ctr < NUM_PM_SYMBOLS; ctr++)
|
|
{
|
|
if (ComboBox_FindStringExact( hCtrl,
|
|
-1,
|
|
pPMSymbols[ctr] ) == CB_ERR)
|
|
{
|
|
ComboBox_InsertString(hCtrl, -1, pPMSymbols[ctr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// If it's the currency symbol, then we want the Euro symbol and dollar
|
|
// sign in the list in addition to the user and system settings (if
|
|
// different).
|
|
//
|
|
if (LCType == LOCALE_SCURRENCY)
|
|
{
|
|
for (ctr = 0; ctr < NUM_CURRENCY_SYMBOLS; ctr++)
|
|
{
|
|
if (ComboBox_FindStringExact( hCtrl,
|
|
-1,
|
|
pCurrencySymbols[ctr] ) == CB_ERR)
|
|
{
|
|
ComboBox_InsertString(hCtrl, -1, pCurrencySymbols[ctr]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumProc
|
|
//
|
|
// This call back function calls Set_List_Values assuming that whatever
|
|
// code called the NLS enumeration function (or dummied enumeration
|
|
// function) has properly set up Set_List_Values for the list box
|
|
// population.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CALLBACK EnumProc(
|
|
LPTSTR lpValueString)
|
|
{
|
|
return (Set_List_Values(0, 0, lpValueString));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumProcEx
|
|
//
|
|
// This call back function calls Set_List_Values assuming that whatever
|
|
// code called the enumeration function has properly set up
|
|
// Set_List_Values for the list box population.
|
|
// Also, this function fixes the string passed in to contain the correct
|
|
// decimal separator and negative sign, if appropriate.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CALLBACK EnumProcEx(
|
|
LPTSTR lpValueString,
|
|
LPTSTR lpDecimalString,
|
|
LPTSTR lpNegativeString,
|
|
LPTSTR lpSymbolString)
|
|
{
|
|
TCHAR szString[SIZE_128];
|
|
LPTSTR pStr, pValStr, pTemp;
|
|
|
|
|
|
//
|
|
// Simplify things if we have a NULL string.
|
|
//
|
|
if (lpDecimalString && (*lpDecimalString == CHAR_NULL))
|
|
{
|
|
lpDecimalString = NULL;
|
|
}
|
|
if (lpNegativeString && (*lpNegativeString == CHAR_NULL))
|
|
{
|
|
lpNegativeString = NULL;
|
|
}
|
|
if (lpSymbolString && (*lpSymbolString == CHAR_NULL))
|
|
{
|
|
lpSymbolString = NULL;
|
|
}
|
|
|
|
//
|
|
// See if we need to do any substitutions.
|
|
//
|
|
if (lpDecimalString || lpNegativeString || lpSymbolString)
|
|
{
|
|
pValStr = lpValueString;
|
|
pStr = szString;
|
|
|
|
while (*pValStr)
|
|
{
|
|
if (lpDecimalString && (*pValStr == CHAR_DECIMAL))
|
|
{
|
|
//
|
|
// Substitute the current user decimal separator.
|
|
//
|
|
pTemp = lpDecimalString;
|
|
while (*pTemp)
|
|
{
|
|
*pStr = *pTemp;
|
|
pStr++;
|
|
pTemp++;
|
|
}
|
|
}
|
|
else if (lpNegativeString && (*pValStr == CHAR_HYPHEN))
|
|
{
|
|
//
|
|
// Substitute the current user negative sign.
|
|
//
|
|
pTemp = lpNegativeString;
|
|
while (*pTemp)
|
|
{
|
|
*pStr = *pTemp;
|
|
pStr++;
|
|
pTemp++;
|
|
}
|
|
}
|
|
else if (lpSymbolString && (*pValStr == CHAR_INTL_CURRENCY))
|
|
{
|
|
//
|
|
// Substitute the current user currency symbol.
|
|
//
|
|
pTemp = lpSymbolString;
|
|
while (*pTemp)
|
|
{
|
|
*pStr = *pTemp;
|
|
pStr++;
|
|
pTemp++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Simply copy the character.
|
|
//
|
|
*pStr = *pValStr;
|
|
pStr++;
|
|
}
|
|
pValStr++;
|
|
}
|
|
*pStr = CHAR_NULL;
|
|
|
|
return (Set_List_Values(0, 0, szString));
|
|
}
|
|
else
|
|
{
|
|
return (Set_List_Values(0, 0, lpValueString));
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumLeadingZeros
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL EnumLeadingZeros(
|
|
LEADINGZEROS_ENUMPROC lpLeadingZerosEnumProc,
|
|
LCID LCId,
|
|
DWORD dwFlags)
|
|
{
|
|
TCHAR szBuf[SIZE_128];
|
|
TCHAR szDecimal[SIZE_128];
|
|
|
|
//
|
|
// If there is no enum proc, return false to indicate a failure.
|
|
//
|
|
if (!lpLeadingZerosEnumProc)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the Decimal Separator for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SDECIMAL, szDecimal, SIZE_128) ||
|
|
((szDecimal[0] == CHAR_DECIMAL) && (szDecimal[1] == CHAR_NULL)))
|
|
{
|
|
szDecimal[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Call enum proc with the NO string. Check to make sure the
|
|
// enum proc requests continuation.
|
|
//
|
|
LoadString(hInstance, IDS_NO_LZERO, szBuf, SIZE_128);
|
|
if (!lpLeadingZerosEnumProc(szBuf, szDecimal, NULL, NULL))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Call enum proc with the YES string.
|
|
//
|
|
LoadString(hInstance, IDS_LZERO, szBuf, SIZE_128);
|
|
lpLeadingZerosEnumProc(szBuf, szDecimal, NULL, NULL);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumNegNumFmt
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL EnumNegNumFmt(
|
|
NEGNUMFMT_ENUMPROC lpNegNumFmtEnumProc,
|
|
LCID LCId,
|
|
DWORD dwFlags)
|
|
{
|
|
TCHAR szDecimal[SIZE_128];
|
|
TCHAR szNeg[SIZE_128];
|
|
int ctr;
|
|
|
|
//
|
|
// If there is no enum proc, return false to indicate a failure.
|
|
//
|
|
if (!lpNegNumFmtEnumProc)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the Decimal Separator for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SDECIMAL, szDecimal, SIZE_128) ||
|
|
((szDecimal[0] == CHAR_DECIMAL) && (szDecimal[1] == CHAR_NULL)))
|
|
{
|
|
szDecimal[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Get the Negative Sign for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SNEGATIVESIGN, szNeg, SIZE_128) ||
|
|
((szNeg[0] == CHAR_HYPHEN) && (szNeg[1] == CHAR_NULL)))
|
|
{
|
|
szNeg[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Call enum proc with each format string. Check to make sure
|
|
// the enum proc requests continuation.
|
|
//
|
|
for (ctr = 0; ctr < NUM_NEG_NUMBER_FORMATS; ctr++)
|
|
{
|
|
if (!lpNegNumFmtEnumProc( pNegNumberFormats[ctr],
|
|
szDecimal,
|
|
szNeg,
|
|
NULL ))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumMeasureSystem
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL EnumMeasureSystem(
|
|
MEASURESYSTEM_ENUMPROC lpMeasureSystemEnumProc,
|
|
LCID LCId,
|
|
DWORD dwFlags)
|
|
{
|
|
TCHAR szBuf[SIZE_128];
|
|
|
|
//
|
|
// If there is no enum proc, return false to indicate a failure.
|
|
//
|
|
if (!lpMeasureSystemEnumProc)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Call enum proc with the metric string. Check to make sure the
|
|
// enum proc requests continuation.
|
|
//
|
|
LoadString(hInstance, IDS_METRIC, szBuf, SIZE_128);
|
|
if (!lpMeasureSystemEnumProc(szBuf))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Call enum proc with the U.S. string.
|
|
//
|
|
LoadString(hInstance, IDS_US, szBuf, SIZE_128);
|
|
lpMeasureSystemEnumProc(szBuf);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumPosCurrency
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL EnumPosCurrency(
|
|
POSCURRENCY_ENUMPROC lpPosCurrencyEnumProc,
|
|
LCID LCId,
|
|
DWORD dwFlags)
|
|
{
|
|
TCHAR szDecimal[SIZE_128];
|
|
TCHAR szSymbol[SIZE_128];
|
|
int ctr;
|
|
|
|
//
|
|
// If there is no enum proc, return false to indicate a failure.
|
|
//
|
|
if (!lpPosCurrencyEnumProc)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the Decimal Separator for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SMONDECIMALSEP, szDecimal, SIZE_128) ||
|
|
((szDecimal[0] == CHAR_DECIMAL) && (szDecimal[1] == CHAR_NULL)))
|
|
{
|
|
szDecimal[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Get the Currency Symbol for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SCURRENCY, szSymbol, SIZE_128) ||
|
|
((szSymbol[0] == CHAR_INTL_CURRENCY) && (szSymbol[1] == CHAR_NULL)))
|
|
{
|
|
szSymbol[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Call enum proc with each format string. Check to make sure the
|
|
// enum proc requests continuation.
|
|
//
|
|
for (ctr = 0; ctr < NUM_POS_CURRENCY_FORMATS; ctr++)
|
|
{
|
|
if (!lpPosCurrencyEnumProc( pPosCurrencyFormats[ctr],
|
|
szDecimal,
|
|
NULL,
|
|
szSymbol ))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EnumNegCurrency
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL EnumNegCurrency(
|
|
NEGCURRENCY_ENUMPROC lpNegCurrencyEnumProc,
|
|
LCID LCId,
|
|
DWORD dwFlags)
|
|
{
|
|
TCHAR szDecimal[SIZE_128];
|
|
TCHAR szNeg[SIZE_128];
|
|
TCHAR szSymbol[SIZE_128];
|
|
int ctr;
|
|
|
|
//
|
|
// If there is no enum proc, return false to indicate a failure.
|
|
//
|
|
if (!lpNegCurrencyEnumProc)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the Decimal Separator for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SMONDECIMALSEP, szDecimal, SIZE_128) ||
|
|
((szDecimal[0] == CHAR_DECIMAL) && (szDecimal[1] == CHAR_NULL)))
|
|
{
|
|
szDecimal[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Get the Negative Sign for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SNEGATIVESIGN, szNeg, SIZE_128) ||
|
|
((szNeg[0] == CHAR_HYPHEN) && (szNeg[1] == CHAR_NULL)))
|
|
{
|
|
szNeg[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Get the Currency Symbol for the current user locale so that
|
|
// it may be displayed correctly.
|
|
//
|
|
if (!GetLocaleInfo(UserLocaleID, LOCALE_SCURRENCY, szSymbol, SIZE_128) ||
|
|
((szSymbol[0] == CHAR_INTL_CURRENCY) && (szSymbol[1] == CHAR_NULL)))
|
|
{
|
|
szSymbol[0] = CHAR_NULL;
|
|
}
|
|
|
|
//
|
|
// Call enum proc with each format string. Check to make sure the
|
|
// enum proc requests continuation.
|
|
//
|
|
for (ctr = 0; ctr < NUM_NEG_CURRENCY_FORMATS; ctr++)
|
|
{
|
|
if (!lpNegCurrencyEnumProc( pNegCurrencyFormats[ctr],
|
|
szDecimal,
|
|
szNeg,
|
|
szSymbol ))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CheckEmptyString
|
|
//
|
|
// If lpStr is empty, then it fills it with a null ("") string.
|
|
// If lpStr is filled only by space, fills with a blank (" ") string.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CheckEmptyString(
|
|
LPTSTR lpStr)
|
|
{
|
|
LPTSTR lpString;
|
|
WORD wStrCType[64];
|
|
|
|
if (!(*lpStr))
|
|
{
|
|
//
|
|
// Put "" string in buffer.
|
|
//
|
|
//lstrcpy(lpStr, TEXT("\"\""));
|
|
if(FAILED(StringCchCopy(lpStr, SIZE_128 + 1, TEXT("\"\""))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (lpString = lpStr; *lpString; lpString = CharNext(lpString))
|
|
{
|
|
GetStringTypeEx( LOCALE_USER_DEFAULT,
|
|
CT_CTYPE1,
|
|
lpString,
|
|
1,
|
|
wStrCType);
|
|
|
|
if (wStrCType[0] != CHAR_SPACE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Put " " string in buffer.
|
|
//
|
|
//lstrcpy(lpStr, TEXT("\" \""));
|
|
if(FAILED(StringCchCopy(lpStr, SIZE_128 + 1, TEXT("\" \""))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetDlgItemRTL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SetDlgItemRTL(
|
|
HWND hDlg,
|
|
UINT uItem)
|
|
{
|
|
HWND hItem = GetDlgItem(hDlg, uItem);
|
|
DWORD dwExStyle = GetWindowLong(hItem, GWL_EXSTYLE);
|
|
|
|
SetWindowLong(hItem, GWL_EXSTYLE, dwExStyle | WS_EX_RTLREADING);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ShowMsg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int ShowMsg(
|
|
HWND hDlg,
|
|
UINT iMsg,
|
|
UINT iTitle,
|
|
UINT iType,
|
|
LPTSTR pString)
|
|
{
|
|
TCHAR szTitle[MAX_PATH];
|
|
TCHAR szMsg[MAX_PATH*2];
|
|
TCHAR szErrMsg[MAX_PATH*2];
|
|
LPTSTR pTitle = NULL;
|
|
|
|
if (iTitle)
|
|
{
|
|
if (LoadString(hInstance, iTitle, szTitle, ARRAYSIZE(szTitle)))
|
|
{
|
|
pTitle = szTitle;
|
|
}
|
|
}
|
|
|
|
if (pString)
|
|
{
|
|
if (LoadString(hInstance, iMsg, szMsg, ARRAYSIZE(szMsg)))
|
|
{
|
|
//wsprintf(szErrMsg, szMsg, pString);
|
|
if(FAILED(StringCchPrintf(szErrMsg, ARRAYSIZE(szErrMsg), szMsg, pString)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
return (MessageBox(hDlg, szErrMsg, pTitle, iType));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (LoadString(hInstance, iMsg, szErrMsg, ARRAYSIZE(szErrMsg)))
|
|
{
|
|
return (MessageBox(hDlg, szErrMsg, pTitle, iType));
|
|
}
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_EnumLocales
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_EnumLocales(
|
|
HWND hDlg,
|
|
HWND hLocale,
|
|
BOOL EnumSystemLocales)
|
|
{
|
|
LPLANGUAGEGROUP pLG;
|
|
DWORD Locale, dwIndex;
|
|
BOOL fSpanish = FALSE;
|
|
UINT ctr;
|
|
TCHAR szBuf[SIZE_300];
|
|
DWORD dwLocaleACP;
|
|
INT iRet = TRUE;
|
|
|
|
//
|
|
// Go through the language groups to see which ones are installed.
|
|
// Display only the locales for the groups that are either already
|
|
// installed or the groups the user wants to be installed.
|
|
//
|
|
pLG = pLanguageGroups;
|
|
while (pLG)
|
|
{
|
|
//
|
|
// If the language group is originally installed and not marked for
|
|
// removal OR is marked to be installed, then add the locales for
|
|
// this language group to the System and User combo boxes.
|
|
//
|
|
if (pLG->wStatus & ML_INSTALL)
|
|
{
|
|
for (ctr = 0; ctr < pLG->NumLocales; ctr++)
|
|
{
|
|
//
|
|
// Save the locale id.
|
|
//
|
|
Locale = (pLG->pLocaleList)[ctr];
|
|
|
|
//
|
|
// See if we need to special case Spanish.
|
|
//
|
|
if ((LANGIDFROMLCID(Locale) == LANG_SPANISH_TRADITIONAL) ||
|
|
(LANGIDFROMLCID(Locale) == LANG_SPANISH_INTL))
|
|
{
|
|
//
|
|
// If we've already displayed Spanish (Spain), then
|
|
// don't display it again.
|
|
//
|
|
if (!fSpanish)
|
|
{
|
|
//
|
|
// Add the Spanish locale to the list box.
|
|
//
|
|
if (LoadString(hInstance, IDS_SPANISH_NAME, szBuf, SIZE_300))
|
|
{
|
|
dwIndex = ComboBox_AddString(hLocale, szBuf);
|
|
ComboBox_SetItemData( hLocale,
|
|
dwIndex,
|
|
LCID_SPANISH_INTL );
|
|
|
|
fSpanish = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't enum system locales that don't have an ACP.
|
|
//
|
|
if (EnumSystemLocales)
|
|
{
|
|
iRet = GetLocaleInfo( Locale,
|
|
LOCALE_IDEFAULTANSICODEPAGE |
|
|
LOCALE_NOUSEROVERRIDE |
|
|
LOCALE_RETURN_NUMBER,
|
|
(PTSTR) &dwLocaleACP,
|
|
sizeof(dwLocaleACP) / sizeof(TCHAR) );
|
|
if (iRet)
|
|
{
|
|
iRet = dwLocaleACP;
|
|
}
|
|
}
|
|
|
|
if (iRet)
|
|
{
|
|
//
|
|
// Get the name of the locale.
|
|
//
|
|
GetLocaleInfo(Locale, LOCALE_SLANGUAGE, szBuf, SIZE_300);
|
|
|
|
//
|
|
// Add the new locale to the list box.
|
|
//
|
|
dwIndex = ComboBox_AddString(hLocale, szBuf);
|
|
ComboBox_SetItemData(hLocale, dwIndex, Locale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pLG = pLG->pNext;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_EnumInstalledCPProc
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CALLBACK Intl_EnumInstalledCPProc(
|
|
LPTSTR pString)
|
|
{
|
|
UINT CodePage;
|
|
LPCODEPAGE pCP;
|
|
|
|
//
|
|
// Convert the code page string to an integer.
|
|
//
|
|
CodePage = Intl_StrToLong(pString);
|
|
|
|
//
|
|
// Find the code page in the linked list and mark it as
|
|
// originally installed.
|
|
//
|
|
pCP = pCodePages;
|
|
while (pCP)
|
|
{
|
|
if (pCP->CodePage == CodePage)
|
|
{
|
|
pCP->wStatus |= ML_ORIG_INSTALLED;
|
|
break;
|
|
}
|
|
|
|
pCP = pCP->pNext;
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_InstallKeyboardLayout
|
|
//
|
|
// Install the Keyboard Layout requested. If the Layout parameter is 0,
|
|
// the function will proceed with the installation of the default layout
|
|
// for the Locale specified. No need to validate the Layout because it's
|
|
// done by the Text Services call.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_InstallKeyboardLayout(
|
|
HWND hDlg,
|
|
LCID Locale,
|
|
DWORD Layout,
|
|
BOOL bDefaultLayout,
|
|
BOOL bDefaultUser,
|
|
BOOL bSystemLocale)
|
|
{
|
|
TCHAR szData[MAX_PATH];
|
|
DWORD dwLayout = Layout;
|
|
DWORD dwLocale = (DWORD)Locale;
|
|
TCHAR szLayout[50];
|
|
HKL hklValue = (HKL)NULL;
|
|
BOOL bOverrideDefaultLayout = FALSE;
|
|
|
|
//
|
|
// Check if input.dll is loaded.
|
|
//
|
|
if (hInputDLL && pfnInstallInputLayout)
|
|
{
|
|
//
|
|
// See if we need to look for the default layout.
|
|
//
|
|
if (!Layout)
|
|
{
|
|
//
|
|
// Look in the INF file for the default layout.
|
|
//
|
|
if (!Intl_GetDefaultLayoutFromInf(&dwLocale, &dwLayout))
|
|
{
|
|
//
|
|
// Try just the language id.
|
|
//
|
|
if (HIWORD(Locale) != 0)
|
|
{
|
|
dwLocale = LANGIDFROMLCID(Locale);
|
|
if (!Intl_GetDefaultLayoutFromInf(&dwLocale, &dwLayout))
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout, TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(SUCCEEDED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_LOCALE_KBD_FAIL, szLayout);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout,TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(SUCCEEDED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_LOCALE_KBD_FAIL, szLayout);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if we need to provide the HKL. This case only occurs when
|
|
// we need to set the Layout as the default. Otherwise, the value
|
|
// can be NULL.
|
|
//
|
|
if (bDefaultLayout)
|
|
{
|
|
hklValue = Intl_GetHKL(dwLocale, dwLayout);
|
|
}
|
|
|
|
//
|
|
// Check if need to override the default layout.
|
|
//
|
|
if (g_bSetupCase && ((HIWORD(dwLayout) & 0xf000) == 0xe000))
|
|
{
|
|
bOverrideDefaultLayout = TRUE;
|
|
}
|
|
|
|
//
|
|
// Install the input Layout.
|
|
//
|
|
if (!(*pfnInstallInputLayout)( dwLocale,
|
|
dwLayout,
|
|
bOverrideDefaultLayout ? FALSE : bDefaultLayout,
|
|
hklValue,
|
|
bDefaultUser,
|
|
g_bSetupCase ? TRUE : bSystemLocale ))
|
|
{
|
|
if (hDlg != NULL)
|
|
{
|
|
GetLocaleInfo(Locale, LOCALE_SLANGUAGE, szData, ARRAYSIZE(szData));
|
|
ShowMsg( hDlg,
|
|
IDS_KBD_LOAD_KBD_FAILED,
|
|
0,
|
|
MB_OK_OOPS,
|
|
szData );
|
|
}
|
|
else
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout, TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
Intl_LogSimpleMessage(IDS_LOG_LOCALE_KBD_FAIL, szLayout);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// If the language has a default layout that has a different locale
|
|
// than the language (e.g. Thai), we want the default locale to be
|
|
// English (so that logon can occur with a US keyboard), but the
|
|
// first Thai keyboard layout should be installed when the Thai
|
|
// locale is chosen. This is why we have two locales and layouts
|
|
// passed back to the caller.
|
|
//
|
|
if (PRIMARYLANGID(LANGIDFROMLCID(dwLocale)) !=
|
|
PRIMARYLANGID(LANGIDFROMLCID(Locale)))
|
|
{
|
|
dwLocale = Locale;
|
|
dwLayout = 0;
|
|
if (!Intl_GetSecondValidLayoutFromInf(&dwLocale, &dwLayout))
|
|
{
|
|
//
|
|
// Try just the language id.
|
|
//
|
|
if (HIWORD(Locale) != 0)
|
|
{
|
|
dwLocale = LANGIDFROMLCID(Locale);
|
|
if (!Intl_GetSecondValidLayoutFromInf(&dwLocale, &dwLayout))
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout, TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
Intl_LogSimpleMessage(IDS_LOG_LOCALE_KBD_FAIL, szLayout);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout,TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
Intl_LogSimpleMessage(IDS_LOG_LOCALE_KBD_FAIL, szLayout);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if we need to provide the HKL. This case only occurs when
|
|
// we need to set the Layout as the default. Otherwise, the value
|
|
// can be NULL.
|
|
//
|
|
if (bDefaultLayout)
|
|
{
|
|
hklValue = Intl_GetHKL(dwLocale, dwLayout);
|
|
}
|
|
|
|
//
|
|
// Install the input Layout.
|
|
//
|
|
if (!(*pfnInstallInputLayout)( dwLocale,
|
|
dwLayout,
|
|
FALSE,
|
|
hklValue,
|
|
bDefaultUser,
|
|
g_bSetupCase ? TRUE : bSystemLocale))
|
|
{
|
|
if (hDlg != NULL)
|
|
{
|
|
GetLocaleInfo(Locale, LOCALE_SLANGUAGE, szData, ARRAYSIZE(szData));
|
|
ShowMsg( hDlg,
|
|
IDS_KBD_LOAD_KBD_FAILED,
|
|
0,
|
|
MB_OK_OOPS,
|
|
szData );
|
|
}
|
|
else
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout, TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
Intl_LogSimpleMessage(IDS_LOG_LOCALE_KBD_FAIL, szLayout);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLayout, TEXT("%08x:%08x"), dwLocale, dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x:%08x"), dwLocale, dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
Intl_LogSimpleMessage(IDS_LOG_LAYOUT_INSTALLED, szLayout);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_InstallKeyboardLayoutList
|
|
//
|
|
// Install all keyboard requested. Pass through the layout list and ask the
|
|
// Text Services to process with the installation.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_InstallKeyboardLayoutList(
|
|
PINFCONTEXT pContext,
|
|
DWORD dwStartField,
|
|
BOOL bDefaultUserCase)
|
|
{
|
|
DWORD dwNumFields, dwNumList, dwCtr;
|
|
DWORD Locale;
|
|
DWORD Layout;
|
|
BOOL bDefaultLayout = FALSE;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
LPTSTR pPos;
|
|
|
|
//
|
|
// Get the number of items in the list.
|
|
//
|
|
dwNumFields = SetupGetFieldCount(pContext);
|
|
if (dwNumFields < dwStartField)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
dwNumList = dwNumFields - dwStartField + 1;
|
|
|
|
//
|
|
// Install all Keyboard layouts from the list.
|
|
//
|
|
for (dwCtr = dwStartField; dwCtr <= dwNumFields; dwCtr++)
|
|
{
|
|
if (SetupGetStringField( pContext,
|
|
dwCtr,
|
|
szBuffer,
|
|
ARRAYSIZE(szBuffer),
|
|
NULL ))
|
|
{
|
|
//
|
|
// Find the colon in order to save the input locale
|
|
// and layout values separately.
|
|
//
|
|
pPos = szBuffer;
|
|
while (*pPos)
|
|
{
|
|
if ((*pPos == CHAR_COLON) && (pPos != szBuffer))
|
|
{
|
|
*pPos = 0;
|
|
pPos++;
|
|
|
|
//
|
|
// Check if related to the invariant locale.
|
|
//
|
|
Locale = TransNum(szBuffer);
|
|
Layout = TransNum(pPos);
|
|
if (Locale != LOCALE_INVARIANT)
|
|
{
|
|
//
|
|
// Only the first one in list would be installed as
|
|
// the default in the Preload section.
|
|
//
|
|
if (dwCtr == dwStartField)
|
|
{
|
|
bDefaultLayout = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bDefaultLayout = FALSE;
|
|
}
|
|
|
|
//
|
|
// Install the keyboard layout requested
|
|
//
|
|
if (Intl_InstallKeyboardLayout( NULL,
|
|
Locale,
|
|
Layout,
|
|
bDefaultLayout,
|
|
bDefaultUserCase,
|
|
FALSE ))
|
|
{
|
|
//
|
|
// Log Layout installation info.
|
|
//
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_LAYOUT, szBuffer);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Log invariant locale blocked.
|
|
//
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_INV_BLOCK, NULL);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
pPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_InstallAllKeyboardLayout
|
|
//
|
|
// Install all keyboard layouts associated with a Language groups.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_InstallAllKeyboardLayout(
|
|
LANGID Language)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
HINF hIntlInf;
|
|
LCID Locale = MAKELCID(Language, SORT_DEFAULT);
|
|
TCHAR szLCID[25];
|
|
INFCONTEXT Context;
|
|
|
|
//
|
|
// Open the INF file
|
|
//
|
|
if (Intl_OpenIntlInfFile(&hIntlInf))
|
|
{
|
|
//
|
|
// Get the locale.
|
|
//
|
|
//wsprintf(szLCID, TEXT("%08x"), Locale);
|
|
if(FAILED(StringCchPrintf(szLCID, ARRAYSIZE(szLCID), TEXT("%08x"), Locale)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Look for the keyboard section.
|
|
//
|
|
if (SetupFindFirstLine( hIntlInf,
|
|
TEXT("Locales"),
|
|
szLCID,
|
|
&Context ))
|
|
{
|
|
bRet = Intl_InstallKeyboardLayoutList(&Context, 5, FALSE);
|
|
}
|
|
|
|
Intl_CloseInfFile(&hIntlInf);
|
|
}
|
|
|
|
return (bRet);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_UninstallAllKeyboardLayout
|
|
//
|
|
// Remove all keyboard layouts associated with a Language groups.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_UninstallAllKeyboardLayout(
|
|
UINT uiLangGroup,
|
|
BOOL DefaultUserCase)
|
|
{
|
|
LPLANGUAGEGROUP pLG = pLanguageGroups;
|
|
LANGID lidCurrent, lidPrev = 0;
|
|
LCID *pLocale;
|
|
BOOL bRet = TRUE;
|
|
|
|
//
|
|
// Bail out if we can't get this API from input.dll.
|
|
//
|
|
|
|
if (pfnUninstallInputLayout)
|
|
{
|
|
//
|
|
// Walk through all language groups.
|
|
//
|
|
while (pLG)
|
|
{
|
|
if (pLG->LanguageGroup == uiLangGroup)
|
|
{
|
|
TCHAR szLang[MAX_PATH];
|
|
|
|
pLocale = pLG->pLocaleList;
|
|
|
|
//
|
|
// Walk through the locale list, remove relevant keyboard
|
|
// layouts by the locale's primary language.
|
|
//
|
|
while (*pLocale)
|
|
{
|
|
lidCurrent = PRIMARYLANGID(*pLocale);
|
|
|
|
//
|
|
// Don't uninstall any US keyboard layouts.
|
|
//
|
|
if (lidCurrent == 0x09)
|
|
{
|
|
pLocale++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The locale list is sorted, so we can avoid redundant
|
|
// UninstallInputLayout calls.
|
|
//
|
|
if (lidCurrent != lidPrev)
|
|
{
|
|
//
|
|
// Uninstall the input layouts associated with
|
|
// this current locale in the list.
|
|
//
|
|
BOOL bSuccess =
|
|
(*pfnUninstallInputLayout)( (LCID) lidCurrent,
|
|
0L,
|
|
DefaultUserCase );
|
|
if (g_bLog)
|
|
{
|
|
//wsprintf(szLang, TEXT("%04x"), lidCurrent);
|
|
if(FAILED(StringCchPrintf(szLang, ARRAYSIZE(szLang), TEXT("%04x"), lidCurrent)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
|
|
Intl_LogSimpleMessage( bSuccess
|
|
? IDS_LOG_LOCALE_LG_REM
|
|
: IDS_LOG_LOCALE_LG_FAIL,
|
|
szLang );
|
|
}
|
|
|
|
if (!bSuccess && bRet)
|
|
{
|
|
bRet = bSuccess;
|
|
}
|
|
|
|
lidPrev = lidCurrent;
|
|
}
|
|
pLocale++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
pLG = pLG->pNext;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetHKL
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
HKL Intl_GetHKL(
|
|
DWORD dwLocale,
|
|
DWORD dwLayout)
|
|
{
|
|
TCHAR szData[MAX_PATH];
|
|
INFCONTEXT Context;
|
|
HINF hIntlInf;
|
|
TCHAR szLayout[25];
|
|
|
|
//
|
|
// Get the HKL based on the input locale value and the layout value.
|
|
//
|
|
if (dwLayout == 0)
|
|
{
|
|
//
|
|
// See if it's the default layout for the input locale or an IME.
|
|
//
|
|
if (HIWORD(dwLocale) == 0)
|
|
{
|
|
return ((HKL)MAKELPARAM(dwLocale, dwLocale));
|
|
}
|
|
else if ((HIWORD(dwLocale) & 0xf000) == 0xe000)
|
|
{
|
|
return ((HKL)IntToPtr(dwLocale));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Open the INF file.
|
|
//
|
|
if (Intl_OpenIntlInfFile(&hIntlInf))
|
|
{
|
|
//
|
|
// Create the Layout string.
|
|
//
|
|
//wsprintf(szLayout, TEXT("%08x"), dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x"), dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
Intl_CloseInfFile(&hIntlInf);
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Use the layout to make the hkl.
|
|
//
|
|
if (HIWORD(dwLayout) != 0)
|
|
{
|
|
//
|
|
// We have a special id. Need to find out what the layout id
|
|
// should be.
|
|
//
|
|
if ((SetupFindFirstLine(hIntlInf, szKbdLayoutIds, szLayout, &Context)) &&
|
|
(SetupGetStringField(&Context, 1, szData, ARRAYSIZE(szData), NULL)))
|
|
{
|
|
dwLayout = (DWORD)(LOWORD(TransNum(szData)) | 0xf000);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the handle
|
|
//
|
|
Intl_CloseInfFile(&hIntlInf);
|
|
|
|
//
|
|
// Return the hkl:
|
|
// loword = input locale id
|
|
// hiword = layout id
|
|
//
|
|
return ((HKL)MAKELPARAM(dwLocale, dwLayout));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (0);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetDefaultLayoutFromInf
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_GetDefaultLayoutFromInf(
|
|
LPDWORD pdwLocale,
|
|
LPDWORD pdwLayout)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
HINF hIntlInf;
|
|
|
|
if (Intl_OpenIntlInfFile(&hIntlInf))
|
|
{
|
|
bRet = Intl_ReadDefaultLayoutFromInf(pdwLocale, pdwLayout, hIntlInf);
|
|
Intl_CloseInfFile(&hIntlInf);
|
|
}
|
|
|
|
return (bRet);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetSecondValidLayoutFromInf
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_GetSecondValidLayoutFromInf(
|
|
LPDWORD pdwLocale,
|
|
LPDWORD pdwLayout)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
HINF hIntlInf;
|
|
|
|
if (Intl_OpenIntlInfFile(&hIntlInf))
|
|
{
|
|
bRet = Intl_ReadSecondValidLayoutFromInf(pdwLocale, pdwLayout, hIntlInf);
|
|
Intl_CloseInfFile(&hIntlInf);
|
|
}
|
|
|
|
return (bRet);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_InitInf
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_InitInf(
|
|
HWND hDlg,
|
|
HINF *phIntlInf,
|
|
LPTSTR pszInf,
|
|
HSPFILEQ *pFileQueue,
|
|
PVOID *pQueueContext)
|
|
{
|
|
BOOL bSpecialCase = TRUE;
|
|
|
|
//
|
|
// Open the Inf file.
|
|
//
|
|
*phIntlInf = SetupOpenInfFile(pszInf, NULL, INF_STYLE_WIN4, NULL);
|
|
if (*phIntlInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogFormatMessage(IDS_LOG_INTL_ERROR);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!SetupOpenAppendInfFile(NULL, *phIntlInf, NULL))
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogFormatMessage(IDS_LOG_SETUP_ERROR);
|
|
}
|
|
|
|
SetupCloseInfFile(*phIntlInf);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Create a setup file queue and initialize default setup
|
|
// copy queue callback context.
|
|
//
|
|
*pFileQueue = SetupOpenFileQueue();
|
|
if ((!*pFileQueue) || (*pFileQueue == INVALID_HANDLE_VALUE))
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogFormatMessage(IDS_LOG_SETUP_ERROR);
|
|
}
|
|
|
|
SetupCloseInfFile(*phIntlInf);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Determine if we are dealing with a special case.
|
|
//
|
|
if ((g_bUnttendMode || g_bSetupCase) && !g_bProgressBarDisplay)
|
|
{
|
|
bSpecialCase = FALSE;
|
|
}
|
|
|
|
//
|
|
// Don't display FileCopy progress operation during GUI mode setup or Unattend mode.
|
|
//
|
|
*pQueueContext = SetupInitDefaultQueueCallbackEx( GetParent(hDlg),
|
|
(bSpecialCase ? NULL : INVALID_HANDLE_VALUE),
|
|
0L,
|
|
0L,
|
|
NULL );
|
|
if (!*pQueueContext)
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogFormatMessage(IDS_LOG_SETUP_ERROR);
|
|
}
|
|
|
|
SetupCloseFileQueue(*pFileQueue);
|
|
SetupCloseInfFile(*phIntlInf);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_OpenIntlInfFile
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_OpenIntlInfFile(
|
|
HINF *phInf)
|
|
{
|
|
HINF hIntlInf;
|
|
|
|
//
|
|
// Open the intl.inf file.
|
|
//
|
|
hIntlInf = SetupOpenInfFile(szIntlInf, NULL, INF_STYLE_WIN4, NULL);
|
|
if (hIntlInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!SetupOpenAppendInfFile(NULL, hIntlInf, NULL))
|
|
{
|
|
SetupCloseInfFile(hIntlInf);
|
|
return (FALSE);
|
|
}
|
|
|
|
*phInf = hIntlInf;
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_CloseInf
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_CloseInf(
|
|
HINF hIntlInf,
|
|
HSPFILEQ FileQueue,
|
|
PVOID QueueContext)
|
|
{
|
|
//
|
|
// Terminate the Queue.
|
|
//
|
|
SetupTermDefaultQueueCallback(QueueContext);
|
|
|
|
//
|
|
// Close the file queue.
|
|
//
|
|
SetupCloseFileQueue(FileQueue);
|
|
|
|
//
|
|
// Close the Inf file.
|
|
//
|
|
SetupCloseInfFile(hIntlInf);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_ReadDefaultLayoutFromInf
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_ReadDefaultLayoutFromInf(
|
|
LPDWORD pdwLocale,
|
|
LPDWORD pdwLayout,
|
|
HINF hIntlInf)
|
|
{
|
|
INFCONTEXT Context;
|
|
TCHAR szPair[MAX_PATH * 2];
|
|
LPTSTR pPos;
|
|
TCHAR szLCID[25];
|
|
|
|
//
|
|
// Get the locale.
|
|
//
|
|
//wsprintf(szLCID, TEXT("%08x"), *pdwLocale);
|
|
if(FAILED(StringCchPrintf(szLCID, ARRAYSIZE(szLCID), TEXT("%08x"), *pdwLocale)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the first (default) LANGID:HKL pair for the given locale.
|
|
// Example String: "0409:00000409"
|
|
//
|
|
szPair[0] = 0;
|
|
if (SetupFindFirstLine( hIntlInf,
|
|
TEXT("Locales"),
|
|
szLCID,
|
|
&Context ))
|
|
{
|
|
SetupGetStringField(&Context, 5, szPair, MAX_PATH, NULL);
|
|
}
|
|
|
|
//
|
|
// Make sure we have a string.
|
|
//
|
|
if (szPair[0] == 0)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Find the colon in the string and then set the position
|
|
// pointer to the next character.
|
|
//
|
|
pPos = szPair;
|
|
while (*pPos)
|
|
{
|
|
if ((*pPos == CHAR_COLON) && (pPos != szPair))
|
|
{
|
|
*pPos = 0;
|
|
pPos++;
|
|
break;
|
|
}
|
|
pPos++;
|
|
}
|
|
|
|
//
|
|
// If there is a layout, then return the input locale and the layout.
|
|
//
|
|
if ((*pPos) &&
|
|
(*pdwLocale = TransNum(szPair)) &&
|
|
(*pdwLayout = TransNum(pPos)))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_ReadSecondValidLayoutFromInf
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_ReadSecondValidLayoutFromInf(
|
|
LPDWORD pdwLocale,
|
|
LPDWORD pdwLayout,
|
|
HINF hIntlInf)
|
|
{
|
|
INFCONTEXT Context;
|
|
int iField = 6;
|
|
TCHAR szPair[MAX_PATH * 2];
|
|
LPTSTR pPos;
|
|
DWORD dwLoc, dwlay, savedLocale = *pdwLocale;
|
|
TCHAR szLCID[25];
|
|
|
|
//
|
|
// Get the locale.
|
|
//
|
|
//wsprintf(szLCID, TEXT("%08x"), *pdwLocale);
|
|
if(FAILED(StringCchPrintf(szLCID, ARRAYSIZE(szLCID), TEXT("%08x"), *pdwLocale)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the first (default) LANGID:HKL pair for the given locale.
|
|
// Example String: "0409:00000409"
|
|
//
|
|
szPair[0] = 0;
|
|
if (SetupFindFirstLine(hIntlInf, TEXT("Locales"), szLCID, &Context))
|
|
{
|
|
while (SetupGetStringField(&Context, iField, szPair, MAX_PATH, NULL))
|
|
{
|
|
//
|
|
// Make sure we have a string.
|
|
//
|
|
if (szPair[0] == 0)
|
|
{
|
|
iField++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Find the colon in the string and then set the position
|
|
// pointer to the next character.
|
|
//
|
|
pPos = szPair;
|
|
while (*pPos)
|
|
{
|
|
if ((*pPos == CHAR_COLON) && (pPos != szPair))
|
|
{
|
|
*pPos = 0;
|
|
pPos++;
|
|
break;
|
|
}
|
|
pPos++;
|
|
}
|
|
|
|
if (*pPos == 0)
|
|
{
|
|
iField++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If there is a layout, then return the input locale and the
|
|
// layout.
|
|
//
|
|
if (((dwLoc = TransNum(szPair)) == 0) ||
|
|
((dwlay = TransNum(pPos)) == 0))
|
|
{
|
|
iField++;
|
|
continue;
|
|
}
|
|
|
|
if (PRIMARYLANGID(LANGIDFROMLCID(dwLoc)) ==
|
|
PRIMARYLANGID(LANGIDFROMLCID(savedLocale)))
|
|
{
|
|
*pdwLayout = dwlay;
|
|
*pdwLocale = dwLoc;
|
|
return (TRUE);
|
|
}
|
|
iField++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_CloseInfFile
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_CloseInfFile(
|
|
HINF *phInf)
|
|
{
|
|
SetupCloseInfFile(*phInf);
|
|
*phInf = INVALID_HANDLE_VALUE;
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_IsValidLayout
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_IsValidLayout(
|
|
DWORD dwLayout)
|
|
{
|
|
HKEY hKey1, hKey2;
|
|
TCHAR szLayout[MAX_PATH];
|
|
|
|
//
|
|
// Get the layout id as a string.
|
|
//
|
|
//wsprintf(szLayout, TEXT("%08x"), dwLayout);
|
|
if(FAILED(StringCchPrintf(szLayout, ARRAYSIZE(szLayout), TEXT("%08x"), dwLayout)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Open the Keyboard Layouts key.
|
|
//
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szLayoutPath, 0L, KEY_READ, &hKey1) != ERROR_SUCCESS)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Try to open the layout id key under the Keyboard Layouts key.
|
|
//
|
|
if (RegOpenKeyEx(hKey1, szLayout, 0L, KEY_READ, &hKey2) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hKey1);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Close the keys.
|
|
//
|
|
RegCloseKey(hKey1);
|
|
RegCloseKey(hKey2);
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_RunRegApps
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_RunRegApps(
|
|
LPCTSTR pszRegKey)
|
|
{
|
|
HKEY hkey;
|
|
DWORD cbData, cbValue, dwType, ctr;
|
|
TCHAR szValueName[32], szCmdLine[MAX_PATH];
|
|
STARTUPINFO startup;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
pszRegKey,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hkey ) == ERROR_SUCCESS)
|
|
{
|
|
startup.cb = sizeof(STARTUPINFO);
|
|
startup.lpReserved = NULL;
|
|
startup.lpDesktop = NULL;
|
|
startup.lpTitle = NULL;
|
|
startup.dwFlags = 0L;
|
|
startup.cbReserved2 = 0;
|
|
startup.lpReserved2 = NULL;
|
|
// startup.wShowWindow = wShowWindow;
|
|
|
|
for (ctr = 0; ; ctr++)
|
|
{
|
|
LONG lEnum;
|
|
|
|
cbValue = sizeof(szValueName) / sizeof(TCHAR);
|
|
cbData = sizeof(szCmdLine);
|
|
|
|
if ((lEnum = RegEnumValue( hkey,
|
|
ctr,
|
|
szValueName,
|
|
&cbValue,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szCmdLine,
|
|
&cbData )) == ERROR_MORE_DATA)
|
|
{
|
|
//
|
|
// ERROR_MORE_DATA means the value name or data was too
|
|
// large, so skip to the next item.
|
|
//
|
|
continue;
|
|
}
|
|
else if (lEnum != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// This could be ERROR_NO_MORE_ENTRIES, or some kind of
|
|
// failure. We can't recover from any other registry
|
|
// problem anyway.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Found a value.
|
|
//
|
|
if (dwType == REG_SZ)
|
|
{
|
|
//
|
|
// Adjust for shift in value index.
|
|
//
|
|
ctr--;
|
|
|
|
//
|
|
// Delete the value.
|
|
//
|
|
RegDeleteValue(hkey, szValueName);
|
|
|
|
//
|
|
// Only run things marked with a "*" in clean boot.
|
|
//
|
|
if (CreateProcess( NULL,
|
|
szCmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_PROCESS_GROUP,
|
|
NULL,
|
|
NULL,
|
|
&startup,
|
|
&pi ))
|
|
{
|
|
WaitForSingleObjectEx(pi.hProcess, INFINITE, TRUE);
|
|
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_RebootTheSystem
|
|
//
|
|
// This routine enables all privileges in the token, calls ExitWindowsEx
|
|
// to reboot the system, and then resets all of the privileges to their
|
|
// old state.
|
|
// Input: bRestart TRUE: restart system
|
|
// FALSE: logoff current session
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
VOID Intl_RebootTheSystem(BOOL bRestart)
|
|
{
|
|
HANDLE Token = NULL;
|
|
ULONG ReturnLength, Index;
|
|
PTOKEN_PRIVILEGES NewState = NULL;
|
|
PTOKEN_PRIVILEGES OldState = NULL;
|
|
BOOL Result;
|
|
|
|
Result = OpenProcessToken( GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&Token );
|
|
if (Result)
|
|
{
|
|
ReturnLength = 4096;
|
|
NewState = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, ReturnLength);
|
|
OldState = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, ReturnLength);
|
|
Result = (BOOL)((NewState != NULL) && (OldState != NULL));
|
|
if (Result)
|
|
{
|
|
Result = GetTokenInformation( Token, // TokenHandle
|
|
TokenPrivileges, // TokenInformationClass
|
|
NewState, // TokenInformation
|
|
ReturnLength, // TokenInformationLength
|
|
&ReturnLength ); // ReturnLength
|
|
if (Result)
|
|
{
|
|
//
|
|
// Set the state settings so that all privileges are
|
|
// enabled...
|
|
//
|
|
if (NewState->PrivilegeCount > 0)
|
|
{
|
|
for (Index = 0; Index < NewState->PrivilegeCount; Index++)
|
|
{
|
|
NewState->Privileges[Index].Attributes = SE_PRIVILEGE_ENABLED;
|
|
}
|
|
}
|
|
|
|
Result = AdjustTokenPrivileges( Token, // TokenHandle
|
|
FALSE, // DisableAllPrivileges
|
|
NewState, // NewState
|
|
ReturnLength, // BufferLength
|
|
OldState, // PreviousState
|
|
&ReturnLength ); // ReturnLength
|
|
if (Result)
|
|
{
|
|
// Restart system
|
|
if (bRestart)
|
|
{
|
|
ExitWindowsEx(EWX_REBOOT, 0);
|
|
}
|
|
// Logoff current session
|
|
else
|
|
{
|
|
ExitWindowsEx(EWX_LOGOFF, 0);
|
|
}
|
|
|
|
|
|
AdjustTokenPrivileges( Token,
|
|
FALSE,
|
|
OldState,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NewState != NULL)
|
|
{
|
|
LocalFree(NewState);
|
|
}
|
|
if (OldState != NULL)
|
|
{
|
|
LocalFree(OldState);
|
|
}
|
|
if (Token != NULL)
|
|
{
|
|
CloseHandle(Token);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_InstallUserLocale
|
|
//
|
|
// When the DefaultUserCase flag is FALSE, this function write information
|
|
// related to the locale for the current user. Otherwise, this function
|
|
// write information for the .DEFAULT user. In the Default user case, the
|
|
// the information are stored in the registry and the NTSUSER.DAT.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_InstallUserLocale(
|
|
LCID Locale,
|
|
BOOL DefaultUserCase,
|
|
BOOL bChangeLocaleInfo )
|
|
{
|
|
HKEY hKey = NULL;
|
|
HKEY hHive = NULL;
|
|
BOOLEAN wasEnabled;
|
|
TCHAR szLCID[25];
|
|
DWORD dwRet;
|
|
|
|
//
|
|
// Save the locale id as a string.
|
|
//
|
|
//wsprintf(szLCID, TEXT("%08x"), Locale);
|
|
if(FAILED(StringCchPrintf(szLCID, ARRAYSIZE(szLCID), TEXT("%08x"), Locale)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the locale is valid.
|
|
//
|
|
if (!IsValidLocale(Locale, LCID_INSTALLED))
|
|
{
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_INVALID_LOCALE, szLCID);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Log user locale info change.
|
|
//
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_USER_LOCALE_CHG, szLCID);
|
|
}
|
|
|
|
//
|
|
// Open the right registry section.
|
|
//
|
|
if (!DefaultUserCase)
|
|
{
|
|
dwRet = RegOpenKeyEx( HKEY_CURRENT_USER,
|
|
c_szCPanelIntl,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey );
|
|
}
|
|
else
|
|
{
|
|
dwRet = RegOpenKeyEx( HKEY_USERS,
|
|
c_szCPanelIntl_DefUser,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey );
|
|
|
|
if (dwRet == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Load the default hive.
|
|
//
|
|
if ((hHive = Intl_LoadNtUserHive( TEXT("tempKey"),
|
|
c_szCPanelIntl,
|
|
NULL,
|
|
&wasEnabled )) == NULL )
|
|
{
|
|
RegCloseKey(hKey);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Save the Locale value in NTUSER.DAT.
|
|
//
|
|
RegSetValueEx( hHive,
|
|
TEXT("Locale"),
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szLCID,
|
|
(lstrlen(szLCID) + 1) * sizeof(TCHAR));
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
RegCloseKey(hHive);
|
|
Intl_UnloadNtUserHive(TEXT("tempKey"), &wasEnabled);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the locale value in the user's control panel international
|
|
// section of the registry.
|
|
//
|
|
if ((dwRet != ERROR_SUCCESS) ||
|
|
(RegSetValueEx( hKey,
|
|
TEXT("Locale"),
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szLCID,
|
|
(lstrlen(szLCID) + 1) * sizeof(TCHAR) ) != ERROR_SUCCESS))
|
|
{
|
|
if (hKey != NULL)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// When the locale changes, update ALL registry information when asked.
|
|
//
|
|
if (bChangeLocaleInfo)
|
|
{
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SABBREVLANGNAME, TEXT("sLanguage"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SCOUNTRY, TEXT("sCountry"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ICOUNTRY, TEXT("iCountry"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_S1159, TEXT("s1159"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_S2359, TEXT("s2359"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_STIMEFORMAT, TEXT("sTimeFormat"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_STIME, TEXT("sTime"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ITIME, TEXT("iTime"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ITLZERO, TEXT("iTLZero"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ITIMEMARKPOSN, TEXT("iTimePrefix"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SSHORTDATE, TEXT("sShortDate"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_IDATE, TEXT("iDate"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SDATE, TEXT("sDate"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SLONGDATE, TEXT("sLongDate"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SCURRENCY, TEXT("sCurrency"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ICURRENCY, TEXT("iCurrency"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_INEGCURR, TEXT("iNegCurr"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ICURRDIGITS, TEXT("iCurrDigits"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SDECIMAL, TEXT("sDecimal"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SMONDECIMALSEP, TEXT("sMonDecimalSep"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_STHOUSAND, TEXT("sThousand"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SMONTHOUSANDSEP, TEXT("sMonThousandSep"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SLIST, TEXT("sList"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_IDIGITS, TEXT("iDigits"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ILZERO, TEXT("iLzero"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_INEGNUMBER, TEXT("iNegNumber"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SNATIVEDIGITS, TEXT("sNativeDigits"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_IDIGITSUBSTITUTION, TEXT("NumShape"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_IMEASURE, TEXT("iMeasure"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_ICALENDARTYPE, TEXT("iCalendarType"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_IFIRSTDAYOFWEEK, TEXT("iFirstDayOfWeek"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_IFIRSTWEEKOFYEAR, TEXT("iFirstWeekOfYear"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SGROUPING, TEXT("sGrouping"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SMONGROUPING, TEXT("sMonGrouping"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SPOSITIVESIGN, TEXT("sPositiveSign"), DefaultUserCase);
|
|
Intl_SetLocaleInfo(Locale, LOCALE_SNEGATIVESIGN, TEXT("sNegativeSign"), DefaultUserCase);
|
|
}
|
|
|
|
//
|
|
// Set the user's default locale in the system so that any new
|
|
// process will use the new locale.
|
|
//
|
|
if (!DefaultUserCase)
|
|
{
|
|
NtSetDefaultLocale(TRUE, Locale);
|
|
}
|
|
|
|
//
|
|
// Flush the International key.
|
|
//
|
|
if (hKey != NULL)
|
|
{
|
|
RegFlushKey(hKey);
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_SetLocaleInfo
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_SetLocaleInfo(
|
|
LCID Locale,
|
|
LCTYPE LCType,
|
|
LPTSTR lpIniStr,
|
|
BOOL bDefaultUserCase)
|
|
{
|
|
TCHAR pBuf[SIZE_128];
|
|
|
|
//
|
|
// Get the default information for the given locale.
|
|
//
|
|
if (GetLocaleInfo( Locale,
|
|
LCType | LOCALE_NOUSEROVERRIDE,
|
|
pBuf,
|
|
SIZE_128 ))
|
|
{
|
|
if (!bDefaultUserCase)
|
|
{
|
|
//
|
|
// Set the default information in the registry.
|
|
//
|
|
// NOTE: We want to use SetLocaleInfo if possible so that the
|
|
// NLS cache is updated right away. Otherwise, we'll
|
|
// simply use WriteProfileString.
|
|
//
|
|
if (!SetLocaleInfo(Locale, LCType, pBuf))
|
|
{
|
|
//
|
|
// If SetLocaleInfo failed, try WriteProfileString since
|
|
// some of the LCTypes are not supported in SetLocaleInfo.
|
|
//
|
|
WriteProfileString(szIntl, lpIniStr, pBuf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the default information in the registry and NTUSER.DAT.
|
|
//
|
|
Intl_SetDefaultUserLocaleInfo(lpIniStr, pBuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_AddPage
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_AddPage(
|
|
LPPROPSHEETHEADER ppsh,
|
|
UINT id,
|
|
DLGPROC pfn,
|
|
LPARAM lParam,
|
|
UINT iMaxPages)
|
|
{
|
|
if (ppsh->nPages < iMaxPages)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
|
|
psp.dwSize = sizeof(psp);
|
|
psp.dwFlags = PSP_DEFAULT;
|
|
psp.hInstance = hInstance;
|
|
psp.pszTemplate = MAKEINTRESOURCE(id);
|
|
psp.pfnDlgProc = pfn;
|
|
psp.lParam = lParam;
|
|
|
|
ppsh->phpage[ppsh->nPages] = CreatePropertySheetPage(&psp);
|
|
if (ppsh->phpage[ppsh->nPages])
|
|
{
|
|
ppsh->nPages++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_AddExternalPage
|
|
//
|
|
// Adds a property sheet page from the given dll.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_AddExternalPage(
|
|
LPPROPSHEETHEADER ppsh,
|
|
UINT id,
|
|
HINSTANCE hInst,
|
|
LPSTR ProcName,
|
|
UINT iMaxPages)
|
|
{
|
|
DLGPROC pfn;
|
|
|
|
if (ppsh->nPages < iMaxPages)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
|
|
if (hInst)
|
|
{
|
|
pfn = (DLGPROC)GetProcAddress(hInst, ProcName);
|
|
if (!pfn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
psp.dwSize = sizeof(psp);
|
|
psp.dwFlags = PSP_DEFAULT;
|
|
psp.hInstance = hInst;
|
|
psp.pszTemplate = MAKEINTRESOURCE(id);
|
|
psp.pfnDlgProc = pfn;
|
|
psp.lParam = 0;
|
|
|
|
ppsh->phpage[ppsh->nPages] = CreatePropertySheetPage(&psp);
|
|
if (ppsh->phpage[ppsh->nPages])
|
|
{
|
|
ppsh->nPages++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_SetDefaultUserLocaleInfo
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_SetDefaultUserLocaleInfo(
|
|
LPCTSTR lpKeyName,
|
|
LPCTSTR lpString)
|
|
{
|
|
HKEY hKey = NULL;
|
|
LONG rc = 0L;
|
|
TCHAR szProfile[REGSTR_MAX_VALUE_LENGTH];
|
|
BOOLEAN wasEnabled;
|
|
|
|
//
|
|
// Open the .DEFAULT control panel international section.
|
|
//
|
|
if ((rc = RegOpenKeyEx( HKEY_USERS,
|
|
c_szCPanelIntl_DefUser,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey )) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Set the value
|
|
//
|
|
rc = RegSetValueEx( hKey,
|
|
lpKeyName,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)lpString,
|
|
(lstrlen(lpString) + 1) * sizeof(TCHAR) );
|
|
|
|
//
|
|
// Flush the International key.
|
|
//
|
|
RegFlushKey(hKey);
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Load the hive.
|
|
//
|
|
if ((hKey = Intl_LoadNtUserHive( TEXT("RegionalSettingsTempKey"),
|
|
c_szCPanelIntl,
|
|
NULL,
|
|
&wasEnabled)) == NULL)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the value.
|
|
//
|
|
rc = RegSetValueEx( hKey,
|
|
lpKeyName,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)lpString,
|
|
(lstrlen(lpString) + 1) * sizeof(TCHAR) );
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
RegCloseKey(hKey);
|
|
Intl_UnloadNtUserHive(TEXT("RegionalSettingsTempKey"), &wasEnabled);
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_DeleteRegKeyValues
|
|
//
|
|
// This deletes all values under a specific key.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_DeleteRegKeyValues(
|
|
HKEY hKey)
|
|
{
|
|
TCHAR szValueName[REGSTR_MAX_VALUE_LENGTH];
|
|
DWORD cbValue = REGSTR_MAX_VALUE_LENGTH;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
if (hKey == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Enumerate values.
|
|
//
|
|
while (RegEnumValue( hKey,
|
|
0,
|
|
szValueName,
|
|
&cbValue,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL ) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Delete the value.
|
|
//
|
|
RegDeleteValue(hKey, szValueName);
|
|
cbValue = REGSTR_MAX_VALUE_LENGTH;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_DeleteRegTree
|
|
//
|
|
// This deletes all subkeys under a specific key.
|
|
//
|
|
// Note: The code makes no attempt to check or recover from partial
|
|
// deletions.
|
|
//
|
|
// A registry key that is opened by an application can be deleted
|
|
// without error by another application. This is by design.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD Intl_DeleteRegTree(
|
|
HKEY hStartKey,
|
|
LPTSTR pKeyName)
|
|
{
|
|
DWORD dwRtn, dwSubKeyLength;
|
|
LPTSTR pSubKey = NULL;
|
|
TCHAR szSubKey[REGSTR_MAX_VALUE_LENGTH]; // (256) this should be dynamic.
|
|
HKEY hKey;
|
|
|
|
//
|
|
// Do not allow NULL or empty key name.
|
|
//
|
|
if (pKeyName && lstrlen(pKeyName))
|
|
{
|
|
if ((dwRtn = RegOpenKeyEx( hStartKey,
|
|
pKeyName,
|
|
0,
|
|
KEY_ENUMERATE_SUB_KEYS | DELETE,
|
|
&hKey )) == ERROR_SUCCESS)
|
|
{
|
|
while (dwRtn == ERROR_SUCCESS)
|
|
{
|
|
dwSubKeyLength = REGSTR_MAX_VALUE_LENGTH;
|
|
dwRtn = RegEnumKeyEx( hKey,
|
|
0, // always index zero
|
|
szSubKey,
|
|
&dwSubKeyLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if (dwRtn == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
dwRtn = RegDeleteKey(hStartKey, pKeyName);
|
|
break;
|
|
}
|
|
else if (dwRtn == ERROR_SUCCESS)
|
|
{
|
|
dwRtn = Intl_DeleteRegTree(hKey, szSubKey);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
//
|
|
// Do not save return code because error has already occurred.
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwRtn = ERROR_BADKEY;
|
|
}
|
|
|
|
return (dwRtn);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_DeleteRegSubKeys
|
|
//
|
|
// This deletes all subkeys under a specific key.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_DeleteRegSubKeys(
|
|
HKEY hKey)
|
|
{
|
|
TCHAR szKeyName[REGSTR_MAX_VALUE_LENGTH];
|
|
DWORD cbKey = REGSTR_MAX_VALUE_LENGTH;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
if (hKey == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Enumerate values.
|
|
//
|
|
while (RegEnumKeyEx( hKey,
|
|
0,
|
|
szKeyName,
|
|
&cbKey,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL ) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Delete the value.
|
|
//
|
|
Intl_DeleteRegTree(hKey, szKeyName);
|
|
cbKey = REGSTR_MAX_VALUE_LENGTH;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_CopyRegKeyValues
|
|
//
|
|
// This copies all values under the source key to the destination key.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD Intl_CopyRegKeyValues(
|
|
HKEY hSrc,
|
|
HKEY hDest)
|
|
{
|
|
DWORD cbValue, dwSubKeyIndex=0, dwType, cdwBuf;
|
|
DWORD dwValues, cbMaxValueData, i;
|
|
TCHAR szValue[REGSTR_MAX_VALUE_LENGTH]; // this should be dynamic.
|
|
DWORD lRet = ERROR_SUCCESS;
|
|
LPBYTE pBuf;
|
|
|
|
if ((lRet = RegQueryInfoKey( hSrc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwValues,
|
|
NULL,
|
|
&cbMaxValueData,
|
|
NULL,
|
|
NULL )) == ERROR_SUCCESS)
|
|
{
|
|
if (dwValues)
|
|
{
|
|
if ((pBuf = HeapAlloc( GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
cbMaxValueData )))
|
|
{
|
|
for (i = 0; i < dwValues; i++)
|
|
{
|
|
//
|
|
// Get values to create.
|
|
//
|
|
cbValue = REGSTR_MAX_VALUE_LENGTH;
|
|
cdwBuf = cbMaxValueData;
|
|
lRet = RegEnumValue( hSrc, // handle of key to query
|
|
i, // index of value to query
|
|
szValue, // buffer for value string
|
|
&cbValue, // address for size of buffer
|
|
NULL, // reserved
|
|
&dwType, // buffer address for type code
|
|
pBuf, // address of buffer for value data
|
|
&cdwBuf ); // address for size of buffer
|
|
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
if ((lRet = RegSetValueEx( hDest,
|
|
szValue,
|
|
0,
|
|
dwType,
|
|
(CONST BYTE *)pBuf,
|
|
cdwBuf )) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, pBuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (lRet);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_CreateRegTree
|
|
//
|
|
// This copies all values and subkeys under the source key to the
|
|
// destination key.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD Intl_CreateRegTree(
|
|
HKEY hSrc,
|
|
HKEY hDest)
|
|
{
|
|
DWORD cdwClass, dwSubKeyLength, dwDisposition, dwKeyIndex = 0;
|
|
LPTSTR pSubKey = NULL;
|
|
TCHAR szSubKey[REGSTR_MAX_VALUE_LENGTH]; // this should be dynamic.
|
|
TCHAR szClass[REGSTR_MAX_VALUE_LENGTH]; // this should be dynamic.
|
|
HKEY hNewKey, hKey;
|
|
DWORD lRet;
|
|
|
|
//
|
|
// Copy values
|
|
//
|
|
if ((lRet = Intl_CopyRegKeyValues( hSrc,
|
|
hDest )) != ERROR_SUCCESS)
|
|
{
|
|
return (lRet);
|
|
}
|
|
|
|
//
|
|
// Copy the subkeys and the subkey values.
|
|
//
|
|
for (;;)
|
|
{
|
|
dwSubKeyLength = REGSTR_MAX_VALUE_LENGTH;
|
|
cdwClass = REGSTR_MAX_VALUE_LENGTH;
|
|
lRet = RegEnumKeyEx( hSrc,
|
|
dwKeyIndex,
|
|
szSubKey,
|
|
&dwSubKeyLength,
|
|
NULL,
|
|
szClass,
|
|
&cdwClass,
|
|
NULL );
|
|
|
|
if (lRet == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
lRet = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
else if (lRet == ERROR_SUCCESS)
|
|
{
|
|
if ((lRet = RegCreateKeyEx( hDest,
|
|
szSubKey,
|
|
0,
|
|
szClass,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hNewKey,
|
|
&dwDisposition )) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Copy all subkeys.
|
|
//
|
|
if ((lRet = RegOpenKeyEx( hSrc,
|
|
szSubKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey )) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Recursively copy the remainder of the tree.
|
|
//
|
|
lRet = Intl_CreateRegTree(hKey, hNewKey);
|
|
|
|
CloseHandle(hKey);
|
|
CloseHandle(hNewKey);
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(hNewKey);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
++dwKeyIndex;
|
|
}
|
|
|
|
return (lRet);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LoadNtUserHive
|
|
//
|
|
// The caller of this function needs to call Intl_UnloadNtUserHive() when
|
|
// the function succeeds in order to properly release the handle on the
|
|
// NTUSER.DAT file.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
HKEY Intl_LoadNtUserHive(
|
|
LPCTSTR lpRoot,
|
|
LPCTSTR lpKeyName,
|
|
LPCTSTR lpAccountName,
|
|
BOOLEAN *lpWasEnabled)
|
|
{
|
|
HKEY hKey = NULL;
|
|
LONG rc = 0L;
|
|
BOOL bRet = TRUE;
|
|
TCHAR szProfile[REGSTR_MAX_VALUE_LENGTH] = {0};
|
|
TCHAR szKeyName[REGSTR_MAX_VALUE_LENGTH] = {0};
|
|
DWORD cchSize;
|
|
|
|
cchSize = MAX_PATH;
|
|
if(NULL == lpAccountName)
|
|
{
|
|
//
|
|
// Get the file name for the Default User profile.
|
|
//
|
|
if (!GetDefaultUserProfileDirectory(szProfile, &cchSize))
|
|
{
|
|
return (NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the file name for the specified account's User profile.
|
|
//
|
|
if (!GetProfilesDirectory(szProfile, &cchSize))
|
|
{
|
|
return (NULL);
|
|
}
|
|
// lstrcat(szProfile, lpAccountName);
|
|
if(FAILED(StringCchCat(szProfile, ARRAYSIZE(szProfile), lpAccountName)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
// lstrcat(szProfile, TEXT("\\NTUSER.DAT"));
|
|
if(FAILED(StringCchCat(szProfile, ARRAYSIZE(szProfile), TEXT("\\NTUSER.DAT"))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Set the value in the Default User hive.
|
|
//
|
|
rc = Intl_SetPrivilegeAccessToken(SE_RESTORE_NAME, TRUE,lpWasEnabled);
|
|
if (NT_SUCCESS(rc))
|
|
{
|
|
//
|
|
// Load the hive and restore the privilege to its previous state.
|
|
//
|
|
rc = RegLoadKey(HKEY_USERS, lpRoot, szProfile);
|
|
Intl_SetPrivilegeAccessToken(SE_RESTORE_NAME, *lpWasEnabled,lpWasEnabled);
|
|
|
|
//
|
|
// If the hive loaded properly, set the value.
|
|
//
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Get the temporary key name.
|
|
//
|
|
//swprintf(szKeyName, TEXT("%s\\%s"), lpRoot, lpKeyName);
|
|
if(SUCCEEDED(StringCchPrintfW(szKeyName, REGSTR_MAX_VALUE_LENGTH, TEXT("%s\\%s"), lpRoot, lpKeyName)))
|
|
{
|
|
if ((rc = RegOpenKeyEx( HKEY_USERS,
|
|
szKeyName,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey )) == ERROR_SUCCESS)
|
|
{
|
|
return (hKey);
|
|
}
|
|
}
|
|
|
|
Intl_UnloadNtUserHive(lpRoot, lpWasEnabled);
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_UnloadNtUserHive
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_UnloadNtUserHive(
|
|
LPCTSTR lpRoot,
|
|
BOOLEAN *lpWasEnabled)
|
|
{
|
|
if (NT_SUCCESS(Intl_SetPrivilegeAccessToken( SE_RESTORE_NAME,
|
|
TRUE,
|
|
lpWasEnabled )))
|
|
{
|
|
RegUnLoadKey(HKEY_USERS, lpRoot);
|
|
Intl_SetPrivilegeAccessToken( SE_RESTORE_NAME,
|
|
*lpWasEnabled,
|
|
lpWasEnabled );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_ChangeUILangForAllUsers
|
|
//
|
|
// LATER: Clean up this function to put all six registry update cases into
|
|
// one loop, with a struct that contains info on the reg key to
|
|
// update/hive to load and the cases in which they are to run.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_ChangeUILangForAllUsers(
|
|
LANGID UILanguageId)
|
|
{
|
|
HKEY hKey;
|
|
HKEY hHive;
|
|
TCHAR szData[MAX_PATH];
|
|
TCHAR* arStrings[1];
|
|
LONG rc = 0L;
|
|
BOOLEAN wasEnabled;
|
|
int i;
|
|
|
|
//
|
|
// Array of user accounts that we care about
|
|
//
|
|
LPTSTR ppDefaultUser[] = { TEXT(".DEFAULT"), TEXT("S-1-5-19"), TEXT("S-1-5-20")};
|
|
TCHAR szRegPath[MAX_PATH];
|
|
|
|
//
|
|
// Save the UILanguageId as a string.
|
|
//
|
|
//wsprintf(szData, TEXT("%08x"), UILanguageId);
|
|
if(FAILED(StringCchPrintf(szData, ARRAYSIZE(szData), TEXT("%08x"), UILanguageId)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// We need to log the event to the MUI event log so admins have warning of tampering (bug 553706)
|
|
//
|
|
// We only have 1 string to log
|
|
arStrings[0]=szData;
|
|
Intl_LogEvent(MSG_REGIONALOPTIONSCHANGE_DEFUILANG, c_szEventSourceName, ARRAYSIZE(arStrings), arStrings);
|
|
|
|
//
|
|
// Now save value for all the users -- in minisetup
|
|
// only the first entry will succeed (see below)
|
|
//
|
|
for (i=0; i< ARRAYSIZE(ppDefaultUser); i++)
|
|
{
|
|
if (!PathCombine(szRegPath, ppDefaultUser[i], TEXT("Control Panel\\Desktop")))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Set the value in .DEFAULT registry.
|
|
//
|
|
if ((rc = RegOpenKeyEx( HKEY_USERS,
|
|
szRegPath,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey )) == ERROR_SUCCESS)
|
|
{
|
|
rc = RegSetValueEx( hKey,
|
|
c_szMUIValue,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szData,
|
|
(lstrlen(szData) + 1) * sizeof(TCHAR) );
|
|
//
|
|
// Sync up UI language pending key
|
|
//
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = RegSetValueEx( hKey,
|
|
szMUILangPending,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szData,
|
|
(lstrlen(szData) + 1) * sizeof(TCHAR) );
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the value into the .DEFAULT user hive
|
|
//
|
|
|
|
//
|
|
// Load the default hive
|
|
//
|
|
if ((hHive = Intl_LoadNtUserHive( TEXT("tempKey"),
|
|
c_szCPanelDesktop,
|
|
NULL,
|
|
&wasEnabled )) == NULL )
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Save the MUI language value in the default user NTUSER.dat
|
|
//
|
|
rc = RegSetValueEx( hHive,
|
|
c_szMUIValue,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szData,
|
|
(lstrlen(szData) + 1) * sizeof(TCHAR));
|
|
|
|
//
|
|
// Sync up UI language pending key
|
|
//
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = RegSetValueEx( hHive,
|
|
szMUILangPending,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szData,
|
|
(lstrlen(szData) + 1) * sizeof(TCHAR) );
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
RegCloseKey(hHive);
|
|
Intl_UnloadNtUserHive(TEXT("tempKey"), &wasEnabled);
|
|
|
|
|
|
//
|
|
// For the minisetup case, S-1-5-19 and S-1-5-20 are not yet loaded,
|
|
// so the above code will have failed. Load the hives directly.
|
|
//
|
|
|
|
if(2 == g_bSetupCase)
|
|
{
|
|
//
|
|
// Array of user account locations that we care about
|
|
//
|
|
LPTSTR ppMiniSetupUsers[] = { TEXT("\\LocalService"), TEXT("\\NetworkService") };
|
|
|
|
for (i=0; i< ARRAYSIZE(ppMiniSetupUsers); i++)
|
|
{
|
|
//
|
|
// Load the appropriate hive
|
|
//
|
|
if ((hHive = Intl_LoadNtUserHive( TEXT("tempKey"),
|
|
c_szCPanelDesktop,
|
|
ppMiniSetupUsers[i],
|
|
&wasEnabled )) == NULL )
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Save the MUI language value in the appropriate NTUSER.dat
|
|
//
|
|
rc = RegSetValueEx( hHive,
|
|
c_szMUIValue,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szData,
|
|
(lstrlen(szData) + 1) * sizeof(TCHAR));
|
|
|
|
//
|
|
// Sync up UI language pending key
|
|
//
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
rc = RegSetValueEx( hHive,
|
|
szMUILangPending,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szData,
|
|
(lstrlen(szData) + 1) * sizeof(TCHAR) );
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
RegCloseKey(hHive);
|
|
Intl_UnloadNtUserHive(TEXT("tempKey"), &wasEnabled);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Install Language Input locales.
|
|
//
|
|
return Intl_InstallKeyboardLayout(NULL,
|
|
MAKELCID(UILanguageId, SORT_DEFAULT),
|
|
0,
|
|
FALSE,
|
|
TRUE,
|
|
FALSE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_CreateEventLog()
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL Intl_CreateEventLog()
|
|
{
|
|
HKEY hk;
|
|
DWORD dwData;
|
|
TCHAR szPath[MAX_PATH+1] = {0};
|
|
HRESULT hr = S_OK;
|
|
size_t cch = 0;
|
|
size_t cb = 0;
|
|
|
|
// Find the windows directory
|
|
if (!GetSystemWindowsDirectory(szPath, MAX_PATH+1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// check retrieved winpath, it needs to have space to append "system32\intl.cpl" at the end
|
|
hr = StringCchLength(szPath, ARRAYSIZE(szPath), &cch);
|
|
if (FAILED(hr) || ((cch + 17) >= MAX_PATH+1))
|
|
{
|
|
// If this really happened, windows wouldn't boot! (kernel32.dll would be too long a path!)
|
|
return FALSE;
|
|
}
|
|
|
|
// append system32\\intl.cpl
|
|
// Add a \ if the winpath didn't have it already
|
|
if (szPath[cch-1] != TEXT('\\'))
|
|
{
|
|
szPath[cch++] = '\\';
|
|
szPath[cch] = '\0';
|
|
}
|
|
|
|
// Add our string
|
|
hr = StringCchCat(szPath, MAX_PATH+1, TEXT("system32\\intl.cpl"));
|
|
if (FAILED(hr))
|
|
{
|
|
// Somehow we couln't fix our strings (may not have had enough space)
|
|
return FALSE;
|
|
}
|
|
|
|
// get the byte count for RegSetValueEx
|
|
hr = StringCbLength(szPath, (MAX_PATH+1) * sizeof(TCHAR), &cb);
|
|
if (FAILED(hr))
|
|
{
|
|
// Our string didn't work.
|
|
return FALSE;
|
|
}
|
|
|
|
// Add our event log source name as a subkey under the System
|
|
// key in the EventLog registry key.
|
|
if (ERROR_SUCCESS != RegCreateKey(HKEY_LOCAL_MACHINE, c_szEventRegistryPath, &hk))
|
|
{
|
|
// Couldn't open/create the registry key
|
|
return FALSE;
|
|
}
|
|
|
|
// Add our file name to the EventMessageFile subkey. (Source for event log strings)
|
|
if (RegSetValueEx(hk, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (LPBYTE) szPath, cb))
|
|
{
|
|
// That didn't work.
|
|
RegCloseKey(hk);
|
|
return FALSE;
|
|
}
|
|
|
|
// Set the supported event types in the TypesSupported subkey.
|
|
dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
|
|
|
|
if (RegSetValueEx(hk, TEXT("TypesSupported"), 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD)))
|
|
{
|
|
// Didn't work.
|
|
RegCloseKey(hk);
|
|
return FALSE;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != RegCloseKey(hk))
|
|
{
|
|
// Couldn't close key (at least it got here though!)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LogEvent()
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_LogEvent(
|
|
DWORD dwEventId, LPCTSTR szEventSource, WORD wNumStrings, LPCWSTR *lpStrings)
|
|
{
|
|
TCHAR szUserName[UNLEN+1];
|
|
PSID psidUser = NULL;
|
|
TCHAR *pszDomain = NULL;
|
|
DWORD cbSid = 0;
|
|
DWORD cbDomain = 0;
|
|
DWORD cbUser = ARRAYSIZE(szUserName);
|
|
SID_NAME_USE snu;
|
|
HANDLE hLog;
|
|
BOOL bResult = FALSE;
|
|
|
|
// Make sure our event source is registered correctly first
|
|
// (We actually don't need to do this if szEventSource isn't us, but right
|
|
// now only us is calling this, so we'll assume we're us.)
|
|
// This is redundant, we don't have to do this every time, however it doesn't
|
|
// hurt much since these events will be very rare and its a lot easier this
|
|
// way, and it has the advantage of repairing us if our registry entries were broken
|
|
//
|
|
// We ignore the error condition, because Application log is better than none!
|
|
Intl_CreateEventLog();
|
|
|
|
// register the event source, first try not having written to the registry
|
|
hLog = RegisterEventSource(NULL, szEventSource);
|
|
if (hLog == NULL)
|
|
{
|
|
// Failed
|
|
goto Exit;
|
|
}
|
|
|
|
// get the sid from the current thread token, this should be the current user who's
|
|
// running the installation
|
|
if (!GetUserName(szUserName, &cbUser))
|
|
{
|
|
// Failed
|
|
goto Exit;
|
|
}
|
|
|
|
// convert user name to its security identifier, first time to get buffer size, second time
|
|
// to actually get the Sid
|
|
if (!LookupAccountName(NULL, szUserName, NULL, &cbSid, NULL, &cbDomain, &snu))
|
|
{
|
|
// allocate the buffers
|
|
psidUser = (PSID) LocalAlloc(LPTR, cbSid);
|
|
if (NULL == psidUser)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// NOTENOTE: cbDomain is in TCHAR.
|
|
pszDomain = (TCHAR*) LocalAlloc(LPTR, cbDomain * sizeof(TCHAR));
|
|
if (NULL == pszDomain)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (!LookupAccountName(NULL, szUserName, psidUser, &cbSid, pszDomain, &cbDomain, &snu))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!ReportEvent(hLog,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0,
|
|
dwEventId,
|
|
psidUser,
|
|
wNumStrings,
|
|
0,
|
|
lpStrings,
|
|
NULL))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If we got this far without going to, then we're true.
|
|
bResult = TRUE;
|
|
|
|
Exit:
|
|
if (NULL != hLog)
|
|
{
|
|
if (!DeregisterEventSource(hLog))
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
if (psidUser)
|
|
{
|
|
if (LocalFree(psidUser))
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
if (pszDomain)
|
|
{
|
|
if (LocalFree(pszDomain))
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LoadLanguageGroups
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_LoadLanguageGroups(
|
|
HWND hDlg)
|
|
{
|
|
LPLANGUAGEGROUP pLG;
|
|
DWORD dwExStyle;
|
|
RECT Rect;
|
|
LV_COLUMN Column;
|
|
LV_ITEM Item;
|
|
int iIndex;
|
|
|
|
//
|
|
// Open the Inf file.
|
|
//
|
|
g_hIntlInf = SetupOpenInfFile(szIntlInf, NULL, INF_STYLE_WIN4, NULL);
|
|
if (g_hIntlInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!SetupOpenAppendInfFile(NULL, g_hIntlInf, NULL))
|
|
{
|
|
SetupCloseInfFile(g_hIntlInf);
|
|
g_hIntlInf = NULL;
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Get all supported language groups from the inf file.
|
|
//
|
|
if (Intl_GetSupportedLanguageGroups() == FALSE)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Close the inf file.
|
|
//
|
|
SetupCloseInfFile(g_hIntlInf);
|
|
g_hIntlInf = NULL;
|
|
|
|
//
|
|
// Enumerate all installed language groups.
|
|
//
|
|
if (Intl_EnumInstalledLanguageGroups() == FALSE)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetSupportedLanguageGroups
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_GetSupportedLanguageGroups()
|
|
{
|
|
UINT LanguageGroup;
|
|
HANDLE hLanguageGroup;
|
|
LPLANGUAGEGROUP pLG;
|
|
INFCONTEXT Context;
|
|
TCHAR szSection[MAX_PATH];
|
|
TCHAR szTemp[MAX_PATH];
|
|
int LineCount, LineNum;
|
|
DWORD ItemCount;
|
|
WORD wItemStatus;
|
|
|
|
//
|
|
// Get the number of supported language groups from the inf file.
|
|
//
|
|
LineCount = (UINT)SetupGetLineCount(g_hIntlInf, TEXT("LanguageGroups"));
|
|
if (LineCount <= 0)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Go through all supported language groups in the inf file.
|
|
//
|
|
for (LineNum = 0; LineNum < LineCount; LineNum++)
|
|
{
|
|
if (SetupGetLineByIndex(g_hIntlInf, TEXT("LanguageGroups"), LineNum, &Context) &&
|
|
SetupGetIntField(&Context, 0, &LanguageGroup))
|
|
{
|
|
//
|
|
// Create the new node.
|
|
//
|
|
if (!(hLanguageGroup = GlobalAlloc(GHND, sizeof(LANGUAGEGROUP))))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
pLG = GlobalLock(hLanguageGroup);
|
|
|
|
//
|
|
// Fill in the new node with the appropriate info.
|
|
//
|
|
pLG->wStatus = 0;
|
|
pLG->LanguageGroup = LanguageGroup;
|
|
pLG->hLanguageGroup = hLanguageGroup;
|
|
(pLG->pszName)[0] = 0;
|
|
pLG->NumLocales = 0;
|
|
pLG->NumAltSorts = 0;
|
|
|
|
//
|
|
// Set the collection
|
|
//
|
|
if ((pLG->LanguageGroup == LGRPID_JAPANESE) ||
|
|
(pLG->LanguageGroup == LGRPID_KOREAN) ||
|
|
(pLG->LanguageGroup == LGRPID_TRADITIONAL_CHINESE) ||
|
|
(pLG->LanguageGroup == LGRPID_SIMPLIFIED_CHINESE) )
|
|
{
|
|
pLG->LanguageCollection = CJK_COLLECTION;
|
|
}
|
|
else if ((pLG->LanguageGroup == LGRPID_ARABIC) ||
|
|
(pLG->LanguageGroup == LGRPID_ARMENIAN) ||
|
|
(pLG->LanguageGroup == LGRPID_GEORGIAN) ||
|
|
(pLG->LanguageGroup == LGRPID_HEBREW) ||
|
|
(pLG->LanguageGroup == LGRPID_INDIC) ||
|
|
(pLG->LanguageGroup == LGRPID_VIETNAMESE) ||
|
|
(pLG->LanguageGroup == LGRPID_THAI))
|
|
{
|
|
pLG->LanguageCollection = COMPLEX_COLLECTION;
|
|
}
|
|
else
|
|
{
|
|
pLG->LanguageCollection = BASIC_COLLECTION;
|
|
}
|
|
|
|
//
|
|
// Get the appropriate display string.
|
|
//
|
|
if (!SetupGetStringField(&Context, 1, pLG->pszName, MAX_PATH, NULL))
|
|
{
|
|
GlobalUnlock(hLanguageGroup);
|
|
GlobalFree(hLanguageGroup);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the list of locales for this language group.
|
|
//
|
|
if (Intl_GetLocaleList(pLG) == FALSE)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Add the language group to the front of the linked list.
|
|
//
|
|
pLG->pNext = pLanguageGroups;
|
|
pLanguageGroups = pLG;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_EnumInstalledLanguageGroups
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_EnumInstalledLanguageGroups()
|
|
{
|
|
HKEY hKey;
|
|
TCHAR szValue[MAX_PATH];
|
|
TCHAR szData[MAX_PATH];
|
|
TCHAR szDefault[SIZE_64];
|
|
DWORD dwIndex, cchValue, cbData;
|
|
LONG rc;
|
|
UINT LanguageGroup, OriginalGroup, DefaultGroup, UILanguageGroup;
|
|
LPLANGUAGEGROUP pLG;
|
|
LCID Locale;
|
|
LANGID Language;
|
|
int Ctr;
|
|
|
|
//
|
|
// Get the original install language so that we can mark that
|
|
// language group as permanent.
|
|
//
|
|
Language = GetSystemDefaultUILanguage();
|
|
if (SUBLANGID(Language) == SUBLANG_NEUTRAL)
|
|
{
|
|
Language = MAKELANGID(PRIMARYLANGID(Language), SUBLANG_DEFAULT);
|
|
}
|
|
|
|
if ((OriginalGroup = Intl_GetLanguageGroup(Language)) == 0)
|
|
{
|
|
OriginalGroup = 1;
|
|
}
|
|
|
|
//
|
|
// Get the default system locale so that we can mark that language
|
|
// group as permanent. During gui mode setup, read the system locale from
|
|
// the registry to make the info on the setup page consistent with intl.cpl.
|
|
// SysLocaleID will be the registry value in case of setup.
|
|
//
|
|
Locale = SysLocaleID;
|
|
if (Locale == (LCID)Language)
|
|
{
|
|
DefaultGroup = OriginalGroup;
|
|
}
|
|
else
|
|
{
|
|
if ((DefaultGroup = Intl_GetLanguageGroup(Locale)) == 0)
|
|
{
|
|
DefaultGroup = 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the UI language's language groups to disable the user from
|
|
// un-installing them. MUISETUP makes sure that each installed UI
|
|
// language has its language group installed.
|
|
//
|
|
Intl_GetUILanguageGroups(&UILangGroup);
|
|
|
|
//
|
|
// Open the HKLM\SYSTEM\CurrentControlSet\Control\Nls\Language Groups
|
|
// key.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szLanguageGroups,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) != ERROR_SUCCESS)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Enumerate the values in the Language Groups key.
|
|
//
|
|
dwIndex = 0;
|
|
cchValue = sizeof(szValue) / sizeof(TCHAR);
|
|
szValue[0] = TEXT('\0');
|
|
cbData = sizeof(szData);
|
|
szData[0] = TEXT('\0');
|
|
rc = RegEnumValue( hKey,
|
|
dwIndex,
|
|
szValue,
|
|
&cchValue,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szData,
|
|
&cbData );
|
|
|
|
while (rc == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If the language group contains data, then it is installed.
|
|
//
|
|
if ((szData[0] != 0) &&
|
|
(LanguageGroup = TransNum(szValue)))
|
|
{
|
|
//
|
|
// Find the language group in the linked list and mark it as
|
|
// originally installed.
|
|
//
|
|
pLG = pLanguageGroups;
|
|
while (pLG)
|
|
{
|
|
if (pLG->LanguageGroup == LanguageGroup)
|
|
{
|
|
pLG->wStatus |= ML_INSTALL;
|
|
|
|
//
|
|
// If this is a language group for a UI language that's
|
|
// installed, then disable the un-installation of this
|
|
// language group.
|
|
//
|
|
Ctr = 0;
|
|
while (Ctr < UILangGroup.iCount)
|
|
{
|
|
if (UILangGroup.lgrp[Ctr] == LanguageGroup)
|
|
{
|
|
pLG->wStatus |= ML_PERMANENT;
|
|
break;
|
|
}
|
|
Ctr++;
|
|
}
|
|
|
|
if (pLG->LanguageGroup == OriginalGroup)
|
|
{
|
|
pLG->wStatus |= ML_PERMANENT;
|
|
}
|
|
if (pLG->LanguageGroup == DefaultGroup)
|
|
{
|
|
pLG->wStatus |= (ML_PERMANENT | ML_DEFAULT);
|
|
|
|
if (LoadString(hInstance, IDS_DEFAULT, szDefault, SIZE_64))
|
|
{
|
|
//lstrcat(pLG->pszName, szDefault);
|
|
if(FAILED(StringCchCat(pLG->pszName, MAX_PATH, szDefault)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
RegCloseKey(hKey);
|
|
return(FALSE);
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
pLG = pLG->pNext;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the next enum value.
|
|
//
|
|
dwIndex++;
|
|
cchValue = sizeof(szValue) / sizeof(TCHAR);
|
|
szValue[0] = TEXT('\0');
|
|
cbData = sizeof(szData);
|
|
szData[0] = TEXT('\0');
|
|
rc = RegEnumValue( hKey,
|
|
dwIndex,
|
|
szValue,
|
|
&cchValue,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szData,
|
|
&cbData );
|
|
}
|
|
|
|
//
|
|
// Close the registry key handle.
|
|
//
|
|
RegCloseKey(hKey);
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LanguageGroupDirExist
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_LanguageGroupDirExist(
|
|
PTSTR pszLangDir)
|
|
{
|
|
TCHAR szLanguageGroupDir[MAX_PATH];
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE FindHandle;
|
|
TCHAR SavedChar;
|
|
|
|
//
|
|
// If it doesn't start with lang, then this is a core language.
|
|
//
|
|
SavedChar = pszLangDir[4];
|
|
pszLangDir[4] = TEXT('\0');
|
|
if (lstrcmp(pszLangDir, TEXT("lang")))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
pszLangDir[4] = SavedChar;
|
|
|
|
//
|
|
// Format the path to the language group directory.
|
|
//
|
|
//lstrcpy(szLanguageGroupDir, pSetupSourcePathWithArchitecture);
|
|
//lstrcat(szLanguageGroupDir, TEXT("\\"));
|
|
//lstrcat(szLanguageGroupDir, pszLangDir);
|
|
if(FAILED(StringCchCopy(szLanguageGroupDir, MAX_PATH, pSetupSourcePathWithArchitecture)) ||
|
|
FAILED(StringCchCat(szLanguageGroupDir, MAX_PATH, TEXT("\\"))) ||
|
|
FAILED(StringCchCat(szLanguageGroupDir, MAX_PATH, pszLangDir)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// See if the language group directory exists.
|
|
//
|
|
FindHandle = FindFirstFile(szLanguageGroupDir, &FindData);
|
|
if (FindHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(FindHandle);
|
|
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LanguageGroupFilesExist
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_LanguageGroupFilesExist()
|
|
{
|
|
TCHAR szLanguageGroupDir[MAX_PATH];
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE FindHandle;
|
|
|
|
//
|
|
// Format the path to the language group directory. Add the wildcard
|
|
// to search for any files located in the lang directory.
|
|
//
|
|
//lstrcpy(szLanguageGroupDir, pSetupSourcePathWithArchitecture);
|
|
//lstrcat(szLanguageGroupDir, TEXT("\\Lang\\*"));
|
|
if(FAILED(StringCchCopy(szLanguageGroupDir, MAX_PATH, pSetupSourcePathWithArchitecture)) ||
|
|
FAILED(StringCchCat(szLanguageGroupDir, MAX_PATH, TEXT("\\Lang\\*"))))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// See if at least one file exists.
|
|
//
|
|
FindHandle = FindFirstFile(szLanguageGroupDir, &FindData);
|
|
if (FindHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(FindHandle);
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Return failure.
|
|
//
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetLocaleList
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_GetLocaleList(
|
|
LPLANGUAGEGROUP pLG)
|
|
{
|
|
TCHAR szSection[MAX_PATH];
|
|
INFCONTEXT Context;
|
|
int LineCount, LineNum;
|
|
LCID Locale;
|
|
|
|
//
|
|
// Get the inf section name.
|
|
//
|
|
//wsprintf(szSection, TEXT("%ws%d"), szLocaleListPrefix, pLG->LanguageGroup);
|
|
if(FAILED(StringCchPrintf(szSection, ARRAYSIZE(szSection), TEXT("%ws%d"), szLocaleListPrefix, pLG->LanguageGroup)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the number of locales for the language group.
|
|
//
|
|
LineCount = (UINT)SetupGetLineCount(g_hIntlInf, szSection);
|
|
if (LineCount <= 0)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Add each locale in the list to the language group node.
|
|
//
|
|
for (LineNum = 0; LineNum < LineCount; LineNum++)
|
|
{
|
|
if (SetupGetLineByIndex(g_hIntlInf, szSection, LineNum, &Context) &&
|
|
SetupGetIntField(&Context, 0, &Locale))
|
|
{
|
|
if (SORTIDFROMLCID(Locale))
|
|
{
|
|
//
|
|
// Add the locale to the alternate sort list for this
|
|
// language group.
|
|
//
|
|
if (pLG->NumAltSorts >= MAX_PATH)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
pLG->pAltSortList[pLG->NumAltSorts] = Locale;
|
|
(pLG->NumAltSorts)++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add the locale to the locale list for this
|
|
// language group.
|
|
//
|
|
if (pLG->NumLocales >= MAX_PATH)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
pLG->pLocaleList[pLG->NumLocales] = Locale;
|
|
(pLG->NumLocales)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Region_GetLocaleLanguageGroup
|
|
//
|
|
// Reads the Language Group Id of the given language.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD Intl_GetLanguageGroup(
|
|
LCID lcid)
|
|
{
|
|
TCHAR szValue[MAX_PATH];
|
|
TCHAR szData[MAX_PATH];
|
|
HKEY hKey;
|
|
DWORD cbData;
|
|
|
|
//wsprintf(szValue, TEXT("%8.8x"), lcid);
|
|
if(FAILED(StringCchPrintf(szValue, ARRAYSIZE(szValue), TEXT("%8.8x"), lcid)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
|
|
szData[0] = 0;
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szInstalledLocales,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
cbData = sizeof(szData);
|
|
RegQueryValueEx(hKey, szValue, NULL, NULL, (LPBYTE)szData, &cbData);
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return (TransNum(szData));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetUILanguageGroups
|
|
//
|
|
// Reads the language groups of all the UI languages installed on this
|
|
// machine.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_GetUILanguageGroups(
|
|
PUILANGUAGEGROUP pUILanguageGroup)
|
|
{
|
|
//
|
|
// Enumerate the installed UI languages.
|
|
//
|
|
pUILanguageGroup->iCount = 0L;
|
|
|
|
EnumUILanguages(Intl_EnumUILanguagesProc, 0, (LONG_PTR)pUILanguageGroup);
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_EnumUILanguagesProc
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CALLBACK Intl_EnumUILanguagesProc(
|
|
LPWSTR pwszUILanguage,
|
|
LONG_PTR lParam)
|
|
{
|
|
int Ctr = 0;
|
|
LGRPID lgrp;
|
|
PUILANGUAGEGROUP pUILangGroup = (PUILANGUAGEGROUP)lParam;
|
|
LCID UILanguage = TransNum(pwszUILanguage);
|
|
|
|
if (UILanguage)
|
|
{
|
|
if ((lgrp = Intl_GetLanguageGroup(UILanguage)) == 0)
|
|
{
|
|
lgrp = 1; // default;
|
|
}
|
|
|
|
while (Ctr < pUILangGroup->iCount)
|
|
{
|
|
if (pUILangGroup->lgrp[Ctr] == lgrp)
|
|
{
|
|
break;
|
|
}
|
|
Ctr++;
|
|
}
|
|
|
|
//
|
|
// Theoritically, we won't go over 64 language groups!
|
|
//
|
|
if ((Ctr == pUILangGroup->iCount) && (Ctr < MAX_UI_LANG_GROUPS))
|
|
{
|
|
pUILangGroup->lgrp[Ctr] = lgrp;
|
|
pUILangGroup->iCount++;
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_SaveValuesToDefault
|
|
//
|
|
// This function copies the current user settings under the srcKey to
|
|
// the Default user under the destKey.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_SaveValuesToDefault(
|
|
LPCTSTR srcKey,
|
|
LPCTSTR destKey)
|
|
{
|
|
HKEY hkeyLayouts;
|
|
HKEY hkeyLayouts_DefUser;
|
|
|
|
//
|
|
// 1. Open the Current user key.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_CURRENT_USER,
|
|
srcKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkeyLayouts ) != ERROR_SUCCESS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 2. Open the .Default hive key.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_USERS,
|
|
destKey,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkeyLayouts_DefUser ) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hkeyLayouts);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 3. Delete .Default key values.
|
|
//
|
|
Intl_DeleteRegKeyValues(hkeyLayouts_DefUser);
|
|
|
|
//
|
|
// 4. Delete .Default subkeys.
|
|
//
|
|
Intl_DeleteRegSubKeys(hkeyLayouts_DefUser);
|
|
|
|
//
|
|
// 5. Copy tree.
|
|
//
|
|
Intl_CreateRegTree(hkeyLayouts, hkeyLayouts_DefUser);
|
|
|
|
//
|
|
// 6. Clean up
|
|
//
|
|
RegCloseKey(hkeyLayouts_DefUser);
|
|
RegCloseKey(hkeyLayouts);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_SaveValuesToNtUserFile
|
|
//
|
|
// This function copy current user setting under the srcKey to the Default
|
|
// user hive under the destKey.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_SaveValuesToNtUserFile(
|
|
HKEY hSourceRegKey,
|
|
LPCTSTR srcKey,
|
|
LPCTSTR destKey)
|
|
{
|
|
HKEY hRegKey;
|
|
HKEY hHive;
|
|
BOOLEAN wasEnabled;
|
|
|
|
//
|
|
// 1. Open the Current user key.
|
|
//
|
|
if (RegOpenKeyEx( hSourceRegKey,
|
|
srcKey,
|
|
0,
|
|
KEY_READ,
|
|
&hRegKey ) != ERROR_SUCCESS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 2. Load the hive to a temporary key location.
|
|
//
|
|
if ((hHive = Intl_LoadNtUserHive( TEXT("TempKey"),
|
|
destKey,
|
|
NULL,
|
|
&wasEnabled )) == NULL)
|
|
{
|
|
RegCloseKey(hRegKey);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 3. Delete .Default key values.
|
|
//
|
|
Intl_DeleteRegKeyValues(hHive);
|
|
|
|
//
|
|
// 4. Delete .Default subkeys.
|
|
//
|
|
Intl_DeleteRegSubKeys(hHive);
|
|
|
|
//
|
|
// 5. Copy tree.
|
|
//
|
|
Intl_CreateRegTree(hRegKey, hHive);
|
|
|
|
//
|
|
// 6. Clean up.
|
|
//
|
|
RegCloseKey(hHive);
|
|
Intl_UnloadNtUserHive(TEXT("TempKey"), &wasEnabled);
|
|
RegCloseKey(hRegKey);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_IsSetupMode
|
|
//
|
|
// Look into the registry if we are currently in setup mode.
|
|
//
|
|
// Return Values:
|
|
//
|
|
// 0 == not in setup
|
|
// 1 == setup
|
|
// 2 == minisetup
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Intl_IsSetupMode()
|
|
{
|
|
HKEY hKey;
|
|
DWORD fSystemSetupInProgress = 0;
|
|
DWORD cbData = 0;
|
|
|
|
//
|
|
// Open the registry key used by setup
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szSetupKey,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Query for the value indicating that we are in setup.
|
|
//
|
|
// SystemSetupInProgress == 1 means we are in system setup
|
|
// or minisetup.
|
|
//
|
|
cbData = sizeof(fSystemSetupInProgress);
|
|
RegQueryValueEx( hKey,
|
|
szSetupInProgress,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&fSystemSetupInProgress,
|
|
&cbData );
|
|
|
|
if(1 == fSystemSetupInProgress)
|
|
{
|
|
//
|
|
// We are in setup or in minisetup. Lets find out which one.
|
|
// Query for the value indicating that we are in mini setup.
|
|
//
|
|
// MiniSetupInProgress == 1 means we are in mini setup
|
|
//
|
|
fSystemSetupInProgress = 0;
|
|
cbData = sizeof(fSystemSetupInProgress);
|
|
RegQueryValueEx( hKey,
|
|
szMiniSetupInProgress,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&fSystemSetupInProgress,
|
|
&cbData );
|
|
if(1 == fSystemSetupInProgress)
|
|
{
|
|
//
|
|
// In minisetup, so set the return value to 2
|
|
//
|
|
fSystemSetupInProgress = 2;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We are just in regular setup
|
|
//
|
|
fSystemSetupInProgress = 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
RegCloseKey(hKey);
|
|
|
|
//
|
|
// Check the value
|
|
//
|
|
if (0 != fSystemSetupInProgress)
|
|
{
|
|
//
|
|
// In setup mode...
|
|
//
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_SETUP_MODE, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ((int)fSystemSetupInProgress);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_IsWinntUpgrade
|
|
//
|
|
// Look into the registry if we are currently in winnt upgrade.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_IsWinntUpgrade()
|
|
{
|
|
HKEY hKey;
|
|
DWORD fUpgradeInProgress = 0;
|
|
DWORD cbData = 0;
|
|
|
|
//
|
|
// Verify that we're in setup first.
|
|
//
|
|
if (!g_bSetupCase)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Open the registry key used by setup.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szSetupKey,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) != ERROR_SUCCESS)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Query for the value indicating that we are in setup.
|
|
//
|
|
cbData = sizeof(fUpgradeInProgress);
|
|
if (RegQueryValueEx( hKey,
|
|
szSetupUpgrade,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&fUpgradeInProgress,
|
|
&cbData ) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
RegCloseKey(hKey);
|
|
|
|
//
|
|
// Check the value.
|
|
//
|
|
if (fUpgradeInProgress)
|
|
{
|
|
//
|
|
// Upgrade scenario.
|
|
//
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_UPGRADE_SCENARIO, NULL);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_IsUIFontSubstitute
|
|
//
|
|
// Look into the registry if we need to substitute the font.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_IsUIFontSubstitute()
|
|
{
|
|
HKEY hKey;
|
|
DWORD fUIFontSubstitute = 0;
|
|
DWORD cbData = 0;
|
|
|
|
//
|
|
// Command line call, no need to check registry
|
|
//
|
|
if (g_bMatchUIFont)
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// Open the registry key used MUI font substitution.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szMUILanguages,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) != ERROR_SUCCESS)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Query for the value indicating that we need to apply font
|
|
// substitution.
|
|
//
|
|
cbData = sizeof(fUIFontSubstitute);
|
|
if (RegQueryValueEx( hKey,
|
|
szUIFontSubstitute,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&fUIFontSubstitute,
|
|
&cbData ) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
RegCloseKey(hKey);
|
|
|
|
//
|
|
// Check the value.
|
|
//
|
|
if (fUIFontSubstitute)
|
|
{
|
|
//
|
|
// Upgrade scenario.
|
|
//
|
|
if (g_bLog)
|
|
{
|
|
Intl_LogSimpleMessage(IDS_LOG_FONT_SUBSTITUTE, NULL);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_ApplyFontSubstitute
|
|
//
|
|
// Search into the intl.inf file to see of the SystemLocale need font
|
|
// substitution.
|
|
//
|
|
// Some MUI languages require shell font to match localized fonts,
|
|
// so we have to update following corresponding registry values
|
|
// HKLM\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes
|
|
// HKLM\Software\Microsoft\Windows NT\CurrentVersion\GRE_Initialize
|
|
// Values are read from intl.inf [FontSubstitute] section
|
|
//
|
|
// When locales are switch out of those specific languages or font match is disabled,
|
|
// font.inf and us intl.inf will restore previous values
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
VOID Intl_ApplyFontSubstitute(LCID SystemLocale)
|
|
{
|
|
HINF hIntlInf;
|
|
TCHAR szLCID[25];
|
|
INFCONTEXT Context;
|
|
TCHAR szFont[MAX_PATH] = {0};
|
|
TCHAR szFontSubst[MAX_PATH] = {0};
|
|
TCHAR szGreFontHeight[MAX_PATH] = {0};
|
|
TCHAR szGreFontHeightValue[MAX_PATH] = {0};
|
|
DWORD dwFontHeight;
|
|
|
|
HKEY hKey;
|
|
|
|
//
|
|
// Open the Intl.inf file.
|
|
//
|
|
if (Intl_OpenIntlInfFile(&hIntlInf))
|
|
{
|
|
//
|
|
// Get the locale.
|
|
//
|
|
//wsprintf(szLCID, TEXT("%08x"), SystemLocale);
|
|
if(FAILED(StringCchPrintf(szLCID, ARRAYSIZE(szLCID), TEXT("%08x"), SystemLocale)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
|
|
//
|
|
// Look for the Font Substitute section.
|
|
//
|
|
if (SetupFindFirstLine( hIntlInf,
|
|
szFontSubstitute,
|
|
szLCID,
|
|
&Context ))
|
|
{
|
|
//
|
|
// Look for the font substitute and height infomation
|
|
//
|
|
if (!SetupGetStringField( &Context,
|
|
1,
|
|
szFont,
|
|
MAX_PATH,
|
|
NULL ) ||
|
|
!SetupGetStringField( &Context,
|
|
2,
|
|
szFontSubst,
|
|
MAX_PATH,
|
|
NULL ) ||
|
|
!SetupGetStringField( &Context,
|
|
3,
|
|
szGreFontHeight,
|
|
MAX_PATH,
|
|
NULL ) ||
|
|
!SetupGetStringField( &Context,
|
|
4,
|
|
szGreFontHeightValue,
|
|
MAX_PATH,
|
|
NULL ))
|
|
|
|
|
|
{
|
|
//
|
|
// Clean up.
|
|
//
|
|
Intl_CloseInfFile(hIntlInf);
|
|
return;
|
|
}
|
|
dwFontHeight = StrToInt(szGreFontHeightValue);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Nothing to do for this specific locale. Clean up.
|
|
//
|
|
Intl_CloseInfFile(hIntlInf);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Close the Intl.inf file
|
|
//
|
|
Intl_CloseInfFile(hIntlInf);
|
|
|
|
//
|
|
// Proceed with the font replacement.
|
|
//
|
|
if (szFont[0] && szFontSubst[0])
|
|
{
|
|
//
|
|
// Open the Font Substitute registry key.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szFontSubstitute,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Set the Font value with the Font Substitute.
|
|
//
|
|
RegSetValueEx( hKey,
|
|
szFont,
|
|
0L,
|
|
REG_SZ,
|
|
(LPBYTE)szFontSubst,
|
|
(lstrlen(szFontSubst) + 1) * sizeof(TCHAR) );
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szGreFontInitialize,
|
|
0L,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
|
|
//
|
|
// Set the GRE_Initialize font height value.
|
|
//
|
|
RegSetValueEx( hKey,
|
|
szGreFontHeight,
|
|
0L,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwFontHeight,
|
|
sizeof(dwFontHeight));
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_OpenLogFile
|
|
//
|
|
// Opens the Region and Languages Options log for writing.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE Intl_OpenLogFile()
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwUnicodeHeader;
|
|
HANDLE hFile;
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
TCHAR lpPath[MAX_PATH];
|
|
|
|
|
|
if(0 == GetWindowsDirectory(lpPath, MAX_PATH))
|
|
{
|
|
// SECURITY: Make sure we null out lpPath
|
|
lpPath[0] = TEXT('\0');
|
|
}
|
|
|
|
PathAppend(lpPath, TEXT("\\regopt.log"));
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL;
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
hFile = CreateFile( lpPath,
|
|
GENERIC_WRITE,
|
|
0,
|
|
&SecurityAttributes,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// If the file did not already exist, add the unicode header.
|
|
//
|
|
if (GetLastError() == 0)
|
|
{
|
|
dwUnicodeHeader = 0xFEFF;
|
|
WriteFile(hFile, &dwUnicodeHeader, 2, &dwSize, NULL);
|
|
}
|
|
#endif
|
|
|
|
return (hFile);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LogMessage
|
|
//
|
|
// Writes lpMessage to the Region and Languages Options log.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_LogMessage(
|
|
LPCTSTR lpMessage)
|
|
{
|
|
DWORD dwBytesWritten;
|
|
HANDLE hFile;
|
|
|
|
if (!g_bLog)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
if (lpMessage == NULL)
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
hFile = Intl_OpenLogFile();
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
SetFilePointer(hFile, 0, NULL, FILE_END);
|
|
|
|
WriteFile( hFile,
|
|
lpMessage,
|
|
_tcslen(lpMessage) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL );
|
|
|
|
SetFilePointer(hFile, 0, NULL, FILE_END);
|
|
|
|
WriteFile( hFile,
|
|
TEXT("\r\n"),
|
|
_tcslen(TEXT("\r\n")) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL );
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LogUnattendFile
|
|
//
|
|
// Writes the unattended mode file to the setup log.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_LogUnattendFile(
|
|
LPCTSTR pFileName)
|
|
{
|
|
DWORD dwSize;
|
|
HANDLE hFile;
|
|
OFSTRUCT fileInfo;
|
|
BOOL bResult;
|
|
CHAR inBuffer[MAX_PATH] = {0};
|
|
DWORD nBytesRead;
|
|
WCHAR outBufferW[MAX_PATH] = {0};
|
|
int nWCharRead;
|
|
DWORD status;
|
|
|
|
//
|
|
// Open the unattended mode file.
|
|
//
|
|
if ((hFile = CreateFile( pFileName,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL )) == INVALID_HANDLE_VALUE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Write the header.
|
|
//
|
|
Intl_LogSimpleMessage(IDS_LOG_UNAT_HEADER, NULL);
|
|
|
|
//
|
|
// Read the unattended mode file in 259 byte chunks.
|
|
//
|
|
while (bResult = ReadFile( hFile,
|
|
(LPVOID)inBuffer,
|
|
MAX_PATH - 1,
|
|
&nBytesRead,
|
|
NULL ) && (nBytesRead > 0))
|
|
{
|
|
//
|
|
// Null terminated string.
|
|
//
|
|
inBuffer[nBytesRead] = '\0';
|
|
|
|
//
|
|
// Convert the ansi data to unicode.
|
|
//
|
|
nWCharRead = MultiByteToWideChar( CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
inBuffer,
|
|
nBytesRead,
|
|
outBufferW,
|
|
MAX_PATH );
|
|
|
|
//
|
|
// Write to the log file.
|
|
//
|
|
if (nWCharRead)
|
|
{
|
|
Intl_LogMessage((LPCTSTR)outBufferW);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the footer.
|
|
//
|
|
Intl_LogSimpleMessage(IDS_LOG_UNAT_FOOTER, NULL);
|
|
|
|
//
|
|
// Cleanup.
|
|
//
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LogSimpleMessage
|
|
//
|
|
// Writes a simple message to the log file.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_LogSimpleMessage(
|
|
UINT LogId,
|
|
LPCTSTR pAppend)
|
|
{
|
|
TCHAR szLogBuffer[4 * MAX_PATH];
|
|
int cchLogBuffer = ARRAYSIZE(szLogBuffer);
|
|
|
|
LoadString(hInstance, LogId, szLogBuffer, cchLogBuffer - 1);
|
|
if (pAppend)
|
|
{
|
|
// _tcscat(szLogBuffer, pAppend);
|
|
if(FAILED(StringCchCatW(szLogBuffer, cchLogBuffer, pAppend)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
}
|
|
Intl_LogMessage(szLogBuffer);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_LogFormatMessage
|
|
//
|
|
// Writes an error message using FormatMessage to the log file.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_LogFormatMessage(
|
|
UINT LogId)
|
|
{
|
|
LPVOID lpMsgBuf = NULL;
|
|
TCHAR szLogBuffer[4 * MAX_PATH];
|
|
int cchLogBuffer = ARRAYSIZE(szLogBuffer);
|
|
|
|
//
|
|
// Load the log message.
|
|
//
|
|
LoadString( hInstance,
|
|
LogId,
|
|
szLogBuffer,
|
|
cchLogBuffer - 1 );
|
|
|
|
//
|
|
// Get the message for the last error.
|
|
//
|
|
if(0 < FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPTSTR) &lpMsgBuf,
|
|
0,
|
|
NULL ))
|
|
{
|
|
//
|
|
// Concatenate the log message and the last error.
|
|
//
|
|
// _tcscat(szLogBuffer, lpMsgBuf);
|
|
if(FAILED(StringCchCatW(szLogBuffer, ARRAYSIZE(szLogBuffer), lpMsgBuf)))
|
|
{
|
|
// This should be impossible, but we need to avoid PREfast complaints.
|
|
}
|
|
|
|
//
|
|
// Free the buffer created by FormatMessage.
|
|
//
|
|
LocalFree(lpMsgBuf);
|
|
}
|
|
else
|
|
{
|
|
// CONSIDER: FormatMessage failed (probably a LocalAlloc failure).
|
|
// Maybe we should append the error code, at least?
|
|
}
|
|
|
|
//
|
|
// Log the message to the log file.
|
|
//
|
|
Intl_LogMessage(szLogBuffer);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_SaveDefaultUserSettings
|
|
//
|
|
// This function will get information from the the current user and write it in
|
|
// the .DEFAULT and NTUSER.DAT file.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_SaveDefaultUserSettings()
|
|
{
|
|
//
|
|
// Check if the Default user settings have been saved already.
|
|
//
|
|
if (g_bSettingsChanged)
|
|
{
|
|
DWORD dwDisposition;
|
|
HKEY hDesKey, hSrcKey;
|
|
|
|
//
|
|
// Set the UI Language for ALL new users of this machine.
|
|
//
|
|
Intl_ChangeUILangForAllUsers(Intl_GetPendingUILanguage());
|
|
|
|
//
|
|
// Copy the International keys and subkeys.
|
|
//
|
|
Intl_SaveValuesToDefault(c_szCPanelIntl, c_szCPanelIntl_DefUser);
|
|
Intl_SaveValuesToNtUserFile(HKEY_CURRENT_USER, c_szCPanelIntl, c_szCPanelIntl);
|
|
|
|
//
|
|
// Copy only the CTFMON information.
|
|
//
|
|
if(RegOpenKeyEx( HKEY_CURRENT_USER,
|
|
c_szCtfmon,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hSrcKey) == ERROR_SUCCESS)
|
|
{
|
|
if(RegOpenKeyEx( HKEY_USERS,
|
|
c_szCtfmon_DefUser,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hDesKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwValueLength, dwType;
|
|
TCHAR szValue[REGSTR_MAX_VALUE_LENGTH];
|
|
|
|
//
|
|
// Get the source value if exist.
|
|
//
|
|
szValue[0] = 0;
|
|
dwValueLength = sizeof(szValue);
|
|
if(RegQueryValueEx( hSrcKey,
|
|
szCtfmonValue,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szValue,
|
|
&dwValueLength) == ERROR_SUCCESS)
|
|
{
|
|
|
|
//
|
|
// Set the destination value.
|
|
//
|
|
RegSetValueEx( hDesKey,
|
|
szCtfmonValue,
|
|
0L,
|
|
dwType,
|
|
(CONST BYTE *)szValue,
|
|
dwValueLength);
|
|
}
|
|
CloseHandle(hDesKey);
|
|
}
|
|
|
|
CloseHandle(hSrcKey);
|
|
}
|
|
Intl_SaveValuesToNtUserFile(HKEY_CURRENT_USER, c_szCtfmon, c_szCtfmon);
|
|
|
|
//
|
|
// Copy the Keyboard Layouts keys and subkeys.
|
|
//
|
|
Intl_SaveValuesToDefault(c_szKbdLayouts, c_szKbdLayouts_DefUser);
|
|
Intl_SaveValuesToNtUserFile(HKEY_CURRENT_USER, c_szKbdLayouts, c_szKbdLayouts);
|
|
|
|
//
|
|
// Copy the Input Method keys and subkeys.
|
|
//
|
|
Intl_SaveValuesToDefault(c_szInputMethod, c_szInputMethod_DefUser);
|
|
Intl_SaveValuesToNtUserFile(HKEY_CURRENT_USER, c_szInputMethod, c_szInputMethod);
|
|
|
|
//
|
|
// Copy the Tips keys and subkeys. Make sure that the CTF
|
|
// destination key exist.
|
|
//
|
|
if (RegCreateKeyEx( HKEY_USERS,
|
|
c_szInputTips_DefUser,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hDesKey,
|
|
&dwDisposition ) == ERROR_SUCCESS)
|
|
{
|
|
CloseHandle(hDesKey);
|
|
Intl_SaveValuesToDefault(c_szInputTips, c_szInputTips_DefUser);
|
|
Intl_SaveValuesToNtUserFile(HKEY_CURRENT_USER, c_szInputTips, c_szInputTips);
|
|
}
|
|
|
|
//
|
|
// Settings saved.
|
|
//
|
|
g_bSettingsChanged = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_SaveDefaultUserInputSettings
|
|
//
|
|
// This function copy .Default user input-related setting to ntuser.dat.
|
|
// There are four things to copy to make keyboard layout work for
|
|
// new users:
|
|
// * "Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ctfmon.exe" (if any)
|
|
// * "Keyboard Layout"
|
|
// * "Control Panel\\Input Method"
|
|
// * "Software\\Microsoft\\CTF" (if any)
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL Intl_SaveDefaultUserInputSettings()
|
|
{
|
|
HKEY hDesKey;
|
|
DWORD dwDisposition;
|
|
|
|
//
|
|
// The following call will copy everything under Windows\CurrentVersion\Run
|
|
// to ntuser.dat.
|
|
//
|
|
Intl_SaveValuesToNtUserFile(HKEY_USERS, c_szCtfmon_DefUser, c_szCtfmon);
|
|
|
|
//
|
|
// Copy the Keyboard Layouts keys and subkeys.
|
|
//
|
|
Intl_SaveValuesToNtUserFile(HKEY_USERS, c_szKbdLayouts_DefUser, c_szKbdLayouts);
|
|
|
|
//
|
|
// Copy the Input Method keys and subkeys.
|
|
//
|
|
Intl_SaveValuesToNtUserFile(HKEY_USERS, c_szInputMethod_DefUser, c_szInputMethod);
|
|
|
|
//
|
|
// Copy the Tips keys and subkeys. Make sure that the CTF
|
|
// destination key exist.
|
|
//
|
|
if (RegCreateKeyEx( HKEY_USERS,
|
|
c_szInputTips_DefUser,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hDesKey,
|
|
&dwDisposition ) == ERROR_SUCCESS)
|
|
{
|
|
CloseHandle(hDesKey);
|
|
Intl_SaveValuesToNtUserFile(HKEY_USERS, c_szInputTips_DefUser, c_szInputTips);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_IsMUIFileVersionSameAsOS
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MUISETUP_EXE_RELATIVE_PATH TEXT("mui\\muisetup.exe")
|
|
#define MUISETUP_INF_RELATIVE_PATH TEXT("mui\\mui.inf")
|
|
|
|
BOOL Intl_IsMUISetupVersionSameAsOS()
|
|
{
|
|
BOOL bSpUpgrade = FALSE;
|
|
DWORD dwDummy = 0;
|
|
DWORD dwBufSize = 0;
|
|
UINT uiLen = 0;
|
|
BYTE *pbBuffer = NULL;
|
|
VS_FIXEDFILEINFO *pvsFileInfo;
|
|
BOOL bResult = TRUE;
|
|
TCHAR tempmsg[MAX_PATH];
|
|
TCHAR build[MAX_PATH];
|
|
TCHAR szAppPath[MAX_PATH];
|
|
TCHAR szInfPath[MAX_PATH];
|
|
HRESULT hr = S_OK;
|
|
|
|
GetSystemWindowsDirectory(szAppPath, ARRAYSIZE(szAppPath));
|
|
GetSystemWindowsDirectory(szInfPath, ARRAYSIZE(szInfPath));
|
|
|
|
//
|
|
// Invoke muisetup to uninstall MUI languages.
|
|
//
|
|
if ((PathAppend(szAppPath, MUISETUP_EXE_RELATIVE_PATH) && Intl_FileExists(szAppPath)) &&
|
|
(PathAppend(szInfPath, MUISETUP_INF_RELATIVE_PATH) && Intl_FileExists(szInfPath)))
|
|
{
|
|
dwBufSize = GetFileVersionInfoSize(szAppPath, &dwDummy);
|
|
if (dwBufSize > 0)
|
|
{
|
|
// allocate enough buffer to store the file version info
|
|
pbBuffer = (BYTE*) LocalAlloc(LPTR, dwBufSize+1);
|
|
if (NULL == pbBuffer)
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
// Get the file version info
|
|
if (!GetFileVersionInfo(szAppPath, dwDummy, dwBufSize, pbBuffer))
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
// get the version from the file version info using VerQueryValue
|
|
if (!VerQueryValue(pbBuffer, TEXT("\\"), (LPVOID *) &pvsFileInfo, &uiLen))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// read the mui.inf version from mui.inf
|
|
GetPrivateProfileString( TEXT("Buildnumber"),
|
|
NULL,
|
|
TEXT("0"),
|
|
tempmsg,
|
|
ARRAYSIZE(tempmsg),
|
|
szInfPath);
|
|
|
|
//wsprintf(build, TEXT("%d"), HIWORD(pvsFileInfo->dwFileVersionLS));
|
|
hr = StringCchPrintf(build, ARRAYSIZE(build), TEXT("%d"), HIWORD(pvsFileInfo->dwFileVersionLS));
|
|
|
|
if (_tcscmp(tempmsg, build))
|
|
{
|
|
bSpUpgrade = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bSpUpgrade = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
Exit:
|
|
if (pbBuffer)
|
|
{
|
|
LocalFree(pbBuffer);
|
|
}
|
|
return bSpUpgrade;
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_IsLIP
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL Intl_IsLIP()
|
|
{
|
|
BOOL bResult = TRUE;
|
|
UINT iLangCount = 0;
|
|
HKEY hKey;
|
|
TCHAR szValue[MAX_PATH];
|
|
TCHAR szData[MAX_PATH];
|
|
DWORD dwIndex, cchValue, cbData;
|
|
DWORD UILang;
|
|
DWORD dwType;
|
|
LONG rc;
|
|
|
|
//
|
|
// First check for the LIP System Key, if it is there, then we are done
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szLIPInstalled,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// if not found, then open the registry key used MUI to doublecheck
|
|
// for LIP enabled system
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
c_szMUILanguages,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) != ERROR_SUCCESS)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Enumerate the values in the MUILanguages key.
|
|
//
|
|
dwIndex = 0;
|
|
cchValue = sizeof(szValue) / sizeof(TCHAR);
|
|
szValue[0] = TEXT('\0');
|
|
cbData = sizeof(szData);
|
|
szData[0] = TEXT('\0');
|
|
rc = RegEnumValue( hKey,
|
|
dwIndex,
|
|
szValue,
|
|
&cchValue,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szData,
|
|
&cbData );
|
|
|
|
while (rc == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If the UI language contains data, then it is installed.
|
|
//
|
|
if ((szData[0] != 0) &&
|
|
(dwType == REG_SZ) &&
|
|
(UILang = TransNum(szValue)) &&
|
|
(GetLocaleInfo(UILang, LOCALE_SNATIVELANGNAME, szData, MAX_PATH)) &&
|
|
(IsValidUILanguage((LANGID)UILang)))
|
|
{
|
|
//
|
|
// if English 0409 key is found, we have a MUI system and not LIP
|
|
//
|
|
if (UILang == 0x0409)
|
|
{
|
|
bResult = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If there are more than one language installed, or then it is
|
|
// also not a LIP system - this can be 0409 + any other language also.
|
|
//
|
|
iLangCount= iLangCount + 1;
|
|
if (iLangCount > 1)
|
|
{
|
|
bResult = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the next enum value.
|
|
//
|
|
dwIndex++;
|
|
cchValue = sizeof(szValue) / sizeof(TCHAR);
|
|
szValue[0] = TEXT('\0');
|
|
cbData = sizeof(szData);
|
|
szData[0] = TEXT('\0');
|
|
rc = RegEnumValue( hKey,
|
|
dwIndex,
|
|
szValue,
|
|
&cchValue,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szData,
|
|
&cbData );
|
|
}
|
|
//
|
|
// Clean up.
|
|
//
|
|
RegCloseKey(hKey);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_RemoveMUIFile
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
void Intl_RemoveMUIFile()
|
|
{
|
|
TCHAR szAppPath[MAX_PATH];
|
|
|
|
if(0 == GetSystemWindowsDirectory(szAppPath, ARRAYSIZE(szAppPath)))
|
|
{
|
|
// SECURITY: Make sure we null out szAppPath
|
|
szAppPath[0] = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// Invoke muisetup to uninstall MUI languages.
|
|
//
|
|
if (PathAppend(szAppPath, MUISETUP_EXE_RELATIVE_PATH) &&
|
|
Intl_FileExists(szAppPath))
|
|
{
|
|
//
|
|
// Only remove MUI if we are not in an SP OS upgrade scenario and if the system is not LIP
|
|
//
|
|
if (!Intl_IsMUISetupVersionSameAsOS() && !Intl_IsLIP())
|
|
{
|
|
SHELLEXECUTEINFO ExecInfo = {0};
|
|
SHFILEOPSTRUCT shFile =
|
|
{
|
|
NULL, FO_DELETE, szAppPath, NULL, FOF_NOCONFIRMATION|FOF_SILENT|FOF_NOERRORUI, 0, 0, 0
|
|
};
|
|
|
|
ExecInfo.lpParameters = TEXT("/u /r /s /o /t");
|
|
ExecInfo.fMask = SEE_MASK_FLAG_NO_UI;
|
|
ExecInfo.lpFile = szAppPath;
|
|
ExecInfo.nShow = SW_SHOWNORMAL;
|
|
ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
|
|
ShellExecuteEx(&ExecInfo);
|
|
|
|
//
|
|
// An additional NULL character must be appended for this
|
|
// multi-string buffer.
|
|
//
|
|
szAppPath[lstrlen(szAppPath) + 1] = 0x00;
|
|
|
|
SHFileOperation(&shFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_CallTextServices
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Intl_CallTextServices()
|
|
{
|
|
TCHAR szAppPath[MAX_PATH];
|
|
|
|
if(0 == GetSystemDirectory(szAppPath, ARRAYSIZE(szAppPath)))
|
|
{
|
|
// SECURITY: Make sure we null out szAppPath
|
|
szAppPath[0] = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// Invoke the Input applet.
|
|
//
|
|
if (PathAppend(szAppPath, TEXT("rundll32.exe")) &&
|
|
Intl_FileExists(szAppPath))
|
|
{
|
|
SHELLEXECUTEINFO ExecInfo = {0};
|
|
|
|
ExecInfo.lpParameters = TEXT("shell32.dll,Control_RunDLL input.dll");
|
|
ExecInfo.lpFile = szAppPath;
|
|
ExecInfo.nShow = SW_SHOWNORMAL;
|
|
ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
|
|
ShellExecuteEx(&ExecInfo);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetPendingUILanguage
|
|
//
|
|
// Look into the registry for the pending UI Language. This function is
|
|
// used for the default user case.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID Intl_GetPendingUILanguage()
|
|
{
|
|
HKEY hKey;
|
|
LANGID dwDefaultUILanguage = 0;
|
|
DWORD cbData = 0;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
|
|
//
|
|
// Open the registry key used by setup.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_CURRENT_USER,
|
|
c_szCPanelDesktop,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) != ERROR_SUCCESS)
|
|
{
|
|
return (GetUserDefaultUILanguage());
|
|
}
|
|
|
|
//
|
|
// Query the pending MUI Language.
|
|
//
|
|
cbData = ARRAYSIZE(szBuffer);
|
|
if (RegQueryValueEx( hKey,
|
|
szMUILangPending,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szBuffer,
|
|
&cbData ) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return (GetUserDefaultUILanguage());
|
|
}
|
|
else
|
|
{
|
|
if ((dwDefaultUILanguage = (LANGID)TransNum(szBuffer)) == 0)
|
|
{
|
|
RegCloseKey(hKey);
|
|
return (GetUserDefaultUILanguage());
|
|
}
|
|
else
|
|
{
|
|
RegCloseKey(hKey);
|
|
return ((LANGID)dwDefaultUILanguage);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_GetDotDefaultUILanguage
|
|
//
|
|
// Retrieve the UI language stored in the HKCU\.Default.
|
|
// This is the default UI language for new users.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LANGID Intl_GetDotDefaultUILanguage()
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwKeyType;
|
|
DWORD dwSize;
|
|
BOOL success = FALSE;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
LANGID langID;
|
|
|
|
//
|
|
// Get the value in .DEFAULT.
|
|
//
|
|
if (RegOpenKeyEx( HKEY_USERS,
|
|
c_szCPanelDesktop_DefUser,
|
|
0L,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(szBuffer);
|
|
if (RegQueryValueEx( hKey,
|
|
c_szMUIValue,
|
|
0L,
|
|
&dwKeyType,
|
|
(LPBYTE)szBuffer,
|
|
&dwSize) == ERROR_SUCCESS)
|
|
{
|
|
if (dwKeyType == REG_SZ)
|
|
{
|
|
langID = (LANGID)_tcstol(szBuffer, NULL, 16);
|
|
success = TRUE;
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
//
|
|
// key exists, we need to check if the key is valid or not
|
|
//
|
|
if (success)
|
|
{
|
|
success = IsValidUILanguage(langID);
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
return (GetSystemDefaultUILanguage());
|
|
}
|
|
return (langID);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetControlReadingOrder
|
|
//
|
|
// Set the specified control to be left-to-right or right-to-left reading order.
|
|
//
|
|
// bUseRightToLeft==FALSE: Use left-to-right reading order
|
|
// bUseRightToLeft==TRUE: Use right-to-left reading order
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SetControlReadingOrder(BOOL bUseRightToLeft, HWND hwnd)
|
|
{
|
|
BOOL bCurrentRTL;
|
|
if (IsRtLLocale(GetUserDefaultUILanguage()))
|
|
{
|
|
// If the current UI langauge is RTL, the dailog is already localized as RTL.
|
|
// In this case, don't change the direction of the control.
|
|
return;
|
|
}
|
|
bCurrentRTL = (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & (WS_EX_RTLREADING)) != 0;
|
|
|
|
if (bCurrentRTL != bUseRightToLeft)
|
|
{
|
|
// Reverse the WS_EX_RTLREADING and WS_EX_RIGHT bit.
|
|
SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) ^ (WS_EX_RTLREADING | WS_EX_RIGHT));
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intl_MyQueueCallback
|
|
//
|
|
// During unattended mode, we don't necessarly want the Files Location
|
|
// dialog from setup to be displayed. If the flag D is passed as parameter
|
|
// then we don't show the dialog and abort the installation.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
UINT WINAPI Intl_MyQueueCallback(PVOID pQueueContext,
|
|
UINT Notification,
|
|
UINT_PTR Param1,
|
|
UINT_PTR Param2)
|
|
{
|
|
if ((g_bDisableSetupDialog) &&
|
|
(g_bUnttendMode) &&
|
|
(SPFILENOTIFY_NEEDMEDIA == Notification))
|
|
{
|
|
// Abort if the installation is about to show a dialog to
|
|
// locate the source file location.
|
|
return FILEOP_ABORT;
|
|
}
|
|
else
|
|
{
|
|
// Pass all other notifications through without modification
|
|
return SetupDefaultQueueCallback(pQueueContext,
|
|
Notification,
|
|
Param1,
|
|
Param2);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Enable/restore to previous state
|
|
// a named priviledge of token of current process
|
|
//
|
|
// Input: pszPrivilegeName = Named Privilege
|
|
// bEnabled = enable/disable the Named Privilege
|
|
// Output *lpWasEnabled = last state of the Named Privilege (enabled/disabled)
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD Intl_SetPrivilegeAccessToken(WCHAR * pszPrivilegeName, BOOLEAN bEnabled, BOOLEAN *lpWasEnabled)
|
|
{
|
|
|
|
HANDLE hProcess;
|
|
HANDLE hAccessToken=NULL;
|
|
LUID luidPrivilegeLUID;
|
|
TOKEN_PRIVILEGES tpTokenPrivilege,tpTokenPrivilegeOld;
|
|
DWORD dwOld, dwErr, dwReturn=ERROR_INTERNAL_ERROR;
|
|
//
|
|
// Get handle of Current Prcoess
|
|
//
|
|
hProcess = GetCurrentProcess();
|
|
if (!hProcess)
|
|
{
|
|
goto done;
|
|
}
|
|
//
|
|
// Get handle of process token
|
|
//
|
|
if (!OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hAccessToken))
|
|
{
|
|
goto done;
|
|
}
|
|
//
|
|
// Get ID of named Privilege
|
|
//
|
|
if (!LookupPrivilegeValue(NULL,pszPrivilegeName,&luidPrivilegeLUID))
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
tpTokenPrivilege.PrivilegeCount = 1;
|
|
tpTokenPrivilege.Privileges[0].Luid = luidPrivilegeLUID;
|
|
if (bEnabled)
|
|
{
|
|
tpTokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
}
|
|
else
|
|
{
|
|
tpTokenPrivilege.Privileges[0].Attributes = 0;
|
|
}
|
|
//
|
|
// Enable the named Privilege
|
|
//
|
|
if (!AdjustTokenPrivileges(hAccessToken,
|
|
FALSE,
|
|
&tpTokenPrivilege,
|
|
sizeof(TOKEN_PRIVILEGES),
|
|
&tpTokenPrivilegeOld,
|
|
&dwOld))
|
|
{
|
|
goto done;
|
|
}
|
|
dwReturn = ERROR_SUCCESS;
|
|
//
|
|
// Get previous state (enabled/disabled)
|
|
//
|
|
if (lpWasEnabled)
|
|
{
|
|
if (tpTokenPrivilegeOld.Privileges[0].Attributes == SE_PRIVILEGE_ENABLED)
|
|
{
|
|
*lpWasEnabled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*lpWasEnabled = FALSE;
|
|
}
|
|
|
|
}
|
|
done:
|
|
if (dwReturn != ERROR_SUCCESS)
|
|
{
|
|
if ( (dwReturn=GetLastError()) == ERROR_SUCCESS)
|
|
{
|
|
dwReturn = ERROR_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
if (hAccessToken)
|
|
{
|
|
CloseHandle(hAccessToken);
|
|
}
|
|
return dwReturn;
|
|
}
|