用Visual C++編寫電子郵件程式 (轉)

gugu99發表於2007-08-17
用Visual C++編寫電子郵件程式 (轉)[@more@]一、概述
本文主要講述如何使用Visual C++用M編寫E-。MAPI是包含在之中的,因此不需要其他額外的部件。MAPI有以下三種形式:

SMAPI,Simple MAPI,簡單的MAPI

CMC,Common Messaging Calls,一般通訊

完整的MAPI
SMAPI和CMC都包含在完整的MAPI中,當想一些高階操作,比如編寫自己的的時候,必須使用完整的MAPI。本文主要闡述如何編寫能夠收發電子的程式,因此使用SMAPI就足夠了。

二、編寫電子郵件程式
3-1 初始化MAPI

要使用MAPI,必須首先對它進行初始化。初始化包括以下三個步驟:

裝載MAPI32.DLL動態連結庫

找到想要呼叫的MAPI地址

登入到電子郵件
3-1-1 裝載MAPI32.DLL

要裝載MAPI,使用者必須程式執行時動態的裝載一個動態連結庫。LoadLibrary函式提供了此功能,它定位一個動態連結庫,並返回HINSTANCE局柄(需要儲存該控制程式碼)。

LoadLibrary的語法如下:
LoadLibrary ( lpLibFileName );
其中lpLibFileName為LPCTSTR結構變數,
是所要呼叫的庫的路徑和名稱。

程式示例:
// 呼叫MAPI32.DLL並計算函式地址
HINSTANCE hInstMail;
hInstMail = ::LoadLibrary ( “MAPI32.DLL” );
if ( hInstMail == NULL )
{
// 錯誤處理
// 受篇幅限制,下面的錯誤處理部分省略
}

3-1-2 確定函式地址

由於MAPI32.DLL是被動態裝載的,因此不知道所要呼叫的函式地址,也就不能一開始就呼叫它們,而要透過函式名獲得函式的地址,並在動態連結庫中查詢每一個函式並核實。因此首先必須為這些函式宣告指標

程式示例:
// 為MAPI32.DLL中的函式宣告函式指標
ULONG (PASCAL *lpfnMAPI) (LHANDLE lhSession,
ULONG ulUIParam, lpMapiMessage lpMessage,
FLAGS flFlags, ULONG ulReserved);
ULONG (PASCAL *lpfnMAPIResolveName) (LHANDLE lhSession,
ULONG ulUIParam, LPTSTR lpszName,
FLAGS ulFlags, ULONG ulReserved,
lpMapiRecipDesc FAR *lppRecip);
ULONG (FAR PASCAL *lpfnMAPILogon)(ULONG ulUIParam,
LPSTR lpszProfileName, LPSTR lpszPass,
FLAGS flFlags, ULONG ulReserved,
LPLHANDLE lplhSession);
ULONG (FAR PASCAL *lpfnMAPILogoff)(LHANDLE lhSession,
ULONG ulUIParam, FLAGS flFlags,
ULONG ulReserved);
ULONG (FAR PASCAL *lpfnMAPIFreeBuffer)(LPVOID lpBuffer);
ULONG (FAR PASCAL *lpfnMAPIAddress)(LHANDLE lhSession,
ULONG ulUIParam, LPSTR lpszCaption,
ULONG nEditFields, LPSTR lpszLabels,
ULONG nRecips, lpMapiRecipDesc lpRecips,
FLAGS flFlags, ULONG ulReserved,
LPULONG lpnNewRecips,
lpMapiRecipDesc FAR *lppNewRecips);
ULONG (FAR PASCAL *lpfnMAPIFindNext)(LHANDLE lhSession,
ULONG ulUIParam, LPSTR lpszMessageType,
LPSTR lpszSeedMessageID, FLAGS flFlags,
ULONG ulReserved, LPSTR lpszMessageID);
ULONG (FAR PASCAL *lpfnMAPIReadMail)(LHANDLE lhSession,
ULONG ulUIParam, LPSTR lpszMessageID,
FLAGS flFlags, ULONG ulReserved,
lpMapiMessage FAR *lppMessage);

為了決定每一個函式的地址,必須為每一個函式呼叫GetProcAddress。

GetProcAddress的語法為:
GetProcAddress (hModule, lpProcName);
其中,hModule為HMODULE結構,是所呼叫DLL模組的控制程式碼;
lpProcName為LPCSTR結構,是函式名稱。

