Chuyên đề giải đáp những thắc mắc về code VBA

Liên hệ QC

maytinhvp01

Thành viên thường trực
Tham gia
27/7/13
Bài viết
390
Được thích
179
Mình muốn nhờ giải thich câu lệnh " If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c) "
trong ví du:
Public Function LonNhat(Ran As Range)
Dim max As Double, v As Integer, d As Integer, c As Integer
max = Ran.Cells(1, 1)
For d = 1 To Ran.Rows.Count
For c = 1 To Ran.Columns.Count
If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c)
Next c
Next d
v = Tim(max, Ran)
LonNhat = max
End Function
-------------------------------------------------------
[INFO1]Thông báo:
Vì topic này:
http://www.giaiphapexcel.com/forum/...ải-thích-các-code-đề-nghị-các-bạn-gửi-vào-đây
đã quá dài nên BQT đóng lại.
Nay tôi mở topic mới với cùng chủ đề: GIẢI THÍCH NHỮNG THẮC MẮC VỀ CODE
Các bạn nếu có nhu cầu giải thích code, vui lòng post tại đây nhé
NDU96081631

[/INFO1]
 
Chỉnh sửa lần cuối bởi điều hành viên:
Chào mn,

Mình cũng biết mò qua chút VBA để làm việc cho tiện.
Mn cho hỏi khi mình sử dụng mảng giá trị lớn Vd:Arr(1000000,5) , thì khi chạy code lần thứ 2 mà ko đống file sẽ bị đầy bộ nhớ. Mình google để khắc phục thì thấy nên dùng thêm đoạn dưới để giải phóng bộ nhớ: Application.CutCopyMode = False
Mình ko hiểu lắm chức năng đoạn này, và có cần để lại nó là True khi đóng code k.

Code của mình:
Mấy dòng này chứ gì?
Application.ScreenUpdating = True => Để cập nhật lại màn hình sau khi chạy macro, tuy nhiên không có dòng này tôi thấy cũng không sao vì hết macro thì Excel cũng tự cập nhật kết quả lên màn hình máy tính.
Application.EnableEvents = True => Bật lại các sự kiện bảng tính. Nếu ở đầu có dòng Application.EnableEvents = False thì cuối buộc phải có dòng này.
Application.StatusBar = "" => Ở trên đã đặt StatusBar = gì gì đó rồi thì ở cuối phải set cho nó về = "" để khỏi thấy mãi cái thông tin kia (dù đã chạy xong macro rồi)
Application.CutCopyMode = False => Nếu trong lệnh có dòng Range(XX:YY).Copy thì cuối code cần có dòng này để hủy trạng thái vùng được copy có cái khung chạy chạy xung quanh. Không có cũng không sao nhưng hết macro thấy chạy chạy chướng mắt.

Vậy thôi, cả 4 dòng đều không có tác dụng trong việc giải phóng bộ nhớ.
 
Upvote 0
Mấy dòng này chứ gì?
Application.ScreenUpdating = True => Để cập nhật lại màn hình sau khi chạy macro, tuy nhiên không có dòng này tôi thấy cũng không sao vì hết macro thì Excel cũng tự cập nhật kết quả lên màn hình máy tính.
Application.EnableEvents = True => Bật lại các sự kiện bảng tính. Nếu ở đầu có dòng Application.EnableEvents = False thì cuối buộc phải có dòng này.
Application.StatusBar = "" => Ở trên đã đặt StatusBar = gì gì đó rồi thì ở cuối phải set cho nó về = "" để khỏi thấy mãi cái thông tin kia (dù đã chạy xong macro rồi)
Application.CutCopyMode = False => Nếu trong lệnh có dòng Range(XX:YY).Copy thì cuối code cần có dòng này để hủy trạng thái vùng được copy có cái khung chạy chạy xung quanh. Không có cũng không sao nhưng hết macro thấy chạy chạy chướng mắt.

Vậy thôi, cả 4 dòng đều không có tác dụng trong việc giải phóng bộ nhớ.
Thank bác. Mấy cái trên thì mình biết nhưng dòng Application.CutCopyMode = False giờ mới gặp.
Cho mình hỏi vậy làm như nào để giải phóng bộ nhớ khi mình chạy code có mảng dữ liệu lớn 2 lần.
 
