2014-10-09 45 views
1

我试图让使用该TAmazonStorageService成分XE7为我们的项目在2007年德尔福DLL在Delphi XE7由返回的字符串数组用于2007年德尔福

但是一个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; 

这里是如何IM “试图” 使用此功能。需要在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返回数组,或者也许是我错了搜索。

有人可以帮忙吗?

在此先感谢。

回答

3

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

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

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

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

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

我不确定这个,**问题是针对@DavidHeffernan:因为宽字符串是** CCOMBSTR **这是一个_reference counting_ ** COM **对象的包装,不是可以安全地返回一个没有预初始化的内存? (我知道这是违背“最佳做法”的,我永远不会这样做(如果我做了我的主管会对我喊),但是只有_theoretically speaking_在现实生活中不安全?(我不是我真的很熟悉COM,但是我的_guess_有它自己的引用计数。我再也不知道了:) – mg30rg 2014-10-10 07:59:51

+1

@ mg30rg'WideString'是'BSTR'的封装,而不是'CComBSTR'。 BSTR'不是引用计数,可以在模块边界使用'out WideString'参数很好。 – 2014-10-10 08:05:16

+0

我现在看到了,谢谢。 – mg30rg 2014-10-10 08:06:45