Outlook add-in using VC++ ATL/COM





5.00/5 (4 votes)
To get instant control over Outlook and various operations of outlook.
Introduction
These codes are written with VC++ ATL/COM for xreating an Outlook add-in and performing various tasks related to Outlook. Those familiar with Outlook operations can understand and can extend these codes to perform other tasks. It helps in creating an Outlook add-in, creating buttons and various controls, creating appointments, retrieving all appointments, and connecting to a database and taking values to and from the database.
I assure you that I would work in the future to enhance various other operations with clear codes.
Using the code
- Start Visual Studio, click on New Project-> Extensibility and choose Shared Addin from it.
- Make sure to check "Create an Add-in using Visual C++/ATL" and "I would like my Add-in to load when the host application loads."
- Build the solution to ensure that it compiles good.
- Next, open up Class View, right-click on your
CConnect
class and select "Add -> Implement Interface…" In the dialog that pops up, select "Microsoft Office 12.0 Object Library <2.4>" type library and add the "IRibbonExtensibility
" interface from it. If your library is not listed in the drop down, choose file and click on the Browse button to choose the MSO.dll file from the Office folder. Click Finish once you are done. - Navigate to the Connect.h file and you should see the auto generated
GetCustomUI()
function. Delete "return E_NOTIMPL;
". And try to make your own code here. - The next step is to import an XML resource file having codes to create buttons or any other control you want to see on Outlook Tabs.
- Right click on the .rc file and choose Add Resource and select import from it. Add the
XML file to the project. For instance Ribbon.xml.
Add these codes inside the
GetCustomUI()
function.
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>
The following code is used to parse the XML and send the values to the GetCustomUI()
function:
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;
}
Add the above functions in Connect.h.
- Open up "stdafx.h" and move the
#import
statement for MSO.dll from the bottom of the file up next to the#import
statement for the Extensibility library inside the#pragma
blocks (remove any 'no_namespace
' annotations from that line as well). - Add "
using namespace Office;
" to the top of the Connect.h file. - Now build the solution with either static or shared libraries.
- You can see the button in the Outlook Calendar screen and you can use any other value to place various controls in Outlook.
Adding a button click event
- Right click on the project and choose Add Class and select ATL Simple Object" in the ATL
category, name it
ButtonCallBack
. - Now in Class View we have several new objects: an ATL interface called
IButtonCallBack
and an implementation class calledCButtonCallBack
. We don't need the implementation, so go ahead and delete all theButtonCallBack
.* files from Solution Explorer. - Under Class view, right click on
IButtonCallBack
and select Add Method. In the Add Method Wizard, add a method namedButtonClicked
with one[in]
parameter of typeIDispatch*
calledRibbonControl
. - Right click on the
CConnect
class and select "Implement Interface…" again to addIButtonCallBack
. Double-click theButtonClicked
function in Class View to be taken to the auto-generated implementation. - Write code under the auto generated function
ButtonClicked()
. - Now right click on project, choose Add Existing item, and import AddIn_i.c from the project folder. And make sure that the file is "Not Using Precompiled Headers" under Configuration Properties. (Just right click the file and select properties).
- In Connect.h, switch the
IDispatch
line inCOM_MAP
toIButtonCallBack
instead ofIRibbonExtensibility
: - Now just Clean the solution and Build it.
- You will see the button in the Outlook Calendar screen and if you click on the button, you will get the MessageBox as added before.
STDMETHOD(ButtonClicked)( IDispatch * RibbonControl){
// Add your function implementation here.
MessageBoxW(NULL, L"The button was clicked!", L"Button Click Event", MB_OK);
}
Working with Appointments, Sending Mails, Saving Contacts, and Accessing Database ATL COM
- Add these two in the stdafx.h file:
- And in Connect.h, add
using namespace Outlook;
. - Add these codes wherever you want on the button click event or under the
OnConnection
function.
#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;
Create and open a new contact
_ContactItemPtr pNewContact;
pApp->CreateItem(olContactItem,(IDispatch**)&pNewContact);
pNewContact->put_LastName(OLESTR("India"));
pNewContact->put_FirstName(OLESTR("Gokulnath"));
pNewContact->Save();
You can use any values of folder names to retrieve values from those folders (olFolderContacts
).
Next is to get an appointment
_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);
Creating the appointments
_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();
Points of Interest
These tasks are really interesting and challenging. Enjoyed it.
History
Version 1.0.