HOW TO: How do I get or set a comment on an Excel cell?

Reference: Q0028

Article last modified on 18-May-2006


The information in this article applies to:

  • XLL+ for Visual Studio 2005 - 5.0
  • XLL+ for Visual Studio .NET - 4.2, 4.3.1, 5.0
  • XLL+ for Visual Studio 6 - 3, 4.1, 4.2, 4.3.1, 5.0

HOW TO: How do I get or set a comment on an Excel cell?

Question

I have tried to use CXlMacros::Note(...) to set a note on a cell, but it fails. How can I attach a note to a cell, and how can I get the text of any existing note?

Answer

The NOTE() and GET.NOTE() XLM macros are not supported in recent versions of Excel. You should instead use Excel's COM API to manipulate the Comment object. The code below demonstrates how to use it.

Note that the methods below will always fail if you have switched off the add-ins's access to the COM API (by setting CXllApp::m_bUseAutomation to FALSE).

Note also that these methods can only be used within a macro function, not from within a worksheet function.

#include <xlpcom.h>
#include <xlpcomhelpers.h>

BOOL GetRange(const CXlOper& xloRef, CDispatch& disp)
{
    CXllCom* c = XllGetTypedApp()->GetCom();
    if (c == NULL)
        return FALSE;
    CString strBook, strSheet;
    CString strBookSheet = xloRef.GetSheetName();
    CXllApp::SplitSheetName(strBookSheet, strBook, strSheet);
    HRESULT hr = c->OleGetRange(strBook, strSheet, xloRef.GetRef(), &disp);
    return (SUCCEEDED(hr));
}

BOOL GetComment(const CXlOper& xloRef, CString& text)
{
    CDispatch dispRange;
    if (!GetRange(xloRef, dispRange))
        return FALSE;

    CDispatch dispComment;
    CVariant res;
    HRESULT hr = ::OleGetProperty(dispRange, L"Comment", &dispComment, 0, 0);
    if (!SUCCEEDED(hr))
        return FALSE;

    hr = ::OleMethod(dispComment, L"Text", &res, 0, 0);
    if (!SUCCEEDED(hr))
        return FALSE;
    text = CString(V_BSTR(&res));
    return TRUE;
}

BOOL SetComment(const CXlOper& xloRef, const char* text)
{
    CDispatch dispRange;
    if (!GetRange(xloRef, dispRange))
        return FALSE;
    
    CDispatch dispComment;
    CVariant res;
    HRESULT hr = ::OleGetProperty(dispRange, L"Comment", &dispComment, 0, 0);
    if (SUCCEEDED(hr))
    {
        // Already has a comment - overwrite it
        hr = ::OleMethod(dispComment, L"Text", &res, 0, 1, &CVariant(text));
    }
    else
    {
        // Use AddComment
        hr = ::OleMethod(dispRange, L"AddComment", &res, 0, 1, &CVariant(text));
    }
    return SUCCEEDED(hr);
}

// Function:    MyAddinFunction
// Purpose:     No description provided

//{{XLP_SRC(MyAddinFunction)
    // NOTE - the FunctionWizard will add and remove mapping code here.
    //    DO NOT EDIT what you see in these blocks of generated code!
IMPLEMENT_XLLFN2(MyAddinFunction, "R#", "MyAddinFunction", "", 
    "User Defined", "No description provided", "", "appscope=1\0"
    , 2)

extern "C" __declspec( dllexport )
LPXLOPER MyAddinFunction()
{
    XLL_FIX_STATE;
    CXlOper xloResult;
//}}XLP_SRC

    CXlOper xloRef;
    LPCSTR comment = "Cheese";
    if (xloRef.GetActiveCell())
    {
        CString oldComment;
        if (GetComment(xloRef, oldComment))
            CXllApp::XlMessageBox("Comment is: " + oldComment, XlMessageBoxTypeInformation);
        if (SetComment(xloRef, comment))
            CXllApp::XlMessageBox("Comment was set", XlMessageBoxTypeInformation);
    }
    return xloResult.Ret();
}