2015-03-11 12 views
6

Vấn đề tôi có là tạo ứng dụng dòng lệnh chung có thể được sử dụng để tải thư viện DLL và sau đó gọi hàm trong thư viện DLL. Tên hàm được chỉ định trên dòng lệnh với các đối số cũng được cung cấp trên dòng lệnh tiện ích.C Truyền tham số dưới dạng danh sách con trỏ void sang hàm được nhập từ LoadLibrary()

Tôi có thể truy cập chức năng bên ngoài từ một DLL được tải động bằng chức năng LoadLibrary(). Khi thư viện được nạp, tôi có thể lấy con trỏ tới hàm bằng cách sử dụng GetProcAddress() Tôi muốn gọi hàm bằng các đối số được chỉ định trên dòng lệnh.

Tôi có thể chuyển danh sách con trỏ trống đến con trỏ hàm mà tôi đã nhận được bởi hàm LoadLibrary() tương tự như ví dụ bên dưới không?

Để giữ mã ví dụ đơn giản, tôi đã xóa kiểm tra lỗi. Có cách nào để có được một cái gì đó giống như làm việc này:

 
    //Somewhere in another dll 
    int DoStuff(int a, int b) 
    { 
     return a + b; 
    } 
 
    int main(int argc, char **argv) 
    { 
     void *retval; 
     void *list = argv[3]; 
     HMODULE dll; 
     void* (*generic_function)(void*); 

     dll = LoadLibraryA(argv[1]); 

     //argv[2] = "DoStuff" 
     generic_function = GetProcAddress(dll, argv[2]); 

     //argv[3] = 4, argv[4] = 7, argv[5] = NULL 
     retval = generic_function(list); 
    } 

Nếu tôi quên đề cập đến thông tin cần thiết, xin vui lòng cho tôi biết. Cảm ơn trước

+0

Calling quy ước cuối cùng có thể cho bạn đau buồn. Các chức năng thực tế mong đợi làm thế nào bạn đang gọi nó là một giả định rõ ràng. Và "... với các đối số đã qua.", Lưu ý * đa số * của câu nói đó, thật thú vị. Bạn đang đi qua * một *. Nếu bạn cần nhiều hơn thế (nghĩa là bạn đang mong đợi điều này để gửi một cách kỳ diệu 'argv [3] ... argv [argc-1]' như là các đối số hàm), điều này sẽ không làm điều đó, và làm nó ngay lập tức trở nên phức tạp . – WhozCraig

+0

Nó không phải là rõ ràng những gì bạn có nghĩa là bởi 'nếu tôi có thể vượt qua một void-pointer-list đến function-pointer'. Nếu hàm bạn đang gọi được định nghĩa là __...MyFunction (void *) __, sau đó có, bạn có thể gọi nó như thế này, nếu không bạn sẽ không thể. Ngoài ra, hãy đảm bảo nó được gắn nhãn là 'declspec (stdcall) '. –

+0

Tôi vừa tìm cách để có công cụ dòng lệnh để gọi, ví dụ: kernel32.dll và bất kỳ hàm nào của nó và chuyển đối số cho nó. Tôi đã hy vọng - như WhozCraig đã nói - cho một cách kỳ diệu để tìm danh sách đối số từ một chức năng nhập khẩu – h4x0r

Trả lời

4

Bạn cần truyền con trỏ hàm trả về LoadLibrary cho một loại có loại đối số phù hợp trước khi gọi. Một cách để quản lý nó là phải có một số chức năng gọi adapter mà làm điều đúng cho tất cả các loại chức năng có thể khiến bạn muốn gọi:

void Call_II(void (*fn_)(), char **args) { 
    void (*fn)(int, int) = (void (*)(int, int))fn_; 
    fn(atoi(args[0]), atoi(args[1])); 
} 
void Call_IS(void (*fn_)(), char **args) { 
    void (*fn)(int, char *) = (void (*)(int, char *))fn_; 
    fn(atoi(args[0]), args[1]); 
} 
...various more functions 

Sau đó, bạn đưa con trỏ bạn nhận được từ GetProcAddress và những lập luận thêm và vượt qua chúng để đúng Call_X chức năng:

void* (*generic_function)(); 

dll = LoadLibraryA(argv[1]); 

//argv[2] = "DoStuff" 
generic_function = GetProcAddress(dll, argv[2]); 

//argv[3] = 4, argv[4] = 7, argv[5] = NULL 

Call_II(generic_function, &argv[3]); 

vấn đề là bạn cần phải biết những gì loại của hàm bạn đang nhận được con trỏ là và gọi hàm bộ chuyển đổi thích hợp. Mà thường có nghĩa là làm cho một bảng của tên chức năng/adapter và làm một tra cứu trong đó.

Vấn đề liên quan là không có hàm tương tự với GetProcAddress sẽ cho bạn biết loại đối số cho hàm trong thư viện - thông tin đó không được lưu trữ ở bất kỳ nơi nào có thể truy cập trong dll.

1

Một thư viện DLL chứa mã đối tượng cho các chức năng là một phần của thư viện cùng với một số thông tin bổ sung để cho phép sử dụng DLL.

