Lỗi FM20.dll khó hiểu trong Office 64 bit

Liên hệ QC

PhanTuHuong

VBA & VB.NET for Excel & AutoCad
Thành viên danh dự
Tham gia
13/6/06
Bài viết
7,121
Được thích
24,279
Tôi đang nâng cấp dự án trong Office 64 bit. Tuy nhiên có 1 vấn đề là khi chạy chương trình luôn nhận được thông báo lỗi FM20.dll dù đã thử bản Office 2016, 2019 (đã update). Chương trình sử dụng control trong FM20.dll đó. Đương nhiên là fix xong thì chạy bình thường. Lỗi này xảy ra từ lâu nhưng Microsoft không sửa thì phải??? Với Office 32 bit thì không xảy ra tình trạng này.

FM20.jpg
 
Kết quả test:
1. Hàm GetActiveHwnd trả về 0 => Sai


Nếu tình huống của hàm này không khắc phục được thì đó là lỗi nghiêm trọng nhiều ứng dụng có khả năng rất cao sẽ không thể dùng được phương pháp này khi nhúng các DLL, OCX khác vào VB6.

2. Thủ tục ghi dữ liệu vào vùng chạy được.

(*) Nếu hàm GetActiveHwnd() fix được, tôi sẽ làm ví dụ test tiếp
1. Tạo DLL sử dùng các Form, control của VB6
2. Tạo DLL sử dụng activex controsl chuẩn của Microsoft "MSCOMCTL.OCX"
3. Tạo COM Add-in để tự gọi khi mở Excel.
Tôi lại ít khi quan tâm mấy cái đó ... với tôi nó quá xa vời và tôi ko có hiểu sâu xa bản chất của nó làm gì

1/ cái tôi quan tâm là có chạy hay không chạy thôi
2/ khi tôi viết lấy dữ liệu hay gì đó nó trả kết quả chính xác như ta code là ok
...
Còn lại ko quan tâm
 
Upvote 0
Tôi lại ít khi quan tâm mấy cái đó ... với tôi nó quá xa vời và tôi ko có hiểu sâu xa bản chất của nó làm gì

1/ cái tôi quan tâm là có chạy hay không chạy thôi
2/ khi tôi viết lấy dữ liệu hay gì đó nó trả kết quả chính xác như ta code là ok
...
Còn lại ko quan tâm

Bạn đã xem xét kỹ lại hàm GetActiveHnwd() chưa? Có đúng là có lỗi? Đây chỉ là bài test nhỏ của tôi để đánh giá cả một vấn đề lớn. Nếu hàm này không chạy được theo cách của bạn thì tôi vẫn nói lại nhiều lần vấn đề về này trên diễn đàn, trình biên dịch trên các platform khác nhau sẽ tạo ra nguyên lý vận hành code khác nhau, địa chỉ vùng nhớ trong platform 32-bit là 4 byte, 64-bit là 8 byte. Vì lẽ đó là mà hệ điều hành Windows tồn tại song song hai bộ DLL cung cấp các hàm API tên gọi giống nhau nhưng kiểu dữ liệu lại khác nhau, để phục vụ cho các process gọi các DLL đó theo từng loại platform tương ứng, chứ không phải dùng chung một file DLL. Thấy bạn nhắc nhiều vấn đề chạy được DLL tạo trong VB6 cho Office 64-bit nên tôi muốn dành thời gian một lần test vấn đề của bạn để xem nó làm theo kiểu nào? Nếu phạm vi nó chỉ làm ở mức A thì chỉ nói ở mức A, nếu nó có thể ứng dụng rộng, đại trà thì ta nói đại trà.
 
Upvote 0
Tôi mượn code sau của anh QuangHai để chứng minh 2 điều cho ai đó thấy
1/ Code VBA và VB6 nó như nhau
2/ chứng mình cho chủ đề này thấy nó chạy rất tốt
3/ Code trong VB6 Như sau

Mã:
Public Sub GetData(StrPath, DataRange, Des())
    Dim ObjConn As Object, RS As Object, StrRequest As String
    Set RS = CreateObject("ADODB.Recordset")
    Set ObjConn = GetExcelConnection(StrPath)
    StrRequest = "SELECT * From " & DataRange
    RS.Open StrRequest, ObjConn, 3, 1
    Des = TransArr(RS.Getrows)
    RS.Close
    Set ObjConn = Nothing
    Set RS = Nothing
End Sub

