GetSaveFileName fails with CDERR_FINDRESFAILURE

291 Views Asked by At

I've written a TOpenPictDialog (source code see below) component, which finally fails under certain circumstands when calling

Result := TDialogFunc(DialogFunc)(DialogData);

in Dialogs.pas. As DialogFunc correctly points to GetOpenFileName I call CommDlgExtendedError afterwards for test to find out what's wrong. It returns CDERR_FINDRESFAILURE. In this case the dialog is simply not showing. My test form only contains a button and the TOpenPictDialog component, when pressing the button, OpenPictDialog1->Execute is called - that's all.

The very strange thing is that it does work perfectly (besides of the TListView flickering on resize) under one of the following circumstands:

a) add ExtDlgs in "uses" in calling form b) add an original TOpenPictureDialog to the form without calling it c) adding the PAS file containing TOpenPictDialog to the project (although TOpenPictDialog has been already installed)

If I write a C++ Builder application with the one calling form I never get TOpenPictDialog working (even if I add the additional TOpenPictureDialog component).

unit PictureDlg;

{$R-,H+,X+}

{$IF CompilerVersion > 23} {$DEFINE GE_DXE2} {$IFEND}

interface

{$IFDEF GE_DXE2}
   uses Winapi.Messages, Winapi.Windows, System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls,
     Vcl.Graphics, Vcl.ExtCtrls, Vcl.Buttons, Vcl.Dialogs, Vcl.ExtDlgs, Vcl.Consts, Vcl.ComCtrls;
{$ELSE}
   uses Messages, Windows, SysUtils, Classes, Controls, StdCtrls,
     Graphics, ExtCtrls, Buttons, Dialogs, ExtDlgs, Consts, ComCtrls;
{$ENDIF}

(*$HPPEMIT '// Alias records for C++ code that cannot compile in STRICT mode yet.' *)
(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *)
(*$HPPEMIT '#if !defined(STRICT)' *)
// (*$HPPEMIT '  #pragma alias "@Vcl@Extdlgs@TOpenPictDialog@Execute$qqrpv"="@Vcl@Extdlgs@TOpenPictDialog@Execute$qqrp6HWND__"' *)
(*$HPPEMIT '#endif' *)
(*$HPPEMIT '#endif' *)

type

{ TOpenPictDialog }

  TOpenPictDialog = class(TOpenDialog)
  private
    FListView: TListView;
    FTopLabel, FBottomLabel: TStaticText;
    FImageCtrl: TImage;
    FSavedFilename: string;
    FOldDialogWndProc: Pointer;
    FDialogMethodInstance: Pointer;
    FDialogHandle: THandle;
    function  IsFilterStored: Boolean;
    procedure DialogWndProc(var Msg: TMessage);
  protected
    procedure DoClose; override;
    procedure DoSelectionChange; override;
    procedure DoShow; override;
    function TaskModalDialog(DialogFunc: Pointer; var DialogData): Bool; override;
  published
    property Filter stored IsFilterStored;
  public
    constructor Create(AOwner: TComponent); override;
    function Execute(ParentWnd: HWND): Boolean; override;
    property DialogListView: TListView read FListView;
    property DialogImage: TImage read FImageCtrl;
    property TopLabel: TStaticText read FTopLabel;
    property BottomLabel: TStaticText read FBottomLabel;
  end;

procedure Register;

implementation

uses 
{$IFDEF GE_DXE2}
{$IF DEFINED(CLR)}
  System.Runtime.InteropServices, System.Reflection, System.Security.Permissions, System.IO,
{$IFEND}
  System.Math, Vcl.Forms, Winapi.CommDlg, Winapi.Dlgs, System.Types, Winapi.ShlObj, Winapi.ActiveX;
{$ELSE}
{$IF DEFINED(CLR)}
  InteropServices, Reflection, Permissions, IO,
{$IFEND}
  Math, Forms, CommDlg, Dlgs, Types, ShlObj, ActiveX;
{$ENDIF}

