/**************************************************************************\ * Module Name: layout.c (corresponds to Win95 ime.c) * * Copyright (c) 1985 - 1999, Microsoft Corporation * * IME Keyboard Layout related functionality * * History: * 03-Jan-1996 wkwok Created \**************************************************************************/ #include "precomp.h" #pragma hdrstop /* * Local Defines. */ #define szLZOpenFileW "LZOpenFileW" #define szLZCopy "LZCopy" #define szLZClose "LZClose" typedef HFILE (WINAPI *LPFNLZOPENFILEW)(LPTSTR, LPOFSTRUCT, WORD); typedef LONG (WINAPI *LPFNLZCOPY)(INT, INT); typedef VOID (WINAPI *LPFNLZCLOSE)(INT); /* * Local Routines. */ UINT StrToUInt(LPWSTR); VOID UIntToStr(UINT, ULONG, LPWSTR, USHORT); BOOL CopyImeFile(LPWSTR, LPCWSTR); INT GetImeLayout(PIMELAYOUT, INT); BOOL WriteImeLayout(HKL, LPCWSTR, LPCWSTR); HKL AssignNewLayout(INT, PIMELAYOUT, HKL); /***************************************************************************\ * ImmGetIMEFileNameW * * Gets the description of the IME with the specified HKL. * * History: * 28-Feb-1995 wkwok Created \***************************************************************************/ UINT WINAPI ImmGetDescriptionW( HKL hKL, LPWSTR lpwszDescription, UINT uBufLen) { IMEINFOEX iiex; UINT uRet; if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return 0; #if defined(CUAS_ENABLE) if (!IS_IME_KBDLAYOUT(hKL)) return 0; #endif uRet = wcslen(iiex.wszImeDescription); /* * ask buffer length */ if (uBufLen == 0) return uRet; if (uBufLen > uRet) { wcscpy(lpwszDescription, iiex.wszImeDescription); } else { uRet = uBufLen - 1; wcsncpy(lpwszDescription, iiex.wszImeDescription, uRet); lpwszDescription[uRet] = L'\0'; } return uRet; } /***************************************************************************\ * ImmGetIMEFileNameA * * Gets the description of the IME with the specified HKL. * * History: * 28-Feb-1995 wkwok Created \***************************************************************************/ UINT WINAPI ImmGetDescriptionA( HKL hKL, LPSTR lpszDescription, UINT uBufLen) { IMEINFOEX iiex; INT i; BOOL bUDC; if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return 0; #if defined(CUAS_ENABLE) if (!IS_IME_KBDLAYOUT(hKL)) return 0; #endif i = WideCharToMultiByte(CP_ACP, (DWORD)0, (LPWSTR)iiex.wszImeDescription, // src wcslen(iiex.wszImeDescription), lpszDescription, // dest uBufLen, (LPSTR)NULL, (LPBOOL)&bUDC); if (uBufLen != 0) lpszDescription[i] = '\0'; return (UINT)i; } /***************************************************************************\ * ImmGetIMEFileNameW * * Gets the file name of the IME with the specified HKL. * * History: * 28-Feb-1995 wkwok Created \***************************************************************************/ UINT WINAPI ImmGetIMEFileNameW( HKL hKL, LPWSTR lpwszFile, UINT uBufLen) { IMEINFOEX iiex; UINT uRet; if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return 0; #if defined(CUAS_ENABLE) if (!IS_IME_KBDLAYOUT(hKL)) { // // #602631 // // Ichitaro12 ATOKLIB.DLL does not check the return value of // ImmGetIMEFileName() // if (uBufLen) *lpwszFile = L'\0'; return 0; } #endif uRet = wcslen(iiex.wszImeFile); /* * ask buffer length */ if (uBufLen == 0) return uRet; if (uBufLen > uRet) { wcscpy(lpwszFile, iiex.wszImeFile); } else { uRet = uBufLen - 1; wcsncpy(lpwszFile, iiex.wszImeFile, uRet); lpwszFile[uRet] = L'\0'; } return uRet; } /***************************************************************************\ * ImmGetIMEFileNameA * * Gets the file name of the IME with the specified HKL. * * History: * 28-Feb-1995 wkwok Created \***************************************************************************/ UINT WINAPI ImmGetIMEFileNameA( HKL hKL, LPSTR lpszFile, UINT uBufLen) { IMEINFOEX iiex; INT i; BOOL bUDC; if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return 0; #if defined(CUAS_ENABLE) if (!IS_IME_KBDLAYOUT(hKL)) { // // #602631 // // Ichitaro12 ATOKLIB.DLL does not check the return value of // ImmGetIMEFileName() // if (uBufLen) *lpszFile = '\0'; return 0; } #endif i = WideCharToMultiByte(CP_ACP, (DWORD)0, (LPWSTR)iiex.wszImeFile, // src wcslen(iiex.wszImeFile), lpszFile, // dest uBufLen, (LPSTR)NULL, (LPBOOL)&bUDC); if (uBufLen != 0) lpszFile[i] = '\0'; return i; } /***************************************************************************\ * ImmGetProperty * * Gets the property and capability of the IME with the specified HKL. * * History: * 28-Feb-1995 wkwok Created \***************************************************************************/ DWORD WINAPI ImmGetProperty( HKL hKL, DWORD dwIndex) { IMEINFOEX iiex; PIMEDPI pImeDpi = NULL; PIMEINFO pImeInfo; DWORD dwRet; if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return 0; if (dwIndex == IGP_GETIMEVERSION) return iiex.dwImeWinVersion; if (iiex.fLoadFlag != IMEF_LOADED) { pImeDpi = FindOrLoadImeDpi(hKL); if (pImeDpi == NULL) { RIPMSG0(RIP_WARNING, "ImmGetProperty: load IME failure."); return 0; } pImeInfo = &pImeDpi->ImeInfo; } else { pImeInfo = &iiex.ImeInfo; } switch (dwIndex) { case IGP_PROPERTY: dwRet = pImeInfo->fdwProperty; break; case IGP_CONVERSION: dwRet = pImeInfo->fdwConversionCaps; break; case IGP_SENTENCE: dwRet = pImeInfo->fdwSentenceCaps; break; case IGP_UI: dwRet = pImeInfo->fdwUICaps; break; case IGP_SETCOMPSTR: dwRet = pImeInfo->fdwSCSCaps; break; case IGP_SELECT: dwRet = pImeInfo->fdwSelectCaps; break; default: RIPMSG1(RIP_WARNING, "ImmGetProperty: wrong index %lx.", dwIndex); dwRet = 0; break; } ImmUnlockImeDpi(pImeDpi); return dwRet; } HKL WINAPI ImmInstallIMEW( LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText) { LPWSTR lpwszImeFileName; LPWSTR lpwszImeFilePart; LPWSTR lpwszImeCopiedPath; int i, nIMEs; PIMELAYOUT pImeLayout = NULL; HKL hImeKL, hLangKL; WCHAR szKeyName[HEX_ASCII_SIZE]; IMEINFOEX iiex; lpwszImeFileName = ImmLocalAlloc(0, (MAX_PATH+1) * sizeof(WCHAR)); if (lpwszImeFileName == NULL) return (HKL)0; lpwszImeCopiedPath = ImmLocalAlloc(0, (MAX_PATH+1) * sizeof(WCHAR)); if (lpwszImeCopiedPath == NULL) { ImmLocalFree(lpwszImeFileName); return (HKL)0; } /* * Get the file name only into lpwszImeFilePart */ GetFullPathNameW(lpszIMEFileName, MAX_PATH, lpwszImeFileName, &lpwszImeFilePart); CharUpper(lpwszImeFileName); if (lpwszImeFilePart == NULL) { ImmLocalFree(lpwszImeFileName); ImmLocalFree(lpwszImeCopiedPath); return (HKL)0; } hImeKL = hLangKL = iiex.hkl = (HKL)0; wcsncpy(iiex.wszImeFile, lpwszImeFilePart, IM_FILE_SIZE-1); iiex.wszImeFile[IM_FILE_SIZE - 1] = L'\0'; if (LoadVersionInfo(&iiex) && iiex.hkl != (HKL)0) { hLangKL = iiex.hkl; } else { ImmLocalFree(lpwszImeFileName); ImmLocalFree(lpwszImeCopiedPath); return (HKL)0; } nIMEs = GetImeLayout(NULL, 0); if (nIMEs != 0) { pImeLayout = (PIMELAYOUT)ImmLocalAlloc(0, nIMEs * sizeof(IMELAYOUT)); if (pImeLayout == NULL) { ImmLocalFree(lpwszImeFileName); ImmLocalFree(lpwszImeCopiedPath); return (HKL)0; } GetImeLayout(pImeLayout, nIMEs); for (i=0; i < nIMEs; i++) { if (_wcsicmp(pImeLayout[i].szImeName, lpwszImeFilePart) == 0) { /* * We got the same IME name, ISV wants to upgrade. */ if (LOWORD(HandleToUlong(hLangKL)) != LOWORD(HandleToUlong(pImeLayout[i].hImeKL))) { /* * IME name conflict, blow out! */ RIPMSG0(RIP_WARNING, "ImmInstallIME: different language!"); goto ImmInstallIMEWFailed; } hImeKL = pImeLayout[i].hImeKL; break; } } } if (ImmGetImeInfoEx(&iiex, ImeInfoExImeFileName, lpwszImeFilePart)) { /* * The specified IME has been activated. Unload it first. */ if (!UnloadKeyboardLayout(iiex.hkl)) { hImeKL = (HKL)0; goto ImmInstallIMEWFailed; } } /* * We will copy to system directory */ #if 0 i = (INT)GetSystemDirectory(lpwszImeCopiedPath, MAX_PATH); lpwszImeCopiedPath[i] = L'\0'; AddBackslash(lpwszImeCopiedPath); wcscat(lpwszImeCopiedPath, lpwszImeFilePart); #else GetSystemPathName(lpwszImeCopiedPath, lpwszImeFilePart, MAX_PATH); #endif CharUpper(lpwszImeCopiedPath); if (_wcsicmp(lpwszImeFileName, lpwszImeCopiedPath) != 0) { /* * path is different, need to copy into system directory */ if (!CopyImeFile(lpwszImeFileName, lpwszImeCopiedPath)) { hImeKL = (HKL)0; goto ImmInstallIMEWFailed; } } if (hImeKL == 0) { hImeKL = AssignNewLayout(nIMEs, pImeLayout, hLangKL); } if (hImeKL != 0) { /* * Write HKL under "keyboard layouts" */ if (WriteImeLayout(hImeKL, lpwszImeFilePart, lpszLayoutText)) { UIntToStr(HandleToUlong(hImeKL), 16, szKeyName, sizeof(szKeyName)); hImeKL = LoadKeyboardLayout(szKeyName, KLF_REPLACELANG); } else { hImeKL = (HKL)0; } } ImmInstallIMEWFailed: if (pImeLayout != NULL) ImmLocalFree(pImeLayout); ImmLocalFree(lpwszImeFileName); ImmLocalFree(lpwszImeCopiedPath); return (HKL)hImeKL; } HKL WINAPI ImmInstallIMEA( LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText) { HKL hKL; LPWSTR lpwszIMEFileName; LPWSTR lpwszLayoutText; DWORD cbIMEFileName; DWORD cbLayoutText; INT i; cbIMEFileName = strlen(lpszIMEFileName) + sizeof(CHAR); cbLayoutText = strlen(lpszLayoutText) + sizeof(CHAR); lpwszIMEFileName = ImmLocalAlloc(0, cbIMEFileName * sizeof(WCHAR)); if (lpwszIMEFileName == NULL) { RIPMSG0(RIP_WARNING, "ImmInstallIMEA: memory failure!"); return (HKL)0; } lpwszLayoutText = ImmLocalAlloc(0, cbLayoutText * sizeof(WCHAR)); if (lpwszLayoutText == NULL) { RIPMSG0(RIP_WARNING, "ImmInstallIMEA: memory failure!"); ImmLocalFree(lpwszIMEFileName); return (HKL)0; } i = MultiByteToWideChar(CP_ACP, (DWORD)MB_PRECOMPOSED, (LPSTR)lpszIMEFileName, // src (INT)strlen(lpszIMEFileName), (LPWSTR)lpwszIMEFileName, // dest (INT)cbIMEFileName); lpwszIMEFileName[i] = L'\0'; i = MultiByteToWideChar(CP_ACP, (DWORD)MB_PRECOMPOSED, (LPSTR)lpszLayoutText, // src (INT)strlen(lpszLayoutText), (LPWSTR)lpwszLayoutText, // dest (INT)cbLayoutText); lpwszLayoutText[i] = L'\0'; hKL = ImmInstallIMEW(lpwszIMEFileName, lpwszLayoutText); ImmLocalFree(lpwszLayoutText); ImmLocalFree(lpwszIMEFileName); return hKL; } /***************************************************************************\ * ImmIsIME * * Checks whether the specified hKL is a HKL of an IME or not. * * History: * 28-Feb-1995 wkwok Created \***************************************************************************/ BOOL WINAPI ImmIsIME( HKL hKL) { IMEINFOEX iiex; #if !defined(CUAS_ENABLE) if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return FALSE; #else if (!ImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayoutWithCUAS, &hKL)) return FALSE; #endif return TRUE; } UINT StrToUInt( LPWSTR lpsz) { UNICODE_STRING Value; UINT ReturnValue; Value.Length = wcslen(lpsz) * sizeof(WCHAR); Value.Buffer = lpsz; /* * Convert string to int. */ RtlUnicodeStringToInteger(&Value, 16, &ReturnValue); return(ReturnValue); } VOID UIntToStr( UINT Value, ULONG Base, LPWSTR lpsz, USHORT dwBufLen) { UNICODE_STRING String; String.Length = dwBufLen; String.MaximumLength = dwBufLen; String.Buffer = lpsz; /* * Convert int to string. */ RtlIntegerToUnicodeString(Value, Base, &String); } BOOL CopyImeFile( LPWSTR lpwszImeFileName, LPCWSTR lpwszImeCopiedPath) { HMODULE hLzExpandDll; BOOL fUnloadExpandDll; LPFNLZOPENFILEW lpfnLZOpenFileW; LPFNLZCOPY lpfnLZCopy; LPFNLZCLOSE lpfnLZClose; OFSTRUCT ofStruc; HFILE hfSource, hfDest; LPSTR lpszImeCopiedPath; INT i, cbBuffer; BOOL fRet = FALSE; hLzExpandDll = GetModuleHandle(L"LZ32"); if (hLzExpandDll) { fUnloadExpandDll = FALSE; } else { WCHAR szLzExpand[MAX_PATH]; GetSystemPathName(szLzExpand, L"LZ32", MAX_PATH); hLzExpandDll = LoadLibrary(szLzExpand); if (!hLzExpandDll) { return FALSE; } fUnloadExpandDll = TRUE; } #define GET_PROC(x) \ if (!(lpfn##x = (PVOID) GetProcAddress(hLzExpandDll, sz##x))) { \ goto CopyImeFileFailed; } GET_PROC(LZOpenFileW); GET_PROC(LZCopy); GET_PROC(LZClose); #undef GET_PROC cbBuffer = (wcslen(lpwszImeCopiedPath) + 1) * sizeof(WCHAR); if ((lpszImeCopiedPath = ImmLocalAlloc(0, cbBuffer)) == NULL) goto CopyImeFileFailed; i = WideCharToMultiByte(CP_ACP, (DWORD)0, lpwszImeCopiedPath, // src wcslen(lpwszImeCopiedPath), lpszImeCopiedPath, // dest cbBuffer, (LPSTR)NULL, (LPBOOL)NULL); if (i == 0) { ImmLocalFree(lpszImeCopiedPath); goto CopyImeFileFailed; } lpszImeCopiedPath[i] = '\0'; hfSource = (*lpfnLZOpenFileW)(lpwszImeFileName, &ofStruc, OF_READ); if (hfSource < 0) { ImmLocalFree(lpszImeCopiedPath); goto CopyImeFileFailed; } hfDest = OpenFile(lpszImeCopiedPath, &ofStruc, OF_CREATE); if (hfDest != HFILE_ERROR) { if ((*lpfnLZCopy)(hfSource, hfDest) >= 0) { fRet = TRUE; } _lclose(hfDest); } (*lpfnLZClose)(hfSource); ImmLocalFree(lpszImeCopiedPath); CopyImeFileFailed: if (fUnloadExpandDll) FreeLibrary(hLzExpandDll); return fRet; } INT GetImeLayout( PIMELAYOUT pImeLayout, INT cEntery) { int i, nIMEs; HKEY hKeyKbdLayout; HKEY hKeyOneIME; WCHAR szKeyName[HEX_ASCII_SIZE]; WCHAR szImeFileName[IM_FILE_SIZE]; CONST DWORD dwKeyNameSize = ARRAY_SIZE(szKeyName); DWORD dwTmp; RegOpenKey(HKEY_LOCAL_MACHINE, gszRegKbdLayout, &hKeyKbdLayout); for (i = 0, nIMEs = 0; RegEnumKey(hKeyKbdLayout, i, szKeyName, dwKeyNameSize) == ERROR_SUCCESS; i++) { if (szKeyName[0] != L'E' && szKeyName[0] != L'e') continue; // this is not an IME based keyboard layout. if (pImeLayout != NULL) { if (nIMEs >= cEntery) break; RegOpenKey(hKeyKbdLayout, szKeyName, &hKeyOneIME); dwTmp = IM_FILE_SIZE; RegQueryValueEx(hKeyOneIME, gszValImeFile, NULL, NULL, (LPBYTE)szImeFileName, &dwTmp); // avoid length problem szImeFileName[IM_FILE_SIZE - 1] = L'\0'; RegCloseKey(hKeyOneIME); CharUpper(szImeFileName); pImeLayout[nIMEs].hImeKL = (HKL)IntToPtr( StrToUInt(szKeyName) ); wcscpy(pImeLayout[nIMEs].szKeyName, szKeyName); wcscpy(pImeLayout[nIMEs].szImeName, szImeFileName); } nIMEs++; } RegCloseKey(hKeyKbdLayout); return nIMEs; } BOOL WriteImeLayout( HKL hImeKL, LPCWSTR lpwszImeFilePart, LPCWSTR lpszLayoutText) { int i; HKEY hKeyKbdLayout; HKEY hKeyOneIME; HKEY hKeyKbdOrder; WCHAR szKeyName[HEX_ASCII_SIZE]; WCHAR szImeFileName[IM_FILE_SIZE]; WCHAR szOrderNum[HEX_ASCII_SIZE]; WCHAR szOrderKeyName[HEX_ASCII_SIZE]; DWORD dwTmp; if (RegOpenKey(HKEY_LOCAL_MACHINE, gszRegKbdLayout, &hKeyKbdLayout) != ERROR_SUCCESS) { RIPMSG0(RIP_WARNING, "WriteImeLayout: RegOpenKey() failed!"); return FALSE; } UIntToStr(HandleToUlong(hImeKL), 16, szKeyName, sizeof(szKeyName)); if (RegCreateKey(hKeyKbdLayout, szKeyName, &hKeyOneIME) != ERROR_SUCCESS) { RIPMSG0(RIP_WARNING, "WriteImeLayout: RegCreateKey() failed!"); RegCloseKey(hKeyKbdLayout); return FALSE; } if (RegSetValueExW(hKeyOneIME, gszValImeFile, 0, REG_SZ, (CONST BYTE*)lpwszImeFilePart, (wcslen(lpwszImeFilePart) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS) { goto WriteImeLayoutFail; } if (RegSetValueExW(hKeyOneIME, gszValLayoutText, 0, REG_SZ, (CONST BYTE*)lpszLayoutText, (wcslen(lpszLayoutText) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS) { goto WriteImeLayoutFail; } switch (LANGIDFROMHKL(hImeKL)) { case LANG_JAPANESE: wcscpy(szImeFileName, L"kbdjpn.dll"); break; case LANG_KOREAN: wcscpy(szImeFileName, L"kbdkor.dll"); break; case LANG_CHINESE: default: wcscpy(szImeFileName, L"kbdus.dll"); break; } if (RegSetValueExW(hKeyOneIME, gszValLayoutFile, 0, REG_SZ, (CONST BYTE*)szImeFileName, (wcslen(szImeFileName) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS) { goto WriteImeLayoutFail; } RegCloseKey(hKeyOneIME); RegCloseKey(hKeyKbdLayout); /* * Update CurrentUser's preload keyboard layout setting */ RegCreateKey(HKEY_CURRENT_USER, gszRegKbdOrder, &hKeyKbdOrder); for (i = 1; i < 1024; i++) { UIntToStr(i, 10, szOrderNum, sizeof(szOrderNum)); dwTmp = sizeof(szOrderKeyName); if (RegQueryValueEx(hKeyKbdOrder, szOrderNum, NULL, NULL, (LPBYTE)szOrderKeyName, &dwTmp) != ERROR_SUCCESS) { break; } if (_wcsicmp(szKeyName, szOrderKeyName) == 0) { /* * We have the same value in the preload! * OK, ISV is developing their IMEs * so even it is in preload, but it can not be loaded */ break; } } if (i < 1024) { /* * Write a subkey under "preload" */ RegSetValueExW(hKeyKbdOrder, szOrderNum, 0, REG_SZ, (CONST BYTE*)szKeyName, (lstrlen(szKeyName) + 1) * sizeof(WCHAR)); RegCloseKey(hKeyKbdOrder); } else { RegCloseKey(hKeyKbdOrder); return FALSE; } return TRUE; WriteImeLayoutFail: RegCloseKey(hKeyOneIME); RegDeleteKey(hKeyKbdLayout, szKeyName); RegCloseKey(hKeyKbdLayout); return FALSE; } #define IMELANGID(hkl) \ LOWORD(HandleToUlong(hkl)) #define IMELAYOUTID(hkl) \ HIWORD(HandleToUlong(hkl)) HKL AssignNewLayout( INT nIMEs, PIMELAYOUT pImeLayout, HKL hLangKL) { DWORD dwNewId = 0; DWORD dwHighId = 0xE01F; DWORD dwLowId = 0xE0FF; INT i; /* * We prefer the value higher than E01F for ISVs, we will use * E001 ~ E01F in Microsoft .INF file */ /* * Find out the high and low one */ for (i = 0; i < nIMEs; ++i) { /* * Let's try to keep the previous behavior, not to * have the duplicated hiword in hkl. */ if (IMELAYOUTID(pImeLayout[i].hImeKL) > dwHighId) { dwHighId = IMELAYOUTID(pImeLayout[i].hImeKL); } if (IMELAYOUTID(pImeLayout[i].hImeKL) < dwLowId) { dwLowId = IMELAYOUTID(pImeLayout[i].hImeKL); } } if (dwHighId < 0xE0FF) { dwNewId = dwHighId + 1; } else if (dwLowId > 0xE001) { dwNewId = dwLowId - 1; } else { /* * Need to find out unused hKL using full search. * Find it one by one. */ DWORD dwId; for (dwId = 0xE020; dwId < 0xE100; ++dwId) { for (i = 0; i < nIMEs; ++i) { if (IMELAYOUTID(pImeLayout[i].hImeKL) == dwId && IMELANGID(pImeLayout[i].hImeKL) == IMELANGID(hLangKL)) { // conflicts with existing IME, try the next dwLowId break; } } if (i >= nIMEs) { break; } } if (dwId < 0xE100) { dwNewId = dwId; } } if (dwNewId == 0) { return NULL; } return (HKL)UIntToPtr(MAKELONG(IMELANGID(hLangKL), dwNewId)); }