Public Function GetExcelConnection(ByVal Path As String)
    Dim StrConn As String, ObjConn As Object, Pro As String, Ext As String
    Set ObjConn = CreateObject("ADODB.Connection")
    Pro = "Provider=Microsoft.ACE.OLEDB.12.0;"
    Ext = ";Extended Properties=""Excel 8.0;"
    StrConn = Pro & "Data Source=" & Path & Ext & "HDR=No" & ";IMEX=1"";"
    ObjConn.Open StrConn
    Set GetExcelConnection = ObjConn
End Function

Public Function TransArr(sArr As Variant) As Variant
    Dim TmpArr As Variant, x As Long, y As Long
    ReDim TmpArr(UBound(sArr, 2), UBound(sArr, 1))
    For x = 0 To UBound(sArr, 2)
        For y = 0 To UBound(sArr, 1)
            TmpArr(x, y) = sArr(y, x)
        Next y
    Next x
    TransArr = TmpArr
End Function

4/ Code Sử dụng từ VBA như sau
Mã:
Rem ========== Luu Y su dung
Rem 1/ Tools\References\Browse..\VBLibraryLoad.exe ==> Su dung Cho RPC As New VBLibraryLoad.cCOM
Rem ==========
Public RPC As New VBLibraryLoad.cCOM
Public LoadCOM As Object, test As Long
Rem ========== Su dung Cho Function
Public Function GetExcelConnection(ByVal Path As String)
    Set LoadCOM = RPC.NewInstance("dllvb6.clsMain")
    Set GetExcelConnection = LoadCOM. GetExcelConnection(Path)
    If Not LoadCOM Is Nothing Then Set LoadCOM = Nothing
End Function
Rem ========== Su dung Cho Sub
Public Sub GetData(StrPath, DataRange, Des())
    Set LoadCOM = RPC.NewInstance("dllvb6.clsMain")
    Call LoadCOM.GetData(StrPath, DataRange, Des())
    If Not LoadCOM Is Nothing Then Set LoadCOM = Nothing
End Sub

5/ Trong file đính kèm là code VBA + VB6 .... chạy tốt và kết quả như nhau
Bài đã được tự động gộp:

Bạn đã xem xét kỹ lại hàm GetActiveHnwd() chưa? Có đúng là có lỗi? Đây chỉ là bài test nhỏ của tôi để đánh giá cả một vấn đề lớn. Nếu hàm này không chạy được theo cách của bạn thì tôi vẫn nói lại nhiều lần vấn đề về này trên diễn đàn, trình biên dịch trên các platform khác nhau sẽ tạo ra nguyên lý vận hành code khác nhau, địa chỉ vùng nhớ trong platform 32-bit là 4 byte, 64-bit là 8 byte. Vì lẽ đó là mà hệ điều hành Windows tồn tại song song hai bộ DLL cung cấp các hàm API tên gọi giống nhau nhưng kiểu dữ liệu lại khác nhau, để phục vụ cho các process gọi các DLL đó theo từng loại platform tương ứng, chứ không phải dùng chung một file DLL. Thấy bạn nhắc nhiều vấn đề chạy được DLL tạo trong VB6 cho Office 64-bit nên tôi muốn dành thời gian một lần test vấn đề của bạn để xem nó làm theo kiểu nào? Nếu phạm vi nó chỉ làm ở mức A thì chỉ nói ở mức A, nếu nó có thể ứng dụng rộng, đại trà thì ta nói đại trà.
Tôi nghĩ thế này .. còn sâu xa bản chất của nó sao thì tôi ko có biết
Bản chất của nó vẫn là DLL 32 bit chỉ có điều tôi lách cho nó chạy trên 64 bit thôi
 

File đính kèm

  • VB6_GPE.rar
    132.5 KB · Đọc: 8
Lần chỉnh sửa cuối:
Upvote 0
Tôi mương code sau của anh QuangHai để chứng minh 2 điều cho ai đó thấy
1/ Code VBA và VB6 nó như nhau
2/ chứng mình cho chủ đề này thấy nó chạy rất tốt
3/ Code trong VB6 Như sau

Mã:
Public Sub GetData(StrPath, DataRange, Des())
    Dim ObjConn As Object, RS As Object, StrRequest As String
    Set RS = CreateObject("ADODB.Recordset")
    Set ObjConn = GetExcelConnection(StrPath)
    StrRequest = "SELECT * From " & DataRange
    RS.Open StrRequest, ObjConn, 3, 1
    Des = TransArr(RS.Getrows)
    RS.Close
    Set ObjConn = Nothing
    Set RS = Nothing
