Технический вопрос по DLL

 

Смотрю внимательно пример DLL из дистрибутива.

Для быстрой справки приведу его здесь:

//+------------------------------------------------------------------+
//|                                              Sample DLL for MQL4 |
//|                   Copyright 2001-2012, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+
#define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
//---
#define MT4_EXPFUNC  extern __declspec(dllexport)
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#pragma pack(push,1)
struct RateInfo
  {
   unsigned int      ctm;
   double            open;
   double            low;
   double            high;
   double            close;
   double            vol;
  };
#pragma pack(pop)
//---
struct MqlStr
  {
   int               len;
   char             *string;
  };
static int CompareMqlStr(const void *left,const void *right);
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
  {
//---
   switch(ul_reason_for_call)
     {
      case DLL_PROCESS_ATTACH:
      case DLL_THREAD_ATTACH:
      case DLL_THREAD_DETACH:
      case DLL_PROCESS_DETACH:
         break;
     }
//---
   return(TRUE);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall GetIntValue(const int ipar)
  {
   printf("GetIntValue takes %d\n",ipar);
   return(ipar);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC double __stdcall GetDoubleValue(const double dpar)
  {
   printf("GetDoubleValue takes %.8lf\n",dpar);
   return(dpar);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC char* __stdcall GetStringValue(char *spar)
  {
   printf("GetDoubleValue takes \"%s\"\n",spar);
   return(spar);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC double __stdcall GetArrayItemValue(const double *arr,const int arraysize,const int nitem)
  {
//---
   if(arr==NULL)
     {
      printf("GetArrayItemValue: NULL array\n");
      return(0.0);
     }
   if(arraysize<=0)
     {
      printf("GetArrayItemValue: wrong arraysize (%d)\n", arraysize);
      return(0.0);
     }
   if(nitem<0 || nitem>=arraysize)
     {
      printf("GetArrayItemValue: wrong item number (%d)\n", nitem);
      return(0.0);
     }
//---
   return(arr[nitem]);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC BOOL __stdcall SetArrayItemValue(double *arr,const int arraysize,const int nitem,const double value)
  {
//---
   if(arr==NULL)
     {
      printf("GetArrayItemValue: NULL array\n");
      return(FALSE);
     }
   if(arraysize<=0)
     {
      printf("GetArrayItemValue: wrong arraysize (%d)\n", arraysize);
      return(FALSE);
     }
   if(nitem<0 || nitem>=arraysize)
     {
      printf("GetArrayItemValue: wrong item number (%d)\n", nitem);
      return(FALSE);
     }
//---
   arr[nitem]=value;
   return(TRUE);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC double __stdcall GetRatesItemValue(const RateInfo* rates,const int rates_total,const int shift,const int nrate)
  {
//---
   if(rates==NULL)
     {
      printf("GetRatesItemValue: NULL array\n");
      return(0.0);
     }
//---
   if(rates_total<0)
     {
      printf("GetRatesItemValue: wrong rates_total number (%d)\n", rates_total);
      return(0.0);
     }
//---
   if(shift<0 || shift>=rates_total)
     {
      printf("GetRatesItemValue: wrong shift number (%d)\n", shift);
      return(0.0);
     }
//---
   if(nrate<0 || nrate>5)
     {
      printf("GetRatesItemValue: wrong rate index (%d)\n", nrate);
      return(0.0);
     }
//---
   int nitem=rates_total-1-shift;
   switch(nrate)
     {
      case 0: return double(rates[nitem].ctm);
      case 1: return rates[nitem].open;
      case 2: return rates[nitem].low;
      case 3: return rates[nitem].high;
      case 4: return rates[nitem].close;
      case 5: return rates[nitem].vol;
     }
//---
   return(0.0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall SortStringArray(MqlStr *arr,const int arraysize)
  {
//---
   if(arr==NULL)
     {
      printf("SortStringArray: NULL array\n");
      return(-1);
     }
   if(arraysize<=0)
     {
      printf("SortStringArray: wrong arraysize (%d)\n", arraysize);
      return(-1);
     }
//---
   qsort(arr,arraysize,sizeof(MqlStr),CompareMqlStr);
//---
   return(arraysize);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC int __stdcall ProcessStringArray(MqlStr *arr,const int arraysize)
  {
   int   len1,len2;
//---
   if(arr==NULL)
     {
      printf("ProcessStringArray: NULL array\n");
      return(-1);
     }
   if(arraysize<=0)
     {
      printf("ProcessStringArray: wrong arraysize (%d)\n", arraysize);
      return(-1);
     }
//---
   for(int i=0; i<arraysize-1; i++)
     {
      if(arr[i].string==NULL) len1=0;
      else len1=strlen(arr[i].string);
      if(arr[i+1].string==NULL) len2=0;
      else len2=strlen(arr[i+1].string);
      //--- uninitialized string
      if(arr[i+1].string==NULL) continue;
      //--- destination string is uninitialized and cannot be allocated within dll
      if(arr[i].string==NULL)   continue;
      //--- memory piece is less than needed and cannot be reallocated within dll
      if(arr[i].len<len1+len2)  continue;
      //--- final processing
      strcat(arr[i].string,arr[i+1].string);
     }
//---
   return(arraysize);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CompareMqlStr(const void *left,const void *right)
  {
   MqlStr *leftstr=(MqlStr *)left;
   MqlStr *rightstr=(MqlStr *)right;
//---
   if(leftstr->string==NULL) return(-1);
   if(rightstr->string==NULL) return(1);
//---
   return(strcmp(leftstr->string,rightstr->string));
  }
//+------------------------------------------------------------------+

 Вроде как нормальный экспорт всех функций.

Для вызова этих функций МТ4 очевидно использует run-time linking, т.е.,

выдает сначала

  HINSTANCE hinstLib = LoadLibrary(TEXT("ExpertSample.dll")); 

а затем, к примеру, для GetIntValue :

 ProcAdd = (MYPROC) GetProcAddress(hinstLib, "GetIntValue");

и т.д.... 

В МТ4 все это работает. 

Вопрос такой  - каким образом МТ4 удается выполнить  GetProcAddress(...), если при экпорте

не экпортируется с-интерфейс ? Ведь МТ4 написан, вроде как, на с++.

Я пробовал из с++ программы вызвать  ExpertSample.dll - функции, однако GetProcAddress(...)

выдает ошибку  127 - не удается найти указанную dll-функцию.

Если же при экспорте,  функции экспортировать вместе с-интерфейсом, т.е. делать так:

extern "C"  __declspec(dllexport) int __stdcall GetIntValue(const int ipar)

  {

   printf("GetIntValue takes %d\n",ipar);

   return(ipar);

  }

то вызов из с++ программы проходит нормально. 

 
Вопрос такой  - каким образом МТ4 удается выполнить  GetProcAddress(...), если при экпорте

не экпортируется с-интерфейс ?

Видимо из-за этого?

#define MT4_EXPFUNC  extern __declspec(dllexport)
 
Daniil:

Видимо из-за этого?

Вот функция экспортируется:
__declspec(dllexport) int __stdcall GetIntValue(const int ipar);

 

Попробуйте вызвать эту функцию в режиме run-time linking из с++ программы,  не получится,  

 GetProcAddress(hinstLib, "GetIntValue")  не найдет это имя.

 

Но если экпортировать эту функцию вот так:

 extern "C"  __declspec(dllexport) int __stdcall GetIntValue(const int ipar),

то  GetProcAddress(hinstLib, "GetIntValue")  нормально находит эту функцию.

 

Разобрался, может кому пригодиться, да и сам могу забыть.

Методы экспорта и  вызова DLL функций методом run-time linking /load-time linking:

1.  Экспорт                    __declspec(dllexport) int  __stdcall GetIntValue(const int ipar) {...}

    или так    extern "C" __declspec(dllexport) int  __stdcall GetIntValue(const int ipar) {...}

    Вызов, таким образом экспортированных функций возможен только механизмом  load-time linking

2. Если поменять работу со стеком при вызове с  __stdcall  на __cdecl,  т.е.  экспортировать вот так:

   Экспорт                    __declspec(dllexport)  int  __cdecl GetIntValue(const int ipar) {...}

   или так    extern "C" __declspec(dllexport) int  __cdecl GetIntValue(const int ipar) {...},

  то вызов, таким образом экспортированных функций возможен как механизмом  load-time linking,

   так и механизмом  run-time linking.

3.  Экспортируемые  функции идут без всякой "оранжировки":

   так  -  int  __stdcall GetIntValue(const int ipar) {...}  или

   так -  int  __cdecl    GetIntValue(const int ipar) {...}

  В этом случае применение .def  файла при сборке обеспечит  оба механизма вызова - load-time linking и  run-time linking

4. Самое хорошее - применение .def  файла при сборке обеспечит  оба механизма вызова - load-time linking и  run-time linking    также  для случаев 1.  и  2.   Т.е. .def файл имеет приоритет над другими способами экспорта, если они присутствуют !

 

Я тоже озадачивался данными вопросами и пришёл к выводу, что имеется проблема в компиляторе Майкрософта. У меня стоит Студия 2010, не знаю как на других версиях. В общем, если мы указываем

extern "C" __declspec(dllexport) int  __stdcall  Func(....)

 то по какой-то причине компилятор изменяет (коверкает) конечное имя функции в DLL, несмотря на параметр extern "C", запрещающий это делать. Т.е. на выходе мы получаем что-то вроде _Func@4. Уж не знаю, баг это или фича такая... Ведь extern "C" специально предназначен для сохранения оригинального имени. А тут такое...

Если же указана директива __cdecl (либо ничего не указано), то имя функции компилируется без искажений, как и положено. Поэтому и получается, что нормально экспортируется только этот вариант. Но для МТ4 он не подходит. 

А вот в  С++ Билдере всё экспортируется без проблем в обоих случаях, имя функции не меняется. Поэтому и МТ4 её нормально видит. И кстати в Билдере можно ещё использовать __export вместо __declspec(dllexport), тоже нормально работает.

Я думал может в настройках Студии надо что-то изменить (или в настройках проекта), но покопавшись так ничего и не нашёл такого. Может кто знает?

 

Рантайм возможно неправильно задан.

С/C++ -- Code Generation -- Runtime Library. Надо MultiThreaded или MultiThreaded Debug (в завиимости от конфигурации), ЕМНИП. Вобщем пощелкайте это свойство.

А вообще имена функций задаются в def файле. Неважно какой вид они имеют. Опять же ЕМНИП.

 
TheXpert:

Рантайм возможно неправильно задан.

С/C++ -- Code Generation -- Runtime Library. Надо MultiThreaded или MultiThreaded Debug (в завиимости от конфигурации), ЕМНИП. Вобщем пощелкайте это свойство.

А вообще имена функций задаются в def файле. Неважно какой вид они имеют. Опять же ЕМНИП.

Может всё-же MultiThreadedDll/MultiThreadedDebugDll ? Речь ведь о Dll как-никак.  Впрочем я попробовал все варианты, не помогает.

Что касается использования def-файла - это всё конечно понятно. Но речь о том, что всё должно работать и без этого файла. В MSDN ведь так и сказано, что __declspec(dllexport) позволяет обойтись без def файла.  А в итоге выходит, что обойтись то конечно можно, но с искажёнными именами.

 
Так вам шашечки или ехать? для MT4 def файл нужен. А там имена задаются.