2013-06-04 38 views
9

Tôi đang thử nghiệm với khả năng tự động gọi các thủ tục hoặc các hàm nằm trong một bảng chức năng. Các ứng dụng cụ thể là một DLL xuất khẩu một con trỏ đến một bảng chức năng cùng với thông tin về số lượng các đối số và các loại. Các ứng dụng máy chủ sau đó có khả năng thẩm vấn các DLL và gọi các chức năng. Nếu chúng là các phương thức đối tượng, tôi có thể sử dụng Rtti để gọi chúng nhưng chúng là các thủ tục và hàm bình thường. Các DLL có xuất con trỏ chức năng bình thường không đối tượng vì DLL có thể được viết bằng bất kỳ ngôn ngữ bao gồm C, Delphi, vvLàm thế nào để tự động gọi một thủ tục hoặc chức năng được đặt tên trong Delphi

Ví dụ, tôi có một kỷ lục đã kê khai và điền vào trong một DLL:

TAPI = record 
     add : function (var a, b : double) : double; 
     mult : function (var a, b : double) : double; 
end; 
PAPI = ^TAPI; 

tôi lấy con trỏ đến kỷ lục này, khai báo là:

apiPtr : PAPI; 

Giả sử tôi cũng có quyền truy cập vào tên của các thủ tục, số lượng các đối số và loại đối số cho mỗi mục trong hồ sơ.

Giả sử tôi muốn gọi hàm cộng. Con trỏ hàm để thêm sẽ là:

@apiPtr^.add // I assume this will give me a pointer to the add function 

tôi giả sử không có cách nào khác ngoài việc sử dụng một số asm để đẩy các đối số cần thiết trên stack và lấy kết quả?

Câu hỏi đầu tiên, quy ước gọi điện thoại tốt nhất để khai báo quy trình là, cdecl là gì? Có vẻ dễ nhất để lắp ráp ngăn xếp trước cuộc gọi.

Câu hỏi thứ hai, có bất kỳ ví dụ trực tuyến nào thực sự thực hiện việc này không? Tôi đã xem qua http://www.swissdelphicenter.ch/torry/showcode.php?id=1745 (DynamicDllCall) mà gần với những gì tôi muốn nhưng tôi đơn giản như dưới đây, bây giờ nó trả về một con trỏ (EAX) cho kết quả:

function DynamicDllCall(proc : pointer; const Parameters: array of Pointer): pointer; 
var x, n: Integer; 
    p: Pointer; 
begin 
n := High(Parameters); 
if n > -1 then begin 
    x := n; 
    repeat 
    p := Parameters[x]; 
    asm 
     PUSH p 
    end; 
    Dec(x); 
    until x = -1; 
end; 
asm 
    CALL proc 
    MOV p, EAX <- must be changed to "FST result" if return value is double 
end; 
result := p; 

cuối;

nhưng tôi không thể làm cho nó hoạt động, nó trả về một giá trị cho các tham số đầu tiên thay vì kết quả. Có lẽ tôi có quy ước gọi sai hoặc có thể tôi hiểu sai cách lấy kết quả trong EAX.

tôi gọi DynamicDllCall như sau:

var proc : pointer; 
    parameters: array of Pointer; 
    x, y, z : double; 
    p : pointer; 
begin 
    x:= 2.3; y := 6.7; 
    SetLength(parameters, 2); 
    parameters[0] := @x; parameters[1] := @y; 
    proc := @apiPtr^.add; 
    p := DynamicDllCall(proc, Parameters); 
    z := double (p^); 

Bất cứ lời khuyên biết ơn nhận được. Tôi đánh giá cao rằng một số có thể cảm thấy đây không phải là cách người ta nên làm về điều này nhưng tôi vẫn tò mò nếu nó là ít nhất có thể.

Cập nhật 1 Tôi có thể xác nhận rằng chức năng thêm đang nhận được các giá trị chính xác để thực hiện việc bổ sung.

Cập nhật 2 Nếu tôi thay đổi chữ ký của thêm vào:

add : function (var a, b, c : double) : double; 

và tôi gán kết quả cho c bên trong thêm, sau đó tôi có thể lấy câu trả lời đúng trong mảng tham số (giả sử tôi thêm một nhiều yếu tố hơn, 3 thay vì 2). Do đó, vấn đề là tôi hiểu sai cách các giá trị được trả về từ các hàm. Bất cứ ai có thể giải thích cách chức năng trả về giá trị và cách tốt nhất để lấy chúng?

Cập nhật 3 Tôi có câu trả lời. Tôi nên đoán. Delphi trả về các kiểu khác nhau thông qua các thanh ghi khác nhau. ví dụ như các số nguyên trả về qua EAX, tăng gấp đôi số tiền còn lại thông qua ST (0). Để sao chép ST (0) vào biến kết quả, tôi phải sử dụng "kết quả FST" thay vì "MOV p, EAX". Tôi ít nhất giờ tôi biết rằng có thể về nguyên tắc có thể làm được điều này. Cho dù đó là một điều hợp lý để làm là một vấn đề khác tôi phải nghĩ đến.

