mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 13:18:51 +00:00
1318 lines
36 KiB
C
1318 lines
36 KiB
C
/*
|
|
* PROJECT: ReactOS IMM32
|
|
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
|
|
* PURPOSE: Implementing IMM32 helper functions
|
|
* COPYRIGHT: Copyright 1998 Patrik Stridvall
|
|
* Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
|
|
* Copyright 2017 James Tabor <james.tabor@reactos.org>
|
|
* Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
|
|
* Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(imm);
|
|
|
|
HANDLE ghImmHeap = NULL; // Win: pImmHeap
|
|
|
|
/* Win: PtiCurrent */
|
|
PTHREADINFO FASTCALL Imm32CurrentPti(VOID)
|
|
{
|
|
if (NtCurrentTeb()->Win32ThreadInfo == NULL)
|
|
NtUserGetThreadState(THREADSTATE_GETTHREADINFO);
|
|
return NtCurrentTeb()->Win32ThreadInfo;
|
|
}
|
|
|
|
BOOL APIENTRY Imm32IsCrossThreadAccess(HIMC hIMC)
|
|
{
|
|
DWORD_PTR dwImeThreadId = NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
|
|
DWORD_PTR dwCurrentThreadId = GetCurrentThreadId();
|
|
return dwImeThreadId != dwCurrentThreadId;
|
|
}
|
|
|
|
// Win: TestWindowProcess
|
|
BOOL APIENTRY Imm32IsCrossProcessAccess(HWND hWnd)
|
|
{
|
|
DWORD_PTR WndPID = NtUserQueryWindow(hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID);
|
|
DWORD_PTR CurrentPID = (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueProcess;
|
|
return WndPID != CurrentPID;
|
|
}
|
|
|
|
// Win: StrToUInt
|
|
HRESULT APIENTRY
|
|
Imm32StrToUInt(LPCWSTR pszText, LPDWORD pdwValue, ULONG nBase)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
RtlInitUnicodeString(&UnicodeString, pszText);
|
|
Status = RtlUnicodeStringToInteger(&UnicodeString, nBase, pdwValue);
|
|
if (!NT_SUCCESS(Status))
|
|
return E_FAIL;
|
|
return S_OK;
|
|
}
|
|
|
|
// Win: UIntToStr
|
|
HRESULT APIENTRY
|
|
Imm32UIntToStr(DWORD dwValue, ULONG nBase, LPWSTR pszBuff, USHORT cchBuff)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
UnicodeString.Buffer = pszBuff;
|
|
UnicodeString.MaximumLength = cchBuff * sizeof(WCHAR);
|
|
Status = RtlIntegerToUnicodeString(dwValue, nBase, &UnicodeString);
|
|
if (!NT_SUCCESS(Status))
|
|
return E_FAIL;
|
|
return S_OK;
|
|
}
|
|
|
|
/* Win: CheckCountry */
|
|
BOOL APIENTRY Imm32IsSystemJapaneseOrKorean(VOID)
|
|
{
|
|
LCID lcid = GetSystemDefaultLCID();
|
|
LANGID LangID = LANGIDFROMLCID(lcid);
|
|
WORD wPrimary = PRIMARYLANGID(LangID);
|
|
if (wPrimary != LANG_JAPANESE || wPrimary != LANG_KOREAN)
|
|
{
|
|
TRACE("The country has no special IME support\n");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
typedef struct tagBITMAPCOREINFO256
|
|
{
|
|
BITMAPCOREHEADER bmciHeader;
|
|
RGBTRIPLE bmciColors[256];
|
|
} BITMAPCOREINFO256, *PBITMAPCOREINFO256;
|
|
|
|
HBITMAP Imm32LoadBitmapFromBytes(const BYTE *pb)
|
|
{
|
|
HBITMAP hbm = NULL;
|
|
const BITMAPCOREINFO256 *pbmci;
|
|
LPVOID pvBits;
|
|
DWORD ib, cbBytes, cColors;
|
|
BITMAP bm;
|
|
|
|
cbBytes = *(const DWORD *)pb;
|
|
if (cbBytes == 0)
|
|
return NULL;
|
|
|
|
pb += sizeof(DWORD);
|
|
ib = sizeof(DWORD);
|
|
|
|
pbmci = (const BITMAPCOREINFO256 *)pb;
|
|
hbm = CreateDIBSection(NULL, (LPBITMAPINFO)pbmci, DIB_RGB_COLORS, &pvBits, NULL, 0);
|
|
if (!hbm || !GetObject(hbm, sizeof(BITMAP), &bm))
|
|
{
|
|
ERR("Invalid bitmap\n");
|
|
return NULL;
|
|
}
|
|
|
|
switch (pbmci->bmciHeader.bcBitCount)
|
|
{
|
|
case 1: cColors = 2; break;
|
|
case 4: cColors = 16; break;
|
|
case 8: cColors = 256; break;
|
|
case 24: case 32:
|
|
cColors = 0;
|
|
break;
|
|
default:
|
|
ERR("Invalid bitmap\n");
|
|
DeleteObject(hbm);
|
|
return NULL;
|
|
}
|
|
|
|
ib += sizeof(BITMAPCOREHEADER);
|
|
pb += sizeof(BITMAPCOREHEADER);
|
|
|
|
ib += cColors * sizeof(RGBTRIPLE);
|
|
pb += cColors * sizeof(RGBTRIPLE);
|
|
|
|
ib += bm.bmWidthBytes * bm.bmHeight;
|
|
if (ib > cbBytes)
|
|
{
|
|
ERR("Invalid bitmap\n");
|
|
DeleteObject(hbm);
|
|
return NULL;
|
|
}
|
|
CopyMemory(pvBits, pb, bm.bmWidthBytes * bm.bmHeight);
|
|
|
|
return hbm;
|
|
}
|
|
|
|
BOOL Imm32StoreBitmapToBytes(HBITMAP hbm, LPBYTE pbData, DWORD cbDataMax)
|
|
{
|
|
HDC hDC;
|
|
BITMAP bm;
|
|
DWORD cbBytes, cColors;
|
|
BITMAPCOREINFO256 bmci;
|
|
BOOL ret;
|
|
LPBYTE pb = pbData;
|
|
|
|
*(LPDWORD)pb = 0;
|
|
|
|
if (!GetObject(hbm, sizeof(BITMAP), &bm))
|
|
{
|
|
ERR("Invalid bitmap\n");
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&bmci, sizeof(bmci));
|
|
bmci.bmciHeader.bcSize = sizeof(BITMAPCOREHEADER);
|
|
bmci.bmciHeader.bcWidth = bm.bmWidth;
|
|
bmci.bmciHeader.bcHeight = bm.bmHeight;
|
|
bmci.bmciHeader.bcPlanes = 1;
|
|
bmci.bmciHeader.bcBitCount = bm.bmBitsPixel;
|
|
|
|
switch (bm.bmBitsPixel)
|
|
{
|
|
case 1: cColors = 2; break;
|
|
case 4: cColors = 16; break;
|
|
case 8: cColors = 256; break;
|
|
case 24: case 32:
|
|
cColors = 0;
|
|
break;
|
|
default:
|
|
ERR("Invalid bitmap\n");
|
|
return FALSE;
|
|
}
|
|
|
|
cbBytes = sizeof(DWORD);
|
|
cbBytes += sizeof(BITMAPCOREHEADER);
|
|
cbBytes += cColors * sizeof(RGBTRIPLE);
|
|
cbBytes += bm.bmWidthBytes * bm.bmHeight;
|
|
if (cbBytes > cbDataMax)
|
|
{
|
|
ERR("Too small\n");
|
|
return FALSE;
|
|
}
|
|
|
|
hDC = CreateCompatibleDC(NULL);
|
|
|
|
ret = GetDIBits(hDC, hbm, 0, bm.bmHeight, NULL, (LPBITMAPINFO)&bmci, DIB_RGB_COLORS);
|
|
|
|
if (ret)
|
|
{
|
|
*(LPDWORD)pb = cbBytes;
|
|
pb += sizeof(DWORD);
|
|
|
|
CopyMemory(pb, &bmci.bmciHeader, sizeof(BITMAPCOREHEADER));
|
|
pb += sizeof(BITMAPCOREHEADER);
|
|
|
|
CopyMemory(pb, &bmci.bmciColors, cColors * sizeof(RGBTRIPLE));
|
|
pb += cColors * sizeof(RGBTRIPLE);
|
|
|
|
ret = GetDIBits(hDC, hbm, 0, bm.bmHeight, pb, (LPBITMAPINFO)&bmci, DIB_RGB_COLORS);
|
|
if (!ret)
|
|
*(LPDWORD)pbData = 0;
|
|
}
|
|
|
|
DeleteDC(hDC);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Win: IsAnsiIMC
|
|
BOOL WINAPI Imm32IsImcAnsi(HIMC hIMC)
|
|
{
|
|
BOOL ret;
|
|
PCLIENTIMC pClientImc = ImmLockClientImc(hIMC);
|
|
if (IS_NULL_UNEXPECTEDLY(pClientImc))
|
|
return -1;
|
|
ret = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
|
|
ImmUnlockClientImc(pClientImc);
|
|
return ret;
|
|
}
|
|
|
|
LPWSTR APIENTRY Imm32WideFromAnsi(UINT uCodePage, LPCSTR pszA)
|
|
{
|
|
INT cch = lstrlenA(pszA);
|
|
LPWSTR pszW = ImmLocalAlloc(0, (cch + 1) * sizeof(WCHAR));
|
|
if (IS_NULL_UNEXPECTEDLY(pszW))
|
|
return NULL;
|
|
cch = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, pszA, cch, pszW, cch + 1);
|
|
pszW[cch] = 0;
|
|
return pszW;
|
|
}
|
|
|
|
LPSTR APIENTRY Imm32AnsiFromWide(UINT uCodePage, LPCWSTR pszW)
|
|
{
|
|
INT cchW = lstrlenW(pszW);
|
|
INT cchA = (cchW + 1) * sizeof(WCHAR);
|
|
LPSTR pszA = ImmLocalAlloc(0, cchA);
|
|
if (IS_NULL_UNEXPECTEDLY(pszA))
|
|
return NULL;
|
|
cchA = WideCharToMultiByte(uCodePage, 0, pszW, cchW, pszA, cchA, NULL, NULL);
|
|
pszA[cchA] = 0;
|
|
return pszA;
|
|
}
|
|
|
|
/* Converts the character index */
|
|
/* Win: CalcCharacterPositionAtoW */
|
|
LONG APIENTRY IchWideFromAnsi(LONG cchAnsi, LPCSTR pchAnsi, UINT uCodePage)
|
|
{
|
|
LONG cchWide;
|
|
for (cchWide = 0; cchAnsi > 0; ++cchWide)
|
|
{
|
|
if (IsDBCSLeadByteEx(uCodePage, *pchAnsi) && pchAnsi[1])
|
|
{
|
|
cchAnsi -= 2;
|
|
pchAnsi += 2;
|
|
}
|
|
else
|
|
{
|
|
--cchAnsi;
|
|
++pchAnsi;
|
|
}
|
|
}
|
|
return cchWide;
|
|
}
|
|
|
|
/* Converts the character index */
|
|
/* Win: CalcCharacterPositionWtoA */
|
|
LONG APIENTRY IchAnsiFromWide(LONG cchWide, LPCWSTR pchWide, UINT uCodePage)
|
|
{
|
|
LONG cb, cchAnsi;
|
|
for (cchAnsi = 0; cchWide > 0; ++cchAnsi, ++pchWide, --cchWide)
|
|
{
|
|
cb = WideCharToMultiByte(uCodePage, 0, pchWide, 1, NULL, 0, NULL, NULL);
|
|
if (cb > 1)
|
|
++cchAnsi;
|
|
}
|
|
return cchAnsi;
|
|
}
|
|
|
|
// Win: InternalGetSystemPathName
|
|
BOOL Imm32GetSystemLibraryPath(LPWSTR pszPath, DWORD cchPath, LPCWSTR pszFileName)
|
|
{
|
|
if (!pszFileName[0] || !GetSystemDirectoryW(pszPath, cchPath))
|
|
{
|
|
ERR("Invalid filename\n");
|
|
return FALSE;
|
|
}
|
|
StringCchCatW(pszPath, cchPath, L"\\");
|
|
StringCchCatW(pszPath, cchPath, pszFileName);
|
|
return TRUE;
|
|
}
|
|
|
|
// Win: LFontAtoLFontW
|
|
VOID APIENTRY LogFontAnsiToWide(const LOGFONTA *plfA, LPLOGFONTW plfW)
|
|
{
|
|
size_t cch;
|
|
RtlCopyMemory(plfW, plfA, offsetof(LOGFONTA, lfFaceName));
|
|
StringCchLengthA(plfA->lfFaceName, _countof(plfA->lfFaceName), &cch);
|
|
cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plfA->lfFaceName, (INT)cch,
|
|
plfW->lfFaceName, _countof(plfW->lfFaceName));
|
|
if (cch > _countof(plfW->lfFaceName) - 1)
|
|
cch = _countof(plfW->lfFaceName) - 1;
|
|
plfW->lfFaceName[cch] = 0;
|
|
}
|
|
|
|
// Win: LFontWtoLFontA
|
|
VOID APIENTRY LogFontWideToAnsi(const LOGFONTW *plfW, LPLOGFONTA plfA)
|
|
{
|
|
size_t cch;
|
|
RtlCopyMemory(plfA, plfW, offsetof(LOGFONTW, lfFaceName));
|
|
StringCchLengthW(plfW->lfFaceName, _countof(plfW->lfFaceName), &cch);
|
|
cch = WideCharToMultiByte(CP_ACP, 0, plfW->lfFaceName, (INT)cch,
|
|
plfA->lfFaceName, _countof(plfA->lfFaceName), NULL, NULL);
|
|
if (cch > _countof(plfA->lfFaceName) - 1)
|
|
cch = _countof(plfA->lfFaceName) - 1;
|
|
plfA->lfFaceName[cch] = 0;
|
|
}
|
|
|
|
static PVOID FASTCALL DesktopPtrToUser(PVOID ptr)
|
|
{
|
|
PCLIENTINFO pci = GetWin32ClientInfo();
|
|
PDESKTOPINFO pdi = pci->pDeskInfo;
|
|
|
|
ASSERT(ptr != NULL);
|
|
ASSERT(pdi != NULL);
|
|
if (pdi->pvDesktopBase <= ptr && ptr < pdi->pvDesktopLimit)
|
|
return (PVOID)((ULONG_PTR)ptr - pci->ulClientDelta);
|
|
else
|
|
return (PVOID)NtUserCallOneParam((DWORD_PTR)ptr, ONEPARAM_ROUTINE_GETDESKTOPMAPPING);
|
|
}
|
|
|
|
// Win: HMValidateHandleNoRip
|
|
LPVOID FASTCALL ValidateHandleNoErr(HANDLE hObject, UINT uType)
|
|
{
|
|
UINT index;
|
|
PUSER_HANDLE_TABLE ht;
|
|
PUSER_HANDLE_ENTRY he;
|
|
WORD generation;
|
|
LPVOID ptr;
|
|
|
|
if (!NtUserValidateHandleSecure(hObject))
|
|
{
|
|
WARN("Not a handle\n");
|
|
return NULL;
|
|
}
|
|
|
|
ht = gSharedInfo.aheList; /* handle table */
|
|
ASSERT(ht);
|
|
/* ReactOS-Specific! */
|
|
ASSERT(gSharedInfo.ulSharedDelta != 0);
|
|
he = (PUSER_HANDLE_ENTRY)((ULONG_PTR)ht->handles - gSharedInfo.ulSharedDelta);
|
|
|
|
index = (LOWORD(hObject) - FIRST_USER_HANDLE) >> 1;
|
|
if ((INT)index < 0 || ht->nb_handles <= index || he[index].type != uType)
|
|
return NULL;
|
|
|
|
if (he[index].flags & HANDLEENTRY_DESTROY)
|
|
return NULL;
|
|
|
|
generation = HIWORD(hObject);
|
|
if (generation != he[index].generation && generation && generation != 0xFFFF)
|
|
return NULL;
|
|
|
|
ptr = he[index].ptr;
|
|
if (ptr)
|
|
ptr = DesktopPtrToUser(ptr);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
// Win: HMValidateHandle
|
|
LPVOID FASTCALL ValidateHandle(HANDLE hObject, UINT uType)
|
|
{
|
|
LPVOID pvObj = ValidateHandleNoErr(hObject, uType);
|
|
if (pvObj)
|
|
return pvObj;
|
|
|
|
if (uType == TYPE_WINDOW)
|
|
SetLastError(ERROR_INVALID_WINDOW_HANDLE);
|
|
else
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return NULL;
|
|
}
|
|
|
|
// Win: TestInputContextProcess
|
|
BOOL APIENTRY Imm32CheckImcProcess(PIMC pIMC)
|
|
{
|
|
HIMC hIMC;
|
|
DWORD_PTR dwPID1, dwPID2;
|
|
|
|
if (IS_NULL_UNEXPECTEDLY(pIMC))
|
|
return FALSE;
|
|
|
|
if (pIMC->head.pti == Imm32CurrentPti())
|
|
return TRUE;
|
|
|
|
hIMC = pIMC->head.h;
|
|
dwPID1 = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTPROCESSID);
|
|
dwPID2 = (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueProcess;
|
|
if (dwPID1 != dwPID2)
|
|
{
|
|
WARN("PID 0x%X != 0x%X\n", dwPID1, dwPID2);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Win: ImmLocalAlloc
|
|
LPVOID APIENTRY ImmLocalAlloc(DWORD dwFlags, DWORD dwBytes)
|
|
{
|
|
if (!ghImmHeap)
|
|
{
|
|
ghImmHeap = RtlGetProcessHeap();
|
|
if (IS_NULL_UNEXPECTEDLY(ghImmHeap))
|
|
return NULL;
|
|
}
|
|
return HeapAlloc(ghImmHeap, dwFlags, dwBytes);
|
|
}
|
|
|
|
// Win: MakeIMENotify
|
|
BOOL APIENTRY
|
|
Imm32MakeIMENotify(HIMC hIMC, HWND hwnd, DWORD dwAction, DWORD_PTR dwIndex, DWORD_PTR dwValue,
|
|
DWORD_PTR dwCommand, DWORD_PTR dwData)
|
|
{
|
|
DWORD dwThreadId;
|
|
HKL hKL;
|
|
PIMEDPI pImeDpi;
|
|
|
|
if (dwAction != 0)
|
|
{
|
|
dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
|
|
if (dwThreadId)
|
|
{
|
|
/* find keyboard layout and lock it */
|
|
hKL = GetKeyboardLayout(dwThreadId);
|
|
pImeDpi = ImmLockImeDpi(hKL);
|
|
if (pImeDpi)
|
|
{
|
|
/* do notify */
|
|
pImeDpi->NotifyIME(hIMC, dwAction, dwIndex, dwValue);
|
|
|
|
ImmUnlockImeDpi(pImeDpi); /* unlock */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hwnd && dwCommand)
|
|
SendMessageW(hwnd, WM_IME_NOTIFY, dwCommand, dwData);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Win: BuildHimcList
|
|
DWORD APIENTRY Imm32BuildHimcList(DWORD dwThreadId, HIMC **pphList)
|
|
{
|
|
#define INITIAL_COUNT 0x40
|
|
#define MAX_RETRY 10
|
|
NTSTATUS Status;
|
|
DWORD dwCount = INITIAL_COUNT, cRetry = 0;
|
|
HIMC *phNewList;
|
|
|
|
phNewList = ImmLocalAlloc(0, dwCount * sizeof(HIMC));
|
|
if (IS_NULL_UNEXPECTEDLY(phNewList))
|
|
return 0;
|
|
|
|
Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
|
|
while (Status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ImmLocalFree(phNewList);
|
|
if (cRetry++ >= MAX_RETRY)
|
|
return 0;
|
|
|
|
phNewList = ImmLocalAlloc(0, dwCount * sizeof(HIMC));
|
|
if (IS_NULL_UNEXPECTEDLY(phNewList))
|
|
return 0;
|
|
|
|
Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
|
|
}
|
|
|
|
if (NT_ERROR(Status) || !dwCount)
|
|
{
|
|
ERR("Abnormal status\n");
|
|
ImmLocalFree(phNewList);
|
|
return 0;
|
|
}
|
|
|
|
*pphList = phNewList;
|
|
return dwCount;
|
|
#undef INITIAL_COUNT
|
|
#undef MAX_RETRY
|
|
}
|
|
|
|
// Win: ConvertImeMenuItemInfoAtoW
|
|
INT APIENTRY
|
|
Imm32ImeMenuAnsiToWide(const IMEMENUITEMINFOA *pItemA, LPIMEMENUITEMINFOW pItemW,
|
|
UINT uCodePage, BOOL bBitmap)
|
|
{
|
|
INT ret;
|
|
pItemW->cbSize = pItemA->cbSize;
|
|
pItemW->fType = pItemA->fType;
|
|
pItemW->fState = pItemA->fState;
|
|
pItemW->wID = pItemA->wID;
|
|
if (bBitmap)
|
|
{
|
|
pItemW->hbmpChecked = pItemA->hbmpChecked;
|
|
pItemW->hbmpUnchecked = pItemA->hbmpUnchecked;
|
|
pItemW->hbmpItem = pItemA->hbmpItem;
|
|
}
|
|
pItemW->dwItemData = pItemA->dwItemData;
|
|
ret = MultiByteToWideChar(uCodePage, 0, pItemA->szString, -1,
|
|
pItemW->szString, _countof(pItemW->szString));
|
|
if (ret >= _countof(pItemW->szString))
|
|
{
|
|
ret = 0;
|
|
pItemW->szString[0] = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Win: ConvertImeMenuItemInfoWtoA
|
|
INT APIENTRY
|
|
Imm32ImeMenuWideToAnsi(const IMEMENUITEMINFOW *pItemW, LPIMEMENUITEMINFOA pItemA,
|
|
UINT uCodePage)
|
|
{
|
|
INT ret;
|
|
pItemA->cbSize = pItemW->cbSize;
|
|
pItemA->fType = pItemW->fType;
|
|
pItemA->fState = pItemW->fState;
|
|
pItemA->wID = pItemW->wID;
|
|
pItemA->hbmpChecked = pItemW->hbmpChecked;
|
|
pItemA->hbmpUnchecked = pItemW->hbmpUnchecked;
|
|
pItemA->dwItemData = pItemW->dwItemData;
|
|
pItemA->hbmpItem = pItemW->hbmpItem;
|
|
ret = WideCharToMultiByte(uCodePage, 0, pItemW->szString, -1,
|
|
pItemA->szString, _countof(pItemA->szString), NULL, NULL);
|
|
if (ret >= _countof(pItemA->szString))
|
|
{
|
|
ret = 0;
|
|
pItemA->szString[0] = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Win: GetImeModeSaver
|
|
PIME_STATE APIENTRY
|
|
Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL)
|
|
{
|
|
PIME_STATE pState;
|
|
WORD Lang = PRIMARYLANGID(LOWORD(hKL));
|
|
for (pState = pIC->pState; pState; pState = pState->pNext)
|
|
{
|
|
if (pState->wLang == Lang)
|
|
break;
|
|
}
|
|
if (!pState)
|
|
{
|
|
pState = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(IME_STATE));
|
|
if (pState)
|
|
{
|
|
pState->wLang = Lang;
|
|
pState->pNext = pIC->pState;
|
|
pIC->pState = pState;
|
|
}
|
|
}
|
|
return pState;
|
|
}
|
|
|
|
// Win: GetImePrivateModeSaver
|
|
PIME_SUBSTATE APIENTRY
|
|
Imm32FetchImeSubState(PIME_STATE pState, HKL hKL)
|
|
{
|
|
PIME_SUBSTATE pSubState;
|
|
for (pSubState = pState->pSubState; pSubState; pSubState = pSubState->pNext)
|
|
{
|
|
if (pSubState->hKL == hKL)
|
|
return pSubState;
|
|
}
|
|
pSubState = ImmLocalAlloc(0, sizeof(IME_SUBSTATE));
|
|
if (!pSubState)
|
|
return NULL;
|
|
pSubState->dwValue = 0;
|
|
pSubState->hKL = hKL;
|
|
pSubState->pNext = pState->pSubState;
|
|
pState->pSubState = pSubState;
|
|
return pSubState;
|
|
}
|
|
|
|
// Win: RestorePrivateMode
|
|
BOOL APIENTRY
|
|
Imm32LoadImeStateSentence(LPINPUTCONTEXTDX pIC, PIME_STATE pState, HKL hKL)
|
|
{
|
|
PIME_SUBSTATE pSubState = Imm32FetchImeSubState(pState, hKL);
|
|
if (IS_NULL_UNEXPECTEDLY(pSubState))
|
|
return FALSE;
|
|
|
|
pIC->fdwSentence |= pSubState->dwValue;
|
|
return TRUE;
|
|
}
|
|
|
|
// Win: SavePrivateMode
|
|
BOOL APIENTRY
|
|
Imm32SaveImeStateSentence(LPINPUTCONTEXTDX pIC, PIME_STATE pState, HKL hKL)
|
|
{
|
|
PIME_SUBSTATE pSubState = Imm32FetchImeSubState(pState, hKL);
|
|
if (IS_NULL_UNEXPECTEDLY(pSubState))
|
|
return FALSE;
|
|
|
|
pSubState->dwValue = (pIC->fdwSentence & 0xffff0000);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* See RECONVERTSTRING structure:
|
|
* https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/RECONVERTSTRING.html
|
|
*
|
|
* The dwCompStrOffset and dwTargetOffset members are the relative position of dwStrOffset.
|
|
* dwStrLen, dwCompStrLen, and dwTargetStrLen are the TCHAR count. dwStrOffset,
|
|
* dwCompStrOffset, and dwTargetStrOffset are the byte offset.
|
|
*/
|
|
|
|
DWORD APIENTRY
|
|
Imm32ReconvertWideFromAnsi(LPRECONVERTSTRING pDest, const RECONVERTSTRING *pSrc, UINT uCodePage)
|
|
{
|
|
DWORD cch0, cchDest, cbDest;
|
|
LPCSTR pchSrc = (LPCSTR)pSrc + pSrc->dwStrOffset;
|
|
LPWSTR pchDest;
|
|
|
|
if (pSrc->dwVersion != 0)
|
|
{
|
|
ERR("\n");
|
|
return 0;
|
|
}
|
|
|
|
cchDest = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, pchSrc, pSrc->dwStrLen,
|
|
NULL, 0);
|
|
cbDest = sizeof(RECONVERTSTRING) + (cchDest + 1) * sizeof(WCHAR);
|
|
if (!pDest)
|
|
return cbDest;
|
|
|
|
if (pDest->dwSize < cbDest)
|
|
{
|
|
ERR("Too small\n");
|
|
return 0;
|
|
}
|
|
|
|
/* dwSize */
|
|
pDest->dwSize = cbDest;
|
|
|
|
/* dwVersion */
|
|
pDest->dwVersion = 0;
|
|
|
|
/* dwStrOffset */
|
|
pDest->dwStrOffset = sizeof(RECONVERTSTRING);
|
|
|
|
/* dwCompStrOffset */
|
|
cch0 = IchWideFromAnsi(pSrc->dwCompStrOffset, pchSrc, uCodePage);
|
|
pDest->dwCompStrOffset = cch0 * sizeof(WCHAR);
|
|
|
|
/* dwCompStrLen */
|
|
cch0 = IchWideFromAnsi(pSrc->dwCompStrOffset + pSrc->dwCompStrLen, pchSrc, uCodePage);
|
|
pDest->dwCompStrLen = (cch0 * sizeof(WCHAR) - pDest->dwCompStrOffset) / sizeof(WCHAR);
|
|
|
|
/* dwTargetStrOffset */
|
|
cch0 = IchWideFromAnsi(pSrc->dwTargetStrOffset, pchSrc, uCodePage);
|
|
pDest->dwTargetStrOffset = cch0 * sizeof(WCHAR);
|
|
|
|
/* dwTargetStrLen */
|
|
cch0 = IchWideFromAnsi(pSrc->dwTargetStrOffset + pSrc->dwTargetStrLen, pchSrc, uCodePage);
|
|
pDest->dwTargetStrLen = (cch0 * sizeof(WCHAR) - pSrc->dwTargetStrOffset) / sizeof(WCHAR);
|
|
|
|
/* dwStrLen */
|
|
pDest->dwStrLen = cchDest;
|
|
|
|
/* the string */
|
|
pchDest = (LPWSTR)((LPBYTE)pDest + pDest->dwStrOffset);
|
|
cchDest = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, pchSrc, pSrc->dwStrLen,
|
|
pchDest, cchDest);
|
|
pchDest[cchDest] = 0;
|
|
|
|
TRACE("cbDest: 0x%X\n", cbDest);
|
|
return cbDest;
|
|
}
|
|
|
|
DWORD APIENTRY
|
|
Imm32ReconvertAnsiFromWide(LPRECONVERTSTRING pDest, const RECONVERTSTRING *pSrc, UINT uCodePage)
|
|
{
|
|
DWORD cch0, cch1, cchDest, cbDest;
|
|
LPCWSTR pchSrc = (LPCWSTR)((LPCSTR)pSrc + pSrc->dwStrOffset);
|
|
LPSTR pchDest;
|
|
|
|
if (pSrc->dwVersion != 0)
|
|
{
|
|
ERR("\n");
|
|
return 0;
|
|
}
|
|
|
|
cchDest = WideCharToMultiByte(uCodePage, 0, pchSrc, pSrc->dwStrLen,
|
|
NULL, 0, NULL, NULL);
|
|
cbDest = sizeof(RECONVERTSTRING) + (cchDest + 1) * sizeof(CHAR);
|
|
if (!pDest)
|
|
return cbDest;
|
|
|
|
if (pDest->dwSize < cbDest)
|
|
{
|
|
ERR("Too small\n");
|
|
return 0;
|
|
}
|
|
|
|
/* dwSize */
|
|
pDest->dwSize = cbDest;
|
|
|
|
/* dwVersion */
|
|
pDest->dwVersion = 0;
|
|
|
|
/* dwStrOffset */
|
|
pDest->dwStrOffset = sizeof(RECONVERTSTRING);
|
|
|
|
/* dwCompStrOffset */
|
|
cch1 = pSrc->dwCompStrOffset / sizeof(WCHAR);
|
|
cch0 = IchAnsiFromWide(cch1, pchSrc, uCodePage);
|
|
pDest->dwCompStrOffset = cch0 * sizeof(CHAR);
|
|
|
|
/* dwCompStrLen */
|
|
cch0 = IchAnsiFromWide(cch1 + pSrc->dwCompStrLen, pchSrc, uCodePage);
|
|
pDest->dwCompStrLen = cch0 * sizeof(CHAR) - pDest->dwCompStrOffset;
|
|
|
|
/* dwTargetStrOffset */
|
|
cch1 = pSrc->dwTargetStrOffset / sizeof(WCHAR);
|
|
cch0 = IchAnsiFromWide(cch1, pchSrc, uCodePage);
|
|
pDest->dwTargetStrOffset = cch0 * sizeof(CHAR);
|
|
|
|
/* dwTargetStrLen */
|
|
cch0 = IchAnsiFromWide(cch1 + pSrc->dwTargetStrLen, pchSrc, uCodePage);
|
|
pDest->dwTargetStrLen = cch0 * sizeof(CHAR) - pDest->dwTargetStrOffset;
|
|
|
|
/* dwStrLen */
|
|
pDest->dwStrLen = cchDest;
|
|
|
|
/* the string */
|
|
pchDest = (LPSTR)pDest + pDest->dwStrOffset;
|
|
cchDest = WideCharToMultiByte(uCodePage, 0, pchSrc, pSrc->dwStrLen,
|
|
pchDest, cchDest, NULL, NULL);
|
|
pchDest[cchDest] = 0;
|
|
|
|
TRACE("cchDest: 0x%X\n", cchDest);
|
|
return cbDest;
|
|
}
|
|
|
|
typedef BOOL (WINAPI *FN_GetFileVersionInfoW)(LPCWSTR, DWORD, DWORD, LPVOID);
|
|
typedef DWORD (WINAPI *FN_GetFileVersionInfoSizeW)(LPCWSTR, LPDWORD);
|
|
typedef BOOL (WINAPI *FN_VerQueryValueW)(LPCVOID, LPCWSTR, LPVOID*, PUINT);
|
|
|
|
static FN_GetFileVersionInfoW s_fnGetFileVersionInfoW = NULL;
|
|
static FN_GetFileVersionInfoSizeW s_fnGetFileVersionInfoSizeW = NULL;
|
|
static FN_VerQueryValueW s_fnVerQueryValueW = NULL;
|
|
|
|
// Win: LoadFixVersionInfo
|
|
static BOOL APIENTRY Imm32LoadImeFixedInfo(PIMEINFOEX pInfoEx, LPCVOID pVerInfo)
|
|
{
|
|
UINT cbFixed = 0;
|
|
VS_FIXEDFILEINFO *pFixed;
|
|
if (!s_fnVerQueryValueW(pVerInfo, L"\\", (LPVOID*)&pFixed, &cbFixed) || !cbFixed)
|
|
{
|
|
ERR("Fixed version info not available\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* NOTE: The IME module must contain a version info of input method driver. */
|
|
if (pFixed->dwFileType != VFT_DRV || pFixed->dwFileSubtype != VFT2_DRV_INPUTMETHOD)
|
|
{
|
|
ERR("DLL is not an IME\n");
|
|
return FALSE;
|
|
}
|
|
|
|
pInfoEx->dwProdVersion = pFixed->dwProductVersionMS;
|
|
pInfoEx->dwImeWinVersion = 0x40000;
|
|
return TRUE;
|
|
}
|
|
|
|
// Win: GetVersionDatum
|
|
static LPWSTR APIENTRY
|
|
Imm32GetVerInfoValue(LPCVOID pVerInfo, LPWSTR pszKey, DWORD cchKey, LPCWSTR pszName)
|
|
{
|
|
size_t cchExtra;
|
|
LPWSTR pszValue;
|
|
UINT cbValue = 0;
|
|
|
|
StringCchLengthW(pszKey, cchKey, &cchExtra);
|
|
|
|
StringCchCatW(pszKey, cchKey, pszName);
|
|
s_fnVerQueryValueW(pVerInfo, pszKey, (LPVOID*)&pszValue, &cbValue);
|
|
pszKey[cchExtra] = 0;
|
|
|
|
return (cbValue ? pszValue : NULL);
|
|
}
|
|
|
|
// Win: LoadVarVersionInfo
|
|
BOOL APIENTRY Imm32LoadImeLangAndDesc(PIMEINFOEX pInfoEx, LPCVOID pVerInfo)
|
|
{
|
|
BOOL ret;
|
|
WCHAR szKey[80];
|
|
LPWSTR pszDesc;
|
|
LPWORD pw;
|
|
UINT cbData;
|
|
LANGID LangID;
|
|
|
|
/* Getting the version info. See VerQueryValue */
|
|
ret = s_fnVerQueryValueW(pVerInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&pw, &cbData);
|
|
if (!ret || !cbData)
|
|
{
|
|
ERR("Translation not available\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (pInfoEx->hkl == NULL)
|
|
pInfoEx->hkl = (HKL)(DWORD_PTR)*pw;
|
|
|
|
/* Try the current language and the Unicode codepage (0x04B0) */
|
|
LangID = LANGIDFROMLCID(GetThreadLocale());
|
|
StringCchPrintfW(szKey, _countof(szKey), L"\\StringFileInfo\\%04X04B0\\", LangID);
|
|
pszDesc = Imm32GetVerInfoValue(pVerInfo, szKey, _countof(szKey), L"FileDescription");
|
|
if (!pszDesc)
|
|
{
|
|
/* Retry the language and codepage of the IME module */
|
|
StringCchPrintfW(szKey, _countof(szKey), L"\\StringFileInfo\\%04X%04X\\", pw[0], pw[1]);
|
|
pszDesc = Imm32GetVerInfoValue(pVerInfo, szKey, _countof(szKey), L"FileDescription");
|
|
}
|
|
|
|
/* The description */
|
|
if (pszDesc)
|
|
StringCchCopyW(pInfoEx->wszImeDescription, _countof(pInfoEx->wszImeDescription), pszDesc);
|
|
else
|
|
pInfoEx->wszImeDescription[0] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Win: LoadVersionInfo
|
|
BOOL APIENTRY Imm32LoadImeVerInfo(PIMEINFOEX pImeInfoEx)
|
|
{
|
|
HINSTANCE hinstVersion;
|
|
BOOL ret = FALSE, bLoaded = FALSE;
|
|
WCHAR szPath[MAX_PATH];
|
|
LPVOID pVerInfo;
|
|
DWORD cbVerInfo, dwHandle;
|
|
|
|
/* Load version.dll to use the version info API */
|
|
Imm32GetSystemLibraryPath(szPath, _countof(szPath), L"version.dll");
|
|
hinstVersion = GetModuleHandleW(szPath);
|
|
if (!hinstVersion)
|
|
{
|
|
hinstVersion = LoadLibraryW(szPath);
|
|
if (IS_NULL_UNEXPECTEDLY(hinstVersion))
|
|
return FALSE;
|
|
|
|
bLoaded = TRUE;
|
|
}
|
|
|
|
#define GET_FN(name) do { \
|
|
s_fn##name = (FN_##name)GetProcAddress(hinstVersion, #name); \
|
|
if (!s_fn##name) goto Quit; \
|
|
} while (0)
|
|
GET_FN(GetFileVersionInfoW);
|
|
GET_FN(GetFileVersionInfoSizeW);
|
|
GET_FN(VerQueryValueW);
|
|
#undef GET_FN
|
|
|
|
/* The path of the IME module */
|
|
Imm32GetSystemLibraryPath(szPath, _countof(szPath), pImeInfoEx->wszImeFile);
|
|
|
|
cbVerInfo = s_fnGetFileVersionInfoSizeW(szPath, &dwHandle);
|
|
if (IS_ZERO_UNEXPECTEDLY(cbVerInfo))
|
|
goto Quit;
|
|
|
|
pVerInfo = ImmLocalAlloc(0, cbVerInfo);
|
|
if (IS_NULL_UNEXPECTEDLY(pVerInfo))
|
|
goto Quit;
|
|
|
|
/* Load the version info of the IME module */
|
|
if (s_fnGetFileVersionInfoW(szPath, dwHandle, cbVerInfo, pVerInfo) &&
|
|
Imm32LoadImeFixedInfo(pImeInfoEx, pVerInfo))
|
|
{
|
|
ret = Imm32LoadImeLangAndDesc(pImeInfoEx, pVerInfo);
|
|
}
|
|
|
|
ImmLocalFree(pVerInfo);
|
|
|
|
Quit:
|
|
if (bLoaded)
|
|
FreeLibrary(hinstVersion);
|
|
TRACE("ret: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
// Win: AssignNewLayout
|
|
HKL APIENTRY Imm32AssignNewLayout(UINT cKLs, const REG_IME *pLayouts, WORD wLangID)
|
|
{
|
|
UINT iKL, wID, wLow = 0xE0FF, wHigh = 0xE01F, wNextID = 0;
|
|
|
|
for (iKL = 0; iKL < cKLs; ++iKL)
|
|
{
|
|
wHigh = max(wHigh, HIWORD(pLayouts[iKL].hKL));
|
|
wLow = min(wLow, HIWORD(pLayouts[iKL].hKL));
|
|
}
|
|
|
|
if (wHigh < 0xE0FF)
|
|
{
|
|
wNextID = wHigh + 1;
|
|
}
|
|
else if (wLow > 0xE001)
|
|
{
|
|
wNextID = wLow - 1;
|
|
}
|
|
else
|
|
{
|
|
for (wID = 0xE020; wID <= 0xE0FF; ++wID)
|
|
{
|
|
for (iKL = 0; iKL < cKLs; ++iKL)
|
|
{
|
|
if (LOWORD(pLayouts[iKL].hKL) == wLangID &&
|
|
HIWORD(pLayouts[iKL].hKL) == wID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iKL >= cKLs)
|
|
break;
|
|
}
|
|
|
|
if (wID <= 0xE0FF)
|
|
wNextID = wID;
|
|
}
|
|
|
|
if (!wNextID)
|
|
return NULL;
|
|
|
|
return (HKL)(DWORD_PTR)MAKELONG(wLangID, wNextID);
|
|
}
|
|
|
|
// Win: GetImeLayout
|
|
UINT APIENTRY Imm32GetImeLayout(PREG_IME pLayouts, UINT cLayouts)
|
|
{
|
|
HKEY hkeyLayouts, hkeyIME;
|
|
WCHAR szImeFileName[80], szImeKey[20];
|
|
UINT iKey, nCount;
|
|
DWORD cbData;
|
|
LONG lError;
|
|
ULONG Value;
|
|
HKL hKL;
|
|
|
|
/* Open the registry keyboard layouts */
|
|
lError = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hkeyLayouts);
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
return 0;
|
|
|
|
for (iKey = nCount = 0; ; ++iKey)
|
|
{
|
|
/* Get the key name */
|
|
lError = RegEnumKeyW(hkeyLayouts, iKey, szImeKey, _countof(szImeKey));
|
|
if (lError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
if (szImeKey[0] != L'E' && szImeKey[0] != L'e')
|
|
continue; /* Not an IME layout */
|
|
|
|
if (pLayouts == NULL) /* for counting only */
|
|
{
|
|
++nCount;
|
|
continue;
|
|
}
|
|
|
|
if (cLayouts <= nCount)
|
|
break;
|
|
|
|
lError = RegOpenKeyW(hkeyLayouts, szImeKey, &hkeyIME); /* Open the IME key */
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
continue;
|
|
|
|
/* Load the "Ime File" value */
|
|
szImeFileName[0] = 0;
|
|
cbData = sizeof(szImeFileName);
|
|
RegQueryValueExW(hkeyIME, L"Ime File", NULL, NULL, (LPBYTE)szImeFileName, &cbData);
|
|
szImeFileName[_countof(szImeFileName) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
|
|
|
|
RegCloseKey(hkeyIME);
|
|
|
|
/* We don't allow the invalid "IME File" values for security reason */
|
|
if (!szImeFileName[0] || wcscspn(szImeFileName, L":\\/") != wcslen(szImeFileName))
|
|
{
|
|
WARN("\n");
|
|
continue;
|
|
}
|
|
|
|
Imm32StrToUInt(szImeKey, &Value, 16);
|
|
hKL = (HKL)(DWORD_PTR)Value;
|
|
if (!IS_IME_HKL(hKL)) /* Not an IME */
|
|
{
|
|
WARN("\n");
|
|
continue;
|
|
}
|
|
|
|
/* Store the IME key and the IME filename */
|
|
pLayouts[nCount].hKL = hKL;
|
|
StringCchCopyW(pLayouts[nCount].szImeKey, _countof(pLayouts[nCount].szImeKey), szImeKey);
|
|
CharUpperW(szImeFileName);
|
|
StringCchCopyW(pLayouts[nCount].szFileName, _countof(pLayouts[nCount].szFileName),
|
|
szImeFileName);
|
|
++nCount;
|
|
}
|
|
|
|
RegCloseKey(hkeyLayouts);
|
|
return nCount;
|
|
}
|
|
|
|
// Win: WriteImeLayout
|
|
BOOL APIENTRY Imm32WriteImeLayout(HKL hKL, LPCWSTR pchFilePart, LPCWSTR pszLayoutText)
|
|
{
|
|
UINT iPreload;
|
|
HKEY hkeyLayouts, hkeyIME, hkeyPreload;
|
|
WCHAR szImeKey[20], szPreloadNumber[20], szPreloadKey[20];
|
|
DWORD cbData;
|
|
LANGID LangID;
|
|
LONG lError;
|
|
LPCWSTR pszLayoutFile;
|
|
|
|
/* Open the registry keyboard layouts */
|
|
lError = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hkeyLayouts);
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
return FALSE;
|
|
|
|
/* Get the IME key from hKL */
|
|
StringCchPrintf(szImeKey, _countof(szImeKey), L"%08X", (DWORD)(DWORD_PTR)hKL);
|
|
|
|
/* Create a registry IME key */
|
|
lError = RegCreateKeyW(hkeyLayouts, szImeKey, &hkeyIME);
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
goto Failure;
|
|
|
|
/* Write "Ime File" */
|
|
cbData = (wcslen(pchFilePart) + 1) * sizeof(WCHAR);
|
|
lError = RegSetValueExW(hkeyIME, L"Ime File", 0, REG_SZ, (LPBYTE)pchFilePart, cbData);
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
goto Failure;
|
|
|
|
/* Write "Layout Text" */
|
|
cbData = (wcslen(pszLayoutText) + 1) * sizeof(WCHAR);
|
|
lError = RegSetValueExW(hkeyIME, L"Layout Text", 0, REG_SZ, (LPBYTE)pszLayoutText, cbData);
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
goto Failure;
|
|
|
|
/* Choose "Layout File" from hKL */
|
|
LangID = LOWORD(hKL);
|
|
switch (LOBYTE(LangID))
|
|
{
|
|
case LANG_JAPANESE: pszLayoutFile = L"kbdjpn.dll"; break;
|
|
case LANG_KOREAN: pszLayoutFile = L"kbdkor.dll"; break;
|
|
default: pszLayoutFile = L"kbdus.dll"; break;
|
|
}
|
|
|
|
/* Write "Layout File" */
|
|
cbData = (wcslen(pszLayoutFile) + 1) * sizeof(WCHAR);
|
|
lError = RegSetValueExW(hkeyIME, L"Layout File", 0, REG_SZ, (LPBYTE)pszLayoutFile, cbData);
|
|
if (IS_ERROR_UNEXPECTEDLY(lError))
|
|
goto Failure;
|
|
|
|
RegCloseKey(hkeyIME);
|
|
RegCloseKey(hkeyLayouts);
|
|
|
|
/* Create "Preload" key */
|
|
RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkeyPreload);
|
|
|
|
#define MAX_PRELOAD 0x400
|
|
for (iPreload = 1; iPreload < MAX_PRELOAD; ++iPreload)
|
|
{
|
|
Imm32UIntToStr(iPreload, 10, szPreloadNumber, _countof(szPreloadNumber));
|
|
|
|
/* Load the key of the preload number */
|
|
cbData = sizeof(szPreloadKey);
|
|
lError = RegQueryValueExW(hkeyPreload, szPreloadNumber, NULL, NULL,
|
|
(LPBYTE)szPreloadKey, &cbData);
|
|
szPreloadKey[_countof(szPreloadKey) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
|
|
|
|
if (lError != ERROR_SUCCESS || lstrcmpiW(szImeKey, szPreloadKey) == 0)
|
|
break; /* Found an empty room or the same key */
|
|
}
|
|
|
|
if (iPreload >= MAX_PRELOAD) /* Not found */
|
|
{
|
|
ERR("\n");
|
|
RegCloseKey(hkeyPreload);
|
|
return FALSE;
|
|
}
|
|
#undef MAX_PRELOAD
|
|
|
|
/* Write the IME key to the preload number */
|
|
cbData = (wcslen(szImeKey) + 1) * sizeof(WCHAR);
|
|
lError = RegSetValueExW(hkeyPreload, szPreloadNumber, 0, REG_SZ, (LPBYTE)szImeKey, cbData);
|
|
RegCloseKey(hkeyPreload);
|
|
return lError == ERROR_SUCCESS;
|
|
|
|
Failure:
|
|
RegCloseKey(hkeyIME);
|
|
RegDeleteKeyW(hkeyLayouts, szImeKey);
|
|
RegCloseKey(hkeyLayouts);
|
|
return FALSE;
|
|
}
|
|
|
|
typedef INT (WINAPI *FN_LZOpenFileW)(LPWSTR, LPOFSTRUCT, WORD);
|
|
typedef LONG (WINAPI *FN_LZCopy)(INT, INT);
|
|
typedef VOID (WINAPI *FN_LZClose)(INT);
|
|
|
|
// Win: CopyImeFile
|
|
BOOL APIENTRY Imm32CopyImeFile(LPWSTR pszOldFile, LPCWSTR pszNewFile)
|
|
{
|
|
BOOL ret = FALSE, bLoaded = FALSE;
|
|
HMODULE hinstLZ32;
|
|
WCHAR szLZ32Path[MAX_PATH];
|
|
CHAR szDestA[MAX_PATH];
|
|
OFSTRUCT OFStruct;
|
|
FN_LZOpenFileW fnLZOpenFileW;
|
|
FN_LZCopy fnLZCopy;
|
|
FN_LZClose fnLZClose;
|
|
HFILE hfDest, hfSrc;
|
|
|
|
/* Load LZ32.dll for copying/decompressing file */
|
|
Imm32GetSystemLibraryPath(szLZ32Path, _countof(szLZ32Path), L"LZ32");
|
|
hinstLZ32 = GetModuleHandleW(szLZ32Path);
|
|
if (!hinstLZ32)
|
|
{
|
|
hinstLZ32 = LoadLibraryW(szLZ32Path);
|
|
if (IS_NULL_UNEXPECTEDLY(hinstLZ32))
|
|
return FALSE;
|
|
bLoaded = TRUE;
|
|
}
|
|
|
|
#define GET_FN(name) do { \
|
|
fn##name = (FN_##name)GetProcAddress(hinstLZ32, #name); \
|
|
if (!fn##name) goto Quit; \
|
|
} while (0)
|
|
GET_FN(LZOpenFileW);
|
|
GET_FN(LZCopy);
|
|
GET_FN(LZClose);
|
|
#undef GET_FN
|
|
|
|
if (!WideCharToMultiByte(CP_ACP, 0, pszNewFile, -1, szDestA, _countof(szDestA), NULL, NULL))
|
|
goto Quit;
|
|
szDestA[_countof(szDestA) - 1] = 0;
|
|
|
|
hfSrc = fnLZOpenFileW(pszOldFile, &OFStruct, OF_READ);
|
|
if (hfSrc < 0)
|
|
goto Quit;
|
|
|
|
hfDest = OpenFile(szDestA, &OFStruct, OF_CREATE);
|
|
if (hfDest != HFILE_ERROR)
|
|
{
|
|
ret = (fnLZCopy(hfSrc, hfDest) >= 0);
|
|
_lclose(hfDest);
|
|
}
|
|
|
|
fnLZClose(hfSrc);
|
|
|
|
Quit:
|
|
if (bLoaded)
|
|
FreeLibrary(hinstLZ32);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmCreateIMCC(IMM32.@)
|
|
*/
|
|
HIMCC WINAPI ImmCreateIMCC(DWORD size)
|
|
{
|
|
if (size < sizeof(DWORD))
|
|
size = sizeof(DWORD);
|
|
return LocalAlloc(LHND, size);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmDestroyIMCC(IMM32.@)
|
|
*/
|
|
HIMCC WINAPI ImmDestroyIMCC(HIMCC block)
|
|
{
|
|
if (block)
|
|
return LocalFree(block);
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmLockIMCC(IMM32.@)
|
|
*/
|
|
LPVOID WINAPI ImmLockIMCC(HIMCC imcc)
|
|
{
|
|
if (imcc)
|
|
return LocalLock(imcc);
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmUnlockIMCC(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmUnlockIMCC(HIMCC imcc)
|
|
{
|
|
if (imcc)
|
|
return LocalUnlock(imcc);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmGetIMCCLockCount(IMM32.@)
|
|
*/
|
|
DWORD WINAPI ImmGetIMCCLockCount(HIMCC imcc)
|
|
{
|
|
return LocalFlags(imcc) & LMEM_LOCKCOUNT;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmReSizeIMCC(IMM32.@)
|
|
*/
|
|
HIMCC WINAPI ImmReSizeIMCC(HIMCC imcc, DWORD size)
|
|
{
|
|
if (!imcc)
|
|
return NULL;
|
|
return LocalReAlloc(imcc, size, LHND);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmGetIMCCSize(IMM32.@)
|
|
*/
|
|
DWORD WINAPI ImmGetIMCCSize(HIMCC imcc)
|
|
{
|
|
if (imcc)
|
|
return LocalSize(imcc);
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmGetIMCLockCount(IMM32.@)
|
|
*/
|
|
DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC)
|
|
{
|
|
DWORD ret;
|
|
HANDLE hInputContext;
|
|
PCLIENTIMC pClientImc;
|
|
|
|
pClientImc = ImmLockClientImc(hIMC);
|
|
if (IS_NULL_UNEXPECTEDLY(pClientImc))
|
|
return 0;
|
|
|
|
ret = 0;
|
|
hInputContext = pClientImc->hInputContext;
|
|
if (hInputContext)
|
|
ret = (LocalFlags(hInputContext) & LMEM_LOCKCOUNT);
|
|
|
|
ImmUnlockClientImc(pClientImc);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmIMPGetIMEA(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmIMPGetIMEA(HWND hWnd, LPIMEPROA pImePro)
|
|
{
|
|
FIXME("(%p, %p)\n", hWnd, pImePro);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmIMPGetIMEW(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmIMPGetIMEW(HWND hWnd, LPIMEPROW pImePro)
|
|
{
|
|
FIXME("(%p, %p)\n", hWnd, pImePro);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmIMPQueryIMEA(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmIMPQueryIMEA(LPIMEPROA pImePro)
|
|
{
|
|
FIXME("(%p)\n", pImePro);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmIMPQueryIMEW(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmIMPQueryIMEW(LPIMEPROW pImePro)
|
|
{
|
|
FIXME("(%p)\n", pImePro);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmIMPSetIMEA(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmIMPSetIMEA(HWND hWnd, LPIMEPROA pImePro)
|
|
{
|
|
FIXME("(%p, %p)\n", hWnd, pImePro);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ImmIMPSetIMEW(IMM32.@)
|
|
*/
|
|
BOOL WINAPI ImmIMPSetIMEW(HWND hWnd, LPIMEPROW pImePro)
|
|
{
|
|
FIXME("(%p, %p)\n", hWnd, pImePro);
|
|
return FALSE;
|
|
}
|