Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > [VC++]импорт методов


Автор: GoldFinch 16.5.2009, 20:21
Есть длл, написанная на VС++, экспортирующая методы классов (возможно сами классы). 
Заголовочных файлов к этой длл нет.

При помощи dumpbin.exe были получены экспорты длл, при помощи lib.exe был получен .lib файл, при помощи undname.exe были получены имена и прототипы методов, при помощи IDA был восстановлен порядок методов в таблицах виртуальных методов (ВМТ)

Тестовый код "UObject o; o.AddRef;" показал что метод UObject::AddRef() импортируется корректно и генерирует корректный код вызова
Код
 call    ?AddRef@UObject@@UAGKXZ
?AddRef@UObject@@UAGKXZ:
  jmp [__imp_?AddRef@UObject@@UAGKXZ]


Теперь надо получить адрес этого метода. 
Допустим надо получить адрес простой импортированной функции. Пишем 
  int foo(){return int(&ExitProcess);}
получаем 
  mov     eax, [__imp__ExitProcess@4]
Для метода, пробуем написать
  int foo(){return int(&UObject::AddRef);}
получаем error C2440: приведение типов: невозможно преобразовать 'unsigned long (__stdcall UObject::* )(void)' в 'int'  Не существует контекста, в котором такое преобразование возможно

Пишем шаблон для преобразования
Код

template<typename T>
inline int int_cast(T x){
    union {
        T x;
        int y;
    } t;
    t.x=x;    
    return t.y;
};

код
  int foo(){return int_cast(&UObject::AddRef);}
