Cùng đi tìm lỗi sai-vui chơi có thưởng lớn

tuhocvba

Administrator
Thành viên BQT
Nào, mời các bạn tham gia:
Vấn đề 1: (Hạn trả lời cho đến hết ngày 22/9/2019)
Mã:
Sub File_Open()
  ' Ban hay thiet dinh duong link file
  Const lk As String = "C:\VBA\tuhocvba.xlsx"
  If Dir(lk) = "" Then
    MsgBox "Khong tim thay file", vbCritical
    Exit Sub
  End If

  With Workbooks.Open(lk)
    With Worksheets.Add
        Range("A1").Value = Date
    
    End With
  End With
End Sub
 

ducdoom

Thành viên mới
Xin chào!
Mình có thử chạy code trên và không thấy có lỗi gì.
Vậy xin cho hỏi lỗi mà chúng ta bàn luận ở đây là lỗi cú pháp hay là lỗi logic nhỉ?
 

bvtvba

Thành viên mới
Xin chào!
Mình có thử chạy code trên và không thấy có lỗi gì.
Vậy xin cho hỏi lỗi mà chúng ta bàn luận ở đây là lỗi cú pháp hay là lỗi logic nhỉ?
File muốn mở nhưng nếu có file trùng tên đang được mở, theo mình biết là không mở được và sẽ sinh ra lỗi.
File muốn mở mà bị đặt password cũng không mở được. Có cách nào kiểm tra và thông báo file đang bị đặt pass.
...
Mình mới nghĩ được đến đây.
 

ducdoom

Thành viên mới
Xin trả lời: thiếu 2 dấu chấm ( . ) của 2 câu With End With
bổ sung như sau:
With Workbooks.Open(lk)
With .Worksheets.Add
.Range("A1").Value = Date
End With
End With
 

tuhocvba

Administrator
Thành viên BQT
Mọi người nghĩ đi, và cụ thể bằng code mới được coi là giải quyết xong. Sẽ chấm điểm và trao thưởng.
Ở VN không có sách nào dạy về thiết kế VBA, nên diễn đàn muốn mọi người làm quen với việc này.
 

ducdoom

Thành viên mới
Code của mình tập trung vào xử lý việc mở file Excel có mật khẩu (bỏ qua các tùy chọn khác như UpdateLinks hay Readonly)
Test trên Excel 2019 - 64bit - Windows 10
Với phương thức Workbooks.Open trên Excel 2019 của mình, nếu file cần mở đang mở sẵn thì Excel chỉ đơn giản là Activate nó lên thôi.
Với các bản excel trước mình đã từng gặp thông báo file excel đang mở và sẽ phải Re-open file đó (vấn đề này mình xin bỏ qua bởi vì nếu có thì người dùng cũng sẽ chỉ cần bấm OK mà thôi)


Mã:
Sub open_file()
    '//Excel 2019 - 64bit
    
    Dim filePath As String
    filePath = "C:\Users\ducdo\Desktop\Book1122.xlsx"
    
    '// 1. luon luon kiem tra su ton tai cua file
    Dim wb As Workbook
    If Dir(filePath) = "" Then
        MsgBox "Khong tim thay file", vbCritical
        Exit Sub
    End If

    '// 2. open
    ' su dung On Error Resume Next de tranh loi 1004 "open method workbook failed"
    ' khi nguoi dung nhap sai mat khau hoac bam Cancel
    On Error Resume Next
    Set wb = Workbooks.Open(Filename:=filePath)
    If err.Number = 1004 Then
        MsgBox "Sai mat khau hoac ban da chon Cancel"
        Exit Sub
    End If
    On Error GoTo 0
    
    '// 3. lam viec voi file
    Dim newSheet As Worksheet
    Set newSheet = wb.Worksheets.Add
    newSheet.Range("A1").Value = Date

End Sub
 

thanhphong

Thành viên mới
Hàm DIR này không dùng được nếu link file có chứa tên có dấu tiếng việt.
Nên mình dùng FSO.
Mã:
Sub File_Open2()
  ' Ban hay thiet dinh duong link file
  Const lk As String = "D:\vba\Sample001.xlsm"
' Dir() khong kiem tra duoc link file co ten viet bang tieng viet co dau. Vi du folder name la tieng viet co dau,
'thi no khong check duoc. Vi vay o day se dung FSO de kiem tra.
'==================================================================
'COMMENTOUT========================================================
'  If Dir(lk) = "" Then
'    MsgBox "Khong tim thay file", vbCritical
'    Exit Sub
'  End If
'==================================================================
'==================================================================
    If checkfile(lk) = False Then
        MsgBox "Khong tim thay file", vbCritical
        Exit Sub
    End If
  On Error Resume Next
    Workbooks.Open (lk)
    If Err.Number = 1004 Then 'Loi nay la khong mo duoc file. Khong mo duoc file thi co rat nhieu ly do
        MsgBox "File khong duoc mo, nen se khong ghi duoc du lieu vao file."
        Exit Sub
    End If
  On Error GoTo 0
    Worksheets.Add
        Range("A1").Value = "tuhocvba.net 20/29/2019"
    
 
