一个备份 Outlook 的VBA

下面这段程序能够实现将你选中的邮件以MSG格式保存到一个目录中,并且生成一个包含邮件内容的TXT文件。举例来说:你在Outlook中选中200封邮件,然后指定 c:\m\ 作为存储目录。运行之后你就会发现,m目录中出现 0 1 2 3….这样的目录,其中是以MSG格式保存的每封邮件,并且每个目录下还有其中邮件TXT格式的内容。程序是以20MB为限,当一个目录中的邮件大于20MB时自动创建另外的目录。之所以选择20MB是因为通常的Mail系统所支持的附件的上限是这个数值。因此,你可以将生成的TXT作为正文,压缩之后的目录作为附件发送到你的邮件服务器上。比较推荐的是 QQ Mail,容量足够大,速度也不慢。当然如果你不放心,或者说觉得它的速度慢,还可以选择一个对你来说速度快的免费邮件服务器,然后在QQ Mail中设定自动收取这个邮箱的邮件。因为 QQ Mail的容量是一直增长的,所以不必担心邮箱爆掉。另外,QQ Mail提供了一个全文搜索的功能,使用户可以很方便的搜索到文件的内容—-这也是为什么要按照TXT保存邮件正文一次。

Function ReplaceCharsForFileName(sName As String) As String
sName = Replace(sName, “/”, ” “, 1, -1)
sName = Replace(sName, “\”, ” “, 1, -1)
sName = Replace(sName, “:”, ” “, 1, -1)
sName = Replace(sName, “?”, ” “, 1, -1)
sName = Replace(sName, “<“, ” “, 1, -1) sName = Replace(sName, “>”, ” “, 1, -1)
sName = Replace(sName, “|”, ” “, 1, -1)
sName = Replace(sName, “*”, ” “, 1, -1)
‘chr(34)='”‘
sName = Replace(sName, Chr(34), ” “, 1, -1)
ReplaceCharsForFileName = sName
End Function

Sub SaveChooseFile()

Dim objItem As Object
Dim strPath As String
Dim strFilePath As String
Dim iSize As Long
Dim iNum As Integer
Dim objFSO As Object

‘exit if you do not choose any files
If ActiveExplorer.Selection.Count = 0 Then Exit Sub

‘the path string should be ended by “\”
strPath = InputBox(“please enter a path :”, ” Enter path”)

Set fso = CreateObject(“Scripting.FileSystemObject”)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)

‘File size counter
iSize = 0

‘Directory counter
iNum = 0

For Each objItem In ActiveExplorer.Selection

‘Create file and dir
If iSize = 0 Then
strFilePath = strPath & Str(iNum)
If Not (objFSO.FolderExists(strFilePath)) Then
objFSO.CreateFolder (strFilePath)
End If
Set ts = fso.CreateTextFile(strFilePath & “\” & Str(iNum) & “Body.txt”, ForAppending, True)
End If

iSize = iSize + objItem.Size

‘write all the chosen file to a file
ts.Write (“==============================================================” & vbCrLf)
ts.Write (objItem.ReceivedTime & vbCrLf)
ts.Write (objItem.SenderName & vbCrLf)
ts.Write (objItem.Subject & vbCrLf)

‘Meeting requirement doesn’t have ‘to’ field
If (objItem.Class <> olMeetingRequest) _
And (objItem.Class <> olMeetingCancellation) _
And (objItem.Class <> olMeetingResponseNegative) _
And (objItem.Class <> olMeetingResponsePositive) _
Then ts.Write (objItem.To & vbCrLf)
ts.Write (objItem.Body & vbCrLf)
ts.Write (“==============================================================”)

‘save the mail as MSG format
objItem.SaveAs strFilePath & “\” & ReplaceCharsForFileName(objItem.Subject) & “.MSG”, OlSaveAsType.olMSGUnicode

‘save the contents to another text if the size is larger than 20MB
If iSize >= 20971520 Then
iSize = 0
iNum = iNum + 1
ts.Close
End If

Next

MsgBox “Email text extraction completed!”, vbOKOnly + vbInformation, “DONE!”

ts.Close
Set objItem = Nothing

End Sub

额外的问题:有时候在保存邮件的过程中会出现如下错误:

当出现错误的时候,可以切换到Debug下面,通过查看邮件的标题得知出现问题的邮件是那封,删除这个邮件即可。这并不是上面程序本身导致的,对于导致问题的邮件,你使用Outlook 直接保存为MSG文件也会出现同样的问题,目前我只在一些会议邀请的邮件上遇到过这样的问题,如果你没有这类邮件应该不会遇到这个问题。

