AI muốn lập trình DLL cho Excel và các loại bằng Delphi thì xem video này nhé! (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,842
Được thích
10,337
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Lần chỉnh sửa cuối:
Nhờ Các Bạn chỉ cho Mạnh Học 1 chút

1/ trên Form Delphi Mình tạo 1 Cái Button xong chèn code sau vào thì nó chạy tốt
Có nghĩa mình gán vào cái Label2 = Kiều Mạnh

Mã:
procedure TSQLDataOffice.GetSheetNameClick(Sender: TObject);
begin
  Label2.Caption:= 'Kiều Mạnh';
//TestLabe1 //Lỗi code không có chạy
end;

2/ Vậy trong 1 Unit khác không thuộc Unit của Form Mình có Uses Unit1( Unit1 của Form )
xong mình khai báo như sau nó không có chạy code mà còn báo Lỗi
Mã:
procedure TestLabe1;
var
  Label2: TLabel;
  Label3: TLabel;
begin
  Label2.Caption := 'Kiều Mạnh';
end;

3/ Mình thử tiếp như sau cũng không có chạy code mà còn báo lỗi
Mã:
procedure TestLabe2(Label2: TLabel);
begin
  Label2.Caption := 'Kiều Mạnh';
end;

Vậy nhờ các Bạn chỉ dùm Mạnh từ 1 Unit khác không phải Unit của Form mà khai báo sử dụng như mục 2 và 3 mà chạy code tốt
Xin cảm ơn
 
Lần chỉnh sửa cuối:
Upvote 0
vậy là Mạnh đã làm xong hoàn chỉnh SQL cho Office theo mấy gợi ý của Bạn @huuduy.duy

1/ Xử lý lỗi người dùng gõ sai Range trên Form xong ( cà chua thật )
2/ Tốc độ truy xuất dữ liệu khá nhanh ... Tùy chọn các Cột Or Filedname cũng thế ... xài ADO quá đơn giản chỉ tích chọn là xong
3/ Có thời gian Mạnh sẻ bổ sung thêm nhiều tùy chọn nữa
4/ Khi nhấn Copy SQL thì nó đã lưu trong Clipboard xong muốn dán nó vào đâu thì thuộc về Bạn
...
Chuyển qua Viết SQL Server Ms ... xong qua TCPIP ( TCPIP cái này hên và xui ) ....
Trình code Delphi thấy tiến bộ thêm 1 chút ... xài Hàm + Sub móc nhau lằng nhằng mỳ tôm luôn -0-0-0-
Cảm Ơn các Bạn đã chỉ Mạnh học Delphi .... ko ngờ được luôn là chưa xài Form Delphi bao giờ chỉ có 1 to 2 tuần mà làm đủ thứ trên Form Delphi

OK.png

Bản hoàn thiện 90% Ai iU tải về mà xài
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Mạnh không viết Virus trong DLL của Delphi ... thế mà khi check Virus Total nó cứ keo có Virus
Úp bài hỏi trang Delphi Developer thì có tay tây phán tào lao và hầu như hiểu sai hết có mấy câu gần đúng

Tay_bố láo.PNG


Cuối cùng Mạnh đã biết tại sao mà nó báo có Virus mà mình không có Viết Virus ... Oan gia đã được tự giải đáp ...
Lưu lên đây làm kỷ niệm mò code

Nó cứ báo có Virus sau khi check Virus Total ... một số AV dị ứng sẻ xóa file đó mà nó không phải là virus quả thực mấy cái AV tào lao thật ... kiểu như BKV Free của Anh quãng he

Báo Virus.png
 
Upvote 0
Mọi người cho hỏi vì sao truyền biến chuỗi từ VBA (String) sang Delphi (ShortString) bị cắt mất một ký tự đầu tiên. Mình đã xử lý bằng cách cho thêm một khoảng trắng vào đầu chuỗi nhưng thấy không hay lắm.
[ lại thêm một người "lao vào" Delphi, hihi ]
 
Upvote 0
Mọi người cho hỏi vì sao truyền biến chuỗi từ VBA (String) sang Delphi (ShortString) bị cắt mất một ký tự đầu tiên. Mình đã xử lý bằng cách cho thêm một khoảng trắng vào đầu chuỗi nhưng thấy không hay lắm.
[ lại thêm một người "lao vào" Delphi, hihi ]

Kiểu ShortString Delphi dành độ rộng tối đa chỉ 255 ký tự, ký tự đầu tiên nó chứa độ rộng của chuỗi nó chứa. Bạn nên dùng kiểu String trong Delphi nếu không có lý do đặc biệt.
 
Upvote 0
Kiểu ShortString Delphi dành độ rộng tối đa chỉ 255 ký tự, ký tự đầu tiên nó chứa độ rộng của chuỗi nó chứa. Bạn nên dùng kiểu String trong Delphi nếu không có lý do đặc biệt.
Mình cũng thích String nhưng máy mình bị lỗi gì đó, nếu dùng string thì tiếng Việt có dấu trong Excel biến thành tiếng Nhật trong ShowMessage Delphi (khi copy từ code VBA sang Word cũng bị tương tự). Với lại khi tạo một chương trình mới, Delphi cũng ghi chú vậy mà: "... To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters."
 
Upvote 0
Mình cũng thích String nhưng máy mình bị lỗi gì đó, nếu dùng string thì tiếng Việt có dấu trong Excel biến thành tiếng Nhật trong ShowMessage Delphi (khi copy từ code VBA sang Word cũng bị tương tự). Với lại khi tạo một chương trình mới, Delphi cũng ghi chú vậy mà: "... To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters."
xem lài bài thớt này khoãng trang 30 to 45 gì đó là làm Ok ... cái méc xịt bốc của mình ý
 
Upvote 0
Truyền WideString (BSTR) qua lại cho đơn giản. WideString là string ở tầng Windows, cấp phát = SysAllocStringXXX, giải phóng = SysFreeString.
VBA và Delphi không quản lý memory của các WideString. Windows quản lý,ông dll gì đó làm, cache alloc/free, quên rầu :D
 
Upvote 0
Truyền WideString (BSTR) qua lại cho đơn giản. WideString là string ở tầng Windows, cấp phát = SysAllocStringXXX, giải phóng = SysFreeString.
VBA và Delphi không quản lý memory của các WideString. Windows quản lý,ông dll gì đó làm, cache alloc/free, quên rầu :D
Cảm ơn các bạn, vụ này mình xử lý được rồi.

Mình hỏi thêm các bạn vấn đề khác: Lúc đầu mình viết hàm trong dll bằng VB6, do không sử dụng được trên Excel64 nên mình mới tự học để chuyển qua Dephi.
Mình tạo dll (API - để thay đổi đường dẫn chứa file dll mà không cần Reg). Mình Build thử ra hai loại là 64bit và 32bit, nhưng loại 64bit không chạy được trên Windows 64, trong khi loại dll 32 thì chạy tốt trên Windows 64. Nếu vậy thì cũng như dll của VB6 chớ chưa hơn được, hic!
 
Upvote 0
Cảm ơn các bạn, vụ này mình xử lý được rồi.

Mình hỏi thêm các bạn vấn đề khác: Lúc đầu mình viết hàm trong dll bằng VB6, do không sử dụng được trên Excel64 nên mình mới tự học để
dòng này trên GPE này từ trước tới nay nói chưa có đúng ... xem lại các bài Mạnh Úp trên GPE tải về test thử xem chạy tốt trên Officex32 và x64 trên duy nhất có 1 file

Tại bạn chưa biết làm cho nó sử dụng được trên Officex64 chứ ko phải là ko sử dụng được nhé
nó rất đơn giản bản thân VB6 nó có luôn mà ít ai biết thế thôi

 
Upvote 0
dòng này trên GPE này từ trước tới nay nói chưa có đúng ... xem lại các bài Mạnh Úp trên GPE tải về test thử xem chạy tốt trên Officex32 và x64 trên duy nhất có 1 file

Tại bạn chưa biết làm cho nó sử dụng được trên Officex64 chứ ko phải là ko sử dụng được nhé
nó rất đơn giản bản thân VB6 nó có luôn mà ít ai biết thế thôi

1. Bạn chỉ ra cách để mình học với
2. Hàm trong dll (VB6) không thể sử dụng kiểu API ?
 
Upvote 0
1. Bạn chỉ ra cách để mình học với
2. Hàm trong dll (VB6) không thể sử dụng kiểu API ?
Thì cứ thử sức mò xem sao nó dễ lắm như ăn kẹo mút vậy thôi
đang tính viết xong cái SQL Server xong thì lập 1 thớt mới hướng dẫn các bạn GPE cách dùng cho Officex64 đấy
Trong khi chờ đợi hãy thử mò đi xem sao nha ...
mà qua Delphi đi nó hay hơn VB6 nhiều mà còn luyến tiếc chi huyền thoại vb6 1 thời vang bóng ???!!!
 
Upvote 0
Đây là video hướng dẫn cách tạo DLL trên Delphi nhưng sử dụng trong VBA, Excel. Đây là phương pháp lập trình chuyên nghiệp nhưng không phải là khó. Giúp bạn bảo mật code của phần mềm tốt hơn.
em chào thầy, đầu tiên em chúc thầy có một buổi tối vui vẻ bên gia đình và người thân! thầy cho em hỏi vài chỗ nhé thầy
Ý 1: em mới bập bọe với Delphi, hôm nay ngồi làm theo clip tại bài 1 của thầy và gõ theo như này
1599309643631.png
Sau khi Build ra thư mục này
1599309690488.png
và em copy MyDLL.dll vào thư mục
1599309729614.png
Sau đó em vào Excel và khai báo như này
1599309766425.png
Trên Sheet em gõ như này và ra kết quả như này! vậy em làm sai chỗ nào mà không ra được kết quả ạ
1599309822155.png1599309834699.png
Mong sớm nhận được trợ giúp từ thầy hoặc các bạn, các anh, các chị nào biết chỉ em với. cái này còn không được sao em quậy banh chành Excel đc nữa. hu....hu.....

Ý 2:
em có lưu file Excel tại đường dẫn này
1599310052081.png
em được một sư huynh chỉ code như này để load MyDLL.dll vào SysWOW64 luôn, không cần copy thủ công nữa
1599310161711.png
nhưng khi chạy Auto_Open thì lỗi
1599310206904.png
 
Upvote 0
em chào thầy, đầu tiên em chúc thầy có một buổi tối vui vẻ bên gia đình và người thân! thầy cho em hỏi vài chỗ nhé thầy
Ý 1: em mới bập bọe với Delphi, hôm nay ngồi làm theo clip tại bài 1 của thầy và gõ theo như này
View attachment 244921
Sau khi Build ra thư mục này
View attachment 244922
và em copy MyDLL.dll vào thư mục
View attachment 244923
Sau đó em vào Excel và khai báo như này
View attachment 244924
Trên Sheet em gõ như này và ra kết quả như này! vậy em làm sai chỗ nào mà không ra được kết quả ạ
View attachment 244925View attachment 244926
Mong sớm nhận được trợ giúp từ thầy hoặc các bạn, các anh, các chị nào biết chỉ em với. cái này còn không được sao em quậy banh chành Excel đc nữa. hu....hu.....

Ý 2:
em có lưu file Excel tại đường dẫn này
View attachment 244927
em được một sư huynh chỉ code như này để load MyDLL.dll vào SysWOW64 luôn, không cần copy thủ công nữa
View attachment 244928
nhưng khi chạy Auto_Open thì lỗi
View attachment 244929
tham khảo xem sao nhé

Thử xem
Mã:
#If VBA7 And Win64 Then
    Declare PtrSafe Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Double, ByVal b As Double) As Double
