3

I am using Delpi XE6, if it matters.

I am writing a DLL that will get a response from Google's Directions API, parse the resulting JSON and determine the total distance of the trip.

GetJSONString_OrDie works as intended. It takes a URL and attempts to get a response from Google. If it does, it returns the JSON string that Google returns.

ExtractDistancesFromTrip_OrDie should take in a JSON string, and parse it to get the total distance of each leg of the journey, and stick those distances in an array of doubles.

SumTripDistances takes in an array of doubles, and sums up the array, and returns the total.

The curious thing is, if I take these functions out of the DLL and put them in a project and call them like that, it works as intended. Only when the are stuck into a DLL is when things seem to go wrong. As said, GetJSONString_OrDie works as intened and returns a JSON string - but when stepping through the DLL's ExtractDistancesForTrip, the passed PChar string is ''. Why is that?

unit Unit1;



interface
uses
  IdHTTP,
  IdSSLOpenSSL,
  System.SysUtils,
  System.JSON;

type
  arrayOfDouble = array of double;

function GetJSONString_OrDie(url : Pchar) : Pchar;
function ExtractDistancesForTrip_OrDie(JSONstring: Pchar) : arrayOfDouble;
function SumTripDistances(tripDistancesArray: arrayOfDouble) : double;

implementation



