views:

529

answers:

3

I have a weird problem trying to use a DLL written in C++ from a Delphi (Turbo Delphi 2006) program.

When I run the Delphi program (see below) from the command line, everything works fine. Also, when I run it from the Delphi environment without debugging (CTRL+SHIFT+F9), everything is fine. However, when running it with debugging (F9), I get the following error:

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

The weird thing is that the error occurs on executing the last 'end.' of the code. Delphi's CPU display says this is somewhere in 'UnsetExceptionHandler', four lines before 'FinalizeUnits', to be more specific, at

00403FDF 3901 cmp [ecx],eax

I'm somewhat at a loss here; Delphi is not my domain (I was the one writing the DLL, and now I need to provide an example program using it). So any help on this issue is greatly appreciated :)

Here's the Delphi code:

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
A: 

I think both DLL_THREAD_DETACH and DLL_PROCESS_DETACH will be called. So you'll be double deleting your calc object.

Try...

if ( calc != NULL )
{
    delete calc;
    calc = NULL;
}
Paul Mitchell
no, i tested that by inserting some cout statements. only DLL_PROCESS_DETACH is called. plus, the problem was also there when i did not delete the calc object.
andreash
A: 

I had a similar problem when loading Dlls with LoadLibrary.

I got round it by calling Application.ProcessMessages before FreeLibrary.

Re0sless
hmm, sounds like a good plan, and i'd like to try this. however, Delphi won't compile my code when I just write Application.ProcessMessages before the FreeLibrary call. I guess I have to include some other unit, but which? Google didn't help me here ...
andreash
Its in the Forms unit, but as you are using a console app I don't know if you can use it.
Re0sless
thanks, yes i can use Forms unit in console app. But > Application.ProcessMessages;right before the> FreeLibrary(DLLHandle);didn't help.
andreash
+2  A: 

Check the calling conventions. I see stdcall in 2 functions and cdecl in one function. Try to change everything to stdcall or cdecl and see if it works

TwinForms
great, that did the trick. i don't really know why i defined the setPath and getError functions as stdcall ... changed them to cdecl and everything works like a charm. thanks a lot!
andreash