程式示例:
// 找到MAPI32.DLL函式的地址,並將它們儲存在函式指標變數裡
(FROC&) lpfnMAPISendMail = GetProcAddress(hInstMail,
“MAPISendMail”);
(FARPROC&) lpfnMAPIResolveName = GetProcAddress(
hInstMail, “MAPIResolveName”);
(FARPROC&) lpfnMAPILogon = GetProcAddress(hInstMail,
“MAPILogon”);
(FARPROC&) lpfnMAPILogoff = GetProcAddress(hInstMail,
“MAPILogoff”);
(FARPROC&) lpfnMAPIFreeBuffer = GetProcAddress(
hInstMail, “MAPIFreeBuffer”);
(FARPROC&) lpfnMAPIAddress = GetProcAddress(hInstMail,
“MAPIAddress”);
(FARPROC&) lpfnMAPIFindNext = GetProcAddress(hInstMail,
“MAPIFindNext”);
(FARPROC&) lpfnMAPIReadMail = GetProcAddress(hInstMail,
“MAPIReadMail”);


3-1-3 登入到電子郵件物件

使用者必須在電子郵件中登入,才能實現MAPI的各種功能。MAPI提供了登入的三種選擇:

登入到一個已經存在的物件。

登入到一個新物件,用的方法確定解釋新資訊。

使用對話方塊提示使用者登入。
我們通常選擇登入到一個已經存在的電子郵件物件,因為合作使用者通常會保持自己的電子郵件程式處於啟用狀態。登入通常使用MAPI提供的函式lpfnMAPILogon。

lpfnMAPILogon的語法為:
lpfnMAPILogon (lpszProfileName, lpszPassword, flFlags,
ulReserved, lplhSession );

其中,lpszProfileName指向一個256字元以內的登入名稱,lpszPassword指向密碼,它們均為LPTSTR結構。flFlags為FLAGS結構,其值詳見表1。ulReserved必須為0。lplhSession為輸出SMAPI的控制程式碼。

表1:lpfnMAPILogon函式中flFlags的值
值 意義
MAPI_FORCE_
在函式呼叫返回之前使用者的所有郵件。
如果MAPI_FORCE_DOWNLOAD沒有被設定,
那麼信件能夠在函式呼叫返回後在後臺被下載。
MAPI_NEW_SESSION 建立一個新會話,
而不是獲得環境的共享會話。如果MAPI_NEW_SESSION沒有被設定,
MAPILogon使用現有的共享會話。
MAPI_LOGON_UI 顯示一個登入對話方塊來提示使用者輸入登入資訊。
例如檢查使用者電子郵件時便是如此。
MAPI_PASSWORD_UI MAPILogon只允許使用者輸入電子郵件的密碼,
而不許改動賬號。

程式示例:
LHANDLE lhSession;
ULONG lResult = lpfnMAPILogon(0, NULL, NULL, 0, 0,
&lhSession);
if (lResult != SUCCESS_SUCCESS)
//SUCCESS_SUCCESS在MAPI.H中被定義
{
// 錯誤處理
}


3-2 閱讀電子郵件

MAPIFindNext和MAPIReadMail使用與閱讀E-mail的兩個基本函式。MAPIFindNext用於定位第一封或下一封電子郵件並返回標識號,MAPIReadMail返回以該標識號為基礎的電子郵件的內容。另外,一個常用的函式是MAPIFreeBuffer,用於釋放。

3-2-1 定位到第一封信

要找到第一封信,需要使用MAPIFindNext函式,其函式宣告如下:

ULONG FAR PASCAL MAPIFindNext(LHANDLE lhSession,
ULONG ulUIParam, LPTSTR lpszMessageType,
LPTSTR lpszSeedMessageID, FLAGS flFlags,
ULONG ulReserved, LPTSTR lpszMessageID )

其中,lhSession為提交SMAPI的會話控制程式碼 ;ulUIParam為父窗體的控制程式碼;lpszMessageType指向一個字串,用來鑑別郵件型別,並加以查詢;lpszSeedMessageID為指向起始資訊ID的指標,其值為0時,MAPIFindNext獲得第一封電子郵件;flFlags的值見表2;ulReserved必須為0;lpszMessageID為輸出值,它是指向資訊ID地址的指標。

表2:MAPIFindNext函式中flFlags的值

值 意義
MAPI_GUARANTEE_FIFO 按郵件傳送的時間順序接受電子郵件。
MAPI_LONG_MSGID 返回信件識別符號可達512字元。
MAPI_UNREAD_ONLY 只列舉沒有閱讀過的電子郵件。

