Xây dựng RPC Server cho Excel / Access bằng Delphi – Tận dụng lại DLL và bảo mật mã nguồn (3 người xem)

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

Tôi tuân thủ nội quy khi đăng bài

phuongnam366377

Thành viên thường trực
Tham gia
25/10/19
Bài viết
246
Được thích
231

Xây dựng RPC Server cho Excel / Access bằng Delphi – Tận dụng lại DLL và bảo mật mã nguồn​

Trong quá trình phát triển các hệ thống tự động hóa cho Excel / Access, chúng ta thường gặp một vấn đề quen thuộc:
  • Logic xử lý nằm ở nhiều máy
  • Mã nguồn VBA dễ bị lộ
  • Khó cập nhật khi thay đổi thuật toán
Một giải pháp khá thú vị là đưa toàn bộ logic lên Server và để Excel / Access gọi từ xa qua RPC (Remote Procedure Call).
Điều này thực chất không phải là công nghệ mới – nó đã tồn tại hơn 30 năm, nhưng ngày nay vẫn được dùng trong nhiều hệ thống hiện đại.

1. Một chút lịch sử: từ DCOM đến gRPC​

Trước đây hệ sinh thái Windows của Microsoft đã sử dụng DCOMCOM+ để các chương trình gọi hàm từ xa.
Ví dụ:
  • Microsoft Excel
  • Microsoft Access
có thể gọi các COM component chạy trên server.
Cách này từng rất phổ biến trong các hệ thống:
  • ERP
  • hệ thống ngân hàng
  • hệ thống doanh nghiệp Windows
Sau này khi internet phát triển, các công nghệ RPC hiện đại xuất hiện như:
  • gRPC
  • REST API
  • Microservices
Dù công nghệ thay đổi, ý tưởng cốt lõi vẫn giống nhau:
Máy khách gọi một hàm trên máy chủ như gọi hàm local.

2. RPC Server cho Excel / Access hoạt động như thế nào?​

Kiến trúc đơn giản:
Mã:
Excel / Access (VBA)
        │
        │ RPCExecute()
        ▼
RPC Server (Delphi)
        │
        │ Load DLL Plugin
        ▼
Business Logic DLL
Luồng xử lý:
1️⃣ Excel / Access gọi
Mã:
RPCExecute("127.0.0.1",9000,"MathPlugin.dll","Add","[5,7]")
2️⃣ RPC Server nhận request
3️⃣ Server load DLL plugin
4️⃣ Gọi hàm bên trong DLL
5️⃣ Trả kết quả lại cho Excel

3. Ví dụ thực tế​

VBA trong Excel / Access​

Mã:
Sub Test()

    Debug.Print RPCExecute("127.0.0.1", 9000, "MathPlugin.dll", "Add", "[5,7]")

End Sub
Kết quả:
Mã:
12

DLL phía Server (Delphi)​

Mã:
function Add(A, B: Integer): Integer;
begin
  Result := A + B;
end;
Server sẽ gọi hàm này và trả kết quả về cho VBA.

4. Điểm mạnh cực kỳ thú vị của mô hình này​

1️⃣ Tái sử dụng DLL đã viết​

Nếu trước đây bạn đã viết:
Mã:
Math.dll
Finance.dll
AI.dll
Bạn không cần viết lại.
Chỉ cần:
Mã:
RPCLoadDLL('MathPlugin.dll');
Server có thể gọi trực tiếp.

2️⃣ Bảo mật mã nguồn​

VBA thường bị lộ logic vì người dùng có thể:
Mã:
Alt + F11
xem toàn bộ code.
Với RPC:
Mã:
Excel / Access
    ↓
chỉ gọi hàm
Logic nằm ở server.
Người dùng không thể xem thuật toán.

3️⃣ Cập nhật cực nhanh​

Nếu thay đổi thuật toán:
Mã:
Replace DLL
Không cần:
  • cập nhật Excel
  • cập nhật Access
  • gửi file mới
Toàn bộ client tự dùng logic mới ngay lập tức.

4️⃣ Hỗ trợ nhiều ngôn ngữ​

Client có thể viết bằng:
  • VBA
  • Python
  • C#
  • JavaScript
  • Go
Miễn là gửi request đúng format.

5. So sánh với các công nghệ RPC khác​

Công nghệNền tảngĐộ phức tạpPhù hợp Excel/Access
COM / DCOMWindowsTrung bình✔ tốt
REST APICross-platformDễ
gRPCCross-platformKhó hơn
RPC + DLL PluginWindowsRất đơn giản✔✔✔
Điểm đặc biệt của mô hình RPC + DLL Plugin:
  • cực nhẹ
  • không cần framework lớn
  • tận dụng code Delphi/C++ cũ

6. Khi nào nên dùng mô hình này?​

Rất phù hợp khi:
✔ Hệ thống nội bộ
✔ Excel / Access làm frontend
✔ Logic phức tạp cần bảo mật
✔ Muốn tái sử dụng DLL cũ
Ví dụ:
  • hệ thống tính giá
  • hệ thống phân tích dữ liệu
  • hệ thống tài chính
  • hệ thống AI nội bộ

7. Kết luận​

Công nghệ như DCOM có thể không còn phổ biến như trước, nhưng ý tưởng Remote Procedure Call vẫn là nền tảng của rất nhiều hệ thống hiện đại.
Ngày nay chúng ta thấy:
  • gRPC
  • microservices
  • API server
đều dựa trên cùng một nguyên lý:
Client chỉ gọi hàm – Server xử lý logic.
Đối với các hệ thống Excel / Access, việc xây dựng một RPC Server với DLL plugin là một giải pháp:
  • nhẹ
  • nhanh
  • bảo mật
  • tái sử dụng code
và cực kỳ phù hợp cho các hệ thống doanh nghiệp nội bộ.

 
1/ Mới Keo ChatGPT viết giới thiệu xong

2/ Code cơ bản tạm xong

3/ Tinh chỉnh xoá rác và các hàm lưu hành nội bộ xong ....

4/ xem xét hôm nào có gió lên thì úp file chính thức lên Github + Mã nguồn mở Plugins và chỉ dẫn cách mở rộng nó

5/ vậy là ai đó cũng có thể viết Server và Client rồi đó

ChatGPT Tóm tắt​

"RPC Data Server cung cấp một cách đơn giản để biến Microsoft Access thành database server mini,

giúp nhiều client truy cập an toàn hơn thông qua RPC và Plugin Architecture.

