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。
谢谢。
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到您自己的代码中并修复其中的错误,然后根据需要使用复制的接口。