Hàm API getopenfilename !

Liên hệ QC

hungpecc1

Thành viên gắn bó
Tham gia
24/8/12
Bài viết
1,709
Được thích
2,304
Giới tính
Nam
Do một số ứng dụng khác không có hàm getopenfilename ---> em dùng hàm Api để tạo hộp thoại open file !

Tuy nhiên kết quả tên file lại khác với tên file khi sử dụng gàm getopenfilename của excel ( chênh nhau 1 ký tự)---> em tìm hiểu mãi mà chưa ra được nguyên nhân, mong các anh chị trên diễn đàn giải thích đoạn code của em sai chỗ nào !!

code của em như sau:
anh (chị) paste code sau vào 1 module --> chạy sub main, ??? không hiểu sao cùng 1 file mà strcomp lại ra kết quả -1
Mã:
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias _
                        "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
Private Type OPENFILENAME
    lStructSize As Long          'The size of this struct (Use the Len function)
    hwndOwner As Long            'The hWnd of the owner window. The dialog will be modal to this window
    hInstance As Long            'The instance of the calling thread. You can use the App.hInstance here.
    lpstrFilter As String        'Use this to filter what files are showen in the dialog. Separate each filter with Chr$(0). The string also has to end with a Chr(0).
    lpstrCustomFilter As String  'The pattern the user has choosed is saved here if you pass a non empty string. I never use this one
    nMaxCustFilter As Long       'The maximum saved custom filters. Since I never use the lpstrCustomFilter I always pass 0 to this.
    nFilterIndex As Long         'What filter (of lpstrFilter) is showed when the user opens the dialog.
    lpstrFile As String          'The path and name of the file the user has chosed. This must be at least MAX_PATH (260) character long.
    nMaxFile As Long             'The length of lpstrFile + 1
    lpstrFileTitle As String     'The name of the file. Should be MAX_PATH character long
    nMaxFileTitle As Long        'The length of lpstrFileTitle + 1
    lpstrInitialDir As String    'The path to the initial path :) If you pass an empty string the initial path is the current path.
    lpstrTitle As String         'The caption of the dialog.
    flags As Long                'Flags. See the values in MSDN Library (you can look at the flags property of the common dialog control)
    nFileOffset As Integer       'Points to the what character in lpstrFile where the actual filename begins (zero based)
    nFileExtension As Integer    'Same as nFileOffset except that it points to the file extention.
    lpstrDefExt As String        'Can contain the extention Windows should add to a file if the user doesn't provide one (used with the GetSaveFileName API function)
    lCustData As Long            'Only used if you provide a Hook procedure (Making a Hook procedure is pretty messy in VB.
    lpfnHook As Long             'Pointer to the hook procedure.
    lpTemplateName As String     'A string that contains a dialog template resource name. Only used with the hook procedure.
End Type
'=========================================================================    
Public Function ShowOpen(Filter As String, InitialDir As String, DialogTitle As String) As String
Dim OFName As OPENFILENAME
    OFName.lStructSize = Len(OFName)
    OFName.hwndOwner = 0
    OFName.lpstrFilter = Filter
    OFName.nMaxFile = 255
    OFName.lpstrFile = Space$(254)
    OFName.lpstrFileTitle = Space$(254)
    OFName.nMaxFileTitle = 255
    OFName.lpstrInitialDir = InitialDir
    OFName.lpstrTitle = DialogTitle
    OFName.flags = 0
    If GetOpenFileName(OFName) Then
        ShowOpen = Trim(OFName.lpstrFile)
    Else
        ShowOpen = ""
    End If
