//Вячеслав Ермолаев. http://bcdev.narod.ru
//Использование template-классов при импортировании функций из dll

void __fastcall TMainForm::OpenTaskActionExecute(TObject *Sender)
{
 void (*RunDLL)(TComponent*, char*,char*);
 try
 {
  HINSTANCE DllInstance;
  AnsiString DllName = LocalBestPmExe + Data->TaskTree_NAME->AsString;
#pragma warn -pia
  if (!(DllInstance = LoadLibrary(DllName.c_str())))
  {
   throw(Exception("Ошибка загрузки DLL:" + DllName));
  }
  if(!(RunDLL = (void (*)(TComponent*, char*,char*))
                 GetProcAddress(DllInstance,ProcName.c_str())))
  {
   throw(Exception("Ошибка получения адреса функции:"+ ProcName.c_str()));
  }
  RunDLL(this,UserCode.c_str(),LabelCode.c_str());
 }
#pragma warn .pia
 catch(Exception& excp)
 {
  ShowErr(mtError,&excp);

   }
}

//Сколько раз Вам приходилось писать такой или примерно такой код загрузки
//динамической  библиотеки и вызова из нее функции? Если Вы занимались
//программированием достаточно серьезных приложений, думаю не один раз.
//Вот и я как то в  очередной раз закончив деэлельку, стал набивать
//в основной программе уже набивший оскомину код загрузки этой dll и вызова
//функции из него, как перед глазами на мгновение материализовался невесть
//откуда-то взявшийся и почему-то очень знакомый тип в клетчатом пиджаке и
//разбитом пенсне и ехидным голосом произнес "А сапожник то без сапог". 
//Тот час же ногам стало холодно и неуютно, запахло сыростью и серой.
//"Допрограммировался"- подумал я, и еще: "Вот так начинается шизофрения.
//Да и пиво вчера надо пить было поменьше. И причем тут сапоги?". Но
//поскольку дальнейшего продолжения не последовало и ботинки вроде бы
//оказались на месте, я решил все же посмотреть, чем было вызвано появление
//сей нечисти , так красочно описанной еще Булгаковым.
//Я вгляделся в только что написанные строчки и ужаснулся:  ну и где же
//
здесь великий и могучий ООП, к поклонникам которого я себя так гордо отношу.
//Передо мной черным по белому чернелась абракадабра, которая по большей
//части к конечной цели - вызову функции с тремя параметрами - прямого
//отношения не имела. Ну да, здесь конечно есть над чем задуматься.
//
Сколько раз я с умным видом пытался втиснуть проблемы бухгалтеров,
//менеджеров, экономистов и т.п. в рамки парадигмы ООП, а на себя 
//времени конечно не хватало. Действительно сапожник. Ну это не беда.
//Чем там у нас отгоняют нечистую силу? Святым писанием? Быстренько
//хватаю Страуструпа "Язык программирования C++" и читаю бессмертные
//формулировки правила "правой руки"(стр.16,Москва,"Радио и связь",1991):
// "a)если вы считаете "это" отдельным понятием, сделайте его классом;
// "б)если вы считаете "это" отдельным объектом, сделайте его 
//    объектом некоторого класса".
//И всего то? Ну в данном вопросе можно обойтись и без помощи аналитика.
//В распоряжении имеем два понятия - два класса: динамическую библиотеку
//и импортируемую функцию. Сейчас мы их быстренько оформим в 
//классы-враперы (от Wraper или Adapter, "Приемы объектно-ориентированного   
//проектирования(паттерны проектирования)",Э.Гамма и др.,Санкт-Петербург,
//"Питер",2001) и будем спать спокойно. Хотя кажется нет. Спят спокойно,
//когда платят налоги. Итак

class TDll {
    HINSTANCE m_DLLInstance;
public:
    TDll(const char* a_Name) {
#pragma option push -w-pia
     if (!(m_DLLInstance = LoadLibrary(a_Name)))
#pragma option pop
      throw(Exception("Ошибка загрузки библиотеки:"+AnsiString(a_Name)));
    }
    ~TDll(){
     if (m_DLLInstance) FreeLibrary(m_DLLInstance);
    }
    operator HINSTANCE() const { return m_DLLInstance; }
};

//С динамической библиотекой я разобрался достаточно просто. А что делать
//с импортируемой функцией с заранее неизвестным типом возвращаемого 
//значения да еще с параметрами, коих число и типы тоже могут быть 
//совершенно различны. Короче говоря, все предложенные мною варианты мною
//самим же были отметены. Изящное решение не никак находилось. К тому же 
//меня постоянно преследовало чувство дежа-вю, в смысле того, что я это 
//уже где-то видел и незачем изобретать велосипед. Ну да, конечно! Старый
//добрый OWL -  шедевр объектно-ориентированного программирования. Кстати,
//проект не совсем умер и поддерживается международной группой энтузиастов.
//Ознакомиться с состоянием дел по OWL  можно на сайте  www.owlnext.org. 
//Итак попробуем адаптировать борландовское решение. Для начала определим
//базовый класс TDllProc