End Sub

Public Function GetExcelConnection(ByVal Path As String)
    Dim StrConn As String, ObjConn As Object, Pro As String, Ext As String
    Set ObjConn = CreateObject("ADODB.Connection")
    Pro = "Provider=Microsoft.ACE.OLEDB.12.0;"
    Ext = ";Extended Properties=""Excel 8.0;"
    StrConn = Pro & "Data Source=" & Path & Ext & "HDR=No" & ";IMEX=1"";"
    ObjConn.Open StrConn
    Set GetExcelConnection = ObjConn
End Function

Public Function TransArr(sArr As Variant) As Variant
    Dim TmpArr As Variant, x As Long, y As Long
    ReDim TmpArr(UBound(sArr, 2), UBound(sArr, 1))
    For x = 0 To UBound(sArr, 2)
        For y = 0 To UBound(sArr, 1)
            TmpArr(x, y) = sArr(y, x)
        Next y
    Next x
    TransArr = TmpArr
End Function

4/ Code Sử dụng từ VBA như sau
Mã:
Rem ========== Luu Y su dung
Rem 1/ Tools\References\Browse..\VBLibraryLoad.exe ==> Su dung Cho RPC As New VBLibraryLoad.cCOM
Rem ==========
Public RPC As New VBLibraryLoad.cCOM
Public LoadCOM As Object, test As Long
Rem ========== Su dung Cho Function
Public Function GetExcelConnection(ByVal Path As String)
    Set LoadCOM = RPC.NewInstance("dllvb6.clsMain")
    Set GetExcelConnection = LoadCOM.ConnectDataName(Path)
    If Not LoadCOM Is Nothing Then Set LoadCOM = Nothing
End Function
Rem ========== Su dung Cho Sub
Public Sub GetData(StrPath, DataRange, Des())
    Set LoadCOM = RPC.NewInstance("dllvb6.clsMain")
    Call LoadCOM.GetData(StrPath, DataRange, Des())
    If Not LoadCOM Is Nothing Then Set LoadCOM = Nothing
End Sub

5/ Trong file đính kèm là code VBA + VB6 .... chạy tốt và kết quả như nhau
Bài đã được tự động gộp:


Tôi nghĩ thế này .. còn sâu xa bản chất của nó sao thì tôi ko có biết
Bản chất của nó vẫn là DLL 32 bit chỉ có điều tôi lách cho nó chạy trên 64 bit thôi

Code này không chứng minh cho chủ đề này được. Nó chỉ chứng mình một phạm vi ứng dụng của nó mà thôi. Tôi có thể nói nguyên lý vì sao nó lại chạy được. Khi các lệnh gọi CreateObject("CalssABC") trong 32-bit. Nó sẽ luôn chạy được trong cả 64-bit vì cái class ABC đó có trong cả hai DLL trong máy tính, nó là cuộc gọi muộn và nhận refrerence tại lúc chạy lệnh chứ không phải lúc biên dịch.

Các DLL khai bán Declare, hay nhúng các actrivex, hay reference các DLL, OCX vào trong project khi biên dịch rồi chạy trong Office 64-bit sẽ lỗi. Vì nó là lưu refrecense đến một thư viện cụ thể rồi.

Quay lại bài hỏi đàu tiên của anh Hướng, cái hình dưới đây:
fm20-2-png.264714

Để ý cái FM20.DLL nó là 32-bit, vì khi mở VB6 bác Hướng đã nhúng các CommandButton hay control gì đó trong file này vào rồi. Tuy nhiên ở Office 64-bit, nó sẽ không thể goi FM20.dll trong thư mục SysWOW64 mà nó lại đòi ở System32. VB6 biên dịch nên các tham chiếu này cố định khi nhúng trong project.
 
Upvote 0
Thôi tôi ko bàn nữa vì bản chất của nó sao thì tôi ko có biết ... chỉ biết thực thế là DLL VB6 chạy tốt cho Office x32 và x64 chung một Files duy nhất

Còn lại ko bàn gì thêm
 
Upvote 0
Tôi vẫn lưu ý với bất kỳ ai áp dụng DLL của VB6 (32-bit) chay trong Office 64-bit nếu code chạy được thì chỉ là tình huống cụ thể (đã nói phía trên), các tình huống khác như khai báo (Declare) các hàm API trong DLL, nhúng ActiveX Control (loại 32-bit như MSCOMCTL.OCX) (Userform -trong FM20-DLL cũng là activex) là lỗi. Đây là kiến thức nên tôi ghi rõ để các bạn nào học, phải biết ứng dụng đúng theo nguyên lý và phạm vì nêu không là cứ nhầm lẫn sai với đúng.
 
