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

MyExcel Liên hệ QC

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Lần chỉnh sửa cuối:

ThangCuAnh

Mới rờ Ét xeo
Tham gia
1/12/17
Bài viết
882
Được thích
780
Giới tính
Nam
Nghề nghiệp
Coder nghỉ hưu, RCE dạo
Test hàm rtcTypeName thôi, hàm này quan trọng chạy đúng chứ không phải chạy nhanh.
Thử test với mọi kiểu dữ liệu của VBA và mọi thứ trên Excel.
Bị sai hay crash chổ nào thì gởi dùm file Excel test lên.
Thanks
 
Upvote 0

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Code với trả két ... chi phí mất thời gian quá
kể từ ngày biết viết cái Hàm kiểu 365 trên VBA ... sau gần 2 năm mới chuyển nó vào Delphi thành công
ko thể ngờ được nó đơn giản lắm chỉ có trên 10 dòng code thôi ... viết 1 cái Hàm chung nhất gán bất cứ 1 Array nào vào là ok

code ngắn lắm nó như sau.. Trong File có sử dụng code của @ThangCuAnh = Cảm ơn lắm lắm
Mã:
Rem https://youtu.be/XICP6C0yJQc
Declare PtrSafe Function ResizeArrayA Lib "MyLibrary64.dll" (ByVal arr As Variant) As Variant
Rem ==========
Function TransArray(ByVal rngIn As Range) As Variant
    Dim arr As Variant
    arr = rngIn.value
    TransArray = ResizeArrayA(arr)
End Function
Rem ==========
Function GetSQLArray(ByVal aPath As Variant, ByVal SQL As Variant) As Variant
    Dim hr As Long
    Dim arr As Variant, ArrDest As Variant
    Dim VB As New MyLibrary.VBLib                                   ''Check References ...MyLibrary64.dll
    arr = DataBaseToArray(aPath, SQL)
    Rem hr = FastTransArrayDirect(arr, ArrDest)                     ''Su dung ham API
    ArrDest = VB.TransposeArray(arr)                                ''Su dung COM
    If IsArray(ArrDest) Then GetSQLArray = ResizeArrayA(ArrDest)
End Function
Rem ========== Tao 1 mang voi so dong va cot tren Range
Function TaoArr(dong As Long, cot As Long) As Variant
    Rem Cu Phap: =TaoArr(10,10) Tao ra 10 dong x 10 cot
    Dim i As Long, J As Long
    ReDim arr(1 To dong, 1 To cot)
    For i = 1 To dong
        For J = 1 To cot
            arr(i, J) = i & "_" & J
        Next
    Next
    TaoArr = ResizeArrayA(arr)
End Function
Rem ==========
Video Demos
Liên kết: https://youtu.be/XICP6C0yJQc
 
Upvote 0

HieuCD

Chuyên gia GPE
Tham gia
14/9/10
Bài viết
8,986
Được thích
19,608
Code với trả két ... chi phí mất thời gian quá
kể từ ngày biết viết cái Hàm kiểu 365 trên VBA ... sau gần 2 năm mới chuyển nó vào Delphi thành công
ko thể ngờ được nó đơn giản lắm chỉ có trên 10 dòng code thôi ... viết 1 cái Hàm chung nhất gán bất cứ 1 Array nào vào là ok

code ngắn lắm nó như sau.. Trong File có sử dụng code của @ThangCuAnh = Cảm ơn lắm lắm
Mã:
Rem https://youtu.be/XICP6C0yJQc
Declare PtrSafe Function ResizeArrayA Lib "MyLibrary64.dll" (ByVal arr As Variant) As Variant
Rem ==========
Function TransArray(ByVal rngIn As Range) As Variant
    Dim arr As Variant
    arr = rngIn.value
    TransArray = ResizeArrayA(arr)
End Function
Rem ==========
Function GetSQLArray(ByVal aPath As Variant, ByVal SQL As Variant) As Variant
    Dim hr As Long
    Dim arr As Variant, ArrDest As Variant
    Dim VB As New MyLibrary.VBLib                                   ''Check References ...MyLibrary64.dll
    arr = DataBaseToArray(aPath, SQL)
    Rem hr = FastTransArrayDirect(arr, ArrDest)                     ''Su dung ham API
    ArrDest = VB.TransposeArray(arr)                                ''Su dung COM
    If IsArray(ArrDest) Then GetSQLArray = ResizeArrayA(ArrDest)