class TDllProc {
public:
  TDllProc(const TDll& a_Dll, const char*  a_Name)
  {
#pragma option push -w-pia
   if(!(m_Proc = GetProcAddress(HINSTANCE(a_Dll),a_Name)))
#pragma option pop
    throw(Exception("Ошибка получения адреса функции:" + AnsiString(a_Name)));
  }
protected:
FARPROC m_Proc;
};
 

//Далее все предельно просто. Надо просто определить ряд template классов,
//являющихся производными от
TDllProc. Прежде всего о соглашениях. Борланд
//предложил использовать следующие соглашения:
//
//  Имя класса для функции, которая не возвращает результат  - TDllProcVX 
//  Имя класса для функции, которая возвращает результат     - TDllProcX.
//   где X - число параметров функции.
//
//Вот этой изюминки - кодирование возврата и числа параметров -   мне не
//хватило, чтобы решить задачу самостоятельно. Остается только с завистью
//следить за полетом мысли профессионалов. Итак, определяем класс-врапер 
//для инкапсуляции функции типа void (*)()

class TDllProcV0 : public TDllProc {
public:
  TDllProcV0(
const TDll& a_Dll, const char*  a_Name)
  :TDllProc(a_Dll,a_Name) {}
  void operator ()() 
  {
    typedef void (*TProc)();
         ((TProc)m_Proc)();
  }
};

//Здесь пока шаблоны не применяются, незачем - нет ни параметров, ни 
//возвращаемого результата. Но вот для инкапсуляции вызова функции 
//без параметров, но возвращающей результат, потребуется уже шаблон:

template <class R>
class
TDllProc0 : public TDllProc {
public:
  TDllProc0(
const TDll& a_Dll, const char*  a_Name)
  : TDllProc(a_Dll,a_Name) {}
  R operator ()() 
  {
    typedef R (* TProc)();
    return ((TProc)m_Proc)();
  }
};

//Также при определении классов  для все последующих  вариантов функций 
//без шаблонов не обойтись. Следующей у нас будет функция типа без 
//возврата результата с одним параметром, по нашим соглашениям TDllProcV1

template <class P1>
class
TDllProcV1 : public TDllProc {
public:
  TDllProcV1(
const TDll& a_Dll, const char*  a_Name)
  : TDllProc(a_Dll,a_Name) {}
  void operator ()(P1 p) 
  {
    typedef void (* TProc)(P1);
    ((TProc)m_Proc)(p1);
  }
};

//Для полной ясности приведем еще объявление класса для функции с 
//одним параметром и с возвратом результата - TDllProc1

template <class R, class P1>
class
TDllProc1 : public TDllProc {
public:
  TDllProc1(
const TDll& a_Dll, const char*  a_Name)
  : TDllProc(a_Dll,a_Name) {}
  R operator ()(P1 p1) 
  {
    typedef R (* TProc)(P1 );
    return ((TProc)m_Proc)(p1);
  }
};

//Думаю приведенного кода для понимания идеи вполне достаточно. Просто  
//нужно последовательно аналогичным порядком определить классы для функций
//с возвратом и без возврата с двумя, тремя и т.д. числом параметров. Вы
//можете остановиться на любом числе. Программисты от Борланда выбрали 
//число 13. Видно у них тоже были проблемы с нечистой силой. Не будем 
//нарушать заведенный порядок и то же остановимся на числе 13. Последние
//два класса для функций с 13-ю параметрами будут выглядеть следующим
//образом

template <class P1,class P2,class P3,class P4,class P5,class P6,class P7,
          class P8,class P9,class P10,class P11,class P12,class P13>
class
TDllProcV13 : public TDllProc {
public:
  TDllProcV13(
const TDll& a_Dll, const char*  a_Name)
  : TDllProc(a_Dll,a_Name) {}
  void operator ()(P1 p1,P2 p2,P3 p3,P4 p4,P5 p5,P6 p6,P7 p7,P8 p8,
                   P9 p9,P10 p10,P11 p11,P12 p12,P13 p13) 
  {
    typedef void (* TProc)(P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13);
    ((TProc)m_Proc)(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13);
  }
};
template <class R,class P2,class P3,class P4,class P5,class P6,class P7,
          class P8,class P9,class P10,class P11,class P12,class P13>
class
TDllProc13 : public TDllProc {
public:
  TDllProc13(
const TDll& a_Dll, const char*  a_Name)
  : TDllProc(a_Dll,a_Name) {}
  R operator ()(P1 p1,P2 p2,P3 p3,P4 p4,P5 p5,P6 p6,P7 p7,P8 p8,
                P9 p9,P10 p10,P11 p11,P12 p12,P13 p13) 
  {
    typedef R (* TProc)(P1 );
    return ((TProc)m_Proc)(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13);
  }
};