程式示例:
// 找到第一條沒有閱讀的電子郵件
char pMessageID [513];
ULONG lResult = lpfnMAPIFindNext(lhSession, NULL, NULL,
NULL, MAPI_LONG_MSGID | MAPI_UNREAD_ONLY,
0, pMessageID);
3-2-2 閱讀資訊
當信件ID被獲取後,就可以呼叫MAPIReadMail
閱讀實際的E-mail資訊了。MAPIReadMail的函式宣告如下:
ULONG FAR PASCAL MAPIReadMail(LHANDLE lhSession,
ULONG ulUIParam, LPTSTR lpszMessageID,
FLAGS flFlags, ULONG ulReserved,
lpMapiMessage FAR * lppMessage);
其中,lppMessage為指向MapiMessage的指標;
除flFlags外的其他引數與lpfnFindNext函式的同名引數意義相同,
flFlags引數的值見表3:

表3:MAPIReadMail函式中flFlags的值:
值 意義
MAPI_BODY_AS_FILE 將郵件資訊寫到一個臨時中,
並且將它作為第一個附件新增到附件列表中。
MAPI_ENVELOPE_ONLY 只讀取郵件標題。
MAPI_PEEK 讀完郵件之後不把它標記為“已讀”。
MAPI_SUPPRESS_ATTACH MAPIReadMail函式不複製附件,
但是將郵件文字寫入MapiMessage結構中。

程式示例:
// 讀取電子郵件
long nFlags = MAPI_SUPPRESS_ATTACH;
if (!bMarkAsRead)
nFlags = nFlags | MAPI_PEEK;
lResult = lpfnMAPIReadMail(lhSession, NULL, pMessageID,
nFlags, 0, &pMessage);
if (lResult != SUCCESS_SUCCESS);
return false;

如果呼叫成功,就可以訪問MapiMessage結構了(使用pMessage):
pMessage- >ulReserved:0
pMessage- >lpszSubject:郵件標題
pMessage- >lpszNoteText:郵件資訊
pMessage- >lpszMessageType:郵件型別

pMessage- >DateReceived:接收時間
pMessage- >lpszConversationID:郵件所屬的會話執行緒ID
pMessage- >flFlags:其值見表4

表4:MapiMessage結構中的flFlags
值 意義
MAPI_RECEIPT_REQUESTED 接收通知被申請。
客戶端應用程式在傳送訊息時設定該項。
MAPI_SENT 郵件已被髮送。
MAPI_UNREAD 郵件是“未讀”狀態。

pMessage- >lpOriginator:指向MapiRecipDesc結構,包含發件人資訊。
pMessage- >nRecipCount:信件者數目。
pMessage- >lpRecips:指向MapiRecipDesc結構陣列,包含接收者資訊。
pMessage- >nFileCount:附件數量。
pMessage- >lpFiles:指向MapiFileDesc結構陣列,
每一個結構包含一個檔案附件。


3-2-3 釋放記憶體

在訪問另一條信件以前應當釋放記憶體,否則會出現記憶體洩漏。

程式示例:

// 釋放記憶體
lpfnMAPIFreeBuffer(pMessage);
3-2-4 定位到下一條信件
定位到下一條信件依然使用MAPIFindNext函式,
該函式宣告及引數意義詳見3-2-1節。下面示範如何定位到下一條信件。

程式示例:
// 定位到下一條沒有閱讀的信件
ULONG lResult = lpfnMAPIFindNext(lhSession, NULL, NULL,
pMessageID, MAPI_LONG_MSGID|MAPI_UNREAD_ONLY,
0, pMessageID);


3-3 傳送電子郵件

傳送電子郵件的一般步驟:

1. 建立MapiMessage結構物件

2. 呼叫MAPIResolveName使傳送者名稱合法

3. 新增附件

4. 呼叫MAPISendMail傳送電子郵件

5. 呼叫MAPIFreeBuffer釋放記憶體

下面詳細分別詳細闡述。

3-3-1 建立MapiMessage結構物件

對於MapiMessage結構,3-2-2節已經做過介紹,下面一步步介紹如何設定其中的值:

1. 為MapiMessage物件分配記憶體:

MapiMessage message;
Memset(&message, 0, sizeof(message));

2. 將ulReserved設定為0:

message.ulReserved = 0;

3. 設定資訊型別指標lpszMessageType,可以為NULL:

message.lpszMessageType = NULL;

4. 設定信件標題(lpszSubject):

char subject[512];
strcpy(subject, sSubject);
message.lpszSubject = subject;

5. 設定信件內容:

char text[5000];
strcpy(text, sMessage);
message.lpszNoteText = text;

6. 設定flFlags標識,詳見3-2-2節中表4:

message.flFlags = MAPI_SENT;

