I have three abilities: Develop software, Write books and Film reality. All are Free as in freedom.
-- www.huzheng.org

English German French Russian Spanish
Chinese Simplified Chinese Traditional Japanese Korean Portuguese



一切有为法,如梦幻泡影,如露亦如电,应作如是观。



 文章搜索
 
文章分类
您所处的位置: 妖光linux编程首页 --> 文章列表 --> 胡正的技术文章
星际译王屏幕取词源代码
2006.1.18

星际译王(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; 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;	
}
=============================

返回列表




菩萨道linux编程 欢迎来信交流!

大其心,容天下之物。虚其心,助天下之善。平其心,观天下之事。潜其心,明天下之理。定其心,应天下之变。

宇宙中有无数的有情众生,我发愿帮助他们觉醒。我的缺点无穷无尽,我发愿要克服他们。佛法是未知的,我发愿弄懂它。觉醒的道路是可望不可及的,我发愿要去实现它。
www.huzheng.org Copyleft 2000-2017 All rights reserved