2012-01-13 14 views
11

Tôi đã trải qua một số kỳ quặc lạ trong Excel khi loại bỏ các mô-đun theo chương trình rồi nhập lại chúng từ tệp. Về cơ bản, tôi có một mô-đun tên là VersionControl có nghĩa vụ xuất các tệp của tôi sang một thư mục được xác định trước và nhập lại chúng theo yêu cầu. Đây là mã cho reimporting (vấn đề với nó được mô tả dưới đây):Thay đổi flushing được thực hiện cho VBProject.VBComponents trong Excel bằng cách sử dụng VBA

Dim i As Integer 
Dim ModuleName As String 
Application.EnableEvents = False 
With ThisWorkbook.VBProject 
    For i = 1 To .VBComponents.Count 
     If .VBComponents(i).CodeModule.CountOfLines > 0 Then 
      ModuleName = .VBComponents(i).CodeModule.Name 
      If ModuleName <> "VersionControl" Then 
       If PathExists(VersionControlPath & "\" & ModuleName & ".bas") Then 
        Call .VBComponents.Remove(.VBComponents(ModuleName)) 
        Call .VBComponents.Import(VersionControlPath & "\" & ModuleName & ".bas") 
       Else 
        MsgBox VersionControlPath & "\" & ModuleName & ".bas" & " cannot be found. No operation will be attempted for that module." 
       End If 
      End If 
     End If 
    Next i 
End With 

Sau khi chạy này, tôi đã nhận thấy rằng một số module không xuất hiện nữa, trong khi một số có bản sao (ví dụ mymodule và mymodule1) . Trong khi bước qua mã, nó trở nên rõ ràng rằng một số mô-đun vẫn nán lại sau cuộc gọi Remove, và chúng được tái nhập trong khi vẫn còn trong dự án. Đôi khi, điều này chỉ dẫn đến việc có mô-đun được gắn với 1, nhưng đôi khi tôi có cả bản gốc và bản sao.

Có cách nào để thực hiện các cuộc gọi tới RemoveImport để chúng tự áp dụng không? Tôi đang suy nghĩ để gọi một hàm Save sau mỗi, nếu có một trong các đối tượng ứng dụng, mặc dù điều này có thể gây ra thiệt hại nếu mọi thứ đi sai trong quá trình nhập.

Ý tưởng?

Chỉnh sửa: thẻ đã thay đổi synchronization thành version-control.

+0

+1 Một cách thông minh để thực hiện một số điều khiển phiên bản tự chế. Tôi nên làm một cái gì đó như thế này bản thân mình. –

+0

Nó được lấy cảm hứng từ [câu hỏi này] (http://stackoverflow.com/questions/131605/best-way-to-do-version-control-for-ms-excel) ở đây trên StackOverflow - phiên bản của tôi chỉ là một khiêm tốn làm lại. – CamilB

+1

Tôi chưa tạo ra bất cứ điều gì như thế này, nhưng những điều tôi muốn thử sẽ là: gọi từ một sổ làm việc/addin khác; sao lưu sổ làm việc trước, thực hiện tất cả các thao tác xóa cùng một lúc, lưu, nhập tất cả cùng một lúc. Bạn cũng có thể lộn xộn xung quanh với phiên bản COM của Rob Bovey's Code Cleaner. Bạn có thể đặt tham chiếu đến nó và truy cập vào chức năng Nhập, Xuất và các chức năng khác. Tôi sẽ quan tâm để xem những gì bạn tìm ra. –

Trả lời

12

Đây là mảng trực tiếp, bạn sẽ thêm và xóa các mục trong quá trình lặp do đó thay đổi số chỉ mục. Hãy thử xử lý mảng ngược. Đây là giải pháp của tôi mà không bất kỳ xử lý lỗi:

Private Const DIR_VERSIONING As String = "\\VERSION_CONTROL" 
Private Const PROJ_NAME As String = "PROJECT_NAME" 

Sub EnsureProjectFolder() 
    ' Does this project directory exist 
    If Len(Dir(DIR_VERSIONING & PROJ_NAME, vbDirectory)) = 0 Then 
     ' Create it 
     MkDir DIR_VERSIONING & PROJ_NAME 
    End If 
End Sub 

Function ProjectFolder() As String 
    ' Ensure the folder exists whenever we try to access it (can be deleted mid execution) 
    EnsureProjectFolder 
    ' Create the required full path 
    ProjectFolder = DIR_VERSIONING & PROJ_NAME & "\" 
End Function 

Sub SaveCodeModules() 

    'This code Exports all VBA modules 
    Dim i%, sName$ 

    With ThisWorkbook.VBProject 
     ' Iterate all code files and export accordingly 
     For i% = 1 To .VBComponents.count 
      ' Extract this component name 
      sName$ = .VBComponents(i%).CodeModule.Name 
      If .VBComponents(i%).Type = 1 Then 
       ' Standard Module 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".bas" 
      ElseIf .VBComponents(i%).Type = 2 Then 
       ' Class 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".cls" 
      ElseIf .VBComponents(i%).Type = 3 Then 
       ' Form 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".frm" 
      ElseIf .VBComponents(i%).Type = 100 Then 
       ' Document 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".bas" 
      Else 
       ' UNHANDLED/UNKNOWN COMPONENT TYPE 
      End If 
     Next i 
    End With 

End Sub 

Sub ImportCodeModules() 
    Dim i%, sName$ 

    With ThisWorkbook.VBProject 
     ' Iterate all components and attempt to import their source from the network share 
     ' Process backwords as we are working through a live array while removing/adding items 
     For i% = .VBComponents.count To 1 Step -1 
      ' Extract this component name 
      sName$ = .VBComponents(i%).CodeModule.Name 
      ' Do not change the source of this module which is currently running 
      If sName$ <> "VersionControl" Then 
       ' Import relevant source file if it exists 
       If .VBComponents(i%).Type = 1 Then 
        ' Standard Module 
        .VBComponents.Remove .VBComponents(sName$) 
        .VBComponents.Import fileName:=ProjectFolder & sName$ & ".bas" 
       ElseIf .VBComponents(i%).Type = 2 Then 
        ' Class 
        .VBComponents.Remove .VBComponents(sName$) 
        .VBComponents.Import fileName:=ProjectFolder & sName$ & ".cls" 
       ElseIf .VBComponents(i%).Type = 3 Then 
        ' Form 
        .VBComponents.Remove .VBComponents(sName$) 
        .VBComponents.Import fileName:=ProjectFolder & sName$ & ".frm" 
       ElseIf .VBComponents(i%).Type = 100 Then 
        ' Document 
        Dim TempVbComponent, FileContents$ 
        ' Import the document. This will come in as a class with an increment suffix (1) 
        Set TempVbComponent = .VBComponents.Import(ProjectFolder & sName$ & ".bas") 

        ' Delete any lines of data in the document 
        If .VBComponents(i%).CodeModule.CountOfLines > 0 Then .VBComponents(i%).CodeModule.DeleteLines 1, .VBComponents(i%).CodeModule.CountOfLines 

        ' Does this file contain any source data? 
        If TempVbComponent.CodeModule.CountOfLines > 0 Then 
         ' Pull the lines into a string 
         FileContents$ = TempVbComponent.CodeModule.Lines(1, TempVbComponent.CodeModule.CountOfLines) 
         ' And copy them to the correct document 
         .VBComponents(i%).CodeModule.InsertLines 1, FileContents$ 
        End If 

        ' Remove the temporary document class 
        .VBComponents.Remove TempVbComponent 
        Set TempVbComponent = Nothing 

       Else 
        ' UNHANDLED/UNKNOWN COMPONENT TYPE 
       End If 
      End If 
      Next i 
     End With 

End Sub 
+2

Tôi muốn cung cấp cho +2 nếu có thể. Trước tiên, bạn tiết lộ nguyên nhân thực sự của lỗi: chỉnh sửa mảng trực tiếp; đó là một sai lầm rất ngu ngốc của tôi, tôi đã không chắc chắn sau đó làm thế nào những mảng làm việc.Hóa ra chúng thực sự là các đối tượng 'Bộ sưu tập'. Thứ hai, mã của bạn xử lý đúng các loại mô-đun khác nhau và lưu chúng với phần mở rộng tệp thích hợp. Tôi thay đổi mã của tôi để làm điều đó quá một thời gian trước đây, nó là khá quan trọng thực sự; +1 để hiển thị mã của bạn, vì vậy mọi người sẽ biết. – CamilB

1

OP ở đây ... Tôi đã cố gắng giải quyết vấn đề kỳ lạ này, nhưng tôi chưa tìm được giải pháp thực sự. Đây là những gì tôi đã làm.

  1. nỗ lực đầu tiên của tôi sau khi đăng câu hỏi là này (spoiler: nó gần làm việc):

    Giữ tháo tách biệt với nhập khẩu, nhưng trong thủ tục tương tự. Điều này có nghĩa là tôi có 3 vòng - một để lưu trữ danh sách tên mô-đun (dưới dạng chuỗi đơn giản), một loại khác để xóa mô-đun và một mô-đun khác để nhập mô-đun từ tệp (dựa trên tên được lưu trữ trong danh sách đã nói ở trên) .

    Vấn đề: một số mô-đun vẫn còn trong dự án khi vòng loại bỏ kết thúc. Tại sao? Tôi không thể giải thích. Tôi sẽ đánh dấu điều này là vấn đề ngu ngốc không. 1. Sau đó, tôi đã cố gắng thực hiện cuộc gọi Remove cho mỗi mô-đun bên trong vòng lặp liên tục cố xóa mô-đun đơn đó cho đến khi không thể tìm thấy mô-đun trong dự án. Điều này đã bị mắc kẹt trong một vòng lặp vô hạn cho một mô-đun nhất định - tôi không thể nói điều gì đặc biệt về cái cụ thể đó.

    Cuối cùng tôi đã tìm ra rằng các mô-đun chỉ thực sự bị xóa sau khi Excel tìm thấy một thời gian để xóa các suy nghĩ của nó. Điều này không làm việc với Application.Wait(). Mã VBA hiện đang chạy thực sự cần thiết để kết thúc cho việc này xảy ra. Kỳ dị.

  2. Thứ hai công việc xung quanh nỗ lực (spoiler: một lần nữa, nó gần làm việc):

    Để cung cấp cho Excel lần yêu cầu để thở sau khi ñuoåi, tôi đặt vòng lặp loại bỏ bên trong một handler bấm vào nút (không có "cuộc gọi Hủy bỏ cho đến khi nó biến mất" vòng lặp) và vòng lặp nhập trong trình xử lý nhấp chuột của một nút khác. Tất nhiên, tôi cần danh sách các tên mô-đun, vì vậy tôi đã tạo cho nó một chuỗi các chuỗi toàn cầu. Nó được tạo ra trong trình xử lý nhấp chuột, trước vòng lặp loại bỏ và nó được cho là được truy cập bởi vòng lặp nhập. Có nên đã làm việc, phải không?

    Sự cố: Mảng chuỗi nói trên trống khi vòng lặp nhập bắt đầu (bên trong trình xử lý nhấp chuột khác). Đó là chắc chắn có khi vòng loại bỏ kết thúc - tôi in nó với Debug.Print. Tôi đoán nó đã được de-phân bổ bởi các removals (??). Đây sẽ là vấn đề ngu ngốc không. 2. Nếu không có mảng chuỗi chứa các tên mô-đun, vòng lặp nhập không làm gì cả, do đó không thể làm việc xung quanh.

  3. Giải pháp cuối cùng, chức năng. Cái này hoạt động.

    Tôi lấy Work-around number 2 và thay vì lưu trữ tên module trong một mảng chuỗi, tôi đã lưu chúng trong một hàng của tờ phụ trợ (tôi gọi là trang tính "Devel").

Đây là nó. Nếu bất cứ ai có thể giải thích vấn đề ngu ngốc không. 1vấn đề ngu ngốc không. 2, tôi cầu xin bạn, làm như vậy. Họ có lẽ không phải là ngu ngốc - tôi vẫn còn ở đầu với VBA, nhưng tôi có kiến ​​thức vững chắc về lập trình trong các ngôn ngữ khác (sane và hiện đại).

Tôi có thể thêm mã để minh họa vấn đề ngu ngốc không. 2, nhưng câu trả lời này đã dài. Nếu những gì tôi đã làm không rõ ràng, tôi sẽ đặt nó ở đây.

+1

Đầu cơ thuần túy: Tôi đoán rằng động cơ VBA của Excel không thể xử lý các môđun loại bỏ ở giữa (có lẽ là một hàm VBA được biên dịch). (Có gì để làm, biên dịch lại VBA và thay thế nó vào để hoàn thành công việc? Quá khó.) Chờ cho đến khi bạn ra khỏi chức năng VBA có ý nghĩa với tôi. –

+0

@ todda.speot.is: Điều đó có ý nghĩa, nhưng một số mô-đun đã bị loại bỏ trong vòng lặp (một trong những điều tôi biết, ít nhất, nó có thể có hành vi kỳ lạ với những người khác nữa, trước khi tôi bắt đầu hiểu những gì đang diễn ra trên). VBA chỉ bị kẹt ở một mô-đun cụ thể và từ chối tiếp tục sau đó. – CamilB

+1

Nhiều suy đoán: Một số mô-đun có các biến toàn cầu và các mô-đun khác thì không. Những người không có thể được loại bỏ tốt, bởi vì không cần phải biên dịch lại các mô-đun khác. Những người có các biến toàn cầu (hoặc có lẽ được tham chiếu từ mô-đun mà hàm của bạn đang chạy) không thể bị xóa cho đến khi kết thúc thực thi. –

1

Để tránh trùng lặp khi nhập khẩu, tôi sửa đổi kịch bản với chiến lược sau:

  • Đổi tên mô-đun hiện
  • mô-đun nhập
  • Xóa mô-đun đổi tên

Tôi không còn trùng lặp trong khi nhập.


Sub SaveCodeModules() 

'This code Exports all VBA modules 
Dim i As Integer, name As String 

With ThisWorkbook.VBProject 
For i = .VBComponents.Count To 1 Step -1 

    name = .VBComponents(i).CodeModule.name 

    If .VBComponents(i).Type = 1 Then 
     ' Standard Module 
     .VBComponents(i).Export Application.ThisWorkbook.Path & "\trunk\" & name & ".module" 
    ElseIf .VBComponents(i).Type = 2 Then 
     ' Class 
     .VBComponents(i).Export Application.ThisWorkbook.Path & "\trunk\" & name & ".classe" 
    ElseIf .VBComponents(i).Type = 3 Then 
     ' Form 
     .VBComponents(i).Export Application.ThisWorkbook.Path & "\trunk\" & name & ".form" 
    Else 
     ' DO NOTHING 
    End If 
Next i 
End With 

End Sub 

Sub ImportCodeModules() 

Dim i As Integer 
Dim delname As String 
Dim modulename As String 

With ThisWorkbook.VBProject 
For i = .VBComponents.Count To 1 Step -1 

    modulename = .VBComponents(i).CodeModule.name 

    If modulename <> "VersionControl" Then 

     delname = modulename & "_to_delete" 

     If .VBComponents(i).Type = 1 Then 
      ' Standard Module 
      .VBComponents(modulename).name = delname 
      .VBComponents.Import Application.ThisWorkbook.Path & "\trunk\" & modulename & ".module" 
      .VBComponents.Remove .VBComponents(delname) 

     ElseIf .VBComponents(i).Type = 2 Then 
      ' Class 
      .VBComponents(modulename).name = delname 
      .VBComponents.Import Application.ThisWorkbook.Path & "\trunk\" & modulename & ".classe" 
      .VBComponents.Remove .VBComponents(delname) 

     ElseIf .VBComponents(i).Type = 3 Then 
      ' Form 
      .VBComponents.Remove .VBComponents(modulename) 
      .VBComponents.Import Application.ThisWorkbook.Path & "\trunk\" & modulename & ".form" 
     Else 
      ' DO NOTHING 
     End If 

    End If 
Next i 

End With 

End Sub 

Mã để được dán trong một module mới "VersionControl"

0

Các đổi tên, nhập khẩu và xóa workaround đã không làm việc trong trường hợp của tôi. Có vẻ như (nhưng đây là suy đoán thuần túy) rằng Excel có thể lưu các đối tượng đã biên dịch trong tệp .XLMS của nó và khi tệp này được mở lại, các đối tượng này được tải lại trong bộ nhớ trước khi hàm ThisWorkbook_open xuất hiện. Và điều này dẫn đến việc đổi tên (hoặc loại bỏ) các mô-đun nhất định thành thất bại hoặc bị trì hoãn (ngay cả khi cố gắng ép buộc nó với cuộc gọi DoEvents). Cách giải quyết duy nhất tôi tìm thấy là sử dụng định dạng nhị phân .XLS. Đối với một số lý do tối nghĩa (tôi nghi ngờ các đối tượng biên dịch không được đóng gói trong tập tin), nó hoạt động cho tôi.

Bạn phải biết rằng bạn sẽ không thể nhập lại bất kỳ mô-đun nào đang được sử dụng hoặc tham chiếu tại thời điểm mã nhập của bạn chạy (đổi tên sẽ không thành công với lỗi 32813/loại bỏ mô-đun sẽ bị trì hoãn cho đến sau khi bạn sẽ cố gắng nhập khẩu, thêm 'gây phiền nhiễu' ở cuối tên mô-đun của bạn).Nhưng đối với bất kỳ mô-đun nào khác, nó sẽ hoạt động.

Nếu tất cả mã nguồn của bạn cần được quản lý, giải pháp tốt hơn là "xây dựng" bảng tính của bạn từ đầu bằng cách sử dụng một số tập lệnh hoặc công cụ hoặc chuyển sang ngôn ngữ lập trình phù hợp hơn. bên trong một phần mềm bộ phần mềm văn phòng;) Tôi đã không thử nó nhưng bạn có thể xem ở đây: Source control of Excel VBA code modules.

1

Tôi đã đấu tranh với vấn đề này trong nhiều ngày nay. Tôi xây dựng một hệ thống điều khiển phiên bản thô tương tự như thế này, mặc dù không sử dụng mảng. Mô-đun điều khiển phiên bản được nhập vào Workbook_Open và sau đó quy trình khởi động được gọi để nhập tất cả các mô-đun được liệt kê trong mô-đun điều khiển phiên bản. Mọi thứ hoạt động tốt trừ Excel bắt đầu tạo các mô-đun điều khiển phiên bản trùng lặp vì nó sẽ nhập mô-đun mới trước khi xóa xong mô-đun hiện tại. Tôi đã làm việc xung quanh điều đó bằng cách thêm Delete vào mô-đun trước đó. Vấn đề sau đó là vẫn còn hai thủ tục có cùng tên. Chip Pearson có một số mã để xóa một thủ tục lập trình, vì vậy tôi đã xóa mã khởi động từ mô-đun điều khiển phiên bản cũ hơn. Tuy nhiên, tôi đã gặp phải vấn đề mà thủ tục không bị xóa vào lúc thủ tục khởi động được gọi. Cuối cùng tôi đã tìm thấy một giải pháp trên một luồng tràn ngăn xếp khác đơn giản đến mức tôi muốn đặt đầu vào một bức tường. Tất cả những gì tôi phải làm là thay đổi cách tôi gọi thủ tục khởi động của mình bằng cách sử dụng

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

Mọi thứ hoạt động hoàn hảo ngay bây giờ. Mặc dù, tôi có thể sẽ quay trở lại và xóa bỏ việc đổi tên dự phòng hiện tại của mô-đun và xóa thủ tục thứ hai và xem liệu điều này có giải quyết được vấn đề ban đầu của tôi hay không. Dưới đây là một chủ đề khác có giải pháp ...

Source control of Excel VBA code modules