Tuy nhiên thư viện DLL không chứa thông tin loại thực tế cần thiết để xác định danh sách đối số cụ thể và loại cho các hàm chứa trong thư viện DLL. Thông tin chính trong thư viện DLL là: (1) danh sách các hàm mà DLL xuất ra cùng với thông tin địa chỉ sẽ kết nối cuộc gọi của hàm với mã nhị phân chức năng thực tế và (2) danh sách mọi DLL bắt buộc rằng các hàm trong thư viện DLL sử dụng. Bạn thực sự có thể mở một thư viện DLL trong một trình soạn thảo văn bản, tôi đề xuất một tệp nhỏ và quét qua các ký hiệu phức tạp của mã nhị phân cho đến khi bạn đến phần chứa danh sách hàm trong thư viện DLL cũng như các DLL cần thiết khác.

Vì vậy, thư viện DLL chứa thông tin tối thiểu cần thiết để (1) tìm một hàm cụ thể trong thư viện DLL để nó có thể được gọi và (2) danh sách các DLL cần thiết khác. trên.

Điều này khác với đối tượng COM thường có thông tin loại để hỗ trợ khả năng làm những gì cơ bản phản ánh và khám phá các dịch vụ của đối tượng COM và cách truy cập các dịch vụ đó. Bạn có thể làm điều này với Visual Studio và các IDE khác tạo ra một danh sách các đối tượng COM được cài đặt và cho phép bạn tải một đối tượng COM và khám phá nó. Visual Studio cũng có một công cụ sẽ tạo ra các tệp mã nguồn cung cấp các sơ khai và bao gồm tệp để truy nhập các dịch vụ và các phương thức của một đối tượng COM.

Tuy nhiên thư viện DLL khác với đối tượng COM và tất cả thông tin bổ sung được cung cấp với đối tượng COM không có sẵn từ thư viện DLL. Thay vào đó, một gói DLL thư viện thường được tạo thành từ (1) thư viện DLL chính nó, (2) tệp .lib chứa thông tin liên kết cho thư viện DLL cùng với các sơ đồ và chức năng để đáp ứng mối liên kết khi xây dựng ứng dụng của bạn DLL thư viện, và (3) một tệp bao gồm với các nguyên mẫu hàm của các hàm trong thư viện DLL.

Vì vậy, bạn tạo ứng dụng của mình bằng cách gọi các hàm nằm trong thư viện DLL nhưng sử dụng thông tin loại từ tệp bao gồm và liên kết với các phần của tệp .lib được liên kết. Thủ tục này cho phép Visual Studio tự động hóa nhiều công việc cần thiết để sử dụng một thư viện DLL.

Hoặc bạn có thể viết mã số LoadLibrary() và xây dựng một bảng các hàm trong thư viện DLL sử dụng GetProcAddress(). Bằng cách làm mã hóa tay tất cả những gì bạn thực sự cần là các nguyên mẫu chức năng của các hàm trong thư viện DLL mà sau đó bạn có thể nhập vào chính bạn và bản thân thư viện DLL. Bạn đang có hiệu lực làm công việc bằng tay rằng trình biên dịch Visual Studio làm cho bạn nếu bạn đang sử dụng thư viện thư viện .lib và bao gồm tệp.

Nếu bạn biết tên hàm thực tế và prototype hàm của một hàm trong một DLL thư viện sau đó những gì bạn có thể làm là phải có tiện ích dòng lệnh của bạn đòi hỏi các thông tin sau:

  • tên của hàm để được gọi là một chuỗi văn bản trên lệnh dòng
  • danh sách các đối số được sử dụng như một loạt các chuỗi văn bản trên dòng lệnh
  • một tham số bổ sung mà mô tả các chức năng nguyên mẫu

Điều này tương tự như cách các hàm trong thời gian chạy C và C++ chấp nhận danh sách đối số biến với các kiểu tham số không xác định hoạt động. Ví dụ, hàm printf() in danh sách các giá trị đối số có chuỗi định dạng theo sau là các đối số cần in. Hàm printf() sử dụng chuỗi định dạng để xác định các loại đối số khác nhau, số lượng đối số mong đợi và loại biến đổi giá trị cần làm.

Vì vậy, nếu tiện ích của bạn đã có một dòng lệnh giống như sau:

dofunc "%s,%d,%s" func1 "name of " 3 " things" 

Và thư viện DLL đã có một chức năng mà nguyên mẫu trông giống như:

void func1 (char *s1, int i, int j); 

thì tiện ích sẽ tự động tạo ra chức năng gọi bằng cách chuyển đổi các chuỗi ký tự của dòng lệnh thành các loại thực tế cần thiết cho hàm được gọi.

Điều này sẽ làm việc cho các chức năng đơn giản lấy các loại dữ liệu cũ, tuy nhiên các loại phức tạp hơn như đối số loại struct sẽ yêu cầu nhiều công việc hơn như bạn cần mô tả của struct cùng với một số loại mô tả đối số sang JSON.

Phụ lục I: Một ví dụ đơn giản

