Găp vấn đề Frame trong VBA

Liên hệ QC
Khi ta unload userform rồi, mà đột ngột ta gọi userform.show thì form lại tự động sống lại
Nếu bạn lấy ví dụ khác thì còn có lý, vì Show là hiển nhiên. Tại sao? Nếu UserForm1 đang chưa được Load mà không thể gọi Show để Load nó và Show nó thì bạn làm sao dùng được UserForm1? Bởi khi mở tập tin thì UserForm1 chưa được Load mà. Vậy bạn làm cách nào hiển thị UserForm1 để làm việc với nó? Thời điểm khi mở tập tin và thời điểm khi UserForm1 được hiển thị và sau đó Unload là hoàn toàn như nhau.

Nhiệm vụ của Show là Load UserForm1 nếu chưa được Load, rồi Show mà. Vậy thì tại sao lại ngạc nhiên khi có thể dùng Show để Load và Show UserForm1?
Vậy cho em hỏi hai cái cơ chế này có liên quan gì tới nhau không?
Tôi cũng không biết có cùng cơ chế hay không. Nếu bạn kiểm tra thấy đúng như thế thì bạn tự kết luận cho mình thôi.
 
"Nhiệm vụ của Show là Load UserForm1 nếu chưa được Load, rồi Show mà."


Nhưng phải thừa nhận một điều là show là một phương thức của userform1. Có nghĩa là phải có đối tượng, form phải được load thì mới gọi phương thức được. Em nghĩ show không hề load form mà do một bàn tay khác thực hiện, show chỉ làm nhiệm vụ hiển thị form. nothing-unload, new-userform, truy cập một thành viên gây ra quá trình tạo đối tượng. đó là mmotj vài từ khóa làm em liên tưởng chúng với nhau.
 
Em chỉ biết nói nôm na, dân dã vầy:
Trong VB cũ, VB.NET, thì form phải load và show, unload, set nothing khi không cần (referent count = 0 => destroy tường minh trong VB và destroy = cơ chế dọn rác của .NET framework)
Còn trong MSForms của VBA thì ông VBA lanh chanh, làm dùm luôn, luôn set referent count to object = 1 khi khởi tạo, tham chiếu, read properties... lần đầu tiên.
Bắt ổng unload form, set nothing thì ông lại set ref count =1, không chịu set = 0 nên không giải phóng được.
Các object khai báo, khởi tạo bằng Dim xxx as New XXX cũng theo cách này.
Các bạn thử kiểm chứng = cách debug với 1 userform
Code của form
Mã:
Option Explicit

Private Sub UserForm_Initialize()
    Debug.Print Me.Name & " initialize"
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    Debug.Print Me.Name & " will be closed with Cancel = " & Str$(Cancel) & " and CloseMode = " & Str$(CloseMode)
End Sub

Private Sub UserForm_Terminate()
    Debug.Print Me.Name & " terminate"
End Sub
Code của macro:
Mã:
Option Explicit

Sub TestForm()
    Debug.Print "UserForm1 at address: 0x" & Hex$(VarPtr(UserForm1))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
 
    UserForm1.Show

    Debug.Print "UserForm1 at address: 0x" & Hex$(VarPtr(UserForm1))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
 
    Unload UserForm1
    Set UserForm1 = Nothing
 
    Debug.Print "UserForm1 at address: 0x" & Hex$(VarPtr(UserForm1))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
End Sub
 
Lần chỉnh sửa cuối:
Có nghĩa là phải có đối tượng, form phải được load thì mới gọi phương thức được. Em nghĩ show không hề load form mà do một bàn tay khác thực hiện, show chỉ làm nhiệm vụ hiển thị form. nothing-unload, new-userform,
Nói chính xác thì khi Show được gọi mà UserForm chưa được Load thì VB sẽ Load nó. Cũng hiển nhiên thôi. Vì nếu không UserForm.Show được khi UserForm chưa được Load thì làm sao dùng UserForm được.

Ý 2 của tôi là những gì tự kiểm tra được thì cứ tự kiểm nghiệm, không cần phải hỏi làm gì.
 
Lần chỉnh sửa cuối:
Bắt ổng unload form, set nothing thì ông lại set ref count =1, không chịu set = 0 nên không giải phóng được.
Chắc chắn là giải phóng. Nhưng do sau đó lại truy cập tới UserForm1 nên VB lại tạo nó.
Dễ kiểm tra thôi.
Đặt Button1 trên Sheet1
Mã:
Sub Button1_Click()
    UserForm1.Show
End Sub
Thêm UserForm1 với 3 CommandButton
Mã:
Private Sub CommandButton1_Click()
    Unload Me
    UserForm1.Show
End Sub

Private Sub CommandButton2_Click()
    Me.Hide
End Sub

Private Sub CommandButton3_Click()
    Unload Me
    UserForm1.Width = 500
End Sub