使用 Outlook VBA 批量保存MSG格式邮件

使用下面的程序,可以实现将选中的邮件以MSG的格式批量保存到指定的目录下。每个文件会以收到的时间命名。

Function DateToString(d As Date) As String
Dim s As String
s = Format([d], “yyyymmddhhnnss”)
DateToString = s
End Function

Sub SaveSelMails()
Dim objItem As Object
Dim strPath As String
Dim strFilename As String

‘Exit if you don’t choose any files
If ActiveExplorer.Selection.Count = 0 Then Exit Sub

strPath = InputBox(“Please enter the full path:”, “Enter path”)

‘write all the chosen emails to the path
For Each objItem In ActiveExplorer.Selection
strFilename = DateToString(objItem.ReceivedTime)
objItem.SaveAs strPath & strFilename & “.msg”, olMSGUnicode
Next

MsgBox “Email text extraction completed!”, vbOKOnly + vbInformation, “DONE!”
Set objItem = Nothing
End Sub

需要注意的是,输入时要输入以 ‘\’ 结尾的字符串,比如: ‘c:\tmp\’

此外,还有更简单的方式:在Outlook中使用Ctrl+左键选中邮件之后,直接拖拽到目录中。

关于 Outlook 选择目录对话框的问题

需要在 Outlook 下通过VBA打开一个选择目录的对话框,刚开始搜索到的解决方案类似如下代码

Dim msDialog As Office.FileDialog
Set msDialog = Application.FileDialog(msoFileDialogFilePicker)
msDialog.Show

但是经过很多次实验,Outlook 2010中根本没有 Application.FileDialog 这样的属性方法。这条路是无法行通的!!!

最后的解决方案是如下代码

Private Type BROWSEINFO
hOwner As Long
pidlRoot As Long
pszDisplayName As String
lpszTitle As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long
End Type

Private Declare Function SHGetPathFromIDList Lib “shell32.dll” Alias _
“SHGetPathFromIDListA” (ByVal pidl As Long, _
ByVal pszPath As String) As Long

Private Declare Function SHBrowseForFolder Lib “shell32.dll” Alias _
“SHBrowseForFolderA” (lpBrowseInfo As BROWSEINFO) _
As Long

Private Const BIF_RETURNONLYFSDIRS = &H1
Public Function BrowseFolder(szDialogTitle As String) As String
Dim X As Long, bi As BROWSEINFO, dwIList As Long
Dim szPath As String, wPos As Integer

With bi
.hOwner = hWndAccessApp
.lpszTitle = szDialogTitle
.ulFlags = BIF_RETURNONLYFSDIRS
End With

dwIList = SHBrowseForFolder(bi)
szPath = Space$(512)
X = SHGetPathFromIDList(ByVal dwIList, ByVal szPath)

If X Then
wPos = InStr(szPath, Chr(0))
BrowseFolder = Left$(szPath, wPos – 1)
Else
BrowseFolder = vbNullString
End If
End Function

Sub dd()
Dim Str As String

Str = BrowseFolder(“c:\\”)

Debug.Print Str

End Sub

代码来自 http://access.mvps.org/access/api/api0002.htm

opendialog

Outlook VBA 将文件多个邮件的内容保存在一个 TXT 文件中

将Outlook中多个邮件的内容保存在一个 TXT 文件中。运行后需要手工输入一个文件名。

Sub SaveBodytoText()
Dim objItem As Object
Dim strFile As String

‘Exit if you don’t choose any files
If ActiveExplorer.Selection.Count = 0 Then Exit Sub

strFile = InputBox(“Please enter the full path and file name for the merged text:”, “Enter File Name”)

‘write content to a file
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set ts = fso.CreateTextFile(strFile, ForAppending, True)

‘write all the chosen file to a file
For Each objItem In ActiveExplorer.Selection
ts.Write (objItem.Body)
Next

MsgBox “Email text extraction completed!”, vbOKOnly + vbInformation, “DONE!”

ts.Close
Set objItem = Nothing

End Sub

此外比较有用的属性还有下面几个:

ReceivedTime 接收时间
SenderName 发件人姓名
Subject 主题
To 收件人(特别注意,有些Outlook邮件没有收件人,也就是没有这个项!)

如何编译DOS版本的Memtest86+

