Click here to Skip to main content
Click here to Skip to main content

Outlook add-in using VC++ ATL/COM

, 5 Feb 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
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

  1. Start Visual Studio, click on New Project-> Extensibility and choose Shared Addin from it.
  2. 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."
  3. Build the solution to ensure that it compiles good.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.

  1. 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).
  2. Add "using namespace Office;" to the top of the Connect.h file.
  3. Now build the solution with either static or shared libraries.
  4. 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

  1. Right click on the project and choose Add Class and select ATL Simple Object" in the ATL category, name it ButtonCallBack.
  2. Now in Class View we have several new objects: an ATL interface called IButtonCallBack and an implementation class called CButtonCallBack. We don't need the implementation, so go ahead and delete all the ButtonCallBack.* files from Solution Explorer.
  3. Under Class view, right click on IButtonCallBack  and select Add Method. In the Add Method Wizard, add a method named ButtonClicked with one [in] parameter of type IDispatch* called RibbonControl.
  4. Right click on the CConnect class and select "Implement Interface…" again to add IButtonCallBack. Double-click the ButtonClicked function in Class View to be taken to the auto-generated implementation.
  5. Write code under the auto generated function ButtonClicked().
  6. STDMETHOD(ButtonClicked)( IDispatch * RibbonControl){
      // Add your function implementation here.
      MessageBoxW(NULL, L"The button was clicked!", L"Button Click Event", MB_OK);
    }
  7. 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).
  8. In Connect.h, switch the IDispatch line in COM_MAP to IButtonCallBack instead of IRibbonExtensibility:   
  9. Now just Clean the solution and Build it.
  10. 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.

Working with Appointments, Sending Mails, Saving Contacts, and Accessing Database ATL COM

  1. Add these two in the stdafx.h file:
  2. #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")
  3. And in Connect.h, add using namespace Outlook;.
  4. Add these codes wherever you want on the button click event or under the OnConnection function.
  5. _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,&param,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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gokulnath007
Software Developer CG-VAK Softwares and Exports
India India
Am a Computer Science and Engineering Graduate.
Currently I am working as a software developer in VC++.
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmemberSankar Chandra Bose7-Jan-13 19:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411023.1 | Last Updated 5 Feb 2013
Article Copyright 2013 by Gokulnath007
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid