Undeclared Identifier using listboxes - delphi

I have created a listbox and used it to add and remove/load and save items to a file which is all well and good. However when I try to use the listbox on another unit, I get an error: undeclared identifier.
The first Unit below MainUnit is where I am trying to use the listbox and getting the error undeclared identifier. Below the MainUnit is the ManageUsersUnit this is the unit in which the listbox is used and created. All the code works with the listbox on the ManageusersUnit but not on any other unit.
unit MainUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Menus, ExtCtrls;
implementation
uses AddTenantUnit, HomeUnit, MainMenuUnit;
{$R *.dfm}
{ the error im getting is in this procedure}
procedure TMainForm.FormCreate(Sender: TObject);.
begin
if fileExists('Newuser.dat')
then
begin
UserListBox.Items.LoadFromFile('Newuser.dat');
end
{endif};
end;
unit ManageUsersUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, MainUnit, StdCtrls, Menus, ComCtrls;
type
TManageUsersForm = class(TForm)
AddUserButton: TButton;
RemoveUserButton: TButton;
ChangeUsernameButton: TButton;
ChangePasswordButton: TButton;
HomeButton: TButton;
UserListBox: TListBox;
UsernameEdit: TEdit;
SaveButton: TButton;
PasswordEdit: TEdit;
SubText1Label: TLabel;
SubText2Label: TLabel;
PassListBox: TListBox;
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
var
ManageUsersForm: TManageUsersForm;
implementation
uses PassWord, AddUserUnit, HomeUnit;
{$R *.dfm}
procedure TManageUsersForm.HomeButtonClick(Sender: TObject);
begin
HomeForm.show;
ManageUsersForm.Close;
end;
//Add UserName
procedure TManageUsersForm.AddUserButtonClick(Sender: TObject);
var
i : integer;
Found : Boolean;
AddUser : string;
begin
{ManageUsersForm.close;
AddUserForm.ShowModal; }
Found := false;
AddUser := UsernameEdit.Text;
i := 0;
while i < userlistbox.Items.Count do
begin
if (UpperCase(AddUser) = Uppercase(userlistbox.Items[i]))
then
Found := True;
inc(i);
end;
{endhwile};
if Found = False then
begin
userlistbox.Items.Add(AddUser);
UsernameEdit.Text := '';
showMessage(AddUser + ' added');
end
else
showMessage(AddUser + ' is already present.');
{endif};
end;
procedure TManageUsersForm.RemoveUserButtonClick(Sender: TObject);
var
deleted : string;
begin
with UserListBox do
begin
if ItemIndex = -1
then
showMessage('You must select a User first.')
else
begin
Deleted := Items.Strings[ItemIndex];
Items.Delete(ItemIndex);
showMessage(Deleted + ' deleted');
end;
{endif};
end;
end;
procedure TManageUsersForm.Button1Click(Sender: TObject);
var
i : integer;
Found : Boolean;
check : string;
begin
check := PasswordEdit.Text;
Found := false;
i := 0;
while i < userlistbox.Items.Count do
begin
if (UpperCase(check) = Uppercase(userlistbox.Items[i]))
then
Found := True;
inc(i);
end;
{endhwile};
if Found = False
then
begin
showMessage(check + ' Incorrect Username');
end
else
showMessage(' well done in file :).');
{endif};
end;
procedure TManageUsersForm.SaveButtonClick(Sender: TObject);
begin
assignfile (userFile,'Newuser.dat');
UserListbox.Items.SaveToFile('Newuser.dat');
showMessage('Saved to file');
end;
procedure TManageUsersForm.FormCreate(Sender: TObject);
begin
if fileExists('Newuser.dat')
then
begin
UserListBox.Items.LoadFromFile('Newuser.dat');
end
{endif};
end;
procedure TManageUsersForm.TestBtnClick(Sender: TObject);
begin
AddUserForm.ShowModal;
end;
end.