#Else
    Rem Declare Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Integer, ByVal b As Integer) As Integer
#End If


Public Function MySum(ByVal x As Double, ByVal y As Double) As Double
    MySum = GetSumDLL(x, y)
End Function
1/ MySum
Capture.PNG

2/ GetSumDLL
Capture2.PNG
 
Lần chỉnh sửa cuối:
Upvote 0
tham khảo xem sao nhé

Thử xem
Mã:
#If VBA7 And Win64 Then
    Declare PtrSafe Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Double, ByVal b As Double) As Double
#Else
    Rem Declare Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Integer, ByVal b As Integer) As Integer
#End If


Public Function MySum(ByVal x As Double, ByVal y As Double) As Double
    MySum = GetSumDLL(x, y)
End Function
1/ MySum
View attachment 244939

2/ GetSumDLL
View attachment 244940

Bạn bỉ “And Win64” đi nhé.
Bài đã được tự động gộp:

em chào thầy, đầu tiên em chúc thầy có một buổi tối vui vẻ bên gia đình và người thân! thầy cho em hỏi vài chỗ nhé thầy
Ý 1: em mới bập bọe với Delphi, hôm nay ngồi làm theo clip tại bài 1 của thầy và gõ theo như này
View attachment 244921
Sau khi Build ra thư mục này
View attachment 244922
và em copy MyDLL.dll vào thư mục
View attachment 244923
Sau đó em vào Excel và khai báo như này
View attachment 244924
Trên Sheet em gõ như này và ra kết quả như này! vậy em làm sai chỗ nào mà không ra được kết quả ạ
View attachment 244925View attachment 244926
Mong sớm nhận được trợ giúp từ thầy hoặc các bạn, các anh, các chị nào biết chỉ em với. cái này còn không được sao em quậy banh chành Excel đc nữa. hu....hu.....

Ý 2:
em có lưu file Excel tại đường dẫn này
View attachment 244927
em được một sư huynh chỉ code như này để load MyDLL.dll vào SysWOW64 luôn, không cần copy thủ công nữa
View attachment 244928
nhưng khi chạy Auto_Open thì lỗi
View attachment 244929

LoadLibrary trả về số Long 8 byte trên Office 64-bit vù thế bạn cần đổi Long thành LongPtr, dùng kỹ thuật #If VBA7 Then để điều hướng VBA với các phiên bản Office. Về ý tưởng của bạn “không cần copy bằng tay” là sai.
 
Lần chỉnh sửa cuối:
Upvote 0
tham khảo xem sao nhé

Thử xem
Mã:
#If VBA7 And Win64 Then
    Declare PtrSafe Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Double, ByVal b As Double) As Double
#Else
    Rem Declare Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Integer, ByVal b As Integer) As Integer
#End If


Public Function MySum(ByVal x As Double, ByVal y As Double) As Double
    MySum = GetSumDLL(x, y)
End Function
1/ MySum
View attachment 244939

2/ GetSumDLL
View attachment 244940
Nhưng trong clip đâu cần thông qua hàm trung gian như vậy a.
 
Upvote 0
Bạn bỉ “And Win64” đi nhé.
Bài đã được tự động gộp:



LoadLibrary trả về số Long 8 bit trên Office 64-bit vù thế bạn cần đổi Long thành LongPtr, dùng kỹ thuật #If VBA7 Then để điều hướng VBA với các phiên bản Office. Về ý tưởng của bạn “không cần copy bằng tay” là sai.
Cho mình hỏi chút là sao khi mình thay dổi đối Số Hàm Mysum trên Cells A1 thì Excel nó đơ xong thoát luôn
1599366698629.png
 
Upvote 0
Nhưng trong clip đâu cần thông qua hàm trung gian như vậy a.

Tôi có bói gì đến LoadLibrary đâu. Cả link bạn trích dẫn cũng không liên quan gì.
Bài đã được tự động gộp:

Cho mình hỏi chút là sao khi mình thay dổi đối Số Hàm Mysum trên Cells A1 thì Excel nó đơ xong thoát luôn
View attachment 244942

Bạn mở code delphi và khai báo trên VBA xem nào.
 
Upvote 0
Bạn mở code delphi và khai báo trên VBA xem nào.
Code Trên Delphi
Mã:
library MyDLL;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  System.SysUtils,
  System.Classes;

{$R *.res}

Function GetSumDLL(a, b: double): double; stdcall;
begin
  Result := a + b;
end;

exports
  GetSumDLL;

begin

end.

Code Trên VBA
Mã:
Declare PtrSafe Function GetSumDLL Lib "MyDLL.dll" (ByVal a As Double, ByVal b As Double) As Double

Public Function MySum(ByVal x As Double, ByVal y As Double) As Double
    MySum = GetSumDLL(x, y)
End Function
 
Upvote 0
Lạ nhỉ em đọc từ bài đầu thì thấy bác tham gia từ năm 2018, thấy bác viết Api, dlll, ocx clien server sql gì tùm lum mà sao học lại cái này nhỉ ===\.
 
Upvote 0
Tôi có bói gì đến LoadLibrary đâu. Cả link bạn trích dẫn cũng không liên quan gì.
Bài đã được tự động gộp:
em không nói là LoadLibrary của thầy ạ. cái đó em hỏi thêm thôi.
theo như trên clip thì máy thầy dùng office 32bit thì phải
còn em dùng office 64 bit, nên em build ra file dll bit và chép vào C:\Windows\SysWOW64
code delphi thế này
Mã:
library MyDLL;
uses
  System.SysUtils,
  System.Classes;
Function GetSum(a, b: Integer): Integer; stdcall;
begin
  Result := a + b;
end;
exports
  GetSum;
begin
end.
trên vba e khai báo thế này
Mã:
#If VBA7 And Win64 Then
    Declare PtrSafe Function GetSum Lib "MyDLL.dll" (ByVal a As Integer, _
                                                    ByVal b As Integer) As Integer
#Else
    Declare Function GetSum Lib "MyDLL.dll" (ByVal a As Integer, _
                                            ByVal b As Integer) As Integer
#End If


Public Function MySum(ByVal x As Integer, ByVal y As Integer) As Integer
    MySum = GetSum(x, y)
End Function
thì kết quả ở E4, E5 theo công thức bên cạnh thì ra 18448
còn khi thay Integer thành Double thì
E3 =getsum(E1;E2) = 0 -> sai
E4=mysum(E1;E2) = 150 -> đúng
như vậy thì tốn thêm 1 hàm trung gian là MySum, trong khi trên video thầy chỉ thì dùng trực tiếp luôn.
1599386359813.png
còn code để LoadLibrary em sửa lại như chỉ dẫn của thầy Long thành LongPrt thì code chạy được, không báo lỗi, nhưng
2 chỗ này
Private Function LoadDLL() As LongPtr
Private Sub FreeDLL(hModule As LongPtr)
LongPrt liệu 32 bit có chạy được không thầy
nếu không được thì phải viết lại như thế nào thầy? có phải viết 2 hàm để Load dành riêng cho 32 bit riêng, 64 bit riêng ko thầy.
1599386629911.png
nội dung code
Mã:
#If VBA7 And Win64 Then
    Private Declare PtrSafe Function FreeLibrary Lib "kernel32" _
    (ByVal hLibModule As LongPtr) As LongPtr
    Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" _
    (ByVal lpLibFileName$) As LongPtr
#Else
    Private Declare Function FreeLibrary Lib "kernel32" _
                            (ByVal hLibModule As Long) As Long
    Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" _
                            (ByVal lpLibFileName$) As Long
#End If
Dim hModule As LongPtr
Rem 1/ Cach viet nay Ap dung Cho File *.DLL cung Folder lam don Gian khai bao su dung Ham dai dong
Rem 2/ Ap dung Cho WindowsX32: Copy Vao Windows\System32\VBLibrary.dll
Rem 3/ Ap dung Cho WindowsX64: Copy Vao Windows\SysWOW64\VBLibrary.dll
Rem 4/ Neu thuc hien Muc 2 Or 3 thi xoa het Code trong Module Nay
Private Function LoadDLL() As LongPtr
    'LoadDLL = LoadLibrary(ThisWorkbook.Path & "\Release\VBLibrary.dll")
#If VBA7 Then
    LoadDLL = LoadLibrary(ThisWorkbook.Path & "\Win64\Debug\MyDLL.dll")
#Else
    LoadDLL = LoadLibrary(ThisWorkbook.Path & "\Win32\Debug\MyDLL.dll")
#End If
End Function
Rem ==========
Private Sub FreeDLL(hModule As LongPtr)
    Do Until FreeLibrary(hModule) = 0
    Loop
End Sub
Rem ==========
Sub Auto_Open()
    Rem Load the DLL into memory
    hModule = LoadDLL()
End Sub
Rem ==========
Sub Auto_Close()
Rem Unload the DLL from memory
    Call FreeDLL(hModule)
End Sub
Rem ==========

xem mấy video thì 1 muốn hết là xuất ra exe, xong tạo thư viện dll thì em xem tới trang 9 rồi thì chỉ có mỗi video làm hàm GetSum nhưng làm trên office 32bit , mấy trang sau em chưa xem tới nên không biết có thêm video nào tạo thư viện hàm, Form trên thư viện dll để có thể gọi và thao tác trên excel không. toàn thấy bàn ADO, addins, tò mò copy vô thử thì toàn lỗi chỗ khai báo trên Delphi. nếu chỉ có mỗi 1 video tạo thư viện DLL như vậy thì khó cho người mới như em để bắt đầu quá ạ. vì đây là diễn đàn excel nên chắc mọi người muốn chương trình hoặc tiện ích họ lập ra chạy trên nền excel.
và trên hết mong có nhiều video đến từ thầy hoặc các anh chị khác hướng dẫn trên máy 64bit để em và mọi người có thể xem và đỡ mắc công thầy trả lời và giải đáp mấy lỗi trên 64bit nếu em có hỏi ạ.
 
Upvote 0

Hay đấy ??!!! ... Không ngờ có thớt này + Sự kiêm trì mò của Mạnh từng bước tiếp cận Delphi ngon lành -0-0-0-
Cảm Ơn các Bạn nhiều

1599542250983.png
 
Upvote 0
em không nói là LoadLibrary của thầy ạ. cái đó em hỏi thêm thôi.
theo như trên clip thì máy thầy dùng office 32bit thì phải
còn em dùng office 64 bit, nên em build ra file dll bit và chép vào C:\Windows\SysWOW64
code delphi thế này
Mã:
library MyDLL;
uses
  System.SysUtils,
  System.Classes;
Function GetSum(a, b: Integer): Integer; stdcall;
begin
  Result := a + b;
end;
exports
  GetSum;
begin
end.
trên vba e khai báo thế này
Mã:
#If VBA7 And Win64 Then
    Declare PtrSafe Function GetSum Lib "MyDLL.dll" (ByVal a As Integer, _
                                                    ByVal b As Integer) As Integer
#Else
    Declare Function GetSum Lib "MyDLL.dll" (ByVal a As Integer, _
                                            ByVal b As Integer) As Integer
#End If


Public Function MySum(ByVal x As Integer, ByVal y As Integer) As Integer
    MySum = GetSum(x, y)