+0

Có sử dụng để có một giới thiệu tốt đẹp để Delphi ASM tại www.delphi3000.com (bài viết/article_3766.asp), nhưng toàn bộ trang web đã biến mất ... –

+1

Có lẽ tài liệu về [Quy trình và chức năng lắp ráp] (http : //docwiki.embarcadero.com/RADStudio/XE4/en/Assembly_Procedures_and_Functions) sẽ hữu ích. Xem chủ đề 'Kết quả Chức năng'. –

+0

Không có nhiều ở đó và các tài liệu chính thức không phải là hữu ích khủng khiếp.Tuy nhiên, tôi đã ghép lại đủ thông tin từ: http://stackoverflow.com/questions/15786404/fld-instruction-x64-bit và http://www.guidogybels.eu/docs/Using%20Assembler%20in% 20Delphi.pdf – rhody

Trả lời

1

Đây là vấn đề khó giải quyết. Một cách để truy cập động các phương thức trong một DLL trong thời gian chạy là sử dụng thư viện giao diện chức năng nước ngoài như libffi, dyncall hoặc DynaCall(). Tuy nhiên, không có cái nào trong số này được chuyển đến môi trường Delphi.

Nếu ứng dụng là giao diện tập hợp các phương thức trong DLL cùng với thông tin Rtti do DLL cung cấp và hiển thị chúng bằng ngôn ngữ kịch bản như Python, một tùy chọn là viết mã Delphi kiểm tra DLL và ghi ra tập lệnh tương thích ctypes có thể được tải vào trình thông dịch Python được nhúng trong thời gian chạy. Vì vậy, miễn là một trong những định nghĩa trước khi bàn tay một bộ hạn chế nhưng đủ loại các phương pháp DLL có thể xử lý, đây là một giải pháp thiết thực.

9

Đây là một vấn đề XY: Bạn muốn làm X, và vì lý do gì, bạn đã quyết định Y sự là giải pháp, nhưng bạn đang gặp sự cố khiến Y làm việc. Trong trường hợp của bạn, Xcuộc gọi chức năng bên ngoài thông qua con trỏYtự đẩy các thông số trên stack. Nhưng để hoàn thành X, bạn không thực sự cần phải thực hiện Y.

Biểu thức @apiPtr^.add sẽ không cung cấp cho bạn một con trỏ tới hàm. Nó sẽ cung cấp cho bạn một con trỏ đến trường add của bản ghi TAPI. (Vì add là thành viên đầu tiên của bản ghi, địa chỉ của trường đó sẽ bằng địa chỉ được giữ trong apiPtr; trong mã, Assert(CompareMem(@apiPtr, @apiPtr^.add, SizeOf(Pointer)).) Trường addgiữ con trỏ đến hàm, vì vậy nếu đó là những gì bạn muốn , chỉ cần sử dụng apiPtr^.add (và lưu ý rằng ^ là tùy chọn trong Delphi).

Quy ước gọi điện thoại tốt nhất để sử dụng là stdcall. Bất kỳ ngôn ngữ nào hỗ trợ xuất các hàm DLL sẽ hỗ trợ quy ước gọi đó.

Bạn không cần lắp ráp hoặc bất kỳ thao tác ngăn xếp khó khăn nào khác để gọi các chức năng của bạn. Bạn đã biết loại của hàm vì bạn đã sử dụng nó để khai báo add. Để gọi hàm được trỏ đến bởi lĩnh vực đó, chỉ cần sử dụng cú pháp giống như để gọi một chức năng bình thường:

z := apiPtr.add(x, y); 

Trình biên dịch biết kiểu được khai báo của trường add, vì vậy nó sẽ sắp xếp ngăn xếp cho bạn.

+0

Bạn đúng rằng tôi có thể sử dụng apiPtr.add (x, y) và đó là những gì tôi đã sử dụng. Nhưng nó có nghĩa là tôi cần các chi tiết của hàm cộng vào lúc biên dịch. Tôi muốn gọi thêm dựa trên thông tin thời gian chạy. Các dll của tôi có thể xuất các phương thức bổ sung để cung cấp thông tin rtti trên mỗi hàm. Thông tin này sẽ cho phép tôi tạo cuộc gọi khi đang chạy. Tại sao? Ứng dụng cụ thể của tôi là cho phép các phương thức như vậy được tự động tạo sẵn cho một công cụ kịch bản khi chạy. Các công cụ kịch bản không nên quan tâm những chức năng có sẵn trong dll, chỉ cần vượt qua chúng trên cho người dùng. – rhody

Các vấn đề liên quan