貝殼兒的寶貝匣子

有一天,可愛的小狗狗問媽媽:幸福是什麼? 狗媽媽說:幸福其實就是你的尾巴. 小狗狗拼命咬自己的尾巴,但是它都咬不到 "媽媽,為什麼我抓不住我的幸福?" "傻孩子,抬起頭去走,幸福就會跟著你的!"
posts - 7, comments - 1, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

DELPHI中的键盘操作

Posted on 2005-11-04 09:45 貝殼兒 阅读(2264) 评论(0)  编辑 收藏 引用 所属分类: Delphi编程
#用程序模拟键盘和鼠标键盘
在Windows大行其道的今天,windows界面程序受到广大用户的欢迎。对这些程序的操作不外乎两种,键盘输入控制和鼠标输入控制。有时,对于繁杂的,或重复性的操作,我们能否通过编制程序来代替手工输入,而用程序来模拟键盘及鼠标的输入呢?答案是肯定的。这主要是通过两个API函数来实现的。   

  下面以Delphi为例来介绍一下如何实现这两个功能。模拟键盘我们用Keybd_event这个api函数,模拟鼠标按键用mouse_event函数。大家不用担心,在delphi里调用api函数是很方便的事。   
  先介绍一下Keybd_event函数。Keybd_event能触发一个按键事件,也就是说回产生一个WM_KEYDOWN或WM_KEYUP消息。当然也可以用产生这两个消息来模拟按键,但是没有直接用这个函数方便。Keybd_event共有四个参数,第一个为按键的虚拟键值,如回车键为vk_return, tab键为vk_tab。第二个参数为扫描码,一般不用设置,用0代替就行。第三个参数为选项标志,如果为keydown则置0即可,如果为keyup则设成“KEYEVENTF_KEYUP”,第四个参数一般也是置0即可。用如下代码即可实现模拟按下i键,其中的$49表示i键的虚拟键值:
keybd_event($49,0,0,0);
                                                      keybd_event($49,0,KEYEVENTF_KEYUP,0); ...   

  mouse_event最好配合setcursorpos(x,y)函数一起使用,与Keybd_event类似,mouse_event有五个参数,第一个为选项标志,为MOUSEEVENTF_LEFTDOWN时表示左键按下,为MOUSEEVENTF_LEFTUP表示左键松开,向系统发送相应消息。第二三个参数分别表示x,y相对位置,一般可设为0,0,第四五个参数并不重要,一般也可设为0,0。若要得到Keybd_event和mouse_event函数的更详细的用法,可以查阅msdn或delphi帮助。下面是关于mouse_event的示例代码:
 
setcursorpos(20,132);
   mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
   mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
   mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
   mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0); ...
上面的代码表示鼠标的双击,若要表示单击,用两个mouse_event即可(一次放下,一次松开)。  
 注意,不管是模拟键盘还是鼠标事件,都要注意还原,即按完键要松开,一个keydown对应一个keyup;鼠标单击 完也要松开, 不然可能影响程序的功能。   
   好了,希望本文能让你对模拟键盘和鼠标按键有个初步的了解,如果想更深入的了解其中的奥妙,可以查阅详实的msdn在线帮助,并多多动手实践。


#解决锁定键盘鼠标的方法
如果你不需要屏蔽Ctrl+Alt+Del组合键,可以使用低级键盘钩子(WH_KEYBOARD_LL)与低级鼠标钩子(WH_MOUSE_LL),这两种消息钩子的好处是不需要放在动态链接库中就可以作全局钩子,将键盘消息与鼠标消息截获.
unit uHookKeyAndMouse;
{ 该单元利用WH_KEYBOARD_LL与WH_MOUSE_LL两种类型的钩子分别截获键盘消息与鼠标消息}
{ 由于这里只是需要将消息屏蔽,故只需对钩子函数的返回结果设为1即可. }
{ 提供两个函数StartHookKeyMouse与StopHookKeyMouse两个函数. }

interface

uses
Windows, Messages, SysUtils;

const
WH_KEYBOARD_LL =13;
WH_MOUSE_LL =14;

procedure StartHookKeyMouse;
procedure StopHookKeyMouse;

implementation

var
hhkLowLevelKybd:HHook=0;
hhkLowLevelMouse:HHook=0;

function LowLevelKeyboardProc(nCode:Integer; WParam:WPARAM; LParam:LPARAM):LRESULT; stdcall;
begin
Result:=1;
if nCode<>0 then Result:=CallNextHookEx(0,nCode,WParam,LParam);
end;

function LowLevelMouseProc(nCode:Integer; WParam:WPARAM; LParam:LPARAM):LRESULT; stdcall;
begin
Result:=1;
if nCode<>0 then Result:=CallNextHookEx(0,nCode,WParam,LParam);
end;

