2009-08-07 7 views
0

У меня есть странная проблема с попыткой использовать DLL, написанную на C++, из программы Delphi (Turbo Delphi 2006).AccessViolation при использовании C++ DLL из Delphi

Когда я запускаю программу Delphi (см. Ниже) из командной строки, все работает нормально. Кроме того, когда я запускаю его из среды Delphi без отладки (CTRL + SHIFT + F9), все в порядке. Однако, при его запуске с отладки (F9), я получаю следующее сообщение об ошибке:

Project Z:\test.exe faulted with message: 'access violation at 0x00403fdf: read of address 0x00a14e74'. Process stopped. Use Step or Run to continue.

Странная вещь, что ошибка происходит на выполнении последнего «конец». кода. Дисплей CPU Делфи говорит, что это где-то в «UnsetExceptionHandler», четыре линии до «FinalizeUnits», чтобы быть более точным, в

00403FDF 3901 cmp [ecx],eax

Я несколько растерян здесь; Delphi не мой домен (я был тем, кто писал DLL, и теперь мне нужно предоставить примерную программу, использующую его). Таким образом, любая помощь по этому вопросу очень ценится :)

Вот код Delphi:

program libiup_integration; 
{$APPTYPE CONSOLE} 

uses 
    SysUtils, 
    WinProcs; 

type 
    T_F_GetError = function() : pchar; stdcall; 

    T_F_SetPath = function(path: pchar) : integer; stdcall; 

    T_F_GetStrat = function(date: integer;time: single;lat: single;lon: single) : single; cdecl; 

var 
    F_GetError : T_F_GetError; 
    PF_GetError : TFarProc; 
    F_SetPath : T_F_SetPath; 
    PF_SetPath : TFarProc; 
    F_GetStrat : T_F_GetStrat; 
    PF_GetStrat : TFarProc; 
    DLLHandle : THandle; 
    errormsg : pchar; 
    h5path : pchar; 
    h5err  : integer; 
    date  : integer; 
    time  : single; 
    lat  : single; 
    lon  : single; 
    strat  : single; 
    i   : integer; 

begin 
    DLLHandle := LoadLibrary('libiup.dll'); 
    if DLLHandle <> 0 then 
    begin 
     { construct the function pointers } 
     PF_GetError := GetProcAddress(DLLHandle, 'getError'); 
     PF_SetPath := GetProcAddress(DLLHandle, 'setPath'); 
     PF_GetStrat := GetProcAddress(DLLHandle, 'getStrat'); 

     { If the function pointer is valid ... } 
     if (PF_GetError <> nil) and (PF_SetPath <> nil) and (PF_GetStrat <> nil) then 
     begin 
      { Assign the function pointer to the function handle } 
      @F_GetError := PF_GetError; 
      @F_SetPath := PF_SetPath; 
      @F_GetStrat := PF_GetStrat; 

      errormsg := StrAlloc(4096); 

      h5path := StrAlloc(256); 
      StrCopy(h5path, 'z:\data\%Y%m.h5'); 
      h5err := F_SetPath(h5path); 
      if h5err < 0 then 
      begin 
       errormsg := F_GetError(); 
       WriteLn(errormsg); 
      end; 

      for i := 1 to 10 do 
      begin 
       date := 4745; 
       time := 12.34 + i/10; 
       lat := -35.321 + i*i; 
       lon := 115.67 - i*i; 

       strat := F_GetStrat(date, time, lat, lon); 
       if strat < 0. then 
       begin 
        errormsg := F_GetError(); 
        WriteLn(errormsg); 
       end; 

       WriteLn('Value returned by getStrat call no. ' + IntToStr(i) + ': ' + FloatToStr(strat)); 
      end; 

      { and finally, delete the function pointers ...} 
      PF_SetPath := nil; 
      PF_GetStrat := nil; 
      PF_GetError := nil; 
      FreeLibrary(DLLHandle); 
      WriteLn('Press ENTER to continue ...'); 
      ReadLn; 
     end 

     else 
     { The function pointer was not valid, so this means that the function was not found in the dll. } 
     begin 
      WriteLn('Function not found'); 
      RaiseLastOSError; 
     end; 
    end 

    else 
    { The LoadLibrary function did not return a valid DLL handle. } 
    begin 
     WriteLn('DLL not loaded'); 
     FreeLibrary(DLLHandle); 
     WriteLn('Press ENTER to continue ...'); 
     ReadLn; 
    end; 
