Reference: Q0043
Article Last Modified on 30-Sep-2007
I want to use ADO (COM) objects from an XLL. How do I do it?
#import statements to include the libraries (or type
libraries) that you need.
OnXllOpenEx() and
OnXllClose()
_variant_t and bstr_t types to wrap up arguments
passed to COM methods.AdoAddin.sln is a Visual Studio .NET 2003 solution. AdoAddin8.sln
is a Visual Studio 2005 solution.To include ADO in your library, add the following line near the top of your application header file:
#import "C:\Program Files\Common Files\System\ado\msado15.dll"
Unfortunately, this leads to a compiler error, because of a conflict with stdio.h,
so you must also add three more lines:
#ifdef EOF #undef EOF #endif #import "C:\Program Files\Common Files\System\ado\msado15.dll"
Within your application class, add the declaration of a COM pointer.
// Names
public:
static LPCSTR m_pszDefName;
// Data
ADODB::_ConnectionPtr m_conn;
bool m_connectionIsOpen;
// Overrides
In your application class constructor, initialize the data members.
CAdoAddinApp::CAdoAddinApp()
: m_connectionIsOpen(false),
m_conn("ADODB.Connection")
{
...
}
In your class's override of OnXllOpenEx(), start using any data
members that will need to be available to add-in functions, eg:.
BOOL CAdoAddinApp::OnXllOpenEx()
{
...
// Prepare arguments for ADODB::_Connection::Open
bstr_t connectionString((LPCSTR)strConnection),
userID(L"Admin"),
password(L"");
long options = 0;
try
{
m_conn->Open(connectionString, userID, password, options);
m_connectionIsOpen = true;
}
catch(_com_error e)
{
XlMessageBox(CString("Failed to open connection: ") + e.ErrorMessage(), XlMessageBoxTypeExclamation);
return FALSE;
}
return TRUE;
}
Note the error-trapping using try...catch, and also note that we report the error and return FALSE if the procedure fails.
Dispose of any application-level objects in OnXllClose(), e.g.:
void CAdoAddinApp::OnXllClose()
{
if (m_connectionIsOpen)
{
m_conn->Close();
m_connectionIsOpen = false;
}
}
In your add-in function, make sure that you wrap any IDispatch interfaces using
_variant_t, and any strings using bstr_t, e.g.:
//{{XLP_SRC(GetNames)
// NOTE - the FunctionWizard will add and remove mapping code here.
// DO NOT EDIT what you see in these blocks of generated code!
IMPLEMENT_XLLFN2(GetNames, "RC", "GetNames", "Filter", "Database"
, "Get names from table", "Filter applied to selection\000",
"\0appscope=1\0", 1)
extern "C" __declspec( dllexport )
LPXLOPER GetNames(const char* Filter)
{
XLL_FIX_STATE;
CXlOper xloResult;
//}}XLP_SRC
// Build query
CString query = "SELECT Name From Table1";
if (Filter[0] != 0)
query += CString(" WHERE Name LIKE '") + Filter + CString("'");
// Run query
ADODB::_RecordsetPtr rs("ADODB.Recordset");
_variant_t source((LPCTSTR)query);
_variant_t conn((IDispatch*)(XllGetTypedApp()->m_conn), true);
try
{
// Open recordset
rs->Open(source, conn, ADODB::adOpenForwardOnly, ADODB::adLockOptimistic, 0);
std::vector<CString> names;
// Read each record
while (!rs->EOF)
{
names.push_back((char*)(_bstr_t)rs->Fields->Item["Name"]->Value);
rs->MoveNext();
}
rs->Close();
xloResult = names;
}
catch(_com_error e)
{
xloResult.Format("#ERROR: %s", e.ErrorMessage());
}
return xloResult.Ret();
}
The sample project discussed above can be downloaded here.