-1

我正在构建一个 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

谢谢大家...谢谢罗伯...您的帖子很有帮助。

4

2 回答 2

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 ...
于 2012-06-11T19:16:44.863 回答
0

TSaveFileDialog具有Dialogtype 的公共属性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;
于 2012-06-12T01:14:08.540 回答