End Function
thì kết quả ở E4, E5 theo công thức bên cạnh thì ra 18448
còn khi thay Integer thành Double thì
E3 =getsum(E1;E2) = 0 -> sai
E4=mysum(E1;E2) = 150 -> đúng
như vậy thì tốn thêm 1 hàm trung gian là MySum, trong khi trên video thầy chỉ thì dùng trực tiếp luôn.
View attachment 244957
còn code để LoadLibrary em sửa lại như chỉ dẫn của thầy Long thành LongPrt thì code chạy được, không báo lỗi, nhưng
2 chỗ này
Private Function LoadDLL() As LongPtr
Private Sub FreeDLL(hModule As LongPtr)
LongPrt liệu 32 bit có chạy được không thầy
nếu không được thì phải viết lại như thế nào thầy? có phải viết 2 hàm để Load dành riêng cho 32 bit riêng, 64 bit riêng ko thầy.
View attachment 244958
nội dung code
Mã:
#If VBA7 And Win64 Then
    Private Declare PtrSafe Function FreeLibrary Lib "kernel32" _
    (ByVal hLibModule As LongPtr) As LongPtr
    Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" _
    (ByVal lpLibFileName$) As LongPtr
#Else
    Private Declare Function FreeLibrary Lib "kernel32" _
                            (ByVal hLibModule As Long) As Long
    Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" _
                            (ByVal lpLibFileName$) As Long
#End If
Dim hModule As LongPtr
Rem 1/ Cach viet nay Ap dung Cho File *.DLL cung Folder lam don Gian khai bao su dung Ham dai dong
Rem 2/ Ap dung Cho WindowsX32: Copy Vao Windows\System32\VBLibrary.dll
Rem 3/ Ap dung Cho WindowsX64: Copy Vao Windows\SysWOW64\VBLibrary.dll
Rem 4/ Neu thuc hien Muc 2 Or 3 thi xoa het Code trong Module Nay
Private Function LoadDLL() As LongPtr
    'LoadDLL = LoadLibrary(ThisWorkbook.Path & "\Release\VBLibrary.dll")
#If VBA7 Then
    LoadDLL = LoadLibrary(ThisWorkbook.Path & "\Win64\Debug\MyDLL.dll")
#Else
    LoadDLL = LoadLibrary(ThisWorkbook.Path & "\Win32\Debug\MyDLL.dll")
#End If
End Function
Rem ==========
Private Sub FreeDLL(hModule As LongPtr)
    Do Until FreeLibrary(hModule) = 0
    Loop
End Sub
Rem ==========
Sub Auto_Open()
    Rem Load the DLL into memory
    hModule = LoadDLL()
End Sub
Rem ==========
Sub Auto_Close()
Rem Unload the DLL from memory
    Call FreeDLL(hModule)
End Sub
Rem ==========

xem mấy video thì 1 muốn hết là xuất ra exe, xong tạo thư viện dll thì em xem tới trang 9 rồi thì chỉ có mỗi video làm hàm GetSum nhưng làm trên office 32bit , mấy trang sau em chưa xem tới nên không biết có thêm video nào tạo thư viện hàm, Form trên thư viện dll để có thể gọi và thao tác trên excel không. toàn thấy bàn ADO, addins, tò mò copy vô thử thì toàn lỗi chỗ khai báo trên Delphi. nếu chỉ có mỗi 1 video tạo thư viện DLL như vậy thì khó cho người mới như em để bắt đầu quá ạ. vì đây là diễn đàn excel nên chắc mọi người muốn chương trình hoặc tiện ích họ lập ra chạy trên nền excel.
và trên hết mong có nhiều video đến từ thầy hoặc các anh chị khác hướng dẫn trên máy 64bit để em và mọi người có thể xem và đỡ mắc công thầy trả lời và giải đáp mấy lỗi trên 64bit nếu em có hỏi ạ.

Bạn đang học thì nên theo các ví dụ đơn giản chứ đừng động vào mấy cái LoadLibrary. Những hàm này phát huy tốt trong Delphi vì có cách lấy địa chỉ hàm và gọi theo kiểu hàm khai báo. Trong VBA không cho ép kiểu nên đồng động vào LoadLibrary hay GetAddressProc là rắc rối lắm mà không hề cần thiết. Vấn đề lỗi 64 bit bạn gặp phải thì phải có ví dụ code của bạn cụ thể và đừng động vào mấy thứ phức tạp khi mới học.
 
Upvote 0
Bạn đang học thì nên theo các ví dụ đơn giản chứ đừng động vào mấy cái LoadLibrary. Những hàm này phát huy tốt trong Delphi vì có cách lấy địa chỉ hàm và gọi theo kiểu hàm khai báo. Trong VBA không cho ép kiểu nên đồng động vào LoadLibrary hay GetAddressProc là rắc rối lắm mà không hề cần thiết. Vấn đề lỗi 64 bit bạn gặp phải thì phải có ví dụ code của bạn cụ thể và đừng động vào mấy thứ phức tạp khi mới học.
em cảm ơn thầy
giờ em ko dùng LoadLibrary nữa mà cho địa chỉ file thẳng vào khai báo luôn đỡ phải chép file nữa.
vậy còn hàm MySum thế nào vậy thầy! em có thử trên 32 bit thì ra kết quả đúng, còn trên 64 bit em khai báo như trên thì ra kết quả sai, phải thông qua 1 bước trung gian trên VBA thì mới ra kết quả đúng. như thế trên 64 bit thiệt quá.
 
Upvote 0
Vấn đề hàm API sử dụng khi nó lhai báo trực tiếp bằng declare trong Module sẽ lỗi trong Excel 64 bit. Bạn chỉ có cáh là viết lại khai báo cấu trúc hàm dưới dạng hàm VbA, trong đó bạn sử dụng hàm declare từ DLL. lỗi chỉ là dùng hàm khai báo Declare như một UDF (hàm dùng trên worksheet). Vấn đề này là giới hạn của Excel 64 bit. Các ngôn ngữ lập trình như Delphi, Visual C/C++ gặp lỗi như nhau. Cách đây mấy năm mình đã report vấn đề này tới Microsoft và họ không có cách nào khác.
 
Lần chỉnh sửa cuối:
Upvote 0
Vấn đề hàm API sử dụng khi nó lhai báo trực tiếp bằng declare trong Module sẽ lỗi trong Excel 64 bit. Bạn chỉ có cáh là viết lại khai báo cấu trúc hàm dươia dạng hàm VbA, trong đó bạn sử dụng hàm declare từ DLL. lỗi chỉ là dùng hàm Declare mhuw một UDF (hàm dùng trên worksheet). Vấn đề này là giới hạn của Excel 64 bit. Các mgoon ngưc lầm trình như Delphi, Visual C/C++ gặp lỗi như nhau. Cacha đây máy năm mình đã report tới Microsoft và họ không có cách nào khác.
Mạnh mới thử cái đó của bạn trên Windows10x64 + Office2016x64 cũng bị vậy
thôi xác định sống chung với lũ thôi hạn chế xài hàm API trên Cells cho Officex64.... nếu xài thì lách qua hàm trung gian của VBA vậy

1599571439333.png
 
Upvote 0
Úp lên đây cho các Bạn yêu thích Delphi Viết COM Add-ins cho Excel

có thể ứng dụng nó viết DLL API riêng và COM Addins riêng xong liên kết nó lại ====> lách Excel ngăn cản gõ sự kiện trên Form nhúng trên COM Add-Ins

(Code này là của tây đó ko phải của Mạnh he )

Nội dung tây nó viết như sau
Modeless form cannot receive key input in Excel Add-in developed by Delphi
To solve this problem, start another UI thread and run message loop in it is needed.

Steps:
1.Compile MultipleUIThreadsinVCLGroup.groupproj
2.Open cmd as Administrator,use regsvr32 MultipleUIThreadsinVCL.DLL to register the Excel Add-in
3.Open Excel

This solves the following issue:
1. Modeless form cannot receive user input. If start message loop in VCL main thread, form can receive key input but all async function in Excel will be broken.
2.When click Excel work sheet, modeless form doesn't lose focus.
3.When modeless form is open, close Excel will cause crash, and then ask user whether to disable the add-in at next start of Excel.
4.Many other message related problems in Excel add-in.
5.gif
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Delphi hổ trợ thấy cũng nhiều thứ quá tốt

Học Delphi Mạnh thấy cái hay của nó là có thể chia Sẻ mã nguồn cho người khác xài mà vẫn bảo vệ tốt mã nguồn của mình

đó là gửi File *.dcu cho người nhận chia sẻ code -0-0-0-

1599881242181.png
 
Lần chỉnh sửa cuối:
Upvote 0
Delphi hổ trợ thấy cũng nhiều thứ quá tốt

Học Delphi Mạnh thấy cái hay của nó là có thể chia Sẻ mã nguồn cho người khác xài mà vẫn bảo vệ tốt mã nguồn của mình

đó là gửi File *.dcu cho người nhận chia sẻ code -0-0-0-

View attachment 245419
Về chia sẻ DCU thì cũng không phải là nhẹ nhàng, thú vị hoàn toàn. DCU cũng có phiên bản (phiên bản compiler). Nếu người dùng có phiên bản khác(phiên bản Delphi - compiler) thì không dùng được.
 
Upvote 0
Upvote 0
Không biết mấy tay nhà báo nó viết vậy có khách quan không nhỉ ?????!!!

View attachment 245433


Theo tôi bỏ đi là được rồi. Học không có tính thực tế.
Muốn dạy thuật toán, tư duy lập trình... thì Python, C++ là quá dữ rồi và tính thực tiễn áp dụng cao.
 
Upvote 0
Theo tôi bỏ đi là được rồi. Học không có tính thực tế.
Muốn dạy thuật toán, tư duy lập trình... thì Python, C++ là quá dữ rồi và tính thực tiễn áp dụng cao.
vậy việc Học Delphi ta có nên bỏ không nhỉ :p
mà Delphi có nguồn gốc từ Pascal
 
Upvote 0
vậy việc Học Delphi ta có nên bỏ không nhỉ :p
mà Delphi có nguồn gốc từ Pascal
Thực ra người ta nói về Pascal, cũ lắm rồi. Delphi không phải là ngôn ngữ, Delphi là MÔI TRƯỜNG lập trình. Ngôn ngữ được dùng là Object Pascal. Pascal và Object Pascal là khác nhau.
 
Upvote 0
oh mà chưa học pascal ngày nào luôn ta ... mấy sư phụ keo nên học lại căn bản pascal ... giờ tính sao đây ???
Nên học pascal cơ bản , vì delphi có ngôn ngữ phần code đơn giản giống hệt pascal

Còn Delphi nên hay không nên thì tùy, nhưng Delphi vẫn kiểu nửa nọ nửa kia vẫn 1 bước nữa tới hoàn toàn hướng đối tượng

Còn phổ thông bỏ pascal là đúng rồi, nhưng học Python hay C++ cũng đều không nên ..... học gì thì xem thế giới họ dạy gì cho học sinh , khác xa ta .... cứ cái gì người lớn có là đem dạy ... hay cứ người lớn nghĩ là đúng .....
 
Upvote 0
Pascl, Python, C++ gì gì đó thì tôi không biết có nên dạy hay không.
Nhưng hướng đối tượng thì hơi khó dạy. HĐT cần tư duy khác hẳn. Số người VN có khả năng tư duy này không nhiều. Số giáo viên có khả năng này lại càng hiếm.

Vào mấy diễn đàn chuyên Java, C# hay C++ sẽ thấy mấy bài tập LTHĐT cứng như que củi, quanh quẩn ba cái ví dụ lớp này lớp nọ. Cái lớp có thể phức tạp kinh khủng nhưng tinh thần HĐT là zê-rô.
Không hề thấy thầy/cô nào dạy học sinh nhìn vào môi trường vấn đề như một khung cảnh mà trong đó có nhiều vật thể tương tác với nhau. Không hề thấy một bài tập nào cho phép học sinh tự do thiết kế các lớp của mình.

