现在的位置: 首页 > 综合 > 正文

VB6中的任务栏图标编程

2013年08月27日 ⁄ 综合 ⁄ 共 4629字 ⁄ 字号 评论关闭

 

VB6中的任务栏图标编程

  杨山河

 

Windows 9X的桌面任务栏显示了当前正在运行的任务,并且右下角的任务栏图标提供了快速访问对于当前不可见的任务。我们的鼠标单击或双击动作都通过任务栏转发给相应的窗口,哪怕该应用程序根本看不到(窗口隐藏了),接收到消息的应用程序对用户的鼠标动作作出反应。例如:我们双击代表声效控制程序的喇叭图标,将弹出“音量控制”程序。其实,在微软的正式文档中称之为“发射架”,意思是所有对于任务栏图标的事件都通过“发射”,让相应的应用程序获得消息。这是Windows 9X对于Windows 3.X的先进特性,如果我们有必要的话,我们的程序应当尽量符合微软徽标要求棗任务栏图标提供了一种访问必须运行但不必时时展现应用程序的手段,因为所有Windows标准程序都是消息驱动的,而消息的接收必须要求有界面元素存在,但很多的应用并没有界面元素(因为没有必要,譬如微软Windows 98自带的计划任务,它不需要每时每刻都开一个桌面窗口,但有时我们需要随机地访问它,并且它自己也必须时刻运行),所以如果需要在应用程序窗口不可见情况下访问这些应用程序的话,必须通过任务栏图标。

如果对于C或C++之类的语言,要实现任务栏图标的编程是很简单的:在创建应用窗口时通过调用Shell API往任务栏加入图标,然后隐藏应用窗口,“耐心”倾听各种鼠标消息,当用户对任务栏图标进行鼠标操作时,系统(具体来说是任务栏)将此消息以调用窗体过程的方式传递给应用程序,应用程序根据具体的消息决定执行何种操作。显然,该过程应当在窗体过程棗C/C++的表演场所中完成,但对于VB语言来说就有些“勉为其难”。VB中标准编程流程中不存在什么“窗体过程”,VB中只能对对象进行编程,只能使用对象的方法、属性等,所以很多的杂志书报提及任务栏编程时举例都回避了最流行的VB语言例子。实际上,利用VB6提供的先进特性,我们可以实现任务栏的VB编程。

VB中实现任务栏编程需要了解以下几点:

任务栏图标与VB应用程序、任务栏之间的关系。这个我们刚才大体上已经讨论过:任务栏图标是应VB应用程序的要求由任务栏加入的,删除、更新等操作均由任务栏管理,但必须VB应用程序“申请”。当VB应用程序不可见时,任务栏图标代表它接收各种事件,VB应用程序的窗体过程处理事件。
VB中并没有提供任务栏编程语句,但我们可以通过API调用来实现。要用到哪些API我们后面将谈到。
VB任务栏图标编程必须用到窗体“子类化”,要用到直接获取/设置窗体过程的API,同时也必须获取窗体过程的地址,这必须用到VB6才提供的操作符“AddressOf”。具体用法见随后提供的实例。如果想在应用窗体的事件中响应任务栏图标转发的事件,结果一定令你失望。窗体的事件同任务栏响应并转发的事件不是一个概念,前者是OLE 技术中的概念,经过VB技术的“包裹”,后者是真正意义上的事件。在VB中响应任务栏图标的事件,必须直接“接管”窗体的真正的窗体过程。
任务栏图标编程并非必要时就不使用,除了使用过程较复杂、易引起系统崩溃原因外,太多的任务栏图标影响桌面“市容”也是一个原因。
下面,我们通过一个简单的实例来说明这个过程。我们的这个例子很简单,就是在一个标准的工程的Form1装入之前往任务栏加入一个图标,装入窗体后利用子类化技术截取发往窗体的消息,在鼠标左键双击事件中发出一个消息框表明消息的确转发到应用程序。

首先,将应用程序的启动过程设为Sub main( ),这可以通过选择project/project1 properties,在弹出的general表中,从startup object项中选择 “sub main”。

其二,往工程中加入一个模块。往其中加入任务栏图标编程需要的数据结构、常量、API声名。为此,需要启动API Text Viewer。

需要加入的数据结构为(相应的意义见注释):

Public Type NOTIFYICONDATA

cbSize As Long ‘本结构的长度

hWnd As Long ‘往任务栏安装图标的应用程序的窗体句柄,系统据此发送消息给相应的应用程序

uID As Long ‘图标的唯一标识,由安装图标的应用程序确定

uFlags As Long ‘图标将接受或表现什么样的行为

uCallbackMessage As Long ‘发给应用程序的回调消息,应当避免同系统预定义消息相同,应当值大于 WM_USER

hIcon As Long ‘图标句柄,即欲在任务栏中显示的图标

szTip As String * 64 ‘如果图标被允许显示 Tip的话,那么当鼠标停留在任务栏中的图标上一段时间,将显示该串