End Sub
Function checkfile(ByVal Target As String) As Boolean
    ''https://tuhocvba.net/threads/filesystemobject.50/post-542
    Dim FSO As Object
    Set FSO = CreateObject("Scripting.FileSystemObject")
    'Target = "C:\Work\Sample.txt"
    If FSO.FileExists(Target) Then
        checkfile = True
    Else
        checkfile = False
    End If
    Set FSO = Nothing
End Function
 

tuhocvba

Administrator
Thành viên BQT
Cám ơn các bạn đã tham gia giải đáp. Hạn là hết ngày 22/9, tuy nhiên hiện tại, các bạn đều đã công bố đáp án công khai, vì vậy mình nghĩ không còn lý do gì để chờ đợi thêm.

Trước hết, đáp án là:
Mã:
On Error Resume Next
  Workbooks.Open (csFileName) 'O day co kha nang sinh ra loi
On Error GoTo 0
Ở trên mình đã trích nguyên văn đáp án. Trong , các bạn đều đưa ra các phương án tốt hơn đáp án.
Đặc biệt là @thanhphong đã phát hiện ra Dir không làm việc với link tiếng việt. Bản thân mình cũng đã kiểm tra và thấy đúng như vậy.
Phương pháp kiểm tra:

Trên cells(1,1) mình nhập đường link:
D:\VBA\ba\bà\Sample001.xlsm
Mã:
  Dim lk As String
  lk = Cells(1, 1)

  If Dir(lk) = "" Then
    MsgBox "Khong tim thay file", vbCritical
    Exit Sub
  End If
Và đúng là đã hiển thị thông báo "Khong tim thay file", mặc dù thực tế là file có tồn tại. Giải pháp bạn sử dụng là FSO là đúng.
Đây là việc bạn đã làm tốt hơn đáp án.

Về việc phải sử dụng On Error Resume Next (phớt lờ lỗi) và sau đó hồi phục lại bằng On Error GoTo 0 (Trả về trạng thái ban đầu, lệnh phớt lờ lỗi bây giờ hết giá trị).

@ducdoom đã sáng tạo khi dùng Err.Number để đưa ra cảnh báo tới người dùng. Đây là việc mà bạn đã làm tốt hơn đáp án.

Kết quả vòng đua thứ nhất:
@bvtvba :50/100 (Chỉ đưa ra được ý tưởng, chưa đưa ra được code).
@ducdoom : 70/100 (Đã đưa ra được code. Chưa chỉ ra được mặt hạn chế của DIR)
@thanhphong : 80/100 (Đã đưa ra được code. Chỉ ra được mặt hạn chế của DIR. Không cho điểm tối đa, vì code bạn đưa ra sau khi đã có người đưa ra code và ý tưởng).

Kết quả này là căn cứ để tính điểm thành tích cho lần tổng kết điểm sau cùng, khi topic này kết thúc.
 

giaiphapvba

Administrator
Thành viên BQT
Nào, chúng ta sang vấn đề mới.
Vấn đề 2: (Hạn trả lời cho đến hết ngày 27/9/2019. Tuy nhiên có thể kết thúc sớm hơn khi có người đưa ra đáp án đúng.)
Mã:
Sub AllSelectSheets()
  Dim sh As Worksheet
  For Each sh In Worksheets
    sh.Select False
  Next
End Sub
______________________________
Các bạn tham khảo các vấn đề trước đây:
Vấn đề 1:
.
.
 

ducdoom

Thành viên mới
Vấn đề 2: (Hạn trả lời cho đến hết ngày 27/9/2019. Tuy nhiên có thể kết thúc sớm hơn khi có người đưa ra đáp án đúng.)
Mã:
Sub AllSelectSheets()
  Dim sh As Worksheet
  For Each sh In Worksheets
    sh.Select False
  Next
End Sub
Sub này mình có thấy gì sai đâu nhỉ?
 

bvtvba

Thành viên mới
Nếu sheet bị ẩn thì không select được và bị lỗi. Nên code có thể sửa là:
Mã:
Sub AllSelectSheets()
  Dim sh As Object

  For Each sh In Sheets
    If sh.Visible = xlSheetVisible Then 'Nếu sheet không bị ẩn thì select
      sh.Select False
    End If
  Next
End Sub
 

giaiphapvba

