views:

257

answers:

3

Hi all, I need call a DLL file in my delphi code, here is the code snippet of the DLL Head file:

#define BookInfoDLL __declspec(dllexport)

struct _BookTime
{
    unsigned char day;
    unsigned char month;
    unsigned short year;
};

struct _stBookData
{
    unsigned char encrypt;
    _BookTime bkTime;
    unsigned int  PageCount;
};

int BookInfoDLL UpdateBooks(const char * const pBookID, 
  const char cBookTypeWord, 
  const _stBookData * const pBookData, 
  const int nBookDataCounter);

I need invoke the dll function "UpdateBooks" in my delphi code. How can I convert those code into delphi? Thank you!

+3  A: 

Use h2pas! Although it is a freepascal tool, it should produce Delphi compatible code.

Kornel Kisielewicz
+1  A: 

Hello Leo,

I have ended my first C header conversion yesterday. The articles and tools from TeamB member Rudy Velthuis were very, very helpful for me, specially

Pitfalls of converting

Conversion Helper Package

Heinz Z.
+2  A: 

Snippet for non-managed Delphi code (not tested, but compiles and changed according suggestions in comments):

interface

type

  TBookTime = packed record
    day   : byte; // unsigned 8-bit  
    month : byte;
    year  : word; // unsigned 16-bit
   end;

  TBookData = packed record
    encrypt   : byte;
    bkTime    : TBookTime;
    PageCount : LongWord;   // unsigned 32-bit
  end;

  TBookDataPtr = ^TBookData;

function UpdateBooks(
           pBookID          : PChar;
           cBookTypeWord    : byte;
           pBookData        : TBookDataPtr;
           nBookDataCounter : integer
         ) : integer; stdcall; external 'dll_file_name.dll' name 'UpdateBooks';

implementation

 // ... 

end;

Simple call UpdateBooks(...) from delphi code.


Update: code changed, thanks for commenting!

Below is snippets for sample calls ...

Common functions and constants for all snippets:

// --- Test data fill utility and constants -----------------------------------

const
  BOOK_ID         = 'Test Book ID';
  BOOK_TYPE_WORD  = 'T';
  BOOK_DATA_COUNT = 5;

procedure FillTestBookData(pBookData : TBookDataPtr; iTestNum : integer);
begin
  if(pBookData = nil) then exit;

  pBookData^.encrypt := iTestNum;
  pBookData^.bkTime.day := iTestNum;
  pBookData^.bkTime.month := iTestNum;
  pBookData^.bkTime.year := 2000 + iTestNum;
  pBookData^.PageCount := iTestNum;

end;

Calling function in common Delphi style:

// --- Test procedure in Delphi style -----------------------------------------

procedure TestBookUpdate_DelphiStyle;
var
  bookArray   : array of TBookData;
  iBookNumber : integer;
begin

  SetLength(bookArray, BOOK_DATA_COUNT);
  try

    for iBookNumber := Low(bookArray) to High(bookArray) do begin
      FillTestBookData( @(bookArray[iBookNumber]), iBookNumber );
    end;

    UpdateBooks( 
      PChar(BOOK_ID), ord(BOOK_TYPE_WORD), 
      @(bookArray[Low(bookArray)]), BOOK_DATA_COUNT 
    );

  finally
    SetLength(bookArray, 0); // no explicit requirement to include in code
  end;

end;

Bonus: same test calls in C-style and Pascal-style :-)

// --- Test procedure in Old Delphi (plain Pascal) style ----------------------

type
  TBookDataOldArray = array[0..0] of TBookData;
  TBookDataOldArrayPtr = ^TBookDataOldArray;

// Store range checking compiler option state
{$IFOPT R+}
  {$DEFINE RANGE_CHECK_ON}
{$ENDIF}

procedure TestBookUpdate_OldDelphiStyle;
var
  bookArrayPtr : TBookDataOldArrayPtr;
  iBookNumber  : integer;
begin

  GetMem(bookArrayPtr, BOOK_DATA_COUNT*sizeof(TBookData));
  try
    // Disable range checking compiler option
    {$R-}

    for iBookNumber := 0 to BOOK_DATA_COUNT - 1 do begin
      FillTestBookData(@(bookArrayPtr^[iBookNumber]), iBookNumber);
    end;

    // Restore range checking compiler option if turned on before disabling
    {$IFDEF RANGE_CHECK_ON}{$R+}{$ENDIF}

    UpdateBooks(
      PChar(BOOK_ID), ord(BOOK_TYPE_WORD), TBookDataPtr(bookArrayPtr), BOOK_DATA_COUNT
    );

  finally
    FreeMem(bookArrayPtr);
  end;

end;

// --- Test procedure in C style ---------------------------------------------

procedure TestBookUpdate_CStyle;
var
  bookArrayPtr  : TBookDataPtr;
  curBookPtr    : TBookDataPtr;
  curBookNumber : integer;
begin

  bookArrayPtr := AllocMem( BOOK_DATA_COUNT * sizeof(TBookData) );
  try
    curBookNumber := 0;
    curBookPtr := bookArrayPtr;
    while(curBookNumber < BOOK_DATA_COUNT) do begin
      FillTestBookData( curBookPtr, curBookNumber );
      inc(curBookNumber);
      inc(curBookPtr, 1);
      // Another pointer increment solution is :
      // curBookPtr := PChar(curBookPtr) + sizeof(TBookData);
    end;

    UpdateBooks( PChar(BOOK_ID), ord(BOOK_TYPE_WORD), bookArrayPtr, BOOK_DATA_COUNT );

  finally
    FreeMem(bookArrayPtr);
  end;

end;
ThinkJet
The param "pBookdata" may hold more than one TBookData struct, "nBookDataCounter" point out the number of TBookData struct.
Leo
Leo, that isn't *necessarily* a problem with ThinkJet's translation, but it will make the function calls look a little strange. You'll have an array, but you'll pass just the first element to the function: `UpdateBooks(..., BookData[0], Length(BookData))`. ThinkJet, it's better to just translate pointer parameters in C++ into pointer parameters in Delphi.
Rob Kennedy
Alignment of records? Probably needs to be packed. Also I think drop all CONST except pbookdata because for that one a dereference (*) was dropped.
Marco van de Voort
Thanks for all commentators! Code changed according to comments, you are welcome to review it another time! :)
ThinkJet