{ TOpenPictDialog }

constructor TOpenPictDialog.Create(AOwner: TComponent);
begin
  FDialogHandle := 0;
  FDialogMethodInstance := NIL;

  inherited Create(AOwner);
  Filter := GraphicFilter(TGraphic);

  FListView := TListView.Create(Self);
  FImageCtrl := TImage.Create(Self);

  with FListView do
  begin
    Name := 'ListView';
    SetBounds(204, 5, 169, 200);
    BevelOuter := bvNone;
    BorderWidth := 6;
    TabOrder := 1;
    Color := clWindow;
    ParentDoubleBuffered := false;
    DoubleBuffered := true;
    OwnerDraw := true;
    Ctl3D := true;

    with FImageCtrl do
    begin
       Picture := nil;
       Name := 'Image';
       Parent := FListView;
    end;
  end;

  FTopLabel := TStaticText.Create(Self);
  with FTopLabel do
  begin
   Name := 'TopLabel';
   SetBounds(6, 6, 157, 23);
   AutoSize := False;
   Caption := 'Preview:';
  end;

  FBottomLabel := TStaticText.Create(Self);
  with FBottomLabel do
  begin
   Name := 'BottomLabel';
   SetBounds(6, 6, 157, 23);
   AutoSize := False;
   Caption := 'Image size: 208 x 149 px';
   Alignment := taCenter;
  end;
end;

procedure TOpenPictDialog.DialogWndProc(var Msg: TMessage);
var
  PreviewRect, ListViewRect, WindowRect, LabelRect: TRect;
  WndControl: HWND;

begin
    Msg.Result := CallWindowProc(FOldDialogWndProc, FDialogHandle, Msg.Msg, Msg.WParam, Msg.LParam);

    if ((Msg.Msg = WM_WINDOWPOSCHANGED) and
            ((TWMWindowPosMsg(Msg).WindowPos.Flags and SWP_NOSIZE) = 0)) or
            (Msg.Msg = WM_SHOWWINDOW) then begin

        PreviewRect := FListView.BoundsRect;

        GetWindowRect(Handle, WindowRect);

        WndControl := FindWindowEx(FDialogHandle, 0, 'SHELLDLL_DefView', nil);
        WndControl := FindWindowEx(WndControl, 0, 'SysListView32', nil);

        if WndControl <> 0 then begin
            GetWindowRect(WndControl, ListViewRect);
            PreviewRect.Top := ListViewRect.Top - WindowRect.Top;
            PreviewRect.Bottom := PreviewRect.Top + ListViewRect.Bottom - ListViewRect.Top;

           if (not EqualRect(PreviewRect, FListView.BoundsRect)) then
              FListView.BoundsRect := PreviewRect;

            LabelRect := PreviewRect;
            Dec(LabelRect.Top, 24);
            LabelRect.Bottom := LabelRect.Top + 16;

            FTopLabel.BoundsRect := LabelRect;

            LabelRect := PreviewRect;
            LabelRect.Top := PreviewRect.Bottom + 9;
            LabelRect.Bottom := LabelRect.Top + 16;

            FBottomLabel.BoundsRect := LabelRect;
        end;
    end;
end;

procedure TOpenPictDialog.DoSelectionChange;
var
  FullName: string;

  function ValidFile(const FileName: string): Boolean;
  begin
    Result := FileGetAttr(FileName) <> -1;
  end;

begin
  FullName := FileName;
  if FullName <> FSavedFilename then
  begin
    FSavedFilename := FullName;
  end;
  inherited DoSelectionChange;
end;

procedure TOpenPictDialog.DoClose;
begin
  if Assigned(FDialogMethodInstance) then begin
    SetWindowLong(FDialogHandle, GWL_WNDPROC, Integer(FOldDialogWndProc));
    FreeObjectInstance(FDialogMethodInstance);
  end;

  FDialogHandle := 0;
  FDialogMethodInstance := NIL;

  inherited DoClose;
  { Hide any hint windows left behind }
  Application.HideHint;