Upvote 0
Thank bác. Mấy cái trên thì mình biết nhưng dòng Application.CutCopyMode = False giờ mới gặp.
Cho mình hỏi vậy làm như nào để giải phóng bộ nhớ khi mình chạy code có mảng dữ liệu lớn 2 lần.
Mảng hay sheet, book đều tự hủy và giải phóng bộ nhớ sau khi xong macro nên không cần lệnh gì cả. Các đối tượng mà người ta thường hay set như Scripting.Dictionary hoặc FileSystemObject thì sẽ chiếm bộ nhớ nếu không giải phóng chúng bằng lệnh Set XXX = Nothing. Tuy nhiên bạn cũng nên Set XXX = Nothing cho tất cả các biến XXX nếu trước đó đã Set XXX = Gì đó.
 
Upvote 0
Dùng mảng to đùng như này mà không chủ động giải phỏng thì có lúc sẽ ăn Ram.
Dùng erase arr để giải phóng, mà khi khai báo là dim arr(10000000000,5) thì việc giải phóng có khi còn phản tác dụng

dim arr()
redim arr(1000000,5)

'code ahihihi ở đây

erase arr'giải phóng.



Ps:
Trong trường hợp này có khi dùng mảng động thì hợp lý hơn, không biết bác Vẹt với bác Rơi nghĩ sao vì cháu cũng chả hiểu rõ mấy cái cơ chế này.
Bài đã được tự động gộp:

Mảng hay sheet, book đều tự hủy và giải phóng bộ nhớ sau khi xong macro nên không cần lệnh gì cả
Trường hợp mảng nhỏ thì thằng VBa nó yêu thương chủ động giải phóng thì phải, mảng to quá nó không siêng như trước kia nữa thì phải,ahihi
 
Upvote 0
Ps:
Trong trường hợp này có khi dùng mảng động thì hợp lý hơn, không biết bác Vẹt với bác Rơi nghĩ sao vì cháu cũng chả hiểu rõ mấy cái cơ chế này.
Theo tôi ...

Với khai báo
Mã:
Dim ARR(999998, 12)
thì trong không gian địa chỉ của process sẽ có một vùng ~ 208 MB được "đặt" cho nhu cầu của mảng Arr. Giá trị của vùng này có thể thay đổi trong suốt quá trình thực hiện process, nhưng độ lớn của nó luôn không đổi cho tới khi kết thúc process.
Với
Mã:
Dim ARR
...
Redim ARR(1 To 999999, 1 To 13)
Thì biến ARR thực ra chỉ chứa địa chỉ của mảng ARR, tức đúng 4 bai trong system 32 bit. Khi chạy Sub thì mới có "đặt" một vùng trong memory ~ 208 MB, và địa chỉ của vùng đó được "ghi nhớ" trong biến ARR. Trước khi ra khỏi sub thì vùng được đặt sẽ được giải phóng.

Có thể tự "ép" giải phóng bộ nhớ - ERASE, nhưng:
- với Dim ARR(999998, 12) thì vùng ~ 208 MB trong memory được "xóa", tức nội dung của vùng đó, nhưng bản thân vùng ~ 208 MB vẫn luôn tồn tại trong memory.
- Với Dim ARR thì vùng được đặt cho ARR sẽ được giải phóng, 4 bai dành cho ARR để ghi nhớ vùng trong memory dành cho ARR sẽ được điền bằng những bai 0. Chỉ có vùng 4 bai là luôn luôn tồn tại suốt quá trình thực hiện process.

Cũng chỉ có lợi khi biến ARR là toàn cục - khi biến là toàn cục mà không dùng ERASE thì vùng chiếm bởi ARR sẽ tồn tại cho tới khi đóng tập tin. Vì thế nếu không dùng nữa thì trước khi ra khỏi sub nào đấy thì dùng ERASE để "ép" giải phóng bộ nhớ. Khi ARR là biến cục bộ thì trước khi ra khỏi Sub VBA sẽ tự giải phóng bộ nhớ.
 