procedure StartHookKeyMouse;
begin
if hhkLowLevelKybd = 0 then
begin
hhkLowLevelKybd := SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, Hinstance, 0);
end;
if hhkLowLevelMouse = 0 then
begin
hhkLowLevelMouse:=SetWindowsHookEx(WH_MOUSE_LL,LowlevelMouseProc,HInstance,0);
end;
end;

procedure StopHookKeyMouse;
begin
if hhkLowLevelKybd <> 0 then
begin
UnhookWindowsHookEx(hhkLowLevelKybd);
hhkLowLevelKybd:=0;
end;
if hhkLowLevelMouse <> 0 then
begin
UnHookWindowsHookEx(hhkLowLevelMouse);
hhkLowLevelMouse:=0;
end;
end;

initialization
hhkLowLevelKybd:=0;
hhkLowLevelMouse:=0;
finalization
if hhkLowLevelKybd <> 0 then UnhookWindowsHookEx(hhkLowLevelKybd);
if hhkLowLevelMouse <> 0 then UnhookWindowsHookEx(hhkLowLevelMouse);
end.


你的方法确实实现了锁定鼠标,但是我想达到的效果是:
1.锁定键盘
2.鼠标只能在我的程序窗口中操作
谢谢


来自:smokingroom, 时间:2005-2-24 17:01:12, ID:2996381
要求2(鼠标只能在我的程序窗口中操作)的实现:
修改LowLevelMouseProc过程如下:

type
PMSLLHOOKSTRUCT=^MSLLHOOKSTRUCT;
MSLLHOOKSTRUCT = record
pt:TPoint;
mouseData:DWORD;
flags:DWORD;
time:DWORD;
dwExtraInfo:DWORD;
end;

var
MouseRect:TRect; //这是你需要限制的Mouse活动范围.

function LowLevelMouseProc(nCode:Integer; WParam:WPARAM; LParam:LPARAM):LRESULT; stdcall;
var
p:PMSLLHOOKSTRUCT;
begin
Result:=0;
if nCode=HC_ACTION then
begin
p:=PMSLLHOOKSTRUCT(LParam);
if (p.pt.X < MouseRect.Left) or (p.pt.X > MouseRect.Right) or
(p.pt.Y < MouseRect.Top) or (p.pt.Y > MouseRect.Bottom) then
Result:=1;
end else
if nCode<>0 then Result:=CallNextHookEx(0,nCode,WParam,LParam);
end;

附取得MouseRect的代码,假定你的主窗体体为MainFrm
MouseRect:=MainFrm.ClientRect;
MouseRect.TopLeft:=MainFrm.ClientToScreen(MouseRect.TopLeft);
MouseRect.BottomRight:=MainFrm.ClientToScreen(MouseRect.BottomRight);


另在Result:=1之前加多一个ClipCursor(@MouseRect)效果会更好,可以有效解决当按下Ctrl+Alt+Del后将Mouse移出窗体后,Mouse失效的情况.
if (p.pt.X < MouseRect.Left) or (p.pt.X > MouseRect.Right) or
(p.pt.Y < MouseRect.Top) or (p.pt.Y > MouseRect.Bottom) then
begin
ClipCursor(@MouseRect)
Result:=1;
end



#Delphi键盘码表

Delphi 键盘码表

资料来源:Delphi Windows 单元文件

