mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-12-24 16:14:05 +00:00
366 lines
10 KiB
C
366 lines
10 KiB
C
#include <tchar.h>
|
|
#include <windowsx.h>
|
|
#include "TextOutSpy.h"
|
|
#include "ThTypes.h"
|
|
#include "GDDataTranfer.h"
|
|
#include "GetWordByIAccEx.h"
|
|
|
|
const int MOUSEOVER_INTERVAL = 300;
|
|
const int REQUEST_MESSAGE_INTERVAL = 500;
|
|
const int WM_MY_SHOW_TRANSLATION = WM_USER + 301;
|
|
|
|
HINSTANCE g_hInstance = NULL;
|
|
HANDLE hSynhroMutex = 0;
|
|
HINSTANCE hGetWordLib = 0;
|
|
UINT_PTR TimerID = 0;
|
|
typedef DWORD (*GetWordProc_t)(TCurrentMode *);
|
|
GetWordProc_t GetWordProc = NULL;
|
|
GDDataStruct gds;
|
|
UINT uGdAskMessage;
|
|
WCHAR Buffer[256];
|
|
DWORD ourProcessID, gdProcessID, winProcessID;
|
|
|
|
static HWND GetWindowFromPoint(POINT pt)
|
|
{
|
|
HWND WndParent,WndChild;
|
|
WndParent = WindowFromPoint(pt);
|
|
if(WndParent == NULL) return WndParent;
|
|
ScreenToClient(WndParent, &pt);
|
|
WndChild = RealChildWindowFromPoint(WndParent, pt);
|
|
if(WndChild == NULL) return WndParent;
|
|
return WndChild;
|
|
};
|
|
|
|
static void SendWordToServer()
|
|
{
|
|
DWORD_PTR SendMsgAnswer;
|
|
DWORD flags;
|
|
LRESULT lr;
|
|
|
|
if( !IsWindow( GlobalData->ServerWND ) )
|
|
return;
|
|
|
|
// Ask for needing to retrieve word - WPARAM = 1
|
|
lr = SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 1, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer);
|
|
if( lr == 0 || SendMsgAnswer == 0) //No answer or no needing
|
|
return;
|
|
|
|
flags = SendMsgAnswer;
|
|
|
|
if (hGetWordLib == 0 && ( flags & GD_FLAG_METHOD_STANDARD ) ) {
|
|
hGetWordLib = LoadLibraryW(GlobalData->LibName);
|
|
if (hGetWordLib) {
|
|
GetWordProc = (GetWordProc_t)GetProcAddress(hGetWordLib, "__gdGetWord");
|
|
}
|
|
else {
|
|
hGetWordLib = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
GlobalData->CurMod.MatchedWord[0] = 0;
|
|
GlobalData->CurMod.WordLen = 0;
|
|
GlobalData->CurMod.BeginPos = 0;
|
|
|
|
if( ( flags & GD_FLAG_METHOD_GD_MESSAGE ) != 0 && uGdAskMessage != 0 ) {
|
|
int n;
|
|
gds.dwSize = sizeof(gds);
|
|
gds.cwData = Buffer;
|
|
gds.dwMaxLength = sizeof(Buffer) / sizeof(Buffer[0]);
|
|
Buffer[0] = 0;
|
|
gds.hWnd = GlobalData->LastWND;
|
|
gds.Pt = GlobalData->LastPt;
|
|
lr = SendMessageTimeout(gds.hWnd, uGdAskMessage, 0, (LPARAM)&gds, SMTO_ABORTIFHUNG, REQUEST_MESSAGE_INTERVAL, &SendMsgAnswer);
|
|
if(lr != 0 && SendMsgAnswer != 0) {
|
|
n = WideCharToMultiByte(CP_UTF8, 0, gds.cwData, lstrlenW(gds.cwData), GlobalData->CurMod.MatchedWord, sizeof(GlobalData->CurMod.MatchedWord) - 1, 0, 0);
|
|
GlobalData->CurMod.MatchedWord[n] = 0;
|
|
GlobalData->CurMod.WordLen = n;
|
|
GlobalData->CurMod.BeginPos = 0;
|
|
if(n > 0) {
|
|
if( IsWindow( GlobalData->ServerWND ) ) {
|
|
#ifdef __WIN64
|
|
GlobalData32->LastWND = HandleToLong( GlobalData->LastWND );
|
|
GlobalData32->CurMod.WordLen = n;
|
|
GlobalData32->CurMod.BeginPos = 0;
|
|
lstrcpyn( GlobalData32->CurMod.MatchedWord, GlobalData->CurMod.MatchedWord, sizeof( GlobalData32->CurMod.MatchedWord ) );
|
|
#endif
|
|
SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 1, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Don't use other methods for GD own windows
|
|
if( winProcessID == gdProcessID )
|
|
return;
|
|
|
|
if( ( flags & GD_FLAG_METHOD_STANDARD ) != 0 && GetWordProc != 0 ) {
|
|
GlobalData->CurMod.WND = GlobalData->LastWND;
|
|
GlobalData->CurMod.Pt = GlobalData->LastPt;
|
|
|
|
LPARAM lparam = GetWordProc(&(GlobalData->CurMod));
|
|
// lparam == 0 - need to reverse RTL text, else don't reverse
|
|
|
|
if (GlobalData->CurMod.WordLen > 0) {
|
|
if( IsWindow( GlobalData->ServerWND ) ) {
|
|
#ifdef __WIN64
|
|
GlobalData32->LastWND = HandleToLong( GlobalData->LastWND );
|
|
GlobalData32->CurMod.WordLen = GlobalData->CurMod.WordLen;
|
|
GlobalData32->CurMod.BeginPos = GlobalData->CurMod.BeginPos;
|
|
lstrcpyn( GlobalData32->CurMod.MatchedWord, GlobalData->CurMod.MatchedWord, sizeof( GlobalData32->CurMod.MatchedWord ) );
|
|
#endif
|
|
SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, lparam, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( ( flags & GD_FLAG_METHOD_IACCESSIBLEEX ) != 0 ) {
|
|
getWordByAccEx( GlobalData->LastPt );
|
|
if (GlobalData->CurMod.WordLen > 0 ) {
|
|
if( IsWindow( GlobalData->ServerWND ) ) {
|
|
#ifdef __WIN64
|
|
GlobalData32->LastWND = HandleToLong( GlobalData->LastWND );
|
|
GlobalData32->CurMod.WordLen = GlobalData->CurMod.WordLen;
|
|
GlobalData32->CurMod.BeginPos = GlobalData->CurMod.BeginPos;
|
|
lstrcpyn( GlobalData32->CurMod.MatchedWord, GlobalData->CurMod.MatchedWord, sizeof( GlobalData32->CurMod.MatchedWord ) );
|
|
#endif
|
|
SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 1, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( ( flags & GD_FLAG_METHOD_UI_AUTOMATION ) != 0 && IsWindow( GlobalData->ServerWND ) ) {
|
|
#ifdef __WIN64
|
|
GlobalData32->CurMod.MatchedWord[0] = 0;
|
|
GlobalData32->CurMod.WordLen = 0;
|
|
GlobalData32->CurMod.BeginPos = 0;
|
|
GlobalData32->LastPt = GlobalData->LastPt;
|
|
#endif
|
|
PostMessage( GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 1 );
|
|
}
|
|
}
|
|
|
|
void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT_PTR nTimerid,DWORD dwTime)
|
|
{
|
|
(void) hWnd;
|
|
(void) nMsg;
|
|
(void) dwTime;
|
|
DWORD wso;
|
|
wso = WaitForSingleObject(hSynhroMutex, 0);
|
|
if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED) {
|
|
KillTimer(0, nTimerid);
|
|
TimerID = 0;
|
|
while( 1 ) {
|
|
POINT curPt;
|
|
HWND targetWnd;
|
|
winProcessID = 0;
|
|
|
|
if( !GetCursorPos( &curPt ) )
|
|
break;
|
|
|
|
if( GlobalData == NULL || GlobalData->LastPt.x != curPt.x || GlobalData->LastPt.y != curPt.y)
|
|
break;
|
|
|
|
if( ( targetWnd = GetWindowFromPoint( curPt ) ) == NULL )
|
|
break;
|
|
|
|
if( GlobalData->LastWND != targetWnd )
|
|
break;
|
|
|
|
GetWindowThreadProcessId( targetWnd, &winProcessID );
|
|
if( winProcessID != ourProcessID ) {
|
|
char className[64];
|
|
if( !GetClassName( targetWnd, className, sizeof(className) ) )
|
|
break;
|
|
if( lstrcmpi( className, "ConsoleWindowClass" ) != 0 )
|
|
break;
|
|
}
|
|
|
|
SendWordToServer();
|
|
|
|
break;
|
|
}
|
|
ReleaseMutex(hSynhroMutex);
|
|
}
|
|
}
|
|
|
|
void HookProc( POINT *ppt )
|
|
{
|
|
HWND WND;
|
|
TCHAR wClassName[64];
|
|
DWORD winProcessID;
|
|
WND = GetWindowFromPoint( *ppt );
|
|
if(WND == NULL) return;
|
|
|
|
if ( !GetClassName(WND, wClassName, sizeof(wClassName) / sizeof(TCHAR)) )
|
|
return;
|
|
|
|
GetWindowThreadProcessId( WND, &winProcessID );
|
|
|
|
if( winProcessID != ourProcessID && lstrcmpi( wClassName, _T("ConsoleWindowClass") ) != 0 )
|
|
return;
|
|
|
|
if(TimerID && ( GlobalData->LastPt.x != ppt->x || GlobalData->LastPt.y != ppt->y ) )
|
|
{
|
|
KillTimer(0, TimerID);
|
|
TimerID = 0;
|
|
}
|
|
|
|
const char* DisableClasses[] = {
|
|
"gdkWindowChild",
|
|
"gdkWindowTemp",
|
|
"Progman",
|
|
"WorkerW",
|
|
};
|
|
int i;
|
|
for (i=0; i<4; i++) {
|
|
if (lstrcmp(wClassName, DisableClasses[i])==0)
|
|
break;
|
|
}
|
|
if (i<4) return;
|
|
|
|
if(GlobalData->LastPt.x != ppt->x || GlobalData->LastPt.y != ppt->y || GlobalData->LastWND != WND )
|
|
{
|
|
GlobalData->LastWND = WND;
|
|
GlobalData->LastPt = *ppt;
|
|
TimerID = SetTimer(0, TimerID, MOUSEOVER_INTERVAL, TimerFunc);
|
|
}
|
|
}
|
|
|
|
#ifdef __WIN64
|
|
|
|
LRESULT CALLBACK GetMessageHookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PMSG pMsg;
|
|
DWORD wso;
|
|
if( nCode == HC_ACTION && wParam == PM_REMOVE )
|
|
{
|
|
pMsg = (PMSG)lParam;
|
|
if( pMsg && ( pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE ) )
|
|
{
|
|
wso = WaitForSingleObject(hSynhroMutex, 0);
|
|
if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED)
|
|
{
|
|
POINT pt;
|
|
pt.x = GET_X_LPARAM( pMsg->lParam );
|
|
pt.y = GET_Y_LPARAM( pMsg->lParam );
|
|
if( pMsg->message == WM_MOUSEMOVE && pMsg->hwnd != NULL )
|
|
ClientToScreen( pMsg->hwnd, &pt );
|
|
HookProc( &pt );
|
|
ReleaseMutex(hSynhroMutex);
|
|
}
|
|
}
|
|
}
|
|
return CallNextHookEx(GlobalData->g_hHook, nCode, wParam, lParam);
|
|
}
|
|
|
|
#else
|
|
|
|
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD wso;
|
|
if ( (nCode == HC_ACTION) && ((wParam == WM_MOUSEMOVE) || (wParam == WM_NCMOUSEMOVE)) ) {
|
|
wso = WaitForSingleObject(hSynhroMutex, 0);
|
|
if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED) {
|
|
HookProc( &(((PMOUSEHOOKSTRUCT)lParam)->pt) );
|
|
ReleaseMutex(hSynhroMutex);
|
|
}
|
|
}
|
|
return CallNextHookEx(GlobalData->g_hHook, nCode, wParam, lParam);
|
|
}
|
|
|
|
#endif
|
|
|
|
DLLIMPORT void ActivateTextOutSpying (int Activate)
|
|
{
|
|
// After call SetWindowsHookEx(), when you move mouse to a application's window,
|
|
// this dll will load into this application automatically. And it is unloaded
|
|
// after call UnhookWindowsHookEx().
|
|
if(GlobalData == NULL) return;
|
|
if (Activate) {
|
|
if (GlobalData->g_hHook != NULL) return;
|
|
#ifdef __WIN64
|
|
GlobalData->g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageHookProc, g_hInstance, 0);
|
|
#else
|
|
GlobalData->g_hHook = SetWindowsHookEx(WH_MOUSE, MouseHookProc, g_hInstance, 0);
|
|
#endif
|
|
}
|
|
else {
|
|
if (GlobalData->g_hHook == NULL) return;
|
|
WaitForSingleObject(hSynhroMutex, 2000);
|
|
UnhookWindowsHookEx(GlobalData->g_hHook);
|
|
if (TimerID) {
|
|
KillTimer(0, TimerID);
|
|
TimerID=0;
|
|
}
|
|
ReleaseMutex(hSynhroMutex);
|
|
GlobalData->g_hHook = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
|
|
DWORD reason /* Reason this function is being called. */ ,
|
|
LPVOID reserved /* Not used. */ )
|
|
{
|
|
(void) reserved;
|
|
switch (reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
g_hInstance = hInst;
|
|
ThTypes_Init();
|
|
#ifdef __WIN64
|
|
if( GlobalData == NULL || GlobalData32 == NULL ) {
|
|
#else
|
|
if( GlobalData == NULL ) {
|
|
#endif
|
|
ThTypes_End();
|
|
return FALSE;
|
|
}
|
|
ourProcessID = GetCurrentProcessId();
|
|
GetWindowThreadProcessId( GlobalData->ServerWND, &gdProcessID );
|
|
if(hSynhroMutex==0) {
|
|
hSynhroMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutSpyMutex");
|
|
if(hSynhroMutex==0) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
uGdAskMessage = RegisterWindowMessage(GD_MESSAGE_NAME);
|
|
FindGetPhysicalCursorPos();
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
// if(hSynhroMutex) WaitForSingleObject(hSynhroMutex, INFINITE);
|
|
if(hSynhroMutex) WaitForSingleObject(hSynhroMutex, 2000);
|
|
if (TimerID) {
|
|
KillTimer(0, TimerID);
|
|
TimerID=0;
|
|
}
|
|
if(hSynhroMutex) {
|
|
ReleaseMutex(hSynhroMutex);
|
|
CloseHandle(hSynhroMutex);
|
|
hSynhroMutex=0;
|
|
}
|
|
{
|
|
MSG msg ;
|
|
while (PeekMessage (&msg, 0, WM_TIMER, WM_TIMER, PM_REMOVE));
|
|
}
|
|
if ( (hGetWordLib != 0) && (hGetWordLib != INVALID_HANDLE_VALUE) ) {
|
|
FreeLibrary(hGetWordLib);
|
|
}
|
|
ThTypes_End();
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
}
|
|
|
|
/* Returns TRUE on success, FALSE on failure */
|
|
return TRUE;
|
|
}
|