ý tưởng thực sự giống một “Mini SQL Server” nhưng đơn giản và nhẹ hơn rất nhiều."



Rảnh hôm nào tinh chỉnh mục số 3 xong thì úp file sau
 
Không thể ngờ được nhờ ChatGPT lại có thể viết ra hệ thống máy chủ SQL mini cho RPC server chạy đa luồng và xử lý Async từ các Client kết nối Pool

và máy chủ VBA hay Exe có thể gián sát Realtime: và gỡ lỗi trực tiếp trong Immediate Windows

Tỉnh chỉnh vài ngày Xong cho Lên Github


RPC SERVER MONITOR

1773200215617.png


Immediate Windows VBE


1773200456434.png
 
Không thể ngờ được nhờ ChatGPT lại có thể viết ra hệ thống máy chủ SQL mini cho RPC server chạy đa luồng và xử lý Async từ các Client kết nối Pool

và máy chủ VBA hay Exe có thể gián sát Realtime: và gỡ lỗi trực tiếp trong Immediate Windows

Tỉnh chỉnh vài ngày Xong cho Lên Github


RPC SERVER MONITOR

View attachment 311196


Immediate Windows VBE


View attachment 311197
Exe server hay web server thì có thấy, vậy Máy chủ VBA hay máy chủ Excel là gì vậy Xếp?
Trong delphi người ta làm như thế này không à:
1773245873124.png
 
Lần chỉnh sửa cuối:
  • Thích
Reactions: A-T
To A-T
Tôi tính úp dự án đang tinh chỉnh lên đây cho ai tò mò thử chơi cho vui và tìm ra lỗi + Fix nó nhưng nghĩ lại thôi bỏ đi vì úp lên bài sau 24h nó không cho chỉnh sửa nếu có sai vô tình thành bài rác và rồi lại có câu hỏi lặp lại thừa

Cách quản lý đối phó thay vì quay đầu là bờ thì quay đầu là xa bờ ... vô tình biến họ thành người không muốn viết bài nữa và tương lai cứ như vậy toàn bài rác ( vì sai không có của chỉnh sửa ) nếu ai đó chưa đọc kỹ theo trình tự và logis các bài liên quan .... thôi bỏ đi chuyện của nó

"bất cứ một kết quả nào cũng có nguyên nhân sinh ra nó ... có thể kết quả này lại là nguyên nhân sinh ra kết quả khác ----> khách vắng teo :p"
 
  • Thích
Reactions: A-T
To A-T
Tôi tính úp dự án đang tinh chỉnh lên đây cho ai tò mò thử chơi cho vui và tìm ra lỗi + Fix nó nhưng nghĩ lại thôi bỏ đi vì úp lên bài sau 24h nó không cho chỉnh sửa nếu có sai vô tình thành bài rác và rồi lại có câu hỏi lặp lại thừa

Cách quản lý đối phó thay vì quay đầu là bờ thì quay đầu là xa bờ ... vô tình biến họ thành người không muốn viết bài nữa và tương lai cứ như vậy toàn bài rác ( vì sai không có của chỉnh sửa ) nếu ai đó chưa đọc kỹ theo trình tự và logis các bài liên quan .... thôi bỏ đi chuyện của nó

"bất cứ một kết quả nào cũng có nguyên nhân sinh ra nó ... có thể kết quả này lại là nguyên nhân sinh ra kết quả khác ----> khách vắng teo :p"
Bản thân Excel tự nó là một COM server/client, nên dùng bất kỳ ngôn ngữ nào thì cũng phải dựa trên công nghệ COM, muốn sync realtime thì phải bẫy sự kiện sheet change (dùng EventSinks của Bác Lý Bình - BinhLy) nếu bạn dùng Delphi, còn không thì chỉ là Manual Sync. Mình không thấy bạn giải thích cái này. Bản thân Excel và các kết nối ADO bảo mật kém, nên chỉ dùng cho những tác vụ không cần bảo mật.
Mình thấy có cty Việt Nam làm excel server nhưng chưa thử:



 
To A-T
Tôi tính úp dự án đang tinh chỉnh lên đây cho ai tò mò thử chơi cho vui và tìm ra lỗi + Fix nó nhưng nghĩ lại thôi bỏ đi vì úp lên bài sau 24h nó không cho chỉnh sửa nếu có sai vô tình thành bài rác và rồi lại có câu hỏi lặp lại thừa

Cách quản lý đối phó thay vì quay đầu là bờ thì quay đầu là xa bờ ... vô tình biến họ thành người không muốn viết bài nữa và tương lai cứ như vậy toàn bài rác ( vì sai không có của chỉnh sửa ) nếu ai đó chưa đọc kỹ theo trình tự và logis các bài liên quan .... thôi bỏ đi chuyện của nó

"bất cứ một kết quả nào cũng có nguyên nhân sinh ra nó ... có thể kết quả này lại là nguyên nhân sinh ra kết quả khác ----> khách vắng teo :p"
Tùy theo người nghĩ theo hướng nào, tiêu cực hay là tích cực. Nếu không chỉnh sửa được bài thì bạn có thể viết bài tiếp theo để đính chính (Nếu có sai và không chỉnh sửa được) về bài trước đó. Làm như thế mọi người sẽ dễ tiếp cận hơn.
 
Lần chỉnh sửa cuối:
Bản thân Excel tự nó là một COM server/client, nên dùng bất kỳ ngôn ngữ nào thì cũng phải dựa trên công nghệ COM, muốn sync realtime thì phải bẫy sự kiện sheet change (dùng EventSinks của Bác Lý Bình - BinhLy) nếu bạn dùng Delphi, còn không thì chỉ là Manual Sync. Mình không thấy bạn giải thích cái này. Bản thân Excel và các kết nối ADO bảo mật kém, nên chỉ dùng cho những tác vụ không cần bảo mật.
Mình thấy có cty Việt Nam làm excel server nhưng chưa thử:



Tôi viết API xem code sau tự hiểu còn nói câu từ này nọ lỡ nói sai lại thêm bài ... kiếm thức của bạn tôi biết mờ nhìn là bạn biết thôi

Mã:
Option Explicit
'====================================================
' RPC DLL API
'====================================================
Public Declare PtrSafe Sub RPCServerStartAPI Lib "RPCServerCore.dll" (ByVal IP As String, ByVal Port As Long)

Declare PtrSafe Function RPC_RestartServer Lib "RPCServerCore.dll" () As Long

