在 Delphi 中定义数组,用于 Windows API 和 COM+ 接口

Ray*_*nis 3 arrays delphi com+ interface

在 Windows 已知文件夹 API 中,IKnownFolderManager::GetFolderIds 定义为:

HRESULT GetFolderIds([out] KNOWNFOLDERID **ppKFId, [in, out] UINT *pCount);
Run Code Online (Sandbox Code Playgroud)

成功调用后,ppKFId 指向 KNOWNFOLDERID (GUID) 数组,调用者最终必须使用 CoTaskMemFree 释放该数组 - 到目前为止,都是标准的。

ShlObj.pas(Delphi Alexandria 11.2)中的Delphi版本定义为:

function GetFolderIds(ppKFId: array of TKnownFolderID; var pCount: UINT): HRESULT; stdcall;
Run Code Online (Sandbox Code Playgroud)

其中 TKnownFolderId 类型为 TGUID。

我的问题是如何在 Delphi 中定义一个既满足“TKnownFolderID 数组”参数又满足 CoTaskMemFree 数组(需要一个指针)的变量?

我尝试过的:

var FolderList: array of TKnownFolderID;
Run Code Online (Sandbox Code Playgroud)

得到一个保护异常,因为Delphi传递FolderList的内容(nil)而不是FolderList的地址(这是我所期望的,因为Delphi下的动态数组是一个指针)。

type FolderListType = array of KNOWNFOLDERID;
var FolderList: FolderListType;
Run Code Online (Sandbox Code Playgroud)

产生与您期望的完全相同的结果。

var FolderList: array[0..0] of KNOWNFOLDERID;
Run Code Online (Sandbox Code Playgroud)

在 CoTaskMemFree 上给出编译器错误,因为 array[0..0] 不是指针。将FolderList 类型转换为指针失败,并显示“无效类型转换”。

type
FolderListType = array of KNOWNFOLDERID;
PFolderListType = ^FolderListType;
var
FolderList: PFolderListType;
Run Code Online (Sandbox Code Playgroud)

在具有不兼容类型的 GetFolderID 上给出编译器错误。

type FolderListType = array of KNOWNFOLDERID;
var FolderList: FolderListType;
begin
  SetLength(FolderList, 1000);
...GetFolderIds(FolderList, ...
Run Code Online (Sandbox Code Playgroud)

因保护异常而失败,GetFolderIds(FolderList[0],... 也是如此

type
  FolderListType = array[0..0] of KNOWNFOLDERID;
  FolderListRec = record
  case boolean of
    true: (FL: FolderListType);
    false: (FLP: pointer);
  end;
var
  FolderListPointer: PKNOWNFOLDERID;
  FolderList: array of FolderListRec;
begin
  FolderList.FLP := @FolderListPointer;
  GetFolderIds(FolderList.FL,...
Run Code Online (Sandbox Code Playgroud)

因保护异常而失败。

var
  FolderListPointer: PKNOWNFOLDERID;
  FolderList: array of KNOWNFOLDERID;
begin
  pointer(FolderList) := @FolderListPointer;
  ...GetFolderIds(FolderList, ...);
  ...
  CoTaskMemFree(FolderListPointer);
  pointer(FolderList) := nil;
Run Code Online (Sandbox Code Playgroud)

成功检索已知文件夹列表,但奇怪的是第二个参数(已知文件夹的计数)未受影响。但是,我真的不喜欢像这样将动态数组类型转换为指针。

将 IKnownFolderManager.GetFolderIDs 的提供定义重写为

function GetFolderIds(var ppKFId: PKnownFolderID; var pCount: UINT): HRESULT; stdcall;
Run Code Online (Sandbox Code Playgroud)

并使用

var FolderList: PKnownFolderID;
begin
  ...GetFolderIds(FolderList, ...);
Run Code Online (Sandbox Code Playgroud)

工作正常。编译干净,完美检索列表和计数,CoTaskMemFree 很高兴。

那么,我是否由于缺乏知识而错过了一些有关定义在这种情况下起作用的 Delphi 变量的信息,或者这只是 Windows 标头的错误翻译?我经常使用 Delphi 中的接口,并且经常发现我认为翻译不佳的内容(例如可选的输出参数被定义为“var”而不是指针 - 这意味着你不能传递“nil”,所以在Delphi 它不再是可选的),但我看不到一种方法可以使这个定义完全工作,所以这让我想知道我是否不像我想象的那样了解 Delphi。

谢谢。

Rem*_*eau 5

GetFolderIds()单位中的声明ShlObj是错误的(并且自 D2010 首次引入以来一直是错误的)。它与 API 的工作方式不兼容。

API 分配一个新数组并向调用者返回指向该数组的指针。一个array of ...(又名“开放数组”)参数无法接收该指针。

正确的声明应该是这样的:

function GetFolderIds(var ppKFId: PKnownFolderID; var pCount: UINT): HRESULT; stdcall;
Run Code Online (Sandbox Code Playgroud)

然后,正确的用法如下:

var
  FolderList: PKnownFolderID;
  Count: UINT;
begin
  ...
  OleCheck(Mgr.GetFolderIds(FolderList, Count));
  ...
  CoTaskMemFree(FolderList);
end;
Run Code Online (Sandbox Code Playgroud)

我已向 Embarcadero 报告了此错误:

RSP-40106:ShlObj.pas 中 IKnownFolderManager.GetFolderIds() 的声明是错误的

在 Embarcadero 修复此问题之前ShlObj,您必须将接口声明复制IKnownFolderManager到您自己的代码中并修复其中的错误,然后根据需要使用复制的接口。