怎让程序运行于在像‘输入法’那栏(200分)

  • 主题发起人 主题发起人 professional
  • 开始时间 开始时间
P

professional

Unregistered / Unconfirmed
GUEST, unregistred user!
怎让程序运行于在像‘输入法’那栏, 用鼠标击它, 弹出的菜单又是怎
做的。
 
这就是TrayIcon编程,有很多这方面的资料的。在这里我提示你一下,
主要是用到API:Shell_NotifyIcon
在MDSN中有详细的文档,只要你查这个API就知道了。:)
 
请在本论坛内搜索,有很多的
 
怎样建立简单的任务栏应用程序

windows 95 和 windows nt 4.0包含一个令人兴奋的特性:任务栏。这个通常位于区域任务条右面的区域能包含小的图标,这些图标能引出大的应用程序或者菜单。本篇文章主要讨论如何使用delphi建立这样的应用程序。
在开始之前,请看下面的需要的接口方面的内容:

从技术方面来说,一个任务栏应用程序非常象普通的应用程序,它有一个消息循环,相应windows的消息来完成相应的功能。

procedure runtrayapplication;
var msg : tmsg;
begin
createwindow;
addtrayicon;
while getmessage(msg,0,0,0) do begin
translatemessage(msg);
dispatchmessage(msg);
end;
deletetrayicon;
end;
你能看到:所有需要做的工作是创建一个窗口,注册一个图标到任务栏,设置它的消息循环,最后关闭它。当然,必须还有增加其他代码完成相应的功能,但是,它是真的不需要担心。

让我们从窗口的创建开始。实际上,这个窗口是不是能在任务栏上能见到的窗口。相应的,这个窗口只是处理消息循环、其它父类的工作。任务窗口(windows 95 & nt)句柄创建消息(例如鼠标单击等)和将消息发到我们的窗口。

procedure createwindow;
var
wc : twndclass;
w : hwnd;
begin
with wc do begin
style := 0;
lpfnwndproc := @wndproc;
cbclsextra := 0;
cbwndextra := 0;
hicon := 0;
hcursor := 0;
hbrbackground :=di2001.jpg;
lpszmenuname := nil;
lpszclassname := 'mytrayiconclass';
hinstance := system.hinstance;
end;
registerclass(wc);
w := windows.createwindow('mytrayiconclass', 'myveryowntrayiconwindow',
ws_overlappedwindow, 0, 0, 0, 0, 0, 0, hinstance, nil);
showwindow(w,sw_hide);
updatewindow(w);
mainwindow := w;
end;
 

这个窗口使用普通的窗口函数创建。注意这个窗口的类型是“ws_overlappedwindow”,但是这个尺寸是0,并且它是隐藏的,所有,它将不会显示出来。

下一步是加(注册)我们的图标。这将需要使用shell_notifyicon这个api函数,这个函数实际上可以完成三个功能,这里只需要它的增加的特性。

procedure addtrayicon;
var icondata : tnotifyicondata;
begin
with icondata do begin
cbsize := sizeof(icondata);
wnd := mainwindow;
uid := 0;
uflags := nif_icon or nif_message or nif_tip;
ucallbackmessage := wm_mycallback;
hicon := loadicon(hinstance,'myicon');
strcopy(sztip,pchar(trayicontip));
end;
shell_notifyicon(nim_add,@icondata);
end;
 

这个最重要的事情是tnotifyicondata的数据结构,它是一个设置window句柄的数据结构,是一个记录参数,对我们来说,我们需要设置这个图标的窗口句柄(这将定义哪个窗口处理消息循环),回调消息号,图标,工具提示等。一旦这个数据设置了,我们就可以增加一个图标到任务栏上了。为了完成这个工作,使用nim_add程序。

现行我们已经加了我们的图标到任务栏,下面需要决定如何处理消息。

const
wm_mycallback = wm_user+1000;
cm_exit = 100; { we worry about... }
cm_about = 101; { ...these later }
 

