使用 VC++ ATL/COM 开发 Outlook 加载项





5.00/5 (4投票s)
即时控制 Outlook 和 Outlook 的各种操作。
介绍
这些代码是用 VC++ ATL/COM 编写的,用于创建 Outlook 加载项并执行与 Outlook 相关的各种任务。熟悉 Outlook 操作的人可以理解并扩展这些代码来执行其他任务。它有助于创建 Outlook 加载项、创建按钮和各种控件、创建约会、检索所有约会以及连接到数据库并从数据库读写值。
我保证未来会用清晰的代码来增强各种其他操作。
使用代码
- 启动 Visual Studio,然后点击“新建项目” -> “扩展性”,然后从中选择“共享加载项”。
- 确保选中“使用 Visual C++/ATL 创建加载项”和“我希望我的加载项在宿主应用程序加载时加载”。
- 生成解决方案以确保其能够成功编译。
- 接下来,打开“类视图”,右键单击您的
CConnect
类,然后选择“添加” -> “实现接口…” 在弹出的对话框中,选择“Microsoft Office 12.0 Object Library <2.4>”类型库,并从中添加“IRibbonExtensibility
”接口。如果您的库未在下拉列表中列出,请选择“文件”并单击“浏览”按钮从 Office 文件夹中选择 MSO.dll 文件。完成后单击“完成”。 - 导航到 Connect.h 文件,您应该会看到自动生成的
GetCustomUI()
函数。删除“return E_NOTIMPL;
”。然后在此处编写您自己的代码。 - 下一步是导入一个 XML 资源文件,其中包含创建 Outlook 选项卡上按钮或您希望看到的任何其他控件的代码。
- 右键单击 .rc 文件,选择“添加资源”,然后从中选择“导入”。将 XML 文件添加到项目中。例如 Ribbon.xml。将这些代码添加到
GetCustomUI()
函数中。
if (!RibbonXml)
return E_POINTER;
*RibbonXml = GetXMLResource(IDR_XML1);
return (*RibbonXml ? S_OK : E_OUTOFMEMORY); return S_OK;
Ribbon.xml
<?xml version="1.0" encoding="utf-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon startFromScratch="false">
<tabs>
<tab idMso="TabCalendar" >
<group id="Customer" label="Customer">
<button id="ReportButton"
size="large"
label="Report Button"
imageMso="SetLanguage"
onAction="OnReportButtonClicked"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
以下代码用于解析 XML 并将值发送到 GetCustomUI()
函数。
HRESULT HrGetResource(int nId, LPCTSTR lpType, LPVOID* ppvResourceData, DWORD* pdwSizeInBytes)
{
HMODULE hModule = _AtlBaseModule.GetModuleInstance();
if (!hModule)
return E_UNEXPECTED;
HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(nId), lpType);
if (!hRsrc)
return HRESULT_FROM_WIN32(GetLastError());
HGLOBAL hGlobal = LoadResource(hModule, hRsrc);
if (!hGlobal)
return HRESULT_FROM_WIN32(GetLastError());
*pdwSizeInBytes = SizeofResource(hModule, hRsrc);
*ppvResourceData = LockResource(hGlobal);
return S_OK;
}
BSTR GetXMLResource(int nId)
{
LPVOID pResourceData = NULL;
DWORD dwSizeInBytes = 0;
HRESULT hr = HrGetResource(nId, TEXT("XML"),
&pResourceData, &dwSizeInBytes);
if (FAILED(hr))
return NULL;
// Assumes that the data is not stored in Unicode.
CComBSTR cbstr(dwSizeInBytes, reinterpret_cast<LPCSTR>(pResourceData));
return cbstr.Detach();
}
SAFEARRAY* GetOFSResource(int nId)
{
LPVOID pResourceData = NULL;
DWORD dwSizeInBytes = 0;
if (FAILED(HrGetResource(nId, TEXT("OFS"),
&pResourceData, &dwSizeInBytes)))
return NULL;
SAFEARRAY* psa;
SAFEARRAYBOUND dim = {dwSizeInBytes, 0};
psa = SafeArrayCreate(VT_UI1, 1, &dim);
if (psa == NULL)
return NULL;
BYTE* pSafeArrayData;
SafeArrayAccessData(psa, (void**)&pSafeArrayData);
memcpy((void*)pSafeArrayData, pResourceData, dwSizeInBytes);
SafeArrayUnaccessData(psa);
return psa;
}
将上述函数添加到 Connect.h 文件中。
- 打开“stdafx.h”,并将 MSO.dll 的
#import
语句从文件底部移到 Extensibility 库的#import
语句旁边,位于#pragma
块内(同时删除该行上的任何 'no_namespace
' 注释)。 - 将“
using namespace Office;
”添加到 Connect.h 文件的顶部。 - 现在使用静态或共享库生成解决方案。
- 您可以在 Outlook 日历屏幕中看到该按钮,您可以使用任何其他值在 Outlook 中放置各种控件。
添加按钮单击事件
- 右键单击项目,选择“添加类”,然后在 ATL 类别中选择“ATL 简单对象”,将其命名为
ButtonCallBack
。 - 现在,在“类视图”中,我们将看到几个新对象:一个名为
IButtonCallBack
的 ATL 接口和一个名为CButtonCallBack
的实现类。我们不需要实现,所以请在“解决方案资源管理器”中删除所有ButtonCallBack
.* 文件。 - 在“类视图”下,右键单击
IButtonCallBack
,然后选择“添加方法”。在“添加方法向导”中,添加一个名为ButtonClicked
的方法,该方法带有一个类型为IDispatch*
的[in]
参数,称为RibbonControl
。 - 再次右键单击
CConnect
类并选择“实现接口…” 以添加IButtonCallBack
。在“类视图”中双击ButtonClicked
函数,即可跳转到自动生成的实现。 - 在自动生成的
ButtonClicked()
函数下编写代码。 - 现在右键单击项目,选择“添加现有项”,然后从项目文件夹中导入 AddIn_i.c。并确保在“配置属性”下该文件“不使用预编译头”。(只需右键单击文件并选择属性)。
- 在 Connect.h 文件中,将
COM_MAP
中的IDispatch
行切换为IButtonCallBack
而不是IRibbonExtensibility
: - 现在只需清理解决方案并生成它。
- 您将在 Outlook 日历屏幕中看到该按钮,如果您单击该按钮,您将看到之前添加的 MessageBox。
STDMETHOD(ButtonClicked)( IDispatch * RibbonControl){
// Add your function implementation here.
MessageBoxW(NULL, L"The button was clicked!", L"Button Click Event", MB_OK);
}
处理约会、发送邮件、保存联系人和访问数据库 ATL COM
- 将以下两项添加到 stdafx.h 文件中。
- 然后在 Connect.h 文件中,添加
using namespace Outlook;
。 - 将这些代码添加到您想要的位置,例如按钮单击事件或
OnConnection
函数下。
#import "C:\Program Files\Microsoft Office\Office14\MSOUTL.OLB"
raw_interfaces_only, raw_native_types, named_guids, auto_search
#import "C:\Program Files\Common Files\System\ado\msado28.tlb" no_namespace rename("EOF", "EndOfFile")
_ApplicationPtr pApp(pApplication);
_NameSpacePtr pNamespace;pApp->GetNamespace(L"MAPI",&pNamespace);
MAPIFolderPtr pFolder;
pNamespace->GetDefaultFolder(olFolderContacts,&pFolder);
_ItemsPtr pItems;
创建并打开一个新联系人
_ContactItemPtr pNewContact;
pApp->CreateItem(olContactItem,(IDispatch**)&pNewContact);
pNewContact->put_LastName(OLESTR("India"));
pNewContact->put_FirstName(OLESTR("Gokulnath"));
pNewContact->Save();
您可以使用任何文件夹名称的值从这些文件夹(olFolderContacts
)中检索值。
接下来是获取一个约会
_AppointmentItemPtr pGetApptt;
pNamespace->GetDefaultFolder(olFolderCalendar,&pFolder);
pFolder->get_Items(&pItems);
long count =5;
pItems->get_Count(&count);
int wd = (int)count;
VARIANT lpVar;
lpVar.vt = VT_INT;
lpVar.intVal = wd;
pItems->Item(lpVar,(IDispatch**)&pGetApptt);
BSTR subject = L"location";
pGetApptt->get_Subject(&subject);
BSTR body;
pGetApptt->get_Body(&body);
ConnectionPtr pConn = NULL;
_CommandPtr pCmdSelect = NULL;
_RecordsetPtr pRstValue = NULL;
_bstr_t strCon("DRIVER={SQL Server};SERVER=localhost;DATABASE=Client;");
_bstr_t strSQLSelect("SELECT City FROM Table1 WHERE ICode = '7'");
//_bstr_t strSQLSelect("INSERT INTO master VALUES"
// " ('gokulnath','1','1','2011-01-04 00:00:00.000','1','2011-01-04 00:00:00.000',1);");
hr = pConn.CreateInstance((__uuidof(Connection)));
hr = pConn->Open(strCon,"","",0);
hr=pCmdSelect.CreateInstance(__uuidof(Command));
pCmdSelect->ActiveConnection = pConn;
pCmdSelect->CommandText = strSQLSelect;
VARIANT rowsaffected;
VARIANT param;
long optl = 0;;
//pCmdSelect->Execute(&rowsaffected,¶m,optl);
pConn->Execute(strSQLSelect,NULL,adCmdText); // only for updating and inserting data into the database
hr=pRstValue.CreateInstance(__uuidof(Recordset));
pRstValue->Open (strSQLSelect, _variant_t((IDispatch *) pConn, true),
adOpenForwardOnly, adLockOptimistic, adCmdText);
// For Selecting values from Database (SELECT Statements)
// Ensure at top of recordset.
pRstValue->MoveFirst();
// If EOF is true, then no data and skip print loop.
if ( pRstValue->EndOfFile ){
}
else
{
// Define strings for output conversions. Initialize to first record's values.
//_bstr_t bstrTitle;
_bstr_t bstrType;
//// Enumerate Recordset and print from each.
//while ( !(pRstValue->EndOfFile) )
//{
bstrType = pRstValue->Fields->GetItem("City ")->Value;
// Use different column values to get those inside GetItem("Your column name")
//}
}
//Close the database
pConn->Close();
// Sending Mails to various accounts and operations.
_MailItemPtr pNewMailItem;
pApp->CreateItem(olMailItem,(IDispatch**)&pNewMailItem);
pNewMailItem->put_BCC(L"mailid1");
pNewMailItem->put_Body(L"Mail Send from Visual C++/ATL");
pNewMailItem->put_To(L"mailid2");
pNewMailItem->Send();
DATE pStart;
SYSTEMTIME sysTime;
memset(&sysTime, 0, sizeof(SYSTEMTIME));
sysTime.wYear = 2013;
sysTime.wMonth = 1;
sysTime.wDay = 4;
SystemTimeToVariantTime(&sysTime, &pStart);
创建约会
_AppointmentItemPtr pGetAppt;
pApp->CreateItem(olAppointmentItem,(IDispatch**)&pGetAppt);
pGetAppt->put_Body(L"Sample Appointment from ATL COM");
pGetAppt->put_Location(L"Coimbatore");
pGetAppt->put_Start(pStart);
pGetAppt->Save();
关注点
这些任务确实很有趣且富有挑战性。我很享受。
历史
版本 1.0。