Delphi 7 opendialog has garbage in filename when used in Windows 10 - delphi

This is my first post here so please forgive me if I am not doing it right.
I am using Delphi 7 on my Windows 10 machine. When I use the TOpenDialog I get garbage in the filename property on close. This is what I get back þƒ‡uÔÁ™ßðæRw. I created a simple form with a button and a edit box to show the problem here. Could someone please assist me.
The code is below.
procedure TForm1.Button1Click(Sender: TObject);
begin
opendialog1.Execute();
end;
procedure TForm1.OpenDialog1Close(Sender: TObject);
begin
edit1.Text := opendialog1.FileName;
end;

Don't use the OnClose event of the dialog. That is invoked after the underlying dialog object, which owns the file name data, has been destroyed.
Instead respond to the dialog when Execute returns.
procedure TForm1.Button1Click(Sender: TObject);
begin
if opendialog1.Execute() then
edit1.Text := opendialog1.FileName;
end;
Note that you must also test the return value of Execute to handle the user cancelling the dialog.

Related

Cannot access a VCL component in a procedure called from FormCreate on a modal form (i.e. directly after opening the form)

Running into a runtime error when I open a modal form (Form2) that, on create, calls another procedure that does somehting with a VCL component. The following program demonstrates the issue.
This is the code on the modal form:
procedure DoLabel;
begin
form2.Label1.Caption := 'A';
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
DoLabel;
end;
This compiles well. However, the program crashes on opening the modal form. The debugger says: access violation at address xxxx. It is probably a basic error, but what am I doing wrong? And how to solve this?
You are using the global Form2 variable, which has not been assigned yet (or at all) while the TForm2 object is still being constructed.
You need to change your code to not rely on that variable during construction (and preferably remove it entirely, unless TForm2 is auto-created at startup, which it does not sound like it is).
For instance, pass the Form's Self pointer, or even its TLabel pointer, as an input parameter to DoLabel(), eg:
procedure DoLabel(ALabel: TLabel);
begin
ALabel.Caption := 'A';
end;
procedure DoFormStuff(AForm: TForm2);
begin
// use AForm as needed...
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
DoLabel(Label1);
DoFormStuff(Self);
end;
Though, in this case, it would make more sense to have DoFormStuff(), and possible DoLabel() too, be a member of the TForm2 class instead of free functions:
procedure TForm2.FormCreate(Sender: TObject);
begin
DoLabel;
DoFormStuff;
end;
procedure TForm2.DoLabel;
begin
Label1.Caption := 'A';
end;
procedure TForm2.DoFormStuff;
begin
// use Self as needed...
end;

Acrobat Reader ActiveX Access Violation on form close

My Delphi application has a form that uses the Acrobat Reader ActiveX control for viewing pdfs. When I use the control's functions (LoadFile, gotoNextPage, gotoPreviousPage, gotoFirstPage, gotoLastPage), then close the form, I get the following error: "Access violation at address 6AF5703C. Read of address 6AF5703C". When I run the app, but do not use the control's functions, and then close the form, the app will exit without error.
Anyone know of a fix or workaround for this issue?
My app is written using Delphi 5 (legacy app). I have Adobe Acrobat Reader DC v15.016.20045 installed.
As I said in a comment to Zam, with the current version downloaded today of Acrobat Reader DC , I get the exact same error as you.
Please try this code and let us know whether it avoids the error for you, because it certainly works for me and there is no AV, either in the FormClose or afterwards.
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
Ref : Integer;
begin
Ref := AcroPdf1.ControlInterface._AddRef;
AcroPdf1.Src := '';
AcroPdf1.Free;
AcroPdf1 := Nil;
end;
This is my FormCreate, which contains my only other code.
procedure TForm1.FormCreate(Sender: TObject);
begin
AFileName := 'd:\aaad7\pdf\printed.pdf';
AcroPdf1.src := AFileName;
AcroPdf1.setZoom(200); // <- this line is to exercise the
// ControlInterface to provoke the AV on shutdown
end;
I have absolutely no idea why my FormClose avoids the AV problem, and before anybody else says so, yes, it looks mad to me, too! Hardly something that deserves the name "solution", but maybe it will suggest a proper solution to someone who knows more about COM and Ole controls than I do.
I originally included the Ref := AcroPdf1._AddRef just as an experiment. I noticed that after it, Ref's value was 9. After AcroPdf1.Src := '', calling AcroPdf1._Release in the debugger evaluator returned a value of 4. I was about to see if the AV was avoided by forcing the RefCount down by repeatedly calling _Release but then Presto!, there was no AV after my first trace into FormClose exited.
Update: I have not tested the following exhaustively, but this simplified FormClose also avoids the AV, on my system at any rate:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
Ref : Integer;
begin
Ref := AcroPdf1.ControlInterface._AddRef;
end;
Obviously, omitting the assignment to Ref shouldn't make any difference.
I'm using Delphi 10 Seattle on 64-bit Win10, btw.
The better solution is to edit the TPDF Object in "AcroPDFLib_Tlb.pas"
Just add the proper destructor to the Code to free the OLE Object:
Declaration
Type
TAcroPDF = class(TOleControl)
...
public
destructor Destroy; override; // <- New Line
...
end;
Implementation
destructor TAcroPDF.Destroy;
begin
FIntf := NIL;
inherited;
end;

