.eml 后缀的邮件文件相信大家并不陌生,应该都见过,没见过也不要紧,随便找个邮箱,选择一封邮件,然后导出或者显示邮件原文,就能看到 .eml 文件格式或者内容了。

  在这里科普一下:

  EML 格式是微软公司为 Outlook 和 Outlook Express 开发的文件格式。

  EML 文件是将邮件归档后生成的文件,保留着原来的HTML格式和标题。大多数邮件客户端都支持EML格式。

  .eml 其实是纯文本文件,内容格式为 MIME,继续科普一下:

  MIME,英文全称为“Multipurpose Internet Mail Extensions”,即多用途互联网邮件扩展,是目前互联网电子邮件普遍遵循的邮件技术规范。

  MIME 邮件由邮件头和邮件体两部分组成。

  邮件头包括:标题,送信人,收信人,创建日期,邮件体内容类型和邮件体编码方式等内容。

  邮件体包括:正文,超文本,内嵌数据和附件等内容。

  MIME 技术规范的完整内容由 RFC 2045-2049 定义,包括了信息格式、媒体类型、编码方式等各方面的内容。

  MIME 格式内容具有一定的编码,一般为 base64 或 quoted-printable 编码算法。

  如果 eml 文件数量不多的话,人工或者手工挨个打开阅读是可行的,但是如果一次需要处理成千上万封邮件内容并提取分析附件文件数据,那么就是相当痛苦的一件事了。

  举个例子,例如某黑客入侵渗透了某公司内网邮件服务器,导出了海量邮件 .eml 文件,准备日后做关键信息提取以及数据分析,那么就必须得使用程序自动化批量处理了。

  核总(https://lcx.cc/)由于某次工作需要,需要处理大量 .eml 邮件文件,于是进行了一番研究。

解析方法

  虽然核总在日常工作中经常接触 .eml 文件,但是对其具体结构并不是十分了解,只知道这是纯文本文件,内容具有一定编码,一般以 base64 为主。

  经过一番搜索后,其实目前主流解析邮件文件的方法基本都是先直接读取文本内容,然后逐行进行解码分析(其实就是对 MIME 格式的分析)。

  这种方法虽然简单,但是缺点也很明显,需要自己写大量代码逐行解析 MIME 格式内容,很麻烦、效率低,而且根据每个人经验水平高低,可能产生大量解码错误。

  那么核总(https://lcx.cc/)就想到既然这个格式已经出现很多年了,那么系统是否有自带 dll、函数、组件已经封装了个这个功能呢?避免重复造轮子?

  于是,核总又进行了更深入的搜索+学习,发现既然微软的 Outlook Express 是可以保存和打开并发送 EML 文件的,那么肯定也是可以解析 EML 文件的。

  那么问题就来了,我们是否可以直接利用微软现有的功能呢?答案当然是可以的!那就是“Cdosys.dll”!

Cdosys.dll

  Cdosys.dll 又称为 CDOSYS,包含了一系列邮件相关的 COM 组件,从 Windows 2000 开始被正式引入的,后续的操作系统都支持,关于 Cdosys.dll 的细节,请看 MSDN 文档。

  使用方法也极其简单,以邮件内容解析为例,核总(https://lcx.cc/)在底下用 vbs 脚本写了个简单例子(目前全网最全的功能),其他语言可以照抄。

vbs 例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
dim ado, oMsg
Set ado = CreateObject("Adodb.Stream")
set oMsg = CreateObject("CDO.Message")

' 读取文件二进制数据流
ado.Type = 1 '设置读取模式:1 = Binary,2 = Text
ado.Mode = 3 '1 只读,2 写入,3 读写。
ado.Open
ado.Loadfromfile "C:\Users\lcx.cc\Desktop\3.eml"

' 将EML数据流载入到CDO.Message
oMsg.DataSource.OpenObject ado, "_stream"

' 解析内容(每个字段不一定有,记得进行错误判断)
on error resume next
wscript.echo ado.Size '文件大小
wscript.echo oMsg.Subject '标题
wscript.echo oMsg.From '发信人
wscript.echo oMsg.Sender '寄件人(实际提交邮件的用户或代理的地址)
wscript.echo oMsg.ReplyTo '回信收件人(用户直接回复邮件时, reply-to 就是默认的收件人. 如果用户不指定它, from 就是默认的收件人.)
wscript.echo oMsg.SentOn '发送时间(此消息提交给服务器的日期/时间)
wscript.echo oMsg.CC '抄送
wscript.echo oMsg.BCC '密送
wscript.echo oMsg.To '收信人
wscript.echo oMsg.ReceivedTime '接收时间
wscript.echo oMsg.BodyPart.Charset '邮件内容编码
wscript.echo oMsg.BodyPart.ContentTransferEncoding '通过网络发送时使用的编码
wscript.echo oMsg.HTMLBodyPart.Charset 'Html内容编码
wscript.echo oMsg.HTMLBody 'Html内容
wscript.echo oMsg.TextBodyPart.Charset '文本内容编码
wscript.echo oMsg.TextBody '文本内容

' 附件
wscript.echo oMsg.Attachments.Count
if oMsg.Attachments.Count > 0 Then
    Dim attach 'as CDO.BodyPart
    For Each attach In oMsg.Attachments
        wscript.echo attach.ContentMediaType '附件文件类型
        wscript.echo attach.FileName '附件文件名
        wscript.echo attach.GetDecodedContentStream.SaveToFile(attach.FileName, 2) '将附件二进制数据保存为源文件(1=不存在时新建/存在时报错(默认), 2=不存在时新建/存在时覆盖)
    Next
End if

' 关闭
ado.Close

  上边的代码可以实现邮件内容的提取,例如:收件人、发件人、时间、标题、内容、编码等信息。

  还可以直接提取原始邮件附件二进制文件数据并保存为原文件,以供后续其他分析。

参考资料

  MSDN CDOEX 参考

  MSDN IMessage 接口

  MIME邮件(EML文件)格式分析

  eml文件的解析和发送

  如何使用Visual C#使用Cdosys.dll库发送带附件的电子邮件

重要摘录

  另外摘录了一些重要内容备用,以防以后原网页被删除用作参考。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
[Mail]MIME邮件(EML文件)格式分析
2008-11-10 13:18 阅读(2433)评论(0)
http://hankundev.blog.sohu.com/104046669.html

MIME,英文全称为“Multipurpose Internet Mail Extensions”,即多用途互联网邮件扩展,是目前互联网电子邮件普遍遵循的邮件技术规范。

MIME邮件由邮件头和邮件体两部分组成。

邮件头包括:标题,送信人,收信人,创建日期,邮件体内容类型和邮件体编码方式等内容。

邮件体包括:正文,超文本,内嵌数据和附件等内容。

MIME技术规范的完整内容由RFC 2045-2049定义,包括了信息格式、媒体类型、编码方式等各方面的内容,本文只介绍其中的一些关键的格式和规范。

1.域

MIME邮件中的重要信息都记录在邮件内的各个域中。
域的基本格式是 {域名}:{内容}。
一个域占一行或多行,域的首行左侧不能有空白字符,占多行的域其后续行必须以空白字符开头。
域的信息内容中还可以包含属性,属性之间以“;”分隔,属性格式是{属性名}="{属性值}"。
域的内容的编码格式是"=?"+编码名称+"?"+B/Q+"?"+编码后的字符序列+"?="。(B代表base64的编码方式,Q代表quoted-printable的编码方式)

2.Content-Type域

Content-Type域定义了邮件中各种内容的形式及属性。
Content-Type域的基本格式是 Content-Type:{主类型}/{子类型}。
Content-Type的主类型分text,image,audio,video和application五种离散类型以及multiple和message两种复合类型。
text类型有plain和html两种常用的子类型。
multiple类型有mixed,alternative和related三种常用的子类型。

3.multiple类型

·multiple类型基本格式
multiple类型基本格式是 Content-Type: multipart/{子类型};boundary="{分段标识}"
三种子类型的对比:
------------------------------------------------------------------
|类型名              |邮件种类  |boundary作用                    |
------------------------------------------------------------------
|multiple/mixed      |有附件    |分隔各附件内容和邮件其他内容    |
------------------------------------------------------------------
|multiple/alternative|有超文本  |分隔纯文本,超文本和邮件其他内容|
------------------------------------------------------------------
|multiple/related    |有内嵌资源|分隔各内嵌资源和邮件其他内容    |
------------------------------------------------------------------
·boundary属性
multiple子类型中都定义了各自的boundary属性,邮件中使用boundary中定义的字符串作为标识,将邮件内容分成不同的段,段内每个字段以"--"+boundary中定义的字符串开始,父段则以"--"+boundary中定义的字符串+"--"结束,不同段之间用空行分隔。

·multiple类型的层次关系
------------------------------
|multiple/mixed              |
------------------------------
| |multiple/related        | |
------------------------------
| | |multiple/alternative| | |
------------------------------
| | |纯文本正文          | | |
------------------------------
| | |超文本正文          | | |
------------------------------
| |内嵌资源                | |
------------------------------
|附件                        |
------------------------------

4.Content-Transfer-Encoding域

Content-Transfer-Encoding域定义段内文字的编码方式,不同段可以有不同的编码方式。
MIME邮件中的数据编码普遍采用base64和quoted-printable两种编码。

·base64编码的算法
编码的原则是将输入数据全部转换成由{'A'-'Z','a'-'z','0'-'9','+','/'}64个ASCII字符组成的字符序列。
编码时将需要转换的数据每次取出6bit,然后转换成十进制数字,查询64个ASCII字符组成的字典表,输出对应位置的ASCII码字符。每3个字节的数据内容会被转换成4个字典中的ASCII码字符。当转换后的数据不是4的整倍数时,用"="来填充。

·quoted-printable编码的算法
编码的原则是将输入的数据转换成可打印的ASCII码字符。
编码时根据读入内容来决定是否进行编码,如果读入的字节处于33-60,62-126范围内,属可直接打印的ASCII字符,则直接输出,否则将该字节分为两个4bit,每个用一个十六进制数来表示,然后再前面加上"=",每个需要编码的字节被转换成三个字符来表示。

----------

分开邮件的各个部分,应该是用正则了.分开之后,对内容做读取的时候,需要用base64和qp两种解码,要根据邮件相关信息来确定具体用哪一种.

一般邮件获取内容编码方式的正则的写法应该是下面这个样子

C# code

private const string encodingReg = "(?<=(Content\\-Transfer\\-Encoding\\:)).*";

做一个TextAnalyze方法来统筹分析

C# code

//c 内容
//charset,文字编码方式,utf-8/gb2312等等
//encoding,加密编码方式,包括qp和base64两种
//因为encoding获得的加密编码标志可能不同,所以用了switch.
private string TextAnalyze(string c, string charset, string encoding)
{
    switch (encoding.ToLower())
    {
        case "quoted-printable":
        case "qp":
        case "q":
            try { c = QDecode(Encoding.GetEncoding(charset), c); }
            catch (Exception e) { throw new Exception("TextAnalyze(quoted-printabl)" + e.Message); }
            break;
        case "base64":
        case "b":
            try
            { c = DecodeBase64(charset, encoding); }
            catch (Exception e) { throw new Exception("TextAnalyze(DecodeBase64)" + e.Message); }
            break;
    }
    return c;
}

base64在c#中处理很简单了,就放一个qp的解码.这个也是在网上辛苦搜到的.当时自己水平太凹,总编不好.

C# code

///   <summary>
///     quoted-printable解码程序.
///   </summary>
///   <param   name="encoding">解码目标字符集</param>
///   <param   name="data">需要解码的字符串</param>
///   <returns></returns>
private static string QDecode(System.Text.Encoding encoding, string data)
{
    MemoryStream strm = new MemoryStream(System.Text.Encoding.Default.GetBytes(data));
    int b = strm.ReadByte();
    MemoryStream dStrm = new MemoryStream();
    while (b > -1)
    {
        //   Hex   eg.   =E4
        if (b == '=')
        {
            byte[] buf = new byte[2];
            strm.Read(buf, 0, 2);
            if (!(buf[0] == '\r' && buf[1] == '\n'))
            {
                int val = int.Parse(System.Text.Encoding.Default.GetString(buf), System.Globalization.NumberStyles.HexNumber);
                //int   val   =   int.Parse(System.Text.Encoding.Default.GetString(buf));
                byte[] temp = new Byte[] { (byte)val };
                dStrm.Write(temp, 0, temp.Length);
            }
        }
        else
        {
            string encodedChar = encoding.GetString(new byte[] { (byte)b });
            byte[] d = System.Text.Encoding.Default.GetBytes(encodedChar);
            dStrm.Write(d, 0, d.Length);
        }

        b = strm.ReadByte();
    }
    return encoding.GetString(dStrm.ToArray());
}

当时对我来讲最困难的就是qp这部分了,这部分解决之后其他的都很好办的.不知道你什么情况.

----------

和大家分享一个解决方案,使用这种方法,可以非常容易读取和"EML"邮件相关的所有信息

1、添加COM组件cdosys.dll的引用,如图

2、相关代码
/// <summary>
/// 获取eml文件的主体内容
/// </summary>
/// <param name="file">eml文件的路径</param>
/// <returns>eml文件的主体内容</returns>
public string ReadEML(string file)
{
    CDO.Message oMsg = new CDO.Message();
    ADODB.Stream stm = null;
    //读取EML文件到CDO.MESSAGE,做分析的话,实际是用了下面的部分
    try
    {
        stm = new ADODB.Stream();
        stm.Open(System.Reflection.Missing.Value,
                 ADODB.ConnectModeEnum.adModeUnknown,
                 ADODB.StreamOpenOptionsEnum.adOpenStreamUnspecified,
                 "", "");
        stm.Type = ADODB.StreamTypeEnum.adTypeBinary;//二进制方式读入

        stm.LoadFromFile(file); //将EML读入数据流

        oMsg.DataSource.OpenObject(stm, "_stream"); //将EML数据流载入到CDO.Message,要做解析的话,后面就可以了。

    }
    catch (IOException ex)
    {

    }
    finally
    {
        stm.Close();
    }
    return oMsg.HTMLBody;//oMsg里包含了邮件相关的所有信息
}

有关“cdosys.dll”模块可以到网上进一步了解,使用它可以方面的完成邮件的所有功能

----------

eml文件的解析和发送
https://www.cnblogs.com/a-zx/articles/2179255.html

转自:http://www.zu14.cn/2009/05/20/read-analyze-send-eml-as-mail-by-csharp-cdosys/

最近,我发布了几篇关于 .NET 和 EML 文件的邮件相关 的博文,引来了一些网友的关注与讨论。尤其是对于“如何解析EML文件的内容”和 “发送现有的EML文件”。

目前,比较主流的解析EML文件的方式,基本是对MIME格式的分析,基于对 RFC822及其后续扩展的标准 的理解。但是,此种方法工作量太大,且过于繁琐。

我是个懒人,喜欢找捷径

大家都知道,微软的 outlook express 是可以保存和打开并发送EML文件的。那么很明显,outlook express 肯定是可以解析EML文件的。

问题就来了:我们可不可以利用微软现有的成果呢?

针对这个问题,我们再回到.NET中发送邮件的功能上,为了体现的明显,我们回到.NET 1.1上,.NET 1.1 发送邮件的是 System.Web.Mail ,这个System.Web.Mail 当时是比较弱的,原因就是它是基于 cdosys.dll 的基础上的且并未做富实现。

cdosys.dll是从windows 2000 开始被正式引入的,后续的操作系统都支持,关于cdosys.dll的细节,请看MSDN。

经过一个晚上对cdosys的研究,终于得出了结果:CDOSYS是可以加载eml文件并进行解析和直接发送的。

cdosys属于COM,在.NET使用,需要添加COM引用。

CDOSYS

添加引用,会在项目的引用里出现下面的2项:

image

下面我对发送EML文件,封装了一个类(只做了基本封装,大家可以自己扩展)

/// <summary>
/// 功能: 发送EML文件
/// 作者: 三角猫
/// 网址: http://www.zu14.cn
/// 声明: 转载务必保留此信息
/// </summary>
public class EmlSender
{
    private string emlFilePath;
    private string smtpServer;
    private string smtpServerPort = "25";
    private string smtpUserName;
    private string smtpPassword;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="EmlFilePath">EML文件的绝对路径</param>
    public EmlSender(string EmlFilePath)
    {
        emlFilePath = EmlFilePath;
    }

    /// <summary>
    /// SMTP服务器地址
    /// </summary>
    public string SmtpServer
    {
        set { smtpServer = value; }
    }

    /// <summary>
    /// SMTP服务器端口号
    /// </summary>
    public string SmtpServerPort
    {
        set { smtpServerPort = value; }
    }

    /// <summary>
    /// SMTP服务器认证帐号
    /// </summary>
    public string SmtpUserName
    {
        set { smtpUserName = value; }
    }

    /// <summary>
    /// SMTP服务器认证密码
    /// </summary>
    public string SmtpPassword
    {
        set { smtpPassword = value; }
    }

    /// <summary>
    /// 使用CDOSYS发送EML文件
    /// </summary>
    public void Send()
    {
        CDO.Message oMsg = new CDO.Message();
        CDO.IConfiguration iConfg = oMsg.Configuration;
        ADODB.Fields oFields = iConfg.Fields;

        //设置CDO相关的发送参数,主要是用于SMTP服务器的认证
         ADODB.Field oField = oFields["http://schemas.microsoft.com/cdo/configuration/sendusing"];
        oField.Value = "2";

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpserverport"];
        oField.Value = smtpServerPort;

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpserver"];
        oField.Value = smtpServer;

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/languagecode"];
        oField.Value = "0x0804";

        //下面三项可以自己根据需要去填写,我比较懒
        oField = oFields["http://schemas.microsoft.com/cdo/configuration/sendemailaddress"];
        oField.Value = "";

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpuserreplyemailaddress"];
        oField.Value = "";

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpaccountname"];
        oField.Value = "";
        //------------------------

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout"];
        oField.Value = "60";

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/sendusername"];
        oField.Value = smtpUserName;

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/sendpassword"];
        oField.Value = smtpPassword;

        oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"];
        oField.Value = "1";

        oFields.Update();

        try
        {
            //读取EML文件到CDO.MESSAGE,做分析的话,实际是用了下面的部分
            ADODB.Stream stm = new ADODB.Stream();
            stm.Open(System.Reflection.Missing.Value,
                     ADODB.ConnectModeEnum.adModeUnknown,
                     ADODB.StreamOpenOptionsEnum.adOpenStreamUnspecified,
                     "", "");
            stm.Type = ADODB.StreamTypeEnum.adTypeBinary;//二进制方式读入

            stm.LoadFromFile(emlFilePath); //将EML读入数据流

            oMsg.DataSource.OpenObject(stm, "_stream"); //将EML数据流载入到CDO.Message,要做解析的话,后面就可以了。

            stm.Close();

            oMsg.Send(); //发送
        }
        catch
        {
            throw;
        }
        finally
        {
            oField = null;
            oFields = null;
            oMsg = null;
        }
    }
}

使用方法:

EmlSender eml = new EmlSender(@"d:\a.eml");
eml.SmtpServer = "smtp.zu14.cn";
eml.SmtpServerPort = "25";
eml.SmtpUserName = "admin@zu14.cn";
eml.SmtpPassword = "*****";
eml.Send();

至此,.NET 关于MAIL 和 EML 相关的内容,算是告一段落了。

----------

如何使用Visual C#使用Cdosys.dll库发送带附件的电子邮件
https://support.microsoft.com/zh-cn/help/310212/how-to-use-the-cdosys-dll-library-to-send-an-e-mail-message-with-attac

摘要

本文介绍如何使用Windows 2000库协作数据对象(CDO)(Cdosys.dll)发送带附件的电子邮件。您可以使用本地SMTP服务器或Microsoft Visual C#中的智能主机服务器在电子邮件正文中发送文本或HTML或网页。

注意 Cdosys.dll库也称为CDOSYS。

更多信息

要按照“摘要”部分中的说明使用CDOSYS,请按照下列步骤操作:

启动Microsoft Visual Studio。

在“ 文件”菜单上,单击“ 新建”,然后单击“ 项目”。

在项目类型下,单击Visual C#,然后单击模板下的控制台应用程序。默认情况下,会创建Program.cs。注意在Microsoft Visual C#.NET 2003中,Visual C#更改为Visual C#Projects。默认情况下,创建Class1.cs。

添加对Microsoft CDO For Windows 2000 Library的引用。为此,请按照下列步骤操作:

在“ 项目”菜单上,单击“ 添加引用”。

在COM选项卡上,找到Microsoft CDO For Windows 2000 Library。

注意在Visual C#.NET 2003中,单击“ 选择”。

要接受您的选择,请在“ 添加引用”对话框中单击“ 确定 ”。如果您收到一个对话框来为您选择的库生成包装器,请单击“ 是”。

在代码窗口中,使用以下代码替换所有代码:

namespace CdoSys
{
using System;
class Class1
{
static void Main(string[] args)
{
try
{
CDO.Message oMsg = new CDO.Message();
CDO.IConfiguration iConfg;

iConfg = oMsg.Configuration;

ADODB.Fields oFields;
oFields = iConfg.Fields;

// Set configuration.
ADODB.Field oField = oFields["http://schemas.microsoft.com/cdo/configuration/sendusing"];

//TODO: To send by using the smart host, uncomment the following lines:
//oField.Value = CDO.CdoSendUsing.cdoSendUsingPort;
//oField = oFields["http://schemas.microsoft.com/cdo/configuration/smtpserver"];
//oField.Value = "smarthost";

// TODO: To send by using local SMTP service.
//oField = oFields["http://schemas.microsoft.com/cdo/configuration/sendusing"];
//oField.Value = 1;

oFields.Update();

// Set common properties from message.

//TODO: To send text body, uncomment the following line:
//oMsg.TextBody = "Hello, how are you doing?";

//TODO: To send HTML body, uncomment the following lines:
//String sHtml;
//sHtml = "<HTML>\n" +
//"<HEAD>\n" +
//"<TITLE>Sample GIF</TITLE>\n" +
//"</HEAD>\n" +
//"<BODY><P>\n" +
//"<h1><Font Color=Green>Inline graphics</Font></h1>\n" +
//"</BODY>\n" +
//"</HTML>";
//oMsg.HTMLBody = sHtml;

//TOTO: To send WEb page in an e-mail, uncomment the following lines and make changes in TODO section.
//TODO: Replace with your preferred Web page
//oMsg.CreateMHTMLBody("http://www.microsoft.com",
//CDO.CdoMHTMLFlags.cdoSuppressNone,
//"", "");
oMsg.Subject = "Test SMTP";

//TODO: Change the To and From address to reflect your information.
oMsg.From = "someone@example.com";
oMsg.To = "someone@example.com";
//ADD attachment.
//TODO: Change the path to the file that you want to attach.
oMsg.AddAttachment("C:\\Hello.txt", "", "");
oMsg.AddAttachment("C:\\Test.doc", "", "");
                                    oMsg.Send();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
}
return;
}
}
}

如果TODO出现在代码中,请按照指示修改代码。

要构建和运行程序,请按F5。

验证是否已发送和接收电子邮件。

参考

有关使用Visual Studio进行Microsoft Office开发的详细信息,请参阅以下Microsoft Developer Network(MSDN)网站:
http://msdn.microsoft.com/en-us/library/aa188489(office.10).aspx
有关如何使用CDOSYS的其他信息,请单击下面的文章编号,以查看Microsoft知识库中相应的文章:

310221如何使用Visual C#.NET将Cdosys.dll库嵌入到新消息中

310224如何使用Visual C#.NET使用Cdosys.dll库处理Drop目录中的邮件

310225如何使用Cdosys.dll库通过使用Visual C#.NET将消息保存到文件

上次更新时间:2019年6月10日

----------

CDOSYS.dll 发邮件,邮件体中文乱码

方案整理者:lgg2tmm
发布时间:2011-06-04

给位大侠,请帮忙看一下。
objEmail.TextBody.Charset = "Gb2312" 错误,不能运行。
要是没有这行,可以运行,但是中文是乱码。

Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adUseClient = 3
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordset = CreateObject("ADODB.Recordset")
objConnection.Open "DSN=cdma;uid=cdma_admin;pwd=cdma_admin"
objRecordset.CursorLocation = adUseClient
objRecordset.Open "SELECT * FROM RECEIVING_RELOG WHERE TO_CHAR(SENDMAILDATE,'YYYY') = TO_CHAR(SYSDATE,'YYYY') AND TO_CHAR(SENDMAILDATE,'MM') = TO_CHAR(SYSDATE,'MM') AND TO_CHAR(SENDMAILDATE,'DD') = TO_CHAR(SYSDATE,'DD')-2 AND ISSUE_DATE is null", objConnection, _
adOpenStatic, adLockOptimistic

Do Until objRecordset.EOF

toid = ""

If objRecordset("ReceiverID1")  <> "" then
toid =toid&objRecordset("ReceiverID1")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID2")  <> "" then
toid =toid&objRecordset("ReceiverID2")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID3")  <> "" then
toid =toid&objRecordset("ReceiverID3")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID4")  <> "" then
toid =toid&objRecordset("ReceiverID4")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID5")  <> "" then
toid =toid&objRecordset("ReceiverID5")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID6")  <> "" then
toid =toid&objRecordset("ReceiverID6")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID7")  <> "" then
toid =toid&objRecordset("ReceiverID7")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID8")  <> "" then
toid =toid&objRecordset("ReceiverID8")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID9")  <> "" then
toid =toid&objRecordset("ReceiverID9")&"@"&"freescale.com"&";"
End If

If objRecordset("ReceiverID10")  <> "" then
toid =toid&objRecordset("ReceiverID10")&"@"&"freescale.com"&";"
End If

SendEmailDate = Month(Now()) & "-" & Day(Now()) & "-" & Year(Now())

Set objEmail = CreateObject("CDO.Message")
objEmail.From = "Receiving_Center@freescale.com"
objEmail.To = toid
objEmail.Subject = "Delivery Notification on " & SendEmailDate
objEmail.TextBody.Charset = "Gb2312"

objEmail.TextBody = "Please pick up your goods within 24 hrs from receiving center. If you have any queries, please call our hotline number 13370329111 or Ext.85686516. Thanks." & vbCrLf & vbCrLf & "Delivery Date:" & objRecordset("REC_DATE") & vbCrLf & "Vendor Name:" & objRecordset("SUPPLIER_NAME") & vbCrLf & "PO#:" & objRecordset("PO_NO") & vbCrLf & "English Description:" & objRecordset("ENGLISH_DESC") & vbCrLf & "Chinese Description:" & objRecordset("CHINESE_DESC") & vbCrLf & "MAWB:" & objRecordset("MAWB") & vbCrLf & "HAWB:" & objRecordset("HAWB") & vbCrLf & "Original:" & objRecordset("ORIGIN") & vbCrLf & vbCrLf & "Best Regards," & vbCrLf & "Receiving Center"

objEmail.Configuration.Fields.Item(") = 2
objEmail.Configuration.Fields.Item(") = "remotesmtp.freescale.net"
objEmail.Configuration.Fields.Item(") = 25
objEmail.Configuration.Fields.Update
objEmail.Send
objRecordset.movenext
Loop

objRecordset.Close
objConnection.Close

推荐解决方案
邮件主体=StrConv(邮件主体,vbunicode)
试一下行不行