Your MainUnit needs to use the ManageUsersUnit unit. If the only references to the form in that other unit are in the implementation section then add it to the uses clause there. As a general rule you should only add to the interface uses clause if absolutely necessary.
unit MainUnit;
/// ...
implementation
uses AddTenantUnit, HomeUnit, MainMenuUnit, ManageUsersUnit;
Your code then references the UserListBox directly, but this reference is a member variable of the TManageUsersForm class, so you must first identify an instance of that class before you access the members of that instance.
In this case you appear to have a public instance already available which you presumably intend to use: ManageUsersForm
So your code to load the file data into the list should be:
ManageUsersForm.UserListBox.Items.LoadFromFile('Newuser.dat');
This will fix your immediate issue. However, there is a lot wrong with this approach.
MainUnit assumes that ManageUsersForm is a valid, existing form instance. If the form is set to AutoCreate in the project then this may be valid but is dangerous since it makes your code
vulnerable to a change in the way that your forms are created.
MainUnit is inappropriately responsible for some of the behaviour of the ManageUsers form. If the ManageUsers form truly manages users then this should include persistence of users to/from files itself.
MainUnit is highly dependent upon the internal details of the ManageUsers form. We say it is "tightly coupled". If the ManageUsers form is modified to use, for example, a grid to a listview to present users, the MainUnit code will break since it relies on the intimate knowledge that the users list is specifically a listbox.
As a result of the way that the VCL works, the user interface controls on a form in Delphi are publicly accessible, but you should regard these as private (or, at most, protected). Your ManageUsers form should provide a public interface to the functionality that it provides to the "outside world" which deals in terms of user data.
There should be no references to the user interface elements of a form - including any event handlers - outside of the form itself (or descendant form classes if you are using an OO hierarchy of form classes).

Related

How to save breakpoints using the Delphi IDE?

How can I save breakpoints using the Delphi IDE? I only know how to store the settings in a .dsk file.
I am using Delphi 2007.
I'm assuming from your mention of the .Dsk file that you are aware that the breakpoints are stored in there, but want to save them yourself for some reason. Of course, the easiest method of getting a list of saved breakpoints is simply to read them from the .Dsk file, but that assumes that it has been saved to disk, which usually
occurs when you close the project file.
You can write your own IDE plug-in to get a list of currently-set breakpoints
and save them in any way you want. The minimalist example below shows how to do this - see the GetBreakpoints method for details. To use this in the IDE, you would create a new package which requires
DesignIde.Dcp. Make sure that the output directory for the .Bpl file is either where
your 3rd-party .Bpls are stored on or is on your path. You can then install the
package in the IDE vie Install packages from the IDE's menu.
As you can see, it works by using the BorlandIDEServices interface in the ToolsAPI units to get an IOTADebuggerServices interface, and then uses that to iterate its SourceBkpts list and saves a number of properties of each IOTASourceBreakpoint in that list.
Note that
You can also retrieve a list of address breakpoints and save those in a similar fashion.
Both kinds of breakpoint interface in ToolsAPI have property setters as well as getters, so you could modify existing breakpoints in code and conceivably create new ones.
Code
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ToolsApi;
type
TBreakpointSaveForm = class(TForm)
Memo1: TMemo;
btnGetBreakpoints: TButton;
procedure btnGetBreakpointsClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
protected
public
procedure GetBreakpoints;
end;
var
BreakpointSaveForm: TBreakpointSaveForm;
procedure Register;
implementation
{$R *.DFM}
procedure TBreakpointSaveForm.GetBreakpoints;
var
DebugSvcs: IOTADebuggerServices;
procedure SaveBreakpoint(BreakPoint : IOTASourceBreakpoint);
begin
Memo1.Lines.Add('File: ' + Breakpoint.FileName);
Memo1.Lines.Add('LineNo: ' + IntToStr(Breakpoint.LineNumber));
Memo1.Lines.Add('Passcount: ' + IntToStr(Breakpoint.Passcount));
Memo1.Lines.Add('');
end;
procedure SaveBreakpoints;
var
i : Integer;
BreakPoint : IOTASourceBreakpoint;
begin
Memo1.Lines.Add('Source breakpoint count : '+ IntToStr(DebugSvcs.GetSourceBkptCount));
for i := 0 to DebugSvcs.GetSourceBkptCount - 1 do begin
Breakpoint := DebugSvcs.SourceBkpts[i];
SaveBreakpoint(Breakpoint);
end;
end;
begin
if not Supports(BorlandIDEServices, IOTADebuggerServices, DebugSvcs) then begin
ShowMessage('Failed to get IOTADebuggerServices interface');
exit;
end;
Memo1.Lines.Clear;
SaveBreakpoints;
end;
procedure Register;
begin
end;
initialization
BreakpointSaveForm := TBreakpointSaveForm.Create(Application);
BreakpointSaveForm.Show;
finalization
if Assigned(BreakpointSaveForm) then
BreakpointSaveForm.Free;
end.
procedure TBreakpointSaveForm.btnGetBreakpointsClick(Sender: TObject);
begin
GetBreakpoints;
end;

How to Pass an Object into a Second New Delphi Form