End Type

需要加入的常量声名有:

Public Const WM_LBUTTONDBLCLK = &H203 ‘鼠标左键双击消息的代码

Public Const WM_USER = &H400 ‘用户自定义消息的基值

 

Public Const GWL_WNDPROC = (-4) ‘子类化窗体过程时,必须使用此参数确定需要替换的窗体类结构中的窗体过程的偏移值。

Public Const NIM_ADD = &H0 ‘表示要往任务栏中加入图标

Public Const NIM_DELETE = &H2 ‘删除图标

Public Const NIM_MODIFY = &H1 ‘修改图标

Public Const NIF_ICON = &H2 ‘允许图标显示

Public Const NIF_MESSAGE = &H1 ‘允许图标消息转发

Public Const NIF_TIP = &H4 ‘允许图标显示图标提示内容串

 

Public Const uID& = 88888 ‘由你自定义的图标的标识值

Public Const cbNotify& = WM_USER + 100 ‘图标转发的回调消息,必须大于 WM_USER

 

用到的API有:

Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long

Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long ‘

Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

‘以上这几个函数用于子类化窗体

Public Declare Function ShellNotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA" (ByVal dwMessage As Long, lpData As NOTIFYICONDATA) As Long

‘此函数用于往任务栏中加入指定的图标,用到了前面提到的NOTIFYICONDATA数据结构。

用到变量有:

Global gHW As Long ‘用于保存VB应用程序的窗口句柄

Global lpPrevWndProc As Long ‘保存被替换前的窗体过程地址

Public myNID As NOTIFYICONDATA ‘用于给定图标的有关信息

还应当设计好函数和窗体,具体见注释:

Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal

lParam As Long) As Long ‘此函数截取了所有的发往窗体的消息

If wParam = uID Then ‘如果消息参数中辅助参数是我们自定义的值,则表明此消息是经任务栏图标转发的

Select Case lParam ‘此参数表明发生了何种事件,如果双击了右键的话就弹出一个消息框

Case WM_LBUTTONDBLCLK

MsgBox "已经转发了鼠标双击消息!" , vbInformation, "任务栏图标编程"

Form1.Show ‘显示窗体

End Select

End If

WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam) ‘调用原来的窗体过程

End Function

 

Public Sub Hook() ‘用于子类化窗体,让自编的函数截取消息,此Sub设定新窗体过程

lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)

End Sub

 

Sub unHook() ‘解除自定义窗体过程

Dim temp As Long

temp = SetWindowLong(gHW, GWL_WNDPROC, lpPrevWndProc)

End Sub

 

Public Sub main()

Load Form1 ‘仅需要装入窗体即可

End Sub

 

下面,我们来看看如何在Form1中处理图标的加入等问题。

在Form_Load()中输入以下代码:

Private Sub Form_Load()

gHW = Me.hWnd ‘初始化全局变量

myNID.cbSize = Len(myNID) ‘填充myNID

myNID.hIcon = Me.Icon

myNID.hWnd = gHW

myNID.szTip = "我的应用程序图标"

myNID.uCallbackMessage = cbNotify

myNID.uFlags = NIF_ICON Or NIF_MESSAGE Or NIF_TIP

myNID.uID = uID

ShellNotifyIcon NIM_ADD, myNID ‘加入任务栏图标

Hook ‘设置新的窗体过程

End Sub

在窗体的卸载过程中,需要撤除图标以及解除截取窗体的过程。

Private Sub Form_Unload(Cancel As Integer)

ShellNotifyIcon NIM_DELETE, myNID ‘删除任务栏图标

UnHook ‘解除挂钩

End Sub

好了,现在你可以保存工程,然后运行。你可以看到任务栏“发射架”中加入了一个普通的窗体图标,那就是你“亲手”加入的。你可以双击它,看会发生什么。如果没有一个消息框弹出,或者系统崩溃,说明你在输入代码时有误,请仔细研读本例代码。

最后,我们来总结以下整个过程需要注意什么:

WindowProc函数(新窗体过程)必须在.Bas 模块中声名,且参数必须正确,函数中不允许出现任何错误(不管是语法上的还是逻辑上的)。此函数的最后必须调用原来的窗体过程,否则的话...(会让你死得很难看!!!)
调试时,如果要终止应用程序,请不要使用VB的调试按钮,只能使用窗体的关闭按钮,否则的话,由于失效的指针使用将会使VB崩溃。
子类化技术使你可以自如截取窗体消息,功能强大,但注意不可滥用,毕竟它有许多副作用。
由于VB的限制,我们只能通过Windows API方式来完成一些特殊任务。使用API,需要注意 API参数的正确以及在哪个模块声明。
本例对于任务栏图标转发的消息作出的反映很简单,仅起“Demo”作用,你可以加入许多你自己的想法棗试试吧,我等着你的好消息!

--

1998/1999 ?不记得了

抱歉!评论已关闭.