Obtain data from available form to autocreated one

I would like to transfer data from the statusbar of Form2 (which is on the list of available forms and used for login purposes), to the statusbar on the MainForm (which is autocreated one).
If I use :
procedure TMainForm.FormShow(Sender: TObject);
begin
AdvOfficeStatusBar1.Panels[0].Text := Form2.AdvOfficeStatusBar1.Panels[0].Text;
end;
I get an access violation error. . Why is this happening? How can I get over this?
Based on your comment I would say that the following is occurring:
The code you would be using for your login form is this:
procedure Login;
begin
with TForm2.Create(nil) do
try
Application.MainForm.Hide;
if ShowModal = mrOK then
Application.MainForm.Show
else
Application.Terminate;
finally
Free;
end;
end;
What is happening here is that there is an implicit variable for TLoginForm. This is not the same as the auto created variable which would be Form2: TForm2; which sits in the TForm2 unit. This variable is freed directly after the form closes.
To see what I mean. If you delete the variable called Form2 from your application, the only part of the code that won't compile is your line in your original post.
What you would have to do if you wanted to do this sort of thing is something like this (I have changed the name of TLoginForm to your Form2).
procedure Login;
begin
Form2 := TForm2.Create(nil);
Application.MainForm.Hide;
if Form2.ShowModal = mrOK then
Application.MainForm.Show
else
begin
Form2.Free;
Application.Terminate;
end;
end;
The you would have to free your Form2 when you close your main form. I would really not recommend doing this sort of thing though. As a quick fix, you would be far better off saving the text AdvOfficeStatusBar1.Panels[0].Text in a global variable in the OnClose event of the Form2 with something like this:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
g_MySavedVariable := AdvOfficeStatusBar1.Panels[0].Text;
end;
And then loading it in the main form with:
procedure TMainForm.FormShow(Sender: TObject);
begin
AdvOfficeStatusBar1.Panels[0].Text := g_MySavedVariable;
end;
Even this is not ideal but should get you up and running.
If you are looking for some code for a login form where you pass data between the login form and the main form then you are probably better off looking at this question in StackOverflow (delphi Login Form) along with the answers by David and Cosmin. My personal preference is for the solution by Cosmin as you don't need to mess with the DPR and there is no global variables involved.

How can I see if text is selected in the browser and then retrieve it?

I want to find out if the TChromiumFMX browser component contains text that the user has selected, and if so, retrieve that text, without the user having to copy it to the clipboard (ctrl-c) first.
To improve on TLama's answer:
If you're not using ShowMessage, the anonymous procedure will not always have completed before Button1Click is exited, hence often yielding no results (or too late). Therefore a Done := true as the last line of the procedure can be checked for to see if the value has been retrieved:
procedure TForm1.Button1Click(Sender: TObject);
var Done: boolean;
begin
Done := false;
Chromium1.Browser.GetFocusedFrame.VisitDomProc(
procedure(const document: ICefDomDocument)
begin
SelectedText := document.SelectionAsText;
Done := true
end
);
while not Done do Application.ProcessMessages
end;
You will have to visit DOM, and as a gift you'll receive a reference to the current ICefDomDocument document interface. The ICefDomDocument interface then offers the SelectionAsText method, which returns the current selection as text (if any). In code you may write something like:
procedure TForm1.Button1Click(Sender: TObject);
begin
Chromium1.Browser.GetFocusedFrame.VisitDomProc(
procedure(const document: ICefDomDocument)
begin
ShowMessage(document.SelectionAsText);
end
);
end;

