从TFileDialogEvent获取IFileDialog的句柄以获取文件名

时间:2012-06-11 18:50:41

标签: delphi

我正在构建一个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 ...你的帖子很有帮助。

2 个答案:

答案 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;