Delphi 7 and Vista/Windows 7 common dialogs - events do not work

JeffR picture JeffR · Dec 12, 2009 · Viewed 7.8k times · Source

I'm trying to modify the Delphi 7 Dialogs.pas to access the newer Windows 7 Open/Save dialog boxes (see Creating Windows Vista Ready Applications with Delphi). I can display the dialogs using the suggested modifications; however, events such as OnFolderChange and OnCanClose no longer function.

This appears to be related to changing the Flags:= OFN_ENABLEHOOK to Flags:=0. When Flags is set to 0 the TOpenDialog.Wndproc is bypassed and the appropriate CDN_xxxxxxx messages are not trapped.

Can anyone suggest further code modifications to the D7 Dialogs.pas that will both display the newer common dialogs and maintain the event features of the original controls?

Thanks...

Answer

mghie picture mghie · Dec 12, 2009

You should use the IFileDialog Interface and call its Advise() method with an implementation of the IFileDialogEvents Interface. The Delphi 7 Windows header units won't contain the necessary declarations, so they must be copied (and translated) from the SDK header files (or maybe there's already another header translation available?), but apart from that additional effort there shouldn't be any problem to call this from Delphi 7 (or even earlier Delphi versions).

Edit:

OK, since you didn't react in any way to the answers I'll add some more information. A C sample on how to use the interfaces can be had here. It's easy to translate it to Delphi code, provided you have the necessary import units.

I threw together a small sample in Delphi 4. For simplicity I created a TOpenDialog descendant (you would probably modify the original class) and implemented the IFileDialogEvents directly on it:

type
  TVistaOpenDialog = class(TOpenDialog, IFileDialogEvents)
  private
    // IFileDialogEvents implementation
    function OnFileOk(const pfd: IFileDialog): HResult; stdcall;
    function OnFolderChanging(const pfd: IFileDialog;
      const psiFolder: IShellItem): HResult; stdcall;
    function OnFolderChange(const pfd: IFileDialog): HResult; stdcall;
    function OnSelectionChange(const pfd: IFileDialog): HResult; stdcall;
    function OnShareViolation(const pfd: IFileDialog;
      const psi: IShellItem; out pResponse: DWORD): HResult; stdcall;
    function OnTypeChange(const pfd: IFileDialog): HResult; stdcall;
    function OnOverwrite(const pfd: IFileDialog; const psi: IShellItem;
      out pResponse: DWORD): HResult; stdcall;
  public
    function Execute: Boolean; override;
  end;

function TVistaOpenDialog.Execute: Boolean;
var
  guid: TGUID;
  Ifd: IFileDialog;
  hr: HRESULT;
  Cookie: Cardinal;
  Isi: IShellItem;
  pWc: PWideChar;
  s: WideString;
begin
  CLSIDFromString(SID_IFileDialog, guid);
  hr := CoCreateInstance(CLSID_FileOpenDialog, nil, CLSCTX_INPROC_SERVER,
    guid, Ifd);
  if Succeeded(hr) then begin
    Ifd.Advise(Self, Cookie);
    // call DisableTaskWindows() etc.
    // see implementation of Application.MessageBox()
    try
      hr := Ifd.Show(Application.Handle);
    finally
      // call EnableTaskWindows() etc.
      // see implementation of Application.MessageBox()
    end;
    Ifd.Unadvise(Cookie);
    if Succeeded(hr) then begin
      hr := Ifd.GetResult(Isi);
      if Succeeded(hr) then begin
        Assert(Isi <> nil);
        // TODO: just for testing, needs to be implemented properly
        if Succeeded(Isi.GetDisplayName(SIGDN_NORMALDISPLAY, pWc))
          and (pWc <> nil)
        then begin
          s := pWc;
          FileName := s;
        end;
      end;
    end;
    Result := Succeeded(hr);
    exit;
  end;
  Result := inherited Execute;
end;

function TVistaOpenDialog.OnFileOk(const pfd: IFileDialog): HResult;
var
  pszName: PWideChar;
  s: WideString;
begin
  if Succeeded(pfd.GetFileName(pszName)) and (pszName <> nil) then begin
    s := pszName;
    if AnsiCompareText(ExtractFileExt(s), '.txt') = 0 then begin
      Result := S_OK;
      exit;
    end;
  end;
  Result := S_FALSE;
end;

function TVistaOpenDialog.OnFolderChange(const pfd: IFileDialog): HResult;
begin
  Result := S_OK;
end;

function TVistaOpenDialog.OnFolderChanging(const pfd: IFileDialog;
  const psiFolder: IShellItem): HResult;
begin
  Result := S_OK;
end;

function TVistaOpenDialog.OnOverwrite(const pfd: IFileDialog;
  const psi: IShellItem; out pResponse: DWORD): HResult;
begin
  Result := S_OK;
end;

function TVistaOpenDialog.OnSelectionChange(
  const pfd: IFileDialog): HResult;
begin
  Result := S_OK;
end;

function TVistaOpenDialog.OnShareViolation(const pfd: IFileDialog;
  const psi: IShellItem; out pResponse: DWORD): HResult;
begin
  Result := S_OK;
end;

function TVistaOpenDialog.OnTypeChange(const pfd: IFileDialog): HResult;
begin
  Result := S_OK;
end;

If you run this on Windows 7 it will show the new dialog and accept only files with the txt extension. This is hard-coded and needs to be implemented by going through the OnClose event of the dialog. There's lots more to be done, but the provided code should suffice as a starting point.