之前研究过Memtest86+的编译,但是没有搞清楚为什么我自己编译出来的DOS可执行程序版本的始终无法运行,与之相对,编译出来的ISO版本或者软盘版都是正常的。经过一番研究,终于找到了问题的原因。在编译之前,需要修改mt86+_loader.asm中的一个定义:

%define fullsize (164504 + buffer – exeh)
; 164504 is the size of memtest86+ V4.20, adjust as needed!

其中这个164504 应该是你编译一次之后memtest.bin文件的大小。这个文件的大小会跟着你使用的gcc编译器版本不同或者其他原因有着不同的大小(万幸,每次编译结果大小是不变的)。在我的环境下,编译出来的memtest.bin大小为176760bytes,因此这个值需要重新修改。

简单解释,这个值是最终生成DOS EXE的文件头,它决定 Load到内存中的有用的文件代码的大小,当这个值偏小(实际有用的代码要更多,装入的少),会导致不可预料的后果。这就是为什么之前编译出来的并不稳定的原因。

顺便说两句

1. 不要尝试在Windows 7 的 XP Mode中安装Ubuntu 12.04,安装过程中会提示无法找到硬盘。推荐使用VirtualBox作为编译环境以及测试的虚拟机。在它上面能够正常运行的话,
在实体机上也不会有问题。
2. 特别注意,修改上述文件之后最好比较一下是否完全编译进入EXE中,最简单的方式是比较新生成的EXE和之前的EXE文件前面310h字节是否相同
3. Ubuntu 12.04没有内置Nasm,安装方法可以参考这篇 insnasm 文章,来自 Linux公社
4. 此外,还提供了一个包含我编译之后的exe和原始mt420.exe的iso镜像以供比较 mtlabz

dosimg

English Version

A. Source code is downloaded from
http://www.memtest.org/
or you can download a 4.20 here memtest86+-4.20.tar 

B. Compiling environment
Ubuntu 12.04 can be downloaded from http://forum.ubuntu.org.cn/viewtopic.php?f=49&t=349550
Nasm can be downloaded from http://www.nasm.us/pub/nasm/releasebuilds/

C. Virtual machine: I use Virtual Box this time. And I have tested the ‘XP mode’ of Windows 7 it fails. I can install the Ubuntu to it. The Ubuntu 12.04 can’t find the HD on it(I use 12.04. And I’m not sure if other version works well). So please don’t’ use XP mode. It will drive you mad.

D. Install the Ubuntu in your Virtual Box. And you should install a Nasm. Ubuntu 12.04 doesn’t contain a nasm 🙁

E. Here is the key-point. You should modify the definiation in mt86+_loader.asm:

%define fullsize (164504 + buffer – exeh)
; 164504 is the size of memtest86+ V4.20, adjust as needed!

164504 is the size of memtest.bin. You run ‘make dos’ once and get the size of memtest.bin This value may be different if the gcc version is different. It reads 176760 bytes in my side.

F. At last build the source again with ‘make dos’. You will get a ‘memtest.exe’. It’s the a DOS executable file.

Note: Be sure to recompile mt86+_loader.asm! Compare the exe you get and the original one. The value in it head should be different.

Outlook 2010 VBA Debug.Print 和 DateToString

一.在Outlook中可以用 Debug.Pring + 内容 直接输出运行结果。不过需要用 ctrl+G 打开Immediate Window

二.下面的程序可以用来将一个 Date 类型转换为 String

Sub DateToString()
Dim d As Date
d = Now

Debug.Print (d)

Dim s As String

s = Format([d], “yyyymmddhhnnss”)

Debug.Print (s)
End Sub

format具体用法可以参考 http://msdn.microsoft.com/en-us/library/gg251755.aspx

vbadatetostring

VC 中输出当前运行的文件和所处的行数以及所在函数名称的方法

通过下面的例子,可以到到 VC 可以在运行过程中输出当前运行的文件和所处的行数以及所在函数名称。如果需要追踪你程序的流程,并且能够获得运行期的Log文件,这将是一个很好的追踪方法

#include “stdafx.h”

void foo()
{
printf(“Run in file %s, line %d Function %s\n”, __FILE__, __LINE__, __FUNCDNAME__);
return;
}
int _tmain(int argc, _TCHAR* argv[])
{
printf(“This is a simple demo to show how to show Filename Line Number and Function Name \n”);
printf(” Powered by www.lab-z.com Z.t \n”);
printf(“Run in file %s, line %d Function %s\n”, __FILE__, __LINE__, __FUNCDNAME__);
foo();
getchar();
return 0;
}

