Sửa lỗi Run-time error '28': Out of stack space trong Excel VBA

intermediate📊 Microsoft Excel2026-06-23| Microsoft Excel (Tất cả các phiên bản), VBA (Visual Basic for Applications) trên Windows và macOS.

Error Message

Run-time error '28': Out of stack space
#excel-vba#loi-vba#go-loi#tran-ngan-xep

Stack Overflow trong VBA là gì?Đã bao giờ bạn gặp trường hợp một macro đột ngột làm treo Excel, chỉ để hiển thị thông báo lỗi 'Run-time error 28' chưa? Điều này thường xảy ra khi mã của bạn bị kẹt trong một vòng lặp không thể thoát ra. Hãy tưởng tượng stack như một chồng đĩa hẹp. Mỗi khi mã của bạn gọi một function mới, nó sẽ thêm một chiếc đĩa vào chồng. Nếu nó liên tục thêm đĩa mà không bao giờ hoàn thành tác vụ và lấy đĩa ra, chồng đĩa sẽ bị sập. Trong VBA, stack là một phần nhỏ của bộ nhớ—thường khoảng 1MB—dành riêng để theo dõi các lệnh gọi procedure đang hoạt động.

Những nguyên nhân phổ biếnKhông gian stack có hạn. Bạn thường chạm trần giới hạn này theo một trong ba cách sau:

  • Infinite Recursion: Một function tự gọi chính nó nhưng thiếu nút "dừng", dẫn đến hàng nghìn lệnh gọi lồng nhau.- Event Cascading: Một event như Worksheet_Change kích hoạt một thay đổi, sau đó thay đổi này lại kích hoạt lại chính event đó trong một vòng lặp.- Overloaded Procedures: Sử dụng các array cục bộ khổng lồ hoặc gọi hàng trăm subroutine lồng sâu vào nhau.## Cách khắc phục 1: Dừng Event Cascading (Cách sửa phổ biến nhất)Hầu hết các lập trình viên gặp phải Error 28 khi viết mã cho các event của worksheet. Hãy tưởng tượng bạn viết một macro để tự động viết hoa văn bản. Nếu macro thay đổi một ô, Excel sẽ nhận thấy thay đổi đó và chạy lại macro. Điều này tạo ra một vòng lặp phản hồi làm cạn kiệt stack chỉ trong vài mili giây.

Mã nguồn gây lỗi```

' Lệnh này sẽ kích hoạt vòng lặp vô tận ngay lập tức Private Sub Worksheet_Change(ByVal Target As Range) Target.Value = UCase(Target.Value) ' Thay đổi này kích hoạt lại sub một lần nữa! End Sub


### Giải phápĐể dừng chu kỳ này, bạn phải yêu cầu Excel tạm thời bỏ qua các thay đổi của chính nó. Sử dụng `Application.EnableEvents` để làm Excel "im lặng" trong khi mã của bạn thực hiện công việc.

Private Sub Worksheet_Change(ByVal Target As Range) On Error GoTo ErrorHandler

' Tắt các event của Excel để ngăn chặn vòng lặp
Application.EnableEvents = False

' Chỉ chạy logic cho các ô cụ thể (ví dụ: A1:A10)
If Not Intersect(Target, Me.Range("A1:A10")) Is Nothing Then
    Target.Value = UCase(Target.Value)
End If

CleanExit: ' LUÔN LUÔN bật lại các event Application.EnableEvents = True Exit Sub

ErrorHandler: MsgBox "An error occurred: " & Err.Description Resume CleanExit End Sub


## Cách khắc phục 2: Xác định Base Case cho RecursionRecursion là một cách mạnh mẽ để điều hướng các thư mục hoặc thực hiện các phép toán phức tạp, nhưng nó cũng đầy rủi ro. Mỗi khi một function tự gọi chính nó, nó sẽ tiêu tốn thêm bộ nhớ của stack 1MB đó. Nếu không có điều kiện thoát rõ ràng, mã sẽ lặn sâu hơn cho đến khi bị sập.
### Mã nguồn gây lỗi```
Function CalculateFactorial(n As Long) As Long
    ' Lệnh này sẽ chạy cho đến khi chạm giới hạn stack
    CalculateFactorial = n * CalculateFactorial(n - 1)
End Function

Giải phápMỗi function đệ quy đều cần một "Base Case". Đây là một câu lệnh If đơn giản để dừng function tự gọi chính nó sau khi đạt được mục tiêu.

Function CalculateFactorial(n As Long) As Long
    ' Base Case: Dừng lại khi n chạm mức 1
    If n <= 1 Then
        CalculateFactorial = 1
        Exit Function
    End If
    
    CalculateFactorial = n * CalculateFactorial(n - 1)
End Function

Cách khắc phục 3: Sử dụng vòng lặp thay vì lồng nhau quá sâuNếu logic của bạn bao gồm việc gọi Sub A, Sub A gọi Sub B, Sub B gọi Sub C qua hàng trăm cấp độ, bạn đang đùa với lửa. Các biến cục bộ lớn, chẳng hạn như các chuỗi có độ dài cố định hoặc các array với hơn 10.000 phần tử được khai báo bên trong một Sub, cũng ngốn không gian stack rất nhanh.

  • Chuyển sang Iteration: Sử dụng vòng lặp For...Next hoặc Do...While thay vì recursion. Các vòng lặp sử dụng heap (bộ nhớ hệ thống chính), có kích thước hàng gigabyte thay vì stack 1MB nhỏ bé.- Di chuyển biến: Khai báo các array lớn ở đầu module của bạn (cấp độ Module) thay vì bên trong một procedure cụ thể để giữ chúng nằm ngoài stack.``` ' Các vòng lặp Iterative an toàn hơn nhiều cho memory stack Function CalculateFactorialIterative(n As Long) As Long Dim result As Long, i As Long result = 1 For i = 1 To n result = result * i Next i CalculateFactorialIterative = result End Function

## How to Verify Your FixĐừng chỉ đoán xem mã đã được sửa hay chưa. Hãy sử dụng các bước debug sau để chắc chắn:
- **Chạy từng bước với F8:** Nhấn `F8` trong trình soạn thảo VBA để chạy mã của bạn theo từng dòng. Nếu bạn thấy vệt sáng màu vàng nhảy ngược lại đầu một sub một cách bất ngờ, bạn đã tìm thấy một vòng lặp event.- **Theo dõi Immediate Window:** Thêm dòng `Debug.Print "Calling Sub at " & Now`. Nếu Immediate Window (Ctrl+G) của bạn hiện ra hàng trăm dòng trong một giây, thì recursion của bạn đang mất kiểm soát.- **Stress Test:** Nếu bạn sử dụng recursion, hãy kiểm tra nó với một giá trị cao (như 500) để xem stack có thể chịu được độ sâu đó mà không bị sập hay không.## Các phương pháp hay nhất để phòng ngừa- **Xử lý lỗi chặt chẽ:** Nếu mã của bạn bị sập trong khi `EnableEvents` đang ở trạng thái False, Excel sẽ tiếp tục "phớt lờ" mọi hành động của người dùng. Luôn luôn sử dụng nhãn `CleanExit` để kích hoạt lại các event.- **Kiểm soát độ sâu:** Nếu logic của bạn yêu cầu lồng nhau sâu hơn 20 hoặc 30 cấp độ, đã đến lúc bạn nên xem xét lại cấu trúc của mình.- **Giới hạn phạm vi biến:** Giữ cho stack gọn nhẹ bằng cách chỉ khai báo những gì bạn thực sự cần bên trong các procedure của mình.

Related Error Notes