2010. szeptember 30., csütörtök
How to store and retrieve WAV files to / from a Paradox blob field at runtime
Problem/Question/Abstract:
How can I write and read to/ from a blob field? I want to save OLE objects like Excel sheets or wav sound into a Paradox table rather than to a file.
Answer:
Solve 1:
procedure TForm1.bnSaveClick(Sender: TObject);
begin
MediaPlayer1.Close;
tabWaves.Append;
{tabWavesAudioData is a TBlobField}
tabWavesAudioData.LoadFromFile(MediaPlayer1.FileName);
{MediaPlayer1.FileName is path to wave file}
tabWavesDescription.Value := MediaPlayer1.FileName;
tabWaves.Post;
end;
procedure TForm1.LoadWaveFromTable;
begin
MediaPlayer2.Close;
{Save the Blob to a file}
tabWavesAudioData.SaveToFile('C:\Temp\WAVETABLE.wav');
{Load wave into MediaPlayer}
MediaPlayer2.FileName := 'C:\Temp\WAVETABLE.wav';
MediaPlayer2.Open;
end;
Solve 2:
uses
MMSystem;
procedure TFrmPlayWaveFromDB.BtnWaveToDBClick(Sender: TObject);
var
theBStream: TBlobStream;
begin
if FlbSelectWave.Filename <> EmptyStr then
begin
FMemStream.Seek(0, soFromBeginning);
FMemStream.LoadFromFile(FlbSelectWave.Filename);
Table1.Edit;
theBStream := TBlobStream.Create(Table1ABlobField, bmReadWrite);
try
FMemStream.SaveToStream(theBStream);
finally
theBStream.Free;
end;
Table1.Post;
end;
end;
procedure TFrmPlayWaveFromDB.FormCreate(Sender: TObject);
begin
{The MemoryStream must persist while the sound is being played, so it can't be a local variable}
FMemStream := TMemoryStream.Create;
Table1.Open;
end;
procedure TFrmPlayWaveFromDB.BtnPlayWaveClick(Sender: TObject);
var
theBStream: TBlobStream;
begin
theBStream := TBlobStream.Create(Table1ABlobField, bmRead);
try
FMemStream.LoadFromStream(theBStream);
sndPlaySound(FMemStream.Memory, SND_ASYNC or SND_MEMORY);
finally
theBStream.Free;
end;
end;
procedure TFrmPlayWaveFromDB.FormDestroy(Sender: TObject);
begin
FMemStream.Free;
Table1.Close;
end;
procedure TFrmPlayWaveFromDB.FlbSelectWaveChange(Sender: TObject);
begin
if FlbSelectWave.Filename <> EmptyStr then
BtnWaveToDB.Enabled := true
else
BtnWaveToDB.Enabled := false;
end;
Solve 3:
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, DBCtrls, DB, DBTables, StdCtrls, Mask, Buttons, ComCtrls;
type
TMainForm = class(TForm)
tblSounds: TTable;
dsSounds: TDataSource;
tblSoundsWaveTitle: TStringField;
tblSoundsWave: TBlobField;
edTitle: TDBEdit;
edFileName: TDBEdit;
Label1: TLabel;
Label2: TLabel;
OpenDialog: TOpenDialog;
tblSoundsFileName: TStringField;
SaveDialog: TSaveDialog;
pnlToobar: TPanel;
sbPlay: TSpeedButton;
sbAdd: TSpeedButton;
sbSave: TSpeedButton;
sbExit: TSpeedButton;
Bevel1: TBevel;
dbnNavigator: TDBNavigator;
stbStatus: TStatusBar;
procedure sbPlayClick(Sender: TObject);
procedure sbAddClick(Sender: TObject);
procedure sbSaveClick(Sender: TObject);
procedure sbExitClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
procedure OnAppHint(Sender: TObject);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
uses
MMSystem;
procedure TMainForm.sbPlayClick(Sender: TObject);
var
B: TBlobStream;
M: TMemoryStream;
begin
B := TBlobStream.Create(tblSoundsWave, bmRead); {create blob stream}
Screen.Cursor := crHourGlass; {wait hourglass}
try
M := TMemoryStream.Create; {create memory stream}
try
M.CopyFrom(B, B.Size); {copy from blob to memory stream}
{Attempt to play sound. Raise exception if something goes wrong}
Win32Check(PlaySound(M.Memory, 0, SND_SYNC or SND_MEMORY));
finally
M.Free;
end;
finally
Screen.Cursor := crDefault;
B.Free;
end;
end;
procedure TMainForm.sbAddClick(Sender: TObject);
begin
if OpenDialog.Execute then
begin
tblSounds.Append;
tblSounds['FileName'] := ExtractFileName(OpenDialog.FileName);
tblSoundsWave.LoadFromFile(OpenDialog.FileName);
edTitle.SetFocus;
end;
end;
procedure TMainForm.sbSaveClick(Sender: TObject);
begin
with SaveDialog do
begin
FileName := tblSounds['FileName']; {initialize file name}
if Execute then {execute dialog}
tblSoundsWave.SaveToFile(FileName); {save blob to file}
end;
end;
procedure TMainForm.sbExitClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Application.OnHint := OnAppHint;
end;
procedure TMainForm.OnAppHint(Sender: TObject);
begin
stbStatus.SimpleText := Application.Hint;
end;
end.
2010. szeptember 29., szerda
Check if a component sits on a normal TForm or on an ActiveX one
Problem/Question/Abstract:
I have a component that is used both on an Active Form and on a normal TForm. The component needs to find out if the application it's being used in is an ActiveX project or a normal project. Is there a good way to find out?
Answer:
In case your component is TControl you can use the functions below:
function IsParentFormActiveXOne(Control: TControl): Boolean;
function GetParentForm(Control: TControl): TCustomForm;
function GetParentForm(Control: TControl): TCustomForm;
begin
Result := nil;
if Assigned(Control) then
if Control is TCustomForm then
begin
Result := Control as TCustomForm;
if Assigned(Result) and (Result is TForm) and
(TForm(Result).FormStyle = fsMDIForm) then
begin
Exit;
end;
end
else
begin
if Assigned(Control.Parent) then
Result := GetParentForm(Control.Parent);
end;
end;
function IsParentFormActiveXOne(Control: TControl): Boolean;
var
Form: TCustomForm;
begin
Form := GetParentForm(Control);
Result := Assigned(Form) and (Form is TCustomActiveForm);
end;
Otherwise simply use:
if TCustomForm(Owner) is TCustomActiveForm then
{ ... }
2010. szeptember 28., kedd
A form to rebuild the structure of Paradox Tables
Problem/Question/Abstract:
How to rebuild the structure of a table with the use of a component.
Answer:
One of the main problem when we modify programs is when the structure of a table is modified. When we have users distributed along the country the update of the program is almost imposible.
I wrote a form that read the structure of every table, compare them with the new strucure and if neccessary rebuild the table.
The form is very simply, contains 2 buttons, a BatchMove and a label. One button (BotStart) is for start the procees, other button (BotQuit) to quit the program.
Im using RxLib (The function DeleteFiles of the FileUtil Unit)
This program contains 3 examples of 3 tables, the program check the structure ov every one.
The code of the form is:
unit UVerUpd;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, Db, DbTables, FileUtil;
type
TFVerUpd = class(TForm)
BotStart: TButton;
BotQuit: TButton;
StatusBar1: TStatusBar;
Bat: TBatchMove;
Label1: TLabel;
procedure BotStartClick(Sender: TObject);
procedure BotQuitClick(Sender: TObject);
procedure FillStructure(Sender: TObject; xBase: TTable);
procedure Check_a_Table(Sender: TObject; Tabla: string);
private
{ Private declarations }
public
{ Public declarations }
xData, xDir: string;
// xdata is the alias name
// xdir is the directory where xdata is located
end;
var
FVerUpd: TFVerUpd;
function GetAliasPath(Base: string): string;
implementation
{$R *.DFM}
procedure TFVerUpd.BotStartClick(Sender: TObject);
begin
BotStart.Enabled := False;
xData := 'Pat41'; // the name of the alias, Pat41 is an example
xDir := GetAliasPath(xData);
// 3 examples
Check_a_Table(Sender, 'Paquete.DB');
Check_a_Table(Sender, 'TabDesc.db');
Check_a_Table(Sender, 'Vehiculo.db');
Close;
end;
procedure TFVerUpd.Check_a_Table(Sender: TObject; Tabla: string);
var
TOld, TNew: TTable;
xRebuild, xFound, xExiste: Boolean;
i, j: Integer;
xField: TField;
begin
StatusBar1.Panels[0].Text := Tabla;
StatusBar1.Panels[1].Text := '';
TOld := TTable.Create(Self);
TNew := TTable.Create(Self);
with TNew do
begin
DataBaseName := xData;
Tablename := Tabla;
FillStructure(Sender, TNew)
end;
xExiste := FileExists(xDir + Tabla);
if not xExiste then
xRebuild := True
else
begin
with TOld do
begin
DataBaseName := xData;
TableType := ttDefault;
Tablename := Tabla;
FieldDefs.Update;
for i := 0 to FieldDefs.Count - 1 do
FieldDefs[i].CreateField(TOld);
end;
// review the fields
xRebuild := False;
i := 0;
while (i <= TNew.FieldDefs.Count - 1) and (not xRebuild) do
begin
xField := TOld.FindField(TNew.FieldDefs[i].Name);
if xField = nil then
xRebuild := True
else
begin
if xField.DataType <> TNew.FieldDefs[i].DataType then
xRebuild := True;
if xField.Size <> TNew.FieldDefs[i].Size then
xRebuild := True;
end;
inc(i);
end;
if TNew.FieldDefs.Count <> TOld.FieldDefs.Count then
xRebuild := True;
// review the keys
TOld.IndexDefs.Update;
for i := 0 to TNew.IndexDefs.Count - 1 do
begin
xFound := False;
j := 1;
while (j <= TOld.Indexdefs.Count) and (not xFound) do
begin
if UpperCase(TNew.IndexDefs[i].Fields) = UpperCase(TOld.IndexDefs[j -
1].Fields) then
if TNew.IndexDefs[i].Name = TOld.IndexDefs[j - 1].Name then
xFound := True;
inc(j);
end;
if not xFound then
begin
xRebuild := True;
end;
end;
if TNew.IndexDefs.Count <> TOld.IndexDefs.Count then
xRebuild := True;
end;
// if the program has to rebuild the table
if xRebuild then
begin
StatusBar1.Panels[1].Text := 'Updating';
if xExiste then
begin
DeleteFiles(xDir + 'xx.*'); // RxLib
TOld.RenameTable('xx');
TNew.CreateTable;
Bat.Source := TOld;
Bat.Destination := TNew;
Bat.Execute;
end
else
TNew.CreateTable;
end;
TOld.Free;
TNew.Free;
end;
procedure TFVerUpd.FillStructure(Sender: TObject; xBase: TTable);
var
Tabla: string;
begin
// this function fills the description of the tables
with xBase do
begin
Tabla := UpperCase(TableName);
/////////////////////////////////////////////
if Tabla = 'PAQUETE.DB' then
begin
with FieldDefs do
begin
clear;
add('Clave_Paq', ftInteger, 0, false);
add('Desc_Paq', ftString, 40, false);
add('Property_Av', ftBoolean, 0, false);
add('Property_Min', ftCurrency, 0, false);
add('Property_Max', ftCurrency, 0, false);
add('Bodily_Av', ftBoolean, 0, false);
end;
with IndexDefs do
begin
clear;
add('', 'Clave_Paq', [ixPrimary, ixUnique]);
end;
end;
/////////////////////////////////////////////
if Tabla = 'TABDESC.DB' then
begin
with FieldDefs do
begin
clear;
add('CLAVE_DTO', ftInteger, 0, false);
add('DESC_DTO', ftString, 40, false);
add('TIPOL', ftInteger, 0, false);
add('TIPO_USO', ftInteger, 0, false);
add('POR_DES', ftFloat, 0, false);
add('REQMEM', ftBoolean, 0, false);
add('MENS_DESC', ftString, 100, false);
add('CLAVE_RES', ftInteger, 0, false);
end;
with IndexDefs do
begin
clear;
add('', 'CLAVE_DTO', [ixPrimary, ixUnique]);
end;
end;
/////////////////////////////////////////////
if Tabla = 'VEHICULO.DB' then
begin
with FieldDefs do
begin
clear;
add('TIPO_VEH', ftInteger, 0, false);
add('DESC_VEH', ftString, 30, false);
add('DIASMIN_VE', ftInteger, 0, false);
add('PRIMAMIN_V', ftCurrency, 0, false);
add('ANTMAX_VEH', ftInteger, 0, false);
add('NUMPAS_VEH', ftInteger, 0, false);
add('DM_ADMIT', ftBoolean, 0, false);
end;
with IndexDefs do
begin
clear;
add('', 'TIPO_VEH', [ixPrimary, ixUnique]);
end;
end;
end;
end;
procedure TFVerUpd.BotQuitClick(Sender: TObject);
begin
Close;
end;
function GetAliasPath(Base: string): string;
var
ParamList: TStringList;
begin
Result := '';
ParamList := TStringList.Create;
try
Session.GetAliasParams(Base, ParamList);
result := Uppercase(ParamList.Values['PATH']) + '\';
finally
ParamList.free;
end;
end;
end.
Component Download: http://www.baltsoft.com/files/dkb/attachment/version.zip
2010. szeptember 27., hétfő
A Component that plots graphs
Problem/Question/Abstract:
A component for creating graphs
Answer:
Here is a component that draws graphs. You can zoom in and out of the graph. The code is shown below. Copy the code to .pas file and install the component. I will add a demo to show how to use this component soon.
unit UGraph;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Math;
type
TOnMouseMove = procedure(Shift: TShiftState; x, y: integer) of object;
TOnMouseDown = procedure(Button: TMouseButton; Shift: TShiftState; x, y: integer) of
object;
TOnMouseUp = procedure(Button: TMouseButton; Shift: TShiftState; x, y: integer) of
object;
TState = (fplotted, fjoined);
TGraph = class;
TPlots = class;
TPoints = class(Tlist)
private
fplots: TPlots;
fptcolor, fcrvcolor: TColor;
fstate: set of Tstate;
procedure fPlot;
procedure fJoin;
protected
function Get(index: integer): PPoint;
public
procedure Plot;
procedure Join;
constructor Create(aplots: TPlots);
function Add(x, y: integer): PPoint;
procedure HideDots;
procedure HideJoins;
procedure Clear; override;
property CurveColor: Tcolor read fcrvcolor write fcrvColor;
property DotColor: Tcolor read fptcolor write fptColor;
property Items[index: integer]: PPoint read Get; default;
end;
TPlots = class(Tlist)
private
fgraph: TGraph;
protected
function Get(index: integer): TPoints;
public
constructor Create(agraph: TGraph);
function Add: TPoints;
procedure Clear; override;
procedure PlotAllDots;
procedure PlotAllJoins;
procedure HideAllDots;
procedure HideAllJoins;
property Items[index: integer]: TPoints read Get; default;
end;
TGraph = class(TGraphicControl)
private
faxcolor, fbkcolor, fgridcolor: Tcolor;
fMouseDown: TOnMouseDown;
fMouseMove: TOnMouseMove;
fMouseUp: TOnMouseUp;
fspc: extended;
ldiv, sdiv: integer;
xaxis, yaxis: integer;
xlc, ylc: integer;
fmag: integer;
fplots: TPlots;
function Translate(x, y: integer): Tpoint;
function GetScale: Extended;
procedure DrawGrid;
procedure DrawAxes;
procedure GetXLineRect(y: integer; var arect: trect);
procedure GetYLineRect(x: integer; var arect: trect);
procedure SetGridColor(acolor: Tcolor);
procedure SetBackColor(acolor: Tcolor);
procedure SetAxisColor(acolor: TColor);
protected
procedure loaded; override;
procedure Paint; override;
{procedure MsgHandler(var msg:TMessage);}
procedure MouseDown(Button: TMouseButton; shift: TShiftState; x, y: integer);
override;
procedure MouseMove(shift: TShiftState; x, y: integer); override;
procedure MouseUp(Button: TMouseButton; shift: TShiftState; x, y: integer);
override;
public
constructor Create(AComponent: TComponent); override;
destructor Destroy; override;
procedure OffSetAxes(x, y: integer);
procedure ResetAxes;
procedure Zoom(mag: integer);
property Plots: TPlots read fplots;
published
property OnMouseDown: TOnMouseDown read fMouseDown write fMouseDown;
property OnMouseMove: TOnMouseMove read fMouseMove write fMouseMove;
property OnMouseUp: TOnMouseUp read fMouseUp write fMouseUp;
property GridColor: Tcolor read fgridcolor write SetGridColor;
property BackColor: Tcolor read fbkcolor write SetBackColor;
property AxisColor: Tcolor read faxcolor write SetAxisColor;
property Scale: extended read GetScale;
property ZoomFactor: integer read fmag;
end;
procedure Register;
implementation
procedure TGraph.MouseDown(Button: TMouseButton; shift: TShiftState; x, y: integer);
var
tp: Tpoint;
begin
tp.x := x - left;
tp.y := y - top;
tp.x := trunc(tp.x / fspc - yaxis);
tp.y := trunc(xaxis - tp.y / fspc);
if (assigned(fMouseDown)) then
fMouseDown(button, shift, tp.x, tp.y);
inherited;
end;
procedure TGraph.MouseMove(shift: TShiftState; x, y: integer);
var
tp: Tpoint;
begin
tp.x := x - left;
tp.y := y - top;
tp.x := trunc(tp.x / fspc - yaxis);
tp.y := trunc(xaxis - tp.y / fspc);
if (assigned(fMousemove)) then
fMousemove(shift, tp.x, tp.y);
inherited;
end;
procedure TGraph.MouseUp(Button: TMouseButton; shift: TShiftState; x, y: integer);
var
tp: Tpoint;
begin
tp.x := x - left;
tp.y := y - top;
tp.x := trunc(tp.x / fspc - yaxis);
tp.y := trunc(xaxis - tp.y / fspc);
if (assigned(fMouseUp)) then
fMouseup(button, shift, tp.x, tp.y);
inherited;
end;
constructor TPoints.Create(aplots: TPlots);
begin
if aplots = nil then
raise Exception.Create('Not a valid Graph object.');
fplots := aplots;
end;
constructor TPlots.Create(agraph: Tgraph);
begin
if agraph = nil then
raise Exception.Create('Not a valid Graph object.');
fgraph := agraph;
end;
procedure TPoints.HideDots;
begin
fstate := fstate - [fplotted];
end;
procedure TPoints.HideJoins;
begin
fstate := fstate - [fjoined];
end;
procedure TPoints.Plot;
begin
fstate := fstate + [fplotted];
fplots.fgraph.invalidate;
end;
procedure TPoints.fPlot;
var
i: integer;
tmp: tpoint;
begin
if count <= 0 then
exit;
with fplots.fgraph do
begin
canvas.pen.color := fptcolor;
canvas.pen.width := 1;
for i := 0 to count - 1 do
begin
tmp := Translate(items[i].x, items[i].y);
canvas.Ellipse(rect(tmp.x - 1, tmp.y - 1, tmp.x + 1, tmp.y + 1));
end;
end;
end;
procedure TPoints.Join;
begin
fstate := fstate + [fjoined];
fplots.fgraph.invalidate;
end;
procedure TPoints.fJoin;
var
i: integer;
tmp: tpoint;
begin
if count <= 0 then
exit;
with fplots.fgraph do
begin
canvas.pen.color := fcrvcolor;
canvas.pen.width := 1;
tmp := Translate(items[0].x, items[0].y);
canvas.moveto(tmp.x, tmp.y);
for i := 1 to count - 1 do
begin
tmp := Translate(items[i].x, items[i].y);
canvas.lineto(tmp.x, tmp.y);
end;
end;
end;
procedure TPlots.PlotAllDots;
var
i: integer;
begin
for i := 0 to count - 1 do
items[i].Plot;
end;
procedure TPlots.PlotAllJoins;
var
i: integer;
begin
for i := 0 to count - 1 do
items[i].join
end;
procedure TPlots.HideAllDots;
var
i: integer;
inv: boolean;
begin
inv := false;
for i := 0 to count - 1 do
if (fplotted in items[i].fstate) then
begin
items[i].fstate := items[i].fstate - [fplotted];
inv := true;
end;
if inv then
fgraph.invalidate;
end;
procedure TPlots.HideAllJoins;
var
i: integer;
inv: boolean;
begin
inv := false;
for i := 0 to count - 1 do
if (fjoined in items[i].fstate) then
begin
items[i].fstate := items[i].fstate - [fjoined];
inv := true;
end;
if inv then
fgraph.invalidate;
end;
function TPlots.Get(index: integer): TPoints;
begin
result := TPoints(inherited Get(index));
end;
function TPlots.Add: TPoints;
begin
result := TPoints.create(self);
inherited Add(result);
end;
procedure TPlots.Clear;
var
i: integer;
tmp: Tpoints;
begin
for i := 0 to count - 1 do
begin
tmp := items[i];
freeandnil(tmp);
end;
inherited;
end;
procedure TPoints.Clear;
var
i: integer;
begin
for i := 0 to count - 1 do
dispose(items[i]);
inherited;
end;
function TPoints.Get(index: integer): PPoint;
begin
result := PPoint(inherited Get(index));
end;
function TPoints.Add(x, y: integer): PPoint;
begin
new(result);
result.x := x;
result.y := y;
inherited Add(result);
end;
function TGraph.GetScale: extended;
begin
if fspc result := sdiv / fspc
else
result := 1;
end;
destructor TGraph.Destroy;
begin
freeandnil(fplots);
inherited;
end;
constructor TGraph.Create(AComponent: TComponent);
begin
fplots := TPlots.create(self);
fmag := 100;
fbkcolor := clwhite;
faxcolor := clnavy;
fgridcolor := RGB(214, 244, 254);
ldiv := 10;
sdiv := 5;
fspc := 1;
inherited;
end;
procedure TGraph.GetXLineRect(y: integer; var arect: trect);
begin
arect.left := left;
arect.right := arect.left + width;
arect.top := top + trunc(y * fspc);
arect.bottom := arect.top + 2;
end;
procedure TGraph.GetYLineRect(x: integer; var arect: trect);
begin
arect.top := top;
arect.bottom := arect.top + height;
arect.left := left + trunc(x * fspc);
arect.right := arect.left + 2;
end;
procedure TGraph.SetGridColor(acolor: Tcolor);
begin
fgridcolor := acolor;
Invalidate;
end;
procedure TGraph.SetBackColor(acolor: Tcolor);
begin
fbkcolor := acolor;
Invalidate;
end;
procedure TGraph.SetAxisColor(acolor: TColor);
begin
faxcolor := acolor;
Invalidate;
end;
procedure TGraph.Zoom(mag: integer);
begin
if mag <= 0 then
mag := 1;
if mag > 100000 then
mag := 100000;
fspc := (mag / 20);
if fspc > 1 then
fspc := trunc(fspc);
fmag := mag;
xlc := Trunc(width / fspc);
ylc := Trunc(height / fspc);
xaxis := Trunc(ylc / 2);
yaxis := Trunc(xlc / 2);
Invalidate;
end;
function TGraph.Translate(x, y: integer): Tpoint;
begin
result.x := trunc((x + yaxis) * fspc);
result.y := trunc((xaxis - y) * fspc);
end;
procedure TGraph.loaded;
begin
Zoom(fmag);
end;
procedure TGraph.ResetAxes;
begin
Zoom(fmag);
end;
procedure TGraph.OffSetAxes(x, y: integer);
var
tmp: trect;
tmpx, tmpy: integer;
begin
canvas.Pen.color := faxcolor;
canvas.Pen.Width := 1;
tmpx := xaxis;
tmpy := yaxis;
xaxis := xaxis - y;
yaxis := yaxis + x;
if (tmpx = xaxis) and (tmpy = yaxis) then
exit;
GetXlineRect(tmpx, tmp);
InvalidateRect(parent.handle, @tmp, false);
GetYlineRect(tmpy, tmp);
InvalidateRect(parent.handle, @tmp, false);
GetXlineRect(xaxis, tmp);
InvalidateRect(parent.handle, @tmp, false);
GetYlineRect(yaxis, tmp);
InvalidateRect(parent.handle, @tmp, false);
end;
procedure TGraph.DrawAxes;
begin
canvas.Pen.color := faxcolor;
canvas.Pen.Width := 1;
canvas.MoveTo(0, trunc(fspc * xaxis));
canvas.lineto(width, trunc(fspc * xaxis));
canvas.MoveTo(trunc(fspc * yaxis), 0);
canvas.lineto(trunc(fspc * yaxis), height);
end;
procedure TGraph.DrawGrid;
var
i, t: integer;
t1, t2: Tpoint;
begin
i := 0;
t := 0;
canvas.pen.color := fbkcolor;
canvas.Brush.color := fbkcolor;
canvas.rectangle(0, 0, width, height);
canvas.Pen.color := fgridcolor;
canvas.Pen.Width := 1;
while i <= width do
begin
if (t mod ldiv) = 0 then
canvas.pen.width := 2
else
canvas.pen.width := 1;
t1.x := i;
t1.y := 0;
canvas.moveto(t1.x, t1.y);
t2.x := i;
t2.y := height;
canvas.lineto(t2.x, t2.y);
i := i + max(trunc(fspc), sdiv);
t := t + 1;
end;
i := 0;
t := 0;
while i <= height do
begin
if (t mod ldiv) = 0 then
canvas.pen.width := 2
else
canvas.pen.width := 1;
t1.x := 0;
t1.y := i;
canvas.moveto(t1.x, t1.y);
t2.x := width;
t2.y := i;
canvas.lineto(t2.x, t2.y);
i := i + max(trunc(fspc), sdiv);
t := t + 1;
end;
end;
procedure TGraph.Paint;
var
i: integer;
begin
DrawGrid;
for i := 0 to fplots.count - 1 do
begin
if (fplotted in fplots[i].fstate) then
fplots[i].fplot;
if fjoined in fplots[i].fstate then
fplots[i].fjoin;
end;
DrawAxes;
end;
procedure Register;
begin
RegisterComponents('My Components', [TGraph]);
end;
end.
2010. szeptember 26., vasárnap
How to restructure a TTable
Problem/Question/Abstract:
How to restructure a TTable
Answer:
unit TTableRestruct;
{Freeware by Brett W. Fleming 1999
SetOperation method added by Bill Todd 1999}
interface
uses
BDE, DbTables;
type
TTableRestructure = class(TObject)
private
function GetField(Index: Integer): PFLDDesc;
function GetFieldLength(Index: Integer): Word;
function GetFieldName(Index: Integer): string;
function GetFieldType(Index: Integer): Word;
function GetFieldUnits(Index: Integer): Word;
function GetOperation(Index: Integer): PCROpType;
procedure SetFieldLength(Index: Integer; const Value: Word);
procedure SetFieldType(Index: Integer; const Value: Word);
procedure SetFieldUnits(Index: Integer; const Value: Word);
procedure SetFieldName(Index: Integer; const Value: string);
procedure DetailError(ErrorCode: DbiResult);
procedure SetOperation(Index: Integer; OpType: PCROpType);
protected
Fields: PFLDDesc;
Operations: PCROpType;
LocalFieldCount: Integer;
procedure DestroyFieldDescriptors;
public
constructor Create;
destructor Destroy; override;
function AddField: Integer;
function DeleteField(Index: Integer): Boolean;
function FindField(Name: string): Integer;
procedure LoadTableStructure(Table: TTable);
procedure SaveTableStructure(Table: TTable);
procedure PrintStructure;
property FieldCount: Integer read LocalFieldCount;
property FieldLength[Index: Integer]: Word read GetFieldLength write SetFieldLength;
property FieldName[Index: Integer]: string read GetFieldName write SetFieldName;
property FieldType[Index: Integer]: Word read GetFieldType write SetFieldType;
property FieldUnits[Index: Integer]: Word read GetFieldUnits write SetFieldUnits;
property Field[Index: Integer]: PFLDDesc read GetField;
property Operation[Index: Integer]: pCROpType read GetOperation write SetOperation;
end;
implementation
uses
SysUtils, Dialogs;
{Purpose:
To add a new field to the table
Parameters:
None
Effects:
A new blank field descriptor is created and added to the internal list of
Field Descriptors which is reallocated to accomodate the new field
Returns:
Index of the new field in the array, or -1 if the operation failed}
function TTableRestructure.AddField: Integer;
var
NewField: PFLDDesc;
NewOperation: pCROpType;
begin
Result := -1;
if (Fields <> nil) then
begin
ReallocMem(Fields, (LocalFieldCount + 1) * SizeOf(FLDDesc));
ReallocMem(Operations, (LocalFieldCount + 1) * SizeOf(CROpType));
{Move to the new field and empty it out}
NewField := Fields;
Inc(NewField, LocalFieldCount);
FillChar(NewField^, SizeOf(FLDDesc), 0);
NewField^.iFldNum := LocalFieldCount + 1;
{Move to the new operation and set it to add}
NewOperation := Operations;
Inc(NewOperation, LocalFieldCount);
NewOperation^ := crAdd;
Inc(LocalFieldCount);
{Return the new fields index}
Result := LocalFieldCount - 1;
end;
end;
{Purpose: To create a new instance of this class and initialize it's data
Parameters: None
Effects: See purpose}
constructor TTableRestructure.Create;
begin
Fields := nil;
Operations := nil;
LocalFieldCount := 0;
end;
{Purpose:
To delete a specific field from the tables description
Parameters:
Index - Index of the field that is to be removed
Effects:
The field is removed from the array of Field Descriptors and the memory that contains the list
is reallocated
Returns:
True if the operation was successfull, False otherwise}
function TTableRestructure.DeleteField(Index: Integer): Boolean;
var
FieldBefore,
FieldAfter: PFLDDesc;
OperationBefore,
OperationAfter: PCROpType;
begin
Result := False;
if (Fields <> nil) and (LocalFieldCount > 0) and (Index >= 0) and (Index < LocalFieldCount) then
begin
{Find the spot before and after the field to delete}
FieldBefore := Fields;
FieldAfter := Fields;
Inc(FieldBefore, Index);
Inc(FieldAfter, Index + 1);
{Find the spot before and after the operation to delete}
OperationBefore := Operations;
OperationAfter := Operations;
Inc(OperationBefore, Index);
Inc(OperationAfter, Index + 1);
{Now copy the data over the field to delete}
Move(FieldAfter^, FieldBefore^, (LocalFieldCount - Index) * SizeOf(FLDDesc));
Move(OperationAfter^, OperationBefore^, (LocalFieldCount - Index) * SizeOf(CROpType));
{Now shrink the allocated memory}
Dec(LocalFieldCount);
ReallocMem(Fields, LocalFieldCount * SizeOf(FLDDesc));
ReallocMem(Operations, LocalFieldCount * SizeOf(CROpType));
Result := True;
end;
end;
{Purpose: To destroy an instance of this class and any memory that was allocated
Parameters: None
Effects: See purpose}
destructor TTableRestructure.Destroy;
begin
DestroyFieldDescriptors;
end;
{Purpose: To destroy an array of field descriptors
Parameters: None
Effects: The Field Descriptors are freed, and the pointer set to nil}
procedure TTableRestructure.DestroyFieldDescriptors;
begin
if Fields <> nil then
begin
FreeMem(Fields);
Fields := nil;
FreeMem(Operations);
Operations := nil;
LocalFieldCount := 0;
end;
end;
{Purpose: To show the details of any Error returned by the BDE routines
Parameters: ErrorCode - Code returned byt the BDE
Effects: None}
procedure TTableRestructure.DetailError(ErrorCode: DbiResult);
var
ErrorInfo: DBIErrInfo;
ErrorString: string;
ErrorString2: string;
begin
if (ErrorCode <> dbiERR_NONE) then
begin
Check(DbiGetErrorInfo(True, ErrorInfo));
if (ErrorCode = ErrorInfo.iError) then
begin
ErrorString := 'Error Number: ' + IntToStr(ErrorInfo.iError) + #10 + #13;
ErrorString := ErrorString + 'Error Code: ' + string(ErrorInfo.szErrcode) + #10 + #13;
if (StrLen(ErrorInfo.szContext[1]) <> 0) then
ErrorString := ErrorString + 'Context1: ' + string(ErrorInfo.szContext[1]) + #10 + #13;
if (StrLen(ErrorInfo.szContext[2]) <> 0) then
ErrorString := ErrorString + 'Context2: ' + string(ErrorInfo.szContext[2]) + #10 + #13;
if (StrLen(ErrorInfo.szContext[3]) <> 0) then
ErrorString := ErrorString + 'Context3: ' + string(ErrorInfo.szContext[3]) + #10 + #13;
if (StrLen(ErrorInfo.szContext[4]) <> 0) then
ErrorString := ErrorString + 'Context4: ' + string(ErrorInfo.szContext[4]) + #10 + #13;
end
else
begin
SetLength(ErrorString2, dbiMaxMsgLen + 1);
Check(DbiGetErrorString(ErrorCode, PChar(ErrorString2)));
SetLength(ErrorString2, StrLen(PChar(ErrorString2)));
ErrorString := ErrorString + ErrorString2;
end;
ShowMessage(ErrorString);
end;
end;
{Purpose: To find a particular field's index by it's name
Parameters: Name - Name of the field to find in the current list of fields
Effects: None
Returns: Index of the field if found, or -1 if not found}
function TTableRestructure.FindField(Name: string): Integer;
var
Index: Integer;
begin
Result := -1;
Index := FieldCount - 1;
while (Index >= 0) and (Result < 0) do
begin
if CompareText(FieldName[Index], Name) = 0 then
Result := Index;
Dec(Index);
end;
end;
{Purpose: To return a pointer to a specified Field Descriptor
Parameters: Index - Index of the field descriptor
Effects: None
Returns: Pointer to a Field Descriptor or nil if Index isn't valid}
function TTableRestructure.GetField(Index: Integer): PFLDDesc;
begin
Result := nil;
if (Fields <> nil) and (Index >= 0) and (Index < LocalFieldCount) then
begin
Result := Fields;
Inc(Result, Index);
end;
end;
{Purpose: Get method for the FieldLength property
Parameters: Index - Index of a field descriptor
Effects: None
Returns: Length of the specified field or 0 if not field not found}
function TTableRestructure.GetFieldLength(Index: Integer): Word;
var
Field: PFLDDesc;
begin
Result := 0;
Field := GetField(Index);
if Field <> nil then
Result := Field^.iLen;
end;
{Purpose: Get method for the FieldName property
Parameters: Index - Index of a field descriptor
Effects: None
Returns: Name of the specified field or '' if not field not found}
function TTableRestructure.GetFieldName(Index: Integer): string;
var
Field: PFLDDesc;
begin
Result := '';
Field := GetField(Index);
if Field <> nil then
Result := string(Field^.szName);
end;
{Purpose: Get method for the FieldType property
Parameters: Index - Index of a field descriptor
Effects: None
Returns: Type of the specified field or -1 if not field not found}
function TTableRestructure.GetFieldType(Index: Integer): Word;
var
Field: PFLDDesc;
begin
Result := 0;
Field := GetField(Index);
if Field <> nil then
Result := Field^.iFldType;
end;
{Purpose: Get method for the FieldUnits property
Parameters: Index - Index of a field descriptor
Effects: None
Returns: Units1 of the specified field or -1 if not field not found}
function TTableRestructure.GetFieldUnits(Index: Integer): Word;
var
Field: PFLDDesc;
begin
Result := 0;
Field := GetField(Index);
if Field <> nil then
Result := Field^.iUnits1;
end;
{Purpose: To get a pointer to an operation type
Parameters: Index - Index of the operation that is desired
Effects: None
Returns: See purpose}
function TTableRestructure.GetOperation(Index: Integer): PCROpType;
begin
Result := nil;
if (Index >= 0) and (Index < FieldCount) then
begin
Result := Operations;
Inc(Result, Index);
end;
end;
{Purpose: To assign a new operation.
Parameters: Index - Index of the operation that is desired
Effects: None
Returns: None}
procedure TTableRestructure.SetOperation(Index: Integer; OpType: PCROpType);
var
ModifyOperations: PCROpType;
begin
ModifyOperations := Operations;
Inc(ModifyOperations, Index);
ModifyOperations^ := crModify;
end;
{Purpose:
To load in the table structure of the specified table
Parameters:
Table - Table whose structure will be loaded into memory
Effects:
Any previous structure is destroyed and replaced by the new structure if the table could be
opened successfully}
procedure TTableRestructure.LoadTableStructure(Table: TTable);
var
Index: Integer;
Field: PFLDDesc;
begin
DestroyFieldDescriptors;
if (Table <> nil) then
begin
Table.Open;
LocalFieldCount := Table.FieldCount;
Fields := AllocMem(LocalFieldCount * SizeOf(FLDDesc));
try
Operations := AllocMem(LocalFieldCount * SizeOf(CROpType));
try
FillChar(Operations^, LocalFieldCount * SizeOf(CROpType), crNOOP);
Check(DbiGetFieldDescs(Table.Handle, Fields));
Field := Fields;
for Index := 1 to LocalFieldCount do
begin
Field^.iFldNum := Index;
Inc(Field);
end;
except
FreeMem(Operations);
Operations := nil;
raise;
end;
except
FreeMem(Fields);
Fields := nil;
raise;
end;
end;
end;
{Purpose: No real purpose, other than for dumping out the current field data
Parameters: None
Effects: None}
procedure TTableRestructure.PrintStructure;
var
Index: Integer;
Field: pFLDDesc;
Op: PCROpType;
Item: string;
List: string;
begin
List := '# - Op - Type - Name' + #10#13;
Field := Fields;
Op := Operations;
for Index := 0 to LocalFieldCount - 1 do
begin
Item := Format('%d - %x - %d - %s', [Field^.iFldNum, Byte(Op^), FieldType[Index], Field^.szName]);
List := List + Item + #10 + #13;
Inc(Field);
Inc(Op);
end;
ShowMessage(List);
end;
{Purpose:
To modify a existing table to match the given field descriptors
Parameters:
Table - Table whose structure will be replaced by the structure in memory
Effects:
The table's structure is modified to match the current structure in memory. Once this is done, changes
can not be undone.}
procedure TTableRestructure.SaveTableStructure(Table: TTable);
var
TableDesc: CRTblDesc;
hDb: hDBIDb;
begin
Table.Open;
FillChar(TableDesc, sizeof(TableDesc), 0);
{Get the database handle from the table's cursor handle...}
Check(DbiGetObjFromObj(hDBIObj(Table.Handle), objDATABASE, hDBIObj(hDb)));
StrPCopy(TableDesc.szTblName, Table.TableName);
TableDesc.iFldCount := LocalFieldCount;
TableDesc.pecrFldOp := Operations;
TableDesc.pFldDesc := Fields;
Table.Close;
DetailError(DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, False));
end;
{Purpose: Set method for the FieldLength property
Parameters: Index - Index of the Field to modify / Value - New length of the field
Effects: The field descriptor is modified to reflect the change}
procedure TTableRestructure.SetFieldLength(Index: Integer; const Value: Word);
var
Field: PFLDDesc;
Operation: PCROpType;
begin
Field := GetField(Index);
if Field <> nil then
begin
Field^.iLen := Value;
Operation := GetOperation(Index);
if Operation^ <> crAdd then
Operation^ := crMODIFY;
end;
end;
{Purpose: Set method for the FieldName property
Parameters: Index - Index of the Field to modify / Value - New Name of the field
Effects: The field descriptor is modified to reflect the change}
procedure TTableRestructure.SetFieldName(Index: Integer; const Value: string);
var
Field: PFLDDesc;
Operation: PCROpType;
begin
Field := GetField(Index);
if Field <> nil then
begin
StrPCopy(Field^.szName, Value);
Operation := GetOperation(Index);
if Operation^ <> crAdd then
Operation^ := crMODIFY;
end;
end;
{Purpose: Set method for the FieldType property
Parameters: Index - Index of the Field to modify / Value - New Type of the field
Effects: The field descriptor is modified to reflect the change}
procedure TTableRestructure.SetFieldType(Index: Integer; const Value: Word);
var
Field: PFLDDesc;
Operation: PCROpType;
begin
Field := GetField(Index);
if Field <> nil then
begin
Field^.iFldType := Value;
Operation := GetOperation(Index);
if Operation^ <> crAdd then
Operation^ := crMODIFY;
end;
end;
{Purpose: Set method for the FieldUnits property
Parameters: Index - Index of the Field to modify / Value - New units of the field
Effects: The field descriptor is modified to reflect the change}
procedure TTableRestructure.SetFieldUnits(Index: Integer; const Value: Word);
var
Field: PFLDDesc;
Operation: PCROpType;
begin
Field := GetField(Index);
if Field <> nil then
begin
Field^.iUnits1 := Value;
Operation := GetOperation(Index);
if Operation^ <> crAdd then
Operation^ := crMODIFY;
end;
end;
end.
2010. szeptember 25., szombat
SQL Super INSERT/UPDATE Macro Class
Problem/Question/Abstract:
SQL Super INSERT/UPDATE Macro Class
Answer:
Ever got tired of dynamically generating SQL insert and update statements ?. Lots of virtually unreadable constructs such as things like .. (assume Data1:string Data2: integer Data3:TdateTime)
SqlCmd := 'insert into MyTable (Field1,Field2,Field2) values (' +
QuotedStr(Data1) + ',' + IntToStr(Data2) + ',' + 'to_date('
+ QuotedStr(FormatdateTime('dd/mm/yyyy',Data3)) + ','
+ QuotedStr('dd/mm/yyyy') + '))';
{Horrible! and it gets worse as the column count gets higher}
This Class takes all the sweat out of this.
A single TQuery is created that handles ALL the SELECT,INSERT,UPDATE and DELETE operations.
FEATURES :
Support for ORACLE and MS-SQL (DateTimes are handled differently by these systems)
Would be grateful if anyone has Interbase,Informix or DB2 that can add functionality for these systems.
DebugMode which display the errant SQL statement and allows modification to correct it. The modified code can be cut to clipboard and is automatically saved to file LastSqlErr.sql on closing debug window.
Automatic error message dialogs or user handled errors via property LastErrorMess and LastSqlCommand.
Single value SQL select returns implemented AsString,AsInteger etc.
INSERT,UPDATE and DELETE super macro methods.
BASIC BUILDING PRIMITIVE FUNCTIONS :
There are a few primitive functions that are used by the Class, but are user callable if required.
function SqlDateToStr(const Dte : TDateTime) : string;
function StrToSqlDate(const DateStr : string) : TDateTime;
These functions are used to convert MS-SQL DateTimes to String and TDateTime. MS-SQL DateTimes are in format 'dd-MMM-yyyy hh:nn:ss.zzz'
function sqlStr(...) : string;
This function is a super set of Borlands QuotedStr(). It has many overloads allowing the conversion of all required datatypes to a SQL string. Str quotes and trailing commas are handled (with comma being TRUE by default). One interesting oveload is an argument of "array of variant" which allows you to specify
an array of differing types to be converted to a SQL string list.
Examples:
sqlStr('Harry'); // Returns 'Harry', (Quotes are inculded)
sqlStr(345.55); // Returns 345.55, (No Quotes)
sqlStr(['GTR',8,Now]); // 'GTR',8,'23-Oct-2002 13:44:23.000'
CLASS CONSTRUCTOR
Create(const DatabaseName : string; DatabaseSystemType : TSQLSystem);
Used to create an instance of the object.
eg.
var MySql : TSQLCommand;
MySql := TSQLCommand.Create(MyDb.DatabaseName,sysOracle); // or
MySql := TSQLCommand.Create('HELPDESK',sysOracle)
DatabaseName is the DatabaseName of an open TDatabase Connection
CLASS PROPERTIES :
SqlQuery : TQuery - Not normally used but can be set as a TDatasource DataSet property for TDBGrids etc.
LastErrorMess : string - Last Error message of a failed SQL statement
LastSQLCommand : string - Last SQL statement of failed SQL
AutoErrorMessage : boolean - Auto display Error Dialogs [Yes/No]
DebugMode : boolean - Pops up Errant SQL statement and allows mods
TerminateOnError : boolean - Terminate app is SQL staement error [Yes/No]
DatabaseName : string - Set by constructor Create(), but can be changed at runtime
DatabaseSystem : TSQLSystem - Set by constructor Create(), but can be Changed at run time
CLASS METHODS :
MISCELLANEOUS
SystemTime : TDateTime - Returns System DateTime of the Database (System independent)
SystemUser : string - Returns Logged in Username of the Database (System independent)
SINGLE VALUE SELECT RETURNS
These function methods are designed to return a single value from a SQL query, such as AsString('select name from emp where id = 990') All the below methods have an alternate overloaded version that takes a select string + array of const formatting options. eg. AsString('select name from emp where id = %d',[990])
See Borlands Format() function for more info.
AsString(const SQLStatement : string) : string
AsInteger(const SQLStatement : string) : integer
AsFloat(const SQLStatement : string) : double
AsDateTime(const SQLStatement : string) : TDateTime
FREE FORM USER COMMANDS
These methods allow for ad-hoc user SQL constructs. The property SqlQury may be used with the commands after Open for Fields retieval or display in a TDBGrid by setting a TDataSource Dataset property to SqlQuery.
Once again FreeFormOpen and Exec have an alternate overloaded option of select string + array of const formatting options.
FreeFormOpen(const SQLStatement : string) : boolean - Used to open a user ad-hoc query
FreeFormClose - Used to close the ad-hoc query as opened by FreeFormOpen
Exec(const SQLStatement : string) : boolean - Used for non cursor queries such as UPDATE etc.
DBMS MACRO COMMANDS
These commands take the sting out of SQL inserts and updates. The Column names are supplied as an array of strings. The update/insert values are specified in an array of variant. Specify tablename and where clause if required and the method will correctly format the SQL statement for the relevant system and execute it.
Insert(ColNames : array of string; Values : array of variant; const TableName : string) : boolean
Update(ColNames : array of string; Values : array of variant; const WhereClause : string; const TableName : string) : boolean
Delete(const WhereClause : string; const TableName : string) : boolean
(Not that clever - here for completeness can also be achieved via Exec('delete from emp where id = 99') )
SIMPLIFIED EXAMPLE OF USE :
procedure MyUpdates;
var
Name: string;
SQL: TSQLCommand;
ID: integer;
begin
SQL := TSQL.Command.Create('MYBASE', sysOracle);
SQL.DebugMode := true;
Label1.Caption := SQL.SystemUser;
Label2.Caption := SQL.SystemTime;
ID := SQL.AsInteger('select ID from EMP where TAXNUM = 345');
Name := SQL.AsString('select NAME from EMP where ID = %d', [ID]);
SQL.Insert(['NAME', 'TAXDATE', 'ID', 'FLAG'],
[Name, Now, ID, 0], 'NEWTAXTAB');
SQL.Update(['TAXDATE', 'FLAG'],
[Now, 5],
'NAME = ' + sqlStr(Name, false), OLDTAXTAB);
SQL.Delete('FLAG = 99', 'ARCTAXTAB');
SQL.FreeFormOpen('select * from EMP);
Label3.Caption := SQL.SqlQuery.Fields[0].AsString;
MyDataSource.DataSet := SQL.SqlQuery;
...
...
SQL.FreeFormClose;
SQL.Free;
end;
Of course the return values of the inserts etc should be checked for TRUE and FALSE, but as stated it is a simplified example for clarity.
unit MahSql;
// =============================================================================
// Mike Heydon Sep 2002
// SQL programming aids
// There must be an open TDatabase connection
// =============================================================================
interface
uses Forms, StdCtrls, SysUtils, Dialogs, DBTables, Controls, DateUtils,
ComCtrls, ExtCtrls, Buttons, Variants;
// NOTE : Uses DateUtils and Variants are Delphi 6 - remove for lower versions
type
TSQLSystem = (sysOracle, sysMsSql); // Informix,DB2 users help appreciated here.
{TSQLCOMMAND CLASS}
TSQLCommand = class(TObject)
protected
procedure ShowDebug;
function OpenQuery(const Command: string;
CheckNull: boolean = true): boolean; virtual;
function ExecQuery(const Command: string): boolean; virtual;
function ExecFunc(const Func: string): string;
private
Memo: TMemo;
Form: TForm;
Status: TStatusBar;
Panel: TPanel;
btnRetry,
btnClose: TBitBtn;
FDatabaseSystem: TSQLSystem;
FDebugID: char;
FTerminateOnError,
FDebugMode,
FAutoErrorMessage: boolean;
FLastSQLCommand,
FLastErrorMess: string;
Query: TQuery;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure RetryClick(Sender: TObject);
procedure SetDatabaseName(const NewValue: string);
function GetDatabaseName: string;
public
constructor Create(const DatabaseName: string;
DatabaseSystemType: TSQLSystem);
destructor Destroy; override;
// Misc functions
function SystemTime: TDateTime;
function SystemUser: string;
// Value returns calls - Always takes field[0] regardles of select cmd
// Optional overload with formating eg.
// AsString('select * from tab1 where N=%s and D=%d,['Fred',99]);
function AsString(const SQLStatement: string): string; overload;
function AsString(const SQLStatement: string;
FormatArguments: array of const): string; overload;
function AsInteger(const SQLStatement: string): integer; overload;
function AsInteger(const SQLStatement: string;
FormatArguments: array of const): integer; overload;
function AsFloat(const SQLStatement: string): double; overload;
function AsFloat(const SQLStatement: string;
FormatArguments: array of const): double; overload;
function AsDateTime(const SQLStatement: string): TDateTime; overload;
function AsDateTime(const SQLStatement: string;
FormatArguments: array of const): TDateTime; overload;
// Free Form
function FreeFormOpen(const SQLStatement: string): boolean; overload;
function FreeFormOpen(const SQLStatement: string;
FormatArguments: array of const): boolean; overload;
procedure FreeFormClose;
function Exec(const SQLStatement: string): boolean; overload;
function Exec(const SQLStatement: string;
FormatArguments: array of const): boolean; overload;
// DBMS Inserts and Updates
function Insert(ColNames: array of string;
Values: array of variant;
const TableName: string): boolean;
function Update(ColNames: array of string;
Values: array of variant;
const WhereClause: string;
const TableName: string): boolean;
function Delete(const WhereClause: string;
const TableName: string): boolean;
// Properties
property SqlQuery: TQuery read Query;
property LastErrorMess: string read FLastErrorMess;
property LastSQLCommand: string read FLastSQLCommand;
property AutoErrorMessage: boolean read FAutoErrorMessage
write FAutoErrorMessage;
property DebugMode: boolean read FDebugMode write FDebugMode;
property TerminateOnError: boolean read FTerminateOnError
write FTerminateOnError;
property DatabaseName: string read GetDatabaseName
write SetDatabaseName;
property DatabaseSystem: TSQLSystem read FDatabaseSystem
write FDatabaseSystem;
end;
// ===================================
// Primitive Class and User Functions
// ===================================
// Date routines
function SqlDateToStr(const Dte: TDateTime): string;
function StrToSqlDate(const DateStr: string): TDateTime;
// Quoted SQL string conversion routines
function sqlStr(Values: array of variant;
DateTimeType: TSQLSystem = sysOracle): string; overload;
function sqlStr(Dte: TDateTime; DateTimeType: TSQLSystem;
AddComma: boolean = true): string; overload;
function sqlStr(Dbl: double; NumDecimals: integer;
AddComma: boolean = true): string; overload;
function sqlStr(const St: string; AddComma: boolean = true): string; overload;
function sqlStr(Num: integer; AddComma: boolean = true): string; overload;
function sqlStr(Flt: extended; AddComma: boolean = true): string; overload;
function sqlStr(Flt: extended; NumDecimals: integer;
AddComma: boolean = true): string; overload;
// -----------------------------------------------------------------------------
implementation
const
CrLf = #13#10; // Carriage Return / LineFeed pair
// =========================
// General Functions
// =========================
// ============================================
// Return an MS-SQL date compatable string
// ============================================
function SqlDateToStr(const Dte: TDateTime): string;
begin
Result := FormatdateTime('dd-MMM-yyyy hh:nn:ss.zzz', Dte);
end;
// ============================================
// Return an SQL date from string
// Format 'dd-MMM-yyyy hh:nn:ss.zzz'
// ============================================
function StrToSqlDate(const DateStr: string): TDateTime;
var
yyyy, dd, mm, hh, nn, ss, zzz: word;
MMM: string;
RetVar: TDateTime;
begin
mm := 0;
dd := StrToIntDef(copy(DateStr, 1, 2), 0);
MMM := UpperCase(copy(DateStr, 4, 3));
yyyy := StrToIntDef(copy(DateStr, 8, 4), 0);
hh := StrToIntDef(copy(DateStr, 13, 2), 0);
nn := StrToIntDef(copy(DateStr, 16, 2), 0);
ss := StrToIntDef(copy(DateStr, 19, 2), 0);
zzz := StrToIntDef(copy(DateStr, 22, 3), 0);
if MMM = 'JAN' then
mm := 1
else if MMM = 'FEB' then
mm := 2
else if MMM = 'MAR' then
mm := 3
else if MMM = 'APR' then
mm := 4
else if MMM = 'MAY' then
mm := 5
else if MMM = 'JUN' then
mm := 6
else if MMM = 'JUL' then
mm := 7
else if MMM = 'AUG' then
mm := 8
else if MMM = 'SEP' then
mm := 9
else if MMM = 'OCT' then
mm := 10
else if MMM = 'NOV' then
mm := 11
else if MMM = 'DEC' then
mm := 12;
if not TryEncodeDateTime(yyyy, mm, dd, hh, nn, ss, zzz, Retvar) then
RetVar := 0.0;
Result := Retvar;
end;
// =================================================
// SQL string convertors - QuotedStr() Super Set
// =================================================
// TDATETIME
function sqlStr(Dte: TDateTime; DateTimeType: TSQLSystem;
AddComma: boolean = true): string; overload;
var
RetVar: string;
begin
if DateTimeType = sysOracle then
RetVar := 'to_date(' +
QuotedStr(FormatdateTime('dd/mm/yyyy hh:nn:ss', Dte)) + ',' +
QuotedStr('DD/MM/YYYY HH24:MI:SS') + ')'
else
RetVar := QuotedStr(SqlDateToStr(Dte));
if AddComma then
RetVar := Retvar + ',';
Result := RetVar;
end;
// DOUBLE
function sqlStr(Dbl: double; NumDecimals: integer;
AddComma: boolean = true): string; overload;
var
Retvar: string;
begin
RetVar := FormatFloat('###########0.' +
StringOfChar('0', NumDecimals), Dbl);
if AddComma then
Retvar := Retvar + ',';
Result := RetVar;
end;
// STRING
function sqlStr(const St: string;
AddComma: boolean = true): string; overload;
var
Retvar: string;
begin
RetVar := QuotedStr(St);
if AddComma then
Retvar := RetVar + ',';
Result := RetVar;
end;
// INTEGER
function sqlStr(Num: integer; AddComma: boolean = true): string; overload;
var
RetVar: string;
begin
RetVar := IntToStr(Num);
if AddComma then
RetVar := Retvar + ',';
Result := RetVar;
end;
// EXTENDED
function sqlStr(Flt: extended; AddComma: boolean = true): string; overload;
var
Retvar: string;
begin
RetVar := FloatToStr(Flt);
if AddComma then
Retvar := Retvar + ',';
Result := RetVar;
end;
// EXTENDED WITH PRECICISION
function sqlStr(Flt: extended; NumDecimals: integer;
AddComma: boolean = true): string; overload;
var
Retvar: string;
begin
RetVar := FormatFloat('###########0.' +
StringOfChar('0', NumDecimals), Flt);
if AddComma then
Retvar := Retvar + ',';
Result := RetVar;
end;
// ARRAY OF VARIANT eg. [0,'Fred',45.44,'Married',Date]
function sqlStr(Values: array of variant;
DateTimeType: TSQLSystem = sysOracle): string;
var
RetVar: string;
i: integer;
VType: TVarType;
begin
RetVar := '';
for i := 0 to High(Values) do
begin
VType := VarType(Values[i]);
case VType of
varDate: RetVar := RetVar + sqlStr(TDateTime(Values[i]),
DateTimeType, false);
varInteger,
varSmallint,
varShortint,
varByte,
varWord,
varLongword,
varInt64: RetVar := RetVar + IntToStr(Values[i]);
varSingle,
varDouble,
varCurrency: RetVar := RetVar + FloatToStr(Values[i]);
varStrArg,
varOleStr,
varString: RetVar := RetVar + QuotedStr(Values[i]);
else
RetVar := RetVar + '????';
end;
RetVar := RetVar + ',';
end;
Delete(RetVar, length(RetVar), 1);
Result := Retvar;
end;
// =============================================================================
// TSQLCommand Class
// =============================================================================
// =========================
// Construct & Destroy
// =========================
constructor TSQLCommand.Create(const DatabaseName: string;
DatabaseSystemType: TSQLSystem);
begin
Query := TQuery.Create(nil);
Query.DatabaseName := DatabaseName;
FLastErrorMess := '';
FLastSQLCommand := '';
FAutoErrorMessage := false;
FDebugMode := false;
FTerminateOnError := false;
FDatabaseSystem := DatabaseSystemType;
end;
destructor TSQLCommand.Destroy;
begin
Query.Free;
end;
// =============================
// Property Get/Set Methods
// =============================
procedure TSQLCommand.SetDatabaseName(const NewValue: string);
begin
Query.Close;
Query.DatabaseName := NewValue;
end;
function TSQLCommand.GetDatabaseName: string;
begin
Result := Query.DatabaseName;
end;
// ==================================================
// Returns a string value from MS-SQL functions
// ==================================================
function TSQLCommand.ExecFunc(const Func: string): string;
var
Value: string;
begin
Value := '';
if OpenQuery(Func, false) then
begin
SetLength(Value, Query.RecordSize + 1);
Query.GetCurrentRecord(PChar(Value));
SetLength(Value, StrLen(PChar(Value)));
end;
Query.Close;
Result := Value;
end;
// =============================================================
// Show and Save Debug Statement if DebugMode = true - INTERNAL
// =============================================================
// Save on form close
procedure TSQLCommand.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Memo.Lines.SaveToFile(ExtractFilePath(Application.ExeName) + 'LastSqlErr.sql');
end;
// Retry click
procedure TSQLCommand.RetryClick(Sender: TObject);
begin
Query.SQL.Assign(memo.Lines);
try
if FDebugID = 'O' then
Query.Open
else
Query.ExecSql;
MessageDlg('SQL Command Ran OK', mtInformation, [mbOk], 0);
except
on E: Exception do
MessageDlg('SQL Command Failed' + CrLf + CrLf + E.Message, mtError, [mbOk], 0);
end;
end;
procedure TSQLCommand.ShowDebug;
var
FName: string;
begin
FName := ExtractFilePath(Application.ExeName) + 'LastSqlErr.sql';
Form := TForm.Create(nil);
Form.BorderIcons := Form.BorderIcons - [biMinimize];
Status := TStatusBar.Create(Form);
Status.Parent := Form;
Status.SimplePanel := true;
Status.SimpleText := ' ' + FName;
Form.Height := 350;
Form.Width := 600;
Form.Caption := 'SQL Error';
Form.Position := poScreenCenter;
Panel := TPanel.Create(Form);
Panel.Parent := Form;
Panel.Align := alTop;
Memo := TMemo.Create(Form);
Memo.Parent := Form;
Memo.Align := alClient;
Memo.Font.Name := 'Courier New';
Memo.Font.Size := 9;
Memo.Lines.Assign(Query.SQL);
btnClose := TBitBtn.Create(Form);
btnClose.Parent := Panel;
btnClose.Kind := bkClose;
btnClose.Left := Form.Width - 90;
btnClose.Top := 8;
btnClose.Anchors := [akRight, akBottom];
btnRetry := TBitBtn.Create(Form);
btnRetry.Parent := Panel;
btnRetry.Kind := bkRetry;
btnRetry.Left := 8;
btnRetry.Top := 8;
btnRetry.ModalResult := mrNone;
btnRetry.OnClick := RetryClick;
Panel.Align := alBottom;
Form.OnClose := FormClose;
Form.ShowModal;
Form.Free; // Free Form and all components in it
end;
// ===============================================
// Open the Query with error checking - INTERNAL
// ===============================================
function TSQLCommand.OpenQuery(const Command: string;
CheckNull: boolean = true): boolean;
var
Retvar,
NullValue: boolean;
begin
FDebugID := 'O';
Retvar := false;
Query.Close;
FLastSQLCommand := Command;
Query.SQL.Text := Command;
try
Query.Open;
if CheckNull then
NullValue := Query.EOF or Query.Fields[0].IsNull
else
NullValue := Query.EOF;
if NullValue then
begin
FLastErrorMess := 'No Records in DataSet';
if FAutoErrorMessage then
MessageDlg('Open Query Failed!' + CrLf + CrLf + FLastErrorMess, mtError,
[mbOk], 0);
end
else
Retvar := true;
except
on E: Exception do
begin
FLastErrorMess := E.Message;
if FAutoErrorMessage then
MessageDlg('Open Query Failed!' + CrLf + CrLf + E.Message, mtError, [mbOk],
0);
if FDebugMode then
ShowDebug;
if FTerminateOnError then
begin
Application.Terminate;
raise Exception.Create('');
end;
end;
end;
Result := Retvar;
end;
// ================================================
// Exec a query - UPDATE/INSERT etc - INTERNAL
// ================================================
function TSQLCommand.ExecQuery(const Command: string): boolean;
var
Retvar: boolean;
begin
FDebugID := 'E';
Retvar := false;
Query.Close;
FLastSQLCommand := Command;
Query.SQL.Text := Command;
try
Query.ExecSQL;
Retvar := true;
except
on E: Exception do
begin
FLastErrorMess := E.Message;
if FAutoErrorMessage then
MessageDlg('Exec Query Failed!' + CrLf + CrLf + E.Message, mtError, [mbOk],
0);
if FDebugMode then
ShowDebug;
if FTerminateOnError then
begin
Application.Terminate;
raise Exception.Create('');
end;
end;
end;
Result := Retvar;
end;
// ====================================================================
// Single Result sets with alternate overload of string/format array
// ====================================================================
// STRING
function TSQLCommand.AsString(const SQLStatement: string): string;
var
Retvar: string;
begin
Query.UniDirectional := true;
if OpenQuery(SQLStatement) then
begin
Retvar := Query.Fields[0].AsString;
Query.Close;
end
else
Retvar := '';
Result := Retvar;
end;
function TSQLCommand.AsString(const SQLStatement: string;
FormatArguments: array of const): string;
begin
Result := AsString(Format(SQLStatement, FormatArguments));
end;
// INTEGER
function TSQLCommand.AsInteger(const SQLStatement: string): integer;
var
Retvar: integer;
begin
Query.UniDirectional := true;
if OpenQuery(SQLStatement) then
begin
Retvar := Query.Fields[0].AsInteger;
Query.Close;
end
else
Retvar := 0;
Result := Retvar;
end;
function TSQLCommand.AsInteger(const SQLStatement: string;
FormatArguments: array of const): integer;
begin
Result := AsInteger(Format(SQLStatement, FormatArguments));
end;
// DOUBLE
function TSQLCommand.AsFloat(const SQLStatement: string): double;
var
Retvar: double;
begin
Query.UniDirectional := true;
if OpenQuery(SQLStatement) then
begin
Retvar := Query.Fields[0].AsFloat;
Query.Close;
end
else
Retvar := 0.0;
Result := Retvar;
end;
function TSQLCommand.AsFloat(const SQLStatement: string;
FormatArguments: array of const): double;
begin
Result := AsFloat(Format(SQLStatement, FormatArguments));
end;
// TDATETIME
function TSQLCommand.AsDateTime(const SQLStatement: string): TDateTime;
var
Retvar: TDateTime;
begin
Query.UniDirectional := true;
if OpenQuery(SQLStatement) then
begin
Retvar := Query.Fields[0].AsDateTime;
Query.Close;
end
else
Retvar := 0.0;
Result := Retvar;
end;
function TSQLCommand.AsDateTime(const SQLStatement: string;
FormatArguments: array of const): TDateTime;
begin
Result := AsDateTime(Format(SQLStatement, FormatArguments));
end;
// ====================================================
// Easy way to open and close free form statements
// ====================================================
function TSQLCommand.FreeFormOpen(const SQLStatement: string): boolean;
begin
Query.UniDirectional := false;
Result := OpenQuery(SQLStatement, false);
end;
function TSQLCommand.FreeFormOpen(const SQLStatement: string;
FormatArguments: array of const): boolean;
begin
Query.UniDirectional := false;
Result := OpenQuery(Format(SQLStatement, FormatArguments), false);
end;
// CLOSE SQL
procedure TSQLCommand.FreeFormClose;
begin
Query.Close;
end;
// EXEC SQL
function TSQLCommand.Exec(const SQLStatement: string): boolean;
begin
Result := ExecQuery(SQLStatement);
end;
function TSQLCommand.Exec(const SQLStatement: string;
FormatArguments: array of const): boolean;
begin
Result := ExecQuery(Format(SQLStatement, FormatArguments));
end;
// ================================
// Inset/Update & Delete Commands
// ================================
// DBMS INSERT
function TSQLCommand.Insert(ColNames: array of string;
Values: array of variant;
const TableName: string): boolean;
var
Cmd: string;
VType: TVarType;
Retvar: boolean;
i: integer;
begin
Query.UniDirectional := true;
if (High(ColNames) = -1) or (High(Values) = -1) or
(High(ColNames) <> High(Values)) then
begin
FLastErrorMess := 'Insert Statement ColNames()/Values() Mismatched';
if FAutoErrorMessage then
MessageDlg('Insert Failed!' + CrLf + CrLf + FLastErrorMess,
mtError, [mbOk], 0);
Retvar := false;
end
else
begin
Cmd := 'insert into ' + TableName + CrLf + '(' + ColNames[0];
for i := 1 to High(ColNames) do
Cmd := Cmd + ',' + ColNames[i];
Cmd := Cmd + ')' + CrLf;
Cmd := Cmd + 'values (';
for i := 0 to High(Values) do
begin
VType := VarType(Values[i]);
case VType of
varDate: Cmd := Cmd + sqlStr(TDateTime(Values[i]),
FDatabaseSystem, false);
varInteger,
varSmallint,
varShortint,
varByte,
varWord,
varLongword,
varInt64: Cmd := Cmd + IntToStr(Values[i]);
varSingle,
varDouble,
varCurrency: Cmd := Cmd + FloatToStr(Values[i]);
varStrArg,
varOleStr,
varString: Cmd := Cmd + QuotedStr(Values[i]);
else
Cmd := Cmd + '????';
end;
Cmd := Cmd + ',';
end;
System.Delete(Cmd, length(Cmd), 1);
Cmd := Cmd + ')';
Retvar := ExecQuery(Cmd);
end;
Result := RetVar;
end;
// DBMS UPDATE
function TSQLCommand.Update(ColNames: array of string;
Values: array of variant;
const WhereClause: string;
const TableName: string): boolean;
var
Cmd, Parm: string;
VType: TVarType;
Retvar: boolean;
i: integer;
begin
Query.UniDirectional := true;
if (High(ColNames) = -1) or (High(Values) = -1) or
(High(ColNames) <> High(Values)) then
begin
FLastErrorMess := 'Update Statement ColNames()/Values() Mismatched';
if FAutoErrorMessage then
MessageDlg('Update Failed!' + CrLf + CrLf + FLastErrorMess,
mtError, [mbOk], 0);
Retvar := false;
end
else
begin
Cmd := 'update ' + TableName + ' set' + CrLf;
for i := 0 to High(Values) do
begin
VType := VarType(Values[i]);
case VType of
varDate: Parm := sqlStr(TDateTime(Values[i]),
FDatabaseSystem, false);
varInteger,
varSmallint,
varShortint,
varByte,
varWord,
varLongword,
varInt64: Parm := IntToStr(Values[i]);
varSingle,
varDouble,
varCurrency: Parm := FloatToStr(Values[i]);
varStrArg,
varOleStr,
varString: Parm := QuotedStr(Values[i]);
else
Parm := '????';
end;
Cmd := Cmd + ColNames[i] + '=' + Parm + ',';
end;
System.Delete(Cmd, length(Cmd), 1);
Cmd := Cmd + CrLf + 'where ' + WhereClause;
Retvar := ExecQuery(Cmd);
end;
Result := RetVar;
end;
// DBMS DELETE
function TSQLCommand.Delete(const WhereClause: string;
const TableName: string): boolean;
var
Cmd: string;
begin
Query.UniDirectional := true;
Cmd := 'delete from ' + TableName + ' where ' + WhereClause;
Result := ExecQuery(Cmd);
end;
// ============================
// Get the system date/time
// ============================
function TSQLCommand.SystemTime: TDateTime;
var
Retvar: TDateTime;
begin
Retvar := 0.0;
Query.UniDirectional := true;
if FDatabaseSystem = sysOracle then
begin
if OpenQuery('select sysdate from dual') then
Retvar := Query.Fields[0].AsDateTime;
end
else
begin
if OpenQuery('select getdate()') then
Retvar := Query.Fields[0].AsDateTime;
end;
Query.Close;
Result := Retvar;
end;
// ============================
// Get the system user name
// ============================
function TSQLCommand.SystemUser: string;
var
Retvar: string;
begin
Retvar := '';
Query.UniDirectional := true;
if FDatabaseSystem = sysOracle then
begin
if OpenQuery('select user from dual') then
Retvar := Query.Fields[0].AsString;
end
else
begin
Retvar := ExecFunc('select system_user');
end;
Query.Close;
Result := Retvar;
end;
end.
2010. szeptember 24., péntek
Screen capture into a BMP file
Problem/Question/Abstract:
Screen capture into a BMP file
Answer:
This little routine grabs the whole screen, assigns it temporary to a bitmap and stores it into file "sample.bmp".
A potential problem:
If your system is set up to HiColor (32k colors = 15 bits per pixel), some programs will not be able to read the result since they are only capable to read 16 bits/ pixel.
procedure TForm1.Button1Click(Sender: TObject);
var
DeskTopDC: HDc;
DeskTopCanvas: TCanvas;
DeskTopRect: TRect;
Bitmap: TBitmap;
begin
DeskTopDC := GetWindowDC(GetDeskTopWindow);
DeskTopCanvas := TCanvas.Create;
DeskTopCanvas.Handle := DeskTopDC;
DeskTopRect := Rect(0, 0, Screen.Width, Screen.Height);
Bitmap := TBitmap.Create;
with Bitmap do
begin
Width := Screen.Width;
Height := Screen.Height;
PixelFormat := pfDevice;
end;
Bitmap.Canvas.CopyRect(DeskTopRect, DeskTopCanvas, DeskTopRect);
Bitmap.SaveToFile('c:\temp\sample.bmp');
Bitmap.Free;
DesktopCanvas.Free;
ReleaseDC(GetDeskTopWindow, DeskTopDC);
end;
2010. szeptember 23., csütörtök
Selection Sort
Problem/Question/Abstract:
Selection sort algorithm
Answer:
Selection Sort
An elementary sorting algorithm that is designed to minimize the number of exchanges that are performed. It works by making n-1 passes over the unsorted portion of the array, each time selecting the largest value. This value is then moved into its final sorted position with a single exchange.
procedure SelectionSort(Items: TStrings);
var
i, n, maxIndex, topIndex: integer;
Dummy: string;
begin
n := Items.Count;
for topIndex := n - 1 downto 1 do
begin
maxIndex := topIndex;
for i := 0 to topIndex - 1 do
if Items[i] > Items[maxIndex] then
maxIndex := i;
Dummy := Items[topIndex];
Items[topIndex] := Items[maxIndex];
Items[maxIndex] := Dummy;
end;
end;
2010. szeptember 22., szerda
Compare two bitmaps
Problem/Question/Abstract:
How to compare two bitmaps
Answer:
If you really only need to know if they're 100% identical then there is a pretty fast way by reading the bitmaps in strings via a MemoryStream:
{ ... }
begin
MemStream := TMemoryStream.Create;
try
Bmp1.SaveToStream(MemStream);
MemStream.Position := 0;
SetLength(S1, MemStream.Size);
MemStream.Read(S1[1], Length(S1));
MemStream.Clear;
Bmp2.SaveToStream(MemStream);
MemStream.Position := 0;
SetLength(S2, MemStream.Size);
MemStream.Read(S2[1], Length(S2));
finally
MemStream.Free;
end;
if S1 = S2 then
{they are identically};
end;
2010. szeptember 21., kedd
How to rearrange items within a TListBox (2)
Problem/Question/Abstract:
I have a TListBox with, say, 10 strings, and I want to be able to use Drag and Drop to reorder those 10 items at runtime. How exactly do I do that?
Answer:
Here's the code I use to do list reordering. Create a new form, place a TListBox on it, set its DragMode property to dmAutomatic and connect the following event handlers.
procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
aListBox: TListBox;
DropIndex: Integer;
begin
aListBox := Sender as TListBox;
DropIndex := aListBox.ItemAtPos(Point(X, Y), False);
if aListBox.ItemIndex < DropIndex then
dec(DropIndex);
aListBox.Items.Move(aListBox.ItemIndex, DropIndex);
aListBox.ItemIndex := DropIndex;
end;
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Sender = ListBox1;
end;
2010. szeptember 20., hétfő
Access the serial port in Windows XP
Problem/Question/Abstract:
How I do to access the serial or paralell port in Windows XP?
Answer:
The example below sends a char 'R' to the port, then reads a line until CR or LF is encountered, then processes a line via ParseData. Then all is repeated until Terminated is True.
procedure ProcessCOMPortData(FPortName: string);
var
LPortHandle: THandle;
LInBuffer: array[0..1023] of Char;
LBytesWritten, LBytesRead: Cardinal;
LOutBuffer: Char;
LOverlapped: TOverlapped;
LEvent: TEvent;
LOldCommTimeouts, LCommTimeouts: TCommTimeouts;
LPCommConfig: PCommConfig;
LCommConfig: TCommConfig;
CommConfigSize: Cardinal;
LEOL: Boolean;
LIndex: Integer;
begin
ZeroMemory(@LOverlapped, SizeOf(TOverlapped));
ZeroMemory(@LCommTimeouts, SizeOf(TCommTimeouts));
ZeroMemory(@LCommConfig, SizeOf(TCommConfig));
LPortHandle := INVALID_HANDLE_VALUE;
LEvent := nil;
LPCommConfig := nil;
try
CommConfigSize := 0;
LPCommConfig := @LCommConfig;
GetDefaultCommConfig(PChar(FPortName), LPCommConfig^, CommConfigSize);
GetMem(LPCommConfig, CommConfigSize);
GetDefaultCommConfig(PChar(FPortName), LPCommConfig^, CommConfigSize);
LEvent := TEvent.Create(nil, True, False, '');
LOverlapped.hEvent := LEvent.Handle;
LPortHandle := CreateFile(PChar(FPortName), GENERIC_READ or GENERIC_WRITE, 0,
nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (LPortHandle = INVALID_HANDLE_VALUE) then
Exit;
SetCommConfig(LPortHandle, LPCommConfig^, CommConfigSize);
GetCommTimeouts(LPortHandle, LOldCommTimeouts);
SetCommTimeouts(LPortHandle, LCommTimeouts);
while not Terminated do
begin
PurgeComm(LPortHandle, PURGE_TXCLEAR or PURGE_RXCLEAR or
PURGE_RXABORT or PURGE_TXABORT);
LOutBuffer := 'R';
WriteFile(LPortHandle, LOutBuffer, Length(LOutBuffer), LBytesWritten,
@LOverlapped);
GetOverlappedResult(LPortHandle, LOverlapped, LBytesWritten, True);
if LBytesWritten = 1 then
begin
ZeroMemory(@LInBuffer, SizeOf(LInBuffer));
ReadFile(LPortHandle, LInBuffer[0], 1, LBytesRead, @LOverlapped);
case LEvent.WaitFor(50) of
wrSignaled:
begin
GetOverlappedResult(LPortHandle, LOverlapped, LBytesRead, True);
LEOL := False;
LIndex := LBytesRead;
while not LEOL and (LIndex < SizeOf(LInBuffer)) do
begin
ReadFile(LPortHandle, LInBuffer[LIndex], 1, LBytesRead, @LOverlapped);
GetOverlappedResult(LPortHandle, LOverlapped, LBytesRead, True);
LEOL := (LInBuffer[LIndex] = #$0A) or (LInBuffer[LIndex] = #$0D);
Inc(LIndex);
end;
try
ParseData(LInBuffer);
except
end;
end;
wrTimeout, wrAbandoned, wrError:
begin
Sleep(100);
end;
end;
end;
end;
finally
SetCommTimeouts(LPortHandle, LOldCommTimeouts);
CloseHandle(LPortHandle);
FreeAndNil(LEvent);
if Assigned(LPCommConfig) then
FreeMem(LPCommConfig);
end;
end;
2010. szeptember 19., vasárnap
Personal settings and the windows registry
Problem/Question/Abstract:
What is the structure of the registry?
What settings to store in the registry, how to do so?
Answer:
Scope
This article is the third article in a series of four articles about personal settings. This article deals with the usage of the registry for retaining personal settings. It explains the working of the registry, how to read & write date in the registry from Delphi, and contains an example: a form which reads its previous position on the screen.
One thing every application i.m.h.o. should contain is a registration in the registry -the registry should have a clue in which directory your application has been stored. You may choose to have this handled by your installtion program, which is a logical place to do so. But the program can also do it itself, so that the user moving the directory with your application from the windows explorer can fix its own settings by simply starting your program.
The registry - its structure
The registry was introduced by Microsoft with Windows 95. In Windows 95 and Windows 98, there are 2 files in your windows directory called user.dat and system.dat which contain the data in your registry. However, in all cases Microsoft provides a utility regedit to browse and change the registry.
When you use this tool to open your registry, you will see 5 base keys:
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIGURATION
These five keys are identical in all window versions. Each one has many subkeys, which in turn have subkeys again. The registry has a strict hierarchical structure. For our purpose only 2 of them are interesting: HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE. The first is, as the name suggests, user dependant, the second is applicable to all the machine. You might think this key contains just hardware related data, but it has a subkey for software as well. The local machine root is a good choice for items applicable to all users.
Reading data
The reading of data from the registry is illustrated with an example. We will cerate a new form which will remember its previous location on the screen. The location is determined by the Top and Left properties of the form. In addition to these, we will read and store the Height and Width properties of the form.
const
cKey = 'SOFTWARE\Test_Company\Test_Application';
// key where item values will be stored and read
procedure TRememberForm.FormCreate(Sender:
TObject);
var
lReg: TRegistry;
getInt: integer;
begin
inherited;
// general purpose:
// read latest Left, Top, Width and Height
lReg := TRegistry.Create;
// create registry object
lReg.RootKey := HKEY_CURRENT_USER;
// set root to current user root,
// to ensure different users on this
// machine have their own setting
lReg.OpenKey(cKey, True);
// Open key where we will read items
if lReg.ValueExists(self.name +
'.Left') then // check if
value for item formname.left exists
begin
getint := lReg.ReadInteger(self.name
+ '.Left'); // Read left position of actual form
if getint > 0 then
self.Left := getint;
end;
if lReg.ValueExists(self.name +
'.Top') then
begin
getint := lReg.ReadInteger(self.name + '.Top');
if getint > 0 then
self.Top := getint;
end;
if lReg.ValueExists(self.name + '.Width') then
begin
getint := lReg.ReadInteger(self.name + '.Width');
if getint > 0 then
self.Width := getint;
end;
if lReg.ValueExists(self.name + '.Height') then
begin
getint := lReg.ReadInteger(self.name + '.Height');
if getint > 0 then
self.Height := getint;
end;
// Close and free
lReg.CloseKey;
lReg.Free;
end;
Let's have a short look at the main points:
lReg := TRegistry.Create;
// create registry object
This line creates the Registry object.
lReg.RootKey := HKEY_CURRENT_USER;
// set root to current user root,
// to ensure different users on this
// machine have their own setting
We set the root to indicate in which of the 5 roots we want to work. HKEY_CURRENT_USER is the default value. Alternately, we might have chosen HKEY_LOCAL_MACHINE if we wanted to save settings for all users on this machine.
lReg.OpenKey(cKey, True);
// Open key where we will read items
The key where items will be stored and read is opened. If the key does not exist, windows automatically creates it. Note that capitalization is important when the registry key is created. Most entries in the registry have first letter uppercase and the rest lowercase, and it is good practice to comply.
if lReg.ValueExists(self.name + '.Left') then
// check if value for item formname. left exists
This statement tests if the item FormName.left exists within the open key.
begin
getint := lReg.ReadInteger(self.name
+ '.Left'); // Read left position of actual form
As an alternative for ReadInteger, the TRegistry object also provides functions to read strings, booleans, dates, datetimes, times, floats, and binary data.
if getint > 0 then
self.Left := getint;
end;
By putting all these code in the FormCreate, the window is automatically positioned at the right spot at creation. Of course the code above can not be really tested when we don't have any data, so the next step is to write these items in the FormDestroy.
Writing data
The FormDestroy event is a good moment to write the data to the registry. The code is a lot shorter:
procedure TRememberForm.FormDestroy(Sender: TObject);
var
lReg: TRegistry;
begin
// open registry, set root and key
lReg := TRegistry.Create;
lReg.RootKey := HKEY_CURRENT_USER;
lReg.OpenKey(cKey, True);
// write last Left, Top, Width and Height
lReg.WriteInteger(self.name + '.Left', self.Left);
lReg.WriteInteger(self.name + '.Top', self.Top);
lReg.WriteInteger(self.name + '.Width', self.Width);
lReg.WriteInteger(self.name + '.Height', self.Height);
// close all
lReg.CloseKey;
lReg.Free;
inherited;
end;
As with reading data, the same applies with writing data. You can easily write booleans, currencies, dates, datetimes, floats, strings and times, and binary data. In addition, there is a WriteExpandString, which is meant for unexpanded strings with "%" in it, such as "Open file %s failed".
WriteInteger creates a name when the name does not yet exist. If a write operation fails, an exception is raised.
Alternatives
There are 2 other objects, TRegIniFile and TRegistryInifile available.
TRegistryIniFile presents a simple interface to the system registry and hides the need to know about the underlying structure of the registry. TRegistryIniFile enables handling the Windows system registry as if it were a Windows 3.x INI file. Instead of processing an INI file, however, TRegistryIniFile reads from and writes to the system registry.
TRegIniFile presents a simple interface to the system registry, hiding the need to know about the underlying structure of the registry. TRegIniFile acts as a helper object to TRegistryIniFile.
Personally I avoid using these 2 classes. The programmer saves some time by not having to understand the registry. But the structure of the registry is rather straightforward hierarchical, and using the registry as if it was an ini-file limits its usage.
Limitations and final words
There are a few limitations which we haven't discussed. First, The registry is not meant a a database for storing vast quantities of data. It is meant for storing initialization and configuration data. Anything over 2Kb had better be stored in a separate file. If you wish, you can mention the corresponding file location in the registry. Then WriteBinary is especially useful for storing recordtypes. Using a record type saves you typing code, and it can be written to and read from the registry with 1 statement, saving both time and storage. And a large registry might slow down all applications.
There is a small bug in the MoveKey procedure under WNT. It should move a key including Subkeys, but it does not, at least not in D2-4. In the help of Delphi 5 this problem has been documented. The problem does not seem to appear under W2K.
Third, it seems Windows NT & Windows 2000 administrators can limit Registry access. Using the registry may conflict with IT-policies in some companies.
Should you like the form we created, right click on the form and choose 'add to repository'. You can then create descendants by choosing File / New / forms.
Component Download: http://www.xs4all.nl/~spaanszt/Delphi/DemoRegistry.zip
2010. szeptember 18., szombat
How to add a calculated field to a dynamically created TTable
Problem/Question/Abstract:
How to add a calculated field to a dynamically created TTable
Answer:
var
f: TField;
i: Integer;
begin
table1.FieldDefs.Update
Table1.Close;
for i := 0 to Table1.FieldDefs.Count - 1 do
{create persistent field that does not exist}
if table1.FindField(table1.FieldDefs[i].Name) = nil then
table1.FieldDefs.Items[i].CreateField(Table1);
{create a calculated field}
f := TStringField.Create(Table1);
f.Name := 'Table1CalcField';
f.FieldName := 'CalcField';
f.DisplayLabel := 'CalcField';
f.Calculated := True;
f.DataSet := Table1;
Table1.Open;
end;
2010. szeptember 17., péntek
Use webcam in Delphi
Problem/Question/Abstract:
Do you need to takes pictures from your Delphi application ?
Answer:
First of all, get the SDK at http://developer.logitech.com
After installation, open delphi and Import ActiveX Control VPortal2 from the list.
Now, create a new form, and put a VideoPortal from the ActiveX panel and a button.
In the uses, add VideoPortal
On the OnShow add:
VideoPortal1.PrepareControl('QCSDK',
'HKEY_LOCAL_MACHINE\Software\JCS Programmation\QCSDK', 0);
VideoPortal1.EnableUIElements(UIELEMENT_STATUSBAR, 0, 0);
VideoPortal1.ConnectCamera2;
VideoPortal1.EnablePreview := 1;
On the ButtonClick add:
var
BMP: TBitmap;
JPG: TJpegImage;
L: string;
begin
F := 'Photos\test.jpg';
VideoPortal1.StampBackgroundColor := clYellow;
VideoPortal1.StampTextColor := clBlack;
VideoPortal1.StampFontName := 'Arial';
VideoPortal1.StampPointSize := 10;
VideoPortal1.StampTransparentBackGround := 0;
L := Format(' %s - %s ', [DateTimeToStr(Now), Num]);
VideoPortal1.PictureToFile(0, 24, 'Temp.bmp', L);
BMP := TBitmap.Create;
JPG := TJpegImage.Create;
BMP.LoadFromFile('Temp.bmp');
JPG.CompressionQuality := 85;
JPG.Assign(BMP);
JPG.SaveToFile(F);
BMP.Free;
JPG.Free;
end;
It's all, run the application, you will see the image from the camera, click on the button to get a picture.
Here is a copy a VideoPortal.Pas (constants).
unit VideoPortal;
interface
// Copyright (c) 1996-2000 Logitech, Inc. All Rights Reserved
// User Interface Element, codes used with EnableUIElement method
const
UIELEMENT_640x480 = 0;
const
UIELEMENT_320x240 = 1;
const
UIELEMENT_PCSMART = 2;
const
UIELEMENT_STATUSBAR = 3;
const
UIELEMENT_UI = 4;
const
UIELEMENT_CAMERA = 5;
const
UIELEMENT_160x120 = 6;
// Camera status codes, returned by CameraState property
const
CAMERA_OK = 0;
const
CAMERA_UNPLUGGED = 1;
const
CAMERA_INUSE = 2;
const
CAMERA_ERROR = 3;
const
CAMERA_SUSPENDED = 4;
const
CAMERA_DUAL_DETACHED = 5;
const
CAMERA_UNKNOWNSTATUS = 10;
// Movie Recording Modes, used with MovieRecordMode property
const
SEQUENCECAPTURE_FPS_USERSPECIFIED = 1;
const
SEQUENCECAPTURE_FPS_FASTASPOSSIBLE = 2;
const
STEPCAPTURE_MANUALTRIGGERED = 3;
// Movie Creation Flags, used with MovieCreateFlags property
const
MOVIECREATEFLAGS_CREATENEW = 1;
const
MOVIECREATEFLAGS_APPEND = 2;
// Notification Codes
const
NOTIFICATIONMSG_MOTION = 1;
const
NOTIFICATIONMSG_MOVIERECORDERROR = 2;
const
NOTIFICATIONMSG_CAMERADETACHED = 3;
const
NOTIFICATIONMSG_CAMERAREATTACHED = 4;
const
NOTIFICATIONMSG_IMAGESIZECHANGE = 5;
const
NOTIFICATIONMSG_CAMERAPRECHANGE = 6;
const
NOTIFICATIONMSG_CAMERACHANGEFAILED = 7;
const
NOTIFICATIONMSG_POSTCAMERACHANGED = 8;
const
NOTIFICATIONMSG_CAMERBUTTONCLICKED = 9;
const
NOTIFICATIONMSG_VIDEOHOOK = 10;
const
NOTIFICATIONMSG_SETTINGDLGCLOSED = 11;
const
NOTIFICATIONMSG_QUERYPRECAMERAMODIFICATION = 12;
const
NOTIFICATIONMSG_MOVIESIZE = 13;
// Error codes used by NOTIFICATIONMSG_MOVIERECORDERROR notification:
const
WRITEFAILURE_RECORDINGSTOPPED = 0;
const
WRITEFAILURE_RECORDINGSTOPPED_FILECORRUPTANDDELETED = 1;
const
WRITEFAILURE_CAMERA_UNPLUGGED = 2;
const
WRITEFAILURE_CAMERA_SUSPENDED = 3;
// Camera type codes, returned by GetCameraType method
const
CAMERA_UNKNOWN = 0;
const
CAMERA_QUICKCAM_VC = 1;
const
CAMERA_QUICKCAM_QUICKCLIP = 2;
const
CAMERA_QUICKCAM_PRO = 3;
const
CAMERA_QUICKCAM_HOME = 4;
const
CAMERA_QUICKCAM_PRO_B = 5;
const
CAMERA_QUICKCAM_TEKCOM = 6;
const
CAMERA_QUICKCAM_EXPRESS = 7;
const
CAMERA_QUICKCAM_FROG = 8; // MIGHT CHANGE NAME BUT ENUM STAYS THE SAME
const
CAMERA_QUICKCAM_EMERALD = 9; // MIGHT CHANGE NAME BUT ENUM STAYS THE SAME
// Camera-specific property codes used by Set/GetCameraPropertyLong
const
PROPERTY_ORIENTATION = 0;
const
PROPERTY_BRIGHTNESSMODE = 1;
const
PROPERTY_BRIGHTNESS = 2;
const
PROPERTY_CONTRAST = 3;
const
PROPERTY_COLORMODE = 4;
const
PROPERTY_REDGAIN = 5;
const
PROPERTY_BLUEGAIN = 6;
const
PROPERTY_SATURATION = 7;
const
PROPERTY_EXPOSURE = 8;
const
PROPERTY_RESET = 9;
const
PROPERTY_COMPRESSION = 10;
const
PROPERTY_ANTIBLOOM = 11;
const
PROPERTY_LOWLIGHTFILTER = 12;
const
PROPERTY_IMAGEFIELD = 13;
const
PROPERTY_HUE = 14;
const
PROPERTY_PORT_TYPE = 15;
const
PROPERTY_PICTSMART_MODE = 16;
const
PROPERTY_PICTSMART_LIGHT = 17;
const
PROPERTY_PICTSMART_LENS = 18;
const
PROPERTY_MOTION_DETECTION_MODE = 19;
const
PROPERTY_MOTION_SENSITIVITY = 20;
const
PROPERTY_WHITELEVEL = 21;
const
PROPERTY_AUTO_WHITELEVEL = 22;
const
PROPERTY_ANALOGGAIN = 23;
const
PROPERTY_AUTO_ANALOGGAIN = 24;
const
PROPERTY_LOWLIGHTBOOST = 25;
const
PROPERTY_COLORBOOST = 26;
const
PROPERTY_ANTIFLICKER = 27;
const
PROPERTY_OPTIMIZATION_SPEED_QUALITY = 28;
const
PROPERTY_STREAM_HOOK = 29;
const
PROPERTY_LED = 30;
const
ADJUSTMENT_MANUAL = 0;
const
ADJUSTMENT_AUTOMATIC = 1;
const
ORIENTATIONMODE_NORMAL = 0;
const
ORIENTATIONMODE_MIRRORED = 1;
const
ORIENTATIONMODE_FLIPPED = 2;
const
ORIENTATIONMODE_FLIPPED_AND_MIRRORED = 3;
const
COMPRESSION_Q0 = 0;
const
COMPRESSION_Q1 = 1;
const
COMPRESSION_Q2 = 2;
const
ANTIFLICKER_OFF = 0;
const
ANTIFLICKER_50Hz = 1;
const
ANTIFLICKER_60Hz = 2;
const
OPTIMIZE_QUALITY = 0;
const
OPTIMIZE_SPEED = 1;
const
LED_OFF = 0;
const
LED_ON = 1;
const
LED_AUTO = 2;
const
LED_MAX = 3;
const
PICTSMART_LIGHTCORRECTION_NONE = 0;
const
PICTSMART_LIGHTCORRECTION_COOLFLORESCENT = 1;
const
PICTSMART_LIGHTCORRECTION_WARMFLORESCENT = 2;
const
PICTSMART_LIGHTCORRECTION_OUTSIDE = 3;
const
PICTSMART_LIGHTCORRECTION_TUNGSTEN = 4;
const
PICTSMART_LENSCORRECTION_NORMAL = 0;
const
PICTSMART_LENSCORRECTION_WIDEANGLE = 1;
const
PICTSMART_LENSCORRECTION_TELEPHOTO = 2;
const
CAMERADLG_GENERAL = 0;
const
CAMERADLG_ADVANCED = 1;
implementation
end.
Example shows how to use the PictureToMemory method in the QuickCam SDK.
type
TMemoryStream = class(Classes.TMemoryStream);
var
MS: TMemoryStream;
lSize: LongInt;
pBuffer: ^Byte;
begin
MS := TMemoryStream.Create;
bitmap1 := TBitmap.Create;
try
if VideoPortal1.PictureToMemory(0, 24, 0, lSize, '') = 1 then
begin
pBuffer := AllocMem(lSize);
if VideoPortal1.PictureToMemory(0, 24, integer(pBuffer), lSize, '') = 1 then
begin
MS.SetPointer(pBuffer, lSize);
bitmap1.loadfromstream(MS);
end;
end;
finally
MS.Free;
FreeMem(pBuffer);
end;
end;
Feliratkozás:
Bejegyzések (Atom)