VK_LBUTTON = 1;
VK_RBUTTON = 2;
VK_CANCEL = 3;
VK_MBUTTON = 4; { NOT contiguous with L & RBUTTON }
VK_BACK = 8;
VK_TAB = 9;
VK_CLEAR = 12;
VK_RETURN = 13;
VK_SHIFT = $10;
VK_CONTROL = 17;
VK_MENU = 18;
VK_PAUSE = 19;
VK_CAPITAL = 20;
VK_KANA = 21;
VK_HANGUL = 21;
VK_JUNJA = 23;
VK_FINAL = 24;
VK_HANJA = 25;
VK_KANJI = 25;
VK_CONVERT = 28;
VK_NONCONVERT = 29;
VK_ACCEPT = 30;
VK_MODECHANGE = 31;
VK_ESCAPE = 27;
VK_SPACE = $20;
VK_PRIOR = 33;
VK_NEXT = 34;
VK_END = 35;
VK_HOME = 36;
VK_LEFT = 37;
VK_UP = 38;
VK_RIGHT = 39;
VK_DOWN = 40;
VK_SELECT = 41;
VK_PRINT = 42;
VK_EXECUTE = 43;
VK_SNAPSHOT = 44;
VK_INSERT = 45;
VK_DELETE = 46;
VK_HELP = 47;
{ VK_0 thru VK_9 are the same as ASCII '0' thru '9' ($30 - $39) }
{ VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' ($41 - $5A) }
VK_LWIN = 91;
VK_RWIN = 92;
VK_APPS = 93;
VK_NUMPAD0 = 96;
VK_NUMPAD1 = 97;
VK_NUMPAD2 = 98;
VK_NUMPAD3 = 99;
VK_NUMPAD4 = 100;
VK_NUMPAD5 = 101;
VK_NUMPAD6 = 102;
VK_NUMPAD7 = 103;
VK_NUMPAD8 = 104;
VK_NUMPAD9 = 105;
VK_MULTIPLY = 106;
VK_ADD = 107;
VK_SEPARATOR = 108;
VK_SUBTRACT = 109;
VK_DECIMAL = 110;
VK_DIVIDE = 111;
VK_F1 = 112;
VK_F2 = 113;
VK_F3 = 114;
VK_F4 = 115;
VK_F5 = 116;
VK_F6 = 117;
VK_F7 = 118;
VK_F8 = 119;
VK_F9 = 120;
VK_F10 = 121;
VK_F11 = 122;
VK_F12 = 123;
VK_F13 = 124;
VK_F14 = 125;
VK_F15 = 126;
VK_F16 = 127;
VK_F17 = 128;
VK_F18 = 129;
VK_F19 = 130;
VK_F20 = 131;
VK_F21 = 132;
VK_F22 = 133;
VK_F23 = 134;
VK_F24 = 135;
VK_NUMLOCK = 144;
VK_SCROLL = 145;
{ VK_L & VK_R - left and right Alt, Ctrl and Shift virtual keys.
Used only as parameters to GetAsyncKeyState() and GetKeyState().
No other API or message will distinguish left and right keys in this way. }
VK_LSHIFT = 160;
VK_RSHIFT = 161;
VK_LCONTROL = 162;
VK_RCONTROL = 163;
VK_LMENU = 164;
VK_RMENU = 165;
VK_PROCESSKEY = 229;
VK_ATTN = 246;
VK_CRSEL = 247;
VK_EXSEL = 248;
VK_EREOF = 249;
VK_PLAY = 250;
VK_ZOOM = 251;
VK_NONAME = 252;
VK_PA1 = 253;
VK_OEM_CLEAR = 254; 



#编写后台监控软件
 后台监控软件,为了达到隐蔽监控的目的,应该满足正常运行时,不显示在任务栏上,在按Ctrl+Alt+Del出现的任务列表中也不显示,管理员可以通过热键调出隐藏的运行界面。要作到这些,必须把当前进程变为一个系统服务,并且定义全局热键。
一、把当前进程变为一个系统服务:
目的是在任务列表中把程序隐藏起来。调用API函数RegisterServiceProcess实现。
二、定义全局热键(本例中定义热键Ctrl+Del+R),步骤:
1、定义捕获Windows消息WM_HOTKEY的钩子函数,即:
procedure WMHotKey(var Msg : TWMHotKey); message WM_HOTKEY;
2、向Windows加入一个全局原子 Myhotkey: GlobalAddAtom('MyHotkey'),
并保留其句柄。
3、向Windows登记热键:调用API函数RegisterHotKey实现。

  后台监控软件,为了达到隐蔽监控的目的,应该满足正常运行时,不显示在任务栏上,在按Ctrl+Alt+Del出现的任务列表中也不显示,管理员可以通过热键调出隐藏的运行界面。要作到这些,必须把当前进程变为一个系统服务,并且定义全局热键。
一、把当前进程变为一个系统服务:
目的是在任务列表中把€程序隐藏起来。调用API函数RegisterServiceProcess实现。
二、定义全局热键(本例中定义热键Ctrl+Del+R),步骤:
1、定义捕获Windows消息WM_HOTKEY的钩子函数,即:
procedure WMHotKey(var Msg : TWMHotKey); message WM_HOTKEY;
2、向Windows加入一个全局原子 Myhotkey: GlobalAddAtom('MyHotkey'),
并保留其句柄。
3、向Windows登记热键:调用API函数RegisterHotKey实现。
三、源程序:



unit Unit1;
interface
uses
Windows, Messages, Forms, Dialogs, Classes, Controls, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{热键标识ID}
id: Integer;
procedure WMHotKey(var Msg : TWMHotKey); message WM_HOTKEY;
{ Privat-Declarations}
public
{ Public-Declarations}
end;
var
Form1 : TForm1;
implementation
const RSP_SIMPLE_SERVICE=1;
function RegisterServiceProcess (dwProcessID, dwType: DWord) : DWord; stdcall; external 'KERNEL32.DLL';
{$R *.DFM}

{捕获热键消息}
procedure TForm1.WMHotKey (var Msg : TWMHotKey);
begin
if msg.HotKey = id then
ShowMessage('Ctrl+Alt+R键被按下!');
form1.Visible :=true;
end;

procedure TForm1.FormCreate(Sender: TObject);
Const
{ALT、CTRL和R键的虚拟键值}
MOD_ALT = 1;
MOD_CONTROL = 2;
VK_R = 82;
begin
{首先判断程序是否已经运行}
if GlobalFindAtom('MyHotkey') = 0 then
begin
{注册全局热键Ctrl + Alt + R}
id:=GlobalAddAtom('MyHotkey');
RegisterHotKey(handle,id,MOD_CONTROL+MOD_Alt,VK_R);
end
else
halt;
end;

{把当前进程变为一个系统服务,从而在任务列表中把程序隐藏起来}
procedure TForm1.Button1Click(Sender: TObject);
begin
RegisterServiceProcess(GetCurrentProcessID,RSP_SIMPLE_SERVICE);
form1.Hide;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
close;
end;
{退出时释放全局热键}
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
UnRegisterHotKey(handle,id);
GlobalDeleteAtom(id);
end;
end.
四、说明:
在后台监控软件中使用以上功能,可真正实现隐蔽运行,热键调出,便于管理员进行管理。程序在Win98,Delphi5.0中运行通过。 


#利用Hook技术实现键盘监控

在许多系统中,出于安全或其它原因,常常要求随时对键盘进行监控,一个专业的监控程序必须具备两点,一是实时;二是作为指示图标运行。实际应用中把利用 Hook(即钩子)技术编写的应用程序添加到Windows的任务栏的指示区中就能够很好的达到这个目的。我在参考了API帮助文档基础上,根据在 Delphi开发环境中的具体实现分别对这两部分进行详细论述。
一、Hook(钩子)的实现:Hook是应用程序在MicrosoftWindows消息处理过程中设置的用来监控消息流并且处理系统中尚未到达目的窗口的某一类型消息过程的机制。如果Hook过程在应用程序中实现,若应用程序不是当前窗口时,该Hook就不起作用;如果Hook在DLL中实现,程序在运行中动态调用它,它能实时对系统进行监控。根据需要,我们采用的是在DLL中实现Hook的方式。


在许多系统中,出于安全或其它原因,常常要求随时对键盘进行监控,一个专业的监控程序必须具备两点,一是实时;二是作为指示图标运行。实际应用中把利用 Hook(即钩子)技术编写的应用程序添加到Windows的任务栏的指示区中就能够很好的达到这个目的。我在参考了API帮助文档基础上,根据在 Delphi开发环境中的具体实现分别对这两部分进行详细论述。
一、Hook(钩子)的实现:Hook是应用程序在MicrosoftWindows消息处理过程中设置的用来监控消息流并且处理系统中尚未到达目的窗口的某一类型消息过程的机制。如果Hook过程在应用程序中实现,若应用程序不是当前窗口时,该Hook就不起作用;如果Hook在DLL中实现,程序在运行中动态调用它,它能实时对系统进行监控。根据需要,我们采用的是在DLL中实现Hook的方式。
1.新建一个导出两个函数的DLL文件,在hookproc.pas中定义了钩子具体实现过程。代码如下:librarykeyspy;
useswindowsmessageshookprocin'hookproc.pas';
exportssetkeyhookendkeyhook;
beginnexthookproc:=0;
procsaveexit:=exitproc;
exitproc:=@keyhookexit;
end.
2.在Hookproc.pas中实现了钩子具体过程:unithookproc;
interfaceusesWindowsMessagesSysUtilsControlsStdCtrls;
varnexthookproc:hhook;
procsaveexit:pointer;
functionkeyboardhook(icode:integer;wparam:wparam;lparam:lparam):lresult;
stdcall;
export;
functionsetkeyhook:bool;export;//加载钩子functionendkeyhook:bool;export;//卸载钩子procedurekeyhookexit;
far;
constafilename='c:\debug.txt';//将键盘输入动作写入文件中vardebugfile:textfile;
implementationfuncionkeyboardhookhandler(icode:integer;wparam:wparam;lparam:lparam):lresult;
stdcall;
export;
beginificode<0thenbeginresult:=callnexthookex(hnexthookprocicodewparamlparam);
exit;
end;
assignfile(debugfileafilename);
append(debugfile);
ifgetkeystate(vk_return)<0thenbeginwriteln(debugfile'');
write(debugfilechar(wparam));
endelsewrite(debugfilechar(wparam));
closefile(debugfile);
result:=0;
end;
functionendkeyhook:bool;
export;
beginifnexthookproc<>0thenbeginunhookwindowshookex(nexthookproc);
nexthookproc:=0;
messagebeep(0);
end;
result:=hnexthookproc=0;
end;
procedurekeyhookexit;
far;
beginifnexthookproc<>0thenendkeyhook;
exitproc:=procsaveexit;
end;
end.
二、Win95/98使用任务栏右方指示区来显示应用程序或工具图标对指示区图标的操作涉及了一个API函数Shell_NotifyIcon,它有两个参数,一个是指向TnotifyIconData结构的指针,另一个是要添加、删除、改动图标的标志。通过该函函数将应用程序的图标添加到指示区中,使其作为图标运行增加专业特色。当程序起动后,用鼠标右键点击图标,则弹出一个菜单,可选择sethook或endhook。
unitkb;
interfaceusesWindowsMessagesSysUtilsClassesGraphicsControlsFormsDialogsStdCtrlsMenusshellapi;
consticon_id=1;
MI_iconevent=wm_user+1;//定义一个用户消息typeTForm1=class(TForm)
PopupMenu1:TPopupMenu;
sethook1:TMenuItem;
endhook1:TMenuItem;
N1:TMenuItem;
About1:TMenuItem;
Close1:TMenuItem;
Gettext1:TMenuItem;
procedureFormCreate(Sender:TObject);
proceduresethook1Click(Sender:TObject);
procedureendhook1Click(Sender:TObject);
procedureFormDestroy(Sender:TObject);
procedureClose1Click(Sender:TObject);
private{Privatedeclarations}
nid:tnotifyicondata;
normalicon:ticon;
public{Publicdeclarations}
procedureicontray(varmsg:tmessage);
messagemi_iconevent;
end;
varForm1:TForm1;
implementation{$R*.DFM}
functionsetkeyhook:bool;
external'keyspy.dll';
functionendkeyhook:bool;
external'keyspy.dll';
proceduretform1.icontray(varmsg:tmessage);
varpt:tpoint;
beginifmsg.lparam=wm_lbuttondownthensethook1click(self);
ifmsg.LParam=wm_rbuttondownthenbegingetcursorpos(pt);
setforegroundwindow(handle);
popupmenu1.popup(pt.xpt.y);
end;
end;
procedureTForm1.FormCreate(Sender:TObject);
beginnormalicon:=ticon.create;
application.title:=caption;
nid.cbsize:=sizeof(nid);
nid.wnd:=handle;
nid.uid:=icon_id;
nid.uflags:=nif_iconornif_messageornif_tip;
nid.ucallbackmessage:=mi_iconevent;
nid.hIcon:=normalicon.handle;
strcopy(nid.sztippchar(caption));
nid.uFlags:=nif_messageornif_iconornif_tip;shell_notifyicon(nim_add@nid);
SetWindowLong(Application.HandleGWL_EXSTYLEWS_EX_TOOLWINDOW);
end;
procedureTForm1.sethook1Click(Sender:TObject);
beginsetkeyhook;
end;
procedureTForm1.endhook1Click(Sender:TObject);
beginendkeyhook;
end;
procedureTForm1.FormDestroy(Sender:TObject);
beginnid.uFlags:=0;
shell_notifyicon(nim_delete@nid);
end;
procedureTForm1.Close1Click(Sender:TObject);
beginapplication.terminate;
end;
该程序虽然只用了几个shellai函数,但是它涉及到了在Delphi中对DLL的引用、钩子实现、对指示区的操作、用户定义消息的处理、文件的读写等比较重要的内容,我相信这篇文章能对许多Delphi的初学者有所帮助。
该程序在Win98、Delphi4.0中正常运行。

  
#建立应用软件注册安全机制

软件的安全性是个永恒的话题,一直困扰着人们,程序员们常常因此熬夜。Novell 公司的网络操作系统NetWare
以其完善可靠的安全管理机制著称,为实现用户登录的安全性和防止非法入侵者闯入,系统提供了几种有效的限制措施和检测功能。本文讨论如何建立完善的软件注
册安全机制,有效地管理用户登录,为应用软件提供可靠的保护,并在Delphi下实现。
软件的安全性是个永恒的话题,一直困扰着人们,程序员们常常因此熬夜。Novell 公司的网络操作系统NetWare 以其完善可靠的安全管理机制著称,为实现用户登录的安全性和防止非法入侵者闯入,系统提供了几种有效的限制措施和检测功能。本文讨论如何建立完善的软件注册安全机制,有效地管理用户登录,为应用软件提供可靠的保护,并在Delphi下实现。

  Delphi 作为一个优秀的编程工具,功能极其强大,令开发周期大大缩短,生成的代码运行速度快,使得Windows 软件设计成为一种乐趣。基本思路是:建立Paradox 格式的用户帐户表Users.DB,并加密保护,由管理员维护。预先在此表中登记并有注册权限的用户,可持本人有效口令登录进入系统。

  实现步骤如下:

  1 .在应用软件主目录(假设为C:\Register)下创建两个子目录,分别命名为Login 和Data。

  2 .从Delphi 的Tools 菜单中启动Database Desktop,并选择Tools/AliasManager ...创建一个新别名。方法是:在Alias Manager 对话框中点New,给出新别名UserInfo,并指向C:\Register\Data 目录,按Keep New、OK 等按钮保存新别名。

  3 .利用Database Desktop 创建Paradox 5.0 for Windows 数据表Users.DB,结构见表1。事实上,对于商业软件还需要更多的域。

表1 表Users.DB 的结构
域名类型大小关键字说明
Name A 16 * 用户名
Password A 16 * 注册口令
Fullname A 32 用户全名
Group I 用户组号
Enabled L 注册允许

  4 .设定Name, Group 和Enabled 为非空域(Required Field),Group 的最小值为0,最大值为3,默认值为3,Enabled 的默认值为True。用户组0 为系统管理员,可进行几乎任何操作;用户组1 为软件操作人员,可进行除用户帐户管理以外的操作,包括建立和删除数据;用户组2 为普通用户,可使用系统资源,无修改权限,而用户组3 仅可浏览系统有限信息。

  5 .用口令"UserPassword" 保护Users.DB 表, 存于Data 目录下。利用Database Desktop 向表中输入若干样本用户(见表2),然后关闭DatabaseDesktop。

表2 表Users.DB 的样本用户
Name Password Fullname Group Enabled
Supervisor AAAAAA Super User 0 True
Director BBBBBB Director User 1 True
Guest 3 True
User1 CCCCCC General User1 1 False
User1 DDDDDD General User1 2 True
User2 EEEEEE General User2 3 True

  表2 显示,允许同一用户以不同身份(用户组)登录进入系统,享有不同的权限。如用户User1,以不同密码登记,分属用户组1 和2。

  6 .返回Delphi IDE,新建项目Register.dpr 并存入C:\Register 目录,主窗体单元命名为Main.pas。用户注册成功后,方能启动主窗体进入系统。本系统仅显示用户信息及登录的日期和时间以表示注册成功(见图5,并不做其它任何事情,这里便是读者应用系统的接口。为此,在主窗体上放入一个TGroupBox 组件,TLabel 和TEdit 组件各五个。将所有TEdit 的Text 属性清空,ReadOnly 属性值设为True,窗体和组件其余属性按表3 设置。

  7 .创建主窗体的OnActivate 事件处理程序,登录成功并激活主窗体时执行,显示用户信息、登录日期和时间。过程如下:

procedure TMainForm.FormActivate(Sender: TObject);
begin
if Time< =StrToTime('12:00:00') then
Caption:=' 上午好,您已成功登录!'
else
Caption:=' 下午好,您已成功登录!';
// 显示登录用户信息、登录日期和时间
NameEdit.Text:=LoginUser.UserName;
FullNameEdit.Text:=LoginUser.UserFullName;
GroupEdit.Text:=IntToStr(LoginUser.UserGroup);
DisplayDateEdit.Text:=DateToStr(Date);
DisplayTimeEdit.Text:=TimeToStr(Time);
end;

表3 主窗体中组件属性设置
组件属性设置
Form1 Caption
Name MainForm
Position poScreenCenter
GroupBox1 Caption 用户信息
Label1 Caption 用户名称:
Label2 Caption 用户全名:
Label3 Caption 用户组号:
Label4 Caption 登录日期:
Label5 Caption 登录时间:
Edit1 Name NameEdit
Edit2 Name FullnameEdit
Edit3 Name GroupEdit
Edit4 Name DisplayDateEdit
Edit5 Name DisplayTimeEdit
  8 .数据模板。选择File/New Data Module 建立数据模板LogDataModule,放入一个Ttable 组件,如图1。属性按表4 设置,命名其单元为MD.pas,存入Login 目录。

表4 数据模板中组件属性设置
组件属性设置
Table1 DatabaseName UserInfo
Name UsersTable
TableName Users.DB
  9 .创建注册窗体。选择File/New Form 创建一个新窗体,并加入组件TLabel、TEdit 和TbitBtn 各两个。窗体和组件属性按表5 设置,命名其单元为Login.pas,存于Login 目录。

表5 注册窗体中组件属性设置
组件属性设置
Form2 ActiveControl UserNameEdit
Caption 请注册
Name LoginDialogForm
Position poScreenCenter
Label1 Caption 用户:
Label2 Caption 密码:
Edit1 Name LoginNameEdit
Text
Edit2 Name PasswordEdit
PasswordChar *
Text
BitBtn1 Caption 注册(&L)
Enabled False
Kind bkOK
Name LoginBitBtn
BitBtn2 Caption 取消(&C)
Enabled True
Kind bkCancel
Name CancelBitBtn

  10 .选择File/New 命令,从New Items 的New 页中选择Unit 项建一个无窗体单元Addition.pas 存入Login 目录,在其interface 段输入下列语句:

uses Classes, Forms, Sysutils;
type
TApplicationUser=class(TComponent)
private
FUserName: string;
FFullName: string;
FUserGroup: Integer;
public
property UserName: string read
FUserName write FUserName;
property UserFullName: string read
FFullName write FFullName;
property UserGroup: Integer read
FUserGroup write FUserGroup;
end;
var
LoginUser: TApplicationUser;
  在implementation 段加入下列语句:

initialization
LoginUser:=TapplicationUser.Create(Application);
  11 .创建注册窗体(LoginDialogForm) 的OnCreate 事件处理程序。在
private 段中声明CountDown 为Integer 变量,作为计数器,并将下述语句
插入begin 与end 之间:

CountDown:=3; // 允许尝试注册3 次

  12 .建立用户名输入框(LoginNameEdit) 的OnChange 事件处理程序,使窗体LoginDialogForm 创建时注册按钮为灰色,用户输入登录名后才可用。不难实现,源代码略。

  13 .创建注册过程。双击注册按钮(LoginBitBtn),在begin 和end 之间输入其OnClick 事件处理程序代码:

ModalResult:=mrNone;
try
Session.AddPassword('UserPassword');
LogDataModule.UsersTable.Open;
//注册信息处理
if not LogDataModule.UsersTable.FindKey
([LoginNameEdit.Text,PasswordEdit.Text]) then
begin
//处理注册次数
dec(CountDown);
if CountDown< =0 then ModalResult:=mrCancel else
begin
 MessageBeep($FFFF);
 Application.MessageBox('请您重试一次!',
 '无效注册信息',mb_iconstop+mb_OK);
 //重设输入焦点
 LoginNameEdit.SetFocus;
end;
 exit;
end;
//检查注册允许值
if not LogDataModule.UsersTable.FieldByName
('Enabled').AsBoolean then
begin
 MessageBeep($FFFF);
 Application.MessageBox( '抱歉,您不能进入系统!',
 '使用权限终止',mb_iconexclamation+mb_OK);
 ModalResult:=mrCancel;
 exit;
end;
//获取注册用户信息
with LogDataModule do
begin
 LoginUser.UserName:=UsersTable.FieldByName('Name')。AsString;
 LoginUser.UserFullName:=UsersTable.FieldByName('Fullname').AsString;
 LoginUser.UserGroup:=UsersTable.FieldByName('Group').AsInteger;
End;
ModalResult:=mrOK;
//关闭表,清除密码
finally
LogDataModule.UsersTable.Close;
Session.RemovePassword('UserPassword');
end;
  14 .在Main.pas 单元的uses 中加入"Addition",在Login.pas 单元的implementation 段增加uses 语句:

uses
MD, Addition;

  15 .打开过程文件Register.dpr,在uses 语句中加入"Controls",将"Application.run;" 改为下列语句,只有注册成功时才启动系统,同时释放注册窗体, 以确保安全和减少内存占用。

if LoginDialogForm.ShowModal=mrOK then
begin
LoginDialogForm.Free;
Application.Run;
end;

  16 .编译、运行。注册窗口见图2,输入用户名和密码后点" 注册" 按钮。

  如果注册信息有误,系统出现提示框,允许重新注册,见图3。若三次尝试均不成功,注册窗口将保护性关闭。

  访问权已被管理员终止的用户(Enabled 的值为False),例如用户User1 以口令CCCCCC 注册至用户组1,系统拒绝进入,见图4。

  注册成功后将启动系统主程序。例如用户Supervisor 以口令AAAAAA 注册,见图5。

  上述方案具有以下特点:

  1 .Supervisor 用户具有至高无上的权利,由系统创建,不能删除,仅Password 和Fullname 字段的值允许自行修改。主要负责系统维护和用户帐户管理等工作,是系统的永久性特权用户。

  2 .确保用户帐户表Users.DB 的安全致关重要,本方案采用了较为可靠的关键性技术,就是注册过程(见步骤13)中"try ...finally ...end"结构的应用。仅当用户按下" 注册" 按钮时,注册模块转送表Users.DB 的密码并将其打开,以处理注册信息。而无论成功与否,在finally 部分都能及时关闭表Users.DB,并立即清除内存中的密码。

  3 .用户帐户管理模块设在主程序中,完成表Users.DB 的维护功能,包括建立用户帐户和用户组,增、删用户,分配访问权限等。只有Supervisor 或其授权用户才可进入。

  4 .组帐户管理:用户按不同级别分组,再按组进行权限分配,以简化管理。如系统可根据登录用户所属的用户组,十分方便地屏蔽相应菜单项,以达到限制用户使用权限的目的。对用户群大的系统,此项技术的优越性尤为显著。

  5 .默认帐户限制:建立用户帐户时,系统默认其Group=3,仅具备浏览有限信息之权限,不致因管理员一时疏忽使新建用户越权使用,危及系统安全。

  6 .单独帐户限制:用户注册时,必须提供完整的个人有效信息才能进入系统。

  7 .注册允许限制:管理员可随时取消或恢复单个用户、用户组甚至系统所有用户(Supervisor 除外)的注册权限,只需设置注册允许字段Enabled 的值为False 或True。

  8 .非法入侵限制:三次注册尝试不成功便保护性关闭注册窗口,以防非法非法入侵者无限制尝试。

  本文介绍了应用软件注册安全性问题的综合解决方案,容易实现,便于扩充-- 如增加注册时间限制和记帐收费功能等,可建立较完善的注册安全机制,适合大型商用软件开发。


#玩转keybd_event
模拟键盘平时不是很常用, 但是当调用某些快捷键执行某项功能时, 它真的是那么的方便呀. 你不信? 看看下面的实现, 你就会大呼: 为什么不早点告诉我? 呵呵, 原来没有blog呀, 都靠这些挣分呢.

1) 显示桌面:

很多软件有显示桌面的功能, 并且大家的方法都是遍历窗口, 然后让它们最小化, 其实 win系统给咱们了一个非常方便的WIN键(就是键盘上在CTRL键和ALT键之间的那个带win标志的按键), 利用它, 可以轻松的完成显示桌面的功能.

keybd_event(VK_LWIN, 0, 0 ,0);
keybd_event('M', 0, 0 ,0);
keybd_event('M', 0, KEYEVENTF_KEYUP ,0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP,0);

其他的操作也类似, 比如直接显示开始的运行,就把上面的'M'换成'R'即可。

直接 keybd_event(VK_LWIN, 0, 0 ,0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP,0);

直接显示“开始”对话框了。

2) 实现快速的全选

