【问题标题】:Delphi PChar string is empty when passed to functionDelphi PChar 字符串在传递给函数时为空
【发布时间】:2015-06-17 12:35:55
【问题描述】:

如果重要的话,我正在使用 Delpi XE6。

我正在编写一个 DLL,它将从 Google 的 Directions API 获得响应,解析生成的 JSON 并确定行程的总距离。

GetJSONString_OrDie 按预期工作。它需要一个 URL 并尝试从 Google 获得响应。如果是,它会返回 Google 返回的 JSON 字符串。

ExtractDistancesFromTrip_OrDie 应该接收一个 JSON 字符串,并对其进行解析以获得旅程中每段的总距离,并将这些距离保存在一个双精度数组中。

SumTripDistances 接收一个双精度数组,对数组求和,然后返回总数。

奇怪的是,如果我从 DLL 中取出这些函数并将它们放入一个项目中并像这样调用它们,它就会按预期工作。只有当它们被困在 DLL 中时,才会出现问题。如前所述,GetJSONString_OrDie 按预期工作并返回一个 JSON 字符串 - 但是当单步执行 DLL 的 ExtractDistancesForTrip 时,传递的 PChar 字符串是 ''。这是为什么呢?

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.

以下是函数的调用方式:

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.

当调用第一个函数GetJSONString_OrDie时,uri被传入并且在查看或打印出来时是正确的。 但是,在创建一个随机字符串并将其传递给ExtractDistancesForTrip_OrDie 之后,该函数只能看到''。如果我要更改 ExtractDistancesForTrip_OrDie 以接受整数、记录或任何内容 - 它会看到垃圾数据或随机数据。如果我通过引用或值传递并不重要。

【问题讨论】:

  • 我回复了这个问题,因为你完全改变了它。请不要那样做。
  • 你为什么要做DLL,你打算让非Delphi程序使用它吗?也许 OLE String 会做得更好?
  • 没有计划让非 Delphi 程序使用它。

标签: json delphi dll


【解决方案1】:

您的调用约定不匹配。您的函数使用register 调用约定,但您将它们导入为stdcall。这就是您传递的参数没有到达的原因。

在实现函数和导入函数时都使用stdcall

GetJSONString_OrDie 中,您正在返回一个指向局部变量缓冲区的指针。一旦函数返回,该局部变量就被销毁并且指针无效。您需要找到一种不同的方法来将字符串数据从该函数中传递出去。

返回一个字符串通常没问题。但是由于这个函数是从一个不起作用的 DLL 中导出的。您需要调用者分配的缓冲区或共享堆上分配的缓冲区。

最后,动态数组不是有效的互操作类型。您不应该跨模块边界传递这些。

【讨论】:

  • 你说的是GetJSONString_OrDie吗?我可以查看和编辑它返回的 PChar,如果它在函数返回后被销毁,那不是不可能吗?
  • 您指的是已释放的内存。内存管理器可能没有覆盖该内存,或将其返回给操作系统。此类错误可能会令人困惑,因为代码有时似乎可以工作。
  • 有道理!我对 Delphi 很陌生,您能否提供一个“您需要调用者分配的缓冲区或分配在共享堆上的缓冲区”的示例。 ?我假设我会在堆上分配一个具有一定大小的字符串,然后将 PChar 复制到其中?但我不知道该怎么做(或者这是否是正确的做法)。
  • 周围有很多例子。确切地使用什么取决于我不知道的许多因素。这个函数是从 DLL 导出的吗?什么消耗它?调用者可以分配缓冲区吗?我不知道这些细节。
  • 我的猜测是正确的。使它们都成为标准调用。 dyn 数组返回值也不行。
猜你喜欢
  • 2012-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-28
  • 2023-04-01
  • 2017-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多