Public Declare PtrSafe Sub RPCServerStopAPI Lib "RPCServerCore.dll" ()

Public Declare PtrSafe Sub RPCLoadFolderAPI Lib "RPCServerCore.dll" (ByVal Folder As Variant)

Public Declare PtrSafe Function RPC_GetRecentLogsV Lib "RPCServerCore.dll" () As Variant

'===== SERVER STATS =====
Public Declare PtrSafe Function RPC_GetClientCount Lib "RPCServerCore.dll" () As Long

Public Declare PtrSafe Function RPC_GetThreadCount Lib "RPCServerCore.dll" () As Long
Public Declare PtrSafe Function RPC_GetThreadPoolMax Lib "RPCServerCore.dll" () As Long

Public Declare PtrSafe Function RPC_GetRequestCount Lib "RPCServerCore.dll" () As LongLong

'====================================================
' INTERNAL STATE
'====================================================

Private NextLogTime As Date
Private LogRunning As Boolean
Private LastLog As String


'====================================================
' START SERVER
'====================================================

Public Sub RPC_StartServer()
    Dim PluginPath As String
   
    PluginPath = ThisWorkbook.Path & "\Plugins\"
   
    RPCServerStartAPI "127.0.0.1", 9000
   
    RPCLoadFolderAPI PluginPath
   
    Debug.Print "RPC Server Started"
   
    RPC_StartLogMonitor
End Sub

Sub RestartRPC()
    If RPC_RestartServer = 1 Then
        MsgBox "Server restarted"
    Else
        MsgBox "Restart failed"
    End If
End Sub

'====================================================
' STOP SERVER
'====================================================
Public Sub RPC_StopServer()
    RPC_StopLogMonitor
    RPCServerStopAPI
   
    Debug.Print "RPC Server Stopped"
End Sub


'====================================================
' START LOG MONITOR
'====================================================

Public Sub RPC_StartLogMonitor()
    If LogRunning Then Exit Sub
    LogRunning = True
    RPC_PollLogs
End Sub


'====================================================
' STOP LOG MONITOR
'====================================================

Public Sub RPC_StopLogMonitor()
    On Error Resume Next
    LogRunning = False
    Application.OnTime NextLogTime, "RPC_PollLogs", , False
End Sub


'====================================================
' POLL SERVER LOGS
'====================================================

Public Sub RPC_PollLogs()
    Dim s As String
    If Not LogRunning Then Exit Sub
    On Error Resume Next
    s = RPC_GetRecentLogsV
    If s <> "" And s <> LastLog Then
        Debug.Print s
        LastLog = s
    End If
   
    NextLogTime = Now + TimeValue("00:00:02")
    Application.OnTime NextLogTime, "RPC_PollLogs"
End Sub


'====================================================
' SERVER STATS
'====================================================

Public Sub RPC_ServerStats()
    Dim Clients As Long
    Dim Threads As Long
    Dim PoolMax As Long
    Dim Requests As LongLong
   
    Clients = RPC_GetClientCount
    Threads = RPC_GetThreadCount
    PoolMax = RPC_GetThreadPoolMax
    Requests = RPC_GetRequestCount
   
    Debug.Print "------ RPC SERVER STATS ------"
    Debug.Print "Clients  : "; Clients
    Debug.Print "Threads  : "; Threads
    Debug.Print "PoolMax  : "; PoolMax
    Debug.Print "Requests : "; Requests
    Debug.Print "------------------------------"
End Sub


'====================================================
' QUICK COMMANDS
'====================================================

Public Sub RPC_Run()
    RPC_StartServer
End Sub

Public Sub RPC_Stop()
    RPC_StopServer
End Sub

Public Sub RPC_Stats()
    RPC_ServerStats
End Sub

Khi chạy trên VBA xong thì các máy khách kết nối nó ghi log sự kiện trong Immadiate Windows

1773283602807.png

Mục đích viết nó cho người dùng cuối gỡ lỗi nếu sai Path,tên hàm các tham số vvv ..... còn cái dấu ?? đó là Icon nên lỗi trên VBA còn trên Exe Delphi nhìn thấy ICon biểu tượng cho đẹp chút thôi
 
Cách quản lý đối phó thay vì quay đầu là bờ thì quay đầu là xa bờ
Cách đây vài tuần tôi đã giải thích 1 lần rồi, thành viên phải có đủ 400 bài viết mới có quyền chỉnh sửa bài viết của mình mà không bị giới hạn thời gian là 24 giờ kể từ thời gian đăng bài. Bạn cố tình không hiểu à? Bạn vẫn còn 1 nick thứ 2, cũng khá nổi và có trình độ tại sao không dùng (để mở chủ đề mới)?
Còn nick này thì viết bài có giá trị cho đủ 400 rồi hãy mở chủ đề?

Tôi sẽ không giải thích lại. Và đây là nhắc nhở lần cuối, đừng công kích ban điều hành nữa.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Tôi viết API xem code sau tự hiểu còn nói câu từ này nọ lỡ nói sai lại thêm bài ... kiếm thức của bạn tôi biết mờ nhìn là bạn biết thôi

Mã:
Option Explicit
'====================================================
' RPC DLL API
'====================================================
Public Declare PtrSafe Sub RPCServerStartAPI Lib "RPCServerCore.dll" (ByVal IP As String, ByVal Port As Long)

Declare PtrSafe Function RPC_RestartServer Lib "RPCServerCore.dll" () As Long

Public Declare PtrSafe Sub RPCServerStopAPI Lib "RPCServerCore.dll" ()

Public Declare PtrSafe Sub RPCLoadFolderAPI Lib "RPCServerCore.dll" (ByVal Folder As Variant)

Public Declare PtrSafe Function RPC_GetRecentLogsV Lib "RPCServerCore.dll" () As Variant

'===== SERVER STATS =====
Public Declare PtrSafe Function RPC_GetClientCount Lib "RPCServerCore.dll" () As Long

Public Declare PtrSafe Function RPC_GetThreadCount Lib "RPCServerCore.dll" () As Long
Public Declare PtrSafe Function RPC_GetThreadPoolMax Lib "RPCServerCore.dll" () As Long

Public Declare PtrSafe Function RPC_GetRequestCount Lib "RPCServerCore.dll" () As LongLong

'====================================================
' INTERNAL STATE
'====================================================

Private NextLogTime As Date
Private LogRunning As Boolean
Private LastLog As String