{ Attempts to get JSON back from Google's Directions API }
function GetJSONString_OrDie(url : Pchar) : PChar;
var
  lHTTP: TIdHTTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
begin
  {Sets up SSL}
  SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  {Creates an HTTP request}
  lHTTP := TIdHTTP.Create(nil);
  {Sets the HTTP request to use SSL}
  lHTTP.IOHandler := SSL;
  try
    {Attempts to get JSON back from Google's Directions API}
    Result := PWideChar(lHTTP.Get(url));
  finally
    {Frees up the HTTP object}
    lHTTP.Free;
    {Frees up the SSL object}
    SSL.Free;
  end;
end;

{ Extracts the distances from the JSON string }
function ExtractDistancesForTrip_OrDie(JSONstring: Pchar) : arrayOfDouble;
var
  jsonObject: TJSONObject;
  jsonArray: TJSONArray;
  numberOfLegs: integer;
  I: integer;
begin
  //raise Exception.Create('ExtractDistancesForTrip_OrDie:array of double has not yet been '+
  //                        'implemented.');

  jsonObject:= nil;
  jsonArray:= nil;
  try
    { Extract the number of legs in the trip }
    jsonObject:= TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(JSONstring), 0) as TJSONObject;
    jsonObject:= (jsonObject.Pairs[0].JsonValue as TJSONArray).Items[0] as TJSONObject;
    jsonArray:= jsonObject.Pairs[2].JSONValue as TJSONArray;
    numberOfLegs:= jsonArray.Count;

    {Resize the Resuls arrayOfDouble}
    SetLength(Result, numberOfLegs);

    {Loop through the json and set the result of the distance of the leg}
    {Distance is in km}
    for I := 0 to numberOfLegs-1 do
    begin
      Result[I]:= StrToFloat((((jsonArray.Items[I] as TJSONObject).Pairs[0].JsonValue as TJSONObject).Pairs[1]).JsonValue.Value);
    end;


  finally
    jsonObject.Free;
    jsonArray.Free;
  end;

end;


function SumTripDistances(tripDistancesArray: arrayOfDouble) : double;
var
  I: integer;
begin
  //raise Exception.Create('GetDistanceBetweenPoints_OrDie:double has not yet been ' +
 //                        'implemented.');

 Result:= 0;
 {Loop through the tripDistancesArray, and add up all the values.}
 for I := Low(tripDistancesArray) to High(tripDistancesArray) do
    Result := Result + tripDistancesArray[I];


end;

end.

Here is how the functions are being called:

program Project3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  ShareMem,
  IdHTTP,
  IdSSLOpenSSL,
  System.SysUtils,
  System.JSON,
  System.Classes;

type
  arrayOfDouble = array of double;

  testRecord = record
    testString : string;
    testInt : integer;
  end;

function GetJSONString_OrDie(url : Pchar) : PWideChar; stdcall; external 'NMBSGoogleMaps.dll';

function ExtractDistancesForTrip_OrDie(JSONstring: pchar) : arrayOfDouble;  stdcall; external 'NMBSGoogleMaps.dll';

function SumTripDistances(tripDistancesArray: arrayOfDouble) : double; stdcall; external 'NMBSGoogleMaps.dll';


var
  jsonReturnString: string;
  jsonReturnString2: Pchar;
  doubles: arrayOfDouble;
  totalJourney: double;
  uri:Pchar;

  a : testRecord;
  begin
  try
    uri:= 'https://maps.googleapis.com/maps/api/directions/json?origin=Boston,MA&destination=Concord,MA&waypoints=Charlestown,MA|Lexington,MA&key=GETYOUROWNKEY';


    { TODO -oUser -cConsole Main : Insert code here }
    jsonReturnString:= GetJSONString_OrDie(uri); //On step-through, uri is fine.

    jsonReturnString2:= stralloc(length(jsonreturnstring)+1);
    strpcopy(jsonreturnstring2, jsonreturnstring);
    jsonreturnstring2 := 'RANDOMJUNK';

    doubles:= ExtractDistancesForTrip_OrDie(jsonReturnString2); //On step-through, jsonReturnString2 is seen as '', rather than 'RANDOMJUNK'
    totalJourney:= SumTripDistances(doubles);
    WriteLn('The total journey was: ');
    WriteLn(totalJourney);

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.

When the first function is called, GetJSONString_OrDie, uri is passed in and is correct when viewed or printed out. However, after creating a random string and passing it into ExtractDistancesForTrip_OrDie, the function only sees ''. If I were to change ExtractDistancesForTrip_OrDie to accept an integer, or a record, or anything - it sees either garbage or random data. It does not matter if I pass by reference or value.

3
  • I reverted the question because you changed it completely. Please don't do that. Commented Jun 17, 2015 at 14:44
  • why do u make it DLL, do you plan for non-Delphi programs to use it ? Maybe OLE String would do better then ? Commented Jun 17, 2015 at 16:45
  • There are no plans for a non-Delphi program to use it. Commented Jun 17, 2015 at 17:31

1 Answer 1

5

Your calling conventions don't match. Your functions use register calling convention but you import them as if they were stdcall. That's why the parameters you pass are not arriving.

Use stdcall both when you implement the function, and also when you import it.

In GetJSONString_OrDie you are returning a pointer to the buffer of a local variable. As soon as the function returns, that local variable is destroyed and the pointer is invalid. You'll need to find a different way to pass string data out of this function.

Returning a string would normally be fine. But since this function is exported from a DLL that won't work. You'd need either a caller allocated buffer, or a buffer allocated on a shared heap.

Finally, dynamic arrays are not valid interop types. You should not pass these across module boundaries.

Sign up to request clarification or add additional context in comments.

19 Comments

Are you talking about GetJSONString_OrDie? I am able to view and edit the PChar that is returned by it, wouldn't that not be possible if it were getting destroyed after the function returns?
You are referring to memory that has been deallocated. The memory manager might not have overwritten that memory, or returned it to the OS. Such bugs can be confusing because the code can appear to work some times.
Makes sense! I'm fairly new to Delphi, could you provide an example of "You'd need either a caller allocated buffer, or a buffer allocated on a shared heap." ? I'm assuming I would allocate a string on the heap with some size, and then copy the PChar into it? But I'm not sure how to go about doing that(or if that is even the correct thing to do).
There are many examples around. Exactly what to use depends on many factors that I don't know. Is this function exported from the DLL? What consumes it? Can the caller allocate the buffer? I don't know any of those details.
My guess was correct. Make them both stdcall. The dyn array return value is no good either.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.