Điển hình là xem tất cả các bài tập về Excel mà học sinh đem lên GPE hỏi. Chỉ toàn là lập công thức này công thức nọ chứ chả thấy bài tập nào hỏi cách thiết lập một bảng tính trải rộng hay cách đọc dữ liệu trong bảng tính cả.
 
Upvote 0
Không biết ai như mình không. Chọn Delphi là ngôn ngữ chính từ năm 2007, đi ngược với xu thế lúc đó trào lưu sang .net. :D
 
Upvote 0
Pascl, Python, C++ gì gì đó thì tôi không biết có nên dạy hay không.
Nhưng hướng đối tượng thì hơi khó dạy. HĐT cần tư duy khác hẳn. Số người VN có khả năng tư duy này không nhiều. Số giáo viên có khả năng này lại càng hiếm.

Vào mấy diễn đàn chuyên Java, C# hay C++ sẽ thấy mấy bài tập LTHĐT cứng như que củi, quanh quẩn ba cái ví dụ lớp này lớp nọ. Cái lớp có thể phức tạp kinh khủng nhưng tinh thần HĐT là zê-rô.
Không hề thấy thầy/cô nào dạy học sinh nhìn vào môi trường vấn đề như một khung cảnh mà trong đó có nhiều vật thể tương tác với nhau. Không hề thấy một bài tập nào cho phép học sinh tự do thiết kế các lớp của mình.

Điển hình là xem tất cả các bài tập về Excel mà học sinh đem lên GPE hỏi. Chỉ toàn là lập công thức này công thức nọ chứ chả thấy bài tập nào hỏi cách thiết lập một bảng tính trải rộng hay cách đọc dữ liệu trong bảng tính cả.

Vì đều ăn xổi ở thì, làm nhanh ăn nhanh, có thừa kế hay lặp lại code đáng đâu.
Còn Excel ở đây cũng thế, hỏi 1 lần
If của các Ngôn ngữ lập trình khác nhau
Mới nhìn thấy khó ... nhìn diết cảm giác nó gần giống nhau -0-0-0-
Mấy cấu trúc
- IF, IF THEN ELSE..., IF ... THEN ELSEIF THEN ... ELSE
- FOR / WHILE / REPEAT UNTIL , LOOP .... DO

thì là cơ bản của cơ bản của hầu hết các ngôn ngữ lập trình rồi.
 
Upvote 0
Các Bạn cho Mình hỏi chút

Mình đang tập làm cái Add-Ins COM trên Delphi cho Excel ( thực ra lâu nay là xài đồ có sẳn của người ta tải về viết lại + thêm vào )
Nay rảnh đang tự mò làm lấy từ A To Z vậy mình muốn hỏi là

1/ khi mình Import cái thư viện tạm keo là của Bill thì Delphi tạo ra File AddInDesignerObjects_TLB.pas
2/ khi mục số 1 thực hiện xong mình có thêm các Unit và thử viết thì thấy chạy ok
3/ Tuy nhiên Mình thắc mắc là sao file AddInDesignerObjects_TLB.pas nó báo lỗi code một vài chỗ có hình kèm theo mà vẫn chạy tốt

Tóm lại lỗi đó là gì và tại sao khi Build DLL ko thấy báo lỗi mà vẫn sử dụng rất tốt ??????????!!!!!!!!!!!!!!

COM.png
 
Upvote 0
rảnh đang mò viết cái hàm CompactRepair API tìm diết ở mô xa .. thì ra trong Delphi nó có hết rồi chỉ việc gắp ra xài -0-0-0-
 
Upvote 0
Anh @batman1 Chỉ Em học và cách sử dụng thư viện *.dll đã Component\Import Component... Với

Em mô tả như sau:

1/ khi em viết cái thư viện NetworkTCPIP.dll trên VB6 xong em sử dụng rất tốt ..
2/ Em thử mở Delphi lên xong Component\Import Component... ( NetworkTCPIP.dll ---> vb6 ) vào thì Delphi nó tạo ra 2 file như Em úp lên đây
3/ vậy khi Em viết API DLL trên Delphi thì Em khai báo sử dụng 2 cái hàm trong Unit NetworkTCPIP_TLB.pas như thế nào ??

Cụ thể 2 hàm đó như sau:

Mã:
function Connect(var IP: WideString; var Port: Integer; var SQL: WideString): _Recordset; safecall;
    function ConnectDataName(var IP: WideString; var Port: Integer; var FileName: WideString;
                             var SQL: WideString): _Recordset; safecall;

4/ 2 Hàm đó Em viết ngay trên Delphi thì OK rồi đó ... vấn đề là em muốn học cách sử dụng Import Component thành các Unit mà xài cho Delphi
xem có sử dụng được hay không. Vì trên Internet em có thấy họ chia sẻ nhiều DLL viết bằng vb6 rất hay mà kẹt là code họ không có cho nên em thử vận dụng kiểu Import Component xem nó như thế nào đó mà :p :p

5/ cách này em hoàn toàn chưa biết gì luôn nếu được Anh chỉ và hướng dẫn cho Em học với có 1 VD mẫu thì với em coi như là quá ok rồi đó. xong từ đó em có thể vận dụng DLL = vb6 Import Component qua Delphi các kiểu rồi đấy hoặc Delphi To Delphi :p

Em cảm ơn
 

File đính kèm

Upvote 0
Anh @batman1 Chỉ Em học và cách sử dụng thư viện *.dll đã Component\Import Component... Với
Bạn có vấn đề thì cứ nhờ tất cả mọi người giúp đỡ thôi chứ sao lại gọi tôi lên bảng?

Nếu nói về Import thì phải là Project -> Import Type Library chứ nhỉ. Tôi không chơi Delphi cũng cỡ 15 năm rồi nên có thể không còn nhớ nhiều. Nhưng chắc chắn Delphi 5 không có Component\Import Component... Nếu có thì có lẽ là Component\Import ActiveX ...
1/ khi em viết cái thư viện NetworkTCPIP.dll trên VB6 xong em sử dụng rất tốt ..
Có nghĩa là thế nào? Cụ thể cái rất tốt ấy có nghĩa là gì, code tuần tự như thế nào? Tôi đã nói với bạn rất nhiều lần là tôi thường không có hứng trả lời bạn vì bạn viết cụt lủn.
2/ Em thử mở Delphi lên xong Component\Import Component... ( NetworkTCPIP.dll ---> vb6 ) vào thì Delphi nó tạo ra 2 file như Em úp lên đây
3/ vậy khi Em viết API DLL trên Delphi thì Em khai báo sử dụng 2 cái hàm trong Unit NetworkTCPIP_TLB.pas như thế nào ??

Cụ thể 2 hàm đó như sau:

Mã:
function Connect(var IP: WideString; var Port: Integer; var SQL: WideString): _Recordset; safecall;
    function ConnectDataName(var IP: WideString; var Port: Integer; var FileName: WideString;
                             var SQL: WideString): _Recordset; safecall;

4/ 2 Hàm đó Em viết ngay trên Delphi thì OK rồi đó ...
Tôi không hiểu bạn nói gì. "2 Hàm đó Em viết ngay trên Delphi thì OK rồi đó" có nghĩa là gì?

Khi bạn đã import type library, và bạn có NetworkTCPIP_TLB.pas thì 2 hàm "kia" nó xuất hiện như 2 phương thức của interface Network trong NetworkTCPIP_TLB.pas rồi còn gì.

Theo tôi nghĩ thì trong system trước đó server NetworkTCPIP.dll đã phải được đăng ký. Sau đó trong Delphi phải gọi CreateComObject để tạo 1 instance của đối tượng COM. Khi đã có interface Network rồi thì gọi phương thức Connect và ConnectDataName của nó thôi.

Tôi nghĩ tất cả là như thế. Nhưng tôi bỏ gần 15 năm rồi nên có thể không còn nhớ nhiều.
 
Upvote 0
1/ trước đây viết VCL.tyles cho DLL là 1 cực hình với người đang tập Delphi ... thì nay trên Delphi 10.4 nó hổ trợ chọn vẹn luôn chỉ khai báo và xài thôi

2/ trước đây viết SQLite trên Delphi phải chuyển nó qua cả ngàn dòng code + SQLite3.dll ... thì nay từ Delphi 10.3 nó có hết rồi như ADO trên Windows vậy chỉ khai báo và lôi nó ra xài thôi vv...

Quá khó .............. mà củng không quá dễ khi vượt cái cái ngưỡng ban đầu nhập môn Delphi

AddIns SQLite cho Excel đang hình thành ... vậy là trong thời gian tới sẻ có 1 cái Addins cho Excel bao gồm:

SQL Excel + Access + Sqlite + SQL Server Ms + SQL TCP/IP ( SQL TCP/IP = Builded SQL và lấy dữ liệu qua Internet )

Hình sau là SQLite Build đang hình thành Viết tìm kiếm File kiểu Explorer folder

Chọn ổ dĩa ===> Show list Files === > chọn File ===> chọn tableName ===> chọn Build SQL ===> thưởng thức

1618029280722.png
 
Lần chỉnh sửa cuối:
Upvote 0
Tôi vẫn đang nghiên cứu C#, viết Add-Ins cũng thấy OK. Hiện tại vẫn phát triển tốt, hiện tại là tự mò nhưng cũng cảm thấy hiểu nhiều về nó rồi. Hầu như là những cái tiện ích VNTools của tôi hoàn toàn chuyển được sang C# rồi. Thấy ái Delphi này ít tài liệu nghiên cứu quá nên chạy mất dép, nhưng những bài viết trong chủ đề này vẫn tò mò muốn xem, thấy bác mò dữ quá.
Xin phép đào mộ xíu, tìm cái vnTool của bác thấy bài viết này. Bác dùng gì để làm addin bằng C# vậy? Mình cũng đang ngâm cứu cái này
 
Upvote 0
Xin phép đào mộ xíu, tìm cái vnTool của bác thấy bài viết này. Bác dùng gì để làm addin bằng C# vậy? Mình cũng đang ngâm cứu cái này
Mình dùng Visual Studio 2019 để viết Add-ins sử dụng thư viện Exceldna, nhưng đã lâu rồi không tiếp dự án, mấy hôm nay rảnh định tiếp tục dự án. Bạn có hứng thú thì có thể lập chủ đề thảo luận trao đổi, tôi sẽ chia sẻ kiến thức nếu biết. Còn đây là giao diện dự án đang bỏ dở.
Hinh.png
Hình giao diện Ribbon TIỆN ÍCH VIỆT hoàn toàn tạo trên C# hết đấy nhé.
 
Upvote 0
mạnh phại khai báo dong này
procedure adxtShowPrintSheet;
phía trên var nữa
Mã:
procedure adxtShowPrintSheet;
var
  Form1: TForm1;
  f: TForm1;
  implementation

{$R *.dfm}
procedure adxtShowPrintSheet;
begin
  f := TForm1.Create(nil);
  try
    f.Show; //.ShowModal;
  finally

  end;
end;
Bạn cho mình hỏi nguyên nhân là mình làm theo bạn hướng dẫn bên trên nhưng:
1. Xài cho office 2019 thì nó chạy ok
2. Xài cho Office 365 thì khi chạy nó lại báo "File not Found" cái MyDLL.dll chép vào 'C:\Windows\SysWOW64'
Cảm ơn bạn !
 
