最近很久没有更新。
在Excel中发送邮件有很多方式,可以使用Outlook,或者使用Lotus Notes。这里使用CDO组件来,CDO组件包括在cdosys.dll这个动态链接库中。关于CDO的文章,网络上已经有很多资源。
这个加载宏使用CDO组件来直接发送邮件,这样不需要Outlook或Lotus Notes这些外部程序。而且使用Outlook时,会有一个烦人的提示窗口。

使用CDO组件发送邮件的前提是你的邮箱支持SMTP服务。
界面:

设置:

发送界面:

下载:Box.net | SkyDrive
VBA Addin, Excel, VBA, 工具
一个小小的功能,做一个记号。
Excel中使用Shockwave Flash控件插入Flash文件时,有时Flash文件会带有一些自定义的右键菜单,如下图:

如果不想在Excel中显示这些菜单,可以通过修改下面的设置来取消。单击菜单“视图”-》“工具栏”-》“控件工具箱”,打开“控件工具箱”窗口,单击“设计模式”,选择Flash控件,再单击“属性”,弹出“属性”窗口。在属性FlashVars中输入menu=false,回车即可。如下图:

退出设计模式,原来的右键菜单已经被取消,剩下系统菜单。不过这个属性的修改不能保存,下次重新打开文件的时候仍然会出现自定义菜单。

如果只想保留“Setting…”和“About”菜单,则可以设置Flash控件的Menu属性为Flash。

