16

I am trying to copy a module from one excel workbook to another using VBA.

My Code:

'Copy Macros

Dim comp As Object
Set comp = ThisWorkbook.VBProject.VBComponents("Module2")
Set Target = Workbooks("Food Specials Rolling Depot Memo 46 - 01.xlsm").VBProject.VBComponents.Add(1)

For some reason, this copies the module, but does not copy the VBA code inside, why?

Please can someone show me where i am going wrong?

Thanks

Shai Rado
  • 33,032
  • 6
  • 29
  • 51
Princess.Bell
  • 373
  • 2
  • 7
  • 27

5 Answers5

16

Sub CopyModule below, receives 3 parameters:

1.Source Workbook (as Workbook).

2.Module Name to Copy (as String).

3.Target Workbook (as Workbook).

CopyModule Code

Public Sub CopyModule(SourceWB As Workbook, strModuleName As String, TargetWB As Workbook)

    ' Description:  copies a module from one workbook to another
    ' example: CopyModule Workbooks(ThisWorkbook), "Module2",
    '          Workbooks("Food Specials Rolling Depot Memo 46 - 01.xlsm")
    ' Notes:   If Module to be copied already exists, it is removed first,
    '          and afterwards copied

    Dim strFolder                       As String
    Dim strTempFile                     As String
    Dim FName                           As String

    If Trim(strModuleName) = vbNullString Then
        Exit Sub
    End If

    If TargetWB Is Nothing Then
        MsgBox "Error: Target Workbook " & TargetWB.Name & " doesn't exist (or closed)", vbCritical
        Exit Sub
    End If

    strFolder = SourceWB.Path
    If Len(strFolder) = 0 Then strFolder = CurDir

    ' create temp file and copy "Module2" into it
    strFolder = strFolder & "\"
    strTempFile = strFolder & "~tmpexport.bas"

    On Error Resume Next
    FName = Environ("Temp") & "\" & strModuleName & ".bas"
    If Dir(FName, vbNormal + vbHidden + vbSystem) <> vbNullString Then
        Err.Clear
        Kill FName
        If Err.Number <> 0 Then
            MsgBox "Error copying module " & strModuleName & "  from Workbook " & SourceWB.Name & " to Workbook " & TargetWB.Name, vbInformation
            Exit Sub
        End If
    End If

    ' remove "Module2" if already exits in destination workbook
    With TargetWB.VBProject.VBComponents
        .Remove .Item(strModuleName)
    End With

    ' copy "Module2" from temp file to destination workbook
    SourceWB.VBProject.VBComponents(strModuleName).Export strTempFile
    TargetWB.VBProject.VBComponents.Import strTempFile

    Kill strTempFile
    On Error GoTo 0

End Sub

Main Sub Code (for running this code with the Post's data):

Option Explicit

Public Sub Main()

Dim WB1 As Workbook
Dim WB2 As Workbook

Set WB1 = ThisWorkbook
Set WB2 = Workbooks("Food Specials Rolling Depot Memo 46 - 01.xlsm")

Call CopyModule(WB1, "Module2", WB2)

End Sub
Shai Rado
  • 33,032
  • 6
  • 29
  • 51
  • Do you have to do it via Import/Export? – Jeremy Thompson Dec 04 '16 at 09:44
  • Didn't check to see other ways of implementing this need, I have been running this code (different versions of it) for a while now, and it's giving me the results I need – Shai Rado Dec 04 '16 at 09:47
  • No worries, just curious as to file seems like a lot of overhead and it does have an Add method – Jeremy Thompson Dec 04 '16 at 09:49
  • Feel free to send a link to another simplier and faster solution, always happy to learn and improve – Shai Rado Dec 04 '16 at 09:53
  • You're right: There is no direct way to copy a module from one project to another. To accomplish this task, you must export the module from the Source VBProject and then import that file into the Destination VBProject. ref: http://www.cpearson.com/excel/vbe.aspx – Jeremy Thompson Dec 04 '16 at 09:58
  • 2
    @Princess.Bell I never got your feedback on this answer, did it work for you as you intended ? – Shai Rado Aug 26 '19 at 13:26
7

Actually, you don't need to save anything to a temporary file at all. You can use the .AddFromString method of the destination module to add the string value of the source. Try the following code:

Sub CopyModule()
    Dim SourceVBProject As VBIDE.VBProject, DestinationVBProject As VBIDE.VBProject
    Set SourceVBProject = ThisWorkbook.VBProject
    Dim NewWb As Workbook
    Set NewWb = Workbooks.Add ' Or whatever workbook object you have for the destination
    Set DestinationVBProject = NewWb.VBProject
    '
    Dim SourceModule As VBIDE.CodeModule, DestinationModule As VBIDE.CodeModule
    Set SourceModule = SourceVBProject.VBComponents("Module1").CodeModule ' Change "Module1" to the relevsant source module
    ' Add a new module to the destination project
    Set DestinationModule = DestinationVBProject.VBComponents.Add(vbext_ct_StdModule).CodeModule
    '
    With SourceModule
        DestinationModule.AddFromString .Lines(1, .CountOfLines)
    End With
End Sub

Should be self-explanatory! The .AddFomString method simply takes a string variable. So in order to get that, we use the .Lines property of the source module. The first argument (1) is the start line, and the second argument is the end line number. In this case, we want all the lines, so we use the .CountOfLines property.

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
Chris Melville
  • 1,476
  • 1
  • 14
  • 30
  • Using at once 'AddFromString' seems not be good, because it might duplicate text "Option Explicit" (if apropriate option is ON). Am first (after creation) I delete all the lines in 'DestinationModule'! – Leon Rom Jan 06 '18 at 14:56
  • @Yogendra gets honorable mention for specifying the Extensibility and Macro settings. – LeftyMaus Sep 04 '18 at 18:10
7

Fantastic Code by Chris Melville, Thanks a ton, just a few small addition which i did & added few comments.

Just make sure, following things are done before running this macro.

  • VB Editor > Tools > References > (Check) Microsoft Visual Basic for Applications Extensibility 5.3

  • File -> Options -> Trust Center -> Trust Center Setttings -> Macro Settings -> Trust Access to the VBA Project object model.

Once you do above thing, copy & paste below code in Source File

Sub CopyMacrosToExistingWorkbook()
'Copy this VBA Code in SourceMacroModule, & run this macro in Destination workbook by pressing Alt+F8, the whole module gets copied to destination File.
    Dim SourceVBProject As VBIDE.VBProject, DestinationVBProject As VBIDE.VBProject
    Set SourceVBProject = ThisWorkbook.VBProject
    Dim NewWb As Workbook
    Set NewWb = ActiveWorkbook ' Or whatever workbook object you have for the destination
    Set DestinationVBProject = NewWb.VBProject
    '
    Dim SourceModule As VBIDE.CodeModule, DestinationModule As VBIDE.CodeModule
    Set SourceModule = SourceVBProject.VBComponents("Module1").CodeModule ' Change "Module1" to the relevsant source module
    ' Add a new module to the destination project
    Set DestinationModule = DestinationVBProject.VBComponents.Add(vbext_ct_StdModule).CodeModule
    '
    With SourceModule
        DestinationModule.AddFromString .Lines(1, .CountOfLines)
    End With
End Sub

Now run the "CopyMacrosToExistingWorkbook" macro in destination file, you will see the source file macro copied to destination file.

Paul Siersma
  • 2,036
  • 1
  • 22
  • 25
  • To use `AddFromString` at once after creation seems not be well, because this might duplicate text `Option Explicit` if apropriate option is ON. Am first (after creation) you should delete all the lines in newly created module: `DestinationModule.DeleteLines 1, DestinationModule.CountOfLines` – Leon Rom Jan 06 '18 at 17:22
1

I had a lot of trouble getting the previous answers to work, so I thought I'd post my solution. This function is used to programmatically copy modules from a source workbook to a newly created workbook that was also created programmatically with a call to worksheet.copy. What doesn't happen when a worksheet is copied to a new workbook is the transfer of the macros that the worksheet depends upon. This procedure iterates through all modules in the source workbook and copies them into the new one. What's more is that it actually worked for me in Excel 2016.

Sub CopyModules(wbSource As Workbook, wbTarget As Workbook)
   Dim vbcompSource As VBComponent, vbcompTarget As VBComponent
   Dim sText As String, nType As Long
   For Each vbcompSource In wbSource.VBProject.VBComponents
      nType = vbcompSource.Type
      If nType < 100 Then  '100=vbext_ct_Document -- the only module type we would not want to copy
         Set vbcompTarget = wbTarget.VBProject.VBComponents.Add(nType)
         sText = vbcompSource.CodeModule.Lines(1, vbcompSource.CodeModule.CountOfLines)
         vbcompTarget.CodeModule.AddFromString (sText)
         vbcompTarget.Name = vbcompSource.Name
      End If
   Next vbcompSource
End Sub

The function should hopefully be as simple as possible and fairly self-explanatory.

Sean Hare
  • 21
  • 3
0

You can try

  • Open both workbooks
  • Open VBA Dev window
  • Dropdown Modules and drag and drop a copy from one module are to another

This is to make sure Module Names are not duplicated. Also if you have modules that contain same named function / Subs then there will be a clash.

I do this and then run debug. Seems to work fine.

P.S. I copy many in to my PERSONAL.xlsb

Ryan M
  • 18,333
  • 31
  • 67
  • 74
SKT
  • 1