Upvote 0
Bạn cho mình hỏi nguyên nhân là mình làm theo bạn hướng dẫn bên trên nhưng:
1. Xài cho office 2019 thì nó chạy ok
2. Xài cho Office 365 thì khi chạy nó lại báo "File not Found" cái MyDLL.dll chép vào 'C:\Windows\SysWOW64'
Cảm ơn bạn !
Nếu bạn viết thông thường + sử dụng các thư viên có liên quan tới Windows có sẳn thì chạy tốt
Còn viết phức tạp nó liên quan tới nhiều thư viện khác nhau thì nó sẻ không chạy được vì thiếu 1 trong số thư viện đó

có rất nhiều nguyên nhân sinh ra nó ... và người viết code có cái nhìn tổng thể trong toàn bộ project của mình thì mới có thể tìm ra

Tôi cũng bị do trên máy khác cũng windows10 mà thiếu 1 thư viện .... tìm copy vào là xong
 
Upvote 0
Trên windows 32 bit thì dll lưu vào system32. Trên windows 64 bit, các dll 64 bit cũng lưu vào system32, chắc để tương thích ngược với phiên bản cũ còn dll 32 bit lưu vào SysWOW64 (windows on windows 64).
 
Upvote 0
Trên windows 32 bit thì dll lưu vào system32. Trên windows 64 bit, các dll 64 bit cũng lưu vào system32, chắc để tương thích ngược với phiên bản cũ còn dll 32 bit lưu vào SysWOW64 (windows on windows 64).
Tây nó nói tiếng Anh ... Google nó dịch ra như sau

GHI CHÚ!
Trên hệ điều hành Windows có kiến trúc 64 Bit,
tệp dll phải nằm trong cả thư mục " sysWOW64 "
cũng như thư mục " System32 ". Nói cách khác,
bạn phải sao chép tệp .dll vào cả hai thư mục.
 
Upvote 0
Tây nó nói tiếng Anh ... Google nó dịch ra như sau

GHI CHÚ!
Trên hệ điều hành Windows có kiến trúc 64 Bit,
tệp dll phải nằm trong cả thư mục " sysWOW64 "
cũng như thư mục " System32 ". Nói cách khác,
bạn phải sao chép tệp .dll vào cả hai thư mục.
Không biết bạn đọc ở trang nào còn tất cả các trang mình đọc đều nói system32 dành cho dll64 còn SysWOW64 dành cho dll32. Vì không thấy trang của MS nên mình không đưa. Do dll của bạn đã đăng ký nên windows đã biết đường dẫn để nạp nên có thể không lỗi. Tuy nhiên windows không nạp cả 2 dll ở 2 folder cùng lúc.
 
Upvote 0
Các bạn tạo DLL, OCX hãy lưu ý platforms của Windows và Office. Chứ không phải chỉ xem xét mỗi Windows. Tôi đã phân phối sản phẩm DLL, OCX nhiều năm cả tây và ta nên những hướng dẫn tôi viết ra đây mọi người đều làm theo và chạy được: http://atoolspro.com/install-activex-controls-bsac-manually.html
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn cho mình hỏi nguyên nhân là mình làm theo bạn hướng dẫn bên trên nhưng:
1. Xài cho office 2019 thì nó chạy ok
2. Xài cho Office 365 thì khi chạy nó lại báo "File not Found" cái MyDLL.dll chép vào 'C:\Windows\SysWOW64'
Cảm ơn bạn !
Bạn làm theo bài #1258 là chính xác
 
Upvote 0
Thời gian vừa qua có 1 số bạn gửi mail + nhắn tin zalo + Facebook cho tôi nhưng vấn đề có liên quan tới Delphi

Bạn nào yêu thích Delphi ==> xin mời vào đây ta trao đổi bàn luận tiếp
Học thì nên có bạn Buôn bán thì gì đó ... Không nên dấu dốt, ko biết thì hỏi là bình thường ???!!!!!!!!!!!!

nay Tôi đăng nhập lại nick này có gì trao đổi bàn luận tại đây tiếp Hoăc lập chủ đề mới thì tùy
 
Upvote 0
Bạn làm theo bài #1258 là chính xác
mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !
 
Upvote 0
mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !

Có lẽ lỗi của DLL A gọi DLL B. Tác giả phải chỉnh lại mã nguồn tring DLL mới ổn.
Bài đã được tự động gộp:

mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !

Có lẽ lỗi của DLL A gọi DLL B. Tác giả phải chỉnh lại mã nguồn tring DLL mới ổn.
 
Upvote 0
mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !
Hi bạn cứ hỏi trong này anh tuân và các anh khác sẽ hướng dẫn cho bạn. còn mình delphi cũng chỉ biết chút ích ah.
Bạn nên xem lại source library của bạn xem có sai chỗ nào không. Mới đầu tập viết thì viết cái đơn giản thôi, gọi form gọi hàm..., từ DLL khác không khó đâu.
 
Upvote 0
Chị Thủy yêu chỉ em học Đeo Phai với
 
Upvote 0
Nhờ ACE giúp mình tối ưu đoạn code tính tổng 1 vùng trong excel, mình thử test tốc độ với mảng 11 cột và 500 ngàn dòng thì VBA chạy 0,8s còn delphi thì 1,1s, mình nghĩ do chưa tối ưu được mảng truyền vào, đọc chủ đề này thấy anh Nguyễn Duy Tuân có nói phải dùng PSafeArray.
- Code duyệt mảng và tính tổng trong VBA mình viết như sau:
Mã:
Sub TestTinhTong2()
    Dim lr As Long, arr As Variant, tong As Double, i As Long, j As Long, t As Single
    t = Timer
    With Sheet1
        lr = .Range("A1000000").End(3).Row
        arr = .Range("A1:K" & lr).value
    End With
    For i = 1 To UBound(arr)
        For j = 1 To UBound(arr, 2)
            tong = tong + arr(i, j)
        Next j
    Next i
    MsgBox tong, , Timer - t
End Sub
- Code delphi
Mã:
function GetSumRange(arr:Variant):Double;stdcall;
 var
  i,j:uint32;
  iSum:Double;
 begin
  iSum:=0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
         iSum:=iSum + arr[i,j];
    end;
  Result:=iSum;
 end;
 exports
  GetSumRange;
- Code gọi hàm trong VBA
Mã:
Declare Function GetSumRange Lib "DLLSum.dll" (ByVal arr As Variant) As Double
Sub TestTinhTong()
    Dim t As Single, lr As Long, arr As Variant
    t = Timer
    With Sheet1
        lr = .Range("A1000000").End(3).Row
        arr = .Range("A1:K" & lr).value
    End With
    MsgBox GetSumRange(arr), , Timer - t
End Sub
 
Upvote 0
Nhờ ACE giúp mình tối ưu đoạn code tính tổng 1 vùng trong excel, mình thử test tốc độ với mảng 11 cột và 500 ngàn dòng thì VBA chạy 0,8s còn delphi thì 1,1s, mình nghĩ do chưa tối ưu được mảng truyền vào, đọc chủ đề này thấy anh Nguyễn Duy Tuân có nói phải dùng PSafeArray.

- Code delphi
Mã:
function GetSumRange(arr:Variant):Double;stdcall;
 var
  i,j:uint32;
  iSum:Double;
 begin
  iSum:=0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
         iSum:=iSum + arr[i,j];
    end;
  Result:=iSum;
 end;

Em thử sửa lại như thế này xem nhanh hơn không? Anh chưa test nhé.
Mã:
function GetSumRange(var arr: Variant): Double; stdcall;
 var
  i,j: uint32;
  iSum: Double;
 begin
  iSum := 0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
         iSum := iSum + TVarData(arr[i,j]).VDouble;
    end;
  Result := iSum;
 end;

Kiểu khai báo tham số của hàm anh thêm "var", trong VBA em phải để ByRef như sau.
Mã:
Declare Function GetSumRange Lib "DLLSum.dll" (ByRef arr As Variant) As Double
 
Upvote 0
Kiểu khai báo tham số của hàm anh thêm "var", trong VBA em phải để ByRef như sau.
Mã:
Declare Function GetSumRange Lib "DLLSum.dll" (ByRef arr As Variant) As Double
tốc độ giờ là 0,7s anh, đã nhanh hơn code vba 0,1s, anh cho em hỏi TVarData là 1 record và Vdouble là 1 tham số hình thức, Khi TVarData gọi phương thức thì phải .varDouble chứ a, và tại sao trong vba mình phải dùng Byref ạ ( e thử để byval thì văng app)
 
Upvote 0
tốc độ giờ là 0,7s anh, đã nhanh hơn code vba 0,1s, anh cho em hỏi TVarData là 1 record và Vdouble là 1 tham số hình thức, Khi TVarData gọi phương thức thì phải .varDouble chứ a, và tại sao trong vba mình phải dùng Byref ạ

Hỏi toàn câu khôn thế :).

Variant là một kiểu hỗn hợp, nó ghi được nhiều kiểu giá trị, mỗi kiểu giá trị nó lưu vào một field của variant record.
Ví dụ biến v kiểu Variant, nó được gán

v = 120.40
Thì cấu trúc của v được lưu v.VType = varDouble; v.VDouble = 120.40

v = "Tôi là Delphi"
Thì cấu trúc của v được lưu v.VType = varOleStr; v.VOleStr= "Tôi là Delphi"

....

+ Muốn nhận thẳng giá trị mà v lưu trữ thì gọi thông qua cấu trúc TVarData(v).Field (field là trường lưu giá trị nhứ VDouble, VOleStr,...). Nếu không gọi thẳng TVarData(v).Field mà gọi kiểu "dễ hiểu" là iSum = iSum + v thì trình biên dịch phải làm thêm cơ số việc đi so sánh kiểu rồi lấy giá trị nên tốc độ sẽ chậm hơn.
+ Muốn kiểm tra kiểu của biến v đang là varDouble hay varOleStr,.... thì dùng VarType(v).

Từ khóa khai báo tham số dạng reference (địa chỉ vùng nhớ) trong hàm và thủ tục:
Trong Delphi var hoặc out, VB/VBA là ByRef là yêu cầu trỏ vào địa chỉ vùng nhớ của dữ liệu. Với cách này thì tham số chuyển tiếp qua các hàm và thủ tục sẽ nhanh, trình biên dịch không phải đi sao chép dữ liệu, đặc biệt với Array lớn sẽ mất nhiều thời gian vì nó phải sao chép. Khi làm với tham số kiểu tham chiếu phải cẩn thận trong code, không được thay đổi giá trị của nó nếu không có yêu cầu.
 
Upvote 0
Hỏi toàn câu khôn thế :).

Variant là một kiểu hỗn hợp, nó ghi được nhiều kiểu giá trị, mỗi kiểu giá trị nó lưu vào một field của variant record.
Ví dụ biến v kiểu Variant, nó được gán

v = 120.40
Thì cấu trúc của v được lưu v.VType = varDouble; v.VDouble = 120.40

v = "Tôi là Delphi"
Thì cấu trúc của v được lưu v.VType = varOleStr; v.VOleStr= "Tôi là Delphi"

....

+ Muốn nhận thẳng giá trị mà v lưu trữ thì gọi thông qua cấu trúc TVarData(v).Field (field là trường lưu giá trị nhứ VDouble, VOleStr,...). Nếu không gọi thẳng TVarData(v).Field mà gọi kiểu "dễ hiểu" là iSum = iSum + v thì trình biên dịch phải làm thêm cơ số việc đi so sánh kiểu rồi lấy giá trị nên tốc độ sẽ chậm hơn.
+ Muốn kiểm tra kiểu của biến v đang là varDouble hay varOleStr,.... thì dùng VarType(v).

