在Windows已知文件夹API中,ICownFolderManager::GetFolderIds定义为:
HRESULT GetFolderIds([out] KNOWNFOLDERID **ppKFId, [in, out] UINT *pCount);
调用成功后,ppKFId指向KNOWNFOLDERID(GUID)数组,调用者最终必须使用CoTaskMemFree释放这些GUID--到目前为止,这是标准的。
Delphi 版本在ShlObj.pas(Delphi Alexandria 11.2)中定义为:
function GetFolderIds(ppKFId: array of TKnownFolderID; var pCount: UINT): HRESULT; stdcall;
其中TKnownFolderId类型为TGUID。
我的问题是,如何在 Delphi 中定义一个变量,使其既满足“TKnownFolderID数组”参数,又能用CoTaskMemFree(需要一个指针)释放数组?
我尝试过:
var FolderList: array of TKnownFolderID;
得到一个保护异常,因为 Delphi 传递的是FolderList(nil)的内容,而不是FolderList的地址(这是我所期望的,因为Delphi下的动态数组是一个指针)。
type FolderListType = array of KNOWNFOLDERID;
var FolderList: FolderListType;
会产生与您预期完全相同的结果。
var FolderList: array[0..0] of KNOWNFOLDERID;
在CoTaskMemFree上出现编译器错误,因为数组[0..0]不是指针。将FolderList类型转换为指针失败,并显示“invalid typecast”。
type
FolderListType = array of KNOWNFOLDERID;
PFolderListType = ^FolderListType;
var
FolderList: PFolderListType;
在具有不相容型别的GetFolderIDs上发生编译器错误。
type FolderListType = array of KNOWNFOLDERID;
var FolderList: FolderListType;
begin
SetLength(FolderList, 1000);
...GetFolderIds(FolderList, ...
失败并显示保护异常,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,...
失败,并出现保护异常。
var
FolderListPointer: PKNOWNFOLDERID;
FolderList: array of KNOWNFOLDERID;
begin
pointer(FolderList) := @FolderListPointer;
...GetFolderIds(FolderList, ...);
...
CoTaskMemFree(FolderListPointer);
pointer(FolderList) := nil;
成功地检索到了已知文件夹列表,但是第二个参数(已知文件夹的计数)没有改变。但是,我真的不喜欢将动态数组类型转换为这样的指针。
正在将提供的ICownFolderManager.GetFolderIDs定义重写为
function GetFolderIds(var ppKFId: PKnownFolderID; var pCount: UINT): HRESULT; stdcall;
和使用
var FolderList: PKnownFolderID;
begin
...GetFolderIds(FolderList, ...);
运行良好。编译干净,完美地检索列表和计数,CoTaskMemFree很高兴。
那么,我是因为缺乏知识而遗漏了一些关于定义在这种情况下可以工作的 Delphi 变量的东西,还是只是Windows头文件的一个糟糕的翻译?我经常在Delphi中使用接口,并且经常发现我认为是糟糕的翻译(例如可选的out参数被定义为“var”而不是指针-这意味着不能传递“nil,”所以在 Delphi 下它不再是可选的),但是我看不出有什么方法可以让这个定义的代码工作,所以这让我怀疑我是否像我认为的那样了解Delphi。
- 谢谢-谢谢
1条答案
按热度按时间j1dl9f461#
ShlObj
单元中GetFolderIds()
的声明是错误的(自D2010首次引入以来一直是错误的)。它与API的工作方式不兼容。API分配一个新的数组,并将指向该数组的指针返回给调用方。
array of ...
(也称为Open Array)参数不能接收该指针。正确的声明应该是这样的:
然后,正确的用法如下所示:
我已经向Embarcadero报告了此错误:
RSP-40106: Declaration of IKnownFolderManager.GetFolderIds() in ShlObj.pas is wrong
在Embarcadero在
ShlObj
中修复此问题之前,您必须将IKnownFolderManager
接口的声明复制到您自己的代码中并修复其中的错误,然后根据需要使用复制的接口。