Recent

Author Topic: How to call Win standard Open & Save/Save-as dialog boxes?  (Read 7800 times)

dculp

  • Full Member
  • ***
  • Posts: 129
How to call Win standard Open & Save/Save-as dialog boxes?
« on: December 03, 2016, 11:10:45 am »
I'm updating a character-based app from Turbo Pascal 7 so that it will run under Win7+. It will remain character-based (no panels, components, etc.).

How can I call the Win standard Open & Save/Save-as dialog boxes? (What function(s) are needed? What parameters do I need to pass? What "uses" files and/or compiler directives are needed?) Some example code (or reference) would be greatly appreciated.

Lazarus 1.6, FPC 3.0.0

Thanks,
Don C.


derek.john.evans

  • Guest
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #1 on: December 03, 2016, 11:20:34 am »
http://stackoverflow.com/questions/454811/opening-an-opendialog-from-vis-c-console-app

LCL Dialogs still work with console apps, although your exe size will jump up.

Also LCL needes to be added to your project requirements.

Code: Pascal  [Select][+][-]
  1. uses   Dialogs, Interfaces;
  2.  
  3.   with TOpenDialog.Create(nil) do begin
  4.     try
  5.       Execute;
  6.     finally
  7.       Free;
  8.     end;
  9.   end;    
  10.  

dculp

  • Full Member
  • ***
  • Posts: 129
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #2 on: December 03, 2016, 12:42:03 pm »
I have the following code which compiles OK. However, I get an error when I run the exe from Windows Explorer (see following). Note: Line 11 (from the error message) is the Execute statement.

Code: Pascal  [Select][+][-]
  1. program Open_save2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.    Dialogs, Interfaces;
  7.  
  8. begin
  9.   with TOpenDialog.Create(nil) do begin
  10.     try
  11.       Execute;
  12.     finally
  13.       Free;
  14.     end;
  15.   end;
  16. end.  
  17.  

---------------- Error ---------------------
[FORMS.PP] ExceptionOccurred
  Sender=EAccessViolation
  Exception=Access violation
  Stack trace:
  $000000010000DA90
  $000000010017EB03 line 978 of win32wsdialogs.pp
  $000000010001989B line 103 of include/commondialog.inc
  $0000000100019D69 line 207 of include/filedialog.inc
  $000000010001A7F2 line 403 of include/filedialog.inc
  $0000000100019542 line 31 of include/commondialog.inc
  $0000000100019BB9 line 53 of include/filedialog.inc
  $0000000100002CD3 line 11 of Open_save2.lpr
  $0000000100002CF6 line 16 of Open_save2.lpr
  $00000001000161D3
  $0000000100016B21
  $00000000772C59CD
  $00000000774FB891
TApplication.HandleException Access violation
  Stack trace:
  $000000010000DA90
  $000000010017EB03
  $000000010001989B
  $0000000100019D69
  $000000010001A7F2
  $0000000100019542
  $0000000100019BB9
  $0000000100002CD3
  $0000000100002CF6
  $00000001000161D3
  $0000000100016B21
  $00000000772C59CD
  $00000000774FB891

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #3 on: December 03, 2016, 02:07:25 pm »
IIRC you cannot use forms until after Application.Initialize is called.
Also: did you add LCL as a dependency to the project?

Bart
« Last Edit: December 03, 2016, 02:09:13 pm by Bart »

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #4 on: December 03, 2016, 03:06:21 pm »
Another possible solution (though not portable, and so not really recommended) is to call directly the Windows API.

