-1

I'm trying to call a C++ DLL function that is defined like this:

int read_record (filep *fptr, int key, char *contents, int *status)

This is a widely used DLL, so I'm pretty certain the problem I'm having is how I'm calling the function.

The DLL docs have this example of how to call it

 TFILETYPE *fptr;         /* file pointer
 char contents[80];   
 int status = 0;   
 int single = key;

 if (read_record(fptr, key, card, &status)) break;
 printf("%s\n", card);

Here's what I think should work, and almost does:

 type
   TCharArray = Array[1..100] of AnsiChar; // Function returns an array less than 100 char

 var
   read_record : function( var fptr: TFILETYPE;
                               Key: Integer;
                           var Contents: TCharArray; // function fills this in
                           var Status: Integer): Integer cdecl stdcall;

 Procedure Test; 
 var
   Contents: TCharArray;
   Status: Integer;
   Key: Integer;

 begin
    @read_record:= GetProcAddress(DLLHandle, 'read_record'); 
    for Key := 1 to 10 do
    begin
      Contents[1] := #0;  // shouldn't be necessary 
      if (read_record( fptr^, Key, Contents, Status) <> 0) OR (Status <> 0)  then
        ShowMessage('Error')
      else
        ShowMessage(Contents);  // This shows the expected proper string on all 10 calls

      ...Other calls at this point to other functions in the DLL result in 
      an Exception writing to x01a.
    end;

Multiple calls from Delphi XE work fine. But after that, when I call different function in the DLL that has always worked in the past, I get an exception writing to x0000001a, which I suspect means I've trashed memory or the stack.

The *fptr pointer datatype I'm using in calls to other functions in the dll, so I don't think that's the problem.

This is the first time I've tried to call a function that returns a string, so I suspect I'm not understanding something with call by reference of string arrays.

Any suggestions on how I should call this function differently to avoid what appears to be trashing of memory?

RobertFrank
  • 7,332
  • 11
  • 53
  • 99

1 Answers1

0

You have a couple of problems here.

First, the function declaration in Delphi is wrong. First, it's declared as both cdecl and stdcallat the same time. It needs to be one or the other; it can't be both simultaneously.

Also, you have an extra level of dereferencing on the fptr variable.

The declaration indicates it's a pointer:

filep *fptr

You've said in your Delphi declaration that it's a pointer:

var fptr: TFileType

But you're passing a pointer to a pointer:

fptr^

Change your call to the DLL function to

if (read_record( fptr, Key, Contents, Status) <> 0) OR (Status <> 0)

(I think your test of the results should actually be <> 0) AND (Status <> 0), but without the docs I'm not sure.)

If you do in fact need to initialize Contents before passing it to the DLL, you should use FillChar(Contents, SizeOf(Contents), #0) (or ZeroMemory) to do so, BTW.

As an additional suggestion, you can simplify your code somewhat (I've chosen stdcall as the calling convention):

var
   read_record : function( var fptr: TFILETYPE;
                           Key: Integer;
                           Contents: PAnsiChar; // function fills this in
                           var Status: Integer): Integer stdcall;

var
  Contents: AnsiString;
  ...    
begin
  SetLength(Contents, 100);
  if (read_record(fptr, Key, PAnsiChar(Contents), Status)...
  ....
end;
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • I think you actually mean he's dereferencing fptr. Anyway, code in the question strangely misses how fptr is declared.. – Sertac Akyuz Mar 10 '14 at 01:04
  • @Sertac: Yes, that's better phrasing. Edited. Thanks. :-) I noticed that missing information as well. – Ken White Mar 10 '14 at 01:06
  • Well, we don't know how `TFILETYPE` is declared in the Delphi code. So I think what you mean when you write "You've said in your Delphi declaration that it's a pointer" is that the `var` parameter semantics means that a pointer to a `TFileType` variable is passed. So, `TFileType` may not itself be a pointer. Then you say, "But you're passing a pointer to a pointer: `fptr^`". Well, if the parameter is of type `TFileType`, then `fptr^` is of type `TFileType`. Your suggestion that the asker passed `fptr` must lead to a compilation error. The question cannot be answered in detail as it stands. – David Heffernan Mar 10 '14 at 10:41
  • @David: Thanks for the feedback. For my own edification, can you explain the difference between my best guess answer to what might be happening here and your doing the same [here](http://stackoverflow.com/a/22288723? (There's no information about the type of sort being done or anything else at the time you posted your answer; the additional information was added in comments well after you posted your speculative (best guess based on the limited information provided) answer. – Ken White Mar 10 '14 at 21:33
  • It's best not to compare them. But what I state in the comments here is true. At the other question, my answer appeared after the asker's most recent comment. – David Heffernan Mar 10 '14 at 22:29
  • @David: "I believe that your fundamental problem is" is speculative based on information that isn't there (but is probably the case based on your best guess from the information that *is* there. I've had issues with DLL declarations being incorrect (and therefore incorrectly dereferenced params leading to issues when making subsequent calls to DLL procs). (Your answer appeared before the poster's most recent comment; you edited during the posting window. I saw both the initial post and your first answer, as well as the one that appears now.) – Ken White Mar 10 '14 at 22:39
  • We know for sure that `fptr` won't compile here, since we know that `fptr^` does compile. Regarding the other question, read the time stamps again. – David Heffernan Mar 10 '14 at 22:57