1

我正在尝试为我们在 Delphi 2007 中的项目制作一个使用 XE7 中的 TAmazonStorageService 组件的 dll。

但是,我有几个内存泄漏,或者数组的所有索引都返回最后一个字符串。

这是我的功能:

function ListBuckets(const PrivateKEY: PAnsiChar; const PublicKEY: PAnsiChar; 
                       out ArrayBuckets: TArray<PAnsiChar>; 
                       out Error: PAnsiChar): Boolean; stdcall;
var
  AmazonConn: TAmazonConnectionInfo;
  AmazonS3: TAmazonStorageService;
  ResponseInfo: TCloudResponseInfo;
  BucketsList: TStrings;

  I: Integer;
begin
  Result := True;

  AmazonConn := TAmazonConnectionInfo.Create(nil);

  AmazonConn.AccountName := string(PublicKEY); { AccessKeyID }
  AmazonConn.AccountKey := string(PrivateKEY); { SecretAccessKeyID }

  AmazonS3 := TAmazonStorageService.Create(AmazonConn);

  ResponseInfo := TCloudResponseInfo.Create;

  Error := '';

  try
    BucketsList := AmazonS3.ListBuckets(ResponseInfo);
    if not Assigned(BucketsList) then
    begin
      Result := False;

      Error := PAnsiChar(AnsiString(ResponseInfo.StatusMessage));
    end
    else
    begin
      SetLength(ArrayBuckets, BucketsList.Count);

      for I := 0 to BucketsList.Count - 1 do
        ArrayBuckets[I] := PAnsiChar(AnsiString(BucketsList.Strings[I]));
    end;
  finally
    BucketsList.Free;
    ResponseInfo.Free;
    AmazonS3.Free;
    AmazonConn.Free;
  end;
end;

exports ListBuckets;

这就是我如何“尝试”使用这个功能。需要在 Delphi XE7 和 Delphi 2007 中工作。

type
  TAnsiCharArray = array of PAnsiChar;

function ListBuckets(const PrivateKEY: PAnsiChar; const PublicKEY: PAnsiChar;
                       out ArrayBuckets: TAnsiCharArray; 
                       out MensagemErro: PAnsiChar): Boolean; stdcall;
                       external 'Test.dll';

function ListBucketsDelphi(const PrivateKEY: string; const PublicKEY: string;
                           out StringListBuckets: TStringList;
                           out Error: string): Boolean;
var
  vAnsiPrivateKEY: PAnsiChar;
  vAnsiPublicKEY: PAnsiChar;
  vAnsiError: PAnsiChar;
  vStringArray: TAnsiCharArray;
  I: Integer;
begin
{$IFDEF UNICODE}
  vAnsiPrivateKEY := PAnsiChar(RawByteString(PrivateKEY));
  vAnsiPublicKEY := PAnsiChar(RawByteString(PublicKEY));
{$ELSE}
  vAnsiPrivateKEY := PAnsiChar(PrivateKEY);
  vAnsiPublicKEY := PAnsiChar(PublicKEY);
{$ENDIF}

  //StringListBuckets need to be created before...

  Result := ListBuckets(vAnsiPrivateKEY, vAnsiPublicKEY, vStringArray, vAnsiMensagemErro);

  if not (Result) then    
    Error := string(vAnsiError);

  try
    if Result then
    begin
      for I := Low(vStringArray) to High(vStringArray) do
        StringListBckets.Append(vStringArray[I]);
    end;
  except
    Result := False;
    Error := '"StringListBuckets" not created.';
  end;
end;

我搜索了一下,但没有发现有关在 unicode dll 中返回数组的信息,或者我搜索错误。

有人可以帮忙吗?

提前致谢。

4

1 回答 1

3

根本无法安全地调用该函数。您有以下问题:

  • 除非您共享内存管理器,否则您将在一个模块中分配内存,并在另一个模块中销毁它。那是违反规则的。
  • 您正在返回指向out函数返回时无效的字符串(您的两个参数)的指针。换句话说,一旦函数返回,这些指针指向的东西就不再存在了。
  • 您正在跨模块边界传递 Delphi 动态数组。Delphi 动态数组对互操作无效。

最重要的是,您的功能try/finally实现不正确。您必须遵循在无数地方都可以看到的众所周知的标准模式。我不认为这是重复这一点的地方。

你需要一个完整的重新设计。一些选项:

  1. 让调用者分配内存,并将其传递给 DLL 进行填充。这将限制您预先决定要分配多少内存。
  2. 使用基于枚举的方法。调用 DLL 以分配一个保存状态的不透明指针。然后重复调用另一个函数,传递不透明指针,并产生一个字符串。当没有更多数据时,调用终结函数进行整理。用于WideString返回字符串,因为这是从共享堆中分配的。
  3. 将返回的信息序列化为例如 JSON。然后在单个字符串中返回它,再次使用WideString以利用共享 COM 堆。
  4. 让可执行文件为 DLL 提供回调函数。然后 DLL 可以分配一个数组并将其传递给回调函数。回调函数必须获取数组的副本。
  5. 声明一个接口,该接口包装了一个可以保存数据的结构。将该接口传递给 DLL。跨模块边界使用接口是安全的。
  6. 使用 COM 安全数组返回数组。
  7. 避免使用单独的 DLL 并在 Delphi 2007 模块中编写所有代码。
于 2014-10-09T22:08:10.763 回答