'====================================================
' START SERVER
'====================================================

Public Sub RPC_StartServer()
    Dim PluginPath As String
  
    PluginPath = ThisWorkbook.Path & "\Plugins\"
  
    RPCServerStartAPI "127.0.0.1", 9000
  
    RPCLoadFolderAPI PluginPath
  
    Debug.Print "RPC Server Started"
  
    RPC_StartLogMonitor
End Sub

Sub RestartRPC()
    If RPC_RestartServer = 1 Then
        MsgBox "Server restarted"
    Else
        MsgBox "Restart failed"
    End If
End Sub

'====================================================
' STOP SERVER
'====================================================
Public Sub RPC_StopServer()
    RPC_StopLogMonitor
    RPCServerStopAPI
  
    Debug.Print "RPC Server Stopped"
End Sub


'====================================================
' START LOG MONITOR
'====================================================

Public Sub RPC_StartLogMonitor()
    If LogRunning Then Exit Sub
    LogRunning = True
    RPC_PollLogs
End Sub


'====================================================
' STOP LOG MONITOR
'====================================================

Public Sub RPC_StopLogMonitor()
    On Error Resume Next
    LogRunning = False
    Application.OnTime NextLogTime, "RPC_PollLogs", , False
End Sub


'====================================================
' POLL SERVER LOGS
'====================================================

Public Sub RPC_PollLogs()
    Dim s As String
    If Not LogRunning Then Exit Sub
    On Error Resume Next
    s = RPC_GetRecentLogsV
    If s <> "" And s <> LastLog Then
        Debug.Print s
        LastLog = s
    End If
  
    NextLogTime = Now + TimeValue("00:00:02")
    Application.OnTime NextLogTime, "RPC_PollLogs"
End Sub


'====================================================
' SERVER STATS
'====================================================

Public Sub RPC_ServerStats()
    Dim Clients As Long
    Dim Threads As Long
    Dim PoolMax As Long
    Dim Requests As LongLong
  
    Clients = RPC_GetClientCount
    Threads = RPC_GetThreadCount
    PoolMax = RPC_GetThreadPoolMax
    Requests = RPC_GetRequestCount
  
    Debug.Print "------ RPC SERVER STATS ------"
    Debug.Print "Clients  : "; Clients
    Debug.Print "Threads  : "; Threads
    Debug.Print "PoolMax  : "; PoolMax
    Debug.Print "Requests : "; Requests
    Debug.Print "------------------------------"
End Sub


'====================================================
' QUICK COMMANDS
'====================================================

Public Sub RPC_Run()
    RPC_StartServer
End Sub

Public Sub RPC_Stop()
    RPC_StopServer
End Sub

Public Sub RPC_Stats()
    RPC_ServerStats
End Sub

Khi chạy trên VBA xong thì các máy khách kết nối nó ghi log sự kiện trong Immadiate Windows

View attachment 311202

Mục đích viết nó cho người dùng cuối gỡ lỗi nếu sai Path,tên hàm các tham số vvv ..... còn cái dấu ?? đó là Icon nên lỗi trên VBA còn trên Exe Delphi nhìn thấy ICon biểu tượng cho đẹp chút thôi
Vâng, nhìn code là biết Sư phụ của Chat GPT => Nên không thể góp ý thêm!
 
---

# RPCServerCore.dll – Xây dựng hệ thống RPC cho Excel / Access / Delphi bằng Plugin

Trong quá trình làm việc với **Excel, Access hoặc các ứng dụng văn phòng**, đôi khi chúng ta cần:

✔ xử lý dữ liệu lớn
✔ thực hiện các phép tính phức tạp
✔ truy vấn dữ liệu từ server
✔ mở rộng chức năng bằng thư viện ngoài

Để giải quyết vấn đề này, tôi xây dựng một **RPC Framework nhỏ gọn** cho phép:

✔ Excel / Access / Delphi / C# / Python gọi hàm từ xa
✔ mở rộng chức năng bằng Plugin DLL
✔ xử lý tập trung tại RPC Server

Thư viện trung tâm của hệ thống là:

Mã:
RPCServerCore.dll


1. Kiến trúc hệ thống

Hệ thống RPC gồm 3 thành phần chính:

Mã:
Client
|
| RPC Call
|
RPC Server (RPCServerCore.dll)
|
| Load Plugin
|
Plugin DLL

Ví dụ:

Mã:
Excel VBA
|
CallRPC("Unique",[1,2,3])
|
RPC Server
|
MathPlugin.dll
|
Unique()


2. Viết Plugin cho RPC Server

Plugin là DLL viết bằng Delphi.

Mỗi plugin export hàm dạng:

Mã:
function PluginName(const P: array of Variant): Variant; stdcall;

Ví dụ plugin tính trung bình:

Mã:
function AveragePlugin(const P: array of Variant): Variant; stdcall;
var
I: Integer;
S: Double;
begin
for I := 0 to High(P) do
S := S + VarAsType(P[I], varDouble);

Result := S / Length(P);
end;

Export hàm:

Mã:
exports
AveragePlugin name 'Average';


3. Ví dụ Plugin lọc dữ liệu Unique
Plugin loại bỏ phần tử trùng:

Mã:
function UniquePlugin(const P: array of Variant): Variant; stdcall;
var
Dict: TDictionary<Variant, Byte>;
List: TList<Variant>;
I: Integer;
OutArr: Variant;
begin

Dict := TDictionary<Variant, Byte>.Create;
List := TList<Variant>.Create;

try
for I := 0 to High(P) do
if not Dict.ContainsKey(P[I]) then
begin
Dict.Add(P[I],0);
List.Add(P[I]);
end;

OutArr := VarArrayCreate([1, List.Count, 1, 1], varVariant);

for I := 0 to List.Count - 1 do
  OutArr[I + 1,1] := List[I];

Result := OutArr;

finally
Dict.Free;
List.Free;
end;

end;

4. Gọi hàm RPC từ Excel VBA

Ví dụ gọi hàm Unique:

Mã:
Sub TestUnique()

Dim V As Variant

V = CallRPC("Unique", "[1,2,3,3,5,5,7,8,8]")

If VarType(V) = vbString Then
Debug.Print V
Exit Sub
End If

If IsArray(V) Then
Sheet1.Range("A1").Resize(UBound(V, 1), 1).Value = V
End If

End Sub

Kết quả:

Mã:
1
2
3
5
7
8