Private Sub UserForm_Initialize()
    MsgBox "Initialize"
End Sub

Private Sub UserForm_Terminate()
    MsgBox "Terminate"
End Sub
1. Nhấn Button1. Ta thấy code UserForm_Initialize được thực hiện, tức VB tạo đối tượng. Tiếp theo nhấn CommandButton1. Ta thấy UserForm_Terminate được thực hiện (thủ phạm là Unload), tức đối tượng được giải phóng. Tiếp ngay theo UserForm_Initialize được thực hiện, tức VB lại tạo đối tượng - thủ phạm là gọi Show mà UserForm1 chưa được Load vào memory nên VB lại tạo đối tượng. Rõ ràng đối tượng thực sự được giải phóng. Nhưng được tạo lại do gọi Show. Nếu sau khi giải phóng mà không gọi Show thì đối tượng không được tạo. Bằng chứng là nếu bỏ UserForm1.Show thì sau khi nhấn Button1 thì UserForm_Initialize được thực hiện, tức chỉ tới thời điểm này UserForm1 mới được Load vào memory, đây là bằng chứng là sau Unload và trước khi nhấn Button1 thì trong memory không có UserForm1.
2. Nhấn Button1. Ta thấy code UserForm_Initialize được thực hiện, tức VB tạo đối tượng. Tiếp theo nhấn CommandButton2. Ta thấy UserForm_Terminate không được thực hiện, bởi UserForm1 chỉ bị ẩn nhưng vẫn tồn tại trong memory, không được giải phóng. Chính vì nó vẫn tồn tại nên nếu lúc này nhấn Button1 thì VB không tạo, ta thấy rõ ràng là UserForm_Initialize không được thực hiện. Show lúc này chẳng qua là hiển thị UserForm1 đang bị ẩn nhưng luôn tồn tại trong memory.
3. Nhấn Button1. Ta thấy code UserForm_Initialize được thực hiện, tức VB tạo đối tượng. Tiếp theo nhấn CommandButton3. Ta thấy UserForm_Terminate được thực hiện (thủ phạm là Unload), tức đối tượng được giải phóng. Tiếp ngay theo UserForm_Initialize được thực hiện, tức VB lại tạo đối tượng - thủ phạm là do truy cập tới UserForm1 (UserForm1.Width = 300). Nhưng VB chỉ tạo đối tượng mà không hiển thị nó. Nếu sau đó nhấn Button1 thì thấy đúng là chiều rộng của UserForm1 đã thay đổi.
 
Em chỉ biết nói nôm na, dân dã vầy:
Trong VB cũ, VB.NET, thì form phải load và show, unload, set nothing khi không cần (referent count = 0 => destroy tường minh trong VB và destroy = cơ chế dọn rác của .NET framework)
Còn trong MSForms của VBA thì ông VBA lanh chanh, làm dùm luôn, luôn set referent count to object = 1 khi khởi tạo, tham chiếu, read properties... lần đầu tiên.
Bắt ổng unload form, set nothing thì ông lại set ref count =1, không chịu set = 0 nên không giải phóng được.
Các object khai báo, khởi tạo bằng Dim xxx as New XXX cũng theo cách này.
Các bạn thử kiểm chứng = cách debug với 1 userform
Code của form
Mã:
Option Explicit

Private Sub UserForm_Initialize()
    Debug.Print Me.Name & " initialize"
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    Debug.Print Me.Name & " will be closed with Cancel = " & Str$(Cancel) & " and CloseMode = " & Str$(CloseMode)
End Sub

Private Sub UserForm_Terminate()
    Debug.Print Me.Name & " terminate"
End Sub
Code của macro:
Mã:
Option Explicit

Sub TestForm()
    Debug.Print "UserForm1 at address: 0x" & Hex$(VarPtr(UserForm1))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
 
    UserForm1.Show

    Debug.Print "UserForm1 at address: 0x" & Hex$(VarPtr(UserForm1))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
 
    Unload UserForm1
    Set UserForm1 = Nothing
 
    Debug.Print "UserForm1 at address: 0x" & Hex$(VarPtr(UserForm1))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
End Sub


Chạy code trên thấy các địa chỉ giống nhau. Tuy nhiên sau khi chạy một cái code sau thì em lại bắt đầu nghi ngờ.


Mã:
Sub TestClass()
    Dim UF As UserForm1
    Set UF = UserForm1
    Unload UserForm1
    UserForm1.Caption = "Meo mun dang yeu"
    MsgBox UF.Caption
End Sub

Nếu địa chỉ không đổi, form không được giải phóng, thì biến UF cũng không thay đổi gì cả. Nhưng khi chạy cái msgbox lại bị lỗi, phải chăng chỉ là sự trùng hợp khi cấp phát bộ nhớ.

VÀ khi chạy thêm code sau thì càng hỏa mù luồn ( để cho đơn giản thì xóa hết code trong userform đi)

