TextOutSpy.c
=============================
#include "TextOutSpy.h"
#include "ThTypes.h"
const int MOUSEOVER_INTERVAL = 300;
const int WM_MY_SHOW_TRANSLATION = WM_USER + 300;
HINSTANCE g_hInstance = NULL;
HANDLE hSynhroMutex = 0;
HINSTANCE hGetWordLib = 0;
typedef void (*GetWordProc_t)(TCurrentMode *);
GetWordProc_t GetWordProc = NULL;
static void SendWordToServer()
{
if (hGetWordLib == 0) {
hGetWordLib = LoadLibrary(GlobalData->LibName);
if (hGetWordLib) {
GetWordProc = (GetWordProc_t)GetProcAddress(hGetWordLib, "GetWord");
}
else {
hGetWordLib = (HINSTANCE)-1;
}
}
if (GetWordProc) {
GlobalData->CurMod.WND = GlobalData->LastWND;
GlobalData->CurMod.Pt = GlobalData->LastPt;
GetWordProc(&(GlobalData->CurMod));
if (GlobalData->CurMod.WordLen > 0) {
DWORD SendMsgAnswer;
SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer);
}
}
}
void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime)
{
if (WaitForSingleObject(hSynhroMutex, 0) == WAIT_OBJECT_0) {
if (GlobalData->TimerID) {
if (KillTimer(0, GlobalData->TimerID))
GlobalData->TimerID=0;
}
ReleaseMutex(hSynhroMutex);
}
if ((GlobalData->LastWND!=0)&&(GlobalData->LastWND == WindowFromPoint(GlobalData->LastPt))) {
if (WaitForSingleObject(hSynhroMutex, 0) == WAIT_OBJECT_0) {
SendWordToServer();
ReleaseMutex(hSynhroMutex);
}
}
}
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if ((nCode == HC_ACTION) && ((wParam == WM_MOUSEMOVE) || (wParam == WM_NCMOUSEMOVE))) {
if (WaitForSingleObject(hSynhroMutex, 0) == WAIT_OBJECT_0) {
if (GlobalData->TimerID) {
if (KillTimer(0, GlobalData->TimerID))
GlobalData->TimerID=0;
}
HWND WND = WindowFromPoint(((PMOUSEHOOKSTRUCT)lParam)->pt);
TCHAR wClassName[64];
if (GetClassName(WND, wClassName, sizeof(wClassName) / sizeof(TCHAR))) {
const char* DisableClasses[] = {
"gdkWindowChild",
"gdkWindowTemp",
};
int i;
for (i=0; i<2; i++) {
if (strcmp(wClassName, DisableClasses[i])==0)
break;
}
if (i<2) {
ReleaseMutex(hSynhroMutex);
return CallNextHookEx(GlobalData->g_hHookMouse, nCode, wParam, lParam);
}
}
GlobalData->TimerID = SetTimer(0, 0, MOUSEOVER_INTERVAL, TimerFunc);
GlobalData->LastWND = WND;
GlobalData->LastPt = ((PMOUSEHOOKSTRUCT)lParam)->pt;
ReleaseMutex(hSynhroMutex);
}
}
return CallNextHookEx(GlobalData->g_hHookMouse, nCode, wParam, lParam);
}
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 (Activate) {
if (GlobalData->g_hHookMouse != NULL) return;
GlobalData->g_hHookMouse = SetWindowsHookEx(WH_MOUSE, MouseHookProc, g_hInstance, 0);
}
else {
if (GlobalData->g_hHookMouse == NULL) return;
if (WaitForSingleObject(hSynhroMutex, 0) == WAIT_OBJECT_0) {
if (GlobalData->TimerID) {
if (KillTimer(0, GlobalData->TimerID))
GlobalData->TimerID=0;
}
ReleaseMutex(hSynhroMutex);
}
UnhookWindowsHookEx(GlobalData->g_hHookMouse);
GlobalData->g_hHookMouse = NULL;
}
}
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
g_hInstance = hInst;
hSynhroMutex = CreateMutex(NULL, FALSE, "StarDictTextOutSpyMutex");
ThTypes_Init();
break;
case DLL_PROCESS_DETACH:
WaitForSingleObject(hSynhroMutex, INFINITE);
if (GlobalData->TimerID) {
if (KillTimer(0, GlobalData->TimerID))
GlobalData->TimerID=0;
}
ReleaseMutex(hSynhroMutex);
CloseHandle(hSynhroMutex);
{
MSG msg ;
while (PeekMessage (&msg, 0, WM_TIMER, WM_TIMER, PM_REMOVE)) {}
}
if ((hGetWordLib != 0)&&(hGetWordLib != (HINSTANCE)(-1))) {
FreeLibrary(hGetWordLib);
}
Thtypes_End();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
=============================
TextOutSpy.h
=============================
#ifndef _TextOutSpy_H_
#define _TextOutSpy_H_
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
DLLIMPORT void ActivateTextOutSpying (int Activate);
#endif /* _TextOutSpy_H_ */
=============================
ThTypes.c
=============================
#include "ThTypes.h"
HANDLE MMFHandle = 0;
TGlobalDLLData *GlobalData = NULL;
void ThTypes_Init()
{
if (!MMFHandle)
MMFHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TGlobalDLLData), "StarDictTextOutHookSharedMem");
if (!GlobalData)
GlobalData = MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
}
void Thtypes_End()
{
if (GlobalData) {
UnmapViewOfFile(GlobalData);
GlobalData = NULL;
}
if (MMFHandle) {
CloseHandle(MMFHandle);
MMFHandle = 0;
}
}
=============================
ThTypes.h
=============================
#ifndef _ThTypes_H_
#define _ThTypes_H_
#include <windows.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct TCurrentMode {
HWND WND;
POINT Pt;
int WordLen;
char MatchedWord[256];
int BeginPos;
} TCurrentMode;
typedef struct TGlobalDLLData {
HWND ServerWND;
HHOOK g_hHookMouse;
DWORD TimerID;
HWND LastWND;
POINT LastPt;
TCurrentMode CurMod;
char LibName[256];
} TGlobalDLLData;
extern TGlobalDLLData *GlobalData;
void ThTypes_Init();
void Thtypes_End();
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
=============================
TextOutHook.c
=============================
#include "TextOutHook.h"
#include "GetWord.h"
#include "HookImportFunction.h"
typedef BOOL WINAPI (*TextOutANextHook_t)(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,int cbString);
TextOutANextHook_t TextOutANextHook = NULL;
typedef BOOL WINAPI (*TextOutWNextHook_t)(HDC hdc, int nXStart, int nYStart, LPCWSTR lpszString,int cbString);
TextOutWNextHook_t TextOutWNextHook = NULL;
typedef BOOL WINAPI (*ExtTextOutANextHook_t)(HDC hdc, int nXStart, int nYStart, UINT fuOptions, CONST RECT *lprc, LPCSTR lpszString, UINT cbString, CONST INT *lpDx);
ExtTextOutANextHook_t ExtTextOutANextHook = NULL;
typedef BOOL WINAPI (*ExtTextOutWNextHook_t)(HDC hdc, int nXStart, int nYStart, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpszString, UINT cbString, CONST INT *lpDx);
ExtTextOutWNextHook_t ExtTextOutWNextHook = NULL;
typedef struct TEverythingParams {
HWND WND;
POINT Pt;
int Active;
int WordLen;
int Unicode;
int BeginPos;
char MatchedWordA[256];
wchar_t MatchedWordW[256];
} TEverythingParams;
TEverythingParams *CurParams = NULL;
static void ConvertToMatchedWordA(TEverythingParams *TP)
{
if (TP->Unicode) {
if (TP->WordLen>0) {
int BeginPos = TP->BeginPos;
TP->BeginPos = WideCharToMultiByte(CP_ACP, 0, TP->MatchedWordW, BeginPos, TP->MatchedWordA, sizeof(TP->MatchedWordA)-1, NULL, NULL);
TP->WordLen = WideCharToMultiByte(CP_ACP, 0, TP->MatchedWordW + BeginPos, TP->WordLen - BeginPos, TP->MatchedWordA + TP->BeginPos, sizeof(TP->MatchedWordA)-1 - TP->BeginPos, NULL, NULL);
TP->WordLen += TP->BeginPos;
TP->MatchedWordA[TP->WordLen] = '\0';
} else {
TP->MatchedWordA[0] = '\0';
}
TP->Unicode = FALSE;
} else {
TP->MatchedWordA[TP->WordLen] = '\0';
}
}
static int MyCopyMemory(char *a, const char *b, int len)
{
int count = 0;
int i;
for (i=0; ix) && (p->x<=rec.right) && (rec.top<=p->y) && (p->y<=rec.bottom)) {
ZeroMemory(&info, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = MIIM_TYPE | MIIM_SUBMENU;
info.cch = 256;
info.dwTypeData = malloc(256);
GetMenuItemInfo(menu, i, TRUE, &info);
if (info.cch>0) {
if (info.cch > 255)
CurParams->WordLen = 255;
else
CurParams->WordLen = info.cch;
CurParams->Unicode = FALSE;
CurParams->WordLen = MyCopyMemory(CurParams->MatchedWordA, info.dwTypeData, CurParams->WordLen);
CurParams->BeginPos = 0;
}
free(info.dwTypeData);
}
}
}
static void GetWordTextOutHook (TEverythingParams *TP)
{
CurParams = TP;
ScreenToClient(TP->WND, &(TP->Pt));
if (TP->Pt.y<0) {
char buffer[256];
GetWindowText(TP->WND, buffer, sizeof(buffer)-1);
CurParams->Active = TRUE;
SetWindowText(TP->WND, buffer);
CurParams->Active = FALSE;
HMENU menu = GetMenu(TP->WND);
if (menu) {
ClientToScreen(TP->WND, &(TP->Pt));
IterateThroughItems(TP->WND, menu, &(TP->Pt));
}
}
else {
RECT UpdateRect;
GetClientRect(TP->WND, &UpdateRect);
UpdateRect.top = TP->Pt.y;
UpdateRect.bottom = TP->Pt.y + 1;
CurParams->Active = TRUE;
InvalidateRect(TP->WND, &UpdateRect, FALSE);
UpdateWindow(TP->WND);
CurParams->Active = FALSE;
}
CurParams = NULL;
}
char* ExtractFromEverything(HWND WND, POINT Pt, int *BeginPos)
{
TEverythingParams CParams;
ZeroMemory(&CParams, sizeof(CParams));
CParams.WND = WND;
CParams.Pt = Pt;
GetWordTextOutHook(&CParams);
ConvertToMatchedWordA(&CParams);
*BeginPos = CParams.BeginPos;
return strdup(CParams.MatchedWordA);
}
static void IsInsidePointA(const HDC DC, int X, int Y, LPCSTR Str, int Count)
{
SIZE Size;
if ((Count > 0) && GetTextExtentPoint32A(DC, Str, Count, &Size)) {
DWORD Flags = GetTextAlign(DC);
POINT Pt;
if (Flags & TA_UPDATECP) {
GetCurrentPositionEx(DC, &Pt);
} else {
Pt.x = X;
Pt.y = Y;
}
if (Flags & TA_CENTER) {
Pt.x-=(Size.cx/2);
} else if (Flags & TA_RIGHT) {
Pt.x-=Size.cx;
}
if (Flags & TA_BASELINE) {
TEXTMETRIC tm;
GetTextMetricsA(DC, &tm);
Pt.y-=tm.tmAscent;
} else if (Flags & TA_BOTTOM) {
Pt.y-=Size.cy;
}
LPtoDP(DC, &Pt, 1);
RECT Rect;
Rect.left = Pt.x;
Rect.right = Pt.x + Size.cx;
Rect.top = Pt.y;
Rect.bottom = Pt.y + Size.cy;
if (((Rect.left <= Rect.right) && (CurParams->Pt.x >= Rect.left) && (CurParams->Pt.x <= Rect.right)) ||
((Rect.left > Rect.right) && (CurParams->Pt.x <= Rect.left) && (CurParams->Pt.x >= Rect.right))) {
//if (PtInRect(&Rect, CurParams->Pt)) {
CurParams->Active = !PtInRect(&Rect, CurParams->Pt);
//CurParams->Active = FALSE;
int BegPos = round(abs((CurParams->Pt.x - Rect.left) / (Rect.right - Rect.left)) * (Count - 1));
while ((BegPos < Count - 1) && GetTextExtentPoint32A(DC, Str, BegPos + 1, &Size) && (Size.cx < CurParams->Pt.x - Rect.left))
BegPos++;
while ((BegPos >= 0) && GetTextExtentPoint32A(DC, Str, BegPos + 1, &Size) && (Size.cx > CurParams->Pt.x - Rect.left))
BegPos--;
if (BegPos < Count - 1)
BegPos++;
CurParams->BeginPos = BegPos;
if (Count > 255)
CurParams->WordLen = 255;
else
CurParams->WordLen = Count;
CurParams->Unicode = FALSE;
CopyMemory(CurParams->MatchedWordA, Str, CurParams->WordLen);
}
}
}
static void IsInsidePointW(const HDC DC, int X, int Y, LPCWSTR Str, int Count)
{
SIZE Size;
if ((Count > 0) && GetTextExtentPoint32W(DC, Str, Count, &Size)) {
DWORD Flags = GetTextAlign(DC);
POINT Pt;
if (Flags & TA_UPDATECP) {
GetCurrentPositionEx(DC, &Pt);
} else {
Pt.x = X;
Pt.y = Y;
}
if (Flags & TA_CENTER) {
Pt.x-=(Size.cx/2);
} else if (Flags & TA_RIGHT) {
Pt.x-=Size.cx;
}
if (Flags & TA_BASELINE) {
TEXTMETRICW tm;
GetTextMetricsW(DC, &tm);
Pt.y-=tm.tmAscent;
} else if (Flags & TA_BOTTOM) {
Pt.y-=Size.cy;
}
LPtoDP(DC, &Pt, 1);
RECT Rect;
Rect.left = Pt.x;
Rect.right = Pt.x + Size.cx;
Rect.top = Pt.y;
Rect.bottom = Pt.y + Size.cy;
// Bug: We don't check Pt.y here, as don't call PtInRect() directly, because
// in Title bar, Start Menu, IE, FireFox, Opera etc., the Rect.top and Rect.bottom will be wrong.
// I try to use GetDCOrgEx(DC, &Pt), but they are not normal HDC that Pt.x and Pt.y will equal to 0 in these cases.
// And use GetWindowRect() then get Rect.left and Rect.top is only useful on Title bar.
if (((Rect.left <= Rect.right) && (CurParams->Pt.x >= Rect.left) && (CurParams->Pt.x <= Rect.right)) ||
((Rect.left > Rect.right) && (CurParams->Pt.x <= Rect.left) && (CurParams->Pt.x >= Rect.right))) {
//if (PtInRect(&Rect, CurParams->Pt)) {
CurParams->Active = !PtInRect(&Rect, CurParams->Pt);
//CurParams->Active = FALSE;
int BegPos = round(abs((CurParams->Pt.x - Rect.left) / (Rect.right - Rect.left)) * (Count - 1));
while ((BegPos < Count - 1) && GetTextExtentPoint32W(DC, Str, BegPos + 1, &Size) && (Size.cx < CurParams->Pt.x - Rect.left))
BegPos++;
while ((BegPos >= 0) && GetTextExtentPoint32W(DC, Str, BegPos + 1, &Size) && (Size.cx > CurParams->Pt.x - Rect.left))
BegPos--;
if (BegPos < Count - 1)
BegPos++;
CurParams->BeginPos = BegPos;
if (Count > 255)
CurParams->WordLen = 255;
else
CurParams->WordLen = Count;
CurParams->Unicode = TRUE;
CopyMemory(CurParams->MatchedWordW, Str, CurParams->WordLen * sizeof(wchar_t));
}
}
}
BOOL WINAPI TextOutACallbackProc(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString, int cbString)
{
if (CurParams && CurParams->Active)
IsInsidePointA(hdc, nXStart, nYStart, lpszString, cbString);
return TextOutANextHook(hdc, nXStart, nYStart, lpszString, cbString);
}
BOOL WINAPI TextOutWCallbackProc(HDC hdc, int nXStart, int nYStart, LPCWSTR lpszString, int cbString)
{
if (CurParams && CurParams->Active)
IsInsidePointW(hdc, nXStart, nYStart, lpszString, cbString);
return TextOutWNextHook(hdc, nXStart, nYStart, lpszString, cbString);
}
BOOL WINAPI ExtTextOutACallbackProc(HDC hdc, int nXStart, int nYStart, UINT fuOptions, CONST RECT *lprc, LPCSTR lpszString, UINT cbString, CONST INT *lpDx)
{
if (CurParams && CurParams->Active)
IsInsidePointA(hdc, nXStart, nYStart, lpszString, cbString);
return ExtTextOutANextHook(hdc, nXStart, nYStart, fuOptions, lprc, lpszString, cbString, lpDx);
}
BOOL WINAPI ExtTextOutWCallbackProc(HDC hdc, int nXStart, int nYStart, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpszString, UINT cbString, CONST INT *lpDx)
{
if (CurParams && CurParams->Active)
IsInsidePointW(hdc, nXStart, nYStart, lpszString, cbString);
return ExtTextOutWNextHook(hdc, nXStart, nYStart, fuOptions, lprc, lpszString, cbString, lpDx);
}
static void InstallTextOutHooks()
{
HookAPI("gdi32.dll", "TextOutA", (PROC)TextOutACallbackProc, (PROC*)&TextOutANextHook);
HookAPI("gdi32.dll", "TextOutW", (PROC)TextOutWCallbackProc, (PROC*)&TextOutWNextHook);
HookAPI("gdi32.dll", "ExtTextOutA", (PROC)ExtTextOutACallbackProc, (PROC*)&ExtTextOutANextHook);
HookAPI("gdi32.dll", "ExtTextOutW", (PROC)ExtTextOutWCallbackProc, (PROC*)&ExtTextOutWNextHook);
}
static void UninstallTextOutHooks()
{
if (TextOutANextHook)
HookAPI("gdi32.dll", "TextOutA", (PROC)TextOutANextHook, NULL);
if (TextOutWNextHook)
HookAPI("gdi32.dll", "TextOutW", (PROC)TextOutWNextHook, NULL);
if (ExtTextOutANextHook)
HookAPI("gdi32.dll", "ExtTextOutA", (PROC)ExtTextOutANextHook, NULL);
if (ExtTextOutWNextHook)
HookAPI("gdi32.dll", "ExtTextOutW", (PROC)ExtTextOutWNextHook, NULL);
}
DLLIMPORT void GetWord (TCurrentMode *P)
{
TCHAR wClassName[64];
if (GetClassName(P->WND, wClassName, sizeof(wClassName) / sizeof(TCHAR))==0)
wClassName[0] = '\0';
TKnownWndClass WndClass = GetWindowType(P->WND, wClassName);
char *p = TryGetWordFromAnyWindow(WndClass, P->WND, P->Pt, &(P->BeginPos));
if (p) {
P->WordLen = strlen(p);
strcpy(P->MatchedWord, p);
free(p);
} else {
P->WordLen = 0;
}
}
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
//ThTypes_Init();
InstallTextOutHooks();
break;
case DLL_PROCESS_DETACH:
UninstallTextOutHooks();
//Thtypes_End();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
=============================
TextOutHook.h
=============================
#ifndef _TextOutHook_H_
#define _TextOutHook_H_
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
#include "ThTypes.h"
char* ExtractFromEverything(HWND WND, POINT Pt, int *BeginPos);
DLLIMPORT void GetWord (TCurrentMode *P);
#endif /* _TextOutHook_H_ */
=============================
GetWord.c
=============================
#include "GetWord.h"
#include "TextOutHook.h"
TKnownWndClass GetWindowType(HWND WND, const char* WNDClass)
{
const char* StrKnownClasses[] = {
"RICHEDIT20A",
"RICHEDIT20W",
"RICHEDIT",
"EDIT",
"INTERNET EXPLORER_SERVER",
"CONSOLEWINDOWCLASS", // NT
"TTYGRAB", // 9x
};
TKnownWndClass KnownClasses[] = {
kwcRichEdit,
kwcRichEdit,
kwcRichEdit,
kwcMultiLineEdit,
kwcInternetExplorer_Server,
kwcConsole,
kwcConsole,
};
int i;
for (i=0; i<7; i++) {
if (strcasecmp(WNDClass, StrKnownClasses[i])==0)
break;
}
if (i<7) {
if (KnownClasses[i] == kwcMultiLineEdit) {
if ((GetWindowLong(WND, GWL_STYLE) & ES_MULTILINE) == 0)
return kwcSingleLineEdit;
}
return KnownClasses[i];
} else
return kwcUnknown;
}
static char* ExtractWordFromRichEditPos(HWND WND, POINT Pt, int *BeginPos)
{
return ExtractFromEverything(WND, Pt, BeginPos);
}
/*
typedef struct TEditParams {
HWND WND;
POINT Pt;
char Buffer[256];
} TEditParams;
static int ExtractWordFromEditPosPack(TEditParams *params)
{
int Result = 0;
int BegPos;
BegPos = SendMessage(params->WND, EM_CHARFROMPOS, 0, params->Pt.x | params->Pt.y << 16);
if (BegPos == -1)
return Result;
int MaxLength;
MaxLength = SendMessage(params->WND, EM_LINELENGTH, BegPos & 0xFFFF, 0);
if (MaxLength <= 0)
return Result;
char *Buf;
Buf = GlobalAlloc(GMEM_FIXED, MaxLength + 1);
if (Buf) {
*Buf = MaxLength;
MaxLength = SendMessage(params->WND, EM_GETLINE, BegPos >> 16, (int)Buf);
Buf[MaxLength] = '\0';
BegPos = (BegPos & 0xFFFF) - SendMessage(params->WND, EM_LINEINDEX, BegPos >> 16, 0) - 1;
int EndPos;
EndPos = BegPos;
while ((BegPos >= 0) && IsCharAlpha(Buf[BegPos]))
BegPos--;
while ((EndPos < MaxLength) && IsCharAlpha(Buf[EndPos]))
EndPos++;
MaxLength = EndPos - BegPos - 1;
if (MaxLength >= 0) {
if (255 >= MaxLength) {
Buf[EndPos] = '\0';
lstrcpy(params->Buffer, Buf + BegPos + 1);
Result = MaxLength;
}
}
GlobalFree(Buf);
}
return Result;
}
*/
static char* ExtractWordFromEditPos(HWND hEdit, POINT Pt, int *BeginPos)
{
return ExtractFromEverything(hEdit, Pt, BeginPos);
/* TEditParams *TP;
TP = malloc(sizeof(TEditParams));
TP->WND = hEdit;
TP->Pt = Pt;
TP->Buffer[0] = '\0';
ScreenToClient(hEdit, &(TP->Pt));
int MaxLength;
MaxLength = ExtractWordFromEditPosPack(TP);
char *Result;
if (MaxLength>0) {
Result = strdup(TP->Buffer);
} else {
Result = NULL;
}
free(TP);
return Result;
*/
}
static char* ExtractWordFromIE(HWND WND, POINT Pt, int *BeginPos)
{
return ExtractFromEverything(WND, Pt, BeginPos);
}
typedef struct TConsoleParams {
HWND WND;
POINT Pt;
RECT ClientRect;
char Buffer[256];
} TConsoleParams;
static int GetWordFromConsolePack(TConsoleParams *params)
{
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hStdOut, &csbi)) {
COORD CurPos;
CurPos.X = csbi.srWindow.Left + params->Pt.x * (csbi.srWindow.Right - csbi.srWindow.Left + 1) / params->ClientRect.right;
CurPos.Y = csbi.srWindow.Top + params->Pt.y * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1) / params->ClientRect.bottom;
if ((CurPos.X >= 0) && (CurPos.X <= csbi.dwSize.X - 1) && (CurPos.Y >= 0) && (CurPos.Y <= csbi.dwSize.Y - 1)) {
int BegPos;
BegPos = CurPos.X;
CurPos.X = 0;
char *Buf = GlobalAlloc(GMEM_FIXED, csbi.dwSize.X + 1);
if (Buf) {
DWORD ActualRead;
if ((ReadConsoleOutputCharacter(hStdOut, Buf, csbi.dwSize.X, CurPos, &ActualRead)) && (ActualRead == csbi.dwSize.X)) {
OemToCharBuff(Buf, Buf, csbi.dwSize.X);
int WordLen;
if (csbi.dwSize.X > 255)
WordLen = 255;
else
WordLen = csbi.dwSize.X;
strncpy(params->Buffer, Buf, WordLen);
GlobalFree(Buf);
return WordLen;
}
}
}
}
}
return 0;
}
static void GetWordFromConsolePackEnd() {}
static BOOL RemoteExecute(HANDLE hProcess, void *RemoteThread, DWORD RemoteSize, void *Data, int DataSize, DWORD *dwReturn)
{
void *pRemoteThread = VirtualAllocEx(hProcess, NULL, RemoteSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pRemoteThread)
return FALSE;
if (!WriteProcessMemory(hProcess, pRemoteThread, RemoteThread, RemoteSize, 0)) {
VirtualFreeEx(hProcess, pRemoteThread, RemoteSize, MEM_RELEASE);
return FALSE;
}
void *pData = VirtualAllocEx(hProcess, NULL, DataSize, MEM_COMMIT, PAGE_READWRITE);
if (!pData) {
VirtualFreeEx(hProcess, pRemoteThread, RemoteSize, MEM_RELEASE);
return FALSE;
}
if (!WriteProcessMemory(hProcess, pData, Data, DataSize, 0)) {
VirtualFreeEx(hProcess, pRemoteThread, RemoteSize, MEM_RELEASE);
VirtualFreeEx(hProcess, pData, DataSize, MEM_RELEASE);
return FALSE;
}
// Bug: I don't know why the next line will fail in Windows XP, so get word from cmd.exe can't work presently.
HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pRemoteThread, pData, 0, 0);
WaitForSingleObject(hThread, INFINITE);
GetExitCodeThread(hThread, dwReturn);
ReadProcessMemory(hProcess, pData, Data, DataSize, 0);
VirtualFreeEx(hProcess, pRemoteThread, RemoteSize, MEM_RELEASE);
VirtualFreeEx(hProcess, pData, DataSize, MEM_RELEASE);
if (hThread) {
CloseHandle(hThread);
return TRUE;
} else {
return FALSE;
}
}
static char* GetWordFromConsole(HWND WND, POINT Pt, int *BeginPos)
{
TConsoleParams *TP;
TP = malloc(sizeof(TConsoleParams));
TP->WND = WND;
TP->Pt = Pt;
ScreenToClient(WND, &(TP->Pt));
GetClientRect(WND, &(TP->ClientRect));
DWORD pid;
GetWindowThreadProcessId(GetParent(WND), &pid);
DWORD MaxWordSize;
if (pid != GetCurrentProcessId()) {
// The next line will fail in Win2k, but OK in Windows XP.
HANDLE ph = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid);
if (ph) {
if (!RemoteExecute(ph, GetWordFromConsolePack, (DWORD)GetWordFromConsolePackEnd - (DWORD)GetWordFromConsolePack, TP, sizeof(TConsoleParams), &MaxWordSize))
MaxWordSize = 0;
CloseHandle(ph);
}
} else {
MaxWordSize = GetWordFromConsolePack(TP);
}
char *Result;
if (MaxWordSize > 0) {
Result = strdup(TP->Buffer);
} else {
Result = NULL;
}
free(TP);
return Result;
}
char* TryGetWordFromAnyWindow(TKnownWndClass WndType, HWND WND, POINT Pt, int *BeginPos)
{
typedef char* (*GetWordFunction_t)(HWND, POINT, int*);
const GetWordFunction_t GetWordFunction[]= {
ExtractFromEverything,
ExtractWordFromRichEditPos,
ExtractWordFromEditPos,
ExtractWordFromEditPos,
ExtractWordFromIE,
GetWordFromConsole,
};
return GetWordFunction[WndType](WND, Pt, BeginPos);
}
=============================
GetWord.h
=============================
#ifndef _GetWord_H_
#define _GetWord_H_
#include <windows.h>
typedef enum TKnownWndClass {
kwcUnknown,
kwcRichEdit,
kwcMultiLineEdit,
kwcSingleLineEdit,
kwcInternetExplorer_Server,
kwcConsole,
} TKnownWndClass;
TKnownWndClass GetWindowType(HWND WND, const char* WNDClass);
char* TryGetWordFromAnyWindow(TKnownWndClass WndType, HWND WND, POINT Pt, int *BeginPos);
#endif
=============================
HookImportFunction.c
=============================
#include "HookImportFunction.h"
#include<tlhelp32.h>
// These code come from: http://dev.csdn.net/article/2/2786.shtm
// I fixed a bug in it and improved it to hook all the modules of a program.
#define MakePtr(cast, ptr, AddValue) (cast)((DWORD)(ptr)+(DWORD)(AddValue))
static PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportModule)
{
if ((szImportModule == NULL) || (hModule == NULL))
return NULL;
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;
if (IsBadReadPtr(pDOSHeader, sizeof(IMAGE_DOS_HEADER)) || (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)) {
return NULL;
}
PIMAGE_NT_HEADERS pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew);
if (IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) || (pNTHeader->Signature != IMAGE_NT_SIGNATURE))
return NULL;
if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)
return NULL;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDOSHeader, pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (pImportDesc->Name) {
PSTR szCurrMod = MakePtr(PSTR, pDOSHeader, pImportDesc->Name);
if (stricmp(szCurrMod, szImportModule) == 0)
break;
pImportDesc++;
}
if (pImportDesc->Name == (DWORD)0)
return NULL;
return pImportDesc;
}
static BOOL IsNT()
{
OSVERSIONINFO stOSVI;
memset(&stOSVI, 0, sizeof(OSVERSIONINFO));
stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
BOOL bRet = GetVersionEx(&stOSVI);
if (FALSE == bRet) return FALSE;
return (VER_PLATFORM_WIN32_NT == stOSVI.dwPlatformId);
}
static BOOL HookImportFunction(HMODULE hModule, LPCSTR szImportModule, LPCSTR szFunc, PROC paHookFuncs, PROC* paOrigFuncs)
{
if (!IsNT() && ((DWORD)hModule >= 0x80000000))
return FALSE;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = GetNamedImportDescriptor(hModule, szImportModule);
if (pImportDesc == NULL)
return FALSE;
PIMAGE_THUNK_DATA pOrigThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->OriginalFirstThunk);
PIMAGE_THUNK_DATA pRealThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->FirstThunk);
while (pOrigThunk->u1.Function) {
if (IMAGE_ORDINAL_FLAG != (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) {
PIMAGE_IMPORT_BY_NAME pByName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule, pOrigThunk->u1.AddressOfData);
// When hook EditPlus, read pByName->Name[0] will case this dll terminate, so call IsBadReadPtr() here.
if (IsBadReadPtr(pByName, sizeof(IMAGE_IMPORT_BY_NAME))) {
pOrigThunk++;
pRealThunk++;
continue;
}
if ('\0' == pByName->Name[0]) {
pOrigThunk++;
pRealThunk++;
continue;
}
BOOL bDoHook = FALSE;
if ((szFunc[0] == pByName->Name[0]) && (strcmpi(szFunc, (char*)pByName->Name) == 0)) {
if (paHookFuncs)
bDoHook = TRUE;
}
if (bDoHook) {
MEMORY_BASIC_INFORMATION mbi_thunk;
VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect);
if (paOrigFuncs)
*paOrigFuncs = (PROC)pRealThunk->u1.Function;
pRealThunk->u1.Function = (DWORD)paHookFuncs;
DWORD dwOldProtect;
VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect);
return TRUE;
}
}
pOrigThunk++;
pRealThunk++;
}
return FALSE;
}
BOOL HookAPI(LPCSTR szImportModule, LPCSTR szFunc, PROC paHookFuncs, PROC* paOrigFuncs)
{
if ((szImportModule == NULL) || (szFunc == NULL)) {
return FALSE;
}
HANDLE hSnapshot;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,0);
MODULEENTRY32 me = {sizeof(MODULEENTRY32)};
BOOL bOk = Module32First(hSnapshot,&me);
while (bOk) {
HookImportFunction(me.hModule, szImportModule, szFunc, paHookFuncs, paOrigFuncs);
bOk = Module32Next(hSnapshot,&me);
}
return TRUE;
}
=============================
HookImportFunction.h
=============================
#ifndef _HookImportFunction_H_
#define _HookImportFunction_H_
#include <windows.h>
BOOL HookAPI(LPCSTR szImportModule, LPCSTR szFunc, PROC paHookFuncs, PROC* paOrigFuncs);
#endif
=============================
mouseover.c
=============================
/*
* This file part of StarDict - A international dictionary for GNOME.
* http://stardict.sourceforge.net
*
* Copyright (C) 2006 Hu Zheng
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "../stardict.h"
#include "../conf.h"
#include <glib/gi18n.h>
#include "mouseover.h"
#include "ThTypes.h"
// StarDict's Mouseover feature get the example delphi source code from Mueller Electronic Dicionary.
// Homepage: http://vertal1.narod.ru/mueldic.html E-mail: svv_soft@mail.ru
const int WM_MY_SHOW_TRANSLATION = WM_USER + 300;
void Mouseover::NeedSpyDll()
{
if (fSpyDLL == 0) {
fSpyDLL = LoadLibrary((gStarDictDataDir+G_DIR_SEPARATOR+"TextOutSpy.dll").c_str());
if (fSpyDLL==0) {
fSpyDLL = (HINSTANCE)-1;
} else {
ActivateSpy_func = (ActivateSpy_func_t)GetProcAddress(fSpyDLL, "ActivateTextOutSpying");
}
}
}
HWND Mouseover::Create_hiddenwin()
{
WNDCLASSEX wcex;
TCHAR wname[32];
strcpy(wname, "StarDictMouseover");
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = (WNDPROC)mouseover_mainmsg_handler;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = stardictexe_hInstance;
wcex.hIcon = NULL;
wcex.hCursor = NULL,
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = wname;
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
// Create the window
return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, stardictexe_hInstance, 0));
}
void Mouseover::ShowTranslation()
{
if (bIsPureEnglish(GlobalData->CurMod.MatchedWord)) {
gpAppFrame->SmartLookupToFloat(GlobalData->CurMod.MatchedWord, GlobalData->CurMod.BeginPos, true);
} else {
char *str1 = g_locale_to_utf8(GlobalData->CurMod.MatchedWord, GlobalData->CurMod.BeginPos, NULL, NULL, NULL);
char *str2 = g_locale_to_utf8(GlobalData->CurMod.MatchedWord + GlobalData->CurMod.BeginPos, GlobalData->CurMod.WordLen - GlobalData->CurMod.BeginPos, NULL, NULL, NULL);
GlobalData->CurMod.BeginPos = strlen(str1);
char *str = g_strdup_printf("%s%s", str1, str2);
g_free(str1);
g_free(str2);
gpAppFrame->SmartLookupToFloat(str, GlobalData->CurMod.BeginPos, true);
g_free(str);
}
}
LRESULT CALLBACK Mouseover::mouseover_mainmsg_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg) {
case WM_MY_SHOW_TRANSLATION:
ShowTranslation();
break;
default:
/*nothing*/;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
Mouseover::Mouseover()
{
fSpyDLL = 0;
ActivateSpy_func = NULL;
}
void Mouseover::Init()
{
ThTypes_Init();
ZeroMemory(GlobalData, sizeof(TGlobalDLLData));
strcpy(GlobalData->LibName, (gStarDictDataDir+G_DIR_SEPARATOR+"TextOutHook.dll").c_str());
GlobalData->ServerWND = Create_hiddenwin();
}
void Mouseover::End()
{
if ((fSpyDLL!=0)&&(fSpyDLL!=(HINSTANCE)-1)) {
stop();
FreeLibrary(fSpyDLL);
}
DestroyWindow(GlobalData->ServerWND);
Thtypes_End();
}
void Mouseover::start()
{
NeedSpyDll();
if (ActivateSpy_func)
ActivateSpy_func(true);
}
void Mouseover::stop()
{
if (ActivateSpy_func)
ActivateSpy_func(false);
}
=============================
mouseover.h
=============================
#ifndef __SD_MOUSEOVER_H__
#define __SD_MOUSEOVER_H__
#include <windows.h>
class Mouseover
{
private:
typedef void (*ActivateSpy_func_t)(bool);
ActivateSpy_func_t ActivateSpy_func;
HINSTANCE fSpyDLL;
void NeedSpyDll();
HWND Create_hiddenwin();
static void ShowTranslation();
static LRESULT CALLBACK mouseover_mainmsg_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
public:
Mouseover();
void Init();
void End();
void start();
void stop();
};
#endif
=============================
stardict.cpp
=============================
bool AppCore::SmartLookupToFloat(const gchar* sWord, int BeginPos, bool bShowIfNotFound)
{
if (sWord==NULL || sWord[0]=='\0')
return true;
char *SearchWord = g_strdup(sWord);
char *P1 = SearchWord + BeginPos;
P1 = g_utf8_next_char(P1);
while (*P1 && !g_unichar_isspace(g_utf8_get_char(P1)))
P1 = g_utf8_next_char(P1);
*P1='\0';
P1 = SearchWord + BeginPos;
if (BeginPos) {
if (g_unichar_isspace(g_utf8_get_char(P1)))
P1 = g_utf8_prev_char(P1);
while (P1>SearchWord && !g_unichar_isspace(g_utf8_get_char(g_utf8_prev_char(P1))))
P1 = g_utf8_prev_char(P1);
}
const gchar **ppWord = (const gchar **)g_malloc(sizeof(gchar *) * oLibs.ndicts());
gchar **ppWordData = (gchar **)g_malloc(sizeof(gchar *) * oLibs.ndicts());
int SearchTimes = 2;
while (SearchTimes) {
glong iIndex;
bool bFound = false;
for (int iLib=0;iLibSearchWord && isascii(*(P3-1)) && g_ascii_isupper(*(P3-1)))
P3--;
} else if (g_ascii_islower(*P2)){
P2++;
while (*P2 && g_ascii_islower(*P2))
P2++;
}
if (*P2) {
*P2='\0';
} else {
if (P3==P1)
break;
}
P1=P3;
} else {
while (P3>SearchWord && isascii(*(P3-1)) && g_ascii_isupper(*(P3-1)))
P3--;
if (P3==P1)
break;
}
} else if (g_ascii_islower(*P2)) {
char *P3 = SearchWord + BeginPos;
while (P3>SearchWord && isascii(*(P3-1)) && g_ascii_islower(*(P3-1)))
P3--;
if (P3>SearchWord && isascii(*(P3-1)) && g_ascii_isupper(*(P3-1)))
P3--;
P2++;
while (*P2 && g_ascii_islower(*P2))
P2++;
if (*P2) {
*P2='\0';
} else {
if (P3==P1)
break;
}
P1=P3;
} else {
break;
}
} else {
if (P1==SearchWord + BeginPos) {
char *EndPointer=P1+strlen(P1);
EndPointer = g_utf8_prev_char(EndPointer);
if (EndPointer!=P1) {
*EndPointer='\0';
SearchTimes = 2;
}
else {
break;
}
} else {
P1 = SearchWord + BeginPos;
SearchTimes = 2;
}
}
}
g_free(ppWord);
g_free(ppWordData);
// not found
if (bShowIfNotFound) {
ShowNotFoundToFloatWin(P1,_(""), false);
oTopWin.InsertHisList(P1); //really need?
}
g_free(SearchWord);
return false;
}
=========================