end. 

dll.h

#ifndef LIBIUP_DLL_H_ 
#define LIBIUP_DLL_H_ 
#ifdef BUILD_DLL 
#define WIN32DLL_API __declspec(dllexport) 
#else 
#define WIN32DLL_API __declspec(dllimport) 
#endif 

#include "stratcalc/SimpleStratosphericColumnCalculator.h" 
#include <iostream> 
#include <string> 
#include "boost/date_time/posix_time/posix_time.hpp" 

#ifdef __cplusplus 
extern "C" {   /* Assume C declarations for C++ */ 
#endif 
    WIN32DLL_API BOOL __stdcall DllMain(HANDLE, DWORD, LPVOID); 
    WIN32DLL_API int setPath(char*); 
    WIN32DLL_API const char* getError(); 
    WIN32DLL_API float getStrat(int, float, float, float); 
    std::string errormsg; 
    SimpleStratosphericColumnCalculator* calc; 
#ifdef __cplusplus 
}      /* End of extern "C" */ 
#endif 

#endif 

dll.cpp

#ifdef BUILD_DLL 
#include "windows.h" 
#include "dll.h" 
#include <iostream> 
// different functions of this library 
= new SimpleStratosphericColumnCalculator(); 


WIN32DLL_API BOOL __stdcall DllMain(HANDLE hModule, 
         DWORD ul_reason_for_call, 
         LPVOID lpReserved 
        ) 
{ 
    switch (ul_reason_for_call) 
    { 
     case DLL_PROCESS_ATTACH: 
      calc = new SimpleStratosphericColumnCalculator(); 
      break; 
     case DLL_THREAD_ATTACH: 
      calc = new SimpleStratosphericColumnCalculator(); 
      break; 
     case DLL_THREAD_DETACH: 
      delete calc; 
      break; 
     case DLL_PROCESS_DETACH: 
      delete calc; 
      break; 
    } 

    return TRUE; 
} 


WIN32DLL_API int setPath(char* Path) 
{ 
    errormsg = ""; 
    return calc->setPath(Path); 
} 

WIN32DLL_API const char* getError() 
{ 
    std::cout << errormsg << std::endl; 
    return errormsg.c_str(); 
} 

WIN32DLL_API float getStrat(int Date, float Time, float Lat, float Lon) 
{ 
    errormsg = ""; 
    if (Lat < -90. || Lat > 90.) 
     errormsg += "Latitude value out of bounds.\n"; 
    if (Lon < 0. || Lon > 360.) 
     errormsg += "Longitude value out of bounds.\n"; 
    if (errormsg != "") 
     return -1.; 
    return (float)calc->getStrat(Date, Time, Lat, Lon); 
} 
#else 
#endif 
+0

'T_F_GetStrat = function (date ...: single; cdecl;' try stdcall convention –

ответ

2

Проверьте соглашения о вызовах. Я вижу stdcall в 2 функциях и cdecl в одной функции. Попробуйте изменить все на stdcall или cdecl и посмотреть, работает ли он

+0

отлично, это сделал трюк. я действительно не знаю, почему я определил функции setPath и getError как stdcall ... изменил их на cdecl, и все работает как шарм. большое спасибо! –

0

Я думаю, что оба Будут вызываться DLL_THREAD_DETACH и DLL_PROCESS_DETACH. Таким образом, вы будете дважды удалять свой объект calc.

Try ...

if (calc != NULL) 
{ 
    delete calc; 
    calc = NULL; 
} 
+0

нет, я тестировал это, вставив некоторые инструкции cout, вызывается только DLL_PROCESS_DETACH plus, проблема была и там, когда я не удалял объект calc. –

0

У меня была аналогичная проблема при загрузке библиотеки DLL с LoadLibrary.

Я обошел его, вызвав Application.ProcessMessages перед FreeLibrary.

+0

Хмм, звучит неплохо, и я бы хотел попробовать. Однако Delphi не будет компилировать мой код, когда я просто пишу Application.ProcessMessages перед вызовом FreeLibrary. Я предполагаю, что мне нужно включить другое подразделение, но что? Google не помог мне здесь ... –

+0

Его в модуле Forms, но поскольку вы используете консольное приложение, я не знаю, можете ли вы его использовать. – Re0sless

+0

спасибо, да, я могу использовать модуль форм в консольном приложении. Но > Application.ProcessMessages; непосредственно перед > FreeLibrary (DLLHandle); не помогло. –