Upvote 0
1/ Tôi vẫn sử dụng FM20.dll trên VB6 tốt + Officex32 và x64 tốt ... còn tại sao thì tôi lại ko có biết
2/ DLL viết = VB6 vẫn sử dụng tốt như trên ... còn tại sao thì như trên ko nói lại nữa

1630054785609.png
 
Upvote 0
mà phát sinh chút là tôi có Viết 1 Hàm API = Delphi để check App chạy cùng Windows
Code trên Delphi như Sau
Mã:
function IsAppInRun(RunName: string): WordBool; stdcall;
Var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  //Reg := TRegistry.Create(KEY_WRITE OR KEY_WOW64_64KEY);
  with Reg do
  begin
    // RootKey := HKEY_LOCAL_MACHINE;
    RootKey := HKEY_CURRENT_USER;
    OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', FALSE);
    //OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', True);
    Result := ValueExists(RunName);
    CloseKey;
    Free;
  end;
end;

Nhưng trên VB6 tôi lại ko cần khai báo Private Declare Function
Mà khai báo như sau vẫn chạy tốt ... còn tại sao thì tôi lại ko có biết vì code chạy nó là của tây viết
Còn tôi chỉ biết Copy và tùy biến sử dụng
Mã:
Public Function IsAppInRun(ByRef AppName As String) As Boolean
    Rem Public Function IsAppInRun(ByVal AppName As String) As Boolean
    Rem Neu key Ton tai = True ; Ko ton tai = Flase
    IsAppInRun = stdCallW(mDLL, "IsAppInRun", vbLong, AppName)      ''Su dung Tot
End Function

Với code trên ... nếu Ap dụng cho VBA thì cũng ko cần khai báo Private Declare Function + #If 32 và 64 nữa

Mà chỉ viết 1 Hàm duy nhất là chạy thôi
 
Lần chỉnh sửa cuối:
Upvote 0
Hàm stdCallW và biến mDLL bạn khai báo trog VBA hay VB6? Đây là điểm khá quan trọng.
Bạn có thể lách với một phạm vi giới hạn nào đó, đến một trường hợp cụ thể là lỗi, như tình huống tôi đưa ra với hàm GetActiveHnwd() chỉ là một ví dụ nhỏ. Khi test các ứng dụng tôi hay test các tình huống mà do các hướng lập trình tạo ra xem lỗi hay không, sau đó mới đi test các nhóm trường hợp khác.
 
Upvote 0
Hàm stdCallW và biến mDLL bạn khai báo trog VBA hay VB6? Đây là điểm khá quan trọng.
Bạn có thể lách với một phạm vi giới hạn nào đó, đến một trường hợp cụ thể là lỗi, như tình huống tôi đưa ra với hàm GetActiveHnwd() chỉ là một ví dụ nhỏ. Khi test các ứng dụng tôi hay test các tình huống mà do các hướng lập trình tạo ra xem lỗi hay không, sau đó mới đi test các nhóm trường hợp khác.
Nói thật lòng là nhưng cái bạn nêu tôi xem ko hiểu gì cả vì nó vượt quá xa sự hiểu biết của tôi
Ngay cái thớt Delphi bên kia cũng thế ... có bài bạn viết mà sau 2 năm tôi quậy bank xác ra xong tôi mới hiểu ra cách bạn nói ...

Còn tôi chỉ biết Copy code đâu đó và tùy biến sử dụng ... quá trình sử dụng mới ngộ ra xong viết tiếp
Vọc nhiều tôi mới ngộ ra chứ .... xong tôi xem lại help + Sách thì mới thấy nó nói đúng còn keo tôi đọc sách là xin thua 100/100

còn cái Hàm stdCallW ... nó là 1 Hàm load DLL API chỉ cần gán nó vào là chạy thôi ... code người Đức viết trên 1 Web quốc tế mà tôi là 1 thành viên của nó như thành Viên Kiều Mạnh của GPE vậy

với code trên ko cần khai báo API mà vẫn sử dụng tốt như tôi trình bày ở trên


Mã:
Private Sub Class_Initialize()   
    mDLL = App.Path & "\VBLibrary.dll"
End Sub
 