5. Truyền dữ liệu từ Excel Range

Excel có thể truyền cả vùng dữ liệu sang RPC.

Mã:
Private Function RangeToRPCArray(r As Range) As String

Dim V As Variant
Dim rr As Long, c As Long
Dim S As String

V = r.Value

S = "["

For rr = 1 To UBound(V, 1)

For c = 1 To UBound(V, 2)

    If Len(V(rr, c)) > 0 Then
        S = S & V(rr, c) & ","
    End If

Next c

Next rr

If Right$(S, 1) = "," Then
S = Left$(S, Len(S) - 1)
End If

S = S & "]"

RangeToRPCArray = S

End Function


6. Sử dụng với Access / ADO
Access cũng có thể gọi RPC:

Mã:
CallRPC("GetRsFromServer","SELECT * FROM Customers")

Server trả về **Recordset**.


7. Các client có thể sử dụng RPC
Framework có thể gọi từ:

• Excel VBA
• Microsoft Access
• Delphi
• C#
• Python

Do RPC dùng **TCP Socket** nên **không phụ thuộc nền tảng**.

---

8. Lưu ý khi chạy RPC Server trong Excel

RPCServerCore.dll cho phép chạy server trực tiếp từ Excel VBA.

Tuy nhiên điều này **không nên dùng trong hệ thống thực tế**.

Các vấn đề có thể xảy ra:

• Excel không phải môi trường server
• Excel dễ treo khi nhiều request
• Excel không quản lý thread tốt
• khi đóng Excel server sẽ dừng
• có thể phát sinh lỗi COM hoặc memory leak

➡ Vì vậy:

Excel chỉ nên dùng để test RPC.

---

9. Khuyến nghị triển khai thực tế

Nên chạy server bằng:

Mã:
RPCServer.exe

hoặc

Mã:
Windows Service

Ví dụ:

Mã:
RPCServerService.exe

Ưu điểm:

✔ chạy nền ổn định
✔ xử lý nhiều client
✔ quản lý thread tốt
✔ không phụ thuộc Excel
✔ dễ ghi log

Excel / Access chỉ đóng vai trò:

RPC Client


10. Mã nguồn demo

Github:


Repository gồm:

1️⃣ RPC_Access_ADO_Client_Test.xlsb
Excel đóng vai trò RPC Client

2️⃣ RPC_Access_ADO_Server_Test.xlsb
Excel chạy thử RPC Server

3️⃣ RPC_Excel_Client_MathPlugin.xlsb
Excel gọi MathPlugin.dll

4️⃣ AccessPlugin.dll
Plugin đọc/ghi Microsoft Access

5️⃣ MathPlugin.dll
Plugin các hàm toán học (1D, 2D array)

6️⃣ RPCServer.exe
RPC Server độc lập

7️⃣ RPCServerCore.dll

Bao gồm:

• RPC Server
• RPC Client
• Plugin Loader

Lưu ý

Các file Excel trong repo chủ yếu dùng để **test RPC**.

Trong hệ thống thực tế nên chạy server bằng:

Mã:
RPCServer.exe
hoặc
Windows Service

để đảm bảo **ổn định khi nhiều client kết nối**.



Downloads
 
Lần chỉnh sửa cuối:
Vâng, nhìn code là biết Sư phụ của Chat GPT => Nên không thể góp ý thêm!
Tôi vừa upload phiên bản mới lên GitHub. Nếu bạn rảnh có thể thử chạy file:

RPC_Access_ADO_Server_Test.xlsb

Khi chạy file này bạn sẽ thấy **RPC Server ghi log các tham số mà máy khách gửi lên**, đúng như mô tả trong bài trước.

Cách tôi xử lý sự kiện trong RPC Server
Sau khi thử nhiều cách khác nhau, tôi chọn phương án sau vì thấy **đơn giản và ổn định nhất**.

Nguyên lý hoạt động:

1. Khi máy khách kết nối


Khi client gửi request lên server:
* Server nhận request
* phân tích các tham số
* ghi log
* lưu log đó vào RAM

2. Khi VBA cần xem log
Trong Excel VBA có thể gọi hàm: RPC_GetRecentLogsV

Hàm này sẽ:
* đọc log đang lưu trong RAM
* trả lại cho VBA
* VBA có thể hiển thị log cho người dùng cuối

3. Quản lý bộ nhớ log
Log chỉ được lưu trong RAM với giới hạn:
MAX_LOG = 2048

Khi số dòng log vượt quá giới hạn này:
* toàn bộ log cũ sẽ bị xoá
* log mới sẽ bắt đầu được ghi lại

Cách này giúp:
• tránh tiêu tốn RAM
• server chạy lâu vẫn ổn định

Các API dùng cho hệ thống log
Các hàm sau được export từ thư viện và có thể sử dụng chung cho:

* RPCServer.exe
* Excel VBA
* các client khác
Mã:
procedure RPC_SetLogCallback(LogProc: TRPCLogProc); stdcall;

procedure RPC_EnableLogLevel(Level: Integer; Enabled: BOOL); stdcall;

function RPC_GetRecentLogs: PWideChar; stdcall;

function RPC_GetRecentLogsV: OleVariant; stdcall;

procedure RPC_ClearLogs; stdcall;

Lưu ý:
Hàm: RPC_GetRecentLogsV được thiết kế riêng cho **VBA**.

Lý do là VBA có thể nhận trực tiếp **Variant**, nên không cần phải:

* chuyển đổi `PWideChar`
* gọi lại các hàm API của Microsoft để convert sang String.


Vai trò của Excel trong hệ thống
Trong kiến trúc này:

Excel chỉ đóng vai trò RPC Client.

Toàn bộ logic xử lý nằm ở: RPC Server + Plugin DLL

Do đó hệ thống:
* không phụ thuộc COM Event của Excel
* Excel chỉ gửi request và nhận kết quả
* server xử lý hoàn toàn độc lập
 
Lần chỉnh sửa cuối:
File sau tiện tay làm Ví dụ mẫu tặng cho Tín đồ Excel

1/ Tải các File trên Github về

2/ chạy máy chủ RPCServer.exe

3/ Mởi file sau lên gõ hàm ra như hình sau

4/ Thay đổi lại SQL và DBpath theo thực tế trên máy Bạn

1773492185209.png

Code cơ bản như sau còn ai đó sử dụng tuỳ chỉnh theo sở thích và mục số 4

