星际译王屏幕取词源代码

来源:互联网 发布:淘宝店铺名片设计网站 编辑:程序博客网 时间:2024/05/01 15:13
http://reciteword.cosoft.org.cn/yaoguang/showarticle.php?category=myarticle&&docpage=0&&newsid=24

星际译王(StarDict)的2.4.6版新增加了Windows下屏幕取词的功能。在开发这个功能时参考了Mueller Electronic Dicionary(一个用Delphi编的词典软件)的源代码,以及网上的文档。开发语言是C,用Dev-cpp编译。
这个功能现在还不太完善,目前有以下问题:
1. 在Win2k系统下,对桌面上的英文取词时为乱码。Windows XP则没有问题。
2. 在标题栏,开始菜单及IE, FireFox, Opear等软件上取词时,获取的Y坐标值不正确。见源码包里的src/win32/TextOutHook.c的IsInsidePointW()里的注释。
3. cmd.exe(命令提示符)无法取词。见源码包里的src/win32/GetWord.c的RemoteExecute()里的注释。
4. Adobe Reader无法取词。可能要像金山词霸那样编个Adobe Reader的插件。
希望高手能帮忙解决。
现在把完整源代码贴到这里:

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; i<=p->x) && (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;   
}
=============================
原创粉丝点击