I have an object that is created on Form1 and I would like to be able to access one of its fields on Form2. I have tried to google it and nobody can give an answer that I can understand. Please excuse me but I am a novice.
Form1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
Ttest=class
public
sName:string;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
myObj:Ttest;
begin
myObj.Create;
myObj.sName := 'Name';
Form2.Show;
end;
end.
Form2
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button2: TButton;
procedure Button2Click(Sender: TObject);
end;
var
Form2: TForm2;
implementation
uses Unit1;
{$R *.dfm}
procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(myObj.sName);//This is not working
end;
end.
You have two forms that both use an object. You should define the object in a separate unit and list it in the Uses clause in the Interface section of both forms. Try using something already defined in a main library, like TStringlist, so you don't get confused with this part.
From what you're showing here, you're attempting to create an instance of that object in one form and do something with it in another form. That's a common thing to do: you may have one unit that asks for a filename and loads a file into a TStringList, then hands that over to another form or unit to deal with.
The way you're doing it, however, can be improved to reduce coupling between the two forms.
What you want to do is define a property like this in TForm2:
TForm2 = class( TForm )
. . .
private
Ftestobj : TTest; // or TStringlist
public
property testobj : TTest read Ftestobj write Ftestobj;
Then in TForm1.OnButtonClick do something like this:
form2.testobj := myobj;
form2.Show;
And then this becomes:
procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(Ftestobj.sName);
end;
I did a whole session in CodeRage 9 on this topic recently, in fact. It's entitled, "Have you embraced your inner plumber yet?" and it's all about moving data in and out of forms like this. (I call it plumbing code.)
Search for "coderage 9" and watch the video. At the end is a link where you can download my example code. That should keep you busy for a while. :)

How to add support of HTML help files (.chm) on Delphi XE2?