end;

procedure TOpenPictDialog.DoShow;
var
  PreviewRect, StaticRect, OldDialogRect: TRect;
  DialogWidth, DialogHeight, NewLeft, NewTop: integer;
const
  SizeIncrease = 25;
begin
  FDialogHandle := GetParent(Handle);
  GetWindowRect(FDialogHandle, OldDialogRect);
  DialogWidth := OldDialogRect.Right - OldDialogRect.Left + SizeIncrease;
  DialogHeight := OldDialogRect.Bottom - OldDialogRect.Top;
  NewLeft := (Screen.Width - DialogWidth) div 2;
  NewTop := (Screen.Height - DialogHeight) div 2;

  GetWindowRect(Handle, PreviewRect);

  MoveWindow(FDialogHandle, NewLeft, NewTop, DialogWidth, DialogHeight, true);
  MoveWindow(Handle, 0, 0, PreviewRect.Right - PreviewRect.Left + SizeIncrease, PreviewRect.Bottom - PreviewRect.Top, false);

  StaticRect := GetStaticRect;
  GetClientRect(Handle, PreviewRect);
  PreviewRect.Left := StaticRect.Left + (StaticRect.Right - StaticRect.Left);
  Inc(PreviewRect.Top, 4);
  Dec(PreviewRect.Right, 8);
  Dec(PreviewRect.Bottom, 20);
  FListView.BoundsRect := PreviewRect;

  FDialogMethodInstance := MakeObjectInstance(DialogWndProc);
  FOldDialogWndProc := Pointer(SetWindowLong(FDialogHandle, GWL_WNDPROC, Integer(FDialogMethodInstance)));

  FSavedFilename := '';
  FListView.ParentWindow := Handle;
  FTopLabel.ParentWindow := Handle;
  FBottomLabel.ParentWindow := Handle;

  inherited DoShow;
end;

[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.SafeSubWindows)]
function TOpenPictDialog.Execute(ParentWnd: HWND): Boolean;
begin
  if NewStyleControls and not (ofOldStyleDialog in Options) and not
     ((Win32MajorVersion >= 6) and UseLatestCommonDialogs) then
    Template := 'DLGTEMPLATE'
  else
{$IF DEFINED(CLR)}
    Template := '';
{$ELSE}
    Template := nil;
{$IFEND}
  Result := inherited Execute(ParentWnd);
end;

function TOpenPictDialog.TaskModalDialog(DialogFunc: Pointer; var DialogData): Bool;
begin
  // This makes sense ONLY if you are compiling with a run-time packages
  // Thanks to Peter Below (www.delphifaq.com)
  TOpenfilename(Dialogdata).hInstance := FindClassHInstance(Classtype);
  Result := inherited TaskModalDialog(DialogFunc, DialogData);
end;

function TOpenPictDialog.IsFilterStored: Boolean;
begin
  Result := not (Filter = GraphicFilter(TGraphic));
end;

procedure Register;
begin
  RegisterComponents('Dialogs', [TOpenPictDialog]);
end;

end.
1

There are 1 best solutions below

0
On

When you copied the code from ExtDlgs.pas to begin writing yours, you didn't copy enough. In particular, you didn't copy the $R directive that links the associated ExtDlgs.rc file, which contains the dialog resource describing the additional layout of the custom dialog box.

Your code tells the API to use a dialog resource named DLGTEMPLATE, but you haven't included that resource in your program. That explains why the error code you get is about a failure to find a resource. Using the ExtDlgs unit has the side effect of linking that unit's associated resources.

Go copy the dialog template from ExtDlgs.rc into your own RC file and link it as ExtDlgs.pas does. Use a different name for the resource, though, to avoid a name clash with the existing DLGTEMPLATE resource. Adjust your code accordingly.