运行结果:

1

注意到其中的文件名给出的是相对路径,可以通过下面的位置设置为完整路径

2

之后的运行结果

Untitled

其中使用的 __FILE__ 和 __LINE__ 是c语言标准中定义的

下面文字来源于 http://topic.okbase.net/200510/2005102110/2170344.html

看ISO14882
6.10.8 Predefined macro names
1 The following macro names148) shall be defined by the implementation:
_ _DATE_ _ The date of translation of the preprocessing translation unit: a character
string literal of the form “Mmm dd yyyy”, where the names of the
months are the same as those generated by the asctime function, and the
first character of dd is a space character if the value is less than 10. If the
date of translation is not available, an implementation-defined valid date
shall be supplied.
_ _FILE_ _ The presumed name of the current source file (a character string literal).149)
_ _LINE_ _ The presumed line number (within the current source file) of the current
source line (an integer constant).149)
_ _STDC_ _ The integer constant 1, intended to indicate a conforming implementation.
_ _STDC_HOSTED_ _ The integer constant 1 if the implementation is a hosted
implementation or the integer constant 0 if it is not.
_ _STDC_VERSION_ _ The integer constant 199901L.150)
_ _TIME_ _ The time of translation of the preprocessing translation unit: a character
string literal of the form “hh:mm:ss” as in the time generated by the
asctime function. If the time of translation is not available, an
implementation-defined valid time shall be supplied.
2 The following macro names are conditionally defined by the implementation:
_ _STDC_IEC_559_ _ The integer constant 1, intended to indicate conformance to the
specifications in annex F (IEC 60559 floating-point arithmetic).
148) See ‘‘future language directions’’ (6.11.9).
149) The presumed source file name and line number can be changed by the #line directive.
150) This macro was not specified in ISO/IEC 9899:1990 and was specified as 199409L in
ISO/IEC 9899/AMD1:1995. The intention is that this will remain an integer constant of type long
int that is increased with each revision of this International Standard.
160 Language §6.10.8
©ISO/IEC ISO/IEC 9899:1999 (E)
_ _STDC_IEC_559_COMPLEX_ _ The integer constant 1, intended to indicate
adherence to the specifications in informative annex G (IEC 60559
compatible complex arithmetic).
_ _STDC_ISO_10646_ _ An integer constant of the form yyyymmL (for example,
199712L), intended to indicate that values of type wchar_t are the
coded representations of the characters defined by ISO/IEC 10646, along
with all amendments and technical corrigenda as of the specified year and
month.
3 The values of the predefined macros (except for _ _FILE_ _ and _ _LINE_ _) remain
constant throughout the translation unit.
4 None of these macro names, nor the identifier defined, shall be the subject of a
#define or a #undef preprocessing directive. Any other predefined macro names
shall begin with a leading underscore followed by an uppercase letter or a second
underscore.
5 The implementation shall not predefine the macro _ _cplusplus, nor shall it define it
in any standard header.
Forward references: the asctime function (7.23.3.1), standard headers (7.1.2).

__FUNCDNAME__ 是 VC 自定义的
介绍在 http://msdn.microsoft.com/en-us/library/b0084kay(VS.71).aspx

__FUNCDNAME__ Valid only within a function and returns the decorated name of the enclosing
function (as a string). __FUNCDNAME__ is not expanded if you use the /EP or /P compiler option.

更新完成

用了一个春节的时间,将原来手工页面编写的网页更换为了 WordPress 界面的。总共花费了大约100多个小时。期间的感悟是:360浏览器真的不好用,不知道为什么家里的电脑强制安装了一个360浏览器,我刚开始也没有在意,用着它就开始了WP之旅,不成想几乎上传几次附件之后就要崩溃一次,整个浏览器都没有反应的。后来的某一天,去给一个朋友检修电脑,她也提到浏览器不正常的问题而正好也是360浏览器,始悟一定是它捣鬼的……果真更新之后正常多了。不太清楚,360使用的是IE内核,但是为什么把浏览器搞的如此之不好用?

另外一个感悟是,Wordpress的安装和使用比想象中的简单多了,功能也是异乎寻常的强大。现在可以方便的显示阅读最多的文章,也可以方便的让别人订阅我的RSS。看起来引入新技术,引入新方法能够提高效率才是最顺应潮流的做法。

最后放上两张很久之前CPascal的网站截图,聊作纪念。