若要获得 Microsoft Excel 中的 VBA 过程的列表的工作簿、 一个 PowerPoint 演示文稿或 Word 文档,您可以使用类和函数公开结合的应用程序对象模型的应用程序扩展库通过 Microsoft vba。当利用自动化功能打开文档时,您可以访问的文档以循环访问集合的 VBComponentsVBProject 对象 ; VBComponents 集合包括标准模块,以及在项目中包含的类模块。在您有了一个 VBComponent 的引用后您可以检索其属性 (包括代码),并根据需要处理该组件。
注意有关 Microsoft Access 的特殊注意事项: Microsoft Access 是一个例外,因为 Visual Basic,应用程序扩展库的不需要访问数据库项目中的代码。Microsoft Access 对象模型公开方法或属性使您直接与访问代码模块。检索或处理与在 Access 数据库中的模块代码有关的警告之一是,必须先打开该模块。
下面的步骤演示了如何创建一个 MFC 自动化客户端从 Office 文档中检索的 Sub 和 函数 的过程列表。该代码示例说明了对 Office 应用程序的类型库和应用程序扩展库的 vba 中使用类包装,该示例还提供了用于 Microsoft Access 数据库的特殊案例考虑。
- 在 Visual 的 c + + 中创建名为 ListMacros 新 MFC 应用程序向导 EXE 项目。选择 基于对话框的 作为类型,然后接受其他所有默认设置。
- 在 视图 菜单上单击 类向导。类向导 对话框中单击 自动化 选项卡,然后执行下列操作:
- 单击 添加类,并选择 从类型库。
- 浏览以找到该 Microsoft Visual Basic 应用程序扩展类型库 (VBE6EXT.olb),然后单击 打开。
注意VBE6EXT.olb 在默认文件夹是Program Files/Common Files/Microsoft Shared/VBA/VBA6。 - 在类型库中选择所有类,然后单击 确定 以创建类包装。
- 重复上述步骤创建 Microsoft Excel、 PowerPoint、 Microsoft Access 和 Microsoft Word 类型库包装类。选择正确的类型库中的您的 Office 的版本。请参见"参考"部分,稍后在本文中有关查找正确的类型库的信息。
- 若要不必类名称冲突添加类包装的每个命名空间通过执行下列操作:
- Excel9.h 的开头添加以下代码:
namespace Excel{
- 下面的代码添加到开头 Excel9.cpp,读取 #endif (不带引号) 的行的下方,并在第一个类的定义之前:
using namespace Excel;
- Excel9.h 的开头添加以下代码:
- 重复这些步骤以创建所剩余的命名空间: MSWord、 PPT、 MSAccess 和 VBAExt。
- 单击 ResourceView,然后打开 IDD_LISTMACROS_DIALOG 对话框。删除对话框上绘制,并将其替换为以下控件的任何控件:
Control Type Properties ------------ ------------------------- Button ID: ID_Run Caption: Run Edit Box ID: IDC_FILENAME List Box ID: IDC_MACROLIST Use Tabstops: Checked
- 在 视图 菜单上单击 类向导、 选择 成员变量 选项卡上、 类名 下拉列表框中选择 CListMacrosDlg,然后执行下列:
- 在控件列表上双击 $ IDC_FILENAME。添加成员变量 对话框中提供的变量名 m_sFilename,然后单击 确定。
- 在控件列表上双击 $ IDC_MACROLIST。添加成员变量 对话框中提供的变量名 m_MacroList 到 控制,更改 类别,然后单击 确定。
- 单击 确定 以关闭 类向导 对话框。
- 双击对话框上的 运行 按钮。单击 确定 以添加该成员函数 OnRun。将下面的代码添加到 CListMacrosDlg::OnRun():
void CListMacrosDlg::OnRun() { USES_CONVERSION; UpdateData(TRUE); COleVariant vOpt(DISP_E_PARAMNOTFOUND, VT_ERROR); //for Optional args //Determine the PROGID for the filename supplied CLSID clsid; HRESULT hr = GetClassFile(T2OLE(m_sFilename), &clsid); if (FAILED(hr)) { CString sMsg; sMsg.Format("Unable to determine progid for file: %s", m_sFilename); AfxMessageBox(sMsg); return; } LPOLESTR lpOleStr; ProgIDFromCLSID(clsid, &lpOleStr); CString sProgID = CString(lpOleStr); sProgID.MakeUpper(); //If the version independent progid is EXCEL.SHEET, WORD.DOCUMENT, or //POWERPOINT.SHOW, then open the file in its associated application. //Once the file is open, call GetMacros to place the list of macro //names in the list box. if(sProgID.Find("EXCEL.SHEET",0)>=0) //Microsoft Excel Workbook { Excel::_Application oApp; oApp.CreateDispatch("Excel.Application"); Excel::Workbooks oBooks = oApp.GetWorkbooks(); Excel::_Workbook oBook = oBooks.Open(m_sFilename, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt); LPDISPATCH lpDisp = oBook.GetVBProject(); GetMacros(lpDisp); oBook.Close(COleVariant((short)FALSE), vOpt, vOpt); oApp.Quit(); } else if(sProgID.Find("WORD.DOCUMENT",0)>=0) //Microsoft Word Document { MSWord::_Application oApp; oApp.CreateDispatch("Word.Application"); MSWord::Documents oDocs = oApp.GetDocuments(); MSWord::_Document oDoc = oDocs.Open(COleVariant(m_sFilename), vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt); // For Office Word 2003 and Office Word 2007, 16 parameters are required. /* MSWord::_Document oDoc = oDocs.Open(COleVariant(m_sFilename), vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt,vOpt,vOpt,vOpt); */ LPDISPATCH lpDisp = oDoc.GetVBProject(); GetMacros(lpDisp); oDoc.Close(COleVariant((short)FALSE), vOpt, vOpt); oApp.Quit(vOpt, vOpt, vOpt); } else if(sProgID.Find("POWERPOINT.SHOW",0)>=0) //Microsoft PowerPoint //Presentation { PPT::_Application oApp; oApp.CreateDispatch("Powerpoint.Application"); PPT::Presentations oAllPres = oApp.GetPresentations(); PPT::_Presentation oPres = oAllPres.Open(m_sFilename, 0, 0, 0); LPDISPATCH lpDisp = oPres.GetVBProject(); GetMacros(lpDisp); oPres.Close(); oApp.Quit(); } else if(sProgID.Find("ACCESS.APPLICATION", 0)>=0) //Microsoft Access DB { //** Special Consideration for Access Code Project MSAccess::_Application oApp; oApp.CreateDispatch("Access.Application"); MSAccess::DoCmd oDoCmd = oApp.GetDoCmd(); oApp.OpenCurrentDatabase(m_sFilename, FALSE); // For Office Access 2003 and Office Access 2007, 3 parameters are required. /*oApp.OpenCurrentDatabase(m_sFilename, FALSE,NULL);*/ MSAccess::_CurrentProject oProj = oApp.GetCurrentProject(); MSAccess::AllObjects oObjs = oProj.GetAllModules(); CString sModName, sProcName, sItem; long lProcKind = 0; for(long i=0;i<=oObjs.GetCount()-1;i++) { MSAccess::AccessObject oObj = oObjs.GetItem(COleVariant(i)); sModName = oObj.GetName(); //Module must be open: oDoCmd.OpenModule(COleVariant(sModName), vOpt); MSAccess::Modules oMods = oApp.GetModules(); MSAccess::Module oMod = oMods.GetItem(COleVariant(sModName)); long lLineCount = oMod.GetCountOfLines(); long j=1; while(j<lLineCount) { sProcName = oMod.GetProcOfLine(j, &lProcKind); if(!sProcName.IsEmpty()){ sItem.Format("%s/t/t%s", sModName, sProcName); m_MacroList.AddString(sItem); j = j + oMod.GetProcCountLines(sProcName, lProcKind); } else { j++; } } } oApp.Quit(0); } else //Other... { CString sMsg; sMsg.Format("Unable to extract macro names for files with progid %s", sProgID); AfxMessageBox(sMsg); } }
- 添加以下包括到 ListMacrosDlg.cpp:
#include <afxpriv.h> #include "Excel9.h" // for Office Excel 2003 and later, use #include "Excel.h" #include "MSWord9.h" // for Office Word 2003 and later, use #include "MSWord.h" #include "MSPPT9.h" // for Office Powerpoint 2003 and later, use #include "msppt.h" #include "MSAcc9.h" // for Office Access 2003 and later, use #include "msacc.h" #include "VBE6Ext.h"
- 单击 ClassView 选项卡,右击 CListMacrosDlg,然后选择 添加成员函数。供应 void 的 函数类型,GetMacros(LPDISPATCH lpDisp) 的 函数声明 中,选择 专用,然后单击 确定。为 CListMacrosDlg::GetMacros 添加以下代码:
void CListMacrosDlg::GetMacros(LPDISPATCH lpDisp) { long i, j; //counters //Clear the list box and set the tab stops j=m_MacroList.GetCount(); for (i=j-1;i>=0;i--){ m_MacroList.DeleteString(i);} m_MacroList.SetTabStops(50); //Iterate the collection of components in the VBA project //referenced by lpDisp. VBAExt::_VBProject oVBProj; oVBProj.AttachDispatch(lpDisp, TRUE); VBAExt::_VBComponents oVBComps = oVBProj.GetVBComponents(); long lCompCount = oVBComps.GetCount(); long lLineCount; long lProcKind = 0; VBAExt::_VBComponent oVBComp; VBAExt::_CodeModule oCode; for(i=1; i<=lCompCount; i++) { oVBComp = oVBComps.Item(COleVariant(i)); oCode = oVBComp.GetCodeModule(); //If the component contains any lines of code, then //retrieve the name of each procedure (Functions and Subs) //add it to the list box along with the module name. CString sProcName, sItem; lLineCount = oCode.GetCountOfLines(); j=1; while(j<lLineCount) { sProcName = oCode.GetProcOfLine(j, &lProcKind); if(!sProcName.IsEmpty()){ sItem.Format("%s/t/t%s", oVBComp.GetName(), sProcName); m_MacroList.AddString(sItem); j = j + oCode.GetProcCountLines(sProcName, lProcKind); } else { j++; } } } }
- 将下面的代码行添加到开始处 CListMacrosApp::InitInstance() ListMacros.cpp 中:
AfxOleInit();
- 生成项目并运行它。选择一个 (.xls、.doc、.mdb 或.ppt) 文件,该文件包含宏,然后单击 运行。在列表框中显示的宏的列表。
其他说明
- 此示例中阐释的代码检索只 Sub 和 函数 的过程。如果您要检索 属性获取、 属性让,或 属性 Set 过程,则必须修改代码。ProcOfLine 的第二个参数可以是下列之一 (该代码示例使用 0 等效于 vbext_pk_Proc):
Constant Value Description ------------- ----- ------------------------------------------------ vbext_pk_Get 3 Procedure that returns the value of a property vbext_pk_Let 1 Procedure that assigns a value to a property vbext_pk_Set 2 Procedure that sets a reference to an object vbext_pk_Proc 0 All procedures other than property procedures
- 如果在 VBA 模块是受密码保护,显示要求输入密码对话框。如果您不知道该密码,该程序不能列出该过程。
- 上面的示例中的 打开 (和 OpenCurrentDatabase) 方法用于与 Office 2000 类型库。Office XP 类型库都有其他需要传递给这些方法的参数。请参见"参考"一节的其他信息。