2008-09-08 32 views
14

¿Es posible extraer todo el código de VBA de un documento "docm" de Word 2007 con la API?Código de macro (VBA) mediante programación desde documentos de Word 2007

He encontrado cómo insertar código VBA en tiempo de ejecución y cómo eliminar todo el código VBA, pero no extraer el código real en una secuencia o cadena que puedo almacenar (e insertar en otros documentos en el futuro).

Cualquier consejo o recurso sería apreciado.

Editar: gracias a todos, la respuesta de Aardvark fue exactamente lo que estaba buscando. He convertido su código para C#, y fue capaz de llamarlo desde una biblioteca de clases utilizando Visual Studio 2008.

using Microsoft.Office.Interop.Word; 
using Microsoft.Vbe.Interop; 

... 

public List<string> GetMacrosFromDoc() 
{ 
    Document doc = GetWordDoc(@"C:\Temp\test.docm"); 

    List<string> macros = new List<string>(); 

    VBProject prj; 
    CodeModule code; 
    string composedFile; 

    prj = doc.VBProject; 
    foreach (VBComponent comp in prj.VBComponents) 
    { 
     code = comp.CodeModule; 

     // Put the name of the code module at the top 
     composedFile = comp.Name + Environment.NewLine; 

     // Loop through the (1-indexed) lines 
     for (int i = 0; i < code.CountOfLines; i++) 
     { 
      composedFile += code.get_Lines(i + 1, 1) + Environment.NewLine; 
     } 

     // Add the macro to the list 
     macros.Add(composedFile); 
    } 

    CloseDoc(doc); 

    return macros; 
} 
+1

Utilice la StringBuilder para optimizar el uso de memoria: StringBuilder sb = new StringBuilder(); para .... sb.AppendLine ("su línea de código"); ... macros.Add (sb.ToString()); – Skuami

Respuesta

9

Vas a tener que agregar una referencia a Microsoft Visual Basic para aplicaciones de extensibilidad 5.3 (o cualquier versión tienes). Tengo el SDK de VBA y tal en mi caja, por lo que puede que no sea exactamente con lo que se envía la oficina.

También debe habilitar el acceso al Modelo de objetos VBA específicamente; consulte el "Centro de confianza" en las opciones de Word. Esto se suma a todas las otras configuraciones de seguridad Macro que proporciona Office.

Este ejemplo extraerá el código del documento actual en el que vive; en sí mismo es una macro VBA (y se mostrará ella misma y cualquier otro código también). También hay una colección Application.vbe.VBProjects para acceder a otros documentos. Aunque nunca lo hice, supongo que una aplicación externa podría abrir archivos usando esta colección de VBProjects también. La seguridad es divertida con esto, así que puede ser complicado.

También me pregunto qué formato de archivo docm es ahora - XML ​​como el docx? ¿Sería eso un mejor enfoque?

Sub GetCode() 

    Dim prj As VBProject 
    Dim comp As VBComponent 
    Dim code As CodeModule 
    Dim composedFile As String 
    Dim i As Integer 

    Set prj = ThisDocument.VBProject 
     For Each comp In prj.VBComponents 
      Set code = comp.CodeModule 

      composedFile = comp.Name & vbNewLine 

      For i = 1 To code.CountOfLines 
       composedFile = composedFile & code.Lines(i, 1) & vbNewLine 
      Next 

      MsgBox composedFile 
     Next 

End Sub 
+1

Esto es increíble, gracias! Convertí tu código a C#, y puedo llamarlo desde una aplicación WPF e imprimir el código de macro en un cuadro de lista. Publicaré la versión de C# del código en mi respuesta editada. –

+1

Si tiene formularios en su proyecto, esto no es ideal. No exporta la parte de definición de control del formulario. – MarkJ

+0

Simplemente confirmando que los archivos '.xlsb' son archivos zip (como se esperaba) pero vbaProject aún se almacena en un archivo' .bin' que es claramente un Documento compuesto (una de las primeras cadenas legibles es 'Root Entry' '). –

25

Se puede exportar el código a los archivos y luego leerlas posteriormente.

He estado usando el código de abajo para ayudar a mantener algunas macros de Excel bajo control de origen (usando Subversion & TortoiseSVN). Básicamente, exporta todo el código a archivos de texto cada vez que lo guardo con el editor de VBA abierto. Puse los archivos de texto en subversión para poder hacer diffs. Debería poder adaptar/robar algo de esto para trabajar en Word.

La comprobación de registro en CanAccessVBOM() corresponde al "Acceso de confianza al proyecto de Visual Basic" en la configuración de seguridad.

Sub ExportCode() 

    If Not CanAccessVBOM Then Exit Sub ' Exit if access to VB object model is not allowed 
    If (ThisWorkbook.VBProject.VBE.ActiveWindow Is Nothing) Then 
     Exit Sub ' Exit if VBA window is not open 
    End If 
    Dim comp As VBComponent 
    Dim codeFolder As String 

    codeFolder = CombinePaths(GetWorkbookPath, "Code") 
    On Error Resume Next 
    MkDir codeFolder 
    On Error GoTo 0 
    Dim FileName As String 

    For Each comp In ThisWorkbook.VBProject.VBComponents 
     Select Case comp.Type 
      Case vbext_ct_ClassModule 
       FileName = CombinePaths(codeFolder, comp.Name & ".cls") 
       DeleteFile FileName 
       comp.Export FileName 
      Case vbext_ct_StdModule 
       FileName = CombinePaths(codeFolder, comp.Name & ".bas") 
       DeleteFile FileName 
       comp.Export FileName 
      Case vbext_ct_MSForm 
       FileName = CombinePaths(codeFolder, comp.Name & ".frm") 
       DeleteFile FileName 
       comp.Export FileName 
      Case vbext_ct_Document 
       FileName = CombinePaths(codeFolder, comp.Name & ".cls") 
       DeleteFile FileName 
       comp.Export FileName 
     End Select 
    Next 

End Sub 
Function CanAccessVBOM() As Boolean 
    ' Check resgistry to see if we can access the VB object model 
    Dim wsh As Object 
    Dim str1 As String 
    Dim AccessVBOM As Long 

    Set wsh = CreateObject("WScript.Shell") 
    str1 = "HKEY_CURRENT_USER\Software\Microsoft\Office\" & _ 
     Application.Version & "\Excel\Security\AccessVBOM" 
    On Error Resume Next 
    AccessVBOM = wsh.RegRead(str1) 
    Set wsh = Nothing 
    CanAccessVBOM = (AccessVBOM = 1) 
End Function 


Sub DeleteFile(FileName As String) 
    On Error Resume Next 
    Kill FileName 
End Sub 

Function GetWorkbookPath() As String 
    Dim fullName As String 
    Dim wrkbookName As String 
    Dim pos As Long 

    wrkbookName = ThisWorkbook.Name 
    fullName = ThisWorkbook.fullName 

    pos = InStr(1, fullName, wrkbookName, vbTextCompare) 

    GetWorkbookPath = Left$(fullName, pos - 1) 
End Function 

Function CombinePaths(ByVal Path1 As String, ByVal Path2 As String) As String 
    If Not EndsWith(Path1, "\") Then 
     Path1 = Path1 & "\" 
    End If 
    CombinePaths = Path1 & Path2 
End Function 

Function EndsWith(ByVal InString As String, ByVal TestString As String) As Boolean 
    EndsWith = (Right$(InString, Len(TestString)) = TestString) 
End Function 
+1

Esto es bueno ya que obtendrá formularios intactos. +1 – Aardvark

Cuestiones relacionadas