很多的时候,比如listctrl实现全选,你可以用listctrl循环设置每一项的状态为选中,多罗索的事情呀。用快捷键试一试CTRL+A,其他的快捷键一样的用法,呵呵,你知道怎么办了吧?

keybd_event(VK_CONTROL, (BYTE)0, 0 ,0);
keybd_event('A',(BYTE)0, 0 ,0); //此处可以用 'A', (BYTE)65, 用'a'不起作用.
keybd_event('A', (BYTE)0, KEYEVENTF_KEYUP,0);
keybd_event(VK_CONTROL, (BYTE)0, KEYEVENTF_KEYUP,0);

3) 执行某些特殊的键,比如数字键,大小写,下面是数字键的例子

bool bState=true; //true为按下NumLock,false反之
BYTE keyState[256];

GetKeyboardState((LPBYTE)&keyState);
if( (bState && !(keyState[VK_NUMLOCK] & 1)) ||
(!bState && (keyState[VK_NUMLOCK] & 1)) )
{
// Simulate a key press
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | 0,
0 );

// Simulate a key release
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
0);
}

4) 你想CTRL+ALT+DELETE三键一起按下,

keybd_event(VK_CONTROL, 0, 0 ,0);
keybd_event(VK_MENU,0, 0 ,0);
keybd_event(VK_DELETE,0, 0 ,0);

keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP ,0);
keybd_event(VK_MENU,0, KEYEVENTF_KEYUP ,0);
keybd_event(VK_DELETE,0, KEYEVENTF_KEYUP ,0);
呵呵,这样不会成功呀,因为这几个键直接是操作系统来截获执行的,而模拟键盘只能发向应用程序,所以这种方法不行的(想显示锁定对话框,用 LockWorkStation();)

5) Window2000/NT/XP已经不提倡用这个函数了,上面的方法只是为了让大家开阔一下思路,怎么替代呢,呵呵,看下面,所以上面的所有代码都可以用这个来完成

//2000下用这个代替 ,包含 "winable.h"
INPUT input[4];
memset(input, 0, sizeof(input));

input[0].type = input[1].type = input[2].type = input[3].type = INPUT_KEYBOARD;

input[0].ki.wVk = input[3].ki.wVk = VK_LWIN;
input[1].ki.wVk = input[2].ki.wVk = 'R';


//接下来释放它,这一点很重要。
input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP;
input[0].ki.time = input[1].ki.time = input[2].ki.time = input[3].ki.time = GetTickCount();

SendInput(4, input, sizeof(INPUT));

感觉比那个有点罗索,呵呵。

====================

附WIN键的部分快捷键:

WIN键+D=快速的切到桌面,再次点击返回

WIN键+E=快速打开资源管理器

WIN键+R=“运行”。

WIN键+M=全部视窗最小化。

WIN键+Shift+M=取消全部视窗最小化。

WIN键+F1=Help。

WIN键+F=“寻找”。

WIN键+Ctrl+F=显示“查找电脑”。

WIN键+Tab=切换工作列的程式。

WIN键+Break=显示系统内容。
只有注册用户登录后才能发表评论。