Administrator
Thành viên BQT
Các bạn chú ý: Đề do diễn đàn đưa lên thì chắc chắn là có lỗi cần khắc phục cho hoàn thiện hơn. Nếu chưa tìm thấy lỗi thì tiếp tục tìm kiếm. Không post bài thắc mắc nhé.
_____
Nhanh quá, bạn @bvtvba đã trả lời đúng. Nếu có sheet bị ẩn thì lệnh select không thực thi được và sẽ bị lỗi, vì vậy code bạn đưa ra là đúng. Bạn trả lời nhanh nhất và chính xác, cho nên xứng đáng nhận điểm tối đa 100 điểm.
Chúng ta có chú ý lệnh select sheet như sau:
Mã:
sh.select true
thì nó chỉ select sheet hiện tại, còn trước đó như thế nào là nó sẽ bỏ qua.
Còn với:
Mã:
sh.select false
thì trước đó select như thế nào vẫn giữ nguyên, và bây giờ select thêm sheet hiện tại nữa. Tương đương với việc bạn ấn phím shift và select nhiều sheet.
Nguồn tham khảo:
Thành tích sau hai vòng đua:
Nick nameLần 1Lần 2Tổng
bvtvba
50​
100​
150​
ducdoom
70​
0​
70​
thanhphong
80​
0​
80​
 

tuhocvba

Administrator
Thành viên BQT
Nào, chúng ta sang vấn đề mới.
Vấn đề 3: (Hạn trả lời cho đến hết ngày 27/9/2019. Tuy nhiên có thể kết thúc sớm hơn khi có người đưa ra đáp án đúng.)
Xin chú ý, lần này không phải là lỗi sai, mà mong muốn các bạn dự đoán kết quả và từ đó đưa ra các bình luận. Do đó, có thể nói, đề lần này khó hơn hai lần trước. Các bạn hãy cân nhắc trước khi đưa ra bình luận, mỗi thành viên chỉ được đưa ra bình luận một lần duy nhất.

Hãy chú ý vào cách sử dụng Hàm Log và Hàm Round ở đoạn code dưới đây rồi từ đó đưa ra bình luận.
Mã:
Sub LogAndRound()
  Dim f As Double

  f = 2.5
  Debug.Print Log(f) & " --> Log(" & f & ")"
  Debug.Print Application.WorksheetFunction.Log(f) & " --> Application.WorksheetFunction.Log(" & f & ")"

  Debug.Print ""
  Debug.Print Round(f) & " --> Round(" & f & ")" ' ● Dap so la gi?
  Debug.Print Application.WorksheetFunction.Round(f, 0) & " --> Application.WorksheetFunction.Round(" & f & ", 0)"

  Debug.Print ""
  Debug.Print "f     Round  Application.WorksheetFunction.Round(f, 0)"
  Debug.Print Application.WorksheetFunction.Rept("-", 54)

  f = 2.1
  Do Until f = 2.6
    Debug.Print f & "     " & Round(f) & "      " & Application.WorksheetFunction.Round(f, 0)
    f = f + 0.1
    If f > 3 Then Exit Do
  Loop
  ' *Tham khảo: Trong cửa sổ soạn thảo code VBE, để hiển thị cửa sổ Immediate, bạn hãy dùng phím tắt Ctrl + G
End Sub
________
Các bạn tham khảo các vấn đề trước đây:
Vấn đề 1:
.
.
Vấn đề 2: .
.
 

giaiphapvba

Administrator
Thành viên BQT
Đã hết hạn trả lời vấn đề 3. Và không ai có câu trả lời. Sau đây là đáp án.
Đầu tiên, kết quả chạy chương trình trên (trước khi chạy thì ấn Ctr+G để hiện cửa sổ Immediate theo dõi kết quả nhé).
Mã:
0.916290731874155 --> Log(2.5)
0.397940008672038 --> Application.WorksheetFunction.Log(2.5)

2 --> Round(2.5)
3 --> Application.WorksheetFunction.Round(2.5, 0)

f     Round  Application.WorksheetFunction.Round(f, 0)
------------------------------------------------------
2.1     2      2
2.2     2      2
2.3     2      2
2.4     2      2
2.5     3      3
2.6     3      3
2.7     3      3
2.8     3      3
2.9     3      3
Vấn đề 1: Tên hàm giống nhau nhưng kết quả khác nhau.
Ở dòng số 1 và số 2, ta thấy kết quả của hai hàm Log cho kết quả khác nhau.
Kết luận 1: Log và Application.WorksheetFunction.Log là khác nhau. Một bên là hàm của VBA và một bên là hàm của Worksheet.
Ở dòng số 4 và 5, ta cũng thấy hai kết quả làm tròn khác nhau. Cùng là 2.5 nhưng Round cho kết quả là 2. Còn Application.WorksheetFunction.Round cho kết quả là 3.
Kết luận 2: Vậy muốn làm tròn, hãy dùng hàm của worksheet.
Kết luận tổng 1: Hàm của Excel Worksheet và hàm của VBA tuy trùng tên nhưng tính năng khác nhau cũng có thể xảy ra. Ví dụ hàm Replace.