Sau đây là mã nguồn cho một ứng dụng giao diện điều khiển của Windows Studio Visual Tôi chạy trong trình gỡ lỗi. Các đối số lệnh trong các thuộc tính là pif.dll PifLogAbort gây ra một thư viện DLL từ một dự án khác, pif.dll, được nạp và sau đó hàm PifLogAbort() trong thư viện đó được gọi.

Lưu ý cách danh sách đối số cho hàm PifLogAbort() được tạo làm cấu trúc chứa mảng. Các giá trị đối số được đưa vào mảng của biến số struct và sau đó hàm được gọi là chuyển toàn bộ giá trị struct theo giá trị. Điều này làm là đẩy một bản sao của các mảng tham số vào ngăn xếp và sau đó gọi hàm. Hàm PifLogAbort() xem chồng dựa trên danh sách đối số của nó và xử lý các phần tử mảng làm đối số hoặc tham số riêng lẻ.

// dllfunctest.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

typedef struct { 
    UCHAR *myList[4]; 
} sarglist; 

typedef void ((*libfunc) (sarglist q)); 
/* 
* do a load library to a DLL and then execute a function in it. 
* 
* dll name.dll "funcname" 
*/ 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    HMODULE dll = LoadLibrary(argv[1]); 
    if (dll == NULL) return 1; 

    // convert the command line argument for the function name, argv[2] from 
    // a TCHAR to a standard CHAR string which is what GetProcAddress() requires. 
    char funcname[256] = {0}; 
    for (int i = 0; i < 255 && argv[2][i]; i++) { 
     funcname[i] = argv[2][i]; 
    } 

    libfunc generic_function = (libfunc) GetProcAddress(dll, funcname); 
    if (generic_function == NULL) return 2; 

    // build the argument list for the function and then call the function. 
    // function prototype for PifLogAbort() function exported from the library DLL 
    // is as follows: 
    // VOID PIFENTRY PifLogAbort(UCHAR *lpCondition, UCHAR *lpFilename, UCHAR *lpFunctionname, ULONG ulLineNo); 
    sarglist xx = {{(UCHAR *)"xx1", (UCHAR *)"xx2", (UCHAR *)"xx3", (UCHAR *)1245}}; 

    generic_function(xx); 

    return 0; 
} 

Ví dụ đơn giản này minh họa một số rào cản kỹ thuật phải được khắc phục. Bạn sẽ cần biết cách dịch các loại tham số khác nhau thành căn chỉnh thích hợp trong vùng nhớ mà sau đó được đẩy lên ngăn xếp.

Giao diện cho hàm ví dụ này là đáng đồng nhất ở chỗ hầu hết các đối số là unsigned char con trỏ ngoại trừ số cuối cùng là int. Với thực thi 32 bit, tất cả bốn loại biến này có cùng độ dài bằng byte. Với một danh sách các loại khác nhau trong danh sách đối số, bạn sẽ cần phải có một sự hiểu biết như thế nào trình biên dịch của bạn sắp xếp các thông số khi nó đẩy các đối số vào ngăn xếp trước khi thực hiện cuộc gọi.

Phụ lục II: Mở rộng ví dụ đơn giản

Một khả năng khác là phải có một tập hợp các hàm helper cùng với một phiên bản khác của struct. struct cung cấp vùng bộ nhớ để tạo bản sao của ngăn xếp cần thiết và các chức năng trợ giúp được sử dụng để tạo bản sao.

Vì vậy, struct và các chức năng trợ giúp của nó có thể trông giống như sau.

typedef struct { 
    UCHAR myList[128]; 
} sarglist2; 

typedef struct { 
    int i; 
    sarglist2 arglist; 
} sarglistlist; 

typedef void ((*libfunc2) (sarglist2 q)); 

void pushInt (sarglistlist *p, int iVal) 
{ 
    *(int *)(p->arglist.myList + p->i) = iVal; 
    p->i += sizeof(int); 
} 

void pushChar (sarglistlist *p, unsigned char cVal) 
{ 
    *(unsigned char *)(p->arglist.myList + p->i) = cVal; 
    p->i += sizeof(unsigned char); 
} 

void pushVoidPtr (sarglistlist *p, void * pVal) 
{ 
    *(void * *)(p->arglist.myList + p->i) = pVal; 
    p->i += sizeof(void *); 
} 

Và sau đó struct và helper chức năng sẽ được sử dụng để xây dựng danh sách đối số như sau sau đó chức năng từ thư viện DLL được gọi với bản sao của ngăn xếp được cung cấp:

sarglistlist xx2 = {0}; 
pushVoidPtr (&xx2, "xx1"); 
pushVoidPtr (&xx2, "xx2"); 
pushVoidPtr (&xx2, "xx3"); 
pushInt (&xx2, 12345); 

libfunc2 generic_function2 = (libfunc2) GetProcAddress(dll, funcname); 
generic_function2(xx2.arglist); 
+0

Cách tiếp cận rất thú vị! Tôi không nghĩ đến việc can thiệp vào thủ tục gọi/đối số-danh sách-qua. Tôi chắc chắn sẽ xem xét thêm về điều này. Cảm ơn bạn đã giải thích/trả lời chi tiết và tinh vi. Làm tốt lắm! – h4x0r

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