7. 用一個指向MapiRecipDesc結構的指標設定傳送者資訊(lpOriginator),或將其設定為NULL:

message.lpOriginator = NULL;

8. 設定接收者數目(nRecipCount),可以是1或更多:

message.nRecipCount = 1;

9. 設定接收者資訊(lpRecips),詳見3-3-2節

10. 設定附件數量(nFileCount)

11. 設定附件資訊,詳見3-3-3節

b3-3-2 正確設定接收者資訊

設定接收者資訊時,應當使用MAPIResolveName函式來為MapiRecipDesc結構物件分配記憶體,並返回一個指標,該指標將被儲存在MapiMessage結構的lpRecips中。MAPIResolveName的函式宣告如下:

ULONG FAR PASCAL MAPIResolveName(LHANDLE lhSession,
ULONG ulUIParam, LPTSTR lpszName,
FLAGS flFlags, ULONG ulReserved,
lpMapiRecipDesc FAR * lppRec)

其中lppRecip即為前面提到的返回的指標。除flFlags外其餘引數與前幾個函式意義相同。flFlags的值詳見表5。

表5:MAPIResolveName中flFlags的值
值 意義
MAPI_AB_NOMODIFY 對話方塊為只讀。如果MAPI_DIALOG被設定,
那麼該項將被忽略。
MAPI_DIALOG 顯示一個名稱解決方案的對話方塊
MAPI_LOGON_UI 如果需要的話,將會顯示儀個對話方塊讓使用者登入
MAPI_NEW_SESSION 新建一個會話

程式示例:
char recipient[512];
strcpy(recipient, sTo);
lResult = lpfnMAPIResolveName(lhSession, 0, recipient,
0, 0, &message.lpRecips);


3-3-3 新增附件

下面的程式示例將演示如何在電子郵件中包含附件。只有一點需要說明:MapiFileDesc結構中flFlags的值,詳見表6。

表6:MapiFileDesc結構中flFlags的值
值 意義
MAPI_OLE 附件是OLE物件。
MAPI_OLE_STATIC 附件是靜態OLE物件。
0 附件將被視為資料檔案

程式示例:
// 設定附件資訊
CString sPath, ileName;
MapiFileDesc FileInfo;
char path[512];
char filename[512];
if (sAttachment == “”)
message.nFileCount = 0;
else
{
int nP= sAttachment.ReverseFind(‘’);
if (nPos == -1)
{
sPath = sAttachment;
}
else
{
sPath = sAttachment;
sFilename = sAttachment.Mid(nPos +1);
}
strcpy(path, sPath);
strcpy(filename, sFilename);

message.nFileCount = 1;
FileInfo.ulReserved = 0;

FileInfo.flFlags = 0;

FileInfo.nPosition = sMessage.GetLength() –1;
FileInfo.lpszPathName = path;
FileInfo.lpszFileName = filename;
FileInfo.lpFileType = NULL;
message.lpFiles = & m_FileInfo;
}


3-3-4 傳送電子郵件

使用MAPISendMail傳送電子郵件,其宣告如下:

ULONG FAR PASCAL MAPISendMail (LHANDLE lhSession,
ULONG ulUIParam, lpMapiMessage lpMessage,
FLAGS flFlags, ULONG ulReserved )

其中,flFlags的允許值為MAPI_DIALOG、MAPI_LOGON_UI和MAPI_NEW_SESSION,其意義與前幾個函式中同名標識意義相同。

程式示例:

lResult = lpfnMAPISendMail(0, 0, &m_message, 0, 0);


3-3-5 釋放記憶體

程式示例:
lpfnMAPIFreeBuffer(m_message.lpRecips);

四、小結
本文比較具體的介紹並演示了編寫一個電子郵件程式的核心部分,如果讀者要編寫電子郵件程式,還需要進行的處理:

1. 加上錯誤處理程式碼。受篇幅限制,本文的程式示例中只有兩處為錯誤處理留空,比較它們的異同。電子郵件程式是非常容易出錯的,因此除這兩處外要在主要函式呼叫完成後都加上錯誤處理,或使用try throw catch塊處理例外。

2. 加上UI處理。

另外,本文所闡述的方法比較簡單易行,事實上,有關電子郵件的程式遠比這複雜得多,因此讀者若需要編寫一個功能強大的電子郵件程式,需要精通MAPI和SMTP/POP3等;如果讀者要編寫一個電子,那麼不妨在精通MAPI和SMTP/POP3之後,閱讀一些有關 Server的資料。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-963554/,如需轉載,請註明出處,否則將追究法律責任。

相關文章