Từ khóa khai báo tham số dạng reference (địa chỉ vùng nhớ) trong hàm và thủ tục:
Trong Delphi var hoặc out, VB/VBA là ByRef là yêu cầu trỏ vào địa chỉ vùng nhớ của dữ liệu. Với cách này thì tham số chuyển tiếp qua các hàm và thủ tục sẽ nhanh, trình biên dịch không phải đi sao chép dữ liệu, đặc biệt với Array lớn sẽ mất nhiều thời gian vì nó phải sao chép. Khi làm với tham số kiểu tham chiếu phải cẩn thận trong code, không được thay đổi giá trị của nó nếu không có yêu cầu.
cám ơn anh nhiều lắm, quả kiến thức quá uyên thâm về delphi, với dạng bài như trên thì còn có thể tối ưu tốc độ nữa được ko anh, vì tốc độ nó cũng chưa vượt trội mấy so với VBA.
 
Upvote 0
cám ơn anh nhiều lắm, quả kiến thức quá uyên thâm về delphi, với dạng bài như trên thì còn có thể tối ưu tốc độ nữa được ko anh, vì tốc độ nó cũng chưa vượt trội mấy so với VBA.
Em thử làm thử yêu cầu tỉnh tổng có điều kiện, như là so sánh theo mã nào đó rồi so sánh tốc độ giữa VBA và Delphi lần nữa.
Phép tính số học thuần tuý có thể tốc độ không chênh nhiều, nhưng khi có nhiều yếu tố khác tham gia vào thì sẽ có sự khác khau nhiều hơn.
 
Lần chỉnh sửa cuối:
Upvote 0
Em thử làm thử yêu cầu tỉnh tổng có điều kiện, như là so sánh theo mã nào đó rồi so sánh tốc độ giữa VBA và Delphi lần nữa.
Phép tính số học thuần tuý có thể tốc độ không chênh nhiều, nhưng khi có nhiều yếu tố khác tham gia vào thì sẽ có sự khác khau nhiều hơn.
Em đã thử pha lẫn text và số, bên Delphi e check vartype(v)=8 , kết quả vẫn same same nhau, em có thử dùng ADO nhưng ADO thì rất chậm luôn, đợi gần 10 phút là chưa ra kết quả ( e có giảm số dòng để check thử code ADO thì vẫn tính ra)
Mã:
function GetSumBySQL(sFullName, sSQL: Variant): double; stdcall;
var
  cnn: TADOConnection;
  qry: TADOQuery;
  i:uint8;
  iSum:double;
begin
  cnn := TADOConnection.Create(nil);
  iSum:=0;
  try
    cnn.ConnectionString := 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=' + sFullName +
                                       ';Extended Properties="Excel 12.0 Xml;HDR=NO";';
    cnn.Connected := True;
    qry := TADOQuery.Create(nil);
    try
      qry.Connection := cnn;
      qry.SQL.Text := sSQL;
      qry.Open;
      while not qry.Eof do
      begin
        for i := 0 to 10 do
          begin
            iSum:=iSum+qry.fields[i].AsFloat;
          end;
      qry.Next;
      end;
      Result := iSum;
    finally
      qry.Free;
    end;
  finally
    cnn.Free;
  end;
end;
 
Upvote 0
Dubgf ADO làm gì vì nó không liên quan đến test array. Ý anh là em chạy vòng lặp rồi tính tổng những dòng thoả mãn điều kiện làm cở sở so sánh với VBA. Chỉ test cho em biết thôi chứ Delphi nhanh thì không còn phải thử nghiệm nhiều đâu.
 
Upvote 0
Dubgf ADO làm gì vì nó không liên quan đến test array. Ý anh là em chạy vòng lặp rồi tính tổng những dòng thoả mãn điều kiện làm cở sở so sánh với VBA. Chỉ test cho em biết thôi chứ Delphi nhanh thì không còn phải thử nghiệm nhiều đâu.
như e đã nói bên trên dữ liệu xem kẽ text và số, thời gian chạy VBA 1,4s, Delphi 1,2S . Có thể trên Excel đã quen với việc dùng mảng, nhưng nếu giải thuật lấy dữ liệu lên và xử lý bằng SQL nhanh hơn thì cũng tốt a
Code Delphi
Mã:
function GetSumRange(var arr: Variant): Double; stdcall;
 var
  i,j: uint32;
  iSum: Double;
 begin
  iSum := 0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
        begin
         if vartype(arr[i,j])=5 then
            iSum :=  iSum + TVarData(arr[i,j]).VDouble;
        end;
    end;
  Result :=iSum;
 end;
Code VBA
Mã:
Sub TestGetSumVBA()
    Dim lr As Long, arr As Variant, isum As Double, i As Long, j As Long, t As Single
    t = Timer
    With Sheet1
        lr = .Range("A1000000").End(3).Row
        arr = .Range("A1:K" & lr).value
    End With
    For i = 1 To UBound(arr)
        For j = 1 To UBound(arr, 2)
            If IsNumeric(arr(i, j)) Then isum = isum + arr(i, j)
        Next j
    Next i
    MsgBox isum, , Timer - t
End Sub
 
Upvote 0
Bài test của em không đúng ý mình và không chạm vào tình huống thực tế. Giả xử trong mảng có cột là mã hàng, cột còn lại là số cần tính tổng. Em hãy viết code tổng tiền của một mã hàng. Không dùng SQL để so sánh VBA và Delphi. Code trên của em dùng so sánh số long/internet không có ý nghĩa gì về phép toán liên quan đến tốc độ cả.

Vấn đề về SQL không liên quan đến chủ đề em đang hỏi vì SQL là một lĩnh vực gần như độc lập với ngôn ngữ và trình biên dịch. Ví dụ hai ngôn ngữ lập trình đều dùng ADO thì nó có thể coi là như nhau vì tốc độ nó nằm bên trong thằng ADO chứ không phải ngôn ngữ lập trình.
 
Upvote 0
Em viết code này, kiểm tra mãi không hiểu nó sai chỗ nào mà các mã hàng đều giống nhau nhưng số lượng nó đã cộng dồn (cái "Code33843" đó chính là cái mã cuối cùng mà nó tìm thấy và add vào dic), tức là nó đã nhận đối tượng khi mình duyệt .values, nhờ anh check giúp, với a cho e hỏi vài câu ạ:
1. Khi debug code : các biến truyền từ excel vào nó đều ko hiện giá trị trong quá trình chạy code, như hình bên dưới ( có cách nào để xem ko ạ)
2. Cái dòng em bôi đỏ là giải phóng PD: theo nguyên lý thì mình duyệt xong tất cả rồi mới giải phóng chứ, mà khi em viết như thế thì nó hiện cảnh báo là PD có thể ko được khởi tạo, còn nếu giải phóng bên trong vòng lặp, nghĩa là mỗi lần duyệt là mỗi lần giải phóng thì nó ko cảnh báo, vậy cái nào tốt hơn a.
Mã:
type
  Product=class
    private
    ProductCode:PWideChar;
    quantity: Double;
    public
    Constructor Create(PC:PWideChar;Qu:Double); overload;
  end;
{$R *.res}
{ Product }
Constructor Product.Create(PC: PWideChar; Qu: Double);
begin
   ProductCode:=PC;
   quantity:=Qu;
end;
function TotalProduct(var arr:Variant): Variant;stdcall;
var
  dic:TDictionary<string,Product>;
  i: UInt32;
  Key:PWideChar;
  Value:Double;
  PD:Product;
  data:Variant;
begin
  dic:=TDictionary<string,Product>.Create;
  try
  for i := VarArrayLowBound(arr,1) to VarArrayHighBound(arr,1) do
    begin
      Key:=TvarData(arr[i,1]).VOleStr;
      Value:=tvardata(arr[i,2]).vdouble;
      if dic.ContainsKey(Key) then
        begin
        PD:=dic.Items[key];
        PD.quantity:=PD.quantity + Value;
        end
      else
        begin
        PD:=Product.Create(Key,Value);
        dic.Add(Key,PD);
        end;
    end;
  data:=VarArrayCreate([1,dic.Count,1,2],varVariant);
  i:=0;
  for PD in dic.Values do
  begin
    i:=i+1;
    data[i,1]:=WideCharToString(PD.ProductCode);
    data[i,2]:=PD.quantity;
  end;
  if PD<>nil then PD.Free;
  finally
    dic.Free;
    Result:=data;
  end;
end;
1633145114218.png1633145159432.png
 
Upvote 0
Em viết code này, kiểm tra mãi không hiểu nó sai chỗ nào mà các mã hàng đều giống nhau nhưng số lượng nó đã cộng dồn (cái "Code33843" đó chính là cái mã cuối cùng mà nó tìm thấy và add vào dic), tức là nó đã nhận đối tượng khi mình duyệt .values, nhờ anh check giúp, với a cho e hỏi vài câu ạ:
1. Khi debug code : các biến truyền từ excel vào nó đều ko hiện giá trị trong quá trình chạy code, như hình bên dưới ( có cách nào để xem ko ạ)
2. Cái dòng em bôi đỏ là giải phóng PD: theo nguyên lý thì mình duyệt xong tất cả rồi mới giải phóng chứ, mà khi em viết như thế thì nó hiện cảnh báo là PD có thể ko được khởi tạo, còn nếu giải phóng bên trong vòng lặp, nghĩa là mỗi lần duyệt là mỗi lần giải phóng thì nó ko cảnh báo, vậy cái nào tốt hơn a.
Mã:
type
  Product=class
    private
    ProductCode:PWideChar;
    quantity: Double;
    public
    Constructor Create(PC:PWideChar;Qu:Double); overload;
  end;
{$R *.res}
{ Product }
Constructor Product.Create(PC: PWideChar; Qu: Double);
begin
   ProductCode:=PC;
   quantity:=Qu;
end;
function TotalProduct(var arr:Variant): Variant;stdcall;
var
  dic:TDictionary<string,Product>;
  i: UInt32;
  Key:PWideChar;
  Value:Double;
  PD:Product;
  data:Variant;
begin
  dic:=TDictionary<string,Product>.Create;
  try
  for i := VarArrayLowBound(arr,1) to VarArrayHighBound(arr,1) do
    begin
      Key:=TvarData(arr[i,1]).VOleStr;
      Value:=tvardata(arr[i,2]).vdouble;
      if dic.ContainsKey(Key) then
        begin
        PD:=dic.Items[key];
        PD.quantity:=PD.quantity + Value;
        end
      else
        begin
        PD:=Product.Create(Key,Value);
        dic.Add(Key,PD);
        end;
    end;
  data:=VarArrayCreate([1,dic.Count,1,2],varVariant);
  i:=0;
  for PD in dic.Values do
  begin
    i:=i+1;
    data[i,1]:=WideCharToString(PD.ProductCode);
    data[i,2]:=PD.quantity;
  end;
  if PD<>nil then PD.Free;
  finally
    dic.Free;
    Result:=data;
  end;
end;
View attachment 267096View attachment 267097

Chỉ test một chút về tốc độ thôi nhưng em lại đưa cả Dic vào làm thêm việc rồi. Code của em trong Constructor bị thiếu một dòng lệnh quan trọng.

Mình sẽ chỉnh cách viết code trong Delphi, cách đặt tên, format nó bạn chú ý, so sánh và nên làm theo chuẩn ngôn ngữ:
1. Kiểu dữ liệu định nghĩa có chứ "T" đứng đầu. Ví dụ class của em tạo tên "Produce" thì nên đặt là "TProduct".
2. Ký tự đầu mỗi word thì viết hoa. Ví dụ "productname" thì Pascal quy ước viết là ProductName.
3. Pascal trước kia do giới hạn không gian soạn thảo thì người ta viết liền các toán tử và biểu thức. Delphi thì tạo một khoảng cách "space". VÍ dụ, Pascal viết là: v:=1+2+3; thì Delphi định dạng là: v := 1 + 2 + 3;
...
Còn vài nguyên tắc nữa em nên tìm hiểu thêm