Upvote 0
Xin giới thiệu với các Bạn SQL Builder TCP/IP ( Builder SQL và lấy dữ liệu qua Internet theo List Files Chia sẻ ở Folder Server )

1/ Ứng dụng viết hoàn toàn = VB6
2/ chạy tốt trên mọi phiên bản Office_x32 và Office_x64
3/ Mình đang sử dụng trên Office365_x64 bit
.....
VB6 viết 1 files duy nhất chạy chung cho 2 nên tảng 32 bit và 64 bit
...
Thực ra nó xong từ lâu nhưng 1 năm qua chuyển vào Delphi cái được cái ko nên chưa Úp tặng cho ai đó cần được
Thời gian tới mình sẻ chuyển các hàm quan trọng vào Delphi ... còn hàm phổ biến thì viết trên VB6 ===> khi nào xong úp tặng cho ai đó sẻ cần nó

Xin mời thưởng thức SQL Builder TCP/IP viết 100/100 trên VB6 chạy trên Office 365 _x64 bít
Liên kết: https://youtu.be/2a62LuirF84
 
Upvote 0
Quay về đúng chủ đề lỗi này, rất nhiều trường hợp bị nhưng Microsoft không quan tâm thì phải, toàn phải vá thủ công:
 
Upvote 0
Xin giới thiệu với các Bạn SQL Builder TCP/IP ( Builder SQL và lấy dữ liệu qua Internet theo List Files Chia sẻ ở Folder Server )

1/ Ứng dụng viết hoàn toàn = VB6
2/ chạy tốt trên mọi phiên bản Office_x32 và Office_x64
3/ Mình đang sử dụng trên Office365_x64 bit
.....
VB6 viết 1 files duy nhất chạy chung cho 2 nên tảng 32 bit và 64 bit
...
Thực ra nó xong từ lâu nhưng 1 năm qua chuyển vào Delphi cái được cái ko nên chưa Úp tặng cho ai đó cần được
Thời gian tới mình sẻ chuyển các hàm quan trọng vào Delphi ... còn hàm phổ biến thì viết trên VB6 ===> khi nào xong úp tặng cho ai đó sẻ cần nó

Xin mời thưởng thức SQL Builder TCP/IP viết 100/100 trên VB6 chạy trên Office 365 _x64 bít
Liên kết: https://youtu.be/2a62LuirF84
Anh có thể gợi ý một vài từ khoá để tìm hiểu làm được giao thức tcp/ip này ko ạ
 
Upvote 0
Đây bạn ! code y chang không chỉnh sửa ! Mình cài office full chức năng.
View attachment 265563
biết ngay mà ...

Lỗi đó là DLL VB6 chỉ chạy ADODB 32 bit khi bạn chạy trên Office 64 bit là lỗi đó

Tải file sau của Ms về cài vào là hết lỗi


Bản chất cái DLL VB6 luôn luôn là 32 bit thì sử dụng mọi cái của nó 32 bit ... còn mình lách cho nó chạy trên 64 bít là chuyện khác
Bài đã được tự động gộp:

câu sau cho ai đó thích tò mò + lý sự nè

1/ Người ta viết 1 cái EXE là VB6
2/ viết 1 cái abcd.dll là VB6
....
Xong từ EXE load hàm trong DLL VB6 mà ko cần phải đăng ký File DLL đó

Hãy tìm hiểu hết trước khi phán xét ... code tây nó viết cho rồi ... còn tôi chỉ copy và sử dụng
 
Lần chỉnh sửa cuối:
Upvote 0
khuyến mại cho phương pháp cuối ... ko mai mốt ai đó sử dụng trên Office 365 64 bit lại la làng tiếp là code tui viết ko sử dụng được

1/ Để sử dụng ADODB trên VB6.dll trên tất cả các bản Officex64 phải cài thư viện sau của ms


2/ để sử dụng các thư viện của Excel như trên thì ta truyền Object từ Excel vào hay ta khởi tạo Object ngay từ trong DLL VB6

3/ Thực hiện 2 mục trên là viết DLL VB6 chạy trên mọi phiên bản Office
 
Upvote 0
Cũng bị lỗi tương tự bạn ơi !
Bài đã được tự động gộp:

Tại dòng này Call LoadCOM.GetData(StrPath, DataRange, Des())
Bài đã được tự động gộp:

Tại dòng này Call LoadCOM.GetData(StrPath, DataRange, Des())
 
Upvote 0
Web KT
Back
Top Bottom