Mã:
Option Explicit
'=========================================================
' RPC Excel Client Module
'
' Author  : Kieu Manh
' Country : Vietnam
'
' Description
' High Performance RPC Client for Excel
'
' Features
' - RPC Query
' - Recordset ? Array
' - Excel Write Engine
' - Query Cache
' - Dashboard Refresh
'
'=========================================================
'---------------------------------------------------------
' RPC SERVER CONFIG
'---------------------------------------------------------
Public Const RPC_IP As String = "127.0.0.1"
Public Const RPC_PORT As Long = 9000
Public Const RPC_DLL As String = "AccessPlugin.dll"
Public Const RPC_FUNC As String = "GETRS"
Public Const RPC_DB As String = "C:\Database_Server\QLBHPN.accdb"
'---------------------------------------------------------
' CACHE ENGINE
'---------------------------------------------------------
Private RPCCache As Object

Private Sub RPCInitCache()
    If RPCCache Is Nothing Then
        Set RPCCache = CreateObject("Scripting.Dictionary")
    End If
End Sub

Public Sub RPCClearCache()
    RPCInitCache
    RPCCache.RemoveAll
End Sub
'---------------------------------------------------------
' RPC CALL
'---------------------------------------------------------
Public Function RPCGetRS(SQL As String) As Object
    On Error GoTo ERR_HANDLER
    Set RPCGetRS = RPC_GetRecordset( _
                        RPC_IP, _
                        RPC_PORT, _
                        RPC_DLL, _
                        RPC_FUNC, _
                        RPC_DB, _
                        SQL)

    Exit Function
ERR_HANDLER:
    MsgBox "RPC Error:" & vbCrLf & _
           Err.Description, _
           vbCritical, _
           "RPC Error"
    Set RPCGetRS = Nothing
End Function
'---------------------------------------------------------
' RECORDSET ? ARRAY
'---------------------------------------------------------
 Public Function RS2Array(RS As Object, Optional Header As Boolean = True)
    Dim Data
    Dim Arr()
    Dim ColNames()
    Dim r As Long
    Dim c As Long
    Dim Rows As Long
    Dim Cols As Long
    Dim Offset As Long

    If RS Is Nothing Then
        RS2Array = CVErr(xlErrNA)
        Exit Function
    End If

    If RS.EOF Then
        RS2Array = ""
        Exit Function
    End If

    ' Lưu tên c?t
    If Header Then
        ReDim ColNames(0 To RS.Fields.Count - 1)
        For c = 0 To RS.Fields.Count - 1
            ColNames(c) = RS.Fields(c).Name
        Next c
    End If

    ' L?y d? li?u nhanh
    Data = RS.GetRows

    RS.Close
    Set RS = Nothing

    Cols = UBound(Data, 1) + 1
    Rows = UBound(Data, 2) + 1

    If Header Then
        Offset = 1
    Else
        Offset = 0
    End If

    ReDim Arr(0 To Rows - 1 + Offset, 0 To Cols - 1)

    ' Header
    If Header Then
        For c = 0 To Cols - 1
            Arr(0, c) = ColNames(c)
        Next c
    End If

    ' Data
    For r = 0 To Rows - 1
        For c = 0 To Cols - 1
            Arr(r + Offset, c) = Data(c, r)
        Next c
    Next r
    RS2Array = Arr
End Function

'---------------------------------------------------------
' SIMPLE RPC QUERY
'
' Example
' =RPC("SELECT * FROM Orders")
'---------------------------------------------------------
Public Function RPC(SQL As String)
    Dim RS As Object
    Set RS = RPCGetRS(SQL)
    RPC = RS2Array(RS, True)
End Function
'---------------------------------------------------------
' CACHE QUERY
'
' Example
' =RPC_CACHE("SELECT * FROM Orders")
' =RPC_CACHE("SELECT * FROM NhapXuatTon")
'---------------------------------------------------------
Public Function RPC_CACHE(SQL As String)
    Dim RS As Object
    Dim Arr
    Dim Key As String
    RPCInitCache
    Key = RPC_DB & "|" & SQL
    If RPCCache.Exists(Key) Then
        RPC_CACHE = RPCCache(Key)
        Exit Function
    End If

    Set RS = RPCGetRS(SQL)
    Arr = RS2Array(RS, True)
    RPCCache.Add Key, Arr
    RPC_CACHE = Arr
End Function
'---------------------------------------------------------
' EXECUTE SQL ? WRITE TO SHEET
'
' Example
' RPCExec "SELECT * FROM Orders", Range("A1")
'---------------------------------------------------------
Public Sub RPCExec(SQL As String, Target As Range)
    Dim RS As Object
    Dim Arr
    Dim Rows As Long
    Dim Cols As Long
    Application.ScreenUpdating = False
    Set RS = RPCGetRS(SQL)
    Arr = RS2Array(RS, True)
    If IsArray(Arr) = False Then Exit Sub

    Rows = UBound(Arr, 1) + 1
    Cols = UBound(Arr, 2) + 1


    Target.Resize(Rows, Cols).Clear
    Target.Resize(Rows, Cols).Value = Arr
    Application.ScreenUpdating = True
End Sub

'---------------------------------------------------------
' DASHBOARD REFRESH
'---------------------------------------------------------
Public Sub RPCRefresh()
    Application.CalculateFull
End Sub
Public Sub RPCRefreshCache()
    RPCClearCache
    Application.CalculateFull
End Sub

'---------------------------------------------------------
' BIG DATA LOADER
'
' Example
' RPC_BIGLOAD "SELECT * FROM Sales", Range("A1")
'---------------------------------------------------------
Public Sub RPC_BIGLOAD(SQL As String, Target As Range)
    Dim T As Double
    T = Timer
    RPCExec SQL, Target
    MsgBox "Load completed in " & _
           Format(Timer - T, "0.00") & _
           " seconds"
End Sub

xem file và tuỳ chỉnh theo nhu cầu ... tạm dừng chủ đề này ở đây viết bản Pro cao cấp hơn của bản này cho kết nối AccessPlugin.dll đa luồng Pool

Tự đông xoá các kết nỗi lỗi và quá hạn chống Pool đầy và treo máy chủ .... Viết SQLite +++ vvv
 

File đính kèm

Giới thiệu cách xây dựng hàm Excel bằng RPC sử dụng thư viện MathPlugin.dll
do người dùng tự định nghĩa



Giới thiệu hàm RPC_VariantExecute – Gọi hàm từ xa cho Excel​