End Function
Rem ========== Tao 1 mang voi so dong va cot tren Range
Function TaoArr(dong As Long, cot As Long) As Variant
    Rem Cu Phap: =TaoArr(10,10) Tao ra 10 dong x 10 cot
    Dim i As Long, J As Long
    ReDim arr(1 To dong, 1 To cot)
    For i = 1 To dong
        For J = 1 To cot
            arr(i, J) = i & "_" & J
        Next
    Next
    TaoArr = ResizeArrayA(arr)
End Function
Rem ==========
Video Demos
Liên kết: https://youtu.be/XICP6C0yJQc
Chưa thấy hàm ResizeArrayA () :p
 
Upvote 0

ThangCuAnh

Mới rờ Ét xeo
Tham gia
1/12/17
Bài viết
882
Được thích
780
Giới tính
Nam
Nghề nghiệp
Coder nghỉ hưu, RCE dạo
Bạn nào cần các prototype của các hàm export từ VBA6/7 Dll mà code VBA ở trên Office cuối cùng phải gọi xuống tới, có thể tham khảo ở đây.
Vd Shell, CreateObject trên VBA sẽ xuống tới hàm nào: rtcShell, rtcCreateObject2... prototype của hàm ra sao....
Hy vọng giúp ích các bạn.
 
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Chưa thấy hàm ResizeArrayA () :p
đó là hàm độc nhất GPE này đấy ... tính tới thời điểm hiện tại ... nên thông cảm cho Mạnh ko phổ biến nó

ứng dụng nó viết Hàm cho Excel rất hay khi ta tính toán mọi cái xong gán vào 1 Array xong ResizeArrayA() ... là nó trả kết quả lên 1 Celss duy nhất trên Sheet ... nên rất nhẹ ... xóa 1 Cells gõ hàm đó là mất hết

kể cả khi ta lập trình viết hàm trên Office 365 thì nó cũng rất tiện ích
 
Upvote 0

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
đó là hàm độc nhất GPE này đấy ... tính tới thời điểm hiện tại ... nên thông cảm cho Mạnh ko phổ biến nó

ứng dụng nó viết Hàm cho Excel rất hay khi ta tính toán mọi cái xong gán vào 1 Array xong ResizeArrayA() ... là nó trả kết quả lên 1 Celss duy nhất trên Sheet ... nên rất nhẹ ... xóa 1 Cells gõ hàm đó là mất hết

kể cả khi ta lập trình viết hàm trên Office 365 thì nó cũng rất tiện ích

Theo tôi quan sát thì kết quả chạy hàm của bạn đang là Excel 365 chạy hàm tạo mảng. Tôi nhìn ra thế bởi quan sát:
1- Excel tự bao viền màu xanh tự động quanh mảng
2- Ô đầu tiên là ô duy nhất có công thức màu đen, các ô trong mảng trả về thì màu xám.
3- Nếu test thì chỉ cần nhập giá trị chen vào vùng mảng này thì công thức sẽ chỉ trả về một giá trị tại ô đầu tiên là #SPILL!
(Ba đặc điểm trên chỉ có Excel 365 làm được. Những hàm UDF viết trong VBA hay bất kể ngôn ngữ nào mà Excel nhận diện được mà trả về một array, chạy trên Excel 365 thì hiển nhiên chạy được như trên)

Tôi chưa thấy vai trò của hàm ResizeArrayA() làm gì tới cơ chế tạo mảng kiểu resize trên sheet, hay để mọi người thấy nó độc ra sao. Có thể bạn demo nhầm ví dụ? Bạn thử demo chạy nó trên Excel không phải 365, hay tốt hơn là gửi DLL lên để tôi và mọi người xem nó hoạt động như thế nào thì sẽ có nhận định đúng hơn.
 
Lần chỉnh sửa cuối:
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Theo tôi quan sát thì kết quả chạy hàm của bạn đang là Excel 365 chạy hàm tạo mảng. Tôi nhìn ra thế bởi quan sát:
1- Excel tự bao viền màu xanh tự động quanh mảng
2- Ô đầu tiên là ô duy nhất có công thức màu đen, các ô trong mảng trả về thì màu xám.
3- Nếu test thì chỉ cần nhập giá trị chen vào vùng mảng này thì công thức sẽ chỉ trả về một giá trị tại ô đầu tiên là #SPILL!
(Ba đặc điểm trên chỉ có Excel 365 làm được. Những hàm UDF viết trong VBA hay bất kể ngôn ngữ nào mà Excel nhận diện được mà trả về một array, chạy trên Excel 365 thì hiển nhiên chạy được như trên)

