當你需要在VC程序中提供郵件支持功能的時候,你有許多種選擇:
1)根據SMTP,POP3,MIME等協議從零開始實現。這要求熟悉RFC 821,RFC 822,RFC 1123, RFC 1652, RFC 1939, RFC2045-2049等一系列協議, 您可以實現一切可能實現的功能,但同時需要花大量的時間。
2)利用一些免費的封裝類,比如CSMTP,CPOP3。雖然可以達到快速實現的目的,但功能非常有限。
3)利用MAPI調用支持MAPI的郵件發送程序( 例如:Outlook )發送郵件,這種實現方式需要客戶端安裝指定的郵件發送程序,不夠靈活
4)利用現成的組件例如:w3 JMail組件來構建您的系統,JMail組件功能強大,可以輕松完成郵件發送、接收、加密、集群傳輸等工作。
雖然JMail用得最多的是ASP下的應用,但它在其它語言中都能夠很好地工作,本文將針對w3 JMail 4.3在VC中的使用展開討論。
一、准備篇
1.1 組件的安裝
因為JMail4.3與以前的版本相比接口有了變動,所以推薦您使用4.3版本,您可以到以下網址下載JMail 4.3
DIMAC公司 http://www.dimac.net/
VC知識庫 http://www.vckbase.com/tools
1.2 快速郵件發送程序MINI版
我們從這個小例子大致了解一下JMail組件的工作過程
#import "jmail.dll" // 導入jmail.dll, 假如jmail.dll不在工程目錄下,您需要寫上完整路徑
void main()
{
CoInitialize(NULL); // COM的初始化
{
// 創建SpeedMailer實例
jmail::ISpeedMailerPtr pSpeedMailer("JMail.SpeedMailer");
// 發送郵件
pSpeedMailer->SendMail("[email protected]", // 發件人郵箱
"[email protected]", // 收件人郵箱
"主題:你好!", // 主題
"正文:大家好才是真的好!", // 正文
"smtp.163.com"); // SMTP服務器
}
CoUninitialize();
}
看了上述代碼您肯定會說真的很容易,這個例子是利用了ISpeedMailer接口來發送簡單的郵件,我想大家一看就清楚。
為了使這個流程看起來不至於令人生畏,我把異常捕捉省略了,在實際使用中應該加入異常捕捉,具體方法請見下文。
1.3 異常捕捉
這裡的異常捕捉與我們所使用的捕獲COM異常沒有兩樣,在這裡稍加描述只是提高本文的完整性,請看如下代碼:
try
{
...
... 發送代碼
...
}
catch( _com_error & e)
{
cerr << "錯誤號: 0x" << hex << e.Error() << endl;
cerr << "錯誤信息: " << e.ErrorMessage() << endl;
cerr << "錯誤描述: " << e.Description() << endl;
}
二、郵件發送篇
下圖是本文附帶的郵件發送程序運行效果圖:
2.1 利用IMessage接口發送帶附件的郵件
在前面我們用ISpeedMailer接口來發送郵件,但功能有限,IMessage接口為我們提供了功能完備的發送功能,請看下面的例子:
jmail::IMessagePtr pMessage("JMail.Message");
// 發件人郵箱
pMessage->From = "[email protected]";
// 發件人姓名
pMessage->FromName = "我的名字";
// 添加收件人1, 無收件人姓名與PGP KEY
pMessage->AddRecipient("[email protected]","","");
// 添加收件人2, 無PGP KEY
pMessage->AddRecipient("[email protected]","收件人名字","");
// 添加收件人3,這裡的PGP KEY只是作為示例,實際中需要用PGP軟件生成
pMessage->AddRecipient("[email protected]","收件人名字","R9Rb7decrQWINuce3uFc0xDG");
// 優先級設置,1-5逐次降低, 3為中級
pMessage->Priority = 3;
// 編碼方式設置, 默認是iso-8859-1
pMessage->Charset = "GB2312";
// 主題
pMessage->Subject = "郵件主題";
// 正文
pMessage->Body = "郵件正文\r\n";
// 如有必要,可以再添加一些正文
pMessage->AppendText("VC知識庫www.VCKBASE.com歡迎您! 祝您身體健康!");
// 添加附件
pMessage->AddAttachment("C:\\CONFIG.SYS", VARIANT_FALSE, "application/octet-stream");
pMessage->AddAttachment("C:\\test.gif", VARIANT_TRUE, "image/gif");
// 開始發送
pMessage->Send("mailserver.com", VARIANT_FALSE);
2.2 發送HTML格式的郵件
在這裡我們需要完成的功能是發送一封帶圖像的HTML郵件,圖像存儲在本地郵件附件裡,而不是通過遠地鏈接,實現的關鍵是如何引用
附件中的圖像:
在調用AddAttachment時它會返回一個稱為content id的字串,比如它返回的是"AABBCCDD",則我們用<IMG SRC="cid:AABBCCDD">這種方式
來引用該圖像,請看如下代碼片斷:
......
// 這裡添加Body是為了使郵件浏覽者在沒有切換到HTML時給予提醒
pMessage->Body = "郵件采用HTML格式,請切換到HTML模式。\r\n";
// 添加附件
_bstr_t bstrCID = pMessage->AddAttachment("D:\\logo.gif", VARIANT_TRUE, "image/gif"); // 返回content id
// 根據content id 生成IMG HTML標記
char pImgHTML[200];
strcpy(pImgHTML, "<IMG SRC=''cid:");
strcat(pImgHTML, (char*)bstrCID);
strcat(pImgHTML, "''>");
// 添加HTML正文內容
pMessage->HTMLBody = "<H2>VC知識庫</H2>";
pMessage->AppendHTML("<BR>www.VCKBASE.com ");
pMessage->AppendHTML(pImgHTML);
// 開始發送
pMessage->Send("", VARIANT_FALSE);
關於content id更詳細的內容請閱讀MIME協議
2.3 特殊郵件頭的構造
To,From,Subject,Message-ID,Date,Received等是標准的郵件頭,自定義郵件頭會自動在前面加X, 例如X-Originating-IP
// 標准郵件頭
pMessage->AddNativeHeader("Message-ID", "VCKBASE:ABCDEFG1234567");
pMessage->AddNativeHeader("Date", "1800-1-1");
// 自定義郵件頭
pMessage->AddHeader("Originating-IP", "123.345.567.789"); // 給它個假冒偽劣IP
pMessage->AddHeader("Mailer", "MyMailBox 1.0"); // 郵件發送器名稱
pMessage->AddHeader("Company", "MyCompany"); // 隨便加點定制信息
有時收到郵件的時候會提示:發件人請求一個收條以表示你已經閱讀過這封郵件,您願意發送一個收條嗎?
發送這樣的信,我們只需要:
pMessage->AddNativeHeader("Disposition-Notification-To", "[email protected]");
[email protected]是回復收條的郵箱。
2.4 郵件服務器需要發信認證的處理
一些SMTP服務器需要認證信息,您需要在Send時提供用戶名與密碼,例如:
pMessage->Send("username:[email protected]")
2.5 不通過SMTP中繼服務器發送郵件
在發信時我們一般把信件提交給SMTP中繼服務器,SMTP中繼服務器負責和目標郵局聯系,並最終將信件提交到收件人所在的郵件服務器。
在沒有可利用的SMTP中繼服務器時,我們可以將服務器名設置為空,例如:
pMessage->Send("", VARIANT_FALSE);
這樣,組件會通過DNS查詢目標域(例如:163.com)中的MX記錄,直接將信件提交到MX記錄中所指定的郵件服務器中。
2.6 經代理網關發送郵件
上文所說的SMTP中繼服務器上的郵件傳輸代理程序(MTA),與WINGATE,CPROXY等代理軟件提供的SMTP代理工作原理是一樣的,所以具體的發送過程也一樣,例如:
代理網關地址 192.16.10.11, 要往[email protected]發信,代碼如下:
......
pMessage->AddRecipient("[email protected]","","");
pMessage->Send("192.16.10.11", VARIANT_FALSE);
2.7 關於郵件的加密傳輸與集群發送
JMail的免費版本並沒有提供加密傳輸與集群發送功能,要想使用這些功能,您需要安裝無限制的JMail標准版或專業版。
對於加密傳輸,您還需要安裝PGP軟件,例如:PGPFreeware
集群發送功能在擁有成千上萬個客戶時是非常有用的,定制好模板後,數據庫中的記錄自動填入模板生成信件發送。
由於筆者只有免費版本的JMail,所以這一功能未能嘗試。
雖然免費版未提供集群發送功能,但我們可以只使用IMessage接口結合數據庫來實現集群發送功能.
2.8 郵件發送時的常見錯誤
當所設置的SMTP服務器不存在,錯誤信息:
The message was undeliverable. All servers failed to receive the message
如果沒有設置SMTP服務器時,試圖發送一封錯誤郵件地址的信,
例如:[email protected] 將返回如下錯誤信息:
WSAGetLastError() returned 11001, Host not found
許多SMTP服務器不支持轉發,或支持轉發但不轉發MAIL FROM不是本地帳號的信,錯誤信息:
The message was undeliverable. All servers failed to receive the message
許多SMTP服務器不接收外部的郵件,例如smtp.sohu.com,所以想要向SOHU發信你可以不設郵件服務器, 而是通過
DNS查詢郵件交換服務器,或者直接將郵件服務器設置為sohumx.sohu.com。
像SOHU信箱會對一些發件人信箱進行過濾,例如您將發件人信箱設為[email protected],或者發件人信箱與收件人信箱一致,
發信時將返回:554<[email protected]>:Sender address rejected:Access denied
(未完待續...)
本文配套源碼