//Ну вот и все. Теперь вернемся к тому, с чего все началось. Моя функция
//будет теперь выглядеть следующим образом  

void __fastcall TMainForm::OpenTaskActionExecute(TObject *Sender)
{
 try
 {
  //TDll создаем динамически, т.к выгрузка dll происходит по окончании 
  //работы программы - это определяется логикой работы приложения
 
TDll*  dll = new TDll(DllName.c_str());
  //если нужно выгрузить dll после вызова функции, объявление
  //выглядело бы так: TDll dll(
DllName.c_str());
  //объявлем объект-функциию 
    TDllProcV3<TComponent*,char*,char*> RunDll(*dll,ProcName.c_str());
  //ну и наконец сам вызов
 
RunDll(this,UserCode.c_str(),LabelCode.c_str());

 }
 catch(Exception& excp)
 {
  ShowErr(mtError,&excp);

   }
}

//Вся процедура вызова функции из динамической библиотеки уложилась в три
//строки: два объявления и вызов. На мой взгляд достаточно лаконично и
//изящно. Теперь вставляем в проект, компилируем и ... получаем:
// [C++Error] Main.cpp(96):E2238 Multiple declaration for TDll
// [C++Error] uticls.h(500):E2344 Earlier declaration for TDll
//Вот мистика! Что-то опять запахло серой. Дрожащей рукой открываю 
//файл uticls.h и вижу, что парни из Борланда тоже не забыли свою 
//технологию из OWL, но дали ее в сильно урезанном виде исключительно, 
//по всей видимости, для своих целей. Ниже весь борландовский кусок:

////////////////////////////////////////////////////
// TDll: Wrapper class encapsulating a DLL
////////////////////////////////////////////////////

class TDll
{
public:
TDll(LPCTSTR name) : m_Hinstance(::LoadLibrary(name)) {}
~TDll()
{
if (m_Hinstance)
::FreeLibrary(m_Hinstance);
}

operator HINSTANCE() const { return m_Hinstance; }
FARPROC GetProcAddress(LPCSTR funcName) const { return ::GetProcAddress(*this, funcName); }

protected:
HINSTANCE m_Hinstance;

private:
TDll(const TDll&);
TDll& operator=(const TDll&);
};

//////////////////////////////////////////////////
// TDllProc: Encapsulates a DLL EntryPoint/FARPROC
//////////////////////////////////////////////////
class TDllProc
{
public:
TDllProc(const TDll& dll, LPCSTR funcName)
{
m_Proc = dll.GetProcAddress(funcName);
}
operator bool() { return m_Proc != 0; }
protected:
FARPROC m_Proc;
};

////////////////////////////////////////////////////////////////
// FARPROC that's CDECL, returns HRESULT and takes one parameter
////////////////////////////////////////////////////////////////

template <class P1>
class TDllProc1 : public TDllProc
{
public:
TDllProc1(const TDll& dll , LPCTSTR funcName) : TDllProc(dll, funcName) {};
HRESULT operator ()(P1 erste)
{
typedef HRESULT (__cdecl* outproc)(P1 erste);
return ((outproc)m_Proc)(erste);
}
};

//////////////////////////////////////////////////////////////////
// FARPROC that's STDCALL, returns HRESULT and take two parameters
//////////////////////////////////////////////////////////////////

template <class P1, class P2>
class TDllStdProc2 : public TDllProc
{
public:
TDllStdProc2(const TDll& dll, LPCTSTR funcName) : TDllProc(dll, funcName) {}
HRESULT operator () (P1 p1, P2 p2)
{
typedef HRESULT (__stdcall* outproc)(P1 p1, P2 p2);
return ((outproc)m_Proc)(p1, p2);
}
};

//////////////////////////////////////////////////////////////////
// FARPROC that's CDECL, returns a void pointer,
// and takes two parameters
//////////////////////////////////////////////////////////////////

template <class P1, class P2>
class TDllProc2 : public TDllProc
{
public:
TDllProc2(const TDll& dll, LPCTSTR funcName) : TDllProc(dll, funcName) {}
void* operator () (P1 p1, P2 p2)
{
typedef void* (__cdecl* outproc)(P1 p1, P2 p2);
return ((outproc)m_Proc)(p1, p2);
}
};
 

//Как видим здесь они ограничились только  тремя функциями. Ну это не так 
//страшно. Чтобы на будущее исключить конфликты, обрамляю свои классы в 
//namespace MyDll и запускаю на трансляцию заново. Все проходит без проблем 
//в том числе и проверка на работоспособность. Ну на этом с чувством 
// выполненного  долга можно и успокоиться.

//Для тех, кто хочет использовать шаблоны для вызова функций и не намерен
//набивать их руками,  мой вариант template классов с демонстрационным
//примером их использования  может скачать отсюда     

 

  


 

        

   

  



Сайт управляется системой uCoz