Lần chỉnh sửa cuối:
Upvote 0
Cũng như chuỗi, mảng có hai dạng là dạng tĩnh và dạng động.

Dim a(1 To 100) as Integer là dạng khai báo tĩnh.
Khi đọc đến dòng này, trình dịch coi a là một mảng một chiều với 100 phần tử Integer. Và dạng này chết cứng luôn cho đến khi a không còn hiện hữu. Không thay đổi gì nữa cả. Trình dịch giành đủ chỗ chứa bấy nhiêu dữ liệu về mảng (mảng 1 chiều, chỉ số bắt đầu từ 1 đến 100, phần tử đầu tiên ở địa chỉ ***) và một dãy 200 bai để chứa các phần tử mảng (mỗi phần từ chiếm 2 bai). Lưu ý là bộ nhớ mà mảng dùng để chứa các phần tử phải liên tục, không được gián đoạn.
Nếu bạn dùng lệnh Erase a để xoá thì VBA chỉ xoá hết các trị của phần tử trong a thôi chứ a vẫn mãi mãi là một mảng Integer 100 phần tử, chiếm hữu bấy nhiêu trong bộ nhớ.
Chỉ khi a không còn hiện hữu, điển hình, exit/end Sub/Function khai báo nó thì trình dịch mới giải phóng bộ nhớ.

Dim a() as Integer là dạng khai báo động. Động ở đây nói về số phần tử và số chiều. Lưu ý rằng đoạn as integer xác định là mảng này chỉ chứa integers.
Khi đọc đến dòng này, trình dịch chỉ biết là bạn muốn một mảng ten a để chứa Integers. Nó chỉ giành riêng cho a của bạn cỡ một vài chục bai để chứa thông tin này.
Để có thẻ sử dụng a, bạn phải dùng một lệnh khác báo cho trình dịch cách giành bộ nhớ:
Redim a(1 To 10, 1 To 10) là lệnh xác định a.
Đọc đến dòng này, trình dịch sẽ giành thêm ra 200 bai để chứa 100 phần tử, và ghi vào chi tiết dữ liệu về a rằng: đây là một mảng 2 chiều, chiều 1 chỉ số 1 đến 10, chiều 2 chỉ số 1 đến 10, phần tử đầu tiên ở địa chỉ ***. Và vì đây là mảng, dù tĩnh hay động, trình dịch vẫn phải dùng một khoảng liên tục trong bộ nhớ cho dãy phần tử của a. Đó là lý do chính tại sao mảng lớn tốn nhiều bộ nhớ.
Mỗi lần bạn Redim (kể cả Redim Preserve) thì trình dịch lại tìm cho bạn một dãy bộ nhớ mới, và thảy bộ nhớ cũ trở vào cho ụ bộ nhớ (tức là giải phóng chỗ cũ này)
Khi bạn Erase a thì trình dịch chỉ phải giải phóng bộ nhớ cũ và không cần phải lấy mới cho a. Mảng a sẽ trở lại trạng thái giống như mới vừa được khai báo.

Như vậy, để làm nhẹ bộ nhớ, bạn có những lệnh căn bản sau:
- gán "" cho các biến string (nếu chúng dài qúa, ngắn thì chả bỏ công)
- Erase mảng động. Chỉ mảng động thôi, mảng tĩnh thì phải đợi ra khỏi tầm vực của biến.
- Set Nothing các Objects mà bạn dựng ra bằng lệnh CreateObject, hoặc lệnh New. Những đối tượng có sẵn của Excel luôn nằm đó, bạn chỉ ngưng con trỏ biến của mình vào chúng thôi. Việc set nothing này không chỉ quan trọng để giải phóng bộ nhớ mà còn giải phóng các tài nguyên khác tránh trường hợp bị khoá, bị rò bộ nhớ (ngày xưa ADO hay bị rò bộ nhớ nếu truy vấn trong cùng workbook cho nên người ta có thói quen set nothing ngay khi không còn dùng nữa, không đợi đến lúc end sub, biến ra khỏi tầm vực).
 
