Đâ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.
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
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
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
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
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
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 ]
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.
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."
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."
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
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
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!
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
Mạnh lang thang trên Google tìm tài liệu viết cái Server xài ( Database.accdb ) trên VB6 vô tình thấy Internet nó keo VB6 có thể viết cho Sub + Function cho Officex32 và Officex64 chạy tốt lỡ quậy rồi thì chơi tiếp .... Úp lên đây nhờ các Bạn test dùm trên Officex32 và Officex64 xem nó có chạy...
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
Mạnh lang thang trên Google tìm tài liệu viết cái Server xài ( Database.accdb ) trên VB6 vô tình thấy Internet nó keo VB6 có thể viết cho Sub + Function cho Officex32 và Officex64 chạy tốt lỡ quậy rồi thì chơi tiếp .... Úp lên đây nhờ các Bạn test dùm trên Officex32 và Officex64 xem nó có chạy...
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 ???!!!
Đâ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
Sau khi Build ra thư mục này
và em copy MyDLL.dll vào thư mục
Sau đó em vào Excel và khai báo như này
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ả ạ
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
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
nhưng khi chạy Autpen thì lỗi
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.....
I am trying to create a 64bit DLL in Delphi XE5 in order to communicate with the 64bit version of Excel 2013, yet without any success. Breaking down the problem to its simplest form, I wrote this s...
stackoverflow.com
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
I am trying to create a 64bit DLL in Delphi XE5 in order to communicate with the 64bit version of Excel 2013, yet without any success. Breaking down the problem to its simplest form, I wrote this s...
stackoverflow.com
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
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.....
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.
I am trying to create a 64bit DLL in Delphi XE5 in order to communicate with the 64bit version of Excel 2013, yet without any success. Breaking down the problem to its simplest form, I wrote this s...
stackoverflow.com
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
Cái đó xin chịu ... mò xong thử ngang thử dọc .... xong thử tới thử lui thấy chạy là tạm ok còn gì kệ nó ... sau này code khá lên tự nhiên sẻ ngộ ra thui
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.
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
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.
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.
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 ạ.
Delphi 10.4.1 is a quality-focused release, and this goes for code completion! There are settings to mimic classic code completion behaviour, plus many fixes.
blogs.embarcadero.com
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
Cảm Ơn các Bạn nhiều
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.
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á.
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.
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
Ú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.
Tôi không biết có sự liên quan nào tới nhận thức hay tâm lý của học sinh hay không, nhưng tôi không hiểu, tại sao không phù hợp với sinh lý của học sinh.
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.
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.
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 .....
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ả.
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ả.
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 ??????????!!!!!!!!!!!!!!
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à
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
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 đó ...
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
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á.
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ở.
Hình giao diện Ribbon TIỆN ÍCH VIỆT hoàn toàn tạo trên C# hết đấy nhé.
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 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
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).
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.
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.
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
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 !
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
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 !
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 !
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 !
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.
học phí cao lắm nha anh. Đợi em soạn giáo án rồi dạy nha
Lâu rồi mới thấy anh xuất hiện nhỉ. Chắc là Đang giãn cách nên mới có thời gian vào diễn đàn đây
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
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
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)
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 ạ
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.
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.
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.
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;
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.
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
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.
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;
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;
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 đó.
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
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.
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.
Ở 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.
- 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.
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;
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;
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
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
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
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]
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.
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
@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
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!
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 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.