Trong nhiều trường hợp khi làm việc với Excel VBA, chúng ta muốn:

  • Xử lý dữ liệu lớn nhanh hơn VBA
  • Sử dụng thư viện viết bằng Delphi / C / C#
  • Tách logic xử lý ra khỏi Excel
  • Mở rộng hệ thống bằng Plugin DLL
Để giải quyết các nhu cầu này, tôi xây dựng một cơ chế RPC (Remote Procedure Call) cho phép Excel gọi các hàm xử lý trên một RPC Server.

Hàm trung tâm của cơ chế này là:

RPC_VariantExecute

Hàm này cho phép Excel VBA gọi một hàm trong Plugin DLL trên RPC Server và nhận kết quả trả về dưới dạng Variant.


Kiến trúc hệ thống​

Thư viện trung tâm:

RPCServerCore.dll

Bao gồm các thành phần:

  • RPC Server
  • RPC Client API
  • Plugin Loader
  • Multi-thread + Connection Pool
  • Synchronization
Luồng hoạt động:

Excel VBA
→ RPC_VariantExecute
→ RPCServerCore.dll
→ Plugin DLL
→ Trả kết quả về Excel

Trong mô hình này:

  • Excel đóng vai trò RPC Client
  • RPC Server thực thi các hàm trong Plugin

Khai báo API trong VBA​

Mã:
Public Declare PtrSafe Function RPC_VariantExecute Lib "RPCServerCore.dll" ( _
ByVal Host As Variant, _
ByVal Port As Long, _
ByVal DLLName As Variant, _
ByVal FuncName As Variant, _
ByVal Params As Variant _
) As Variant


Các tham số của hàm​

Host
Địa chỉ IP hoặc hostname của RPC Server

Ví dụ

Mã:
"127.0.0.1"


Port
Cổng TCP mà RPC Server đang lắng nghe

Ví dụ

Mã:
9000


DLLName
Tên Plugin DLL trên server

Ví dụ

Mã:
"MathPlugin.dll"


FuncName
Tên hàm trong Plugin DLL

Ví dụ

Mã:
"Unique"
"Average"


Params
Chuỗi tham số truyền vào hàm.

Định dạng mảng:

Mã:
[value1,value2,value3]

Ví dụ

Mã:
"[1,2,3,4]"
"[10,20,30]"

Server sẽ:

  1. Bỏ dấu [ ]
  2. Tách các phần tử theo dấu ,
  3. Thử chuyển sang kiểu số
  4. Nếu không chuyển được sẽ giữ dạng chuỗi

Giá trị trả về​

Hàm trả về Variant

Kết quả có thể là:

  • Giá trị đơn
  • Mảng 1 chiều (1D Array)
  • Mảng 2 chiều (2D Array)
Nếu có lỗi server sẽ trả về:

Mã:
#RPC_ERROR: message


Ví dụ sử dụng trong VBA​

Mã:
Sub TestUnique()
    Dim V As Variant
    V = CallRPC("Unique", "[1,2,3,3,5,5,7,8,8]")
    Sheet2.Cells.Clear
    Sheet2.Range("A1").Resize(UBound(V, 1), 1).Value = V
End Sub

Kết quả:

1
2
3
4


Truyền dữ liệu từ Excel Range​

Ví dụ dữ liệu trong Excel

A1:B3

1 2
3 4
5 6

Chuỗi RPC sẽ là:

Mã:
[1,2,3,4,5,6]

Chuỗi này được gửi lên RPC Server để xử lý.


Ứng dụng trong Excel​

Cơ chế RPC này cho phép Excel:

1. Tăng tốc xử lý dữ liệu​

Ví dụ:

  • Unique
  • Sort
  • Statistics
  • Matrix calculation

2. Trả về bảng dữ liệu (2D Array)​

Plugin có thể trả về:

  • Table
  • Dataset
  • Matrix
Excel chỉ cần:

Mã:
Range.Resize(...).Value = V


3. Hỗ trợ Unicode​

RPC hỗ trợ chuỗi Unicode nên có thể xử lý tiếng Việt có dấu như:

Cộng
Hòa

Hội
Chủ
Nghĩa
Việt
Nam


Kết luận​

RPC_VariantExecute cung cấp một cơ chế đơn giản để Excel gọi các hàm xử lý trên RPC Server.

Giải pháp này giúp:

  • Mở rộng khả năng xử lý của Excel
  • Tách logic xử lý khỏi VBA
  • Xây dựng hệ thống Plugin linh hoạt
Excel chỉ cần:

Mã:
RPC_VariantExecute(...)

để gọi bất kỳ hàm nào trên RPC Server.



Minh họa gọi hàm RPC trực tiếp trong ô Excel để lọc giá trị duy nhất và trả về kết quả Unicode từ RPC Server.

1773543979109.png

Module VBA dùng chung cho RPC_VariantExecute, cho phép người dùng tự xây dựng các hàm gọi từ xa cho Excel.

Mã:
Option Explicit

'=========================================================
' modRPCClientCore
'
' RPC Excel Client Core Module
'
' Module nay cung cap cac ham co ban de Excel VBA
' goi ham tren RPC Server thong qua RPCServerCore.dll
'
' Bao gom:
'   - Khai bao API RPC_VariantExecute
'   - Cau hinh RPC Server
'   - Ham goi RPC tong quat
'   - Ham chuyen Excel Range -> RPC Array
'
' Author : Kieu Manh
'=========================================================


'=========================================================
' RPC API
'=========================================================
'
' RPC_VariantExecute
'
' Goi ham trong Plugin DLL tren RPC Server
' thong qua TCP Socket.
'
' Params phai dang chuoi mang:
'   "[1,2,3]"
'
' Ket qua tra ve Variant:
'   - Gia tri don
'   - Mang 1 chieu
'   - Mang 2 chieu
'
' Neu loi:
'   "#RPC_ERROR: message"
'=========================================================
Public Declare PtrSafe Function RPC_VariantExecute Lib "RPCServerCore.dll" ( _
            ByVal Host As Variant, _
            ByVal Port As Long, _
            ByVal DLLName As Variant, _
            ByVal FuncName As Variant, _
            ByVal Params As Variant _
    ) As Variant