这个实际的窗口处理过程也是相当普通。几个窗口消息(如wm_nccreate)必须处理。然而,对我们来说,更重要的事情是处理wm_mycallback和wm_command消息:

function wndproc (window : hwnd; msg, wparam, lparam : integer): integer; stdcall;
begin
result := 0;
case msg of
wm_nccreate : result := 1;
wm_destroy : postquitmessage(0);
wm_command : begin { a command was chosen from the popup menu }
if (wparam = cm_exit) then
postmessage(window,wm_destroy,0,0)
else if (wparam = cm_about) then
messagebox(0,'shell test copyright ?'+
'jani j鋜vinen 1996.',
'about shell test',mb_ok)
else opendesktopicon(wparam-cm_about);
end;
wm_mycallback : begin { our icon was clicked }
if (lparam = wm_lbuttondown) then
showiconpopupmenu
else if (lparam = wm_rbuttondown) then
showaboutpopupmenu;
end;
else result := defwindowproc(window,msg,wparam,lparam);
end;
end;
 

就象你看到的一样,当用户单击图标时,windows提示我们。注意我们不使用通常使用的wm_lbuttondown 消息,而使用wm_mycallback message,详细的消息信息存储在lparam参数中。

当用户单击鼠标右键,我们创建一个菜单在桌面上。

type
ticondata = array[1..100] of string;
var
icondata : ticondata;
procedure showiconpopupmenu;
var
shellfolder : ishellfolder;
enumidlist : ienumidlist;
result : hresult;
dummy : ulong;
itemidlist : titemidlist;
pntr : pitemidlist;
strret : tstrret;
popupmenu : hmenu;
itemid : integer;
pos : tpoint;
procedure addtomenu(item : string);
var s : string;
begin
icondata[itemid-cm_about] := item;
s := extractfilename(item);
if (system.pos('.',s) <> 0) then setlength(s,system.pos('.',s)-1);
appendmenu(popupmenu,mf_enabled or mf_string,itemid,pchar(s));
inc(itemid);
end;
begin
popupmenu := createpopupmenu;
itemid := cm_about+1;
shgetdesktopfolder(shellfolder);
shellfolder.enumobjects(mainwindow,shcontf_nonfolders,enumidlist);
pntr := @itemidlist;
result := enumidlist.next(1,pntr,dummy);
while (result = noerror) do begin
shellfolder.getdisplaynameof(pntr,shgdn_forparsing,@strret);
with strret do addtomenu(string(cstr));
result := enumidlist.next(1,pntr,dummy);
end;
enumidlist.release;
shellfolder.release;
getcursorpos(pos);
appendmenu(popupmenu,mf_separator,0,'');
appendmenu(popupmenu,mf_enabled or mf_string,cm_exit,'e&xit');
setforegroundwindow(mainwindow);
trackpopupmenu(popupmenu,tpm_leftalign or tpm_leftbutton,
pos.x,pos.y,0,mainwindow,nil);
destroymenu(popupmenu);
end;
 

上面的程序看起来有点复杂,你可以将它分成两个部分来看:创建和显示菜单。

列举创建菜单是用windows的外壳接口完成的。首先,我们使用shgetdesktopforlder函数得到使用桌面的ishellfolder接口。使用这个接口,我们能得到另一个接口的实例:ienumidlist。这个接口通常实现实际的列举工作。我们简单的重复调用这个函数直到错误值返回(例如:所有的菜单被列举)。当我们得到一个菜单,我们使用addtomenu函数加它。

当所有的菜单被列举和创建后,现在我们需要运行这个菜单。我们将找到的菜单保存到一个全局的list变量中,每一个菜单都拥有它的菜单号。这确保我们能得到它的索引。

opendesktopicon(wparam-cm_about)

当然,wparam中储存了用户单击鼠标的菜单的菜单号(id)。

下面我们将处理运行用户选择的菜单。