How do do things during Delphi form startup

I have a form one which I want to show a file open dialog box before the full form opens.
I already found that I can't do UI related stuff in FormShow, but it seems that I can in FormActivate (which I protect from being called a second time...)
However, if the user cancels out of the file open dialog, I want to close the form without proceeding.
But, a form close in the activate event handler generates an error that I can't change the visibility of the form.
So how does one do some UI related operation during form start up and then perhaps abort the form (or am I trying to stuff a function into the form that should be in another form?)
TIA
It would be best (i think) to show the file open dialog BEFORE you create and show the form. If you want to keep all code together you might add a public class procedure OpenForm() or something:
class procedure TForm1.OpenForm( ... );
var
O: TOpenDialog;
F: TForm1;
begin
O := TOpenDialog.Create();
try
// set O properties.
if not O.Execute then Exit
F := TForm1.Create( nil );
try
F.Filename := O.FIlename;
F.ShowModal();
finally
F.Free();
end;
finally
O.Free();
end;
end;
Set a variable as a condition of the opendialog and close the form on the formshow event if the flag is not set correctly.
procedure TForm1.FormCreate(Sender: TObject);
begin
ToClose := not OpenDialog1.Execute;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if ToClose then Close();
end;
or even more simply
procedure TForm1.FormShow(Sender: TObject);
begin
if not OpenDialog1.Execute then Close();
end;
If you want to keep the logic conditioning the opening self-contained in the Form, you can put a TOpenDialog in your Form and use a code like this in your OnShow event:
procedure TForm2.FormShow(Sender: TObject);
begin
if OpenDialog1.Execute(Handle) then
Color := clBlue
else
PostMessage(Handle, WM_CLOSE, 0, 0); // NB: to avoid any visual glitch use AlpaBlend
end;
If you don't need this encapsulation, a better alternative can be to check the condition before trying to show the form, for instance by embedding the Form2.Show call in a function that tests all the required conditions first.
Two Ways....
1. using oncreate and onactivate
create a global flag or even 2
var
aInitialized:boolean;
Set the flag to false in the oncreate handler.
aInitialized := false; //we have not performed our special code yet.
Inside onActivate have something like this
if not aInitialized then
begin
//our one time init code. special stuff or whatever
If successful
then set aInitialized := true
else aInitialized := false
end;
And how to close it without showing anything just add your terminate to the formshow. of course you need to test for some reason to close.. :)
Procedure Tmaindlg.FormShow(Sender: TObject);
Begin
If (shareware1.Sharestatus = ssExpired) or (shareware1.Sharestatus = ssTampered) Then
application.Terminate;
End;
In your DPR you will need to add a splash screen type effect. In my case I am showing progress as the application starts. You could also just show the form and get some data.
Code from the splash.pas
Procedure tsplashform.bumpit(str: string);
Begin
label2.Caption := str;
gauge1.progress := gauge1.progress + trunc(100 / items);
update;
If gauge1.progress >= items * (trunc(100 / items)) Then Close;
End;
Program Billing;
uses
Forms,
main in 'main.pas' {maindlg},
Splash in 'splash.pas' {splashform};
{$R *.RES}
Begin
Application.Initialize;
Application.Title := 'Billing Manager';
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update;
splash.items := 5;
SplashForm.bumpit('Loading Main...');
Application.CreateForm(Tmaindlg, maindlg);
SplashForm.bumpit('Loading Datamodule...');
Application.CreateForm(TfrmSingleWorkorder, frmSingleWorkorder);
SplashForm.bumpit('Loading SQL Builder...');
Application.CreateForm(TDm, Dm);
SplashForm.bumpit('Loading Security...');
Application.CreateForm(TSQLForm, SQLForm);
SplashForm.bumpit('Loading Reports...');
Application.CreateForm(Tpickrptdlg, pickrptdlg);
Application.Run;
End.

Resources