'=========================================================
' RPC CONFIG
'=========================================================
Public Const RPC_HOST As String = "127.0.0.1"
Public Const RPC_PORT As Long = 9000
Public Const RPC_DLL As String = "MathPlugin.dll"
'=========================================================
' GENERIC RPC CALL
'
' FuncName
'   Ten ham trong Plugin DLL
'
' Params
'   Chuoi tham so dang mang
'   Vi du:
'       "[1,2,3]"
'=========================================================
Public Function CallRPC(FuncName As String, Params As String) As Variant
    On Error GoTo ERR_HANDLER
    CallRPC = RPC_VariantExecute( _
                    RPC_HOST, _
                    RPC_PORT, _
                    RPC_DLL, _
                    FuncName, _
                    Params)

    Exit Function
ERR_HANDLER:
    CallRPC = "#VBA_ERROR: " & Err.Description
End Function


'=========================================================
' RANGE -> RPC ARRAY STRING
'
' Chuyen du lieu tu Excel Range thanh chuoi RPC
'
' Vi du:
'
' Excel Range:
'   1   2
'   3   4
'
' RPC Params:
'   "[1,2,3,4]"
'=========================================================
Public Function RangeToRPCArray(Rng As Range) As String
    Dim V As Variant
    Dim i As Long, j As Long
    Dim S As String

    V = Rng.Value

    S = "["

    For i = 1 To UBound(V, 1)
        For j = 1 To UBound(V, 2)

            If Len(V(i, j)) > 0 Then
                S = S & V(i, j) & ","
            End If

        Next j
    Next i

    If Right$(S, 1) = "," Then
        S = Left$(S, Len(S) - 1)
    End If

    S = S & "]"
    RangeToRPCArray = S
End Function

Cách hàm sử dụng trực tiếp trong ô Excel
Mã:
A1=RPCUniqueRange(A1:B24)

Hàm trong Module
Mã:
'=========================================================
' HAM UNIQUE TU RANGE (GO TRUC TIEP TRONG CELL)
'
' Vi du su dung trong Excel:
'
'   =RPCUniqueRange(A1:B24)
'
' Ham se:
'   - Chuyen du lieu Range thanh chuoi RPC
'   - Goi ham Unique tren RPC Server
'   - Tra ve mang ket qua cho Excel
'
' Excel se tu dong do ket qua xuong cac dong
'=========================================================
Function RPCUniqueRange(Rng As Range) As Variant
    Dim Payload As String
    Dim V As Variant

    On Error GoTo ERR_HANDLER

    ' Chuyen Range thanh chuoi RPC
    Payload = RangeToRPCArray(Rng)

    ' Goi ham Unique tren RPC Server
    V = CallRPC("Unique", Payload)

    ' Neu server tra ve chuoi loi
    If VarType(V) = vbString Then
        RPCUniqueRange = V
        Exit Function
    End If

    ' Tra ket qua ve Excel
    RPCUniqueRange = V

    Exit Function

ERR_HANDLER:
    RPCUniqueRange = "#VBA_ERROR: " & Err.Description
End Function

File mẫu kèm theo tải dự án trên Github
 

File đính kèm

Lần chỉnh sửa cuối:
Xong máy chủ Phiên Bản Pro

Tiếp theo xử lý dữ liệu SQLite, JSON ++++ vvv thành đa nền tảng


Cập nhật RPCServerCore.dll: Server thông minh, plugin linh hoạt, tối ưu CPU 24/7​

Thông báo phiên bản mới của RPCServerCore.dll đã bổ sung hai tính năng đột phá, giúp server hoạt động hiệu quả hơn và quá trình phát triển plugin trở nên liền mạch.


1.​

Server giờ đây tự động điều chỉnh số luồng theo số lượng client kết nối:

  • Nhiều client → nhiều luồng hơn, xử lý nhanh và mượt mà.
  • Ít client → giảm luồng, tiết kiệm CPU và tài nguyên.
Ví dụ thực tế: Khi server chạy đa luồng và client kết nối theo pool với Async Callback, 50 client gửi request đồng thời → server mở đủ luồng, đảm bảo mọi callback được xử lý ngay lập tức. Khi chỉ còn 5 client → server giảm luồng, CPU không bị lãng phí, nhưng hiệu năng vẫn tối ưu.


2.​

Không còn phải restart server mỗi khi thêm hoặc cập nhật plugin. Với RPCPluginHotReload:

  1. Copy Plugin.dll vào thư mục plugin.
  2. Server tự động load plugin chỉ sau vài giây.
Các client đang kết nối theo pool với Async Callback vẫn hoạt động bình thường, không bị gián đoạn.

Lợi ích:

  • Thử nghiệm và cập nhật plugin ngay lập tức.
  • Giảm downtime server.
  • Tăng tốc phát triển tính năng mới.

3.​

Khi chạy server liên tục 24/7, các luồng “không hoạt động” có thể gây lãng phí CPU và bộ nhớ. Trước đây, server cố định số luồng dẫn đến:

  • Luồng chết vẫn chiếm tài nguyên → CPU bị “ăn” không cần thiết.
  • Khi nhiều client đồng thời kết nối → thiếu luồng xử lý, gây chậm hoặc treo callback.
Với RPCThreadAutoScaler:

  1. Tăng luồng tự động khi client kết nối → mọi request được xử lý ngay lập tức.
  2. Giảm luồng tự động khi client rút lui → luồng chết biến mất, CPU được giải phóng.
  3. Kết hợp với pool + Async Callback, server luôn xử lý đa client hiệu quả mà không lãng phí tài nguyên, ngay cả khi hoạt động liên tục suốt tuần.
Kết quả thực tế:

  • Server 24/7 nhẹ nhàng, ổn định và nhanh nhẹn.
  • CPU chỉ hoạt động khi cần, giảm tải điện năng và tài nguyên.
  • Không còn luồng chết, server xử lý mọi client mà không gián đoạn.

Tóm tắt lợi ích​

  • Server thông minh: số luồng tự động điều chỉnh theo nhu cầu.
  • Phát triển plugin linh hoạt: hot-reload plugin, không cần restart.
  • Ổn định cho môi trường nhiều client: đặc biệt khi kết nối pool + Async Callback.
  • Tối ưu CPU 24/7: tiết kiệm tài nguyên, tránh luồng chết, server luôn mượt mà.
Phiên bản này biến RPCServerCore.dll thành nền tảng RPC nhanh nhạy, thông minh, dễ quản lý, lý tưởng cho các ứng dụng đa client và môi trường plugin liên tục thay đổi.


 
Lần chỉnh sửa cuối:

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

Back
Top Bottom