HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...) {
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
if(!pDisp) {
MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010);
_exit(0);
}
// Variables used.
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[200];
char szName[200];
// Convert down to ANSI.
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed.
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
if(FAILED(hr)) {
sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// Allocate memory for arguments.
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments...
for(int i=0; i<cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS.
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts.
if(autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call.
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
sprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// End variable-argument section.
va_end(marker);
delete [] pArgs;
return hr;
}
void CNameProceduresDlg::OnRun()
{
using namespace Access;
//The path to the Northwind database. Be sure to modify this to point to
//the northwind.mdb or northwind.accdb file (or nwind.mdb file) on your system
char* pszDatabasePath = "C:\\Program Files\\Microsoft Office\\Office12\\Samples\\northwind 2007.accdb";
//These variables use the Class-Wizard generated wrapper classes to automate
//Access and DAO.
_Application app;
Database db;
Containers containers;
Container container;
Documents documents;
Document document;
long documentcount;
long i;
Modules modules;
Module module;
CString documentname;
//General purpose buffer
char buf[512];
//Not all arguments are required for automation methods. This COleVariant
//is useful for filling in optional parameters.
COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
// Start Access and get Application object...
if(!app.CreateDispatch("Access.Application"))
{
AfxMessageBox("Couldn't start Access.");
return;
}
//Open a database.
//app.OpenCurrentDatabase(pszDatabasePath, true);
app.OpenCurrentDatabase(pszDatabasePath, true, ""); // Access 2002 and Office Access 2003
// requires
// password argument
//Obtain a reference to the containers collection of the open database.
db = app.CurrentDb();
containers = db.GetContainers();
//Code to get the LPDISPATCH for the "Modules" item assigned to an Access
//Container object variable
{
VARIANT rVal = {0};
VARIANT name = {0};
name.vt = VT_BSTR;
name.bstrVal = ::SysAllocString(L"Modules");
//Occassionally, a method cannot be accessed through the wrapper classes that are
//generated by the Class-Wizard. In these cases, we can use the COM
//IDispatch interface to access the method or property.
AutoWrap(DISPATCH_PROPERTYGET|DISPATCH_METHOD, &rVal,
containers.m_lpDispatch, L"item", 1, name);
container.AttachDispatch(rVal.pdispVal);
VariantClear(&name);
}
//Obtain the count of the "documents" or pages of code in the database.
documents = container.GetDocuments();
documentcount = documents.GetCount();
if( 0 != documentcount)
{
//Loop through the documents.
for( i = 0; i < documentcount; i++)
{
{
// Code to assign the loop-counter to the "Item()" property of the
//Documents collection
VARIANT rVal = {0};
VARIANT name = {0};
name.vt = VT_I4;
name.lVal = i;
AutoWrap(DISPATCH_PROPERTYGET|DISPATCH_METHOD, &rVal,
documents.m_lpDispatch, L"item", 1, name);
document.AttachDispatch(rVal.pdispVal);
}
//Obtain the name of the document.
documentname = document.GetName();
// The module must be open before we can see its code.
DoCmd docmd = app.GetDoCmd(); //Access2000 has the DoCmd interface
//IDoCmd docmd = app.GetDoCmd(); //Access97 has the IDoCmd interface
docmd.OpenModule(COleVariant(documentname), covOptional);
//Obtain the number of lines in the module we want.
modules = app.GetModules();
module = modules.GetItem(COleVariant(documentname));
long linecount = module.GetCountOfLines();
//STRATEGY: There is no list of procedures in a module, but we can
//find out which procedure a given line belongs to, and we can find
//out how many lines a given procedure has, so we can work through a
//file to find all the procedure names.
// Set the counter for the next line if not a sub or a function.
for ( long linenumber = 1; linenumber <= linecount; linenumber++)
{
long nProcType = 0; //0 = vbext_pk_Proc, Sub or Function
//assign address of long containing type to the pointer
CString procname = module.GetProcOfLine(linenumber, &nProcType);
//If IsEmpty is false, this is a real procedure.
if (false == procname.IsEmpty())
{
//Write the name of the procedure into a buffer, along with the
//module that contains it.
sprintf(buf,"%s.%s", documentname, procname);
//Add the procedure name to the list box
m_iList.AddString(buf);
//Move the line number to the end of the procedure.
linenumber = --linenumber + module.GetProcCountLines(procname, nProcType);
} // end if
} // end for
} // end for ( e = 0; i < documentcount;...
} // end if(0!= documentcount...
//Shut down the instance of Access that we launched.
app.Quit(0);
} // end OnRun function