For instance, for requesting a present file name (project must include the 'Graphical Application Win 32 (-WG)' flag):
Code: Pascal  [Select][+][-]
  1. program TestDlg;
  2.  
  3. {$IFDEF FPC}
  4.   {$MODE OBJFPC}{$H+}
  5. {$ENDIF}
  6. {$APPTYPE CONSOLE}
  7.  
  8. uses
  9.   Windows {$IFNDEF FPC}, CommDlg{$ENDIF};
  10.  
  11. {$IFDEF FPC}
  12. function  GetOpenFileName(var OpenFile: TOpenFilename): Bool; stdcall; external 'comdlg32.dll' name 'GetOpenFileNameA';     // AnsiVersion
  13. {$ENDIF}
  14.  
  15. function  AskForFile(const WinHandle: THandle; const FileFilters: string; var FileName: string): Boolean; forward;
  16. function  AFF_OFSize(const CurSize: integer): integer; forward;
  17.  
  18. function  AskForFile(const WinHandle: THandle; const FileFilters: string; var FileName: string): Boolean;
  19. var FileToProcess: TOpenFileName;
  20. var CFileName: array [0..Pred(MAX_PATH+10)] of Char;
  21. var CFileNameOnly: array [0..Pred(MAX_PATH+10)] of Char;
  22. begin
  23.   FillChar(FileToProcess, SizeOf(FileToProcess), 0);
  24.   FillChar(CFileName, SizeOf(CFileName), 0);
  25.   FillChar(CFileNameOnly, SizeOf(CFileNameOnly), 0);
  26.   //
  27.   FileToProcess.lStructSize := AFF_OFSize(SizeOf(FileToProcess));
  28.   FileToProcess.hWndOwner := WinHandle;
  29.   FileToProcess.hInstance := HInstance;
  30.   FileToProcess.lpstrFilter := PChar(FileFilters);
  31.   FileToProcess.nFilterIndex := 1;            // First filter is current filter by default
  32.   FileToProcess.lpstrFile := CFileName;
  33.   FileToProcess.nMaxFile := Pred(Length(CFileName));
  34.   FileToProcess.lpstrFileTitle := CFileNameOnly;
  35.   FileToProcess.nMaxFileTitle := Pred(Length(CFileNameOnly));
  36.   FileToProcess.lpstrInitialDir := '.\';      // Current directory by default
  37.   FileToProcess.Flags := OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST or OFN_HIDEREADONLY ;
  38.   //
  39.   FileName := '';
  40.   result := GetOpenFileName(FileToProcess);
  41.   if result then
  42.     FileName := string(CFileName);
  43. end;
  44. // Size of OPENFILENAME Structure Different with Versions of Windows
  45. //   (76 Bytes for Windows 95/98/NT/ME - 88 Bytes for Windows 2000/XP/...)
  46. function  AFF_OFSize(const CurSize: integer): integer;
  47. const WIN_2000      = 5;
  48. const OLDWIN_OFSSIZE  = 76;
  49. var OSVersion: TOSVersionInfo;
  50. begin
  51.   result := CurSize;
  52.   OSVersion.dwOSVersionInfoSize := SizeOf(OSVersion);
  53.   if GetVersionEx(OSVersion) then
  54.     if (OSVersion.dwPlatformId<VER_PLATFORM_WIN32_NT) or (OSVersion.dwMajorVersion<WIN_2000) then
  55.       result := OLDWIN_OFSSIZE;
  56. end;
  57.  
  58. var FileName: string;
  59. const FILE_FILTERS: string = 'Text Files (*.txt)' + #00 + '*.txt'+ #00 + 'All Files (*.*)' + #00 + '*.*' + #00 + #00;
  60.  
  61. begin
  62.   if  not AskForFile(0, FILE_FILTERS, FileName) then
  63.     WriteLn('No file selected.')
  64.   else
  65.     WriteLn('File selected: ' + FileName);
  66. end.
  67.  
« Last Edit: December 03, 2016, 03:35:36 pm by ChrisF »

dculp

  • Full Member
  • ***
  • Posts: 129
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #5 on: December 03, 2016, 09:53:19 pm »
IIRC you cannot use forms until after Application.Initialize is called.
Also: did you add LCL as a dependency to the project?

Bart

The following seems to work OK but questions -
1.  "if dlg.Execute({Handle}) then" --> won't compile if I uncomment Handle (per original code) - gives "identifier not found".
What is the purpose of Handle? Is it needed here? If so, where do I find it or how do I declare it?
2. Application.Initialize; --> this allowed compilation.
Do I need a counterbalancing statement (like Application.Free; or something else) at the end?
3. This code will be converted to a unit. This unit will be a small part of a much larger project. Should Application.Initialize be part of the unit or should it be in the main project file. (Until now Application.Initialize has not been needed to convert the old Turbo 7 code.)

Code: Pascal  [Select][+][-]
  1. program Open_save3;
  2. // Adapted from http://stackoverflow.com/questions/33756036/how-can-i-select-a-file-in-delphi
  3.  
  4. {$mode objfpc}{$H+}
  5.  
  6. uses
  7.    Dialogs, Interfaces,
  8.    CRT, // for readkey
  9.    Forms; // for Application.Initialize
  10.  
  11. var
  12.   selectedFile: string;
  13.   dlg: TOpenDialog;
  14.  
  15. begin
  16.   Application.Initialize;
  17.  
  18.   selectedFile := '';
  19.   dlg := TOpenDialog.Create(nil);
  20.   try
  21.     dlg.InitialDir := '';
  22.     dlg.Filter := 'All files (*.*)|*.*';
  23.     if dlg.Execute({Handle}) then
  24.       selectedFile := dlg.FileName;
  25.   finally
  26.     dlg.Free;
  27.   end;
  28.  
  29.   if selectedFile <> '' then
  30.     //    <your code here to handle the selected file>
  31.     begin
  32.     writeln('File = ', selectedFile);
  33.     readkey;
  34.     end;
  35.  
  36.   Application.Free; // needed?
  37. end.
  38.  