End Function
'================================================================
[B]Sub Main()[/B]
    Dim s1$, s2$
        s1 = Application.GetOpenFileName()
        s2 = ShowOpen("All File(*.*)" + Chr(0) + "*.*" + Chr(0), "C:\", "Test")
        [I][B]MsgBox StrComp(s1, s2[/B][/I])
[B]End Sub[/B]

Thanks!
 
Lần chỉnh sửa cuối:
Hôm nay thứ 6/13 --> thảo nào đầu óc điên điên không nghĩ ra:
* Strcomp khác nhau -- thì debug.Print 2 chuỗi ra so biết liền +-+-+-+

*--> trong chuỗi thừa ký tự vbnullchar =Chr(0) :

==> sửa thành :
Mã:
[COLOR=#000000]ShowOpen = Replace(Trim(OFName.lpstrFile),chr(0),"")[/COLOR]
là ok hết

*tất nhiên nếu thừa ký tự chr(0) khi mình paste đường dẫn vào cửa sổ Run thì file vẫn mở lên bình thường : nhưng khi dùng với ADO thì nó sẽ báo lỗi unrecogize ngay
* Vấn đề ở đây là tại sao hàm lại sinh ra ký tự chr(0) : trong khi em vẫn đặt biến lpstrFile = space$(254) --> hổng có lẽ em chr(0) sinh ra do mình đặt bộ lọc filter ??????+-+-+-+
 
Upvote 0
Hôm nay thứ 6/13 --> thảo nào đầu óc điên điên không nghĩ ra:
* Strcomp khác nhau -- thì debug.Print 2 chuỗi ra so biết liền +-+-+-+

*--> trong chuỗi thừa ký tự vbnullchar =Chr(0) :

==> sửa thành :
Mã:
[COLOR=#000000]ShowOpen = Replace(Trim(OFName.lpstrFile),chr(0),"")[/COLOR]
là ok hết

*tất nhiên nếu thừa ký tự chr(0) khi mình paste đường dẫn vào cửa sổ Run thì file vẫn mở lên bình thường : nhưng khi dùng với ADO thì nó sẽ báo lỗi unrecogize ngay
* Vấn đề ở đây là tại sao hàm lại sinh ra ký tự chr(0) : trong khi em vẫn đặt biến lpstrFile = space$(254) --> hổng có lẽ em chr(0) sinh ra do mình đặt bộ lọc filter ??????+-+-+-+

Kết quả trả về bao giờ cũng có chr(0) ở cuối. Chr(0) là do Windows copy vào để biết chỗ mà dữ liệu kết thúc.

Khi bạn gọi hàm thì bạn phải thiết lập nhiều trường của cấu trúc OPENFILENAME để Windows biết: filter thế nào, chọn 1 hay multiselect, tiêu đề của sổ chọn, thư mục bắt đầu ... Và cái quan trọng nhất: bạn phải cung cấp 1 buffer để Windows copy vào nó kết quả mà người dùng lựa chọn. Cái buffer này chính là Space$(254) mà bạn "thế" vào OFName.lpstrFile. Khi người dùng chọn thì Windows sẽ copy kết quả vào OFName.lpstrFile.

Nói cho cùng thì cái buffer mà bạn cung cấp có thể chứa những ký tự bất kỳ, vd. toàn "a". Nếu bạn chọn "C:\hichic.txt" mà Windows chỉ copy vào buffer tới ký tự "t" cuối cùng thì làm sao bạn biết được "dữ liệu" nó tới đâu?

Vậy Windows làm rất lô gíc. Nó copy vào buffer: "C:\hichic.txt"&chr(0). Tức chr(0) là dấu hiệu nhận biết "dữ liệu" đã kết thúc. Bạn có thể lọc ra dễ dàng

Mã:
ShowOpen = Left(OFName.lpstrFile, InStr(1, OFName.lpstrFile, Chr(0)) - 1)

Ở trên là (chỗ đỏ đỏ) khi bạn dùng phiên bản ANSI - GetOpenFileNameA (A --> ANSI). Khi bạn dùng phiên bản unicode GetOpenFileNameW - W là viết tắt Wide - thì trong chuỗi "dữ liệu" trả về có thể có chr(0) - vd. ký tự "a" sẽ được biểu diễn bởi 2 bai: bai chr(61) - a, và bai chr(0). Vậy để kết thúc dữ liệu thì Windows dùng 2 bai chr(0) (bản thân dữ liệu có thể chứa bai chr(0) nên không thể dùng 1 bai chr(0) để" đánh dấu" điểm kết thúc dữ liệu được, vậy Windows dùng 2 bai chr(0) để "đánh dấu"). Tức trong buffer sẽ có <toàn bộ đường dẫn>chr(0)chr(0). Như vậy để tìm điểm kết thúc của "dữ liệu" thì phải tìm chuỗi 2 bai chr(0) liên tục

Tất nhiên trường hợp của bạn là chọn 1 tập tin nên cấu trúc "dữ liệu" trả về trong buffer như tôi trình bầy trên. Nếu trong OFName.Flags bạn có OFN_ALLOWMULTISELECT thì cấu trúc "dữ liệu" trả về trong buffer như sau:

1. Trường hợp GetOpenFileNameA:
<toàn bộ đường dẫn thư mục>chr(0)<tên file1>chr(0)<tên file2>chr(0)...<tên filen>chr(0)chr(0)

Dữ liệu kết thúc bởi 2 bai chr(0) để biết đường mà lọc ra hết n tập tin. Biết chúng có chung thư mục (ở ngay đầu) thì "ghép" với thư mục sẽ có full filename.

2. Trường hợp GetOpenFileNameW:
<toàn bộ đường dẫn thư mục>chr(0)chr(0)<tên file1>chr(0)chr(0)<tên file2>chr(0)chr(0)...<tên filen>chr(0)chr(0)chr(0)chr(0)

Dữ liệu kết thúc bởi 4 bai chr(0) để biết đường mà lọc ra hết n tập tin. Biết chúng có chung thư mục (ở ngay đầu) thì "ghép" với thư mục sẽ có full filename.
---------
Bạn thấy Windows thao tác có lô gíc không? Nhất là khi MultiSelect thì đường dẫn tới thư mục chỉ được liệt kê 1 lần ở ngay đầu.
 
Lần chỉnh sửa cuối:
Upvote 0
Hôm nay thứ 6/13 --> thảo nào đầu óc điên điên không nghĩ ra:
* Strcomp khác nhau -- thì debug.Print 2 chuỗi ra so biết liền +-+-+-+

*--> trong chuỗi thừa ký tự vbnullchar =Chr(0) :

==> sửa thành :
Mã:
[COLOR=#000000]ShowOpen = Replace(Trim(OFName.lpstrFile),chr(0),"")[/COLOR]
là ok hết

*tất nhiên nếu thừa ký tự chr(0) khi mình paste đường dẫn vào cửa sổ Run thì file vẫn mở lên bình thường : nhưng khi dùng với ADO thì nó sẽ báo lỗi unrecogize ngay
* Vấn đề ở đây là tại sao hàm lại sinh ra ký tự chr(0) : trong khi em vẫn đặt biến lpstrFile = space$(254) --> hổng có lẽ em chr(0) sinh ra do mình đặt bộ lọc filter ??????+-+-+-+

Chuẩn của API với chuỗi:

1. Đầu tiên yêu cầu cấp phát bộ nhớ cho biến (space$(255)) để lưu chuỗi (một mảng các ký tự liên tiếp)
2. Gán chuỗi vào biến, khi đó bộ nhớ còn lại của biến có thể còn thừa chưa dùng. API làm một việc đưa vbNulChar (Chr(0)) vào sau ký tự cuối cùng của chuỗi để báo hiệu người lập trình biết đoạn kết.
3. Người lập trình phải lấy chuỗi cần lấy chứ không được lấy nguyên 255 ký tự.

Mã:
Function GetAPIstr(ByVal sAPIString As String) As String
    GetAPIstr = Left(sAPIString, InStr(sAPIString, vbNullChar) - 1)
End Function
Mã:
ShowOpen = GetAPIstr(OFName.lpstrFile)
 
Upvote 0
Tôi tò mò 1 chút: Không biết cái ỨNG DỤNG KHÁC mà bạn đang nói là ứng dụng nào vậy? Access chăng?
em đang sử dụng vba trong auotcad :
Autocad này chỉ là con nuôi của bác Bill , không được như Excel - con đẻ của bác Bill :
Trong auotcad vba không hỗ trợ các hội thoại thông dung!!!
** Em xin chân thành cảm ơn anh Switom và anh Nguyễn Duy Tuân đã giúp em hiểu ra vấn đề!

---------
Bạn thấy Windows thao tác có lô gíc không? Nhất là khi MultiSelect thì đường dẫn tới thư mục chỉ được liệt kê 1 lần ở ngay đầu.

Đọc bài viết của anh --> Windows quá logic không thừa không thiếu gì cả,( như cách diễn đạt của anh vậy)^ ^
 
Lần chỉnh sửa cuối:
Upvote 0
em đang sử dụng vba trong auotcad :
Autocad này chỉ là con nuôi của bác Bill , không được như Excel - con đẻ của bác Bill :
Trong auotcad vba không hỗ trợ các hội thoại thông dung!!!

Vậy sao bạn không dùng MS Common Dialog Control cho gọn?
Ví dụ thế này:
Mã:
Sub ShowOpen()
  Dim FileName As String
  With CreateObject("MSComDlg.CommonDialog")
    .InitDir = "D:\ABC"
    .Filter = "Excel Files (*.xls)|*.xls|All files (*.*)|*.*"
    .ShowOpen
    FileName = .FileName
    If Len(FileName) Then MsgBox FileName
  End With
End Sub
 
Upvote 0
Vậy sao bạn không dùng MS Common Dialog Control cho gọn?
Ví dụ thế này:
Mã:
Sub ShowOpen()
  Dim FileName As String
  With CreateObject("MSComDlg.CommonDialog")
    .InitDir = "D:\ABC"
    .Filter = "Excel Files (*.xls)|*.xls|All files (*.*)|*.*"
    .ShowOpen
    FileName = .FileName
    If Len(FileName) Then MsgBox FileName
  End With
End Sub

Em cũng định dùng MS common Dialog Control : tuy nhiên em thấy không phải máy nào cũng có tập tin COMDLG32.OCX - hình như tập tin này không tự động đi kèm theo bộ office hay window, mà phải cài visual basic hay visual foxpro ...
Cụ thể em test trên một số máy hệ điều hành khác nhau đều báo lỗi can't create Ojbject!
--> em mới nghĩ đến getopenFilename API
 
Upvote 0
Em cũng định dùng MS common Dialog Control : tuy nhiên em thấy không phải máy nào cũng có tập tin COMDLG32.OCX - hình như tập tin này không tự động đi kèm theo bộ office hay window, mà phải cài visual basic hay visual foxpro ...
Cụ thể em test trên một số máy hệ điều hành khác nhau đều báo lỗi can't create Ojbject!
--> em mới nghĩ đến getopenFilename API

Đúng rồi! Thêm nữa là tất cả các ActiveX Controls (OCX) chuẩn của Bill đều không hỗ trợ unicode, không chạy được trên Office 64-bit. Từ Office 2010 đến nay Bill đều cho ra hai loại 32, 64-bit. Ngày càng nhiều người cài HĐH & ứng dụng 64-bit để khai thác phần cứng. Dùng API là cách tốt nhất.
 
Lần chỉnh sửa cuối:
Upvote 0
OpenFile trong VB6, VBA với các Office 32-bit, 64-bit, hỗ trợ unicode

Tặng các thành viên hàm OpenFile cho phép làm việc trong môi trường VB6, VBA với các Office 32-bit, 64-bit, hỗ trợ unicode (chữ có dấu).

Các bạn chạy 2 thử thủ tục để test:
+ TestGetSingleFile: mở một file
+ TestGetMultiFile: mở chiều file. Chức năng mở nhiều file hiện nay chưa được tốt, chỉ mở được khoản 3 file với tên file và đường dẫn ngắn. Các thành viên có thể hoàn thiện thuộc này tiếp giúp tôi.


[GPECODE=vb]
Option Explicit

Const MAX_PATH = 255
Public Const OFN_ALLOWMULTISELECT = &H200
Public Const OFN_CREATEPROMPT = &H2000
Public Const OFN_ENABLEHOOK = &H20
Public Const OFN_ENABLETEMPLATE = &H40
Public Const OFN_ENABLETEMPLATEHANDLE = &H80
Public Const OFN_EXPLORER = &H80000 ' new look commdlg
Public Const OFN_EXTENSIONDIFFERENT = &H400
Public Const OFN_FILEMUSTEXIST = &H1000
Public Const OFN_HIDEREADONLY = &H4
Public Const OFN_LONGNAMES = &H200000 ' force long names for 3.x modules
Public Const OFN_NOCHANGEDIR = &H8
Public Const OFN_NODEREFERENCELINKS = &H100000
Public Const OFN_NOLONGNAMES = &H40000 ' force no long names for 4.x modules
Public Const OFN_NONETWORKBUTTON = &H20000
Public Const OFN_NOREADONLYRETURN = &H8000
Public Const OFN_NOTESTFILECREATE = &H10000
Public Const OFN_NOVALIDATE = &H100
Public Const OFN_OVERWRITEPROMPT = &H2
Public Const OFN_PATHMUSTEXIST = &H800
Public Const OFN_READONLY = &H1
Public Const OFN_SHAREAWARE = &H4000
Public Const OFN_SHAREFALLTHROUGH = 2
Public Const OFN_SHARENOWARN = 1
Public Const OFN_SHAREWARN = 0
Public Const OFN_SHOWHELP = &H10

#If VBA7 Then
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As Long
Private Type OPENFILENAME
lStructSize As Long 'The size of this struct (Use the Len function)
hwndOwner As LongPtr 'The hWnd of the owner window. The dialog will be modal to this window
hInstance As LongPtr 'The instance of the calling thread. You can use the App.hInstance here.
lpstrFilter As String 'Use this to filter what files are showen in the dialog. Separate each filter with Chr$(0). The string also has to end with a Chr(0).
lpstrCustomFilter As String 'The pattern the user has choosed is saved here if you pass a non empty string. I never use this one
nMaxCustFilter As Long 'The maximum saved custom filters. Since I never use the lpstrCustomFilter I always pass 0 to this.
nFilterIndex As Long 'What filter (of lpstrFilter) is showed when the user opens the dialog.
lpstrFile As String 'The path and name of the file the user has chosed. This must be at least MAX_PATH (260) character long.
nMaxFile As Long 'The length of lpstrFile + 1
lpstrFileTitle As String 'The name of the file. Should be MAX_PATH character long
nMaxFileTitle As Long 'The length of lpstrFileTitle + 1
lpstrInitialDir As String 'The path to the initial path :) If you pass an empty string the initial path is the current path.
lpstrTitle As String 'The caption of the dialog.
flags As Long 'Flags. See the values in MSDN Library (you can look at the flags property of the common dialog control)
nFileOffset As Integer 'Points to the what character in lpstrFile where the actual filename begins (zero based)
nFileExtension As Integer 'Same as nFileOffset except that it points to the file extention.
lpstrDefExt As String 'Can contain the extention Windows should add to a file if the user doesn't provide one (used with the GetSaveFileName API function)
lCustData As Long 'Only used if you provide a Hook procedure (Making a Hook procedure is pretty messy in VB.
lpfnHook As Long 'Pointer to the hook procedure.
lpTemplateName As String 'A string that contains a dialog template resource name. Only used with the hook procedure.
pvReserved As Long
dwReserved As Long
FlagsEx As Long
End Type
Private Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr
#Else
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As Long
Private Type OPENFILENAME
lStructSize As Long 'The size of this struct (Use the Len function)
hwndOwner As Long 'The hWnd of the owner window. The dialog will be modal to this window
hInstance As Long 'The instance of the calling thread. You can use the App.hInstance here.
lpstrFilter As String 'Use this to filter what files are showen in the dialog. Separate each filter with Chr$(0). The string also has to end with a Chr(0).
lpstrCustomFilter As String 'The pattern the user has choosed is saved here if you pass a non empty string. I never use this one
nMaxCustFilter As Long 'The maximum saved custom filters. Since I never use the lpstrCustomFilter I always pass 0 to this.
nFilterIndex As Long 'What filter (of lpstrFilter) is showed when the user opens the dialog.
lpstrFile As String 'The path and name of the file the user has chosed. This must be at least MAX_PATH (260) character long.
nMaxFile As Long 'The length of lpstrFile + 1
lpstrFileTitle As String 'The name of the file. Should be MAX_PATH character long
nMaxFileTitle As Long 'The length of lpstrFileTitle + 1
lpstrInitialDir As String 'The path to the initial path :) If you pass an empty string the initial path is the current path.
lpstrTitle As String 'The caption of the dialog.
flags As Long 'Flags. See the values in MSDN Library (you can look at the flags property of the common dialog control)
nFileOffset As Integer 'Points to the what character in lpstrFile where the actual filename begins (zero based)
nFileExtension As Integer 'Same as nFileOffset except that it points to the file extention.
lpstrDefExt As String 'Can contain the extention Windows should add to a file if the user doesn't provide one (used with the GetSaveFileName API function)
lCustData As Long 'Only used if you provide a Hook procedure (Making a Hook procedure is pretty messy in VB.
lpfnHook As Long 'Pointer to the hook procedure.
lpTemplateName As String 'A string that contains a dialog template resource name. Only used with the hook procedure.
pvReserved As Long
dwReserved As Long
FlagsEx As Long
End Type
Private Declare Function GetActiveWindow Lib "user32" () As Long
#End If

Public Function OpenFile(Optional Filter As String = vbNullString, _
Optional FilterIndex As Long = 0, _
Optional InitialDir As String = vbNullString, _
Optional DialogTitle As String = vbNullString, _
Optional Flag As Long = 0) As Variant
Dim OFName As OPENFILENAME
If Len(Filter) = 0 Then Filter = "All Files(*.*)|*.*|"
Filter = Replace(Filter, "|", vbNullChar)
OFName.lStructSize = Len(OFName)
OFName.hwndOwner = GetActiveWindow
'OFName.hInstance = Application.hInstance
OFName.lpstrFilter = StrConv(Filter, vbUnicode)
OFName.nFilterIndex = FilterIndex
OFName.nMaxFile = MAX_PATH
OFName.lpstrFile = Space$(MAX_PATH)
OFName.lpstrFileTitle = Space$(MAX_PATH)
OFName.nMaxFileTitle = MAX_PATH
OFName.lpstrInitialDir = StrConv(InitialDir, vbUnicode)
OFName.lpstrTitle = StrConv(DialogTitle, vbUnicode)
OFName.flags = Flag
If GetOpenFileName(OFName) Then
If OFN_ALLOWMULTISELECT And Flag Then
OpenFile = StrConv(GetAPIstr(OFName.lpstrFile, vbNullChar & vbNullChar & vbNullChar & vbNullChar), vbFromUnicode)
OpenFile = Split(OpenFile, vbNullChar) 'convert to 1D array
Else
OpenFile = StrConv(GetAPIstr(OFName.lpstrFile), vbFromUnicode)
End If
Else
OpenFile = ""
End If
End Function

Function GetAPIstr(ByVal sAPIString As String, Optional ByVal sTerminatedText As String = vbNullChar & vbNullChar) As String
GetAPIstr = Left(sAPIString, InStr(sAPIString, sTerminatedText))
End Function

'TEST---------------------------------------------------------------
Sub TestGetSingleFile()
Dim FullName
Cells.Clear
FullName = OpenFile("All Files(*.*)|*.*|" & _
"Excel File|*.xl*|")
Cells(1, 1) = FullName
MsgBox FullName
End Sub
'-------------------------------------------------------------------
Sub TestGetMultiFile()
Dim FullName
Dim I&
Cells.Clear
FullName = OpenFile("All Files(*.*)|*.*|" & _
"Excel File|*.xl*|", , , OFN_EXPLORER Or OFN_ALLOWMULTISELECT)
If Not VarType(FullName) = (vbArray Or vbString) Then Exit Sub
For I = LBound(FullName) To UBound(FullName)
Cells(I + 1, 1) = FullName(I)
Next I
MsgBox "So file la: " & UBound(FullName) - LBound(FullName) + 1
End Sub

[/GPECODE]
 
Lần chỉnh sửa cuối:
Upvote 0
Tặng các thành viên hàm OpenFile cho phép làm việc trong môi trường VB6, VBA với các Office 32-bit, 64-bit, hỗ trợ unicode (chữ có dấu).

Các bạn chạy 2 thử thủ tục để test:
+ TestGetSingleFile: mở một file
+ TestGetMultiFile: mở chiều file. Chức năng mở nhiều file hiện nay chưa được tốt, chỉ mở được khoản 3 file với tên file và đường dẫn ngắn. Các thành viên có thể hoàn thiện thuộc này tiếp giúp tôi.

*Hình như code này không chạy được trên window 8 (64bits) -- Không biết là do máy em có vấn đề? hay là do HDH win 8
*Theo em ta nên hàm này càng giống với hàm application.getopenfilename() trong excel càng tốt ( vì tính chất thân thiện ,quen thuộc dễ sử dụng ):
* Do máy ở nhà đang là win8 -->không test được: -->em nghĩ phải bổ sung thêm OFN_Pathmustexist --> đề phòng người dùng chọn 2 folder rồi ấn ok --> hàm sẽ báo lỗi????
* Hàm của anh không có chức năng filter index như getopenfilename của excel <--- cái này không quan trong, nhưng em nghĩ thừa còn hơn thiếu ^^
 
Upvote 0
*Hình như code này không chạy được trên window 8 (64bits) -- Không biết là do máy em có vấn đề? hay là do HDH win 8
*Theo em ta nên hàm này càng giống với hàm application.getopenfilename() trong excel càng tốt ( vì tính chất thân thiện ,quen thuộc dễ sử dụng ):
* Do máy ở nhà đang là win8 -->không test được: -->em nghĩ phải bổ sung thêm OFN_Pathmustexist --> đề phòng người dùng chọn 2 folder rồi ấn ok --> hàm sẽ báo lỗi????
* Hàm của anh không có chức năng filter index như getopenfilename của excel <--- cái này không quan trong, nhưng em nghĩ thừa còn hơn thiếu ^^

Có tham số Flag đó, thích thì đưa vào "OFN* OR OFN*". Có mã nguồn đó các bạn sửa theo ý.

Hình như code này không chạy được trên window 8 (64bits) ? Bạn chụp hình báo lỗi xem thế nào?
 
Upvote 0
Tặng các thành viên hàm OpenFile cho phép làm việc trong môi trường VB6, VBA với các Office 32-bit, 64-bit, hỗ trợ unicode (chữ có dấu).

Các bạn chạy 2 thử thủ tục để test:
+ TestGetSingleFile: mở một file
+ TestGetMultiFile: mở chiều file. Chức năng mở nhiều file hiện nay chưa được tốt, chỉ mở được khoản 3 file với tên file và đường dẫn ngắn. Các thành viên có thể hoàn thiện thuộc này tiếp giúp tôi.


[GPECODE=vb]
#If VBA7 Then
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As Long
Private Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr
#Else
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As Long
Private Declare Function GetActiveWindow Lib "user32" () As Long
#End If
[/GPECODE]

Anh Tuân, cho em hỏi, VBA7 là VBA trong môi trường Windows 64-bit phải không ạ? Em thì chưa xài 64-bit bao giờ nên thắc mắc.

Mà nếu là vậy thì cái dưới này phải là biến LongPtr chứ nhỉ?

Mã:
#If VBA7 Then
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As [B][COLOR=#ff0000]Long[/COLOR][/B]
 
Upvote 0
Có tham số Flag đó, thích thì đưa vào "OFN* OR OFN*". Có mã nguồn đó các bạn sửa theo ý.

Hình như code này không chạy được trên window 8 (64bits) ? Bạn chụp hình báo lỗi xem thế nào?

nó không báo lỗi gì ?? mà không hiện lên hội thoại open filename!,Run code không thấy một hiện tượng bình thường, cũng như bất thường nào cả ???
* Không biết có bạn nào dùng win 8 64 chưa nhỉ? các bạn test thử xem, không biết là do máy mình hay là do win không hỗ trợ nhỉ
 
Upvote 0
Anh Tuân, cho em hỏi, VBA7 là VBA trong môi trường Windows 64-bit phải không ạ? Em thì chưa xài 64-bit bao giờ nên thắc mắc.


VBA7 để định hướng trình biên dịch chạy code của khối trong Office 2010 trở lên (32-bit hoặc 64-bit). LongPtr trình biên dịch sẽ tự convert về 32-bit nếu Office là 32-bit, về số Long 64-bit nếu Office là 64-bit.

Mà nếu là vậy thì cái dưới này phải là biến LongPtr chứ nhỉ?

Mã:
#If VBA7 Then
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As [B][COLOR=#ff0000]Long[/COLOR][/B]

Hàm API GetOpenFileName trả về giá trị là số nguyên (theo khai báo kiểu VB) nhưng thực chất hàm này nó trả về giá trị Boolean (0/1))

http://msdn.microsoft.com/en-us/library/windows/desktop/ms646927(v=vs.85).aspx

Return value

Type: BOOL

If the user specifies a file name and clicks the OK button, the return value is nonzero. The buffer pointed to by the lpstrFile member of the OPENFILENAME structure contains the full path and file name specified by the user.

If the user cancels or closes the Open dialog box or an error occurs, the return value is zero. To get extended error information, call the CommDlgExtendedError function, which can return one of the following values.

Vì vậy không cần thiết phải lấy số LongPtr để lưu giá trị 0/1.
Thường những hàm trả về giá trị là các điều khiển của hệ thống: HANDLE, HOOK, HINSTANCE, hDC, HFONT,... thì cần LongPtr để lưu đủ giá trị số nguyên.
 
Upvote 0
VBA7 để định hướng trình biên dịch chạy code của khối trong Office 2010 trở lên (32-bit hoặc 64-bit). LongPtr trình biên dịch sẽ tự convert về 32-bit nếu Office là 32-bit, về số Long 64-bit nếu Office là 64-bit.

Sẳn vấn đề này cho em hỏi tại bài này:

http://www.giaiphapexcel.com/forum/...-tạo-hiệu-ứng-trong-Excel&p=527652#post527652

Có thành viên nói không xài được trên Win 64-bit, thì em sửa như thế này có được không?

Mã:
#If [COLOR=#ff0000][B]VBA7 [/B][/COLOR]Then
    Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As [COLOR=#0000ff][B]LongPtr[/B][/COLOR]
    Private Type POINTAPI
        x As [COLOR=#0000ff][B]LongPtr[/B][/COLOR]
        y As [COLOR=#0000ff][B]LongPtr[/B][/COLOR]
    End Type
#Else
    Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    Private Type POINTAPI
        x As Long
        y As Long
    End Type
#End If

Cám ơn Anh.
 
Upvote 0
nó không báo lỗi gì ?? mà không hiện lên hội thoại open filename!,Run code không thấy một hiện tượng bình thường, cũng như bất thường nào cả ???
* Không biết có bạn nào dùng win 8 64 chưa nhỉ? các bạn test thử xem, không biết là do máy mình hay là do win không hỗ trợ nhỉ

Mình vừa sửa lại code và update tại bài có mã nguồn bạn copy lại xem được khôn nhé. Code bổ sung khai báo trong kiểu OPENFILENAME, và thêm tham số FilterIndex trong hàm OpenFile().

[GPECODE=vb]
Private Type OPENFILENAME
lStructSize As Long 'The size of this struct (Use the Len function)
hwndOwner As LongPtr 'The hWnd of the owner window. The dialog will be modal to this window
hInstance As LongPtr 'The instance of the calling thread. You can use the App.hInstance here.
lpstrFilter As String 'Use this to filter what files are showen in the dialog. Separate each filter with Chr$(0). The string also has to end with a Chr(0).
lpstrCustomFilter As String 'The pattern the user has choosed is saved here if you pass a non empty string. I never use this one
nMaxCustFilter As Long 'The maximum saved custom filters. Since I never use the lpstrCustomFilter I always pass 0 to this.
nFilterIndex As Long 'What filter (of lpstrFilter) is showed when the user opens the dialog.
lpstrFile As String 'The path and name of the file the user has chosed. This must be at least MAX_PATH (260) character long.
nMaxFile As Long 'The length of lpstrFile + 1
lpstrFileTitle As String 'The name of the file. Should be MAX_PATH character long
nMaxFileTitle As Long 'The length of lpstrFileTitle + 1
lpstrInitialDir As String 'The path to the initial path :) If you pass an empty string the initial path is the current path.
lpstrTitle As String 'The caption of the dialog.
flags As Long 'Flags. See the values in MSDN Library (you can look at the flags property of the common dialog control)
nFileOffset As Integer 'Points to the what character in lpstrFile where the actual filename begins (zero based)
nFileExtension As Integer 'Same as nFileOffset except that it points to the file extention.
lpstrDefExt As String 'Can contain the extention Windows should add to a file if the user doesn't provide one (used with the GetSaveFileName API function)
lCustData As Long 'Only used if you provide a Hook procedure (Making a Hook procedure is pretty messy in VB.
lpfnHook As Long 'Pointer to the hook procedure.
lpTemplateName As String 'A string that contains a dialog template resource name. Only used with the hook procedure.
pvReserved As Long
dwReserved As Long
FlagsEx As Long
End Type
[/GPECODE]

Qua đây mới thấy, GPECODE chưa đi kịp với sự phát triển với "thời đại" là kiểu LongPtr không được định dạng đúng. Các Admin: Duyệt, Kyo, TranThanhPhong giúp bổ sung nhé.
 
Upvote 0
Sẳn vấn đề này cho em hỏi tại bài này:

http://www.giaiphapexcel.com/forum/...-tạo-hiệu-ứng-trong-Excel&p=527652#post527652

Có thành viên nói không xài được trên Win 64-bit, thì em sửa như thế này có được không?

Mã:
#If VBA7 [/B][/COLOR]Then
    Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As [COLOR=#0000ff][B]LongPtr[/B][/COLOR]
    Private Type POINTAPI
        x As [COLOR=#0000ff][B]LongPtr[/B][/COLOR]
        y As [COLOR=#0000ff][B]LongPtr[/B][/COLOR]
    End Type
#Else
    Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    Private Type POINTAPI
        x As Long
        y As Long
    End Type
#End If

Cám ơn Anh.

Khai báo lại như sau:

Mã:
    Private Type POINTAPI
        x As Long
        y As Long
    End Type
#If VBA7 Then
    Private Declare [COLOR=#ff0000][B]PtrSafe [/B][/COLOR] Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As [COLOR=#0000ff][B]Long[/B][/COLOR]
#Else
    Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
#End If

X, Y là toạ độ màn hình, giá trị cao nhất của nó không vượt quá giá trị lớn nhất của kiểu Long nên không cần LongPtr. Kiểu giá trị trả về của GetCursorPos cũng vậy.
 
Upvote 0
Tặng các thành viên hàm OpenFile cho phép làm việc trong môi trường VB6, VBA với các Office 32-bit, 64-bit, hỗ trợ unicode (chữ có dấu).

Các bạn chạy 2 thử thủ tục để test:
+ TestGetSingleFile: mở một file
+ TestGetMultiFile: mở chiều file. Chức năng mở nhiều file hiện nay chưa được tốt, chỉ mở được khoản 3 file với tên file và đường dẫn ngắn. Các thành viên có thể hoàn thiện thuộc này tiếp giúp tôi.

Để thỏa mãn kiến nghị của dân Excel quen dùng Application.GetOpenFilename tôi nghĩ ta nên sửa code (thuộc loại trang điểm thôi) như sau. Nhất là khi code của ta không phục vụ mọi loại giá trị của các thông số của hàm API.
1. Hàm OpenFile chỉ có các thông số: FileFilter, FilterIndex, DialogTitle, InitialDir, MultiSelect
Không có Flag vì nếu có thì để thiết lập thì user phải biết các giá trị OFN_* và ý nghĩa của chúng. Ngoài ra ta cố ý sẽ dùng OFN_EXPLORER, không để user tự quyết, vì dùng old-style dialog boxes thì kết quả trả về sẽ không được ngăn cách bởi chr(0) và do vậy việc lọc kết quả trả về sẽ không còn đúng nếu dùng code hiện hành.

2. Tôi bỏ
Mã:
    OFName.hwndOwner = GetActiveWindow
    OFName.lpstrFileTitle = Space$(MAX_PATH)
    OFName.nMaxFileTitle = MAX_PATH

3. Trường hợp MultiSelect thì ta dùng buffer "đủ lớn" thôi. Tôi nghĩ quãng 65535 là đủ. Giá trị có thể sửa tăng lên.
----------
Trong code của Tuân mà tôi thử sửa thì Filter không kết thúc bởi "|", tức ta sẽ dùng vd. "Text Files (*.txt)|*.txt" chứ không dùng "Text Files (*.txt)|*.txt|". Code sẽ tự "lo liệu" sao cho Filter kết thúc bởi 2 vbNullChar trước khi gọi hàm API.
-----------
Tôi đề nghị sửa code của Tuân như sau (phần khai báo để nguyên):
Mã:
Option Explicit
 
Const MAX_PATH = 255
Public Const OFN_ALLOWMULTISELECT = &H200
Public Const OFN_CREATEPROMPT = &H2000
Public Const OFN_ENABLEHOOK = &H20
Public Const OFN_ENABLETEMPLATE = &H40
Public Const OFN_ENABLETEMPLATEHANDLE = &H80
Public Const OFN_EXPLORER = &H80000                         '  new look commdlg
Public Const OFN_EXTENSIONDIFFERENT = &H400
Public Const OFN_FILEMUSTEXIST = &H1000
Public Const OFN_HIDEREADONLY = &H4
Public Const OFN_LONGNAMES = &H200000                       '  force long names for 3.x modules
Public Const OFN_NOCHANGEDIR = &H8
Public Const OFN_NODEREFERENCELINKS = &H100000
Public Const OFN_NOLONGNAMES = &H40000                      '  force no long names for 4.x modules
Public Const OFN_NONETWORKBUTTON = &H20000
Public Const OFN_NOREADONLYRETURN = &H8000
Public Const OFN_NOTESTFILECREATE = &H10000
Public Const OFN_NOVALIDATE = &H100
Public Const OFN_OVERWRITEPROMPT = &H2
Public Const OFN_PATHMUSTEXIST = &H800
Public Const OFN_READONLY = &H1
Public Const OFN_SHAREAWARE = &H4000
Public Const OFN_SHAREFALLTHROUGH = 2
Public Const OFN_SHARENOWARN = 1
Public Const OFN_SHAREWARN = 0
Public Const OFN_SHOWHELP = &H10
 
#If VBA7 Then
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias _
                        "GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As Long
Private Type OPENFILENAME
    lStructSize As Long          'The size of this struct (Use the Len function)
    hwndOwner As LongPtr         'The hWnd of the owner window. The dialog will be modal to this window
    hInstance As LongPtr         'The instance of the calling thread. You can use the App.hInstance here.
    lpstrFilter As String        'Use this to filter what files are showen in the dialog. Separate each filter with Chr$(0). The string also has to end with a Chr(0).
    lpstrCustomFilter As String  'The pattern the user has choosed is saved here if you pass a non empty string. I never use this one
    nMaxCustFilter As Long       'The maximum saved custom filters. Since I never use the lpstrCustomFilter I always pass 0 to this.
    nFilterIndex As Long         'What filter (of lpstrFilter) is showed when the user opens the dialog.
    lpstrFile As String          'The path and name of the file the user has chosed. This must be at least MAX_PATH (260) character long.
    nMaxFile As Long             'The length of lpstrFile + 1
    lpstrFileTitle As String     'The name of the file. Should be MAX_PATH character long
    nMaxFileTitle As Long        'The length of lpstrFileTitle + 1
    lpstrInitialDir As String    'The path to the initial path :) If you pass an empty string the initial path is the current path.
    lpstrTitle As String         'The caption of the dialog.
    flags As Long                'Flags. See the values in MSDN Library (you can look at the flags property of the common dialog control)
    nFileOffset As Integer       'Points to the what character in lpstrFile where the actual filename begins (zero based)
    nFileExtension As Integer    'Same as nFileOffset except that it points to the file extention.
    lpstrDefExt As String        'Can contain the extention Windows should add to a file if the user doesn't provide one (used with the GetSaveFileName API function)
    lCustData As Long            'Only used if you provide a Hook procedure (Making a Hook procedure is pretty messy in VB.
    lpfnHook As Long             'Pointer to the hook procedure.
    lpTemplateName As String     'A string that contains a dialog template resource name. Only used with the hook procedure.
    pvReserved As Long
    dwReserved As Long
    FlagsEx As Long
End Type
Private Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr
#Else
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias _
                        "GetOpenFileNameW" (pOpenfilename As OPENFILENAME) As Long
Private Type OPENFILENAME
    lStructSize As Long          'The size of this struct (Use the Len function)
    hwndOwner As Long            'The hWnd of the owner window. The dialog will be modal to this window
    hInstance As Long            'The instance of the calling thread. You can use the App.hInstance here.
    lpstrFilter As String        'Use this to filter what files are showen in the dialog. Separate each filter with Chr$(0). The string also has to end with a Chr(0).
    lpstrCustomFilter As String  'The pattern the user has choosed is saved here if you pass a non empty string. I never use this one
    nMaxCustFilter As Long       'The maximum saved custom filters. Since I never use the lpstrCustomFilter I always pass 0 to this.
    nFilterIndex As Long         'What filter (of lpstrFilter) is showed when the user opens the dialog.
    lpstrFile As String          'The path and name of the file the user has chosed. This must be at least MAX_PATH (260) character long.
    nMaxFile As Long             'The length of lpstrFile + 1
    lpstrFileTitle As String     'The name of the file. Should be MAX_PATH character long
    nMaxFileTitle As Long        'The length of lpstrFileTitle + 1
    lpstrInitialDir As String    'The path to the initial path :) If you pass an empty string the initial path is the current path.
    lpstrTitle As String         'The caption of the dialog.
    flags As Long                'Flags. See the values in MSDN Library (you can look at the flags property of the common dialog control)
    nFileOffset As Integer       'Points to the what character in lpstrFile where the actual filename begins (zero based)
    nFileExtension As Integer    'Same as nFileOffset except that it points to the file extention.
    lpstrDefExt As String        'Can contain the extention Windows should add to a file if the user doesn't provide one (used with the GetSaveFileName API function)
    lCustData As Long            'Only used if you provide a Hook procedure (Making a Hook procedure is pretty messy in VB.
    lpfnHook As Long             'Pointer to the hook procedure.
    lpTemplateName As String     'A string that contains a dialog template resource name. Only used with the hook procedure.
    pvReserved As Long
    dwReserved As Long
    FlagsEx As Long
End Type
Private Declare Function GetActiveWindow Lib "user32" () As Long
#End If
 
Public Function OpenFile(Optional Filter As String = vbNullString, _
                        Optional FilterIndex As Long = 1, _
                        Optional InitialDir As String = vbNullString, _
                        Optional DialogTitle As String = vbNullString, _
                        Optional MultiSelect As Boolean = False) As Variant
Dim OFName As OPENFILENAME, size As Long
    OFName.lStructSize = Len(OFName)
    If Len(Filter) = 0 Then
        Filter = "All Files(*.*)|*.*" & vbNullChar
    Else
        Filter = Filter & vbNullChar
    End If
    Filter = Replace(Filter, "|", vbNullChar)
    OFName.lpstrFilter = StrConv(Filter, vbUnicode)
    OFName.nFilterIndex = FilterIndex
    If MultiSelect Then
        size = 65535
    Else
        size = MAX_PATH
    End If
    OFName.lpstrFile = String(size, vbNullChar)     ' [COLOR=#ff0000]khong dung  Space$(...)[/COLOR]
    OFName.nMaxFile = size
    OFName.lpstrInitialDir = StrConv(InitialDir, vbUnicode)
    OFName.lpstrTitle = StrConv(DialogTitle, vbUnicode)
    If MultiSelect Then OFName.flags = OFN_EXPLORER Or OFN_ALLOWMULTISELECT
    
    If GetOpenFileName(OFName) Then
        If MultiSelect Then
            OpenFile = StrConv(GetAPIstr(OFName.lpstrFile, vbNullChar & vbNullChar & vbNullChar & vbNullChar), vbFromUnicode)
            OpenFile = Split(OpenFile, vbNullChar) 'convert to 1D array
        Else
            OpenFile = StrConv(GetAPIstr(OFName.lpstrFile), vbFromUnicode)
        End If
    Else
        OpenFile = ""
    End If
End Function
 
Function GetAPIstr(ByVal sAPIString As String, Optional ByVal sTerminatedText As String = vbNullChar & vbNullChar) As String
    GetAPIstr = Left(sAPIString, InStr(sAPIString, sTerminatedText))
End Function
 
'TEST---------------------------------------------------------------
Sub TestGetSingleFile()
    Dim FullName
    Cells.Clear
    FullName = OpenFile("All Files(*.*)|*.*|" & _
                        "Excel File|*.xl*")
    Cells(1, 1) = FullName
    MsgBox FullName
End Sub
'-------------------------------------------------------------------
Sub TestGetMultiFile()
    Dim FullName
    Dim I&
    Cells.Clear
    FullName = OpenFile("All Files(*.*)|*.*|" & _
                        "Excel File|*.xl*", , , , True)
    If Not VarType(FullName) = (vbArray Or vbString) Then Exit Sub
    For I = LBound(FullName) To UBound(FullName)
        Cells(I + 1, 1) = FullName(I)
    Next I
    MsgBox "So file la: " & UBound(FullName) - LBound(FullName) + 1
End Sub
 
Upvote 0
Mình vừa sửa lại code và update tại bài có mã nguồn bạn copy lại xem được khôn nhé. Code bổ sung khai báo trong kiểu OPENFILENAME, và thêm tham số FilterIndex trong hàm OpenFile().


Qua đây mới thấy, GPECODE chưa đi kịp với sự phát triển với "thời đại" là kiểu LongPtr không được định dạng đúng. Các Admin: Duyệt, Kyo, TranThanhPhong giúp bổ sung nhé.

em đã test lại code , nhưng vấn không thể hiện hộp thoại open filename, với win 8 64 bits trong khi hàm getopenfilename của excel vấn hoạt động bình thường --> không biết có phải là do cấu trúc win 8 khác với win khác không nhỉ?
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom