2009-04-01 41 views
19

Tôi muốn có thể kiểm soát nguồn mô-đun VBA của bảng tính Excel (hiện đang sử dụng Excel 2003 SP3) để tôi có thể chia sẻ và quản lý mã được sử dụng bởi nhiều bảng tính khác nhau - và do đó tôi muốn tải lại chúng từ các tệp khi bảng tính được mở.Kiểm soát nguồn của mô-đun mã VBA Excel

Tôi có một mô-đun gọi là Loader.bas, mà tôi sử dụng để thực hiện hầu hết công việc lừa (tải và dỡ bất kỳ mô-đun nào khác được yêu cầu) - và tôi muốn có thể tải nó lên từ một tệp ngay sau khi bảng tính được mở.

Tôi đã đính kèm mã sau vào sự kiện Workbook_Open (trong lớp ThisWorkbook).

Private Sub Workbook_Open() 
    Call RemoveLoader 
    Call LoadLoader 
End Sub 

đâu RemoveLoader (cũng trong lớp ThisWorkbook) chứa đoạn mã sau:

Private Sub RemoveLoader() 
    Dim y As Integer 
    Dim OldModules, NumModules As Integer 
    Dim CompName As String 

    With ThisWorkbook.VBProject 
     NumModules = ThisWorkbook.VBProject.VBComponents.Count 
     y = 1 
     While y <= NumModules 
      If .VBComponents.Item(y).Type = 1 Then 
       CompName = .VBComponents.Item(y).Name 
       If VBA.Strings.InStr(CompName, "Loader") > 0 Then 
        OldModules = ThisWorkbook.VBProject.VBComponents.Count 
        .VBComponents.Remove .VBComponents(CompName) 
        NumModules = ThisWorkbook.VBProject.VBComponents.Count 
        If OldModules - NumModules = 1 Then 
         y = 1 
        Else 
         MsgBox ("Failed to remove " & CompName & " module from VBA project") 
        End If 
       End If 
      End If 
      y = y + 1 
     Wend 
    End With 
End Sub 

Mà có lẽ là một chút overcomplicated và hơi thô - nhưng tôi đang cố gắng tất cả mọi thứ tôi có thể tìm thấy để có được nó để tải mô-đun bên ngoài!

Thông thường, khi tôi mở bảng tính, hàm RemoveLoader tìm thấy có một mô-đun "Loader1" đã được đưa vào dự án VBA mà nó không thể loại bỏ và cũng không tải được mô-đun Trình tải mới từ tệp.

Bất kỳ ý tưởng nào nếu những gì tôi đang cố gắng làm là có thể? Excel có vẻ rất thích gắn thêm 1 vào các tên mô-đun này - khi tải hoặc xóa (tôi không chắc chắn).

+0

Tôi quên thêm rằng tôi đã làm một số suy nghĩ và đưa ra điều này: http://grumpyop.wordpress.com/2009/04/20/version-control-for-excel-workbooks-part-2/ Lưu ý rằng cũng có một cái gì đó gọi là VBAMaven được đề cập trong các ý kiến ​​giống như một số dịch vụ bên ngoài có thể giúp đỡ. –

Trả lời

4

Nhìn vào trang VBAMaven. Tôi có một giải pháp trong nhà sử dụng các khái niệm tương tự. Tôi có một thư viện chung với một loạt các mã nguồn, một kiến ​​xây dựng và một kịch bản "nhập khẩu" VB. Ant kiểm soát việc xây dựng, trong đó có một tập tin excel trống và đẩy mã cần thiết vào nó. @Mike là hoàn toàn chính xác - bất kỳ định nghĩa mô-đun trùng lặp nào sẽ tự động có một số được nối thêm vào tên mô-đun. Ngoài ra, các lớp học mô-đun (như trong Sheet và ThisWorkbook) các lớp học yêu cầu điều trị đặc biệt. Bạn không thể tạo các mô-đun đó, bạn phải đọc tệp đầu vào và ghi bộ đệm vào mô-đun thích hợp. Đây là tập lệnh VB mà tôi hiện đang sử dụng để thực hiện việc này. Phần có chứa văn bản @ delimited (tức là @build file @) là trình giữ chỗ - bản dựng kiến ​​thay thế các thẻ này bằng nội dung có ý nghĩa. Nó không hoàn hảo, nhưng làm việc cho tôi.

'' 
' Imports VB Basic module and class files from the src folder 
' into the excel file stored in the bin folder. 
' 

Option Explicit 

Dim pFileSystem, pFolder, pPath 
Dim pShell 
Dim pApp, book 

Dim pFileName 

pFileName = "@build [email protected]" 

Set pFileSystem = CreateObject("Scripting.FileSystemObject") 

Set pShell = CreateObject("WScript.Shell") 
pPath = pShell.CurrentDirectory 

If IsExcelFile (pFileName) Then 
    Set pApp = WScript.CreateObject ("Excel.Application") 
    pApp.Visible = False 
    Set book = pApp.Workbooks.Open(pPath & "\build\" & pFileName) 
Else 
    Set pApp = WScript.CreateObject ("Word.Application") 
    pApp.Visible = False 
    Set book = pApp.Documents.Open(pPath & "\build\" & pFileName) 
End If 


'Include root source folder code if no args set 
If Wscript.Arguments.Count = 0 Then 
    Set pFolder = pFileSystem.GetFolder(pPath & "\src") 
    ImportFiles pFolder, book 
    ' 
    ' Get selected modules from the Common Library, if any 
    @common [email protected]@common [email protected] 
Else 
    'Add code from subdirectories of src . . . 
    If Wscript.Arguments(0) <> "" Then 
     Set pFolder = pFileSystem.GetFolder(pPath & "\src\" & Wscript.Arguments(0)) 
     ImportFiles pFolder, book 
    End If 
End If 





Set pFolder = Nothing 
Set pFileSystem = Nothing 
Set pShell = Nothing 


If IsExcelFile (pFileName) Then 
    pApp.ActiveWorkbook.Save 
Else 
    pApp.ActiveDocument.Save 
End If 

pApp.Quit 
Set book = Nothing 
Set pApp = Nothing 


'' Loops through all the .bas or .cls files in srcFolder 
' and calls InsertVBComponent to insert it into the workbook wb. 
' 
Sub ImportFiles(ByVal srcFolder, ByVal obj) 
    Dim fileCollection, pFile 
    Set fileCollection = srcFolder.Files 
    For Each pFile in fileCollection 
     If Right(pFile, 3) = "bas _ 
      Or Right(pFile, 3) = "cls _ 
      Or Right(pFile, 3) = "frm Then 
      InsertVBComponent obj, pFile 
     End If 
    Next 
    Set fileCollection = Nothing 
End Sub 


'' Inserts the contents of CompFileName as a new component in 
' a Workbook or Document object. 
' 
' If a class file begins with "Sheet", then the code is 
' copied into the appropriate code module 1 painful line at a time. 
' 
' CompFileName must be a valid VBA component (class or module) 
Sub InsertVBComponent(ByVal obj, ByVal CompFileName) 
    Dim t, mName 
    t = Split(CompFileName, "\") 
    mName = Split(t(UBound(t)), ".") 
    If IsSheetCodeModule(mName(0), CompFileName) = True Then 
     ImportCodeModule obj.VBProject.VBComponents(mName(0)).CodeModule, _ 
         CompFileName 
    Else 
     If Not obj Is Nothing Then 
      obj.VBProject.VBComponents.Import CompFileName 
     Else 
      WScript.Echo "Failed to import " & CompFileName 
     End If 
    End If 
End Sub 

'' 
' Imports the code in the file fName into the workbook object 
' referenced by mName. 
' @param target destination CodeModule object in the excel file 
' @param fName file system file containing code to be imported 
Sub ImportCodeModule (ByVal target, ByVal fName) 
    Dim shtModule, code, buf  
    Dim fso 
    Set fso = CreateObject("Scripting.FileSystemObject") 
    Const ForReading = 1, ForWriting = 2, ForAppending = 3 
    Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0 

    Set buf = fso.OpenTextFile(fName, ForReading, False, TristateUseDefault) 
    buf.SkipLine 
    code = buf.ReadAll 

    target.InsertLines 1, code 
    Set fso = Nothing 
End Sub 


'' 
' Returns true if the code module in the file fName 
' appears to be a code module for a worksheet. 
Function IsSheetCodeModule (ByVal mName, ByVal fName) 
    IsSheetCodeModule = False 
    If mName = "ThisWorkbook" Then 
     IsSheetCodeModule = False 
    ElseIf Left(mName, 5) = "Sheet" And _ 
     IsNumeric(Mid (mName, 6, 1)) And _ 
     Right(fName, 3) = "cls Then 
     IsSheetCodeModule = True 
    End If 
End Function 

'' 
' Returns true if fName has a xls file extension 
Function IsExcelFile (ByVal fName) 
    If Right(fName, 3) = "xls" Then 
     IsExcelFile = True 
    Else 
     IsExcelFile = False 
    End If 
End Function 
+0

Cảm ơn vì điều này. Tôi đã xem kịch bản của bạn và vbaMaven và có vẻ như cả hai phương thức đều tạo một bảng tính hoàn toàn mới và sao chép các macro vào chúng. Đúng không? Tôi đã hy vọng tìm một phương pháp để tải các macro vào bảng tính khi bảng tính được mở - nhưng tôi đã hoàn toàn thất bại để làm việc đó. Có lẽ tôi đang cố gắng làm một cái gì đó mà không thể được thực hiện với Excel. Bây giờ tôi bắt đầu xem xét thay thế các macro của tôi bằng một tập lệnh bên ngoài truy cập Excel qua COM - vì vậy ít nhất tôi có thể giữ tập lệnh dưới quyền kiểm soát phiên bản. –

+0

Có, ý tưởng cơ bản là bảng tính 'trống' được sao chép và mã được sao chép vào đó. Nếu tôi đã biết về vbaMaven trước khi tôi bắt đầu, tôi chỉ có thể đã đi với nó ... Lý do tôi đã chọn để đi tuyến đường này là để mỗi dòng mã có thể được dưới sự kiểm soát nguồn. Nếu bạn có mã trong sổ làm việc tải mã khác, thì mô-đun tải mã của bạn không nằm dưới cùng một mức kiểm soát như mọi thứ khác - nó được nén bên trong tệp excel. – DaveParillo

+0

Ngoài ra, bạn phải sửa đổi mã sự kiện rất cẩn thận trên các sự kiện Auto_Open hoặc Workbook_Open. Cố gắng chỉnh sửa đối tượng ThisWorkbook từ bên trong chức năng Auto_Open là một đường dẫn nhanh để gặp lỗi excel. – DaveParillo

2

Thông thường, điều "Loader1" xảy ra khi Excel được yêu cầu nhập mô-đun và mô-đun đã tồn tại có cùng tên. Vì vậy, nếu bạn nhập "Loader", sau đó tải nó một lần nữa và bạn sẽ nhận được "Loader1". Điều này có thể là do Excel không biết (hoặc có thể không quan tâm) nếu nó thực sự giống nhau hoặc một đoạn chức năng mới chỉ xảy ra có cùng tên mô-đun, vì vậy nó vẫn nhập nó.

Tôi không thể nghĩ ra một giải pháp hoàn hảo, nhưng tôi nghĩ tôi sẽ cố gắng đặt logic tải/dỡ trong một add-in - rằng Workbook_Open có vẻ hơi dễ bị tổn thương và có nó trong tất cả các sổ làm việc sẽ là một nỗi đau lớn nếu mã bao giờ cần phải thay đổi (không bao giờ nói không bao giờ). Logic XLA có thể phức tạp hơn (khó khăn hơn để bẫy các sự kiện cần thiết, một điều) nhưng ít nhất nó sẽ chỉ tồn tại ở một nơi.

+0

Cảm ơn câu trả lời Mike. Bạn có thể chính xác, một AddIn có thể là cách duy nhất để làm điều đó. Tôi đã cố gắng để tránh các giải pháp AddIn, bởi vì tôi muốn lưu trữ các macro trong SubVersion - vì vậy tôi muốn lưu trữ chúng như là các tập tin văn bản vì vậy tôi có thể hợp nhất và diff một cách dễ dàng - chứ không phải là một tập tin XLA nhị phân. –

+0

Có thể sử dụng một addin nhỏ để tải các mô-đun? –

+0

Tôi không chắc cách sử dụng AddIn sẽ trợ giúp trong tình huống này. Để tải các mô-đun VBA không nhị phân vào excel, tôi vẫn sẽ phải chạy một số loại mã khi Sổ làm việc được mở - và tôi không nghĩ rằng việc giữ mã Trình tải trong một AddIn sẽ thay đổi bản chất của vấn đề . –

3

Tôi đã làm việc chính xác điều này trong nhiều tháng. Tôi nghĩ tôi đã tìm ra.

Nếu dự án VB đang cố gắng loại bỏ một mô-đun có chứa một cái gì đó trong ngăn xếp cuộc gọi, nó trì hoãn việc loại bỏ cho đến khi ngăn xếp cuộc gọi bật mô-đun được thay thế.

Để tránh mô-đun trong ngăn xếp cuộc gọi, hãy khởi chạy mã của bạn bằng Ứng dụng.OnTime

Private Sub Workbook_Open() 

    'WAS: module_library (1) 

    Application.OnTime (Now + TimeValue("00:00:01")), "load_library_kicker_firstiter" 

End Sub 

Nếu bạn tự sửa chữa mã như tôi, bạn cũng sẽ phải khởi chạy mã ghi đè mã 'gọi' với cùng chiến lược đó.

tôi đã không thực hiện thử nghiệm rộng rãi chưa, tôi hoàn toàn âm lễ kỷ niệm, nhưng điều này khiến tôi vô cùng chặt chẽ để mã tự chữa bệnh đơn giản 99,9% trong vòng một file .xls độc lập mà không cần bất kỳ thủ đoạn khác

+1

CẢM ƠN BẠN. Đây là vấn đề chính xác của tôi gần 4 năm sau đó. – enderland

12

Có một giải pháp tuyệt vời cho vấn đề kiểm soát phiên bản vba ở đây: https://github.com/hilkoc/vbaDeveloper

Phần tốt đẹp về việc này là xuất mã của bạn tự động, ngay sau khi bạn lưu sổ làm việc của mình. Ngoài ra, khi bạn mở một sổ làm việc, nó sẽ nhập mã.

Bạn không cần chạy bất kỳ tập lệnh xây dựng hoặc lệnh maven nào và bạn không cần thực hiện bất kỳ thay đổi nào đối với sổ làm việc của mình. Nó hoạt động cho tất cả.

Nó cũng đã giải quyết được sự cố nhập khi các mô-đun như ModName đang được nhập dưới dạng ModName1 thành một mô-đun trùng lặp. Việc nhập khẩu hoạt động như nó phải, ngay cả khi làm nó nhiều lần.

Là phần thưởng, nó đi kèm với trình định dạng mã đơn giản, cho phép bạn định dạng mã vba của mình khi bạn viết mã trong Trình chỉnh sửa VBA.

+1

Tôi vừa trải qua toàn bộ quá trình xây dựng, và có hai vấn đề, tôi muốn hỏi xem bạn đã tìm ra: 1. Băng Add-Ins không hiển thị menu vbaDeveloper, trừ khi tôi chạy refreshMenu () Phương thức được cung cấp trong mô-đun Menu 2. Xuất mã không tự động xảy ra, thay vào đó tôi phải kích hoạt hành động theo cách thủ công –

+3

1 Trình đơn sẽ được tạo bởi sự kiện workbook_open. Cho rằng để làm việc xây dựng lưu và đóng bổ trợ. Sau đó, sau khi mở lại, sự kiện workbook_open sẽ được kích hoạt, trừ khi bạn vô hiệu hóa thủ công với: Application.EnableEvents = False 2 Việc nhập/xuất tự động bị tắt theo mặc định: https://github.com/hilkoc/vbaDeveloper/issues /số 8 – CodeKid

Các vấn đề liên quan