none
调用哪个类可以实现发送SMTP协议的邮件 RRS feed

答案

  • void CDocument::OnFileSendMail()
    {
    	ASSERT_VALID(this);
    	ASSERT(_afxIsMailAvail);   // update handler always gets called first
    
    	CWaitCursor wait;
    
    	_AFX_MAIL_STATE* pMailState = _afxMailState;
    	if (pMailState->m_hInstMail == NULL)
    		pMailState->m_hInstMail = ::AfxCtxLoadLibraryA("MAPI32.DLL");
    
    	if (pMailState->m_hInstMail == NULL)
    	{
    		AfxMessageBox(AFX_IDP_FAILED_MAPI_LOAD);
    		return;
    	}
    	ASSERT(pMailState->m_hInstMail != NULL);
    
    	ULONG (PASCAL *lpfnSendMail)(ULONG, ULONG_PTR, MapiMessage*, FLAGS, ULONG);
    	(FARPROC&)lpfnSendMail = GetProcAddress(pMailState->m_hInstMail, "MAPISendMail");
    	if (lpfnSendMail == NULL)
    	{
    		AfxMessageBox(AFX_IDP_INVALID_MAPI_DLL);
    		return;
    	}
    	ASSERT(lpfnSendMail != NULL);
    
    	TCHAR szTempName[_MAX_PATH];
    	TCHAR szPath[_MAX_PATH];
    	BOOL bRemoveTemp = FALSE;
    	if (m_strPathName.IsEmpty() || IsModified())
    	{
    		// save to temporary path
    		VERIFY(GetTempPath(_countof(szPath), szPath) != 0);
    		VERIFY(GetTempFileName(szPath, _T("afx"), 0, szTempName) != 0);
    
    		// save it, but remember original modified flag
    		BOOL bModified = IsModified();
    		BOOL bResult = DoSave(szTempName, FALSE);
    		SetModifiedFlag(bModified);
    		if (!bResult)
    		{
    			TRACE(traceAppMsg, 0, "Warning: file save failed during File.Send Mail.\n");
    			return;
    		}
    		bRemoveTemp = TRUE;
    	}
    	else
    	{
    		// use actual file since it isn't modified
    		Checked::tcsncpy_s(szTempName, _countof(szTempName), m_strPathName, _TRUNCATE);
    	}
    #ifdef _UNICODE
    	char szTempNameA[_MAX_PATH];
    	_wcstombsz(szTempNameA, szTempName, _countof(szTempNameA));
    #endif
    
    	// build an appropriate title for the attachment
    	TCHAR szTitle[_MAX_PATH];
    	if (!m_strPathName.IsEmpty())
    		AfxGetFileName(m_strPathName, szTitle, _countof(szTitle));
    	else
    	{
    		Checked::tcsncpy_s(szTitle, _countof(szTitle), m_strTitle, _TRUNCATE);
    		if (CString(::PathFindExtension(m_strTitle)).GetLength() == 0) // no extension
    		{
    			// append the default suffix if there is one
    			CString strExt;
    			CDocTemplate* pTemplate = GetDocTemplate();
    			if (pTemplate != NULL &&
    				pTemplate->GetDocString(strExt, CDocTemplate::filterExt))
    			{
    				if( lstrlen(szTitle) + lstrlen(strExt) < _countof(szTitle) )
    				{
    					Checked::tcscat_s(szTitle, _countof(szTitle), strExt);
    				}
    			}
    		}
    	}
    
    #ifdef _UNICODE
    	char szTitleA[_MAX_PATH];
    	_wcstombsz(szTitleA, szTitle, _countof(szTitleA));
    #endif
    
    	// prepare the file description (for the attachment)
    	MapiFileDesc fileDesc;
    	memset(&fileDesc, 0, sizeof(fileDesc));
    	fileDesc.nPosition = (ULONG)-1;
    #ifdef _UNICODE
    	fileDesc.lpszPathName = szTempNameA;
    	fileDesc.lpszFileName = szTitleA;
    #else
    	fileDesc.lpszPathName = szTempName;
    	fileDesc.lpszFileName = szTitle;
    #endif
    
    	// prepare the message (empty with 1 attachment)
    	MapiMessage message;
    	memset(&message, 0, sizeof(message));
    	message.nFileCount = 1;
    	message.lpFiles = &fileDesc;
    
    	// prepare for modal dialog box
    	AfxGetApp()->EnableModeless(FALSE);
    	HWND hWndTop;
    	CWnd* pParentWnd = CWnd::GetSafeOwner(NULL, &hWndTop);
    
    	// some extra precautions are required to use MAPISendMail as it
    	// tends to enable the parent window in between dialogs (after
    	// the login dialog, but before the send note dialog).
    	pParentWnd->SetCapture();
    	::SetFocus(NULL);
    	pParentWnd->m_nFlags |= WF_STAYDISABLED;
    
    	int nError = lpfnSendMail(0, (ULONG_PTR)pParentWnd->GetSafeHwnd(),
    		&message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
    
    	// after returning from the MAPISendMail call, the window must
    	// be re-enabled and focus returned to the frame to undo the workaround
    	// done before the MAPI call.
    	::ReleaseCapture();
    	pParentWnd->m_nFlags &= ~WF_STAYDISABLED;
    
    	pParentWnd->EnableWindow(TRUE);
    	::SetActiveWindow(NULL);
    	pParentWnd->SetActiveWindow();
    	pParentWnd->SetFocus();
    	if (hWndTop != NULL)
    		::EnableWindow(hWndTop, TRUE);
    	AfxGetApp()->EnableModeless(TRUE);
    
    	if (nError != SUCCESS_SUCCESS &&
    		nError != MAPI_USER_ABORT && nError != MAPI_E_LOGIN_FAILURE)
    	{
    		AfxMessageBox(AFX_IDP_FAILED_MAPI_SEND);
    	}
    
    	// remove temporary file, if temporary file was used
    	if (bRemoveTemp)
    		CFile::Remove(szTempName);
    }
    
    void CDocument::OnUpdateFileSendMail(CCmdUI* pCmdUI)
    {
    	ASSERT_VALID(this);
    
    	if (_afxIsMailAvail == (BOOL)-1)
    	{
    		_afxIsMailAvail = ::GetProfileInt(_T("MAIL"), _T("MAPI"), 0) != 0 &&
    			SearchPath(NULL, _T("MAPI32.DLL"), NULL, 0, NULL, NULL) != 0;
    	}
    
    	// enable the Send... menu item if available
    	pCmdUI->Enable(_afxIsMailAvail);
    	CMenu* pMenu = pCmdUI->m_pMenu;
    	if (!_afxIsMailAvail && pMenu != NULL)
    	{
    		// remove the Send... menu and surrounding separators
    		UINT nStateAbove = pMenu->GetMenuState(pCmdUI->m_nIndex-1, MF_BYPOSITION);
    		UINT nStateBelow = pMenu->GetMenuState(pCmdUI->m_nIndex+1, MF_BYPOSITION);
    		pMenu->RemoveMenu(pCmdUI->m_nIndex, MF_BYPOSITION);
    		if (nStateAbove & nStateBelow & MF_SEPARATOR)
    		{
    			// a separator must be removed since the Send... is gone
    			if (nStateAbove != (UINT)-1)
    				pMenu->RemoveMenu(pCmdUI->m_nIndex-1, MF_BYPOSITION);
    			else if (nStateBelow != (UINT)-1)
    				pMenu->RemoveMenu(pCmdUI->m_nIndex, MF_BYPOSITION);
    		}
    	}
    }
    以上就是MFC的CDocument 类的实现代码,实际上是通过调用位于MAPI32.dll 动态库中的MAPISendMail 函数来实现邮件发送的。 默认的行为CDoument 会调用默认的邮件处理程序,弹出新建邮件界面。

    你只需要 在自己的Document 类中,重载CDocument 的OnFileSendMail 函数,并对基类的代码进行适当剪裁,就可以实现了。主要是忽略DoSave 之类的保存文档的相关操作,忽略MAPIFileDesc 结构体的填充。 对MapiMessage 结构体填充发送地址,邮件内容等信息就可以了。
    • 已标记为答案 XuesongGao 2010年1月31日 6:54
    2010年1月28日 3:04
    版主

全部回复

  • 补充:开发语言为C++,visual studio 2008
    Cedar
    2010年1月27日 1:14
  • 发邮件有两种选择,1)MAPI 2) Windows Mail

    MAPI:
    http://msdn.microsoft.com/zh-cn/library/cc485655%28VS.71%29.aspx

    Windows Mail 实际上是用Outlook Express 的功能帮你发邮件,其实Office 的Outlook 也是用这个组件发邮件的。这个组件是COM的,可以使用ISMTPTransport接口的SendMessage 发邮件。 在Window 7上微软已经决定弃用Windows Mail了,但在Vista XP 下都是可用的。


    2010年1月27日 16:11
    版主
  • 这儿还有一个在Socket 层面上自己做的:
    http://stackoverflow.com/questions/58210/c-smtp-example
    2010年1月27日 16:37
    版主
  • 谢谢您的解答。根据您的解释我清楚了:
    MAPI 需要读取文件以发送附件。如果应用程序使其数据文件在 OnFileSendMail 函数调用期间一直保持打开,则该文件需要以共享模式打开以便可由多个进程访问。

    请问如果不发送附件,邮件内容只用正文没有附件也可以用MAPI发送吗,如果不能还有其他的函数(除了 Windows Mail)可以调用吗?
    Cedar
    2010年1月28日 0:45
  • void CDocument::OnFileSendMail()
    {
    	ASSERT_VALID(this);
    	ASSERT(_afxIsMailAvail);   // update handler always gets called first
    
    	CWaitCursor wait;
    
    	_AFX_MAIL_STATE* pMailState = _afxMailState;
    	if (pMailState->m_hInstMail == NULL)
    		pMailState->m_hInstMail = ::AfxCtxLoadLibraryA("MAPI32.DLL");
    
    	if (pMailState->m_hInstMail == NULL)
    	{
    		AfxMessageBox(AFX_IDP_FAILED_MAPI_LOAD);
    		return;
    	}
    	ASSERT(pMailState->m_hInstMail != NULL);
    
    	ULONG (PASCAL *lpfnSendMail)(ULONG, ULONG_PTR, MapiMessage*, FLAGS, ULONG);
    	(FARPROC&)lpfnSendMail = GetProcAddress(pMailState->m_hInstMail, "MAPISendMail");
    	if (lpfnSendMail == NULL)
    	{
    		AfxMessageBox(AFX_IDP_INVALID_MAPI_DLL);
    		return;
    	}
    	ASSERT(lpfnSendMail != NULL);
    
    	TCHAR szTempName[_MAX_PATH];
    	TCHAR szPath[_MAX_PATH];
    	BOOL bRemoveTemp = FALSE;
    	if (m_strPathName.IsEmpty() || IsModified())
    	{
    		// save to temporary path
    		VERIFY(GetTempPath(_countof(szPath), szPath) != 0);
    		VERIFY(GetTempFileName(szPath, _T("afx"), 0, szTempName) != 0);
    
    		// save it, but remember original modified flag
    		BOOL bModified = IsModified();
    		BOOL bResult = DoSave(szTempName, FALSE);
    		SetModifiedFlag(bModified);
    		if (!bResult)
    		{
    			TRACE(traceAppMsg, 0, "Warning: file save failed during File.Send Mail.\n");
    			return;
    		}
    		bRemoveTemp = TRUE;
    	}
    	else
    	{
    		// use actual file since it isn't modified
    		Checked::tcsncpy_s(szTempName, _countof(szTempName), m_strPathName, _TRUNCATE);
    	}
    #ifdef _UNICODE
    	char szTempNameA[_MAX_PATH];
    	_wcstombsz(szTempNameA, szTempName, _countof(szTempNameA));
    #endif
    
    	// build an appropriate title for the attachment
    	TCHAR szTitle[_MAX_PATH];
    	if (!m_strPathName.IsEmpty())
    		AfxGetFileName(m_strPathName, szTitle, _countof(szTitle));
    	else
    	{
    		Checked::tcsncpy_s(szTitle, _countof(szTitle), m_strTitle, _TRUNCATE);
    		if (CString(::PathFindExtension(m_strTitle)).GetLength() == 0) // no extension
    		{
    			// append the default suffix if there is one
    			CString strExt;
    			CDocTemplate* pTemplate = GetDocTemplate();
    			if (pTemplate != NULL &&
    				pTemplate->GetDocString(strExt, CDocTemplate::filterExt))
    			{
    				if( lstrlen(szTitle) + lstrlen(strExt) < _countof(szTitle) )
    				{
    					Checked::tcscat_s(szTitle, _countof(szTitle), strExt);
    				}
    			}
    		}
    	}
    
    #ifdef _UNICODE
    	char szTitleA[_MAX_PATH];
    	_wcstombsz(szTitleA, szTitle, _countof(szTitleA));
    #endif
    
    	// prepare the file description (for the attachment)
    	MapiFileDesc fileDesc;
    	memset(&fileDesc, 0, sizeof(fileDesc));
    	fileDesc.nPosition = (ULONG)-1;
    #ifdef _UNICODE
    	fileDesc.lpszPathName = szTempNameA;
    	fileDesc.lpszFileName = szTitleA;
    #else
    	fileDesc.lpszPathName = szTempName;
    	fileDesc.lpszFileName = szTitle;
    #endif
    
    	// prepare the message (empty with 1 attachment)
    	MapiMessage message;
    	memset(&message, 0, sizeof(message));
    	message.nFileCount = 1;
    	message.lpFiles = &fileDesc;
    
    	// prepare for modal dialog box
    	AfxGetApp()->EnableModeless(FALSE);
    	HWND hWndTop;
    	CWnd* pParentWnd = CWnd::GetSafeOwner(NULL, &hWndTop);
    
    	// some extra precautions are required to use MAPISendMail as it
    	// tends to enable the parent window in between dialogs (after
    	// the login dialog, but before the send note dialog).
    	pParentWnd->SetCapture();
    	::SetFocus(NULL);
    	pParentWnd->m_nFlags |= WF_STAYDISABLED;
    
    	int nError = lpfnSendMail(0, (ULONG_PTR)pParentWnd->GetSafeHwnd(),
    		&message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
    
    	// after returning from the MAPISendMail call, the window must
    	// be re-enabled and focus returned to the frame to undo the workaround
    	// done before the MAPI call.
    	::ReleaseCapture();
    	pParentWnd->m_nFlags &= ~WF_STAYDISABLED;
    
    	pParentWnd->EnableWindow(TRUE);
    	::SetActiveWindow(NULL);
    	pParentWnd->SetActiveWindow();
    	pParentWnd->SetFocus();
    	if (hWndTop != NULL)
    		::EnableWindow(hWndTop, TRUE);
    	AfxGetApp()->EnableModeless(TRUE);
    
    	if (nError != SUCCESS_SUCCESS &&
    		nError != MAPI_USER_ABORT && nError != MAPI_E_LOGIN_FAILURE)
    	{
    		AfxMessageBox(AFX_IDP_FAILED_MAPI_SEND);
    	}
    
    	// remove temporary file, if temporary file was used
    	if (bRemoveTemp)
    		CFile::Remove(szTempName);
    }
    
    void CDocument::OnUpdateFileSendMail(CCmdUI* pCmdUI)
    {
    	ASSERT_VALID(this);
    
    	if (_afxIsMailAvail == (BOOL)-1)
    	{
    		_afxIsMailAvail = ::GetProfileInt(_T("MAIL"), _T("MAPI"), 0) != 0 &&
    			SearchPath(NULL, _T("MAPI32.DLL"), NULL, 0, NULL, NULL) != 0;
    	}
    
    	// enable the Send... menu item if available
    	pCmdUI->Enable(_afxIsMailAvail);
    	CMenu* pMenu = pCmdUI->m_pMenu;
    	if (!_afxIsMailAvail && pMenu != NULL)
    	{
    		// remove the Send... menu and surrounding separators
    		UINT nStateAbove = pMenu->GetMenuState(pCmdUI->m_nIndex-1, MF_BYPOSITION);
    		UINT nStateBelow = pMenu->GetMenuState(pCmdUI->m_nIndex+1, MF_BYPOSITION);
    		pMenu->RemoveMenu(pCmdUI->m_nIndex, MF_BYPOSITION);
    		if (nStateAbove & nStateBelow & MF_SEPARATOR)
    		{
    			// a separator must be removed since the Send... is gone
    			if (nStateAbove != (UINT)-1)
    				pMenu->RemoveMenu(pCmdUI->m_nIndex-1, MF_BYPOSITION);
    			else if (nStateBelow != (UINT)-1)
    				pMenu->RemoveMenu(pCmdUI->m_nIndex, MF_BYPOSITION);
    		}
    	}
    }
    以上就是MFC的CDocument 类的实现代码,实际上是通过调用位于MAPI32.dll 动态库中的MAPISendMail 函数来实现邮件发送的。 默认的行为CDoument 会调用默认的邮件处理程序,弹出新建邮件界面。

    你只需要 在自己的Document 类中,重载CDocument 的OnFileSendMail 函数,并对基类的代码进行适当剪裁,就可以实现了。主要是忽略DoSave 之类的保存文档的相关操作,忽略MAPIFileDesc 结构体的填充。 对MapiMessage 结构体填充发送地址,邮件内容等信息就可以了。
    • 已标记为答案 XuesongGao 2010年1月31日 6:54
    2010年1月28日 3:04
    版主
  • Good~:)
    0xBAADF00D
    2010年1月29日 15:44
    版主