Mã:
Sub TestForm()
    Dim k As UserForm1, a As UserForm1
  
    Unload UserForm1
    Set k = UserForm1
    Debug.Print "Dia chi cua k la: " & Hex(ObjPtr(k))
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
  
    Unload UserForm1
    Set a = UserForm1
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
  
    Unload UserForm1
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
    Unload UserForm1
    Debug.Print "UserForm1 pointer to address: 0x" & Hex$(ObjPtr(UserForm1))
  
End Sub

Địa chỉ thay đổi tùm lum hết-->Địa chỉ không thay đổi chỉ là do một nguyên nhân đặc biệt nào đó mà thôi.
 
Lần chỉnh sửa cuối:
Chắc chắn là giải phóng. Nhưng do sau đó lại truy cập tới UserForm1 nên VB lại tạo nó.
Dễ kiểm tra thôi.
Đặt Button1 trên Sheet1
Mã:
Sub Button1_Click()
    UserForm1.Show
End Sub
Thêm UserForm1 với 3 CommandButton
Mã:
Private Sub CommandButton1_Click()
    Unload Me
    UserForm1.Show
End Sub

Private Sub CommandButton2_Click()
    Me.Hide
End Sub

Private Sub CommandButton3_Click()
    Unload Me
    UserForm1.Width = 500
End Sub

Private Sub UserForm_Initialize()
    MsgBox "Initialize"
End Sub

Private Sub UserForm_Terminate()
    MsgBox "Terminate"
End Sub
1. Nhấn Button1. Ta thấy code UserForm_Initialize được thực hiện, tức VB tạo đối tượng. Tiếp theo nhấn CommandButton1. Ta thấy UserForm_Terminate được thực hiện (thủ phạm là Unload), tức đối tượng được giải phóng. Tiếp ngay theo UserForm_Initialize được thực hiện, tức VB lại tạo đối tượng - thủ phạm là gọi Show mà UserForm1 chưa được Load vào memory nên VB lại tạo đối tượng. Rõ ràng đối tượng thực sự được giải phóng. Nhưng được tạo lại do gọi Show. Nếu sau khi giải phóng mà không gọi Show thì đối tượng không được tạo. Bằng chứng là nếu bỏ UserForm1.Show thì sau khi nhấn Button1 thì UserForm_Initialize được thực hiện, tức chỉ tới thời điểm này UserForm1 mới được Load vào memory, đây là bằng chứng là sau Unload và trước khi nhấn Button1 thì trong memory không có UserForm1.
2. Nhấn Button1. Ta thấy code UserForm_Initialize được thực hiện, tức VB tạo đối tượng. Tiếp theo nhấn CommandButton2. Ta thấy UserForm_Terminate không được thực hiện, bởi UserForm1 chỉ bị ẩn nhưng vẫn tồn tại trong memory, không được giải phóng. Chính vì nó vẫn tồn tại nên nếu lúc này nhấn Button1 thì VB không tạo, ta thấy rõ ràng là UserForm_Initialize không được thực hiện. Show lúc này chẳng qua là hiển thị UserForm1 đang bị ẩn nhưng luôn tồn tại trong memory.
3. Nhấn Button1. Ta thấy code UserForm_Initialize được thực hiện, tức VB tạo đối tượng. Tiếp theo nhấn CommandButton3. Ta thấy UserForm_Terminate được thực hiện (thủ phạm là Unload), tức đối tượng được giải phóng. Tiếp ngay theo UserForm_Initialize được thực hiện, tức VB lại tạo đối tượng - thủ phạm là do truy cập tới UserForm1 (UserForm1.Width = 300). Nhưng VB chỉ tạo đối tượng mà không hiển thị nó. Nếu sau đó nhấn Button1 thì thấy đúng là chiều rộng của UserForm1 đã thay đổi.
Giải phóng thì chắc chắn là đúng rồi anh. Chỉ là tại sao khi kiểm tra địa chỉ lại có lúc trùng, có lúc không trùng.
 
Chỉ là tại sao khi kiểm tra địa chỉ lại có lúc trùng, có lúc không trùng.
Không biết bạn kiểm tra gì, thế nào. Chỉ một câu mà không có code thì khó nói chuyện.

Đối tượng được tạo lần sau có thể nằm cùng chỗ với lần trước mà cũng có thể nằm chỗ khác. Nhất là được tạo lại sau một thời gian dài. Vì chỗ trống của lần trước trong khoảng thời gian qua đã được Manager Memory phân cho những dữ liệu khác, đối tượng khác. Không ai quan tâm tới vị trí "vật lý" trong memory, vì có thể mỗi lần là chỗ khác, không phải bất di bất dịch. Quan trọng là khi cần truy cập tới "chỗ ấy" thì biết phải dùng hàm gì để đọc ra vd. địa chỉ hiện thời.
 
Lần chỉnh sửa cuối:
Web KT
Back
Top Bottom