derek.john.evans

  • Guest
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #6 on: December 03, 2016, 11:01:28 pm »
1.  "if dlg.Execute({Handle}) then" --> won't compile if I uncomment Handle (per original code) - gives "identifier not found".

Execute doesn't take a parameter (?) Unsure why you have handle there.

2. Application.Initialize; --> this allowed compilation.

Yes, I had a look at line 978 of win32wsdialogs.pp. There is some OS checking for Vista which most likely requires Application.Initialize. Im still running XP, so I didn't see the issue. mmmm. Lazarus bug or not?

Code: Pascal  [Select][+][-]
  1. function CanUseVistaDialogs(const AOpenDialog: TOpenDialog): Boolean;
  2. begin
  3.   Result := (WindowsVersion >= wvVista) and not (ofOldStyleDialog in AOpenDialog.Options);
  4. end;    
  5.  

3. This code will be converted to a unit. This unit will be a small part of a much larger project. Should Application.Initialize be part of the unit or should it be in the main project file.

I'd say part of a main entry point (lpr) which handles these issues. From memory, "uses Interfaces" must also be in the lpr file.


Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #7 on: December 03, 2016, 11:24:15 pm »
Lazarus bug or not?

Known issue, won't be fixed.
There was a bugreport about it in the past.
Too lazy to search it myself.

Bart

dculp

  • Full Member
  • ***
  • Posts: 129
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #8 on: December 03, 2016, 11:37:07 pm »
1.  "if dlg.Execute({Handle}) then" --> won't compile if I uncomment Handle (per original code) - gives "identifier not found".

Execute doesn't take a parameter (?) Unsure why you have handle there.

This was from http://stackoverflow.com/questions/33756036/how-can-i-select-a-file-in-delphi
 
3. This code will be converted to a unit. This unit will be a small part of a much larger project. Should Application.Initialize be part of the unit or should it be in the main project file.

I'd say part of a main entry point (lpr) which handles these issues. From memory, "uses Interfaces" must also be in the lpr file.

Application.Initialize; --> Do I need a counterbalancing statement (like Application.Free; or something else) at the end?

derek.john.evans

  • Guest
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #9 on: December 03, 2016, 11:44:01 pm »
Lazarus doesn't, so, I guess not. Create a new Windows project, and look at the LPR file.
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Interfaces, // this includes the LCL widgetset
  10.   Forms, unit1
  11.   { you can add units after this };
  12.  
  13. {$R *.res}
  14.  
  15. begin
  16.   RequireDerivedFormResource:=True;
  17.   Application.Initialize;
  18.   Application.CreateForm(TForm1, Form1);
  19.   Application.Run;
  20. end.  
  21.  

derek.john.evans

  • Guest
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #10 on: December 03, 2016, 11:47:37 pm »
If you look at the end of the unit Forms you will see what is happending.
Code: Pascal  [Select][+][-]
  1. initialization
  2.   RegisterPropertyToSkip(TForm, 'OldCreateOrder', 'VCL compatibility property', '');
  3.   RegisterPropertyToSkip(TForm, 'TextHeight', 'VCL compatibility property', '');
  4.   RegisterPropertyToSkip(TForm, 'Scaled', 'VCL compatibility property', '');
  5.   RegisterPropertyToSkip(TForm, 'TransparentColorValue', 'VCL compatibility property', '');
  6.   LCLProc.OwnerFormDesignerModifiedProc:=@IfOwnerIsFormThenDesignerModified;
  7.   ThemesImageDrawEvent:=@ImageDrawEvent;
  8.   IsFormDesign := @IsFormDesignFunction;
  9.   Screen:=TScreen.Create(nil);
  10.   Application:=TApplication.Create(nil);
  11.  
  12. finalization
  13.   //DebugLn('forms.pp - finalization section');
  14.   LCLProc.OwnerFormDesignerModifiedProc:=nil;
  15.   HintWindowClass:=nil;
  16.   FreeThenNil(Application);
  17.   FreeThenNil(Screen);      
  18.  

derek.john.evans

  • Guest
Re: How to call Win standard Open & Save/Save-as dialog boxes?
« Reply #11 on: December 03, 2016, 11:58:05 pm »
This was from http://stackoverflow.com/questions/33756036/how-can-i-select-a-file-in-delphi

O, right. Yes, Delphi has an overloaded method which takes a ParentWnd handle. The doc's state:

The ParentWnd parameter is a window handle of type HWND MSDN. If none is provided, the current form handle is used,

Either way, it isn't in Delphi 7 (as far as I can see) and you dont have a parent handle.

 

TinyPortal © 2005-2018