How to add support of HTML help files (.chm) on Delphi XE2? We need to use A-links (A-keywords) on HelpContext property of every control to lookup help pages. Delphi XE2 has native support of HTML help files by unit HTMLHelpViewer. But how to use it?
It's not hard with F1 jump to a context.
Select Edit1 and press F1 . Help opens and Overview.htm is shown.
Prerequisite.
Edit1 Help settings:
sample.chm source settings.
sample.ali
IDH_Overview=Overview.htm
IDH_welcom=FirstTopic.htm
IDH_UsingtheMenus=Overview.htm
sample.h
#define IDH_Creating_Projects_and_Topics 1005
#define IDH_Overview 1003
#define IDH_UsingtheMenus 1009
Unit1.pas
unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, HTMLHelpViewer, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
HHALINKLOOKUP: TButton;
JumpAnchor: TButton;
Edit1: TEdit;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure HHALINKLOOKUPClick(Sender: TObject);
procedure JumpAnchorClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
hpPath : string;
link : HH_AKLINK;
procedure TForm1.FormCreate(Sender: TObject);
begin
hpPath := ExtractFilePath(Application.ExeName) +
'HelpFile\sample.chm';
Application.HelpFile := hpPath;
end;
procedure TForm1.HHALINKLOOKUPClick(Sender: TObject);
var
link : HH_AKLINK;
szUrl,szKey,szMsgText,szMsgTitle,szWindow : AnsiString;
begin
szKey := Edit1.Text; // 'UsingtheMenus';
szUrl :='Overview.htm';
szMsgText :='Error: Can''t find "'+Edit1.Text+'"!';
szMsgTitle :='Error: HH_ALINK_LOOKUP';
szWindow :='main';
with link do begin
cbStruct := sizeof(HH_AKLINK) ;
fReserved := False;
pszKeywords := PChar(szKey);
pszUrl := nil;
pszMsgText := PChar(szMsgText);
pszMsgTitle := PChar(szMsgTitle);
pszWindow := PChar(szWindow);
fIndexOnFail:= False;
end;
HtmlHelpW(0, hpPath+'>main', HH_DISPLAY_TOPIC, DWORD_PTR(nil));
HtmlHelpW(0, hpPath, HH_ALINK_LOOKUP, DWORD_PTR(#link));
end;
procedure TForm1.JumpAnchorClick(Sender: TObject);
begin
HtmlHelpW(0, hpPath+'::/Overview.htm#'+Edit1.Text+'>main', HH_DISPLAY_TOPIC, DWORD(nil));
end;
end.
Here is a ready to use sample.chm and the source Download
There is a trick how to easily, to jump, not only to the .htm file but jumps directly to an anchor.
Change sample.ali
IDH_Overview=Overview.htm
IDH_welcom=FirstTopic.htm
IDH_UsingtheMenus=Overview.htm#UsingtheMenus
Insert an anchor at the place, you want to jump to in Overview.htm
[...]
<A NAME="UsingtheMenus" </A>
<P><STRONG>Using the Menus and Toolbars</STRONG>
<P>The menus and toolbars provide a complete set of tools
[...]
Now it is possible with F1, jump directly to the desired point in overview.htm.
I suspect that to use A-links you need to do the following:
Assign an Application.OnHelp handler as described below.
Assign Application.HelpFile during program startup.
Call Application.HelpKeyword if you wish to invoke the help system with an A-link.
Set the HelpKeyword property for any GUI controls that you wish to respond to context sensitive F1 key presses.
The OnHelp handler looks like this:
function TMainForm.ApplicationHelp(Command: Word;
Data: THelpEventData; var CallHelp: Boolean): Boolean;
var
Link: THH_AKLink;
ALink: string;
begin
CallHelp := False;
Result := True;
//argh, WinHelp commands
case Command of
HELP_COMMAND:
begin
ZeroMemory(#Link, SizeOf(Link));
Link.cbStruct := SizeOf(Link);
ALink := PChar(Data); // we are going to re-purpose the keyword as an A-link
Link.pszKeywords := PChar(AnsiString(ALink)); // seems we have to pass a PAnsiChar ..
Link.fIndexOnFail := True;
HtmlHelp(GetDesktopWindow, Application.HelpFile, HH_ALINK_LOOKUP,
DWORD_PTR(#Link));
end;
end;
end;
The HtmlHelpViewer unit contains methods named LookupALink which do the same. But I don't see how they could ever be called.
The above approach is a little bit hacky because it interprets keywords as A-Links. If you want context sensitive help, I can't see what else you can do.
Not sure how Xe2 viewer works (I'm on 2007) but I just use Eric Granges port of the Microsoft HTML help API, which unsurprisingly, is called HTMLhelpAPI.pas.
You can call an Alink using the function
ChmShowTopic(const filename,atopic:string):HWND;

Delphi - TXMLDocument created at run-time generates AV, with component on the form is working

I'm creating an instance of TXMLDocument at runtime, to load and parse a XML file. You can check the code below:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc, StdCtrls;
type
Txml = class(TForm)
// XMLDocument1: TXMLDocument;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
xml: Txml;
implementation
{$R *.dfm}
procedure Txml.FormCreate(Sender: TObject);
var i,j:integer;
aNode:IXMLNode;
ws:String;
XMLDocument1:TXMLDocument;
begin
Memo1.Lines.Clear;
XMLDocument1 := TXMLDocument.Create(nil);
try
XMLDocument1.LoadFromFile('C:\a.xml');
XMLDocument1.Active := true;
aNode := XMLDocument1.ChildNodes.First;
while aNode<>nil do
begin
for i := 0 to aNode.ChildNodes.Count-1 do
begin
if aNode.ChildNodes[i].NodeName = 'Role' then
begin
Memo1.Lines.Add('Tag - '+aNode.ChildNodes[i].ChildNodes['Tag'].Text);
for j := 0 to aNode.ChildNodes[i].ChildNodes.Count-1 do
if aNode.ChildNodes[i].ChildNodes[j].HasChildNodes then
begin
ws := VarToStr(aNode.ChildNodes[i].ChildNodes[j].ChildValues['Tag']);
if trim(ws)<>'' then
Memo1.Lines.Add(ws);
ws := VarToStr(aNode.ChildNodes[i].ChildNodes[j].ChildValues['Value']);
if trim(ws)<>'' then
Memo1.Lines.Add(ws);
end;
end;
end;
aNode := aNode.NextSibling;
end;
XMLDocument1.Active := false;
finally
FreeAndNil(XMLDocument1);
end;
end;
end.
The problem is that this is generating an AV. As you probably have seen, before the component was on the form (// XMLDocument1: TXMLDocument;).
Why when the component was on the form the code was working, but when I'm creating it at run-time it generates AV?
LE:
solution: based on the answers/comments and Delphi Help:
XMLDocument1 : IXMLDocument; //not TXMLDocument
XMLDocument1 := LoadXMLDocument(...);
FreeAndNil;// must be deleted
From what I know you should be using interface IDoc: IXMLDocument; instead.
From docs:
When TXMLDocument is created without an Owner, it behaves like an
interfaced object. That is, when all references to its interface are
released, the TXMLDocument instance is automatically freed. When
TXMLDocument is created with an Owner, however, it behaves like any
other component, and is freed by its Owner.
In other words, when creating a TXMLDocument instance with a nil Owner, do not call Free() or FreeAndNil() on the instance, and you must assign the object to an IXMLDocument variable so its now-active reference count is managed properly.
You need to provide an Owner to TXMLDocument when creating it in run-time.
XMLDocument1 := TXMLDocument.Create(xml);

detect usb drive/device using delphi

i can't figure out the formatting rules here .. too many lines of code in my example to add 4 spaces to each line, so here is the link to the code i need help with
http://nitemsg.blogspot.com/2011/01/heres-unit-written-in-delphi-7-that-you.html
The problem I have is that I don't know enough about delphi to use this code with a form.
I am a drag and drop programmer only.
An example with a showmessage('friendly name =' + ... ) when a USB device is detected is what I need.
cheers,
If you are only familiar with drag-and-drop programming, and don't know much about objects or other units, then you need to get yourself familiarized with using objects other than auto-created forms and the components you drop in them.
The code at this link is an entire unit. You need to create a new Unit in your project (File > New > Unit). It will look something like this:
unit Unit1;
interface
implementation
end.
Now when you save the unit, the name of the unit will automatically change to the filename (without the extension) like this:
unit MahUSB;
interface
implementation
end.
In this example, you should use the same unit name as that source you're trying to use. Save the unit as 'MahUSB.pas', and should be in the same folder as the rest of your project (or elsewhere, just a suggestion). Copy/Paste all the code from that website and replace everything in this unit now.
Now in order to actually use this, you need to create an instance of this object. ONLY ONE INSTANCE (I say that just because by the looks of this, there's no need for more than one).
Very important: Seeing as you are not familiar with objects, let me quickly explain something. Objects need to be created in order to work. At the same time, anything that's created also needs to be free'd when you're done with it. In this case, we will create this object when your application starts, and free the object when your application closes.
Now on your MAIN FORM (not any other forms) you need to put an event handler for both OnCreate and OnDestroy. You also need to declare a variable to represent this object. In the declaration of your main form, add a variable 'USB' with the type of this object. Make sure that goes under the 'private' or 'public' section, either one is ok. Also make sure you declare the "MahUSB" unit at the top of your main unit in the uses clause.
Declaring the object in your main form:
type
TForm1 = class(TForm)
private
USB: TUsbClass;
public
end;
Creating/freeing object when your app starts/closes:
procedure TForm1.FormCreate(Sender: TObject);
begin
USB:= TUsbClass.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if assigned(USB) then USB.Free;
end;
Now we're not done yet. Now we need to add the event handlers. Notice at the top of this unit you got, there are two types called TOnDevVolumeEvent and TOnUsbChangeEvent. These are event types. The parameters in the event handlers must be identical to the parameters declared in these types. So now in your main form, declare these event handler procedures...
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
USB: TUsbClass;
procedure VolumeEvent(const bInserted : boolean; const sDrive : string);
procedure ChangeEvent(const bInserted : boolean;
const ADevType,ADriverName, AFriendlyName : string);
public
end;
Now just one more thing we have to do before this will work. The USB object needs to know what event handlers to use, therefore, we need to assign these procedures to the events. Upon your form's creation, we need to assign these events...
procedure TForm1.FormCreate(Sender: TObject);
begin
USB:= TUsbClass.Create;
USB.OnUsbChange:= Self.ChangeEvent;
USB.OnDevVolume:= Self.VolumeEvent;
end;
When all is said and done, your main form unit should look something like this:
unit uUSBTest;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, MahUSB;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
USB: TUsbClass;
procedure VolumeEvent(const bInserted : boolean; const sDrive : string);
procedure ChangeEvent(const bInserted : boolean;
const ADevType,ADriverName, AFriendlyName : string);
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ChangeEvent(const bInserted: boolean; const ADevType,
ADriverName, AFriendlyName: string);
begin
ShowMessage('Change event for "'+AFriendlyName+'"');
end;
procedure TForm1.VolumeEvent(const bInserted: boolean;
const sDrive: string);
begin
ShowMessage('Volume event for "'+sDrive+'\"');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
USB:= TUsbClass.Create;
USB.OnUsbChange:= Self.ChangeEvent;
USB.OnDevVolume:= Self.VolumeEvent;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if assigned(USB) then USB.Free;
end;
end.
And there you are! You will have these two event handler procedures where you can further handle either of those two events.

Resources