procedure opendesktopicon(number : integer);
var
s : string;
i : integer;
begin
s := icondata[number];
i := shellexecute(0,nil,pchar(s),nil,nil,sw_shownormal);
if (i < 32) then begin
s := 'could not open selected item "'+s+'". '+
'result was: '+inttostr(i)+'.';
messagebox(0,pchar(s),'shell test',mb_ok);
end;
end;
 

上面,win 32 api函数shellexecute做了所有的工作。

现在你应该能用delphi创建简单的任务栏的程序了。
 
同上。。二

Windows 95状态指示区的DELPHI编程

Windows 95作为Windows家族的一个里程碑,不仅增加了系统的稳定性,更在易用性上下了很大的功夫。其中任务栏的引入可以说是易用性方面的典范。系统运行的每一个应用程序都在任务栏的任务切换区有对应的按钮,用户可以简便、快捷地在应用程序间切换。通过任务栏的状态指示区,用户可以很方便地到当前系统时间,调节声音大小,切换输入法等。Windows
   95也允许应用程序在状态指示区上放置图标,以指示应用程序目前的状态,或者提醒用户某个事件的发生。现在,越来越多的应用程序,如拨号软件、网络监视软件、杀毒软件、字典等都把自己的图标加到状态指示区上。在大家享受如此方便快捷的服务的同时,作为程序员则更加关注此功能的实现方法,以使自己的产品立于不败之地。

  

   针对这个问题,我查阅了大量的技术资料及Delphi 3.0所带的源代码,成功地解决了它。以下是对这个问题分析及解决它的三个步骤。

  

   1.在状态指示区上安装、更改、卸载图标

  

   Windows 95是基于事件驱动机制的操作系统。任何一个控件都是通过发消息或对系统消息的处理来实现功能的。状态指示区实际上也是一个标准控件。对它的控制和操纵,与其他的标准控件一样,也是通过发消息和处理消息进行的。应用程序通过系统功能调用Shell_NotifyIcon向状态指示区发消息,它的函数说明如下(摘自Delphi的Source/RTL/WIN/shellapi.pas文件):

   function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL; stdcall;

  

   其中:

  

   dwMessage:要发给状态指示区的消息,可以取以下三个值:

   NIM_ADD 在状态指示区上增加一个图标

   NIM_MODIFY 修改一个状态指示区的图标

   NIM_DELETE 删除状态指示区的一个图标

   lpData:指向TNotifyIconDataA结构的指针,此结构是要增加的图标的信息。

   PNotifyIconData = ^TNotifyIconDataA

   TNotifyIconDataA = record

   cbSize : DWORD;

   Wnd : HWND;

   uID : UINT;

   uFlags : UINT;

   uCallbackMessage : UINT;

   hIcon : HICON;

   szTip : array [0..63] of AnsiChar;

   end;

  

   各字段的含义如下所述:

  

   cbSize:记录TNotifyIconDataA的大小,设置为SizeOf( TNotifyIconDataA);

   Wnd:接收状态指示区鼠标事件的窗口的句柄,例如:Form1.Handle;

   uID:图标的ID(状态指示区鼠标事件的wPara参数的值);

   uFlags:此条消息的有效范围(低三位有效)

   NIF_MESSAGE 0x1 // uCallbackMessage参数有效

   NIF_ICON 0x2 // hIcon is valid参数有效

   NIF_TIP 0x4 // szTip is valid参数有效

   uCallbackMessage:系统回送消息的ID;

   hIcon:显示在状态指示区上的图标的句柄;

   szTip:鼠标移动到图标上时系统显示的提示信息。

  

  
   在安装图标时,需要对结构的每一个字段都赋值。而当更改、卸载图标时,则有些字段可以不赋值。当更改图标信息时,除设置需要更改的内容外,要把cbSize,Wnd,uID设置成正确的值,还要按照更改的内容将uFlags调到的相应的位置。例如,只更改图标的提示信息时,把cbSize,Wnd,uID设置为正确的值,szTip赋值为要更改成的提示信息,uFlags赋值为4即可。需要删除图标时,只需要把cbSize,Wnd,uID设置成正确的值即可。

  

   2.对状态指示区上图标鼠标事件的响应

  

  
   现在,我们已经在状态指示区上安装了自己的图标,但它并不产生任何实际效果。因为当应用程序接收到鼠标事件后,除了可以显示提示信息外,没有产生任何动作。我们现在要做的就是要响应图标上的鼠标事件。

  

   当用户在你的图标上移动、点击时,状态指示区会向应用程序发送如下的消息:

  

   messageID = uCallbackMessage

   wParam = uID

   lParam = mouse event (如:WM_LBUTTONDOWN)

  

   我们只需在主窗口的定义中,声明一个过程,来响应这个消息,在它的实现部分就可以对消息进行处理了。

  

   3.隐藏任务切换区的应用程序按钮

  
   完成了上面这些工作以后,我们已经可以在状态指示区成功地安装上图标,而且也可以响应图标上的事件了。美中不足的是,在状态指示区显示应用程序图标的同时,任务切换区也存在此应用程序的按钮,怎么让它不出现在任务切换区上呢?这里又要提到一个系统调用ShowWindow,它的说明如下(摘自Delphi的Source/RTL/WIN/
   windows.pas文件):

  

   function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall;

   其中:

   hWnd:要改变显示状态的窗口的句柄;

   nCmdShow:窗口要改变成为的显示状态:

   SW_HIDE 隐藏本窗口,激活另一个窗口

   SW_SHOW 激活本窗口

  

   当一个应用程序的所有窗口都隐藏时,它的任务按钮就不显示。在用户选取状态指示区的图标时,再让某一个窗口激活就可以了。

  

   4. 样例

  

  
   下面是我在研究过程中做的一个小例子。它运行时在状态指示区上安装一个图标。此图标共有两个运行状态:激活状态和非激活状态。在激活状态下,状态显示区显示一个“笑脸”图标;非激活状态显示一个“哭脸”图标。用户右击此图标,则弹出一个菜单,选取“激活”菜单项时,将切换运行状态,状态指示区的图标也做相应的变化。选取“关闭”菜单项时,卸载状态指示区的图标。

  

   unit Utest;

   interface

   uses

   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, Menus,
   shellapi, StdCtrls;

   const

   ICON_ID = 1; // 图标在本应用程序中的编号

   MI_ICONEVENT = WM_USER + 1; // 图标上的鼠标事件

   type

   TForm1 = class(TForm)

   pop1: TPopupMenu; // 在图标上右点鼠标时弹出的菜单

   status1: TMenuItem; // pop1菜单的第一项,选取它会改变程序的状态

   N1: TMenuItem;

   close1: TMenuItem; // pop1菜单的最后一项,它会关闭此应用程序

   procedure FormCreate(Sender: TObject);

   procedure FormClose(Sender: TObject; var Action: TCloseAction);

   procedure close1Click(Sender: TObject);

   procedure status1Click(Sender: TObject);

   private

   normalicon, disabledicon: TIcon; //需放在状态指示区的图标

   status: Boolean;

   procedure InstallIcon; //在状态指示区上安装图标

   procedure ChangeIcon(status:Boolean);//根据参数,显示不同的图标

   procedure UnInstallIcon; //卸载图标

   procedure IconOnClick(var message: TMessage); message MI_ICONEVENT; //响应图标上的鼠标事件

   end;

   var

   Form1: TForm1;

   implementation

   {$R *.DFM}

   procedure TForm1.FormCreate(Sender: TObject);

   begin

   InstallIcon;

   ChangeIcon(False );

   ShowWindow(Application.Handle, SW_HIDE ); //隐藏应用程序主窗口机

   end;

   procedure TForm1.InstallIcon;

   var IconData: TNotifyIconData;

   begin

   normalicon := TIcon.Create;

   disabledicon := TIcon.Create;

   normalicon.LoadFromFile( 'normal.ico' );

   disabledicon.LoadFromFile( 'disable.ico' );

   IconData.cbSize := SizeOf( IconData );

   IconData.Wnd := Handle;

   IconData.uID := ICON_ID;

   IconData.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;

   IconData.uCallBackMessage := MI_ICONEVENT;

   IconData.hIcon := normalicon.Handle;

   IconData.szTip := '我可以在状态指示区上加图标了!';

   Shell_NotifyIcon( NIM_ADD, @IconData );

   end;

   procedure TForm1.ChangeIcon( status: boolean );

   var IconData: TNotifyIconData;

   begin

   IconData.cbSize := SizeOf(IconData);

   IconData.wnd := Handle;

   IconData.uID := ICON_ID;

   if IconData.fconectado then IconData.hIcon := normalicon.Handle else IconData.hIcon :=
   disabledIcon.Handle;

   IconData.uFlags := NIF_ICON;

   Shell_NotifyIcon(NIM_MODIFY, @IconData);

   end;

   procedure TForm1.UnInstallIcon;

   var IconData: TNotifyIconData;

   begin

   IconData.cbSize := SizeOf( IconData );

   IconData.Wnd := Handle;

   IconData.uID := ICON_ID;

   Shell_NotifyIcon( NIM_DELETE, @IconData );

   end;

   procedure TForm1.IconOnClick( var message: Tmessage);

   var p : TPoint;

   begin

   if (message.lParam = WM_LBUTTONDOWN) then

   ShowWindow( Application.Handle, SW_SHOW );

   if (message.lParam = WM_RBUTTONDOWN) then begin

   GetCursorPos( p );

   pop1.Popup( p.x ,p.y );

   ChangeIcon( status );

   end;

   end;

   procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

   begin

   UnInstallIcon;

   normalicon.Free;

   disabledicon.Free;

   end;

   procedure TForm1.FormActivate(Sender: TObject);

   begin

   ShowWindow( Application.Handle, SW_HIDE );

   end;

   procedure TForm1.close1Click(Sender: TObject);

   begin

   Close;

   end;

   procedure TForm1.status1Click(Sender: TObject);

   begin

   status := not status;

   if status then status1.Caption := 'Enable'

   else status1.Caption := 'Disable';

   end;

   end.
 
