2010. március 31., szerda
A Class for Get Tokens, Parse Strings, Count Words, Search Tokens
Problem/Question/Abstract:
A general way for get tokens, parse strings, count words and search for a specific token on a string.
Answer:
There are many functions and ways for get a token on a string. I have written a general class for handle tokens and parse strings.
The class is named as TToken. I want to describe it with some examples.
var
xTk: TToken;
i: Integer;
s: string;
b: Boolean;
begin
xTk := TToken.Create;
{//////////
the class has some variables:
TEXT contains the string to handle
SEPS contains the set of characters that separate tokens
}
xTk.Text := 'Here is my example. Contains commas, dots and spaces.';
xTk.Seps := [' ', ',', '.'];
{//////////
with the method COUNT I can count the number of tokens.
I can use it on two ways, I can call the method and the variable NUMTOKENS save the number of tokens or I can assign the method to a memory variable. Here is the example of the two ways.
}
i := xTk.Count;
ShowMessage(IntToStr(i));
ShowMessage(IntToStr(xTk.NumTokens));
{//////////
When I want to search all tokens on a sequential way Im going to use the methods FIRT and NEXT. Im going to use two Variables: MORETOKENS and LASTTOKEN. MORETOKENS is a boolean variabale that indicates that after I execute the First or Next method I have a token that is saved on the LASTTOKEN variable
}
xTk.First;
while xTk.MoreTokens do
begin
ShowMessage(xTk.LastToken);
xTk.Next;
end;
{//////////
I can assign the Firt and Next method to a memory variable and I can use the variable NOTOKEN that contains the negative value of MORETOKENS
}
s := xTk.First;
while not xTk.NoToken do
begin
ShowMessage(s);
s := xTk.Next;
end;
{//////////
I can search for a specific token with the SEARCH method
}
b := xTk.Search('IS');
if b then
ShowMessage('Found it')
else
ShowMessage('Not found it');
b := xTk.Search('YOUR');
if b then
ShowMessage('Found it')
else
ShowMessage('Not found it');
xTk.Free;
end;
The class is:
unit UToken;
{
Class for handle Tokens
Author: Alejandro Castro
}
interface
uses SysUtils;
type
TToken = class(Tobject)
private
xCharText: string;
function xGetToken(xText: string): string;
public
Seps: set of char; // Separators
Text: string; // string to handle
LastToken: string; // token readed with first or next method
NoToken: Boolean; // flag that indicate that there ARENT more tokens
MoreTokens: Boolean; // flag that indicate that there ARE more tokens
NumTokens: Integer; // no of tokens on Text
constructor Create;
function Count: Integer; // count the number of tokens
function First: string; // Find the First Token
function Next: string; // Find the Next Token
function Search(TokSearch: string): Boolean; // Search for a specific token
end;
implementation
constructor TToken.Create;
begin
Seps := [];
Text := '';
xCharText := '';
NoToken := True;
MoreTokens := False;
LastToken := '';
end;
function TToken.Count: Integer;
var
i, xLen: Integer;
xFlag: Boolean;
begin
NumTokens := 0;
xLen := length(Text);
i := 1;
xFlag := False;
while (i <= xLen) and (xLen <> 0) do
begin
if (Text[i] in Seps) then
xFlag := False
else
begin
if (not xFlag) then
begin
xFlag := True;
inc(NumTokens);
end;
end;
inc(i);
end;
Result := NumTokens;
end;
function TToken.Next: string;
begin
Result := xGetToken(xCharText);
LastToken := Result;
if Result = '' then
NoToken := True
else
NoToken := False;
MoreTokens := not NoToken;
end;
function TToken.First: string;
begin
Result := xGetToken(Text);
LastToken := Result;
if Result = '' then
NoToken := True
else
NoToken := False;
MoreTokens := not NoToken;
end;
function TToken.xGetToken(xText: string): string;
var
i, xLen: Integer;
xFlag, xAgain: Boolean;
begin
Result := '';
xLen := length(xText);
i := 1;
xFlag := False;
xAgain := True;
while (i <= xLen) and (xLen <> 0) and (xAgain) do
begin
if (xText[i] in Seps) then
begin
xAgain := (xAgain and (not xFlag));
xFlag := False
end
else
begin
if (not xFlag) then
begin
xFlag := True;
xAgain := true;
end;
Result := Result + xText[i];
end;
inc(i);
end;
xCharText := copy(xText, i, xLen);
end;
function TToken.Search(TokSearch: string): Boolean;
var
xAgain: Boolean;
begin
Result := False;
xAgain := True;
First;
while (not noToken) and (xAgain) do
begin
if UpperCase(LastToken) = UpperCase(TokSearch) then
begin
Result := true;
xAgain := False;
end;
Next;
end;
end;
end.
Component Download: http://www.baltsoft.com/files/dkb/attachment/UToken.zip
2010. március 30., kedd
Get computer name and user
Problem/Question/Abstract:
This article presents a code snipet which shows how to get the current user and the computer name.
Answer:
Obviously this is not a complete program, but you can use the Windows API calls GetUserName and GetComputerName as shown below.
uses
windows...
var
u: array[0..127] of Char;
c: array[0..127] of Char;
user: string;
computer: string;
sz: dword;
begin
sz := SizeOf(u);
GetUserName(u, sz);
sz := SizeOf(c);
GetComputerName(c, sz);
user := u;
computer := c;
end;
2010. március 29., hétfő
A VCL Component to print labels
Problem/Question/Abstract:
A simple component to print labels
Answer:
A simple VCL componet to print labels.
A few days ago I wrote an article about a class to print labels (3156)
With the help of Mike Heydon we have rewritten the class to convert it to a component and easier to use.
What do we need to print labels ?
The size (height and width) of every label.
The number of labels per row.
The top and left margin.
The kind of measure: pixels,inches or millimetres.
The font to use.
And of course data to fill the labels.
With the next component we can do it very simply, Im going to use a pseudo-code to explain the use of the component TPrtLabels:
begin
PrtLabels.Measurements := plmInches; // plmMillimetres or plmPixels
PrtLabels.Font := FontDialog1.Font; // I get the font from a Font Dialog
PrtLabels.LabelsPerRow := 4; // 4 Label per row
PrtLabels.LabelWidth := 3; // only an example
PrtLabels.LabelHeight := 1.5; // only an example
PrtLabels.LeftMargin := 0; // only an example
PrtLabels.TopMargin := 0; // only an example
PrtLabels.Open; // open the printer
Table.First // Im going to read a customer table
while not Table.Eof do
begin
PrtLabels.Add(["Name", "Street", "City"]); // I fill the content of every label
Table.Next;
end;
PrtLabels.Close; // close the printer and print any label pending on the buffer
PrtLabels.Free;
end;
We need only 3 methods: Open, Add and Close.
The properties that we need are:
Measurements(plmInches, plmMillimetres or plmPixels)
LabelsPerRow
LabelWidth
LabelHeight
LeftMargin
TopMargin
Font
The componet:
unit ULabels2;
{
VCL Component to print labels
Authors:
Mike Heydon
Alejandro Castro
Date: 1/Abr/2002
}
interface
uses SysUtils, Windows, Classes, Graphics, Printers;
type
TPrtLabelMeasures = (plmPixels, plmInches, plmMillimetres);
TPrtLabels = class(TComponent)
private
FFont: TFont;
FMeasurements: TPrtLabelMeasures;
FTopMargin,
FLeftMargin,
FLabelHeight,
FLabelWidth: double; // Selected Measure
FLabelLines,
FLabelsPerRow: word; // ABS Pixels
TopMarginPx,
LeftMarginPx,
LabelHeightPx,
LabelWidthPx: integer;
TabStops: array of word;
DataArr: array of array of string;
CurrLab: word;
procedure SetFont(Value: TFont);
procedure IniDataArr;
procedure FlushBuffer;
procedure SetDataLength(xLabelLines, xLabelsPerRow: Word);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Add(LabLines: array of string);
procedure Close;
procedure Open;
published
property Font: TFont read FFont write SetFont;
property Measurements: TPrtLabelMeasures read FMeasurements write FMeasurements;
property LabelWidth: double read FLabelWidth write FLabelWidth;
property LabelHeight: double read FLabelHeight write FLabelHeight;
property TopMargin: double read FTopMargin write FTopMargin;
property LeftMargin: double read FLeftMargin write FLeftMargin;
property LabelsPerRow: word read FLabelsPerRow write FLabelsPerRow;
// property LabelLines : word read FLabelLines write FLabelLines;
end;
procedure Register;
implementation
const
MMCONV = 25.4;
procedure Register;
begin
RegisterComponents('Mah2001', [TPrtLabels]);
end;
constructor TPrtLabels.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FMeasurements := plmInches;
FLabelHeight := 0.0;
FLabelWidth := 0.0;
FTopMargin := 0.0;
FLeftMargin := 0.0;
FLabelsPerRow := 1;
FLabelLines := 1;
FFont := TFont.Create;
TabStops := nil;
DataArr := nil;
end;
destructor TPrtLabels.Destroy;
begin
FFont.Free;
TabStops := nil;
DataArr := nil;
inherited Destroy;
end;
procedure TPrtLabels.SetFont(Value: TFont);
begin
FFont.Assign(Value);
end;
procedure TPrtLabels.SetDataLength(xLabelLines, xLabelsPerRow: Word);
begin
if (xLabelLines + xLabelsPerRow) > 1 then
SetLength(DataArr, xLabelLines, xLabelsPerRow);
end;
procedure TPrtLabels.Open;
var
PixPerInX, PixPerInY, i: integer;
begin
if (FLabelsPerRow + FLabelLines) > 1 then
begin
SetLength(TabStops, FLabelsPerRow);
SetDataLength(FLabelLines, FLabelsPerRow);
// SetLength(DataArr,FLabelLines,FLabelsPerRow);
Printer.Canvas.Font.Assign(FFont);
Printer.BeginDoc;
PixPerInX := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
PixPerInY := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
case FMeasurements of
plmInches:
begin
LabelWidthPx := trunc(LabelWidth * PixPerInX);
LabelHeightPx := trunc(LabelHeight * PixPerInY);
TopMarginPx := trunc(TopMargin * PixPerInX);
LeftMarginPx := trunc(LeftMargin * PixPerInY);
end;
plmMillimetres:
begin
LabelWidthPx := trunc(LabelWidth * PixPerInX * MMCONV);
LabelHeightPx := trunc(LabelHeight * PixPerInY * MMCONV);
TopMarginPx := trunc(TopMargin * PixPerInX * MMCONV);
LeftMarginPx := trunc(LeftMargin * PixPerInY * MMCONV);
end;
plmPixels:
begin
LabelWidthPx := trunc(LabelWidth);
LabelHeightPx := trunc(LabelHeight);
TopMarginPx := trunc(TopMargin);
LeftMarginPx := trunc(LeftMargin);
end;
end;
for i := 0 to FLabelsPerRow - 1 do
TabStops[i] := LeftMarginPx + (LabelWidthPx * i);
IniDataArr;
end;
end;
procedure TPrtLabels.Close;
begin
if (FLabelsPerRow + FLabelLines) > 1 then
begin
FlushBuffer;
Printer.EndDoc;
TabStops := nil;
DataArr := nil;
end;
end;
procedure TPrtLabels.IniDataArr;
var
i, ii: integer;
begin
CurrLab := 0;
for i := 0 to High(DataArr) do // FLabelLines - 1 do
for ii := 0 to High(DataArr[i]) do //FLabelsPerRow do
DataArr[i, ii] := '';
end;
procedure TPrtLabels.FlushBuffer;
var
i, ii, y, SaveY: integer;
begin
if CurrLab > 0 then
begin
if Printer.Canvas.PenPos.Y = 0 then
Printer.Canvas.MoveTo(0, TopMarginPx);
y := Printer.Canvas.PenPos.Y;
SaveY := y;
for i := 0 to fLabelLines - 1 do
begin
for ii := 0 to fLabelsPerRow - 1 do
begin
Printer.Canvas.TextOut(TabStops[ii], y, DataArr[i, ii]);
end;
inc(y, Printer.Canvas.Textheight('X'));
end;
if (LabelHeightPx + SaveY) + LabelHeightPx > Printer.PageHeight then
Printer.NewPage
else
Printer.Canvas.MoveTo(0, LabelHeightPx + SaveY);
IniDataArr;
end;
end;
procedure TPrtLabels.Add(LabLines: array of string);
var
i: integer;
begin
if Length(LabLines) > FLabelLines then
begin
FLabelLines := Length(LabLines);
SetDataLength(fLabelLines, fLabelsPerRow);
end;
inc(CurrLab);
for i := 0 to high(LabLines) do
if i <= FLabelLines - 1 then
DataArr[i, CurrLab - 1] := LabLines[i];
if CurrLab = FLabelsPerRow then
FlushBuffer;
end;
end.
Component Download: http://www.baltsoft.com/files/dkb/attachment/ULabels2.zip
2010. március 28., vasárnap
Example of a Windows Service, with a thread
Problem/Question/Abstract:
Delphi 5&6 has a template project for services, but it is incomplete. This example builds on that template and completes the service. It also shows how to start a thread that beeps every 2 seconds. You can use this as a base when developing servers as services.
Answer:
This example shows how to use the service template in delphi, taking it a step further and making a complete example. The source for this is included in the ntservice.zip file.
Coded under D6, but works for D5 if you copy the source parts after creating a template service.
Below are all the source files listed one by one.
To test the source, create a Service with Delphi, and pase these sources on top of the automatically generated source.
program NTService;
uses
SvcMgr,
NTServiceMain in 'Units\NTServiceMain.pas' {ExampleService: TService},
NTServiceThread in 'Units\NTServiceThread.pas';
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TExampleService, ExampleService);
Application.Run;
end.
{*
Windows Service Template
========================
Author Kim Sandell
emali: kim.sandell@nsftele.com
Disclaimer Freeware. Use and abuse at your own risk.
Description A Windows NT Service skeleton with a thread.
Works in WinNT 4.0, Win 2K, and Win XP Pro
The NTServiceThread.pas contains the actual
thread that is started under the service.
When you want to code a service, put the code in
its Execute() method.
Example To test the service, install it into the SCM with
the InstallService.bat file. The go to the Service
Control Manager and start the service.
The Interval can be set to execute the Example Beeping
every x seconds. It depends on the application if it
needs a inerval or not.
Notes This example has the service startup options set to
MANUAL. If you want to make a service that starts
automatically with windows then you need to change this.
BE CAREFULT ! If your application hangs when running as a
service THERE IS NO WAY to terminate the application.
History Description
========== ============================================================
24.09.2002 Initial version
*}
unit NTServiceMain;
interface
uses
Windows, Messages, SysUtils, Classes, SvcMgr,
NTServiceThread;
type
TExampleService = class(TService)
procedure ServiceExecute(Sender: TService);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServicePause(Sender: TService; var Paused: Boolean);
procedure ServiceContinue(Sender: TService; var Continued: Boolean);
procedure ServiceShutdown(Sender: TService);
private
{ Private declarations }
fServicePri: Integer;
fThreadPri: Integer;
{ Internal Start & Stop methods }
function _StartThread(ThreadPri: Integer): Boolean;
function _StopThread: Boolean;
public
{ Public declarations }
NTServiceThread: TNTServiceThread;
function GetServiceController: TServiceController; override;
end;
var
ExampleService: TExampleService;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
ExampleService.Controller(CtrlCode);
end;
function TExampleService.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TExampleService.ServiceExecute(Sender: TService);
begin
{ Loop while service is active in SCM }
while not Terminated do
begin
{ Process Service Requests }
ServiceThread.ProcessRequests(False);
{ Allow system some time }
Sleep(1);
end;
end;
procedure TExampleService.ServiceStart(Sender: TService; var Started: Boolean);
begin
{ Default Values }
Started := False;
fServicePri := NORMAL_PRIORITY_CLASS;
fThreadPri := Integer(tpLower);
{ Set the Service Priority }
case fServicePri of
0: SetPriorityClass(GetCurrentProcess, IDLE_PRIORITY_CLASS);
1: SetPriorityClass(GetCurrentProcess, NORMAL_PRIORITY_CLASS);
2: SetPriorityClass(GetCurrentProcess, HIGH_PRIORITY_CLASS);
3: SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
end;
{ Attempt to start the thread, if it fails free it }
if _StartThread(fThreadPri) then
begin
{ Signal success back }
Started := True;
end
else
begin
{ Signal Error back }
Started := False;
{ Stop all activity }
_StopThread;
end;
end;
procedure TExampleService.ServiceStop(Sender: TService;
var Stopped: Boolean);
begin
{ Try to stop the thread - signal results back }
Stopped := _StopThread;
end;
procedure TExampleService.ServicePause(Sender: TService; var Paused: Boolean);
begin
{ Attempt to PAUSE the thread }
if Assigned(NTServiceThread) and (not NTServiceThread.Suspended) then
begin
{ Suspend the thread }
NTServiceThread.Suspend;
{ Return results }
Paused := (NTServiceThread.Suspended = True);
end
else
Paused := False;
end;
procedure TExampleService.ServiceContinue(Sender: TService;
var Continued: Boolean);
begin
{ Attempt to RESUME the thread }
if Assigned(NTServiceThread) and (NTServiceThread.Suspended) then
begin
{ Suspend the thread }
if NTServiceThread.Suspended then
NTServiceThread.Resume;
{ Return results }
Continued := (NTServiceThread.Suspended = False);
end
else
Continued := False;
end;
procedure TExampleService.ServiceShutdown(Sender: TService);
begin
{ Attempt to STOP (Terminate) the thread }
_StopThread;
end;
function TExampleService._StartThread(ThreadPri: Integer): Boolean;
begin
{ Default result }
Result := False;
{ Create Thread and Set Default Values }
if not Assigned(NTServiceThread) then
try
{ Create the Thread object }
NTServiceThread := TNTServiceThread.Create(True);
{ Set the Thread Priority }
case ThreadPri of
0: NTServiceThread.Priority := tpIdle;
1: NTServiceThread.Priority := tpLowest;
2: NTServiceThread.Priority := tpLower;
3: NTServiceThread.Priority := tpNormal;
4: NTServiceThread.Priority := tpHigher;
5: NTServiceThread.Priority := tpHighest;
end;
{ Set the Execution Interval of the Thread }
NTServiceThread.Interval := 2;
{ Start the Thread }
NTServiceThread.Resume;
{ Return success }
if not NTServiceThread.Suspended then
Result := True;
except
on E: Exception do
; // TODO: Exception Logging
end;
end;
function TExampleService._StopThread: Boolean;
begin
{ Default result }
Result := False;
{ Stop and Free Thread }
if Assigned(NTServiceThread) then
try
{ Terminate thread }
NTServiceThread.Terminate;
{ If it is suspended - Restart it }
if NTServiceThread.Suspended then
NTServiceThread.Resume;
{ Wait for it to finish }
NTServiceThread.WaitFor;
{ Free & NIL it }
NTServiceThread.Free;
NTServiceThread := nil;
{ Return results }
Result := True;
except
on E: Exception do
; // TODO: Exception Logging
end
else
begin
{ Return success - Nothing was ever started ! }
Result := True;
end;
end;
end.
{*
A Windows NT Service Thread
===========================
Author Kim Sandell
Email: kim.sandell@nsftele.com
*}
unit NTServiceThread;
interface
uses
Windows, Messages, SysUtils, Classes;
type
TNTServiceThread = class(TThread)
private
{ Private declarations }
public
{ Public declarations }
Interval: Integer;
procedure Execute; override;
published
{ Published declarations }
end;
implementation
{ TNTServiceThread }
procedure TNTServiceThread.Execute;
var
TimeOut: Integer;
begin
{ Do NOT free on termination - The Serivce frees the Thread }
FreeOnTerminate := False;
{ Set Interval }
TimeOut := Interval * 4;
{ Main Loop }
try
while not Terminated do
begin
{ Decrement timeout }
Dec(TimeOut);
if (TimeOut = 0) then
begin
{ Reset timer }
TimeOut := Interval * 4;
{ Beep once per x seconds }
Beep;
end;
{ Wait 1/4th of a second }
Sleep(250);
end;
except
on E: Exception do
; // TODO: Exception logging...
end;
{ Terminate the Thread - This signals Terminated=True }
Terminate;
end;
end.
2010. március 27., szombat
A component to prevent your form to be placed out of visible area
Problem/Question/Abstract:
Just put this component on your form and set as active and your form will not be moved out of screen visible area.
Answer:
unit ScreenSnap;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs,
ShellAPI;
type
TNoOutScreen =
class(TComponent)
private
OldWndProc: Pointer;
NewWndProc: Pointer;
FDistance: Integer;
procedure NewWndMethod(var Msg: TMessage);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Distance: Integer read FDistance write FDistance default 30;
end;
procedure Register;
implementation
constructor TNoOutScreen.Create(AOwner: TComponent);
begin
inherited;
if (not (csDesigning in ComponentState)) then
begin
NewWndProc := MakeObjectInstance(NewWndMethod);
OldWndProc := Pointer(SetWindowLong(TForm(Owner).Handle, gwl_WndProc,
LongInt(NewWndProc)));
end
else
begin
NewWndProc := nil;
OldWndProc := nil;
end;
FDistance := 30;
end;
destructor TNoOutScreen.Destroy;
begin
if (Assigned(NewWndProc)) then
FreeObjectInstance(NewWndProc);
inherited;
end;
procedure TNoOutScreen.NewWndMethod(var Msg: TMessage);
var
Pabd: APPBARDATA;
ScreenWidth: Integer;
ScreenHeight: Integer;
ScreenRect: TRect;
TaskBarRect: TRect;
begin
if (Msg.Msg = WM_EXITSIZEMOVE) then
begin
Pabd.cbSize := SizeOf(APPBARDATA);
SHAppBarMessage(ABM_GETTASKBARPOS, Pabd);
ScreenWidth := GetSystemMetrics(SM_CXSCREEN);
ScreenHeight := GetSystemMetrics(SM_CYSCREEN);
ScreenRect := Rect(0, 0, ScreenWidth, ScreenHeight);
TaskBarRect := Pabd.rc;
if ((TaskBarRect.Left = -2) and (TaskBarRect.Bottom = (ScreenHeight + 2)) and
(TaskBarRect.Right = (ScreenWidth + 2))) then
ScreenRect.Bottom := TaskBarRect.Top
else if ((TaskBarRect.Top = -2) and (TaskBarRect.Left = -2) and (TaskBarRect.Right
= (ScreenWidth + 2))) then
ScreenRect.Top := TaskBarRect.Bottom
else if ((TaskBarRect.Left = -2) and (TaskBarRect.Top = -2) and
(TaskBarRect.Bottom = (ScreenHeight + 2))) then
ScreenRect.Left := TaskBarRect.Right
else if ((TaskBarRect.Right = (ScreenWidth + 2)) and (TaskBarRect.Top = -2) and
(TaskBarRect.Bottom = (ScreenHeight + 2))) then
ScreenRect.Right := TaskBarRect.Left;
if (TForm(Owner).Left < (ScreenRect.Left + FDistance)) then
TForm(Owner).Left := ScreenRect.Left;
if (TForm(Owner).Top < (ScreenRect.Top + FDistance)) then
TForm(Owner).Top := ScreenRect.Top;
if ((TForm(Owner).Left + TForm(Owner).Width) > (ScreenRect.Right - FDistance))
then
TForm(Owner).Left := ScreenRect.Right - TForm(Owner).Width;
if ((TForm(Owner).Top + TForm(Owner).Height) > (ScreenRect.Bottom - FDistance))
then
TForm(Owner).Top := ScreenRect.Bottom - TForm(Owner).Height;
end;
Msg.Result := CallWindowProc(OldWndProc, TForm(Owner).Handle, Msg.Msg, Msg.WParam,
Msg.LParam);
end;
procedure Register;
begin
RegisterComponents('Christian', [TNoOutScreen]);
end;
end.
2010. március 26., péntek
Formating integers
Problem/Question/Abstract:
How do I format a integer into a nice format. Eg. 1,200,000
Answer:
Lots of people don't realise this, but FormatFloat can be used to format integers as well:
i := 1200000;
s := FormatFloat('#,0', i);
Memo1.lines.add(s);
This will display a formated version of 1200000. If you live in the USA you will get "1,200,000". Depending on the local settings of your Windows environment you might get "1.200.000" (eg you live in The Netherlands). You can also make different formats for negative numbers. Just checkout the Help on FormatFloat.
2010. március 25., csütörtök
An easy pulsing effect
Problem/Question/Abstract:
How can I create a pulsing effect on a color in my program easily?
Answer:
It is easy because you only have to set a TDateTime variable at the beginning of your program, and then you are able to use pulsing colors like in QBASIC the color codes after 16. But it's finer.
So I show a program code. On Form1 there is a Timer1. Its interval is 50. Then the code should look like:
unit uPulse;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
{the .interval should be about 50, if it's high,
the framerate will be low}
private
{ Private declarations }
public
{ Public declarations }
end;
TFadingMode = (fmFlash, fmPulse); {Two types of the effect.}
var
Form1: TForm1;
Starting_Time: TDateTime;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Starting_Time := Now;
end;
function TimeElapsed(Since: TDateTime): integer;
var
h, m, s, ms: Word;
begin
DecodeTime(Now - Since, h, m, s, ms);
Result := m * 60000 + s * 1000 + ms;
{
It may be: Result:=h*3600000+m*60000+s*1000+ms;
But not necessary.
}
end;
procedure TForm1.Timer1Timer(Sender: TObject);
const
PulsingRate = 500;
FadingMode = fmPulse;
{Modify these to get different results}
var
f: Byte; {the formula}
begin
case FadingMode of
{fades from color1 to color2 then turns back to color1}
fmFlash: f := 255 * (TimeElapsed(Starting_Time) mod PulsingRate) div PulsingRate;
{fades from color1 to color2 then fades back to color1}
fmPulse: f := 255 * Abs(TimeElapsed(Starting_Time) mod PulsingRate * 2 -
PulsingRate) div PulsingRate;
end;
{Then set the color with the RGB function}
Canvas.Brush.Color := RGB(f, 0, 0);
{
currently modifying RED, but you can get other colors like:
RGB(f,0,0) - black->red
RGB(255-f,0,0) - red->black
RGB(0,f,0) - black->green
RGB(50,150,f) - green->blue
RGB(f,f,0) - yellow->black
RGB(f,f,f) - white->black
}
{then do the job... paint something etc.}
Canvas.Ellipse(50, 50, 150, 150);
end;
end.
It's good because you can refresh the screen anytimes, the result depends only on the time elapsed. You don't have to declare a lot of variables.
And you can use the formula everywhere where is a color used. Just refresh it often!
How can I create a pulsing effect on a color in my program easily?
Answer:
It is easy because you only have to set a TDateTime variable at the beginning of your program, and then you are able to use pulsing colors like in QBASIC the color codes after 16. But it's finer.
So I show a program code. On Form1 there is a Timer1. Its interval is 50. Then the code should look like:
unit uPulse;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
{the .interval should be about 50, if it's high,
the framerate will be low}
private
{ Private declarations }
public
{ Public declarations }
end;
TFadingMode = (fmFlash, fmPulse); {Two types of the effect.}
var
Form1: TForm1;
Starting_Time: TDateTime;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Starting_Time := Now;
end;
function TimeElapsed(Since: TDateTime): integer;
var
h, m, s, ms: Word;
begin
DecodeTime(Now - Since, h, m, s, ms);
Result := m * 60000 + s * 1000 + ms;
{
It may be: Result:=h*3600000+m*60000+s*1000+ms;
But not necessary.
}
end;
procedure TForm1.Timer1Timer(Sender: TObject);
const
PulsingRate = 500;
FadingMode = fmPulse;
{Modify these to get different results}
var
f: Byte; {the formula}
begin
case FadingMode of
{fades from color1 to color2 then turns back to color1}
fmFlash: f := 255 * (TimeElapsed(Starting_Time) mod PulsingRate) div PulsingRate;
{fades from color1 to color2 then fades back to color1}
fmPulse: f := 255 * Abs(TimeElapsed(Starting_Time) mod PulsingRate * 2 -
PulsingRate) div PulsingRate;
end;
{Then set the color with the RGB function}
Canvas.Brush.Color := RGB(f, 0, 0);
{
currently modifying RED, but you can get other colors like:
RGB(f,0,0) - black->red
RGB(255-f,0,0) - red->black
RGB(0,f,0) - black->green
RGB(50,150,f) - green->blue
RGB(f,f,0) - yellow->black
RGB(f,f,f) - white->black
}
{then do the job... paint something etc.}
Canvas.Ellipse(50, 50, 150, 150);
end;
end.
It's good because you can refresh the screen anytimes, the result depends only on the time elapsed. You don't have to declare a lot of variables.
And you can use the formula everywhere where is a color used. Just refresh it often!
2010. március 24., szerda
MySQl experiences
Problem/Question/Abstract:
Some tips on using MySQl with delphi
Answer:
I’ve used Delphi (versions 4 & 5) with MySql versions 3.23 and higher for a few years and did one project which involved a data import utility reading data into the database and then displaying graphs on website using ISAPI dlls written in Delphi.
First tip- get yourself a good front end; my-manager from ems-tech.com or sqlyog are both excellent and simplify development enormously. Both cost but will repay the effort in next to no time.
Next download the zeos libraries from http://www.zeoslib.net/http://www.zeoslib.net/ - these are superb- though take a little getting used to. Installing is a bit of a pig-with 6 different folders needed that have to be added to the environment library path. The zeos libraries aren’t just for mysql BTW, other databases are supported as well.
Next, I’ve found it simplest to keep the appropriate libmysql.dll in the same folder as the Delphi application. At one point during my import utility development, things started going very strange – every time I tried to connect to a database, I got really odd access violations. A quick search determined I had 4 different libmysqls on the pc and my app was picking up the wrong one. It doesn’t help that utilities like sqlyog or my-manager install their own versions – this makes it easy to get confused. I ended up removing all but the newest libmysql dll and then having to reinstall sqlyog etc but that fixed it, - the website, code and sqlyog etc all worked fine- so if you get funny a/vs check your lib dlls.
I’ve always tended to develop using classes and that’s true with zeos- less hassle than wotrking with components on forms or data modules. The code accompanying this shows how to create a class- I call it TgvDB. This handles all the initialisation of properties etc and lets you create a TGVdb instance dynamically. This creates a Connection and query and simplifies returning data or running queries – if your variable is db1 then
NumRecords := db1.Select('select * from table'); // Return all records
Db1.exec('Update table2 set column1 = 0 ');
In all rows, sets column 1 = 0.
for returned data, use the queryrec property to get at the values.
while not db.queryrec.eof do
begin
value := db.queryrec.fields('column1').asstring;
db.queryrec.next;
end;
Code:
unit mysql;
interface
uses
ZConnection, Db, ZAbstractRODataset, ZAbstractDataset, ZDataset, zdbcIntfs, classes;
type
TGvDb = class
private
FDataBase: TZConnection;
FDB: TZQuery;
FLastError: string;
public
constructor Create; overload;
destructor Destroy; override;
function Select(SQL: string): integer;
function Exec(sql: string): boolean;
function LockTables(tablename: string): boolean;
procedure UnLockTables;
property QueryRec: TzQuery read FDB;
property LastError: string read FLastError write FLastError;
end;
function NewQuery: Tgvdb;
implementation
uses Sysutils;
function NewQuery: Tgvdb;
begin
Result := Tgvdb.Create;
end;
{ TGvDb }
function TGvDb.LockTables(tablename: string): boolean;
begin
fdb.Sql.Text := 'LOCK TABLES ' + Tablename;
try
fdb.ExecSql;
Result := True;
except
Result := False;
end;
end;
procedure TGvDb.UnlockTables;
begin
fdb.Sql.Text := 'UNLOCK TABLES';
fdb.ExecSql;
end;
constructor TGvDb.Create; // Used to create new cities
begin
FDatabase := TZConnection.Create(nil);
FDatabase.HostName := 'localhost';
FDatabase.User := '';
FDatabase.Password := '';
Fdatabase.Protocol := 'mysql';
FDatabase.Database := 'mysql';
FDatabase.Catalog := 'mysql';
FDatabase.Port := 3306;
Fdb := TZQuery.Create(nil);
Fdb.Connection := FDatabase;
end;
destructor TGvDb.Destroy;
begin
FDb.Free;
FDatabase.Free;
end;
function TGvDb.Exec(sql: string): boolean;
begin
Fdb.Active := false;
Fdb.Sql.Text := SQL;
try
Fdb.ExecSql;
FLastError := '';
result := true;
except
on E: Exception do
begin
Result := False;
FLastError := E.Message;
end;
end;
end;
function TGvDb.Select(SQL: string): integer;
begin
Fdb.Active := false;
Fdb.Sql.Text := SQL;
try
Fdb.Open;
FLastError := '';
result := Fdb.RecordCount;
except
on E: Exception do
begin
Result := 0;
FLastError := E.Message;
end;
end;
end;
end.
Some tips on using MySQl with delphi
Answer:
I’ve used Delphi (versions 4 & 5) with MySql versions 3.23 and higher for a few years and did one project which involved a data import utility reading data into the database and then displaying graphs on website using ISAPI dlls written in Delphi.
First tip- get yourself a good front end; my-manager from ems-tech.com or sqlyog are both excellent and simplify development enormously. Both cost but will repay the effort in next to no time.
Next download the zeos libraries from http://www.zeoslib.net/http://www.zeoslib.net/ - these are superb- though take a little getting used to. Installing is a bit of a pig-with 6 different folders needed that have to be added to the environment library path. The zeos libraries aren’t just for mysql BTW, other databases are supported as well.
Next, I’ve found it simplest to keep the appropriate libmysql.dll in the same folder as the Delphi application. At one point during my import utility development, things started going very strange – every time I tried to connect to a database, I got really odd access violations. A quick search determined I had 4 different libmysqls on the pc and my app was picking up the wrong one. It doesn’t help that utilities like sqlyog or my-manager install their own versions – this makes it easy to get confused. I ended up removing all but the newest libmysql dll and then having to reinstall sqlyog etc but that fixed it, - the website, code and sqlyog etc all worked fine- so if you get funny a/vs check your lib dlls.
I’ve always tended to develop using classes and that’s true with zeos- less hassle than wotrking with components on forms or data modules. The code accompanying this shows how to create a class- I call it TgvDB. This handles all the initialisation of properties etc and lets you create a TGVdb instance dynamically. This creates a Connection and query and simplifies returning data or running queries – if your variable is db1 then
NumRecords := db1.Select('select * from table'); // Return all records
Db1.exec('Update table2 set column1 = 0 ');
In all rows, sets column 1 = 0.
for returned data, use the queryrec property to get at the values.
while not db.queryrec.eof do
begin
value := db.queryrec.fields('column1').asstring;
db.queryrec.next;
end;
Code:
unit mysql;
interface
uses
ZConnection, Db, ZAbstractRODataset, ZAbstractDataset, ZDataset, zdbcIntfs, classes;
type
TGvDb = class
private
FDataBase: TZConnection;
FDB: TZQuery;
FLastError: string;
public
constructor Create; overload;
destructor Destroy; override;
function Select(SQL: string): integer;
function Exec(sql: string): boolean;
function LockTables(tablename: string): boolean;
procedure UnLockTables;
property QueryRec: TzQuery read FDB;
property LastError: string read FLastError write FLastError;
end;
function NewQuery: Tgvdb;
implementation
uses Sysutils;
function NewQuery: Tgvdb;
begin
Result := Tgvdb.Create;
end;
{ TGvDb }
function TGvDb.LockTables(tablename: string): boolean;
begin
fdb.Sql.Text := 'LOCK TABLES ' + Tablename;
try
fdb.ExecSql;
Result := True;
except
Result := False;
end;
end;
procedure TGvDb.UnlockTables;
begin
fdb.Sql.Text := 'UNLOCK TABLES';
fdb.ExecSql;
end;
constructor TGvDb.Create; // Used to create new cities
begin
FDatabase := TZConnection.Create(nil);
FDatabase.HostName := 'localhost';
FDatabase.User := '';
FDatabase.Password := '';
Fdatabase.Protocol := 'mysql';
FDatabase.Database := 'mysql';
FDatabase.Catalog := 'mysql';
FDatabase.Port := 3306;
Fdb := TZQuery.Create(nil);
Fdb.Connection := FDatabase;
end;
destructor TGvDb.Destroy;
begin
FDb.Free;
FDatabase.Free;
end;
function TGvDb.Exec(sql: string): boolean;
begin
Fdb.Active := false;
Fdb.Sql.Text := SQL;
try
Fdb.ExecSql;
FLastError := '';
result := true;
except
on E: Exception do
begin
Result := False;
FLastError := E.Message;
end;
end;
end;
function TGvDb.Select(SQL: string): integer;
begin
Fdb.Active := false;
Fdb.Sql.Text := SQL;
try
Fdb.Open;
FLastError := '';
result := Fdb.RecordCount;
except
on E: Exception do
begin
Result := 0;
FLastError := E.Message;
end;
end;
end;
end.
2010. március 23., kedd
Get all table names in a database
Problem/Question/Abstract:
at some point along the way, you will need to get all the table names from some database, you look at the help... not much help... you have to use an Alias and you don't want one... pretty simple anyway...
Answer:
the example you find at the Delphi 5 help:
MyStringList := TStringList.Create;
try
Session.GetTableNames('DBDEMOS', '*.db', False, False, MyStringList);
{ Add the table names to a list box }
ListBox1.Items = MyStringList;
finally
MyStringList.Free;
end;
which would've been easier to write just:
Session.GetTableNames('DBDEMOS', '*.db', False, False, ListBox1.Items);
here they use the Session component and an Alias 'DBDEMOS', but you don't have an Alias and you don't want to bother in creating one for the installation program, you wanna use just the typical database component...
then all you have to do is this:
drop your database component in the form, fill all the needed properties:
databasename, Drivername, etc and make this call:
database1.Session.GetTableNames(database1.DatabaseName, '', False, False,
ListBox1.Items)
here we use the embeded 'session' component and your own database name
both solutions give you the list of all the tables in ListBox1.Items, only in the second case you don't have an alias and you can directly specify the location of the database you need
important:
set the third parameter to 'false' if you're not using Paradox or dBASE databases, and
set the fourth parameter to 'false' if you want only the table names
...finally an example of this... I had a situation where I had this database with a variable number of tables on it... and I had to open all of them, I had a maximum of 5 tables, so I created an array of TTables (yes, an array)
Const MAX_TABLES=4
{.. }
database1: TDatabase;
Private
TableCount: Integer;
AllMyTables: array[0..MAX_TABLES] of TTable;
//then I created the tables at runtime
for X := 0 to MAX_TABLES do
begin
AllMyTables[X] := TTable.Create(Form1);
AllMyTables[X].database := database1
end
//then called the function to retrieve all the table names without using an
//Alias:
database1.Session.GetTableNames(database1.DatabaseName, '', False, False,
ListBox1.Items)
//Now I can open all the tables =o)
TableCount := ListBox1.Items.Count;
//note we save the 'TableCount' so later we
//can use it to iterate through the tables in the array
for X := 0 to TableCount do
begin
AllMyTables[X].TableName := ListBox1.Items[X];
AllMyTables[X].Active := True
end;
//of course, at the end don't forget to free the tables, since we created them
//dinamically
for X := 0 to MAX_TABLES do
begin
AllMyTables[X].Active := False;
AllMyTables[X].Free
end
of course I missed some optimizations or stuff, but I just wanted to give you the idea and an example so... I hope it is useful.
at some point along the way, you will need to get all the table names from some database, you look at the help... not much help... you have to use an Alias and you don't want one... pretty simple anyway...
Answer:
the example you find at the Delphi 5 help:
MyStringList := TStringList.Create;
try
Session.GetTableNames('DBDEMOS', '*.db', False, False, MyStringList);
{ Add the table names to a list box }
ListBox1.Items = MyStringList;
finally
MyStringList.Free;
end;
which would've been easier to write just:
Session.GetTableNames('DBDEMOS', '*.db', False, False, ListBox1.Items);
here they use the Session component and an Alias 'DBDEMOS', but you don't have an Alias and you don't want to bother in creating one for the installation program, you wanna use just the typical database component...
then all you have to do is this:
drop your database component in the form, fill all the needed properties:
databasename, Drivername, etc and make this call:
database1.Session.GetTableNames(database1.DatabaseName, '', False, False,
ListBox1.Items)
here we use the embeded 'session' component and your own database name
both solutions give you the list of all the tables in ListBox1.Items, only in the second case you don't have an alias and you can directly specify the location of the database you need
important:
set the third parameter to 'false' if you're not using Paradox or dBASE databases, and
set the fourth parameter to 'false' if you want only the table names
...finally an example of this... I had a situation where I had this database with a variable number of tables on it... and I had to open all of them, I had a maximum of 5 tables, so I created an array of TTables (yes, an array)
Const MAX_TABLES=4
{.. }
database1: TDatabase;
Private
TableCount: Integer;
AllMyTables: array[0..MAX_TABLES] of TTable;
//then I created the tables at runtime
for X := 0 to MAX_TABLES do
begin
AllMyTables[X] := TTable.Create(Form1);
AllMyTables[X].database := database1
end
//then called the function to retrieve all the table names without using an
//Alias:
database1.Session.GetTableNames(database1.DatabaseName, '', False, False,
ListBox1.Items)
//Now I can open all the tables =o)
TableCount := ListBox1.Items.Count;
//note we save the 'TableCount' so later we
//can use it to iterate through the tables in the array
for X := 0 to TableCount do
begin
AllMyTables[X].TableName := ListBox1.Items[X];
AllMyTables[X].Active := True
end;
//of course, at the end don't forget to free the tables, since we created them
//dinamically
for X := 0 to MAX_TABLES do
begin
AllMyTables[X].Active := False;
AllMyTables[X].Free
end
of course I missed some optimizations or stuff, but I just wanted to give you the idea and an example so... I hope it is useful.
2010. március 22., hétfő
Convert long IP addresses to short ones and vice versa
Problem/Question/Abstract:
How to convert long IP addresses to short ones and vice versa
Answer:
IP converting (long/ short). Example: 34753784563 instead of 193.234.22.12, used by different applications like IRC (DCC algorithm):
Convert long IP addresses to short ones:
function shortIP(const s: string): string;
var
Ip: int64;
a, b, c, d: Byte;
begin
IP := StrToInt64(s);
a := (IP and $FF000000) shr 24;
b := (IP and $00FF0000) shr 16;
c := (IP and $0000FF00) shr 8;
d := (IP and $000000FF);
Result := Format('%d.%d.%d.%d', [a, b, c, d]);
end;
Convert short IP addresses to long ones:
function LongIP(IP: string): string;
var
IPaddr: array[1..4] of Word;
Temp: string;
Res: DWord;
I: Integer;
begin
Temp := IP + '.';
for I := 1 to 4 do
begin
try
IPaddr[i] := StrToInt(copy(Temp, 1, pos('.', Temp) - 1));
Delete(temp, 1, pos('.', Temp));
if (IPaddr[i] > 255) then
raise Exception.Create('');
except
{Check the IP}
result := 'Invalid IP address.';
Exit;
end;
end;
Res := (ipaddr[1] shl 24) + ipaddr[1] + (ipaddr[2] shl 16) + ipaddr[2] +
(ipaddr[3] shl 8) + ipaddr[3] + (ipaddr[4]);
Result := Format('%u', [res]);
end;
How to convert long IP addresses to short ones and vice versa
Answer:
IP converting (long/ short). Example: 34753784563 instead of 193.234.22.12, used by different applications like IRC (DCC algorithm):
Convert long IP addresses to short ones:
function shortIP(const s: string): string;
var
Ip: int64;
a, b, c, d: Byte;
begin
IP := StrToInt64(s);
a := (IP and $FF000000) shr 24;
b := (IP and $00FF0000) shr 16;
c := (IP and $0000FF00) shr 8;
d := (IP and $000000FF);
Result := Format('%d.%d.%d.%d', [a, b, c, d]);
end;
Convert short IP addresses to long ones:
function LongIP(IP: string): string;
var
IPaddr: array[1..4] of Word;
Temp: string;
Res: DWord;
I: Integer;
begin
Temp := IP + '.';
for I := 1 to 4 do
begin
try
IPaddr[i] := StrToInt(copy(Temp, 1, pos('.', Temp) - 1));
Delete(temp, 1, pos('.', Temp));
if (IPaddr[i] > 255) then
raise Exception.Create('');
except
{Check the IP}
result := 'Invalid IP address.';
Exit;
end;
end;
Res := (ipaddr[1] shl 24) + ipaddr[1] + (ipaddr[2] shl 16) + ipaddr[2] +
(ipaddr[3] shl 8) + ipaddr[3] + (ipaddr[4]);
Result := Format('%u', [res]);
end;
2010. március 21., vasárnap
Updates with Oracle
Problem/Question/Abstract:
How can I commit the Updates with Oracle 8.0.6 and Delphi 5.0 ? I don't want to use a DBNavigator, but buttons.
Answer:
I would add a new TQuery component to your Databasa module. Call it CommitQuery. Edit the SQL property of CommitQuery by writing 'commit'; in the editor. And change the database alias so that the query will be posted in the right database. Create the ButtonOnClick procedure and add the line CommitQuery.Execute;
Or you can do something like this
Database1.startTransaction;
try
Somequery.edit;
// Do someting wityh the query.
Somequery.post;
Database1.Commmit;
except
Database1.Rollback
end;
2010. március 20., szombat
Center a Form efficiently
Problem/Question/Abstract:
Center a Form efficiently
Answer:
To center a form after having changed its dimensions at run-time,
the poScreenCenter won't do it - it only works when the form is shown.
The following code shows 2 solutions how to handle this "problem":
// this works, but the form will be redrawn two times
// (one redraw for each assignment)
Form1.Left := (Screen.Width div 2) - (Form.Width div 2);
Form1.Top := (Screen.Height div 2) - (Form.Height div 2);
// this is better.. the form is redrawn only once
Form1.SetBounds((Screen.Width - AForm.Width) div 2,
(Screen.Height - AForm.Height) div 2,
ATop, Form1.Width, Form1.Height);
2010. március 19., péntek
How to drag and drop text from a TRichEdit to other components
Problem/Question/Abstract:
I would like to select text in a TRichEdit control, then drag and drop the text on another (non TRichEdit) component (ie. TEdit or TMemo). Simulating this behavior would be fine. The drag- related events are not firing when I drag text, so I assume the drag and drop behavior is embedded in the Windows control. But I don't see any drag-related messages in the Windows SDK online help.
Answer:
I've got a unit "uGenDragDrop", that implements IDropTarget (amongst others), and allows you to easily add OLE Drag and Drop support to any Delphi component (i.e. allow you to drag and drop not only within a Delphi application, but also in and out of Delphi applications).
Here is a snippet of code that implements OLE drop support for a TMemo on a form.
uses
uGenDragDrop;
procedure TForm1.FormCreate(Sender: TObject);
begin
DTMemo := TDropTarget.Create(Memo1);
DTMemo.AddFormat(CF_TEXT, [asComplete], [meGlobMemory]);
end;
procedure TForm1.Memo1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := TRUE;
end;
procedure TForm1.Memo1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
Memo1.Lines.Add((Source as TStorageMedium).GetText);
end;
2010. március 18., csütörtök
Check the BDE version in your application
Problem/Question/Abstract:
Check the BDE version in your application
Answer:
Sometimes you may want to have your application check that the proper BDE version is installed.
Use the following code:
program BDEVersion;
uses
Forms,
DB,
BDE,
Dialogs,
SysUtils;
{$R *.RES}
var
ThisVersion: SYSVersion;
begin
DbiGetSysVersion(ThisVersion);
ShowMessage('BORLAND DATABASE ENGINE VERSION = ' + IntToStr(ThisVersion.iVersion));
end.
2010. március 17., szerda
An almost complete include file to detect different versions of Borland compilers
Problem/Question/Abstract:
An almost complete include file to detect different versions of Borland compilers
Answer:
File: Compilers.inc
Include file to determine which compiler is currently being used to build the project/ component. This file uses ideas from Brad Stowers DFS.inc file (www.delphifreestuff.com). The following symbols are defined:
// COMPILER_1 : Kylix/Delphi/BCB 1.x is the compiler.
// COMPILER_1_UP : Kylix/Delphi/BCB 1.x or higher is the compiler.
// COMPILER_2 : Kylix/Delphi 2.x or BCB 1.x is the compiler.
// COMPILER_2_UP : Kylix/Delphi 2.x or higher, or BCB 1.x or higher is the compiler.
// COMPILER_3 : Kylix/Delphi/BCB 3.x is the compiler.
// COMPILER_3_UP : Kylix/Delphi/BCB 3.x or higher is the compiler.
// COMPILER_4 : Kylix/Delphi/BCB 4.x is the compiler.
// COMPILER_4_UP : Kylix/Delphi/BCB 4.x or higher is the compiler.
// COMPILER_5 : Kylix/Delphi/BCB 5.x is the compiler.
// COMPILER_5_UP : Kylix/Delphi/BCB 5.x or higher is the compiler.
// COMPILER_6 : Kylix/Delphi/BCB 6.x is the compiler.
// COMPILER_6_UP : Kylix/Delphi/BCB 6.x or higher is the compiler.
// COMPILER_7 : Kylix/Delphi/BCB 7.x is the compiler.
// COMPILER_7_UP : Kylix/Delphi/BCB 7.x or higher is the compiler.
//
// Only defined if Windows is the target:
// CPPB : Any version of BCB is being used.
// CPPB_1 : BCB v1.x is being used.
// CPPB_3 : BCB v3.x is being used.
// CPPB_3_UP : BCB v3.x or higher is being used.
// CPPB_4 : BCB v4.x is being used.
// CPPB_4_UP : BCB v4.x or higher is being used.
// CPPB_5 : BCB v5.x is being used.
// CPPB_5_UP : BCB v5.x or higher is being used.
//
// Only defined if Windows is the target:
// DELPHI : Any version of Delphi is being used.
// DELPHI_1 : Delphi v1.x is being used.
// DELPHI_2 : Delphi v2.x is being used.
// DELPHI_2_UP : Delphi v2.x or higher is being used.
// DELPHI_3 : Delphi v3.x is being used.
// DELPHI_3_UP : Delphi v3.x or higher is being used.
// DELPHI_4 : Delphi v4.x is being used.
// DELPHI_4_UP : Delphi v4.x or higher is being used.
// DELPHI_5 : Delphi v5.x is being used.
// DELPHI_5_UP : Delphi v5.x or higher is being used.
// DELPHI_6 : Delphi v6.x is being used.
// DELPHI_6_UP : Delphi v6.x or higher is being used.
// DELPHI_7 : Delphi v7.x is being used.
// DELPHI_7_UP : Delphi v7.x or higher is being used.
//
// Only defined if Linux is the target:
// KYLIX : Any version of Kylix is being used.
// KYLIX_1 : Kylix 1.x is being used.
// KYLIX_1_UP : Kylix 1.x or higher is being used.
{$IFDEF Win32}
{$IFDEF VER150}
{$DEFINE COMPILER_7}
{$DEFINE DELPHI}
{$DEFINE DELPHI_7}
{$ENDIF}
{$IFDEF VER140}
{$DEFINE COMPILER_6}
{$DEFINE DELPHI}
{$DEFINE DELPHI_6}
{$ENDIF}
{$IFDEF VER130}
{$DEFINE COMPILER_5}
{$IFDEF BCB}
{$DEFINE CPPB}
{$DEFINE CPPB_5}
{$ELSE}
{$DEFINE DELPHI}
{$DEFINE DELPHI_5}
{$ENDIF}
{$ENDIF}
{$IFDEF VER125}
{$DEFINE COMPILER_4}
{$DEFINE CPPB}
{$DEFINE CPPB_4}
{$ENDIF}
{$IFDEF VER120}
{$DEFINE COMPILER_4}
{$DEFINE DELPHI}
{$DEFINE DELPHI_4}
{$ENDIF}
{$IFDEF VER110}
{$DEFINE COMPILER_3}
{$DEFINE CPPB}
{$DEFINE CPPB_3}
{$ENDIF}
{$IFDEF VER100}
{$DEFINE COMPILER_3}
{$DEFINE DELPHI}
{$DEFINE DELPHI_3}
{$ENDIF}
{$IFDEF VER93}
{$DEFINE COMPILER_2} // C_UP_UPB v1 compiler is really v2
{$DEFINE CPPB}
{$DEFINE CPPB_1}
{$ENDIF}
{$IFDEF VER90}
{$DEFINE COMPILER_2}
{$DEFINE DELPHI}
{$DEFINE DELPHI_2}
{$ENDIF}
{$IFDEF VER80}
{$DEFINE COMPILER_1}
{$DEFINE DELPHI}
{$DEFINE DELPHI_1}
{$ENDIF}
{$IFDEF COMPILER_1}
{$DEFINE COMPILER_1_UP}
{$ENDIF}
{$IFDEF COMPILER_2}
{$DEFINE COMPILER_1_UP}
{$DEFINE COMPILER_2_UP}
{$ENDIF}
{$IFDEF COMPILER_3}
{$DEFINE COMPILER_1_UP}
{$DEFINE COMPILER_2_UP}
{$DEFINE COMPILER_3_UP}
{$ENDIF}
{$IFDEF COMPILER_4}
{$DEFINE COMPILER_1_UP}
{$DEFINE COMPILER_2_UP}
{$DEFINE COMPILER_3_UP}
{$DEFINE COMPILER_4_UP}
{$ENDIF}
{$IFDEF COMPILER_5}
{$DEFINE COMPILER_1_UP}
{$DEFINE COMPILER_2_UP}
{$DEFINE COMPILER_3_UP}
{$DEFINE COMPILER_4_UP}
{$DEFINE COMPILER_5_UP}
{$ENDIF}
{$IFDEF COMPILER_6}
{$DEFINE COMPILER_1_UP}
{$DEFINE COMPILER_2_UP}
{$DEFINE COMPILER_3_UP}
{$DEFINE COMPILER_4_UP}
{$DEFINE COMPILER_5_UP}
{$DEFINE COMPILER_6_UP}
{$ENDIF}
{$IFDEF COMPILER_7}
{$DEFINE COMPILER_1_UP}
{$DEFINE COMPILER_2_UP}
{$DEFINE COMPILER_3_UP}
{$DEFINE COMPILER_4_UP}
{$DEFINE COMPILER_5_UP}
{$DEFINE COMPILER_6_UP}
{$DEFINE COMPILER_7_UP}
{$ENDIF}
{$IFDEF DELPHI_2}
{$DEFINE DELPHI_2_UP}
{$ENDIF}
{$IFDEF DELPHI_3}
{$DEFINE DELPHI_2_UP}
{$DEFINE DELPHI_3_UP}
{$ENDIF}
{$IFDEF DELPHI_4}
{$DEFINE DELPHI_2_UP}
{$DEFINE DELPHI_3_UP}
{$DEFINE DELPHI_4_UP}
{$ENDIF}
{$IFDEF DELPHI_5}
{$DEFINE DELPHI_2_UP}
{$DEFINE DELPHI_3_UP}
{$DEFINE DELPHI_4_UP}
{$DEFINE DELPHI_5_UP}
{$ENDIF}
{$IFDEF DELPHI_6}
{$DEFINE DELPHI_2_UP}
{$DEFINE DELPHI_3_UP}
{$DEFINE DELPHI_4_UP}
{$DEFINE DELPHI_5_UP}
{$DEFINE DELPHI_6_UP}
{$ENDIF}
{$IFDEF DELPHI_7}
{$DEFINE DELPHI_2_UP}
{$DEFINE DELPHI_3_UP}
{$DEFINE DELPHI_4_UP}
{$DEFINE DELPHI_5_UP}
{$DEFINE DELPHI_6_UP}
{$DEFINE DELPHI_7_UP}
{$ENDIF}
{$IFDEF CPPB_3}
{$DEFINE CPPB_3_UP}
{$ENDIF}
{$IFDEF CPPB_4}
{$DEFINE CPPB_3_UP}
{$DEFINE CPPB_4_UP}
{$ENDIF}
{$IFDEF CPPB_5}
{$DEFINE CPPB_3_UP}
{$DEFINE CPPB_4_UP}
{$DEFINE CPPB_5_UP}
{$ENDIF}
{$IFDEF CPPB_3_UP}
// C++ Builder requires this if you use Delphi components in run-time packages.
{$OBJEXPORTALL On}
{$ENDIF}
{$ELSE (not Windows)}
// Linux is the target
{$DEFINE KYLIX}
{$DEFINE KYLIX_1}
{$DEFINE KYLIX_1_UP}
{$ENDIF}
2010. március 16., kedd
How to send mail in HTML format from a Delphi application
Problem/Question/Abstract:
I would like to create a program which is able to send mail in HTML format. I tested some code but got a 'Connection Failed' message when I attempted to send the mail.
Answer:
You may have to logon to the smtp server via Pop3 first:
var
oLogon: TNMPop3;
oMail: TNMSmtp;
begin
oLogon := TNMPop3.Create(self);
try
with oLogon do
begin
Host := 'pop.mail.yahoo.com';
UserID := 'user';
Password := 'password';
end;
oLogon.Connect;
oLogon.Disconnect;
finally
oLogon.Free;
end;
oMail := TNMSmtp.Create(self);
with oMail do
begin
try
Host := 'smtp.mail.yahoo.com';
Port := 25;
UserID := 'YourID';
Connect;
SubType := mtHTML;
{ set all other properties, e. g. FromName, FromAddress, ReplyTo, Subject, etc }
PostMessage.FromAddress := 'yourname@yahoo.com';
PostMessage.FromName := 'YourName';
PostMessage.Subject := 'My First HTML mail';
PostMessage.ToAddress.Add('yourname@yahoo.com');
{Replace [ ] brackets in the following three lines with < > ones}
PostMessage.Body.Add('[html] [head] [/head]');
PostMessage.Body.Add('[body] [h1]My 1st html msg[/h1] [/body]');
PostMessage.Body.Add('[/html]');
Connect;
SendMail;
Disconnect;
finally
Free;
end;
end;
end;
2010. március 15., hétfő
Create a TPaintBox that can be scrolled by mouse wheel
Problem/Question/Abstract:
I have a TPaintBox that shows part of my bitmap and I would like to be able to scroll by using the mouse wheel, both up and down and all 4 directions. I assume this needs some message handlers, but have no idea of which ones.
Answer:
The WM_MOUSEWHEEL message is sent to the focus window when the mouse wheel is rotated. As far as I know, it's impossible to do this with a TPaintBox as it's a TGraphicControl descendant and can't receive this message. The solution could be to place it in a scrollbox. Then define OnMouseWheelDown and OnMouseWheelUp event handlers to the scroll box and insert a ScrollBy(..) method call. Also, you'll need your scrollbox to receive the focus. And the last thing is to write a CM_HITTEST message handler for the paint box. In the message result return the HTNOWHERE constant. This will force the parent scrollbox to handle mouse messages on its own. Here's an example:
{ ... }
TMyPaintBox = class(TPaintBox)
protected
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
end;
{ ... }
procedure TMyPaintBox.CMHitTest(var Message: TCMHitTest);
begin
Message.Result := Windows.HTNOWHERE;
end;
Here's how scrollbox events handlers can look like
{scrolls content to the right direction}
procedure TForm1.MyScrollBox1MouseWheelDown(Sender: TObject;
Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
MyScrollBox1.ScrollBy(5, 0);
end;
{scrolls content to the left}
procedure TForm1.MyScrollBox1MouseWheelUp(Sender: TObject;
Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
MyScrollBox1.ScrollBy(-5, 0);
end;
{sets focus to the scrollbox}
procedure TForm1.MyScrollBox1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
MyScrollBox1.SetFocus;
end;
2010. március 14., vasárnap
How to display different icons depending on the screen resolution
Problem/Question/Abstract:
How to display different icons depending on the screen resolution
Answer:
Just check for the current resolution and change the icon handle of the application. Of course, you have to create new icons in your resource. Put this in the project (.DPR) file of your application source:
Application.Initialize;
Application.CreateForm(TForm1, Form1);
case GetDeviceCaps(GetDC(Form1.Handle), HORZRES) of
640: Application.Icon.Handle := LoadIcon(hInstance, 'ICON640');
800: Application.Icon.Handle := LoadIcon(hInstance, 'ICON800');
1024: Application.Icon.Handle := LoadIcon(hInstance, 'ICON1024');
1280: Application.Icon.Handle := LoadIcon(hInstance, 'ICON1280');
end;
Application.Run;
2010. március 13., szombat
How to get the font settings as defined in the Windows display properties
Problem/Question/Abstract:
I'm using a TMenuBar control and I would like my menus to be the same size as defined in the Windows display properties. The same goes for the rest of my application. How can I find out what the windows settings for fonts and sizes for menus are?
Answer:
function GetCaptionFont: TFont;
var
ncMetrics: TNonClientMetrics;
begin
ncMetrics.cbSize := sizeof(TNonClientMetrics);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(TNonClientMetrics), @ncMetrics,
0);
Result := TFont.Create;
Result.Handle := CreateFontIndirect(ncMetrics.lfCaptionFont);
end;
In Windows XP, applications seem to have a concept of a system font. That is, labels and captions seem to appear in another font other than MS Sans Serif. I was wondering if it is possible to detect this font and use it in Delphi applications. Currently, everything seems hard-coded to MS Sans Serif.
procedure GetCaptionFont(afont: TFont);
var
ncMetrics: TNonClientMetrics;
begin
assert(assigned(afont));
ncMetrics.cbSize := sizeof(TNonClientMetrics);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof(TNonClientMetrics), @ncMetrics, 0);
afont.Handle := CreateFontIndirect(ncMetrics.lfCaptionFont);
end;
The TNonClientMetrics structure also contains information on other fonts used in the non-client area:
lfCaptionFont: Font used in regular captions
lfSmCaptionFont: Font used in small captions
lfMenuFont: Font used in menus
lfStatusFont: Font used in status bars
lfMessageFont: Font used in message boxes
The problem with changing the forms font (and with it all controls that have Parentfont = true) is that it will likely change the size of some controls that autosize depending on fonts, and that can screw up your layout.
2010. március 12., péntek
How to compare two pf24bit images
Problem/Question/Abstract:
How to compare two pf24bit images
Answer:
The code below compares two pf24bit images and tells you if they are alike or not. It also gives you the lines and pixels that are different:
function Tbilde_form.CompareBitmaps(B1, B2: TBitmap): boolean;
var
ps1, pr1, ps, pr: PRGBTriple;
I, J, Bps: Integer;
tid: TDateTime;
function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint;
begin
Dec(Alignment);
Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment;
Result := Result div 8;
end;
begin
tid := now;
Result := True;
ps1 := b1.ScanLine[0];
pr1 := b2.ScanLine[0];
Bps := BytesPerScanLine(b1.Width, 24, 32);
for I := 0 to b1.Height - 1 do
begin
ps := PRGBTriple(PChar(ps1) - Bps * I);
pr := PRGBTriple(PChar(pr1) - Bps * I);
for J := 0 to b1.Width - 1 do
begin
if not CompareMem(Pr, Ps, SizeOf(TRGBTriple)) then
begin
memo1.lines.Add('Line:' + inttostr(I) + ' point: ' + inttostr(j));
Result := False;
{Break}
end;
Inc(pr);
Inc(ps)
end;
{if not Result then Break}
end;
tid_label.caption := timetostr(now - tid);
end;
2010. március 11., csütörtök
Check whether a user has a shortcut installed
Problem/Question/Abstract:
Check whether a user has a shortcut installed
Answer:
The following routine checks whether a shortcut or a file with a given name is either on the desktop, in the start menu or in its programs submenu. It will both check in the user's private desktop/ start menu.. as in the all-users settings.
The return value shows where the first installation was found, it may be used as in .FormCreate() at the bottom of the example.
Because shortcuts are just files, it is not case-sensitive.
LinkExists ('SourceCoder') = LinkExists ('sourcecoder')
uses
Registry;
type
TInstallationPlace = (le_None, le_CommonDesktop, le_CommonProgs, le_CommonStart,
le_UserDesktop, le_UserProgs, le_UserStart);
// check whether a shortcut or a file with name s is either on
// the desktop, in the start menu or in its programs submenu.
function LinkExists(const s: string): TInstallationPlace;
var
cDesktop,
cProgs,
cStart,
uDesktop,
uProgs,
uStart: string;
function myExists(const s: string): boolean;
begin
// s can be directory or a file, so FileExists() won't do it..
myExists := FileGetAttr(s) >= 0;
end;
begin
// check whether we have the link in All_User's Desktop!
cDesktop := '';
cProgs := '';
cStart := '';
uDesktop := '';
uProgs := '';
uStart := '';
with TRegistry.Create do
begin
RootKey := HKEY_LOCAL_MACHINE;
if OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',
false) then
begin
cDesktop := ReadString('Common Desktop');
cProgs := ReadString('Common Programs');
cStart := ReadString('Common Start Menu');
end;
CloseKey;
RootKey := HKEY_CURRENT_USER;
if OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',
false) then
begin
uDesktop := ReadString('Desktop');
uProgs := ReadString('Programs');
uStart := ReadString('Start Menu');
end;
CloseKey;
Free;
end;
// check in all 3 places for our link
Result := le_None;
s := '\' + s;
if myExists(cDesktop + s) then
Result := le_CommonDesktop
else if myExists(cProgs + s) then
Result := le_CommonProgs
else if myExists(cStart + s) then
Result := le_CommonStart
else if myExists(cDesktop + ChangeFileExt(s, '.lnk')) then
Result := le_CommonDesktop
else if myExists(cProgs + ChangeFileExt(s, '.lnk')) then
Result := le_CommonProgs
else if myExists(cStart + ChangeFileExt(s, '.lnk')) then
Result := le_CommonStart
else if myExists(uDesktop + s) then
Result := le_UserDesktop
else if myExists(uProgs + s) then
Result := le_UserProgs
else if myExists(uStart + s) then
Result := le_UserStart
else if myExists(uDesktop + ChangeFileExt(s, '.lnk')) then
Result := le_UserDesktop
else if myExists(uProgs + ChangeFileExt(s, '.lnk')) then
Result := le_UserProgs
else if myExists(uStart + ChangeFileExt(s, '.lnk')) then
Result := le_UserStart
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if LinkExists('SourceCoder') <> le_None then
ShowMessage('yes')
else
ShowMessage('no');
end;
2010. március 10., szerda
How to save a TCollectionItem from a component to a stream
Problem/Question/Abstract:
I have written a component which uses TCollections as properties. Now I need a way to save the TCollectionItems to a file. How can I do that?
Answer:
You may try these routines. I have tested them with TStatusBar.Panels collection and they worked for me:
procedure LoadCollectionFromStream(Stream: TStream; Collection: TCollection);
begin
with TReader.Create(Stream, 4096) do
try
CheckValue(vaCollection);
ReadCollection(Collection);
finally
Free;
end;
end;
procedure LoadCollectionFromFile(const FileName: string; Collection: TCollection);
var
FS: TFileStream;
begin
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadCollectionFromStream(FS, Collection);
finally
FS.Free;
end;
end;
procedure SaveCollectionToStream(Collection: TCollection; Stream: TStream);
begin
with TWriter.Create(Stream, 4096) do
try
WriteCollection(Collection);
finally
Free;
end;
end;
procedure SaveCollectionToFile(Collection: TCollection; const FileName: string);
var
FS: TFileStream;
begin
FS := TFileStream.Create(FileName, fmCreate or fmShareDenyWrite);
try
SaveCollectionToStream(Collection, FS);
finally
FS.Free;
end;
end;
Note: It's obvious, the Collection variable must point to the initialized instance of a TCollection
descendant.
2010. március 9., kedd
Change the position of a list item in a TListView (2)
Problem/Question/Abstract:
How can I swap two items (and their subitems) in a TListview? Is there a command ?
Answer:
Exchange two items in a TListView:
procedure TForm1.Button2Click(Sender: TObject);
var
temp: TListItem;
i1, i2: Integer;
begin
i1 := 0;
i2 := 0;
{ pick two items to exchange at random }
while i1 = i2 do
begin
i1 := Random(listview1.items.count);
i2 := Random(listview1.items.count);
end;
{ exchange them, need to create a temp item for this }
temp := TListitem.create(listview1.items);
try
temp.Assign(listview1.items[i1]);
listview1.items[i1].Assign(listview1.items[i2]);
listview1.items[i2].Assign(temp);
finally
temp.free
end;
end;
2010. március 8., hétfő
RTTI - determining property information
Problem/Question/Abstract:
A RTTI question - it is possible to determine if a certain property is Read-Only, Write-Only or stored?
Answer:
The following code checks whether a property can be written to, read or whether it is stored.
function IsWriteProp(Info: PPropInfo): Boolean;
begin
Result := Assigned(Info) and (Info^.SetProc <> nil)
end;
function IsReadProp: Boolean;
begin
Result := Assigned(Info) and (Info^.GetProc <> nil)
end;
function IsStoredProp: Boolean;
begin
Result := Assigned(Info) and TYPINFO.IsStoredProp(FObj, Info)
end;
2010. március 7., vasárnap
Debugging IIS5 the easy way
Problem/Question/Abstract:
IIS debugging is kind of painful... until you understand what is going on behind the scenes!
Answer:
Introduction
If you developed ISAPI dlls and tried to debug them under IIS5 you probabily went trough one of the most painful exercises on the Windows platform. "What doesn't kill you makes you stronger" they say, but there is a limit in my opinion. Well, tonight after having unsuccesfully tried all the "how-to"s on the web, I dediced to see what is going on and why it is (apparently) that difficult.
It turned out that it's very simple and what happens behind the scenes in nothing but regular COM stuff. The key steps are just 3. It's as easy as that! No registry changes or anything else.
Setting up the debugging environment
In this example I will assume your ISAPI dll runs in a virtual directory called "DebuggingIIS".
Open the Internet Services Manager utility located under Control Panel\Administrative Tools.
Find your virtual directory and after right-clicking on it and selecting "Properties" change it's Application protection to "High" as in the following screenshot.
What happened after you did it is IIS created a special COM+ application which will be responsible for loading your ISAPI dlls and everything that as to do with your virtual directory.
If you open up the Component Services up will see this:
Now, right click on the COM+ Application, select "Properties" and follow me through 2 additional steps before we are ready to start debugging directly from Delphi.
The first page that will appear is the following:
You will need the Application ID in Delphi so copy it by right clicking on it.
Now move to the page Identity and switch the default setting to "Interactive User" as for the following screenshot:
Belive it or not, we are done with IIS and COM+
Now you can go in your Delphi ISAPI project and after clicking on Run\Parameters enter the following parameters:
The value after ProcessID is the Application ID we copied in the Application Services snap in.
Happy debugging!
2010. március 6., szombat
How to display bitmaps in a TDBGrid
Problem/Question/Abstract:
How can I add a bitmap to an individual cell in a TDBGrid and save the grid as a bitmap afterwards?
Answer:
Solve 1:
To display a bitmap in a cell, set DefaultDrawing to False and create a DrawDataCell handler similar to the following:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState)
var
Graf: TBitmap;
begin
if Field is TBlobField then
begin
Graf := TBitmap.Create;
Graf.Assign(Field);
DBGrid1.Canvas.StretchDraw(Rect, Graf);
Graf.Free;
end
else
begin
DBGrid1.Canvas.TextOut(Rect.Left + 1, Rect.Top + 1, Field.DisplayText);
end;
end;
Solve 2:
To display the bitmap:
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
MyRect: TRect;
MyImageIndex: Integer;
begin
DBGrid1.Canvas.FillRect(Rect);
MyImageIndex := Column.Index mod 2;
ImageList1.Draw(DBGrid1.Canvas, Rect.Left + 2, Rect.Top + (Rect.Bottom - Rect.Top -
ImageList1.Height) div 2, MyImageIndex, Column.Grid.Enabled);
MyRect := Rect;
MyRect.Left := MyRect.Left + ImageList1.Width + 4;
DBGrid1.DefaultDrawColumnCell(MyRect, DataCol, Column, State);
end;
To save the grid:
procedure TForm1.Button1Click(Sender: TObject);
var
MyBitmap: TBitmap;
begin
MyBitmap := TBitmap.Create;
try
MyBitmap.Width := DBGrid1.ClientWidth;
MyBitmap.Height := DBGrid1.ClientHeight;
MyBitmap.Canvas.Brush := DBGrid1.Brush;
MyBitmap.Canvas.FillRect(DBGrid1.ClientRect);
MyBitmap.Canvas.Lock;
try
DBGrid1.PaintTo(MyBitmap.Canvas.Handle, 0, 0);
Clipboard.Assign(MyBitmap);
finally
MyBitmap.Canvas.Unlock;
end;
finally
MyBitmap.Free;
end;
end;
2010. március 5., péntek
How to get the position of the Windows Taskbar
Problem/Question/Abstract:
I want to get some desktop settings in variables, like background color etc. But I don't want to use the registry, does anybody know a different way? I also want to know the height of the taskbar, and the position of the taskbar (top of the screen, bottom, left or right).
Answer:
For the following example put a RadioGroup on your form and give it 5 items:
implementation
type
TTaskBarPosition = (tpHide, tpBottom, tpLeft, tpRight, tpTop);
function FindTaskBarPos(aWorkArea: TRect): TTaskBarPosition;
begin
if aWorkArea.Left <> 0 then
begin
Result := tpLeft;
Exit;
end;
if aWorkArea.Top <> 0 then
begin
Result := tpTop;
Exit;
end;
if aWorkArea.Right <> Screen.Width then
begin
Result := tpRight;
Exit;
end;
if aWorkArea.Bottom <> Screen.Height then
begin
Result := tpBottom;
Exit;
end;
Result := tpHide;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
WorkArea: TRect;
begin
Color := clBackground;
SystemParametersInfo(SPI_GETWORKAREA, 0, @WorkArea, 0);
RadioGroup1.ItemIndex := Ord(FindTaskBarPos(WorkArea));
end;
end.
2010. március 4., csütörtök
Create a semicolon delimited list of paths
Problem/Question/Abstract:
I'm looking for a component that will build a semicolon delimited list of paths, like the one Delphi presents you to build the include path, etc.
Answer:
No need for a component, just use straight Object Pascal. Example:
procedure TForm1.Button1Click(Sender: TObject);
var
sList: TStringList;
i, iRes: integer;
sTmp: string;
begin
sTmp := '$(DELPHI)\Lib;$(DELPHI)\Bin;$(DELPHI)\Imports;$(DELPHI)\Projects\Bpl';
sList := TStringList.Create;
try
iRes := Pos(';', sTmp);
while iRes > 0 do
begin
sList.Add(Copy(sTmp, 1, iRes - 1));
Delete(sTmp, 1, iRes);
iRes := Pos(';', sTmp);
end;
if sTmp <> EmptyStr then
sList.Add(sTmp);
showmessage(sList.Text);
sTmp := '';
for i := 0 to sList.Count - 1 do
if i < sList.Count - 1 then
sTmp := sTmp + sList[i] + ';'
else
sTmp := sTmp + sList[i];
showmessage(sTmp);
finally
FreeAndNil(sList);
end;
end;
2010. március 2., kedd
Call CopyFileEx and let the callback update a progress bar
Problem/Question/Abstract:
Does anyone have an example of using CopyFileEx with a CopyProgressRoutine? I have created a function that takes the same parameters as the CopyProgressRoutine, but when I pass it using @ or Addr() I get a Variable Required error message.
Answer:
Let's assume you call CopyFileEx and want the callback to update a progress bar. The callback cannot be an objects method but you can use the lpData parameter of CopyFileEx to pass any kind of data to the callback, e.g. a form reference. So, if you want to serve a progress form in the callback that would look like this (untested !):
type
TProgressForm = class(TForm)
AbortButton: TButton;
ProgressBar: TProgressBar;
procedure AbortButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
FCancel: BOOL;
end;
{form has fsStayOnTop formstyle!}
implementation
{$R *.DFM}
procedure TProgressForm.AbortButtonClick(Sender: TObject);
begin
FCancel := True;
end;
{Note: could use int64 instead of COMP, but that would make this D4 specific}
function CopyCallback(TotalFileSize, TotalBytesTransferred, StreamSize,
StreamBytesTransferred: COMP; dwStreamNumber, dwCallbackReason: DWORD;
hSourceFile, hDestinationFile: THandle; progressform: TProgressForm): DWORD; stdcall;
var
newpos: Integer;
begin
Result := PROCESS_CONTINUE;
if dwCallbackReason = CALLBACK_CHUNK_FINISHED then
begin
newpos := Round(TotalBytesTransferred / TotalFileSize * 100);
with progressform.Progressbar do
if newpos <> Position then
Position := newpos;
Application.ProcessMessages;
end;
end;
function DoFilecopy(const source, target: string): Boolean;
var
progressform: TProgressForm;
begin
progressform := TProgressform.Create;
try
progressform.Show;
Application.ProcessMessages;
Result := CopyFileEx(PChar(source), PChar(target), @CopyCallback,
Pointer(progressform), @progressform.FCancel, 0);
finally
progressform.Hide;
progressform.free;
end;
end;
2010. március 1., hétfő
Get the TObject from an IInterface
Problem/Question/Abstract:
I'm trying to find a way to get the TObject reference from an IInterface object. I know that if the interface loses the reference the object will destroy but I've got a special need. The environment is based on interfaces but there is one component that can't use interfaces. So to work around it I assign the interface to a local variable, convert the interface to an object, call the component and when all is done get rid of the interface variable. Is this the right approach or can anything go wrong?
Answer:
function GetImplementingObject(const I: IInterface): TObject;
const
AddByte = $04244483; {opcode for ADD DWORD PTR [ESP+4], Shortint}
AddLong = $04244481; {opcode for ADD DWORD PTR [ESP+4], Longint}
type
PAdjustSelfThunk = ^TAdjustSelfThunk;
TAdjustSelfThunk = packed record
case AddInstruction: longint of
AddByte: (AdjustmentByte: shortint);
AddLong: (AdjustmentLong: longint);
end;
PInterfaceMT = ^TInterfaceMT;
TInterfaceMT = packed record
QueryInterfaceThunk: PAdjustSelfThunk;
end;
TInterfaceRef = ^PInterfaceMT;
var
QueryInterfaceThunk: PAdjustSelfThunk;
begin
Result := Pointer(I);
if Assigned(Result) then
try
QueryInterfaceThunk := TInterfaceRef(I)^.QueryInterfaceThunk;
case QueryInterfaceThunk.AddInstruction of
AddByte: Inc(PChar(Result), QueryInterfaceThunk.AdjustmentByte);
AddLong: Inc(PChar(Result), QueryInterfaceThunk.AdjustmentLong);
else
Result := nil;
end;
except
Result := nil;
end;
end;
Feliratkozás:
Bejegyzések (Atom)