关于全屏取色功能,网络上有很多关于如何实现的文章,但这些文章所讲的都是同出一辙,都是利用几个API来实现,其实现步骤如下:1、用API GetCursorPos来获得当前鼠标的位置坐标;2、用API GetDC来获取桌面DC;3、用API GetPixel获得当前鼠标所处位置的颜色值。
以上就是用API实现全屏取色的过程。读者从中可以看出其实现很简单,但用此种方法实现过全屏取色功能的读者都知道,其实这种方法用起来不太方便。首先,这种方法在进行取色时必须一直按着鼠标,直到取得满意的颜色才松手。如果取色时间过长,对于用户来说,这确实不是一件好事。其次,在完成取色而松开鼠标时,往往容易造成鼠标的轻微移动而致使所取得的颜色发生改变,从而不得不重新进行一次全屏取色。对于程序员来说,其程序的目的就是让用户尽可能的方便,显然这种全屏取色方法不是程序员所期望的。
针对此种方法的局限性,笔者认为用Windows钩子中的鼠标钩子来实现全屏取色更为方便。采用鼠标钩子进行全屏取色的步骤如下:1、主程序向实现鼠标钩子的DLL发送全屏取色指令;2、鼠标钩子DLL获取全屏取色指令,并执行全屏取色的相应指令;3、用户移动鼠标时,鼠标钩子DLL将当前鼠标位置的颜色值发送给主程序;4、主程序接收鼠标钩子DLL发送过来的颜色值并进行显示。
关于Windows钩子的使用,网络上有很多资料,读者可以上网搜索一下,我这里就不多重述了。应用程序与DLL共享数据有多种方法,比如利用内存映像文件共享数据、用消息WM_COPYDATA来传送数据等。考虑到程序的简洁性与方便性,我在这里使用Windows消息WM_COPYDATA来进行数据的传送,关于Windows消息WM_COPYDAT的相关说明可参考微软的MSDN,我这里不再重述。
为了让读者对其实现有更深入的理解,以下给出了全部源代码,这些源代码在WindowsXP下的Borland C++ Builder 6.0英文版编译通过,其生成的文件有两个:Test.exe为主程序,MouseHook.dll为实现全屏取色的鼠标钩子DLL。
//-------------------------MouseHook.DLL的源程序------------------------------ #include <vcl.h> #include <windows.h> #pragma hdrstop //------------------------------------------------------------------------- // Important note about DLL memory management when your DLL uses the // static version of the RunTime Library: // // If your DLL exports any functions that pass String objects (or structs/ // classes containing nested Strings) as parameter or function results, // you will need to add the library MEMMGR.LIB to both the DLL project and // any other projects that use the DLL. You will also need to use MEMMGR.LIB // if any other projects which use the DLL will be performing new or delete // operations on any non-TObject-derived classes which are exported from the // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, // the file BORLNDMM.DLL should be deployed along with your DLL. // // To avoid using BORLNDMM.DLL, pass string information using "char *" or // ShortString parameters. // // If your DLL uses the dynamic version of the RTL, you do not need to // explicitly add MEMMGR.LIB as this will be done implicitly for you //--------------------------------------------------------------------------- #pragma argsused HHOOK MouseHook = NULL; // 钩子句柄 HINSTANCE DLLInst = NULL; // DLL实例 HINSTANCE AppInst = NULL; // 调用DLL应用程序的实例 COPYDATASTRUCT *CopyData; // 消息WM_COPYDATA所传送的数据 typedef struct // 颜色值结构 { unsigned int Red; unsigned int Green; unsigned int Blue; }COLORINFO, *LPCOLORINFO; COLORINFO ColorInfo; //------------------------------------------------------------------------- int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { if(DLLInst == NULL) DLLInst = hinst; if(reason == DLL_PROCESS_ATTACH) // 装载DLL { CopyData = new COPYDATASTRUCT; AppInst = FindWindow("TForm1", "Form1"); // 寻找调用此DLL的目标窗体 } else if(reason == DLL_PROCESS_DETACH) // 卸载DLL { delete CopyData; } return 1; } extern "C" __declspec(dllexport) void __stdcall DisableMouseHook(void) { if(MouseHook != NULL) { UnhookWindowsHookEx(MouseHook); // 卸掉钩子 MouseHook = NULL; } } //------------------------------------------------------------------------- LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode == HC_ACTION) { if(wParam == WM_MOUSEMOVE) // 如果移动鼠标则进行处理 { MOUSEHOOKSTRUCT *l=(MOUSEHOOKSTRUCT *)lParam; // 获得鼠标信息 TColor Color = (TColor)GetPixel(GetDC(0), l->pt.x, l->pt.y); // 获得当前位置的颜色值 ColorInfo.Red = GetRValue(Color); // 分别得到当前位置颜色//值的R、G、B值 ColorInfo.Green = GetGValue(Color); ColorInfo.Blue = GetBValue(Color); CopyData->dwData = 1; // 本程序中用来标志当前//取色的状态,0表示停止取色,1表示进行取色 CopyData->cbData = sizeof(COLORINFO); // 所传送数据的大小 CopyData->lpData = &ColorInfo; // 所传送的数据 SendMessage(AppInst, WM_COPYDATA, (WPARAM)NULL, (LPARAM)CopyData); // 发送数据 } else if(wParam == WM_LBUTTONUP) // 如果按下鼠标左键则停止钩子 { CopyData->dwData = 0; // 停止取色标志 CopyData->cbData = sizeof(CopyData->dwData); CopyData->lpData = NULL; SendMessage(AppInst, WM_COPYDATA, (WPARAM)NULL, (LPARAM)CopyData); } } return(CallNextHookEx(MouseHook, nCode, wParam, lParam)); } //-------------------------------------------------------------------------extern "C" __declspec(dllexport) void __stdcall EnableMouseHook(void) { if(MouseHook == NULL) { MouseHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseHookProc, DLLInst, 0); // 安装钩子 } } //------------------------------------------------------------------------- 以下是主程序的头文件源代码: //----------------------------TestUnit.H源代码---------------------------- #ifndef TestUnitH #define TestUnitH //--------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <Buttons.hpp> #include <ExtCtrls.hpp> //--------------------------------------------------------------------- typedef void (__stdcall *ENABLEMOUSEHOOK)(void); // 对MouseHook.dll //里导出函数的声明 typedef void (__stdcall *DISABLEMOUSEHOOK)(void); typedef struct // 颜色值结构 { unsigned int Red; unsigned int Green; unsigned int Blue; }COLORINFO, *LPCOLORINFO; //--------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TButton *Button2; TGroupBox *GroupBox1; TLabeledEdit *LabeledEdit1; TLabeledEdit *LabeledEdit2; TLabeledEdit *LabeledEdit3; TPanel *Panel1; TButton *Button1; void __fastcall Button2Click(TObject *Sender); void __fastcall Button1Click(TObject *Sender); private: // User declarations LPCOLORINFO ColorInfo; HINSTANCE DLLInst; // 要调用的DLL实例 ENABLEMOUSEHOOK EnableMouseHook; // 允许鼠标钩子的函数 DISABLEMOUSEHOOK DisableMouseHook; // 停止鼠标钩子的函数 public: // User declarations void __fastcall OnWMCopyData(TMessage &Message); // 处理WM_COPYDATA消息的函数 BEGIN_MESSAGE_MAP // 对WM_COPYDATA消息进行映射 VCL_MESSAGE_HANDLER(WM_COPYDATA, TMessage, OnWMCopyData); END_MESSAGE_MAP(TForm); __fastcall TForm1(TComponent* Owner); __fastcall ~TForm1(void); }; //--------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------- #endif 以下是主程序的具体实现源代码: //----------------------TestUnit.cpp----------------------------------- #include <vcl.h> #pragma hdrstop #include "TestUnit.h" //--------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; COPYDATASTRUCT *CopyData;; //--------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { DLLInst = LoadLibrary("MouseHook.dll"); // 装入鼠标钩子DLL if(!DLLInst) PostQuitMessage(0); EnableMouseHook = (ENABLEMOUSEHOOK)GetProcAddress(DLLInst, "EnableMouseHook"); DisableMouseHook = (DISABLEMOUSEHOOK)GetProcAddress(DLLInst, "DisableMouseHook"); if(!(EnableMouseHook && DisableMouseHook)) PostQuitMessage(0); CopyData = new COPYDATASTRUCT; } //--------------------------------------------------------------------- __fastcall TForm1::~TForm1() { FreeLibrary(DLLInst); // 卸载鼠标钩子DLL delete CopyData; } //--------------------------------------------------------------------- void __fastcall TForm1::OnWMCopyData(TMessage &Message) { CopyData = (COPYDATASTRUCT *)Message.LParam; // 获得数据 if(CopyData->dwData == 0) // 停止鼠标钩子 { DisableMouseHook(); } else { ColorInfo = (LPCOLORINFO)CopyData->lpData; // 获得颜色值信息 LabeledEdit1->Text = ColorInfo->Red; // 显示相应的颜色信息 LabeledEdit2->Text = ColorInfo->Green; LabeledEdit3->Text = ColorInfo->Blue; Panel1->Color = (TColor)RGB(ColorInfo->Red, ColorInfo->Green, ColorInfo->Blue); } } //--------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { EnableMouseHook(); // 开始鼠标钩子 } //--------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { PostQuitMessage(0); // 退出程序 } //--------------------------------------------------------------------- 以上就是利用Windows鼠标钩子进行全屏取色的具体实现,读者在编译的时候,一定要记得把以下的两个编译开关关掉,否则编译的程序在没有装Borland C++ Builder或Borland Dephi的机子上不能运行:1、打开菜单Project下的Options窗体,把Packages页里的Build with runtime packages选项前的钩去掉;2、还是在Project下的Options窗体,把Linker页里的Use dynamic RTL选项前的钩去掉。 如果读者有兴趣,还可在这基础上进一步改进该程序,比如让程序进行全屏取色时,隐藏主程序的窗体,而出来一个显示颜色值的小框框(就像网络蚂蚁的显示下载框一样)。在具体实现的过程中,作者发现这么一个问题:当鼠标移到调用钩子DLL的主程序的标题栏时,得不到鼠标所处位置的颜色。对于这个问题,作者百思不得其解,如果哪位读者知道原因,麻烦与作者联系,作者在此表示感谢。
|