有必要吗?就一个小小的shell_NotifyIcon函数,写了这么长两篇文章
其实这种东西,自己看着help去研究才好玩呢!
 
嘻嘻,文章长没关系,关键是这¥200,呵呵……
 
我也反对在这里贴这么长的代码,又不是自己写的东东,
再说,大家如果都这样,就容易养成不查文献、文档的
坏毛病,怎么能提高?其实,真正的帮助别人应该是给
别人提供一个“入口”,让别人自己去研究,自己探索,
才能更快的成长。你说如果别人把嚼过的东西吐出来给
你,你愿意吃吗?
 
我刚才也想说的,但想了想没说,

毕竟人家的出发点是好的,积极性也是可以肯定的,
但效果不怎么好,最好能提供下载地址。
想法最重要,毕竟编程要靠自己。
 
是啊是啊,我可能说的太过分了。不过,我觉得Help和
MSDN的东西其实都足够详细的了,一般的困难是在于不
知道如何入手,而不是有了思路后不知道如何写程序。
所以,我还是建议多给思路,少贴代码,留给别人研究
的空间。
 
Delphi 5 自带例子,用的着如上废话!
 
请问一下:DELPHI的自带例子是什么名字,当然是有关输入法的
 
如果是控件,我这有!
xxxz007@yeah.net
 
xlf,我可以看看你的控件吗?
 
要的话见我自己编的一个小软件,自己经过练习实现的。
mail to :loopy@netease.com
 
我也有一个控件, cooltrayicon, 挺不错的, 想要的话
我可以给你发. mailto:xieke@mail.ru
 
接受答案了.
 
后退
顶部