spiegel-keyman/windows/src/engine/keyman32/calldll.cpp
2021-08-17 17:16:56 +10:00

363 lines
10 KiB
C++

/*
Name: calldll
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 11 Mar 2009
Modified Date: 13 Oct 2014
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888
11 Dec 2009 - mcdurdin - I934 - x64 - Initial version
12 Mar 2010 - mcdurdin - I934 - x64 - Complete
12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings
04 May 2010 - mcdurdin - I2351 - Robustness - verify _td return value
03 Oct 2011 - mcdurdin - I3094 - IMX DLLs do not support x64
03 Oct 2011 - mcdurdin - I3091 - KMGetContext crashes with assertion failure due to buffer size mismatch
24 Jan 2012 - mcdurdin - I3173 - Keyman Developer crash when unloading forced keyboard in rare cases
04 Nov 2012 - mcdurdin - I3525 - V9.0 - Merge of I3173 - Keyman can crash when Keyman_StopForcingKeyboard called and keyboard is not already forced
05 Nov 2012 - mcdurdin - I3547 - V9.0 Use _countof to tidyup code
13 Oct 2014 - mcdurdin - I4452 - V9.0 - Chinese keyboard is not working correctly
*/
#include "pch.h"
typedef BOOL (WINAPI *InitDllFunction)(PSTR name);
/* Add a dll to the list of dlls associated with the keyboard */
static LPIMDLL AddIMDLL(LPINTKEYBOARDINFO lpkbi, LPSTR kbdpath, LPSTR dllfilename)
{
DWORD j;
for(j = 0; j < lpkbi->nIMDLLs; j++)
if(!_stricmp(lpkbi->IMDLLs[j].Filename, dllfilename)) break;
if(j < lpkbi->nIMDLLs) return &lpkbi->IMDLLs[j];
char drive[_MAX_DRIVE], dir[_MAX_DIR], fullname[_MAX_PATH], newdllname[_MAX_FNAME];
strcpy_s(fullname, _countof(fullname), kbdpath);
_splitpath_s(fullname, drive, _countof(drive), dir, _countof(dir), NULL, 0, NULL, 0); // I3547
#ifdef _WIN64
char dllname[_MAX_FNAME], dllext[_MAX_EXT], newdllext[_MAX_EXT]; // I3094
_splitpath_s(dllfilename, NULL, 0, NULL, 0, dllname, _countof(dllname), dllext, _countof(dllext)); // I3547
strcpy_s(newdllext, _countof(newdllext), ".x64"); // I3547
strcat_s(newdllext, _countof(newdllext), dllext); // I3547
_makepath_s(fullname, _countof(fullname), drive, dir, dllname, newdllext); // I3547
_makepath_s(newdllname, _countof(newdllname), NULL, NULL, dllname, newdllext); // I3547
#else
_makepath_s(fullname, _countof(fullname), drive, dir, dllfilename, NULL); // I3547
strcpy_s(newdllname, _countof(newdllname), dllfilename); // I3094 // I3547
#endif
/* Load the DLL */
HINSTANCE hModule = LoadLibrary(fullname); // Try keyboard dir first
if(!hModule) hModule = LoadLibrary(newdllname); // Try anywhere on the path -- LIBRARY_NAME dir first // I3094
if(!hModule) return NULL;
/* Initialise the DLL */
InitDllFunction idf = (InitDllFunction) GetProcAddress(hModule, "KeymanIMInit");
if(idf && !(*idf)(lpkbi->Name))
{
FreeLibrary(hModule);
return NULL;
}
/* Add the DLL to the list of DLLs */
LPIMDLL imd = new IMDLL[lpkbi->nIMDLLs + 1];
if(lpkbi->nIMDLLs > 0)
{
memcpy(imd, lpkbi->IMDLLs, sizeof(IMDLL) * lpkbi->nIMDLLs);
delete lpkbi->IMDLLs;
}
lpkbi->IMDLLs = imd;
strncpy(imd->Filename, dllfilename, 255);
imd->Filename[255] = 0;
imd->nHooks = 0;
imd->Hooks = NULL;
imd->hModule = hModule;
lpkbi->nIMDLLs++;
return imd;
}
/* Add a dll hook function to the list of hook functions associated with a single dll */
static BOOL AddIMDLLHook(LPIMDLL imd, LPSTR funcname, DWORD storeno, PWCHAR *dpString)
{
/* Get the procedure address for the function */
IMDLLHOOKProc dhp = (IMDLLHOOKProc) GetProcAddress(imd->hModule, funcname);
if(!dhp) return FALSE;
/* Add the function to the list of functions in the DLL */
LPIMDLLHOOK hooks = new IMDLLHOOK[imd->nHooks+1];
if(imd->nHooks > 0)
{
memcpy(hooks, imd->Hooks, sizeof(IMDLLHOOK) * imd->nHooks);
delete imd->Hooks;
}
imd->Hooks = hooks;
strncpy(imd->Hooks[imd->nHooks].name, funcname, 31);
imd->Hooks[imd->nHooks].name[31] = 0;
imd->Hooks[imd->nHooks].storeno = storeno;
imd->Hooks[imd->nHooks].function = dhp;
*dpString = (PWCHAR) &imd->Hooks[imd->nHooks++];
return TRUE;
}
/* Call a DLL callback for all DLLs loaded with a given keyboard */
BOOL CallbackDLLs(LPINTKEYBOARDINFO lpkbi, PSTR cmd)
{
//SendDebugMessageFormat(0, sdmKeyboard, 0, "ActivateDLLs: Enter");
for(DWORD i = 0; i < lpkbi->nIMDLLs; i++)
{
InitDllFunction idf = (InitDllFunction) GetProcAddress(lpkbi->IMDLLs[i].hModule, cmd);
if(idf) (*idf)(lpkbi->Name);
}
return TRUE;
}
/* Load the dlls associated with a keyboard */
BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi)
{
char fullname[_MAX_PATH];
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Enter");
if(lpkbi->nIMDLLs > 0) if(!UnloadDLLs(lpkbi)) return FALSE;
if(!GetKeyboardFileName(lpkbi->Name, fullname, _MAX_PATH)) return FALSE;
if (!lpkbi->Keyboard) return FALSE;
for(DWORD i = 0; i < lpkbi->Keyboard->cxStoreArray; i++)
{
LPSTORE s = &lpkbi->Keyboard->dpStoreArray[i];
if(s->dwSystemID == TSS_CALLDEFINITION)
{
/* Break the store string into components */
PCHAR p = wstrtostr(s->dpString), q, r, context;
q = strtok_s(p, ":", &context);
r = strtok_s(NULL, ":", &context);
if(!q || !r)
{
s->dwSystemID = TSS_CALLDEFINITION_LOADFAILED;
delete[] p;
continue;
}
LPIMDLL imd = AddIMDLL(lpkbi, fullname, q);
if(imd && AddIMDLLHook(imd, r, i, &s->dpString)) s->dwSystemID = TSS_CALLDEFINITION;
else s->dwSystemID = TSS_CALLDEFINITION_LOADFAILED;
delete[] p;
}
}
//SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Exit");
return TRUE;
}
BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi)
{
if(!lpkbi)
{
SetLastError(ERROR_INVALID_PARAMETER); // I3173 // I3525
return FALSE;
}
CallbackDLLs(lpkbi, "KeymanIMDestroy");
for(DWORD i = 0; i < lpkbi->nIMDLLs; i++)
{
FreeLibrary(lpkbi->IMDLLs[i].hModule);
if(lpkbi->IMDLLs[i].Hooks) delete lpkbi->IMDLLs[i].Hooks;
}
delete lpkbi->IMDLLs;
lpkbi->IMDLLs = NULL;
lpkbi->nIMDLLs = 0;
return TRUE;
}
BOOL ActivateDLLs(LPINTKEYBOARDINFO lpkbi)
{
if(!lpkbi)
{
SetLastError(ERROR_INVALID_PARAMETER); // I3173 - not actually used by this issue but added for completeness // I3525
return FALSE;
}
CallbackDLLs(lpkbi, "KeymanIMActivate");
return TRUE;
}
BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi)
{
if(!lpkbi)
{
SetLastError(ERROR_INVALID_PARAMETER); // I3173 // I3525
return FALSE;
}
CallbackDLLs(lpkbi, "KeymanIMDeactivate");
return TRUE;
}
void CallDLL(LPINTKEYBOARDINFO lpkbi, DWORD storenum)
{
//SendDebugMessageFormat(0, sdmKeyboard, 0, "CallDll: Enter");
if (!lpkbi->Keyboard) return;
if(storenum >= lpkbi->Keyboard->cxStoreArray) return;
LPSTORE s = &lpkbi->Keyboard->dpStoreArray[storenum];
if(s->dwSystemID != TSS_CALLDEFINITION) return;
if(s->dpString == NULL) return;
LPIMDLLHOOK imdh = (LPIMDLLHOOK) s->dpString;
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
if(_td->TIPFUpdateable) { // I4452
(*imdh->function)(_td->state.msg.hwnd, _td->state.vkey, _td->state.charCode, Globals::get_ShiftState());
}
//SendDebugMessageFormat(0, sdmKeyboard, 0, "CallDll: Exit");
}
extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!_td->app) return FALSE;
while(backlen-- > 0) _td->app->QueueAction(QIT_BACK, 0);
while(*buf) _td->app->QueueAction(QIT_CHAR, *buf++);
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMQueueAction(int ItemType, DWORD dwData)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!_td->app) return FALSE;
return _td->app->QueueAction(ItemType, dwData);
}
extern "C" BOOL _declspec(dllexport) WINAPI KMGetContext(PWSTR buf, DWORD len)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!_td->app) return FALSE;
PWSTR q = _td->app->ContextBufMax(len);
if(!q) return FALSE; // context buf does not exist
wcscpy_s(buf, len+1, q); // I3091
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMDisplayIM(HWND hwnd, BOOL FShowAlways)
{
if(hwnd == 0) return KMHideIM();
HWND hwndFocus = GetFocus();
if(hwndFocus == 0) return FALSE;
if(*Globals::hwndIM() == hwnd)
{
*Globals::hwndIMAlways() = FShowAlways;
return TRUE;
}
*Globals::hwndIM() = hwnd;
*Globals::hwndIMAlways() = FShowAlways;
POINT pt;
RECT r;
GetCaretPos(&pt);
int n;
//if(pt.x == 0 && pt.y == 0)
n = SWP_NOMOVE;
//else n = 0;
ClientToScreen(hwndFocus, &pt);
GetWindowRect(hwnd, &r);
if(pt.x + r.right - r.left >= GetSystemMetrics(SM_CXSCREEN))
pt.x = GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left);
if(pt.y - (r.bottom-r.top) > 0) pt.y -= (r.bottom-r.top);
else pt.y += 32; // guessing...
SetWindowPos(hwnd, HWND_TOPMOST, pt.x,pt.y,0,0, n|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE);
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMHideIM()
{
if(*Globals::hwndIM() != 0)
{
ShowWindow(*Globals::hwndIM(), SW_HIDE);
//PostMessage(hwndIM, WM_USER+2134, (WPARAM) hwndIMOldFocus, 0);
*Globals::hwndIM() = 0;
*Globals::hwndIMAlways() = FALSE;
}
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMGetActiveKeyboard(PSTR buf, int nbuf)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!_td->lpActiveKeyboard) return FALSE;
strncpy_s(buf, nbuf, _td->lpActiveKeyboard->Name, nbuf-1);
buf[nbuf-1] = 0;
return TRUE;
}
extern "C" BOOL _declspec(dllexport) WINAPI KMGetKeyboardPath(PSTR keyboardname, PSTR buf, int nbuf)
{
return GetKeyboardFileName(keyboardname, buf, nbuf);
}
void KMUpdateIM()
{
if(*Globals::hwndIM() != 0)
{
ShowWindow(*Globals::hwndIM(), SW_HIDE);
//PostMessage(hwndIM, WM_USER+2134, (WPARAM) hwndIMOldFocus, 0);
*Globals::hwndIM() = 0;
*Globals::hwndIMAlways() = FALSE;
}
}
BOOL IsIMWindow(HWND hwnd)
{
if(hwnd == *Globals::hwndIM()) return TRUE;
if(!*Globals::hwndIM()) return FALSE;
return IsChild(*Globals::hwndIM(), hwnd);
}