Upvote 0
Các anh chị cho mình hỏi với:
Mã:
Dim Str
Str = Format(Abs(Number), "000000000000000000")
ý nghĩa "000000000000000000" ở đây là gì vậy. Nhờ các anh chị giải thích giúp mình với
 
Upvote 0
Các anh chị cho mình hỏi với:
Mã:
Dim Str
Str = Format(Abs(Number), "000000000000000000")
ý nghĩa "000000000000000000" ở đây là gì vậy. Nhờ các anh chị giải thích giúp mình với
Định dạng số với bằng ấy con số. Thí dụ Format(1234, "0000000") sẽ thành "0001234"
 
Upvote 0
Em chào A/C,
Em đang muốn lấy giá trị của ô Em đang nhập dữ liệu dở trong ô (em vẫn chưa chuyển con trỏ sang ô khác) thì code sẽ lấy giá trị vào ô B1 theo giá trị em đang nhập dở trong ô hiện hành.
VD: chữ Việt nam. Nếu em đang nhập vào ô hiện hành được chữ Việt -> Thì kết quả ô B1 sẽ hiện lên chữ Việt
Hiện tại Em đang viết code:
Range("B1").Value = Cells(Target.Row, Target.Column)
Code này phải nhập xong trong ô và chuyển sang ô khác thì giá trị nó mới lấy lên ô B1. Mong A/C giúp Em. Cảm ơn A/C nhiều!
 
Upvote 0
Code này phải nhập xong trong ô và chuyển sang ô khác thì giá trị nó mới lấy lên ô B1. Mong A/C giúp Em. Cảm ơn A/C nhiều!
Bắt buộc phải vậy, Sheet Excel không có sự kiện changing, chỉ có sự kiện change. Textbox trong user form thì có
Với lại chỉ cần Range("B1").Value = Target.Value
 
Upvote 0
Gia đình mình cho em hỏi, Sự kiện Keydown trên Userform thì em biết làm, còn trên một ô thì mình viết thế nào anh chị nhỉ?
Ví dụ khi trỏ chuột tại ô A1, Em muốn khi bấm phím Enter (hoặc phím bất kỳ) thì nó sẽ chạy macro "Macro1".
Nhờ a chị chỉ giúp ạ!
 
Upvote 0
Gia đình mình cho em hỏi, Sự kiện Keydown trên Userform thì em biết làm, còn trên một ô thì mình viết thế nào anh chị nhỉ?
Ví dụ khi trỏ chuột tại ô A1, Em muốn khi bấm phím Enter (hoặc phím bất kỳ) thì nó sẽ chạy macro "Macro1".
Nhờ a chị chỉ giúp ạ!
Làm được, và code rất là dài.
 
Upvote 0
Gia đình mình cho em hỏi, Sự kiện Keydown trên Userform thì em biết làm, còn trên một ô thì mình viết thế nào anh chị nhỉ?
Ví dụ khi trỏ chuột tại ô A1, Em muốn khi bấm phím Enter (hoặc phím bất kỳ) thì nó sẽ chạy macro "Macro1".
Nhờ a chị chỉ giúp ạ!
Chỉ rồi thì có làm được hơm? Hay là anh chị viết luôn cho em chứ em "gà VBA lắm"
 
Upvote 0
Em chào A/C,
Em đang muốn xây dựng đoạn code cứ 15 giây thì code chạy sub DuLieu 1 lần (có nút bấm chạy và dừng).
Hiện tại Em mới mày mò ra được đoạn code nếu bấm vào thì sau 15 giây nó đã chạy. Nhưng sau đó nó chưa lặp lại được ạ. Đây là đoạn code của Em:
Sub CapNhatDuLieu()
Application.OnTime Now + TimeSerial(0, 0, 15), "DuLieu"
End Sub
Mong A/C giúp Em hoàn thiện tiếp đoạn code trên. Em cảm ơn A/C!
 
Upvote 0
hình như chưa điền đúng tên sub
Mã:
Sub CapNhatDuLieu()
Application.OnTime Now + TimeSerial(0, 0, 15), "CapNhatDuLieu"
End Sub
 
Upvote 0
Web KT
Back
Top Bottom