Tôi chưa thấy vai trò của hàm ResizeArrayA() làm gì tới cơ chế tạo mảng kiểu resize trên sheet, hay để mọi người thấy nó độc ra sao. Có thể bạn demo nhầm ví dụ? Bạn thử demo chạy nó trên Excel không phải 365, hay tốt hơn là gửi DLL lên để tôi và mọi người xem nó hoạt động như thế nào thì sẽ có nhận định đúng hơn.
1/ mục số 1
1.png
2/ Mục số 2
2.png

3/ Mục số 3 thử lỗi
3.png


4/ Video
 

File đính kèm

  • Video_2021-12-20_095132.rar
    494.1 KB · Đọc: 3
Upvote 0

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts

Chính là 3 đặc điểm tôi nó đó là Excel 365 chứ không phải hàm ResizeArrayA() tạo ra như vậy. Thêm một quan sát. Viền màu xanh khi công thức trả về #SPILL! là đường đứt đoạn, còn không lỗi thì viền nét liên. Nên nếu bạn gửi DLL lên tôi xem nó hoạt động rao sao thì nhận định của tôi có thể đúng hơn với hàm của bạn.
 
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Chính là 3 đặc điểm tôi nó đó là Excel 365 chứ không phải hàm ResizeArrayA() tạo ra như vậy. Thêm một quan sát. Viền màu xanh khi công thức trả về #SPILL! là đường đứt đoạn, còn không lỗi thì viền nét liên. Nên nếu bạn gửi DLL lên tôi xem nó hoạt động rao sao thì nhận định của tôi có thể đúng hơn với hàm của bạn.
3 mục có hết rồi mà ... nếu ko có hàm ResizeArrayA() thì làm sao nó trả kết quả lên Sheet ???!!!
Mới thử hàm của Ms Excel 365 nó cũng như sau
1/ ...
1.PNG
2/ ...
2.PNG
3/ ...
3.PNG
 
Lần chỉnh sửa cuối:
Upvote 0

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Bạn nào cần các prototype của các hàm export từ VBA6/7 Dll mà code VBA ở trên Office cuối cùng phải gọi xuống tới, có thể tham khảo ở đây.
Vd Shell, CreateObject trên VBA sẽ xuống tới hàm nào: rtcShell, rtcCreateObject2... prototype của hàm ra sao....
Hy vọng giúp ích các bạn.

Cảm ơn and vì sự kỳ công RE ra bộ hàm có prototype từ VBAxxx.dll.
Từ tài liệu C anh gửi đây
https://github.com/HongThatCong/VB_VBA_567/blob/main/VB_VBA_567.h

Em test hàm rtcInputBox thì đang gặp lỗi không biết ví sao

Theo khai báo C trong tài liêu của anh là

C:
#define VBAPI   __declspec(dllimport) __stdcall
VBAPI LPVARIANT     rtcInputBox(LPVARIANT pvarPrompt, LPVARIANT pvarTitle, LPVARIANT pvarDefault,
                                LPVARIANT pvarXPos, LPVARIANT pvarYPos,
                                LPVARIANT pvarHelpFile, LPVARIANT pvarContext);

Kiểu LPVARIANT em không thấy có định nghĩa, có thể là
C:
typedef  VARIANT* LPVARIANT;

Em port nó vào Delphi như sau:

SQL:
unit VBADef;

interface
uses
  Windows,
  ActiveX,
  Variants;

const
  VBADLL7 = 'C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL';

type
  LPVARIANT = ^TVariantArg;

function rtcInputBox1(const pvarPrompt, pvarTitle, pvarDefault,
                                pvarXPos, pvarYPos,
                                pvarHelpFile, pvarContext: LPVARIANT): LPVARIANT; stdcall;

function rtcInputBox2(pvarPrompt, pvarTitle, pvarDefault,
                                pvarXPos, pvarYPos,
                                pvarHelpFile, pvarContext: TVariantArg): TVariantArg; stdcall;

implementation

function rtcInputBox1; external VBADLL7 name 'rtcInputBox';
function rtcInputBox2; external VBADLL7 name 'rtcInputBox';

end.

Khi chạy thì gặp lỗi với cả hai cách khai báo rtcInputBox1, rtcInputBox2.

Khi port sang VBA thì cũng gặp lỗi.
C#:
Declare PtrSafe Function VBAInputBox Lib _
                        "C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL" _
                        Alias "rtcInputBox" _
                            (ByVal pvarPrompt As Variant, _
                             Optional ByVal pvarTitle As Variant = vbNullString, _
                             Optional ByVal pvarDefault As Variant = vbNullString, _
                             Optional ByVal pvarXPos As Variant = 0, _
                             Optional ByVal pvarYPos As Variant = 0, _
                             Optional ByVal pvarHelpFile As Variant = vbNullString, _
                             Optional ByVal pvarContext As Variant = 0) As Variant

Sub TestVBAMon()
    VBAInputBox "Tuan", "abc"
End Sub

Các lỗi này đều làm crash Excel. Anh kiểm tra xem cả hai trường hợp này vì sao lại bị vậy?
 
Upvote 0

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
3 mục có hết rồi mà ... nếu ko có hàm ResizeArrayA() thì làm sao nó trả kết quả lên Sheet ???!!!
Mới thử hàm của Ms Excel 365 nó cũng như sau
1/ ...
View attachment 270501
2/ ...
View attachment 270502
3/ ...
View attachment 270503

Trong các code của bạn nếu bỏ hàm ResizeArrayA() thì Excel 365 đã trả về mảng trên sheet rồi. Hy vọng nhìn thấy demo khác của bạn cho rõ ràng hơn.

 

File đính kèm

  • UDFinExcel365.gif
    UDFinExcel365.gif
    47.2 KB · Đọc: 1
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Trong các code của bạn nếu bỏ hàm ResizeArrayA() thì Excel 365 đã trả về mảng trên sheet rồi. Hy vọng nhìn thấy demo khác của bạn cho rõ ràng hơn.

á hiểu nói gì rồi ... thì 2 năm trước viết trên VBA chạy trên Office 2019 ... nó cũng ra như thế ... chỉ là ko có viền quanh vùng dữ liệu của hàm thôi .... có thể xem lại video 2 na9m trước

chỉ khác là ko có viền màu xanh bao quanh vùng dữ liệu của hàm thôi

1639973269319.png


mà thấy hơi kỳ lạ chút ... ít ngày nữa mới thử trên các bản Office sau
cũng là trên Excel 365 chạy code thuần VBA thì ko có viền màu xanh ... mà chạy code trong Delphi lại có ???!!!

khó hiểu + tò mò chút ... ít ngày nữa rảnh xem lại sau
 
Lần chỉnh sửa cuối:
Upvote 0

ThangCuAnh

Mới rờ Ét xeo
Tham gia
1/12/17
Bài viết
882
Được thích
780
Giới tính
Nam
Nghề nghiệp
Coder nghỉ hưu, RCE dạo
Uhm Tuân, 2 hàm rtcMsgBox và rtcInput mình chưa debug, mình có ghi trên github.
À, Tuân khai báo sai rồi, Optional trong VB/VBA không có nghĩa là truyền NULL/nil/0, mà là phải truyền vào 1 variant rõ ràng, có varType = VT_ERROR và scode = DISP_E_PARAMNOTFOUND (0x80020004)
Trong C, Windows SDK thì VARIANT = Variant/TVarData/OleVariant trong Delphi.
Khác cái tên, type thôi, chứ ở dưới, cấu trúc record/struct, trong memory đều là VARIANT hết. Miễn sao truyền đúng là được. Mã máy nó không biết Variant, variiếc gì đâu.
LPVARIANT = VARIANT * trong C, = PVarData, PVariant, POleVariant trong Delphi.

Trong code VBA của Tuân phải là ByRef nhen, không phải ByVal
Bạn @Kiều Mạnh khiêm tốn lại chút, chỉ là 1 cái hàm không có gì mà phải nổ là độc nhất, chưa bao giờ có, rồi không share, cả Dll cũng không úp. Bạn up dll đi, tôi cho bạn lại code hàm đó của bạn.
Bớt lại đi
 
Lần chỉnh sửa cuối:
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Uhm Tuân, 2 hàm rtcMsgBox và rtcInput mình chưa debug, mình có ghi trên github.
À, Tuân khai báo sai rồi, Optional trong VB/VBA không có nghĩa là truyền NULL/nil/0, mà là phải truyền vào 1 variant rõ ràng, có varType = VT_ERROR và scode = DISP_E_PARAMNOTFOUND (0x80020004)
Trong C, Windows SDK thì VARIANT = Variant/TVarData/OleVariant trong Delphi.
Khác cái tên, type thôi, chứ ở dưới, cấu trúc record/struct, trong memory đều là VARIANT hết. Miễn sao truyền đúng là được. Mã máy nó không biết Variant, variiếc gì đâu.
LPVARIANT = VARIANT * trong C, = PVarData, PVariant, POleVariant trong Delphi.

