我正在构建一个TFileSaveDialog后代组件。后代有一个PushButton,其事件由:
处理function TFileDialogEvent.OnButtonClicked(const pfdc: IFileDialogCustomize;
dwIDCtl: DWORD): HResult; stdcall;
var
iImageEnIO: TImageEnIO;
iFilename: string;
iName: PChar;
pfd: IFileDialog;
begin
if dwIDCtl = dwVisualGroup8ID then
begin
iImageEnIO := TImageEnIO.Create(nil);
try
FileDialog.QueryInterface(
StringToGUID('{8016B7B3-3D49-4504-A0AA-2A37494E606F}'),
pfd);
// How to get correct valid handle to IFileDialog?
pfd.GetFileName(iName);
iFilename := string(iName);
if FileExists(iFilename) then
begin
该组件还可以正确显示各种控制标签中的图像信息。该组件成功返回所选文件名并允许更改文件夹,但在OnButtonClicked事件中从pfd.GetFileName(iName)获取文件名返回的文件名无效。我认为问题是由于没有获得pfd:IFileDialog的正确句柄引起的。
更新 我通过定义来解决这个问题 FileDialog:IFileDialog作为组件级别的var然后我调用了
function TFileDialogEvent.OnButtonClicked(const pfdc: IFileDialogCustomize;
dwIDCtl: DWORD): HResult; stdcall;
var
iImageEnIO: TImageEnIO;
iFilename: string;
pFolder: PWideChar;
iFolder: string;
iName: PChar;
pfd: IFileDialog;
hr: HRESULT;
aShellItem: IShellItem;
begin
if dwIDCtl = dwVisualGroup8ID then
begin
iImageEnIO := TImageEnIO.Create(nil);
try
FileDialog.QueryInterface(IFileDialog, pfd);
pfd.GetFileName(iName);
// Get the ShellItem
hr := SHCreateItemFromParsingName(iName, nil,
StringToGUID(SID_IShellItem), aShellItem);
// Get the folder
pfd.GetFolder(aShellItem);
// Get the folder displayname
aShellItem.GetDisplayName(SIGDN_FILESYSPATH, pFolder);
iFolder := string(pFolder);
if DirectoryExists( iFolder) then
iFilename := IncludeTrailingPathDelimiter( iFolder) + string(iName);
if FileExists(iFilename) then
begin
谢谢大家...谢谢你Rob ...你的帖子很有帮助。
答案 0 :(得分:4)
您正在查询对象以寻找与GUID {8016B7B3-3D49-4504-A0AA-2A37494E606F}匹配的接口,并将结果存储在IFileDialog
引用中。问题是{8016B7B3-3D49-4504-A0AA-2A37494E606F}是IFileDialogCustomize
接口的GUID,而不是IFileDialog
。您尝试调用GetFileName
,这是IFileDialog
接口的第六种方法,但由于该变量实际上包含IFileDialogCustomize
接口,因此控制最终被转移到那个界面。编译器无法为您捕获类型不匹配,部分原因是您在运行时动态构造GUID(因此它在编译时不知道值),部分原因是QueryInterface
的第二个参数是无类型的(因此无法知道变量的类型应与第一个参数匹配)。
比在运行时计算GUID更简单。接口类型自动可用作其关联的GUID(当它们具有GUID时)。要请求IFileDialog
接口,只需将该标识符直接传递给QueryInterface
:
FileDialog.QueryInterface(IFileDialog, pfd);
如果您使用QueryInterface
运营商,则甚至无需致电as
:
pfd := FileDialog as IFileDialog;
直接致电QueryInterface
时,您需要确保检查错误代码的结果。如果使用as
运算符,则不受支持的接口将引发异常。如果您只是想要传递失败而没有太多错误检查,请改用Supports
函数:
if Supports(FileDialog, IFileDialog, pfd) then ...
答案 1 :(得分:0)
TSaveFileDialog
具有Dialog
类型的公共IFileDialog
属性,因此您无需手动搜索它,您已经可以直接访问它,例如:
function TFileDialogEvent.OnButtonClicked(const pfdc: IFileDialogCustomize; dwIDCtl: DWORD): HResult; stdcall;
var
iImageEnIO: TImageEnIO;
iFilename: string;
iName: PWideChar;
begin
if dwIDCtl <> dwVisualGroup8ID then
Result := E_NOTIMPL
else
begin
Result := S_OK;
if FAILED(Self.Dialog.GetFileName(iName)) then Exit;
try
iFilename := string(iName);
finally
CoTaskMemFree(iName);
end;
iImageEnIO := TImageEnIO.Create(nil);
try
if FileExists(iFilename) then
begin
...
end;
finally
iImageEnIO.Free;
end;
end;
end;