компилится успешно, однако получаем адрес функции которая вызывает метод через таблицу методов
Код
?foo@@YAHXZ proc near
mov     eax, offset ??_9UObject@@$B3AG ; [thunk]: UObject::`vcall'{4,{flat}}
retn
?foo@@YAHXZ endp
??_9UObject@@$B3AG proc near

arg_0= dword ptr  4

mov     eax, [esp+arg_0]
mov     eax, [eax]
jmp     dword ptr [eax+4] ;4 - смещение метода в ВМТ
??_9UObject@@$B3AG endp

а хочется совсем не этого, хочется получить реальный адрес этого метода, т.е. [__imp_?AddRef@UObject@@UAGKXZ]

как это сделать?

Автор: Earnest 16.5.2009, 21:27
А метод случаем не виртуальный? Когда ты просишь адрес виртуального метода, то тебе компилятор подсовывает адрес именно функции-переходника, т.к. предполоагается полиморфный вызов в зависимости от this. 
И еще один вопрос: неужели банальный GetProcAddress не поможет (с истинным декорированным именем, конечно)? Если функция импортируется, то GetProcAddress должна возвратить ее, а метод с точки зрения линкера - та же функция... С преобразованием типа тут тоже возможны проблемы, конечно... 

Автор: GoldFinch 16.5.2009, 21:53
Earnest, метод виртуальный. сделать его невиртуальным невозможно, т.к. не будет соответствовать манглинг.

GetProcAddress я както использовал, но потом от этого отказался вроде изза большего объема кода
Код

//глобальные переменные
  typ1 (class1::*met1)(agrlist1);
  typ2 (class1::*met1)(agrlist2);
  ...
//импорт
  (FARPROC&)met1=GetProcAddress(hMod,"?met1?class1?...");
  (FARPROC&)met2=GetProcAddress(hMod,"?met2?class2?...");
  ...
//вызов
  r=(obj->*met)(arg1,arg2,...);

проблем с преобразованием типа нет, а вот вызов выглядит не очень красиво

Автор: Earnest 16.5.2009, 22:14
Я и не предлагала сделать его невиртуальным, просто скзала, как компилятор интерпретирует такие вызовы (и заодно указатели). GetProcAddress - это стандартный метод получения адресов функций, чего тут мудрить. А если 
не нравится код, напиши обертку к импортируемым классам (чтобы для клиента выглядело все как у "нормальных").
И не совсем понимаю, каким образом твой первый метод решал эту проблему (некрасивого кода вызова).

Кстати, ты ведь вроде говорил, что смог построить lib-файл. Так что тогда мешает подключить билиотеку стандартным оразом? Ну, заголовочный файл напиши руками, ты ведь вроде выяснил сигнатуры... Насколько я понимаю, цель именно такая: использовать библиотеку как если бы у нее были открыт интерфейсы (т.е. lib + h)?

Автор: GoldFinch 17.5.2009, 00:08
Earnest, 
я уже писал в 1м посте, что вызовы методов работают нормально, obj->met(),
мне же надо получить адрес метода, не для того чтобы его вызвать, а для последующей работы с этим адресом.

Автор: Earnest 17.5.2009, 00:12
Наверное, пропустила.
В общем, адрес метода - это совсем не адрес с точки зрения компилятора и языка, так что просто &CMyObj::Method будет возвращать адрес стаба, как у тебя и получается. А вот GetProcAddress про C++ и прочий полиморфизм не в курсе, должна выдавать ровно то, что просят. Насчет длинного и некрасивого кода - как писал кто-то из гуру, любую проблему в программировании можно решить с помощью правильно спроектированных прокладок. А на C++ они к тому же почти бесплатны в смысле издержек.

Автор: GoldFinch 17.5.2009, 00:58
Earnest, даже если забыть об издержках работы программы, появляются издержки при ее разработке: для того чтобы найти декорированное имя метода для GetProcAddress надо свернуть с MSVC и искать его в таблице экспорта длл, что несколько неудобно.


Если же задуматься об издержках на импорт, то получается что при статическом импорте компилятор удаляет все неиспользуемые импорты и они импортируются поиском в таблице экспорта длл при загрузке программы. При вызове метода через указатель на объект (obj->met) адрес метода берется из ВМТ.
При динамическом импорте через GetProcAddress импортирование происходит поиском в таблице экспорта длл в момент вызова GetProcAddres. Будут ли удалены лишние вызовы GetProcAddress - неизвестно. При вызове метода адрес метода берется из переменной с адресом.
Однако у меня конкретная длл, которая не будет меняться за все время использования моей программы. Поэтому для импорта можно забить готовые адреса методов сразу на этапе компиляции, они не изменятся. При этом получится наибольшая скорость вызова функций, хотя не очень понятно как это будет выглядеть в коде, и как генерировать этот код из длл.


Автор: mes 17.5.2009, 10:46
Цитата(GoldFinch @  16.5.2009,  19:21 Найти цитируемый пост)

Тестовый код "UObject o; o.AddRef;" показал что метод UObject::AddRef() импортируется корректно и генерирует корректный код вызова
Код


 call    ?AddRef@UObject@@UAGKXZ
?AddRef@UObject@@UAGKXZ:
  jmp [__imp_?AddRef@UObject@@UAGKXZ]




Код

void test (UObject& o) {    o.AddRef();  } 

а такой тестовый код, что дает ?

Автор: GoldFinch 17.5.2009, 13:11
mes, вызов через ВМТ

Автор: mes 17.5.2009, 13:38
Цитата(GoldFinch @  17.5.2009,  12:11 Найти цитируемый пост)
вызов через ВМТ 

тогда то, что Вы получили в первом примере, относится к оптимизации и получено вследствие того, что компилятору известен реальный тип объекта в компил-тайм.
И задача (без знания реального имени функции-реализации), имхо, не выполнима  

Автор: GoldFinch 19.6.2009, 21:18
ап!

Цитата(Earnest @  17.5.2009,  01:12 Найти цитируемый пост)
как писал кто-то из гуру, любую проблему в программировании можно решить с помощью правильно спроектированных прокладок.


попробовал написать обертку
Код

//=============== .h =====================

class Foo;

class Foo_Imp_
{
protected:
    static void (Foo::*Met1_ptr)(int);
    static int (Foo::*Met2_ptr)();
    static int (Foo::*Met3_ptr)(int,char);
    // ... еще методы ...
protected:
    Foo_Imp_()
    {
        if (!imported_)
        {
            imported_=true;
            HMODULE hDll=TheDll::Handle();  //загружает и возвращает хендл длл "the.dll"
            (FARPROC&)Met1_ptr=GetProcAddress(hDll,"??Met1@@Foo");
            (FARPROC&)Met2_ptr=GetProcAddress(hDll,"??Met2@@Foo");
            (FARPROC&)Met3_ptr=GetProcAddress(hDll,"??Met3@@Foo");
            // ... еще методы ...
        }
    }
private:
    static bool imported_;
};


class Foo :Foo_Imp_
{
public:
    char** VMT; //ВМТ класса
    int thedata1; //какието известные члены класса
    char stuff[0x34]; //какието неизвестные члены класса
public:
    void Met1(int arg1) {return (this->*Foo_Imp_::Met1_ptr)(arg1);}
    int Met2() {return (this->*Foo_Imp_::Met2_ptr)();}
    int Met3(int arg1,char arg2) {return (this->*Foo_Imp_::Met3_ptr)(arg1,arg2);}
    // ... еще методы ...
};


//================ .cpp =================

bool Foo_Imp_::imported_=false;

void (Foo::*Foo_Imp_::Met1_ptr)(int);
int (Foo::*Foo_Imp_::Met2_ptr)();
int (Foo::*Foo_Imp_::Met3_ptr)(int,char);
// ... еще методы ...



есть какие-нибудь идеи как сделать это короче?

Автор: Earnest 22.6.2009, 09:39
GoldFinch, а зачем? Ты код буста видел? Цель в том, чтобы был короче-яснее клиентский код, а обслуживающий... ну, по возможности.
Насчет сократить, я бы попробовала так (имея в виду, что методов сильно не 3).
Во-первых, таблица вместо перечисления методов, причем не только имя, но и закодированная сигнатура.
Во-вторых, набор типизированных шаблонов для вызова методов по сигнатуре. Шаблоны нужно делать по числу аргументов: для 0, 1, 2, 3 и сколько там по максимуму.
Методы имлементировать придется явно, как у и у тебя, но через шаблоны.
И еще не поняла, зачем тебе в обертке имитировать тело настоящего класса, все эти VMT, члены-данные... Т.е. если ты можешь правильно установить VMT, но нафига вообще огород городить - напиши объявление класса с правильно расположенными виртуальными функциями.

Автор: GoldFinch 22.6.2009, 13:18
Earnest, обертка чтобы спрятать указатели на методы, и она превращает виртуальные методы в длл в невиртуальные, это позволяет вызывать их не зная их порядок в ВМТ (хотя они в оригинале виртуальные, полиморфизм там вроде не используется)

количество методов в 1 классе - до сотен (200-300), классы и длл писал не я
код буста не видел, но мне надо будет скорее всего писать все это руками, по мере необходимости добавляя новые методы 

Автор: mes 22.6.2009, 13:52
Цитата(GoldFinch @  19.6.2009,  20:18 Найти цитируемый пост)
попробовал написать обертку

с певого взгляда бросилось в глаза

Цитата(GoldFinch @  19.6.2009,  20:18 Найти цитируемый пост)
Код

class Foo_Imp_
{
protected:
    static void (Foo::*Met1_ptr)(int);
    static int (Foo::*Met2ptr)();  

Зачем сразу ограничивать себя, принуждая дата-члены класса делать статическими.
Этот момент интуитивно напрашивается в таком исполнении :
Код

class Foo;
struct PtrTable
{
    void (Foo::*Met1)(int);
    int  (Foo::*Met2)();

    PtrTable () { /* import */ }

};

class Foo
{
    public:
      void  Met1 (int i) {  return (this->*table().Met1) (i); }
      int   Met2 ()      {  return (this->*table().Met2) ();  }

    private:
        static PtrTable& table() { static PtrTable table; return table; } // 1.
};


1. Выполнил как функцию, так как таблица принадлежит только этому классу, но если не подходит можно вернуть к "классическому" виду
 инициализации таблица произойдет при первом вызове функции PtrTable. Если нужно ручное управление инициализацией, то можно добавить функцию:
static void Init () { PtrTable(); } или поместить пустой вызов PtrTable() в конструктор в зависимости от требований.
ну а красиво  скрыть это : (this->*table().Met1)  я уверен Вы справитесь сами smile




Автор: GoldFinch 17.7.2009, 12:13
ап!

почитал про static, действительно оно обладает замечательным свойством инициализировать переменные только 1 раз

получается что 
отдельный класс PtrTable и "static PtrTable& table() { static PtrTable table; return table; }"
можно заменить на статическую переменную с адесом метода прямо внутри обертки метода
при этом эту переменную можно сделать классом, в конструкторе которого и импортировать метод

Код

// обертка winapi
class Dll
{
public:
    template<typename T>
    static void import(T& f, const char* name)
    {
        if( !handle_ )
            handle_ = LoadLibraryA("test_dll.dll");
        (FARPROC&)f=GetProcAddress(handle_,name);
    }
private:
    static HMODULE handle_;
};
HMODULE Dll::handle_ = 0;

Код

// импортируемый класс
class Foo
{
...

    void init(char* str) 
    { 
        static struct Imp_
        {
            void (Foo::*met)(char* str);
            Imp_() { Dll::import(met,"?init@Foo@@UAEXPAD@Z"); }            
        } imp_;
        return (this->*imp_.met)(str); 
    }

...
};


получается что вся информация о методе сосредоточена в одном месте, и ее можно завернуть в компактный макрос

Автор: GoldFinch 17.7.2009, 12:49
а еще лучше выкинуть класс Imp_ из метода:
Код

struct Imp_
{
    void* addr;
    Imp_(const char* name) { Dll::import(addr,name); }            

    template<typename T>
    T call_(T proto)
    {
        BOOST_STATIC_ASSERT(sizeof(proto)==sizeof(addr));
        union U
        {
            void* x;
            T y;
        } u = {addr};
        return u.y;
    }
};

Код

class Foo
{
public:
...
    void init(char* str) 
    { 
        static Imp_ imp_("?init@Foo@@UAEXPAD@Z");
        return (this->*imp_.call_(&Foo::init))(str); 
    }
...
}


Добавлено через 14 минут и 30 секунд
хм... только вот не получается получить адрес конструктора и деструктора =\

Автор: GoldFinch 17.7.2009, 13:19
значит всетаки так:
Код

template<typename T>
struct Imp_
{
    T addr;
    Imp_(const char* name) { Dll::import(addr,name); }            
};

class Foo
{
public:
    int* vftable;
    char* str;
public:    
    Foo()
    {
        static Imp_<void(Foo::*)()> imp_("??0Foo@@QAE@XZ");
        (this->*imp_.addr)(); 

        Dll::import(vftable,"??1Foo@@UAE@XZ");
    }
    ~Foo() 
    {
        static Imp_<void(Foo::*)()> imp_("??0Foo@@QAE@XZ");
        (this->*imp_.addr)();
    }

    void init(char* str) 
    { 
        static Imp_<void(Foo::*)(char*)> imp_("?init@Foo@@UAEXPAD@Z");
        return (this->*imp_.addr)(str); 
    }

    void test() 
    { 
        static Imp_<void(Foo::*)()> imp_("?test@Foo@@UAEXXZ");
        return (this->*imp_.addr)(); 
    }    

};

Автор: mes 17.7.2009, 14:36
Цитата(GoldFinch @  17.7.2009,  12:19 Найти цитируемый пост)
   return (this->*imp_.addr)(); 

добавьте в структуру Imp_ :   
Код

 operator T () { return addr; } 

и отпадет надобность писать .addr
 smile 

Автор: GoldFinch 17.7.2009, 16:33
mes, я решил пойти еще дальше, и написать эту структуру так:
Код

namespace import {

    ...

    template<typename T>
    T inline union_cast(void* x)
    {
        BOOST_STATIC_ASSERT(sizeof(T)==sizeof(x));
        union U
        {
            void* x;
            T y;
        } u = {x};
        return u.y;
    }

    template<typename retT=void>
    struct Import
    {
        void* addr;
        Import(const char* name) { Dll::import(addr,name); }

        template<typename classT>
        retT operator() (classT* that) 
        { return (that->*union_cast<retT(classT::*)()>(addr))();    }

        template<typename classT, typename T1>
        retT operator() (classT* that, T1 arg1) 
        { return (that->*union_cast<retT(classT::*)(T1)>(addr))(arg1); }

        template<typename classT, typename T1, typename T2>
        retT operator() (classT* that, T1 arg1, T2 arg2) 
        { return (that->*union_cast<retT(classT::*)(T1,T2)>(addr))(arg1,arg2); } 
        
        ...
    };

} // namespace import

тогда ее можно использовать так:
Код

class Foo
{
    ...

    int sum(int a,char b)
    {
        static import::Import<int> imp_("?sum@Foo@@QAEHHD@Z");
        return imp_(this,a,b); 
    }
};

Автор: GoldFinch 17.7.2009, 17:17
объявляю эту тему форума репозиторием версий кода импорта методов =)

перенес аргумент шаблона retT из класса в operator()
Код

    template<typename T=void> struct ret_tag{};

    struct Import
    {
        void* addr;
        Import(const char* name) { Dll::import(addr,name); }

        template<typename retT,typename classT>
        retT operator() (ret_tag<retT>,classT* that) 
        { return (that->*union_cast<retT(classT::*)()>(addr))();    }

        template<typename retT,typename classT, typename T1>
        retT operator() (ret_tag<retT>,classT* that, T1 arg1) 
        { return (that->*union_cast<retT(classT::*)(T1)>(addr))(arg1); }

        template<typename retT,typename classT, typename T1, typename T2>
        retT operator() (ret_tag<retT>,classT* that, T1 arg1, T2 arg2) 
        { return (that->*union_cast<retT(classT::*)(T1,T2)>(addr))(arg1,arg2); } 

    };

// ************************* использование *****************
class Foo
{
    ...

    int sum(int a,char b)
    {
        static import::Import imp_("?sum@Foo@@QAEHHD@Z");
        return imp_(import::ret_tag<int>(),this,a,b); 
    }
};


в стл или бусте есть класс 
template<typename T> struct какое_нибудь_имя{}; ?

Добавлено через 7 минут и 15 секунд
и есть ли гденить в бусте (в стл нет) аналог такой функции?
Код

    template<typename yT,typename xT>
    yT inline union_cast(xT x)
    {
        BOOST_STATIC_ASSERT(sizeof(xT)==sizeof(yT));
        union U
        {
            xT x;
            yT y;
        } u = {x};
        return u.y;
    }


(нужна для приведения указателей на члены классов, т.к. они не приводятся приведеним типов в С-стиле)

Автор: mes 17.7.2009, 20:22
Цитата(GoldFinch @  17.7.2009,  15:33 Найти цитируемый пост)
решил пойти еще дальше, и написать эту структуру так:

Цитата(GoldFinch @  17.7.2009,  15:33 Найти цитируемый пост)
      union U
        {
            void* x;
            T y;
        } u = {x};
        return u.y;


Mне кажется Вас опять относит в сторону - снова перекладываете работу по контролю типов с компилятора на свои плечи...

Автор: GoldFinch 18.7.2009, 10:22
mes, если компилятор не умеет присваивать такие указатели, мне приходится делать это за него.


переписал с использованием boost.preprocessor :

Код

//import.h

...

#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/preprocessor/array/size.hpp>
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/if.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>

#define IMPORT_IMPL_ARG_DECL_(z, i, data)    \
    BOOST_PP_COMMA_IF(i)                    \
    BOOST_PP_ARRAY_ELEM(i,data)                \
    BOOST_PP_CAT(arg,i)                        \
    /*--*/

#define IMPORT_IMPL_ARGLIST_DECL_(a_arg)    \
    BOOST_PP_REPEAT(                        \
        BOOST_PP_ARRAY_SIZE(a_arg),            \
        IMPORT_IMPL_ARG_DECL_,                \
        a_arg)                                \
    /*--*/

#define IMPORT_IMPL_EMPTY_1_(x)     /*nothing*/

#define IMPORT_IMPL_SIGNATURE_(name,arg_t,arg_n)    \
    name(                                            \
        BOOST_PP_IF(arg_n,                            \
            IMPORT_IMPL_ARGLIST_DECL_,                \
            IMPORT_IMPL_EMPTY_1_                    \
        )((arg_n,arg_t))                            \
    )                                                \
    /*--*/

#define IMPORT_IMPL_ARGLIST_(arg_n)        \
    BOOST_PP_COMMA_IF(arg_n)            \
    BOOST_PP_ENUM_PARAMS( arg_n, arg)    \
    /*--*/

#define IMPORT_METHOD(ret_t,name,arg_t,arg_n,exp_name)    \
    ret_t IMPORT_IMPL_SIGNATURE_(name,arg_t,arg_n)        \
    {                                                    \
        static import::Import imp_(exp_name);            \
        return imp_(import::ret_tag<ret_t>(),this        \
            IMPORT_IMPL_ARGLIST_(arg_n)    );                \
    }                                                    \
    /*--*/

#define IMPORT_CONSTRUCTOR(clsname,arg_t,arg_n,exp_name,vft_name)    \
    IMPORT_IMPL_SIGNATURE_(clsname,arg_t,arg_n)                        \
    {                                                                \
        import::Dll::import(vftable,vft_name);                        \
        static import::Import imp_(exp_name);                        \
        imp_(import::ret_tag<>(),this                                \
            IMPORT_IMPL_ARGLIST_(arg_n)    );                            \
    }                                                                \
    /*--*/

#define IMPORT_DESTRUCTOR(clsname,exp_name)        \
    ~clsname()                                    \
    {                                            \
        static import::Import imp_(exp_name);    \
        imp_(import::ret_tag<>(),this);            \
    }                                            \
    /*--*/

...

Код

// foo.h

#ifndef Foo_h__
#define Foo_h__

#include "Import.h"

class Foo
{
public:
    int* vftable;
    char* str;
public:    
    IMPORT_CONSTRUCTOR( Foo, (),0, "??0Foo@@QAE@XZ", "??1Foo@@UAE@XZ")
    IMPORT_DESTRUCTOR( Foo, "??1Foo@@UAE@XZ")

    IMPORT_METHOD(void,    init,    (char*),1,        "?init@Foo@@UAEXPAD@Z")
    IMPORT_METHOD(int,    sum,    (int,char),2,    "?sum@Foo@@QAEHHD@Z")
    IMPORT_METHOD(void,    test,    (),0,            "?test@Foo@@UAEXXZ")
};

#endif // Foo_h__


оригинальный класс:
Код

class __declspec(dllexport) Foo {
public:
    Foo();

    virtual void init(char* str);
    virtual void test();

    int sum(int a, char b); 

    virtual ~Foo();

private:
    char* str_;
};


Добавлено через 48 секунд
косушки расползлись =\

Автор: mes 18.7.2009, 14:09
Цитата(GoldFinch @  17.7.2009,  16:17 Найти цитируемый пост)

(нужна для приведения указателей на члены классов, т.к. они не приводятся приведеним типов в С-стиле) 

я так понял, что это проблема возникает при присвоении результата импорта. так ?
импортом я никогда не занимался , но мне полагаю, что результат этой функции можно рассматривать, как указатель на обычную функцию.
Тогда не возникнет проблем, в случае разных компиляторов (this как первый параметр или последний.)

Вобщем я так вижу
Код

class Foo 
{
 public: 
      void f (int m)  { static Imp<void (*)(Foo*, int )> imp; imp(this, m); }
}

Сейчас времени больше нет, приду если что не понятно поподробней опишу.

Автор: GoldFinch 18.7.2009, 19:07
mes, там __thiscall, this передается в ecx

Автор: mes 18.7.2009, 20:05
Цитата(GoldFinch @  18.7.2009,  18:07 Найти цитируемый пост)
там __thiscall, this передается в ecx 

теперь тогда понятно в чем загвоздка. Имхо импортировать такую функцию как метод не красиво,хотя бы потому что работать будет работать только если Ваш код и длл откомпилированы однотипным компилятором.
Как решение предполагаю, сделать адаптер, который при вызове импортируемой функции будет создавать для нее нужный контекст. Без низкоуровневого кода здесь не обойдется (тут я умываю руки), зато все зависимости останутся в этой обертке.
 smile 



Автор: GoldFinch 18.7.2009, 20:21
mes, так и есть, нужная мне длл, и мой код компилятся одним компилятором

Автор: mes 18.7.2009, 20:50
Цитата(GoldFinch @  18.7.2009,  19:21 Найти цитируемый пост)
так и есть, нужная мне длл, и мой код компилятся одним компилятором 

в таком случае самым простым способом будет все таки union... (и программист как гарант типо-преобразований)
мне кажется только сам union было бы удобно спрятать в Вашем адаптере (Imp_) :
Код

template<typename T>
struct ThiscallImport_
{   
      union
      {
            T       mtd;
            void*   proc;             
       } addr;

       operator T () { return addr.mtd; } 

      ThiscallImport_(const char* name) { Dll::import(addr.proc,name); }            
};

ну и добавить в него необходимые проверки, в частности на размер данных юниона, и на соответсвитствие нужному компилятору.

Автор: GoldFinch 6.8.2009, 14:28
ап.
практически всю информацию о методе можно выдрать из его типа.
в msvc есть интринсик _AddressOfReturnAddress() возвращающий указатель на фрейм стека

поэтому возможен следующий код
Код

class Foo
{
...

    void init(char* some_arg_name)
    {        
        static import::Import2 imp_("?init@Foo@@UAEXPAD@Z");
        return imp_( _AddressOfReturnAddress(), this, &Foo::init );
    }

где
Код

namespace import {
    struct Import2
    {
        void* addr;
        Import2(const char* name) { Dll::import(addr,name); }

        template<typename retT, typename clsT, typename T1>
        retT operator() ( void* frame, clsT* that, retT (clsT::*ptr)(T1) )
        {
#pragma pack(push,4)
            struct StackFrame
            {
                char* ret;
                T1 a1;
            } *f = (StackFrame*)frame;
#pragma pack(pop)
            return (that->*union_cast<retT(clsT::*)(T1)>(addr))(f->a1);
        }

    };
} // namespace import


Добавлено через 9 минут и 56 секунд
с макросами это будет
Код

#define IMPORT_METHOD_2(retT,name,args,exp_name)                \
    retT name args                                                \
    {                                                            \
        static import::Import2 imp_(exp_name);                    \
        return imp_( _AddressOfReturnAddress(), this, &name );    \
    }

// void Foo::init(char*);
IMPORT_METHOD_2( void, Foo::init, (char* some_arg_name), "?init@Foo@@UAEXPAD@Z" )


вместо 
Код

IMPORT_METHOD(void,    init,    (char*),1,        "?init@Foo@@UAEXPAD@Z")

и никакого BOOST_PP

Автор: GoldFinch 7.8.2009, 14:45
а вот адрес конструктора получить нельзя =\

Добавлено @ 14:49
другое дело, что конструктор можно объявить как обычный метод, и вызывать его явно.

а с деструктором проблем нет, т.к. его сигнатура всегда известна

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)