There was a question similar to this however the user was using something way more advanced so I was quite confused.
This is the procedure the exception flares up. Specifically on the ADOCon.connected line. I am using the dbgo stuff and Microsoft access for my database.
The exception I am getting is: EAcessViolation. I'm wondering what mistake I've made to cause it and how to solve it. I have run the procedure on both with a pre-existing database and a new one. When there is a pre-existing database the exception is one the 19th line and without it is on the 14th line. As a user has mentioned, I have read the documentation however I am still confused on how to solve the error. The error is definitely here as this is the first piece of access I call.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,DB, ADODB,ComObj;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
ADOCom:TADOcommand;
ADOCon:TADOConnection;
ADOQ:TADOQuery;
nameDB:string;
db:OLEVariant;
begin
namedb:='Brill.accdb';
if not fileexists(namedb) then
begin
db:=createOLEObject('ADOX.Catalog');
db.create('Provider=Microsoft.ACE.OLEDB.12.0;Data Source='+nameDB+';');
db:=null;
ADOCon.connectionstring:='Provider=Microsoft.ACE.OLEDB.12.0;Data Source='+
nameDB+';';
ADOCon.connected:=True;
ADOCon.loginprompt:=False;
end
else
ADOCon.connectionstring:='Provider=Microsoft.ACE.OLEDB.12.0;Data Source='+
nameDB+';';
end.
Your code is riddled with errors.
When you declare variables of a particular class type in your code, you're responsible for creating an instance of that class and assigning it to the variable before using it, and cleaning up when you're finished with it.
var
ADOCon: TAdoConnection;
ADOCom:TADOcommand;
ADOQ:TADOQuery;
begin
ADOCon := TADOConnection.Create(nil);
try
// Set up connection properties here
ADOQ := TADOQuery.Create(nil);
try
// Set up ADOQ properties and use query here
finally
ADOQ.Free;
end;
finally
ADOCon.Free;
end;
end;
Also, your use of the db: OleVariant (and all code related to it) is doing absolutely nothing. You get an instance, assign properties to that instance, and then throw it away, which means you can just delete that variable and the three lines of code related to it entirely; they serve zero purpose.
Of course, the better solution than any of the above is to add a TDataModule to your form, drop a TADOConnection and TADOQuery on it, set the properties in the Object Inspector or the OnCreate of the datamodule. You can then move that datamodule into the list of available forms, move it up to be created before your main form, and have access from anywhere in your app that uses that datamodule, and the datamodule will free everything properly when you exit your application. It also separates all of the database code from your user interface, cleaning up your code considerably.
Related
I am getting this error with my delphi code below after following this tutorial: Delphi 7: ADO, need basic coding example . I have seen other solutions however I am unsure what they really mean or what I should actually do. I was wondering if someone could see if there was an obvious problem or explain what I should be checking. Error I am getting on the ADOConnection1:=TADOConnection.create(nil) line.
[Microsoft][ODBC MANAGER] data source name not found and default driver not specfied
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,ADODB,Stdctrls, DB;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
procedure createdb;
procedure closedb;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
Procedure TForm1.createdb;
var
nameDB:string;
connectionstring:string;
begin
connectionstring:='Provider=Microsoft.ACE.OLEDB.12.0;Data
Source=E:\Project;';
namedb:='Project1db.accdb';
ADOConnection1:=TADOConnection.Create(nil);
ADOConnection1.LoginPrompt:=false;
ADOConnection1.Connected:=true;
ADOConnection1.Execute('CREATE DATABASE IF NOT EXISTS
Project1db.accdb',cmdtext);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
createdb;
closedb;
end;
Procedure TForm1.closedb;
begin
ADOConnection1.Free;
end;
end.
Assuming your Project1db.accdb already exists in E:\Project, the way to set
up your AdoConnection is as follows:
In the IDE, click your AdoConnection and set its LoginPrompt property to False.
Click the [...] button in the ConnectionString property
In the ConnectionString pop-up, click the Use Connection String radiobutton then
click the Build button below it.
On the Provider tab, set the Provider type to
Microsoft.ACE.OLEDB.12.0
On the Connection tab, set the Data Source to
e:\Project\Project1db.accdb
Then click the Test Connection button. You should get a pop-up confirming that
the connection succeeded.
If the Project1DB.accdb file doesn't already exist, you could use #Victoria's for example this link to create it or use MS Access to do it. But you will still need something like your existing code to open the database.
As I said in a comment, you shouldn't be calling ADOConnection1 := TAdoConnection.Create(Nil) so delete that line and the one ADOConnection1.Free. I hope you can understand now that you can use the Object Inspector to configure the AdoConnection, etc.
Btw, I'm sorry if my comment on one of your questions yesterday confused you into trying to use ODBC (which is fine if you are the administrator of your machine but can be trickier if you aren't). To use an existing ODBC datasource, you would need to set the AdoConnection's Provider type to something like Microsoft OLE DB Provider for ODBC Drivers, rather than any of the Access-specific providers - ODBC is a generic way of working with various different types of database (including Access). Btw, you can use an ODBC dsn to create an Access database, as an alternative to creating the database using the ADOX objects as per the article #Victoria linked.
In a runtime only package, I've defined a TFrame descendant which publishes the OnLoaded event:
type
TMyMethod = procedure() of object;
TMyFrame = class(TFrame)
protected
FOnLoaded : TMyMethod;
procedure Loaded(); override;
published
property OnLoaded : TMyMethod read FOnLoaded write FOnLoaded;
end;
implementation
{$R *.dfm}
procedure TMyFrame.Loaded();
begin
inherited;
if(Assigned(FOnLoaded))
then FOnLoaded();
end;
In a designtime only package, I've registered TMyFrame component as follows:
unit uMyRegistrations;
interface
uses
Classes, uMyFrame;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyTestComponents', [
TMyFrame
]);
end;
I've installed the designtime package, I can find TMyFrame in the tool palette and its OnLoaded event is shown in the object inspector.
I've dragged a TMyFrame into a form, then I've assigned the OnLoaded event by doubleclicking from the object inspector.
After assigning the event, I noticed that an access violation error message appears each time I try to open the form's file in Delphi (It let me open the ".pas" file, but I can't switch to visual designer view).
Did I correctly published the OnLoaded event? If so, what else is wrong?
Further Informations:
I'm using Delphi 2007 (don't know if it matters).
The error also appears by doing the same thing with different parent classes (Not only for TFrame descendants).
Updated (somewhat less bogus) answer
You accepted my original answer, but what I wrote was not correct. Rob Kennedy pointed to an article by former Embarcadero developer Allen Bauer on the topic of Assigned.
Allen explains that the Assigned function only tests one pointer of the two pointers in a method pointer. The IDE at design time takes advantage of this by assigning sentinel values to any published method properties (i.e. events). These sentinel values have nil for one of the two pointers in the method pointer (the one that Assigned checks), and an index identifying the property value in the other pointer.
All this means that False is returned when you call Assigned at design time. So long as you check published method pointers with Assigned before calling them, then you will never call them at design time.
So what I originally wrote cannot be true.
So I dug a bit deeper. I used the following very simple code, testing with XE7:
type
TMyControl = class(TGraphicControl)
protected
FSize: Integer;
procedure Loaded; override;
end;
....
procedure TMyControl.Loaded;
begin
inherited;
FSize := InstanceSize;
end;
....
procedure Register;
begin
RegisterComponents('MyTestComponents', [TMyControl]);
end;
This was enough to cause an AV in the IDE at design time whenever the Loaded method was executed.
My conclusion is that the IDE does some rather underhand things when streaming, and your objects are not in a fit state to use when the Loaded method is called. But I don't really have a better understanding than that.
Original (very bogus) answer
You must not execute event handlers at design time, and your code does just that. The reason being that at design time the event handler's code is not available.
The control's code is available, the IDE has loaded it – but the code that implements the event handler is not. That code is not part of the design time package, it is part of the project that is currently open in the IDE. After all, it might not even compile yet!
The Loaded method should defend against this like so:
procedure TMyFrame.Loaded();
begin
inherited;
if not (csDesigning in ComponentState) and Assigned(FOnLoaded) then
FOnLoaded();
end;
I'm trying to use a Form from another Unit, but the code isn't recognizing the other Unit.
Example:
unit uImpressao;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, uniGUITypes, uniGUIAbstractClasses,
uniGUIClasses, uniGUIForm, uniGUIBaseClasses, uniPanel, uniURLFrame;
type
TfImpressao = class(TUniForm)
ufRelatorio: TUniURLFrame;
UniImage1: TUniImage;
procedure UniImage1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses
MainModule, Main, uBancoHoras;
procedure TfImpressao.UniImage1Click(Sender: TObject);
begin
fBh.iTeste.Visible := false;
end;
end.
unit uBancoHoras;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, uniGUITypes, uniGUIAbstractClasses,
uniGUIClasses, uniGUIForm, uniLabel, pngimage, uniImage, uniGUIBaseClasses,
uniPanel, uniPageControl, uniButton, uniBitBtn, uniSpeedButton, uniCanvas,
uniDateTimePicker, uniMemo, uniMultiItem, uniComboBox, uniEdit, uniBasicGrid,
uniDBGrid, uniDBMemo, uniRadioButton, uniDBText, uniRadioGroup, frxClass,
frxDBSet;
type
TfBH = class(TUniForm)
iTeste : TUniImage;
private
{ Private declarations }
public
{ Public declarations }
end;
var
url: string;
function fBH: TfBH;
implementation
{$R *.dfm}
uses
MainModule, Main, uImpressao;
function fBH: TfBH;
begin
Result := TfBH(MM.GetFormInstance(TfBH));
end;
procedure TfBH.iTesteClick(Sender: TObject);
begin
fImpressao.ShowModal;
end;
When I try using the uImpressao unit in uBancohoras unit, returns the error "Undeclared identifier 'fImpressao'".
Using uBancoHoras unit in the uImpressao unit, works fine.
I don't understand why this error is happening with one unit, but not with the other.
I hope you can help me!
Note: Please note that this answer was based on the original code posted in the question, which was subsequently replaced in its entirety with new and vastly different code.
fBH is not declared in unit A,so fBH.iTeste.Visible := True; can't possibly work; there is no such variable. You deleted the global variable fBH that the IDE creates for you (although, interestingly you left the var statement above it right above the implementation keyword).
Either add back in the declaration (by adding var fBH: TfBH; between the end of the class declaration and the implementation keyword), or create an instance of the form in unit B when you need to use it and access it through the local variable from there.
(Whichever direction you go, you never address a form using the variable from within that form's methods; use Self instead. Don't use fBH.ShowModal; use either Self.ShowModal or ShowModal instead.)
In uBancoHoras you have defined
function fBH: TfBH;
...
implementation
...
function fBH: TfBH;
begin
Result := TfBH(MM.GetFormInstance(TfBH));
end;
So you have defined a global function called fBH that returns an instance of the TfBH form class, seemingly through some sort of factory method (probably defined in MainModule?).
There is no corresponding method or variable in uImpressao with the name fImpressao, however - the the compiler error that fImpressao is an undeclared identifier.
Assuming that MM.GetFormInstance is suited to the task, and also assuming you wish to keep this design pattern, you would have to define (in uImpressao)something like :
function fImpressao: TfImpressao;
...
implementation
...
function fImpressao : TfImpressao;
begin
Result := TfImpressao(MM.GetFormInstance(TfImpressao));
end;
We can't see the implementation details of MM.GetFormInstance, however, so there is no guarantee that this will work - it only follows the pattern that fBH has set. Agreed with Ken that you should perhaps consider a better way to manage your forms. Resorting to global variables or global methods that reach across units to dig up a class instance feels like a headache waiting to happen...
I've got the following code in Form1.
public
{ Public declarations }
cas: integer;
end;
Then I work with the variable, and then I call another form with Form2.ShowModal; On Form2 I try to execute the following: Label9.Caption:=Format('%ds',[Form1.cas]);. But no matter what I do, in Form1 'cas' is assigned the proper value but in Form2 it always shows "0s". Why does that happen?
EDIT:
Now I have in the first unit called 'kolecka' this
var
Form1: TForm1;
barvy: array[1..6] of TColor;
kola: array[1..22] of TShape;
valid: integer;
bezi: boolean;
presnost: real;
skore: integer;
chyb: integer;
kliku: integer;
cas: integer;
and this in the other unit called 'dialog':
implementation
uses
kolecka;
{$R *.dfm}
procedure Statistiky();
begin
With Form2 do begin
Label8.Caption:=IntToStr(kolecka.skore);
Label9.Caption:=Format('%ds',[kolecka.cas]);
Label10.Caption:=IntToStr(kolecka.cas);
Label11.Caption:=IntToStr(skore);
Label12.Caption:=Format('%.2f%%',[presnost]);
end;
end;
But it still doesn't work.. still shows a zero.
EDIT2:
I feel like every answer says something different and I'm very confused..
EDIT3: This is how 'cas' is manipulated in Form1
procedure TForm1.Timer3Timer(Sender: TObject);
begin
cas:=cas+1;
Form1.Label5.Caption:=IntToStr(cas);
end;
FOUND IT!
Meh. I figured out where was the problem.
I was assigning the label captions on Form2 Create and not Show, so of course they were at 0 >.>
In your original question, you declared a field in an object, and you thought it was a global, perhaps?
unit unit1;
interface
uses Stuff;
type
TForm1 = class(TForm)
public
THisIsAFieldInAnObject:Integer;
end
var
ThisIsAGlobal:Integer;
implementation
uses OtherStuff;
...
Notice where you put globals above. Global variables are not fields inside a class. Where you put something, when you write code is called "the context you are in". Inside a class declaration, something like public makes sense as a visiblity specifier. It does not make things global, it makes them visible to users of the class.
To access the global, access it as unitName.VariableName, and don't forget to add 'Uses unitName' to the other unit.
Update You are now correctly accessing the global variable, and it doesn't contain the value you expected. That's where we start debugging. Set a breakpoint on the place where you set the variable, and on any other place where it is changed back to 0. Now set a breakpoint on the place where you read the variable. I find that variable writes work better when they actually happen, and when they aren't over-written by a subsequent write to the same place, that contains a different value. Variables are like a box which contains a number. Zero things writing to it (the code you thought got called did not get called) or two things writing to it (the thing you think should be there but is not there because the second write zapped the first value) are common sources of your sort of confusion.
You have a global variable: kolecka.cas and a field kolecka.Form1.cas. Those are different.
public
{ Public declarations }
cas: integer;//This is the field
end;
var
Form1: TForm1;
cas: integer;//This is the global variable
Label9.Caption:=Format('%ds',[kolecka.cas]); accesses the global variable.
cas in an instance method of TForm1 refers to the field.
On Label10.Caption:=IntToStr(kolecka.cas);, you're actually reading the cas global variable of unit kolecka, not the Form1's one.
In the first case, you could be trying to manipulateForm1.cas after call Form2.ShowModal. Please take note that, code following a ShowModal call won't be executed until you close the form shown with ShowModal.
UPDATE
It seems you're manipulating unit's cas variable, not form's one. I exactly do not know how Delphi treats this case. But it a good practice to explicity indicate which cas instance you are manipulating. Use this code:
self.cas := self.cas + 1;
The first thing I would check is if you have only 1 variable named Form1.
My best guess would be that you have 1 in unit Unit1(Where TForm1 is declared) and 1 in unit Kolecka, but that's just an assumption.
I have an application that on startup checks some conditions and launches an external program in the OnShow event of the Main Form. The problem is that if there is an error when launching the external program, I want the application to terminate immediately. But there is an issue with that, in that EurekaLog catches my exceptions and somehow disrupts the message loop there by negating all calls to Application.Teminate and any other normal shutdown methods.
So here is my question, would ExitProcess be the best route then to immediately terminating my application when this condition exists?
By the time OnShow has fired, you're too far into the program to decide that you don't really want the program to run. You should make that determination sooner. OnShow is not the place to decide that the form shouldn't be shown.
This is the sort of thing you should check before you even create the main form. Put your checks in the DPR file, and if you determine that the program shouldn't run, then simply call exit.
begin
Application.Initialize;
if not ApplicationShouldReallyStart then
exit;
Application.CreateForm(TMainAppForm, MainAppForm);
Application.Run;
end.
Fill in your own implementation of ApplicationShouldReallyStart. (And it really should be a separate function, not in-line in the DPR file. The IDE gets confused if the begin-end block in the DPR file gets too complex.)
Aside from that, do not call ExitProcess. Call Halt instead. Halt calls ExitProcess, but it also calls unit finalization sections and other Delphi-specific process-shutdown tasks.
Work WITH the system, not AGAINST it! You can't simply die in the middle of things. If you want to die do it within the rules--WM_CLOSE or maybe your own routine that says why it's dying and then sends a WM_CLOSE.
You better send a wmClose message to the window. Else you have a big chance to get into trouble because of other messages send to the form.
I wrote a small application to test a theory and here is what I would suggest.
Call the CLOSE method.
The following example unit closes the application with no errors in D2009.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
close;
end;
end.
While I fully agree with Rob Kennedy here, I want to note, that you may use EurekaLog's routines to control error dialog behaviour.
For example:
uses
ExceptionLog, ECore;
...
begin
ForceApplicationTermination(tbTerminate);
// ... <- Bad code goes there
end;
That way, the application will be closed right after displaying error dialog.