2016-12-22 5 views
3

Я хочу вызвать функцию C из dll C++ по его адресу.Вызвать функцию в C++ по ее адресу, не зная тип возвращаемого значения

Я знаю, как это сделать, когда тип возврата известен из этого отдельного вопроса: Calling a function through its address in memory in c/c++.

Однако, если я не знаю тип возврата, что я могу сделать? Я пробовал «typedef auto», но, похоже, вы не можете использовать auto как это.

+3

Что бы вы ожидали, если вы не знаете тип возврата? Способ вызова функции (соглашение о вызове) зависит от типа возвращаемого значения. – jtbandes

+6

Плохая новость заключается в том, что если вы не знаете тип возврата во время компиляции, это * невозможно * без фрагмента рукописного языка ассемблера. Хорошей новостью является то, что кто-то уже подготовил для вас язык ассемблера: [libffi] (https://sourceware.org/libffi/). – zwol

+0

Знаете ли вы тип возврата во время выполнения? –

ответ

2

Если возвращаемый тип действительно неизвестен или не имеет значения, вы можете использовать void в определении функции, или если это любой указатель, вы можете использовать void *, но если функция находится в C-кодированной DLL и вы будете использовать его в коде на C++, тогда вы можете использовать и делиться почти всеми типами, определенными в вашем коде на C, потому что C++ - это надмножество C.

При этом я подготовил небольшой пример со структурой под названием PyObject совместно используемых в коде C и C++.

Чтобы сделать это, лучше создать заголовок с общими типами/определений:

#ifndef PYOBJECT_DLL_H 
#define PYOBJECT_DLL_H 

#ifdef __cplusplus 
extern "C" { 
#endif 

// Common structure definition 
typedef struct PyObject{ 
    int field1; 
    char *field2; 
} PyObject; 

// Public function pointer type declaration 
typedef PyObject *(*__stdcall getPyObject_t)(int arg1, const char *arg2); 

#ifdef __cplusplus 
} 
#endif 

#endif // PYOBJECT_DLL_H 

Давайте предположим, что код C с экспортируемой функцией является чем-то вроде:

#include "pyobject.h" 
#include <stdlib.h> 

#ifdef __cplusplus 
extern "C" 
#endif 
__declspec(dllexport) PyObject * getPyObject(int arg1, char *arg2); 

PyObject *getPyObject(int arg1, char *arg2){ 
    PyObject *obj = (PyObject *)malloc(sizeof(PyObject)); 
    obj->field1 = arg1; 
    obj->field2 = arg2; 
    return obj; 
} 

Наконец, код C++ с использованием функции и данных, созданных в библиотеке, будет:

#include "pyobject.h" 
#include <iostream> 
#include <windows.h> 

int main() { 
    HINSTANCE dll = LoadLibrary("pyobject.dll"); 
    if (dll == NULL) { 
     std::cerr << "Cannot open pyobject.dll. Error: " << GetLastError() << "\n"; 
     return 1; 
    } 

    getPyObject_t getPyObject = (getPyObject_t) GetProcAddress(dll, "getPyObject"); 
    if (getPyObject == NULL) { 
     std::cerr << "Cannot locate 'getPyObject' function in dll. Error: " << GetLastError() << "\n"; 
     FreeLibrary(dll); 
     return 2; 
    } 

    PyObject *obj = getPyObject(3, "test"); 
    std::cout << "PyObject == { field1: " << obj->field1 << ", field2: \"" << obj->field2 << "\"}\n"; 

    FreeLibrary(dll); 
    return 0; 
} 

Редактировать

Как @raymondchen отметил в своем комментарии, игнорируя тип возвращаемого значения, когда функция C возвращает большой агрегат (например, struct), это не очень хорошо, потому что функция C ожидает, что у вызывающего уже было зарезервированное пространство стека для хранения возвращенного агрегата, но если вызывающий объект обрабатывает функцию как void или что-то еще, тогда компилятор не сохранит это пространство, непредсказуемые эффекты (возможно, заканчивающиеся ошибкой ошибки сегментации).

Чтобы избежать этого, всегда лучше определять правильный тип как в кодах C и C++ (или в общем заголовке), особенно когда функция C возвращает агрегат.

+0

Если тип возвращаемого значения является большой совокупностью, то соглашение о вызове изменяется. –

Смежные вопросы