Mã:
type
  TProduct = class
  private
    ProductCode: PWideChar;
    Quantity: Double;
  public
    constructor Create(PC: PWideChar; Qu: Double); overload;
  end;

{$R *.res}
  { TProduct }

constructor TProduct.Create(PC: PWideChar; Qu: Double);
begin
  Inherited Create; // Phải thêm vào thừa kế
  ProductCode := PC;
  Quantity := Qu;
end;

function TotalProduct(var arr: Variant): Variant; stdcall;
var
  dic: TDictionary<string, TProduct>;
  i: Integer;
  Key: PWideChar;
  Value: Double;
  PD: TProduct;
  Data: Variant;
begin
  dic := TDictionary<string, TProduct>.Create;
  try
    for i := VarArrayLowBound(arr, 1) to VarArrayHighBound(arr, 1) do
    begin
      //Kiểm tra tồn tại không đồng thời nhận luôn Product nếu đã có
      Key := TVarData(arr[i, 1]).VOleStr;
      if dic.TryGetValue(Key, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
    end;
    Data := VarArrayCreate([1, dic.Count, 1, 2], varVariant);
    i := 0;
    for PD in dic.Values do
    begin
      i := i + 1;
      Data[i, 1] := string(PD.ProductCode);
      Data[i, 2] := PD.Quantity;
      PD.Free; //Giải phóng trong vòng lặp
    end;
  finally
    dic.Free;
    Result := Data;
  end;
end;

Trả lời các câu hỏi:
1. Trong Delphi không như bên VB6/VBA, khi debug nó không hiển thị giá trị cũng như các thành phần của các Interface, Variant. Việc này người lập trình phải nắm rõ cấu trúc hoặc phân tách giá trị để test.
2. Lỗi khi giải phòng là do trong Constructor không có "Inherited". Anh đã sửa trong code trên.

Ý của anh là em chỉ cần viết một thủ tục test nhỏ thế này thôi để so sánh tốc độ. VÌ khi so sách tốc độ thì giảm tối đa việc đưa các thành phần bên ngoài chen vào.

Code test:
Mã:
function TestSUM(var arr: Variant): Double; stdcall;
var
  I: Integer;
begin
  Result := 0;
  for I := VarArrayLowBound(arr, 1) to VarArrayHighBound(arr, 1) do
  begin
    if TVarData(arr[I, 1]).VOleStr = 'ABC' then
      Result := Result + TVarData(arr[I, 2]).VDouble
  end;
end;

Đoạn code mình sửa và thêm chưa test, nhưng đoán 100% ok. Khi em chạy nếu có lỗi đâu thì sửa đó.
 
Lần chỉnh sửa cuối:
Upvote 0
2. Lỗi khi giải phòng là do trong Constructor không có "Inherited". Anh đã sửa trong code trên.
1. Cái này ko phải lỗi mà chỉ là warning thôi a, và khi thêm Inherited thì vẫn còn cái cảnh báo đó, nhưng e nghĩ nó cũng ko quan trọng vì mình hiểu được sẽ huỷ lúc nào.
2. Code bên trên a viết thiếu chỗ gán biến Key nếu key chưa tồn tại, nhưng gán xong kết quả vẫn như ban đầu e gửi lên
Mã:
if dic.TryGetValue(TVarData(arr[i, 1]).VOleStr, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        Key:=TVarData(arr[i, 1]).VOleStr;
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
3. Code này e sửa các biến Pwidechar thành string là nó chạy a, tốc độ quá lý tưởng, 0,9s, trong khi code trên VBA nếu code đúng theo logic code delphi thì chạy treo máy luôn ( ko biết có phải lỗi ko), còn code khai báo mảng mặc định ban đầu thì chạy 5,5s
==> code như delphi ( chạy treo máy)
Mã:
Sub testTotalProductVBA()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq()
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                dic.Item(arr(i, 1)) = dic.Item(arr(i, 1)) + arr(i, 2)
            Else
                dic.Add arr(i, 1), arr(i, 2)
            End If
        Next i
        ReDim kq(1 To dic.Count, 1 To 2)
        For i = 1 To dic.Count
            kq(i, 1) = dic.keys()(i - 1)
            kq(i, 2) = dic.items()(i - 1)
            If i = 1000 Then Stop
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(i - 1, 2).value = kq
    End With
    MsgBox Timer - t
End Sub
==> Code chạy 5,5s
Mã:
Sub testTotalProductVBA2()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq(), k As Long
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        ReDim kq(1 To UBound(arr), 1 To 2)
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                kq(dic.Item(arr(i, 1)), 2) = kq(dic.Item(arr(i, 1)), 2) + arr(i, 2)
            Else
                k = k + 1
                kq(k, 1) = arr(i, 1)
                kq(k, 2) = arr(i, 2)
                dic.Add arr(i, 1), k
            End If
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(k, 2).value = kq
    End With
    MsgBox Timer - t
End Sub
 
Upvote 0
1. Cái này ko phải lỗi mà chỉ là warning thôi a, và khi thêm Inherited thì vẫn còn cái cảnh báo đó, nhưng e nghĩ nó cũng ko quan trọng vì mình hiểu được sẽ huỷ lúc nào.
2. Code bên trên a viết thiếu chỗ gán biến Key nếu key chưa tồn tại, nhưng gán xong kết quả vẫn như ban đầu e gửi lên
Mã:
if dic.TryGetValue(TVarData(arr[i, 1]).VOleStr, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        Key:=TVarData(arr[i, 1]).VOleStr;
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
3. Code này e sửa các biến Pwidechar thành string là nó chạy a, tốc độ quá lý tưởng, 0,9s, trong khi code trên VBA nếu code đúng theo logic code delphi thì chạy treo máy luôn ( ko biết có phải lỗi ko), còn code khai báo mảng mặc định ban đầu thì chạy 5,5s
==> code như delphi ( chạy treo máy)
Mã:
Sub testTotalProductVBA()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq()
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                dic.Item(arr(i, 1)) = dic.Item(arr(i, 1)) + arr(i, 2)
            Else
                dic.Add arr(i, 1), arr(i, 2)
            End If
        Next i
        ReDim kq(1 To dic.Count, 1 To 2)
        For i = 1 To dic.Count
            kq(i, 1) = dic.keys()(i - 1)
            kq(i, 2) = dic.items()(i - 1)
            If i = 1000 Then Stop
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(i - 1, 2).value = kq
    End With
    MsgBox Timer - t
End Sub
==> Code chạy 5,5s
Mã:
Sub testTotalProductVBA2()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq(), k As Long
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        ReDim kq(1 To UBound(arr), 1 To 2)
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                kq(dic.Item(arr(i, 1)), 2) = kq(dic.Item(arr(i, 1)), 2) + arr(i, 2)
            Else
                k = k + 1
                kq(k, 1) = arr(i, 1)
                kq(k, 2) = arr(i, 2)
                dic.Add arr(i, 1), k
            End If
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(k, 2).value = kq
    End With
    MsgBox Timer - t
End Sub

Đã làm Delphi thì cứ yên tâm về tốc độ. Theo hiểu biết cũng như những gì mình test thì tốc độ như C++. Xét chung thì VBA không có cửa để so sánh tốc độ với Delphi đâu. Một bên là script một bên là mã máy (Delphi), chưa kể những hàm hệ thống các chuyên gia viết bằng Assemply.
 
Upvote 0
2. Cái dòng em bôi đỏ là giải phóng PD: theo nguyên lý thì mình duyệt xong tất cả rồi mới giải phóng chứ, mà khi em viết như thế thì nó hiện cảnh báo là PD có thể ko được khởi tạo, còn nếu giải phóng bên trong vòng lặp, nghĩa là mỗi lần duyệt là mỗi lần giải phóng thì nó ko cảnh báo, vậy cái nào tốt hơn a.
Ở vòng FOR đầu các đối tượng Product được tạo ra liên tiếp và chúng được nhớ trong từ điển với tư cách là VALUE. Vòng FOR thứ 2 duyệt từng đối tượng được nhớ trong từ điển. Lẽ ra phải giải phóng các đối tượng này trong vòng FOR. Làm như hiện thời thì:
- chỉ đối tượng PD CUỐI CÙNG khi ra khỏi vòng FOR được giải phóng.
- do ngoài các vòng FOR không có chỗ nào đối tượng PD được tạo (vì chúng được tạo trong vòng FOR), nhưng Delphi không chắc lắm nên chỉ lưu ý là PD có thể ko được khởi tạo.

Nếu không hủy các đối tượng PD trong vòng FOR thì rất có thể chúng được hủy trước khi ra khỏi SUB. Nhưng để khỏi lăn tăn, hồ nghi thì nên hủy trong vòng FOR. Hủy sau FOR là chỉ hủy PD CUỐI CÙNG.
 
Upvote 0
Ở vòng FOR đầu các đối tượng Product được tạo ra liên tiếp và chúng được nhớ trong từ điển với tư cách là VALUE. Vòng FOR thứ 2 duyệt từng đối tượng được nhớ trong từ điển. Lẽ ra phải giải phóng các đối tượng này trong vòng FOR. Làm như hiện thời thì:
- chỉ đối tượng PD CUỐI CÙNG khi ra khỏi vòng FOR được giải phóng.
- do ngoài các vòng FOR không có chỗ nào đối tượng PD được tạo (vì chúng được tạo trong vòng FOR), nhưng Delphi không chắc lắm nên chỉ lưu ý là PD có thể ko được khởi tạo.

Nếu không hủy các đối tượng PD trong vòng FOR thì rất có thể chúng được hủy trước khi ra khỏi SUB. Nhưng để khỏi lăn tăn, hồ nghi thì nên hủy trong vòng FOR. Hủy sau FOR là chỉ hủy PD CUỐI CÙNG.
- cám ơn a, vậy là khi PD nó trỏ tới vùng nhớ khác thì vùng nhớ được tạo ra trước đó vẫn còn lưu trên ram, nó được dic(value) quản lý và vòng lặp sau thì vừa duyệt và vừa giải phóng luôn.
- Code bên trên em viết anh có biết vì sao khi dùng dữ liệu gốc là Pwidechar từ excel truyền qua thì khi duyệt qua các dic.values, ta lại nhận các giá trị Productcode giống nhau ( cụ thể ở đây là productcode cuối cùng được thêm vào dic) nhưng ngặt nỗi nó lại trả về đúng cột thứ 2 quantity.
 
Upvote 0
- cám ơn a, vậy là khi PD nó trỏ tới vùng nhớ khác thì vùng nhớ được tạo ra trước đó vẫn còn lưu trên ram, nó được dic(value) quản lý và vòng lặp sau thì vừa duyệt và vừa giải phóng luôn.
- Code bên trên em viết anh có biết vì sao khi dùng dữ liệu gốc là Pwidechar từ excel truyền qua thì khi duyệt qua các dic.values, ta lại nhận các giá trị Productcode giống nhau ( cụ thể ở đây là productcode cuối cùng được thêm vào dic) nhưng ngặt nỗi nó lại trả về đúng cột thứ 2 quantity.
Buồn buồn viết tí thôi. Tôi có Delphi đâu để mà test. Không test thì nhìn bằng mắt có nhiều chỗ sẽ sơ ý bỏ qua.
 
Upvote 0
Bạn sử dụng COM thì nên dùng Widestring

Mã:
type
  TProduct = class
  private
    ProductCode: WideString;
    Quantity: Double;
  public
    constructor Create(PC: WideString; Qu: Double); overload;
  end;

{$R *.res}


{$R *.res}
  { TProduct }

constructor TProduct.Create(PC: WideString; Qu: Double);
begin
  Inherited Create; // Phải thêm vào thừa kế
  ProductCode := PC;
  Quantity := Qu;
end;

function TotalProduct(var arr: Variant): Variant; stdcall;
var
  dic: TDictionary<string, TProduct>;
  i: Integer;
  Key:  WideString;
  Value: Double;
  PD: TProduct;
  Data: Variant;
begin
  dic := TDictionary<string, TProduct>.Create;
  try
    for i := VarArrayLowBound(arr, 1) to VarArrayHighBound(arr, 1) do
    begin
      //Kiểm tra tồn tại không đồng thời nhận luôn Product nếu đã có
      Key := TVarData(arr[i, 1]).VOleStr;
      if dic.TryGetValue(Key, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
    end;
    Data := VarArrayCreate([1, dic.Count, 1, 2], varVariant);
    i := 0;
    for PD in dic.Values do
    begin
      i := i + 1;
      Data[i, 1] := (PD.ProductCode);
      Data[i, 2] := PD.Quantity;
      PD.Free; //Giải phóng trong vòng lặp
    end;
  finally
    dic.Free;
    Result := Data;
  end;
end;
 
Upvote 0
Bạn sử dụng COM thì nên dùng Widestring

Mã:
type
  TProduct = class
  private
    ProductCode: WideString;
    Quantity: Double;
  public
    constructor Create(PC: WideString; Qu: Double); overload;
  end;

{$R *.res}


{$R *.res}
  { TProduct }

constructor TProduct.Create(PC: WideString; Qu: Double);
begin
  Inherited Create; // Phải thêm vào thừa kế
  ProductCode := PC;
  Quantity := Qu;
end;

function TotalProduct(var arr: Variant): Variant; stdcall;
var
  dic: TDictionary<string, TProduct>;
  i: Integer;
  Key:  WideString;
  Value: Double;
  PD: TProduct;
  Data: Variant;
begin
  dic := TDictionary<string, TProduct>.Create;
  try
    for i := VarArrayLowBound(arr, 1) to VarArrayHighBound(arr, 1) do
    begin
      //Kiểm tra tồn tại không đồng thời nhận luôn Product nếu đã có
      Key := TVarData(arr[i, 1]).VOleStr;
      if dic.TryGetValue(Key, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
    end;
    Data := VarArrayCreate([1, dic.Count, 1, 2], varVariant);
    i := 0;
    for PD in dic.Values do
    begin
      i := i + 1;
      Data[i, 1] := (PD.ProductCode);
      Data[i, 2] := PD.Quantity;
      PD.Free; //Giải phóng trong vòng lặp
    end;
  finally
    dic.Free;
    Result := Data;
  end;
end;
Trước mình dùng pwidechar nên nó lỗi bạn
 
Upvote 0
Lâu lâu quay lại tham gia góp vui.
Mã ASM của các dạng GetSumRange vẫn còn chậm. Bị vướng 2 cái sau, nên khó đè đầu ngoạn mục thằng VBA được. Viết đúng nó phải nhanh hơn gấp 10 lần
1. Trong vòng for i, biến j được get lại nhiều lần qua 2 hàm VarArrayLowBound và VarArrayHighBound
Nhìn kỹ thì VarArrayLowBound(arr, 2) và VarArrayHighBound(arr, 2) là hằng khi vào hàm.
Nên chúng ta đẩy (cache) giá trị nó ra ngoài vòng for i.
2. iSum := iSum + TVarData(arr[i, j]).VDouble => Delphi compiler chèn mã lệnh call tới hàm internal linkproc VarArrayGet, làm chậm đi ít nữa
Nên ép kiểu qua PSafeArray rồi truy xuất pointer tới trực tiếp từng giá trị double luôn.
Dùng trực tiếp các hàm API về SafeArray, seek pointer tới ngay vùng đầu data của PSafeArray.
Vd hàm API SafeArrayPtrOfIndex
Các bạn đọc source hàm _VarArrayGet trong file System.Variants.pas để tham khảo thêm
Giải quyết 1 và 2 đủ bóp cổ code thông dịch của VBA

PS: bổ sung, nếu biến truyền vào hàm các bạn muốn dùng truyền theo kiểu byref giống VBA và các bạn không thay đổi nội dung biến đó, các bạn thay vì viết var X: YYYY thì có thể viết const X: YYYY
Compiler sẽ sinh mã truyền by pointer cho các bạn, và kiểm tra luôn nếu code các bạn có tiềm tàng thay đổi nội dung biến hay không, và sẽ báo error hay warning ngay lúc compile.
VD: GetSumRange(const arr: Variant): Double; stdcall;
Khai báo là var arr: Variant thì compiler mặc định xem như biến arr sẽ bị thay đổi

1634206517138.png
Vài góp ý nhỏ, chân chọng, bét xì ga :D
 
Lần chỉnh sửa cuối:
Upvote 0
2 năm trước tôi thấy Atools.dll có 2 chức năng trong 1 File đó là COM và API viết = Delphi trong 1 Files rất hay
mấy ngày trước tùng có hỏi COM Delphi bất chợt tôi mới nhớ lại cái ngày ấy và vọc có 2 ngày thế là viết Xong

COM Delphi và API Delphi chung vào 1 File tạm keo MyLibrary.dll

1/ Nếu sử dụng API thì chỉ cần Copy vào System là sử dụng

2/ Nếu Sử dụng COM thì Đăng ký nó xong ... Từ VBE : Tools\References\Browse..\MyLibrary.dll\Open\OK
và sử dụng

Demo cho ai đó tò mò chút

Cảm ơn @minhtungph đã hỏi làm Anh tò mò ... phải chăng code két khi đạt đến độ chất thì lượng sẻ biến đổi theo :wiggle::weight_lift2:@#$


Lưu ý:
1/ Demo là DLL 64 bit thì chỉ chạy trên Office 64 bít
2/ thay đổi lại SQL ADODB và thưởng thức
 

File đính kèm

Upvote 0
Bạn úp file Excel và code cả VBA, Delphi đi. Mình code, optimize lại cho.
Giờ làm data cho file Excel lười lắm.
Phải tránh được hàm internal mà Delphi compiler chèn vô, _VarArrayGet thì sẽ nhanh hơn nữa.
Vì _VarArrayGet tạo 1 bản copy Variant cho phần tử arr[i, j]
 
Upvote 0
Các bạn cẩn thận với string truyền vào và trả ra từ VBA qua Delphi nhé.
String trong VBA là kiểu BSTR của Windows, và kiểu WideString trong Delphi cũng là BSTR.
Không thể tùy ý dùng PWideChar/PChar (Unicode) được. Phải dùng các hàm API của BSTR của Windows.
Mình sẽ giải thích về cái này rõ hơn sau.
 
Upvote 0
Mạnh mới test lại các kiểu thì thấy hàm đó viết lộn một chút Mạnh điều chỉnh lại như sau mới chạy OK khi ta sử dụng ADO lấy dữ liệu từ 1 Sheet vào Mảng sử dung Phức thức GetRows ... xong dùng hàm chuyển mảng gán lên Range
Mã:
function TransArr(ssArr: OleVariant): OleVariant; stdcall;
var
    tmpArr    : OleVariant;
    x, y    : integer;
    lcol,lRows  : integer;
begin
  lcol := VarArrayHighBound(ssArr, 2); //Cot
  lRows := VarArrayHighBound(ssArr, 1); //dong
  tmpArr := VarArrayCreate([1, lcol + 1, 1, lRows + 1],varVariant);
    for x:= 0 to lcol do begin
        for y:= 0  to lRows do begin
            tmpArr[x + 1, y + 1] := ssArr[y, x];
        end; // y
    end; // x
    Result := tmpArr;
end;
Mô tả sơ bộ 1 chút
1/ Nếu ta ko + thêm 1 trong hàm thì khi lấy lên ta phải cộng thêm 1 khi Resize
Mã:
Range("A2").Resize(UBound(dArr, 1) + 1, UBound(dArr, 2) + 1) = dArr
2/ Còn nếu ta đã cộng thêm 1 trong Hàm TransArr thì khi ta lấy lên gán dữ liệu ko + thêm 1 nữa
Mã:
Range("A2").Resize(UBound(dArr, 1), UBound(dArr, 2)) = dArr
3/ 1 ở đây là 1 cột và 1 dòng

Cảm ơn thuyyeu99 Viết cho Mạnh cái Hàm hay và hay hết tất cả là Mạnh biết xài mảng 2dArray trong Delphi
@ThangCuAnh ... 2 năm trước mới nhập Môn Delphi ... Mạnh viết hàm này nó chạy rất chậm với 1 Array lớn ... vậy có cách nào viết lại nó cho nó chạy nhanh hơn không Mong trợ giúp
Xin cảm ơn
 
Upvote 0
Mã:
function TransArr(ssArr: OleVariant): OleVariant; stdcall;
var
    tmpArr    : Variant;
begin
    tmpArr := ssArr.Value2;
    Result := tmpArr;
end;
Anh mạnh thử code thế này xem thử xem.
 
Upvote 0
Upvote 0
Các bạn cẩn thận với string truyền vào và trả ra từ VBA qua Delphi nhé.
String trong VBA là kiểu BSTR của Windows, và kiểu WideString trong Delphi cũng là BSTR.
Không thể tùy ý dùng PWideChar/PChar (Unicode) được. Phải dùng các hàm API của BSTR của Windows.
Mình sẽ giải thích về cái này rõ hơn sau.
Em chào anh @ThangCuAnh theo em tìm hiểu thì sự khác biệt cơ bản giữa Excel và delphi theo bảng dưới, tuy nhiên em vẫn chưa áp dụng một cách chính xác khi sử dụng. Em thấy anh chia sẻ "Phải dùng các hàm API của BSTR của Windows." anh có thể gợi ý rõ hơn về cách dùng được không ạ
Chân thành cảm ơn anh!

OLE TYPEDelphi Type
BSTRWIDETRING
BYTEShortInt
CURRENCYCurrency
DateTDateTime
DECIMALTdecimal
DoubleDouble
floatsingle
GUIDGUID
INTSYSINT
LongInteger
LPSTRPchar
LPPWSTRPwidechar
shortsmallint
unsigned charbyte
unsigned intsysunint
unsigned longUint
VariantOlevariant
unsigned shortword
 
Upvote 0
Nói chung chung mình không nói được bạn Bảo Ninh. Phải có code cụ thể.
Code của Mạnh chậm do cũng bị trường hợp 2 hàm internal của Delphi compiler chèn vào là _VarArrayGet và _VarArrayPut
Delphi compiler nó làm hậu trường nhiều lắm, vô số kể. Để code mình nhanh thì mình phải biết nó làm gì và cách tránh.
 
Upvote 0
Nói chung chung mình không nói được bạn Bảo Ninh. Phải có code cụ thể.
Code của Mạnh chậm do cũng bị trường hợp 2 hàm internal của Delphi compiler chèn vào là _VarArrayGet và _VarArrayPut
Delphi compiler nó làm hậu trường nhiều lắm, vô số kể. Để code mình nhanh thì mình phải biết nó làm gì và cách tránh.
nói vậy = xin chịu ... nếu được code lại cho mạnh code đó ... may ra học được
Lý thuyết nói theo sách cũng xin = thua
 
Upvote 0

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

Back
Top Bottom