Spreadsheet Excel, Flash, 右键菜单, 控件
VB中的Timer控件提供了很好的计时器功能,VBA的原始功能中没有包括Timer控件,取而代之的是Application.OnTime方法,但是在定时重复运行某个过程的情况下OnTime方法使用并不太方便。
简单的API Timer函数
使用API函数同样可以实现类似的OnTime功能,并且可以方便的循环运行指定的程序。只需要两个简单的API函数SetTimer和KillTimer。
Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Public lTimerID As Long
Sub StartTimer(lDuration As Long)
If lTimerID = 0 Then
lTimerID = SetTimer(0&, 0&, lDuration, AddressOf OnTime)
Else
Call StopTimer
lTimerID = SetTimer(0&, 0&, lDuration, AddressOf OnTime)
End If
End Sub
Sub StopTimer()
KillTimer 0&, lTimerID
End Sub
Sub OnTime()
' 计时器触发后运行的代码放在这
' ...
Cells(1, 1) = Now
End Sub
示例下载:Box | SkyDrive
单个事件的Timer类
我们也可以用这两个API函数创建一个类来完全模拟VB中Timer控件的方法,同时实现事件触发。这样做有一个困难,在类模块中不能使用AddressOf函数。一种方法是利用标准模块来获取回调函数的地址然后再类模块中调用。
创建一个名称为clsTimer的类模块,代码如下:
Option Explicit
Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private bEnable As Boolean
Private lDuration As Long
Private lTimerId As Long
Public Event Timer()
Public Property Let Enabled(ByVal vData As Boolean)
bEnable = vData
If bEnable = True Then
StartTimer
Else
EndTimer
End If
End Property
Public Property Get Enabled() As Boolean
Enabled = bEnable
End Property
Public Property Let Interval(ByVal vData As Long)
If vData < 0 Then vData = 0
lDuration = vData
If lDuration > 0 And bEnable = True Then
EndTimer
StartTimer
End If
End Property
Public Property Get Interval() As Long
Interval = lDuration
End Property
Private Sub Class_Initialize()
bEnable = False
lDuration = 0
lTimerId = 0
End Sub
Private Sub Class_Terminate()
EndTimer
bEnable = False
lDuration = 0
lTimerId = 0
End Sub
Friend Sub cRaiseEvent(ByVal hwnd As Long, ByVal uMsg As Long, ByVal lEventID As Long, ByVal dwTime As Long)
If lEventID = lTimerId Then
RaiseEvent Timer
End If
End Sub
Private Sub StartTimer()
If bEnable = True And lDuration > 0 Then
lTimerId = SetTimer(0&, 0&, lDuration, GetAddressof(Me))
End If
End Sub
Private Sub EndTimer()
If lTimerId Then
KillTimer 0&, lTimerId
lTimerId = 0
End If
End Sub
然后创建一个标准模块,代码如下:
Public modTimer As clsTimer
' 获取Timer Callback函数的地址
Public Function ReturnAddress(lpAddress As Long) As Long
ReturnAddress = lpAddress
End Function
' Timer callback函数
Public Sub TimerProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long)
Call modTimer.cRaiseEvent(hwnd, uMsg, idEvent, dwTime)
End Sub
Public Function GetAddressof(myTimer As clsTimer) As Long
Set modTimer = myTimer
GetAddressof = ReturnAddress(AddressOf TimerProc)
End Function
要使用类模块的事件,只能在工作表或窗体这些类模块中调用clsTimer类。声明时需要使用WithEvents。
Private WithEvents cTimer1 As clsTimer
这个方法在只有一个clsTimer类实例时能很好的运行,但是当创建两个实例时,只有第二个实例的事件可以触发(不知道是不是类模块的代码写得不对或者还是其它原因,应该和标准模块中的回调函数TimerProc的地址有关,不同的类实例对应的这个回调函数的地址是一样的,这样可能导致始终只有最后一个声明的类实例能触发事件)。
示例下载:Box | SkyDrive
完整功能的Timer类
EH的Joforn发了一个挖雷的游戏,完全模拟Windows自带挖雷游戏的功能,里面使用了Paul Caton的类模块Call Back函数。使用这个类模块CallBack函数,可以在类模块中实现AddressOf函数的功能,因此可以实现多个clsTimer类实例的事件触发。
新的clsTimer类实现了和VB中Timer控件完全一样的功能。
属性:
Enabled:Boolean类型;设为True,启动计时器;设为False,则关闭计时器,默认为False。
Interval:Long类型;计时器间隔时间,单位为毫秒,默认为0。
事件:
Timer
示例下载:Box | SkyDrive
注意:使用以上的类模块,切记在退出Excel工作簿之前关闭clsTimer类,否则会导致Excel崩溃。
VBA Class, Timer, VBA
大家都知道,VBA中自带的MsgBox不能自动关闭,某些特殊情况下需要在指定时间内用户没有点击消息框按钮后能继续运行代码,需要用到定时关闭的消息框,之前的方法是使用Wscript.Shell的Popup方法,或者自定义窗体。
EH的ldy兄推荐了一个未公开的API可以实现定时关闭消息框,感觉十分有用。稍作修改,将wType参数改成可以提示输入VBA的vbMsgboxStyle常数。
这个API函数的参数如下:
- hwnd:窗口句柄,可以设为0
- lpText:消息框显示内容,类似于MsgBox函数的第一个参数Prompt
- lpCaption:消息框标题,类似于MsgBox函数的第三个参数Caption
- wType:消息框类型,类似于MsgBox函数的第二个参数Buttons
- wlange:不是太明白这个参数,0或者1都看不出什么差别
- dwTimeout:延时时间,单位是毫秒
返回的值和vbMsgBoxResult常数一样,多了一个返回值32000表示超过延时时间未选择任何按钮。
Private Declare Function MsgBoxEx Lib "user32" Alias "MessageBoxTimeoutA" ( _
ByVal hwnd As Long, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal wType As VbMsgBoxStyle, _
ByVal wlange As Long, _
ByVal dwTimeout As Long) As Long
Private Sub TestMsgboxEx()
Dim ret As Long
ret = MsgBoxEx(0, "请选择", "两秒后自动关闭", vbYesNo + vbInformation, 1, 2000)
If ret = 32000 Then
Debug.Print "超时关闭"
ElseIf ret = vbYes Then
Debug.Print "选择Yes"
ElseIf ret = vbNo Then
Debug.Print "选择No"
End If
End Sub
VBA API, VBA
昨天进入到自己的SkyDrive公开文件夹的时候发现改成了新的界面,并且使用的是Office.live.com域名,原来是MS Office Web Apps正式发布了。
这里说只在美国、英国、加拿大和爱尔兰四个地区首先发布,但是我这里进去之后实际上是可以直接使用的,不知道是不是Hotmail修改了地区的原因。
新的界面根据时间分类,比起之前的SkyDrive方便了不少。“新建”的功能包括Excel、PowerPoint、Word和OneNote。并提供“以Zip文件下载”。还没有使用过Office2010的保存到SkyDrive的功能,下次要试试了。
Spreadsheet Excel, Office2010, SkyDrive, 在线服务
最近评论