Vấn đề 2: Do Until f = 2.6
Nói là chạy tới f=2.6 nhưng kết quả đã cho thấy nó chạy tới f = 2.9
f ở đây khai báo là double, cho nên đã phát sinh sai số, thực tế nó không chạy tới f = 2.6, không đúng ý đồ người code. Hãy chú ý điều này.
 

tuhocvba

Administrator
Thành viên BQT
Bài lần trước khó quá, nên lần này sẽ giảm mức độ khó xuống, chúng ta lại đi tìm lỗi sai trong thiết kế nhé.
Vấn đề 4: Hạn trả lời đến hết ngày 11/10/2019. Trường hợp đã có người trả lời đúng thì có thể kết thúc sớm hơn.
Chương trình sau cần cải thiện ra sao?
Mã:
Sub Hello()
  Dim Name As String

  Name = Application.InputBox(prompt:="Ho Ten:", Title:="Nhap thong tin", Default:="Nguyen Van A", Type:=2)  'Type:=2 la kieu ky tu Text
  If VarType(Name) = vbBoolean Then Exit Sub      ' An nut Cancel nen thoat chuong trinh
  If Name = "" Then Name = "Nguyen Van A"
  MsgBox prompt:="Xin chao " & Name & " , ban khoe khong?", Buttons:=vbInformation, Title:="tuhocvba.net"
End Sub
_____
Các bạn tham khảo các vấn đề trước đây:
Vấn đề 1:
.
.
Vấn đề 2: .
.
Vấn đề 3:
.
 

ducdoom

Thành viên mới
If VarType(Name) = vbBoolean Then Exit Sub
Câu lệnh này sẽ không thoát chương trình dù cho người dùng có bấm Cancel
Khi người dùng bấm Cancel thì biến Name sẽ trả về giá trị là False
nên ta sẽ sửa thành If Name = False Then Exit Sub
Mã:
Sub hello1()

  Dim Name As String
  Name = Application.InputBox(prompt:="Ho Ten:", Title:="Nhap thong tin", Default:="Nguyen Van A", Type:=2)  'Type:=2 la kieu ky tu Text
  If Name = False Then Exit Sub     ' An nut Cancel nen thoat chuong trinh
  If Name = "" Then Name = "Nguyen Van A"
  MsgBox prompt:="Xin chao " & Name & " , ban khoe khong?", Buttons:=vbInformation, Title:="tuhocvba.net"

End Sub
 

thanhphong

Thành viên mới
Lỗi: ấn cancel nhưng chương trình vẫn tiếp tục, không kết thúc.
Name khai báo là string, cho nên với phương thức Application.InputBox , khi ấn Cancel thì giá trị trả về sẽ là "False".
Cách khắc phục thứ nhất, sửa khai báo Name là String => Variant.
Mã:
Sub Hello()
  Dim Name As Variant

  Name = Application.InputBox(prompt:="Ho Ten:", Title:="Nhap thong tin", Default:="Nguyen Van A", Type:=2)  'Type:=2 la kieu ky tu Text
  If VarType(Name) = vbBoolean Then Exit Sub      ' An nut Cancel nen thoat chuong trinh
  If Name = "" Then Name = "Nguyen Van A"
  MsgBox prompt:="Xin chao " & Name & " , ban khoe khong?", Buttons:=vbInformation, Title:="tuhocvba.net"
End Sub
Trường hợp nếu vẫn giữ khai báo là String, thì ta sẽ dùng hàm InputBox , ở giai đoạn kiểm tra, để phán đoán người dùng ấn nút Cancel hay không, ta sẽ dùng hàm StrPtr.
Mã:
Sub Hello()
  Dim Name As String

  Name = InputBox(prompt:="Ho Ten:", Title:="Nhap thong tin", Default:="Nguyen Van A")  'Type:=2 la kieu ky tu Text
  If StrPtr(Name) = 0 Then Exit Sub      ' An nut Cancel nen thoat chuong trinh
  If Name = "" Then Name = "Nguyen Van A"
  MsgBox prompt:="Xin chao " & Name & " , ban khoe khong?", Buttons:=vbInformation, Title:="tuhocvba.net"
End Sub
Cách giải quyết của @ducdoom sẽ không giải quyết được tình huống: Người dùng nhập tên là "False" thì chương trình sẽ bị kết thúc mà không ra hộp thoại chào False. Ở chương trình này, người ta chỉ muốn can thiệp khi ấn nút Cancel thì phải hủy, còn nhập tên là "False" thì vẫn phải chạy bình thường, mình hiểu thế.
 
Top