Trong code VBA của Tuân phải là ByRef nhen, không phải ByVal
Bạn @Kiều Mạnh khiêm tốn lại chút, chỉ là 1 cái hàm không có gì mà phải nổ là độc nhất, chưa bao giờ có, rồi không share, cả Dll cũng không úp. Bạn up dll đi, tôi cho bạn lại code hàm đó của bạn.
Bớt lại đi
chỉ là cái hàm thôi ... nhưng chưa thấy cái thứ 2 thì tạm keo nó là thế ... khi nào thấy cái thứ 2 thì keo lại nha
ko có gì mà nổ cả ... đó là sự thật ... ko tin lục hết GPE lên mà xem :D:p

mới thử lại trên Office thường ... ko có viền bao quanh màu xanh .... chỉ có vậy còn lại chạy tốt
còn nhiều thứ nữa rảnh mới xem lại sau

đồng ý là sau bài này sẻ bớt lại hehehehehe-0-0-0-
 
Lần chỉnh sửa cuối:
Upvote 0

ThangCuAnh

Mới rờ Ét xeo
Tham gia
1/12/17
Bài viết
882
Được thích
780
Giới tính
Nam
Nghề nghiệp
Coder nghỉ hưu, RCE dạo
Share code, public cho mọi người thì tốt, chứ đúng là share code cho cậu là đúng sai lầm.
Cậu chỉ biết đạo code, copy & paste code của người khác cho ra 1 đống tả pín lù, mà chả hiểu cái gì bên trong, ở dưới cả.
 
Upvote 0

Kiều Mạnh

IIIIIIIIIIIIIIIII
Tham gia
9/6/12
Bài viết
4,718
Được thích
3,372
Giới tính
Nam
Share code, public cho mọi người thì tốt, chứ đúng là share code cho cậu là đúng sai lầm.
Cậu chỉ biết đạo code, copy & paste code của người khác cho ra 1 đống tả pín lù, mà chả hiểu cái gì bên trong, ở dưới cả.
mạnh cảm ơn nhưng code đã chia sẻ nha. ... nhưng cái gì ra cái đó ... ko phải copy tất cả của ai đó hết đâu
nói vậy là thái quá đấy ... xem xét lại
Bài đã được tự động gộp:

thôi ko nói qua lại nũa mất lòng ... vẫn câu nói mấy na9m trước ... Mạnh rất ngưởng mộ nếu có đi qua bình dương alo nhậu vài lon
 
Lần chỉnh sửa cuối:
Upvote 0

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,543
Được thích
9,973
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Uhm Tuân, 2 hàm rtcMsgBox và rtcInput mình chưa debug, mình có ghi trên github.
À, Tuân khai báo sai rồi, Optional trong VB/VBA không có nghĩa là truyền NULL/nil/0, mà là phải truyền vào 1 variant rõ ràng, có varType = VT_ERROR và scode = DISP_E_PARAMNOTFOUND (0x80020004)
Trong C, Windows SDK thì VARIANT = Variant/TVarData/OleVariant trong Delphi.
Khác cái tên, type thôi, chứ ở dưới, cấu trúc record/struct, trong memory đều là VARIANT hết. Miễn sao truyền đúng là được. Mã máy nó không biết Variant, variiếc gì đâu.
LPVARIANT = VARIANT * trong C, = PVarData, PVariant, POleVariant trong Delphi.

Trong code VBA của Tuân phải là ByRef nhen, không phải ByVal
Bạn @Kiều Mạnh khiêm tốn lại chút, chỉ là 1 cái hàm không có gì mà phải nổ là độc nhất, chưa bao giờ có, rồi không share, cả Dll cũng không úp. Bạn up dll đi, tôi cho bạn lại code hàm đó của bạn.
Bớt lại đi

Em đã sửa khai báo API trong VBA chuyển ByVal về ByRef nhưng vẫn crash.
C#:
Declare PtrSafe Function VBAInputBox2 Lib _
                        "C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL" _
                        Alias "rtcInputBox" _
                            (pvarPrompt As Variant, _
                             pvarTitle As Variant, _
                             pvarDefault As Variant, _
                             pvarXPos As Variant, _
                             pvarYPos As Variant, _
                             pvarHelpFile As Variant, _
                             pvarContext As Variant) As Variant

Sub TestVBAMon()
    VBAInputBox2 "Tuan", "abc", "", 0, 0, "", 0
End Sub

Khi nào rảnh anh kiểm tra xem?
 
Upvote 0
Web KT

Group

DIỄN ĐÀN GIẢI PHÁP EXCEL
Top Bottom