2007. január 31., szerda
Threaded Brute Forcing Class
Problem/Question/Abstract:
How to create a simple brute forcing engine in a delphi class.
Answer:
{-----------------------------------------------------------------------------
Unit Name: classThreadBruteForce
Version: 1.0
Release Date: 21-Aug-2002
Compiler directives:
TINY - removes unnessecary error messages. test that output
is not empty
and
OPTIMIZE (less information is available)
Purpose:
Description:
A TThread which generates brute force combinations through the
onDo event.
Notes:
Charset contains the characters (these are sorted internally)
onFinished event provided
Not exactly fast but it does the job.
Use it like this:-
brute := TBruteThread.create(true);
brute.charset := 'abcdefghijklmnopqrstuvwxyz'; // Chars to brute
brute.numCharacters := 5; // Max chars
brute.onDo := Form1ThreadOnDo;
brute.resume;
Dependancies:
History:
Copyright 2002 by Stewart Moss
All rights reserved.
-----------------------------------------------------------------------------}
unit classBruteForce;
interface
uses classes, sysutils;
type
TBruteThread = class(TThread)
private
FNumChars: Integer;
FCharset: string;
FonDo: TNotifyEvent;
FonFinished: TNotifyEvent;
CharCount: string;
minChar: char;
maxChar: char;
imaxChar: integer;
incBruteLock: boolean;
// locks the incBrute function
procedure init;
function incBrute(posi: integer): integer;
function StringBubbleSort(StrIn: string): string;
public
{$IFNDEF OPTIMIZE}
BruteCount: integer; // not recommended on large bruteforce,
// use your own counter
{$ENDIF}
BruteResult: string;
procedure execute; override;
published
property onDo: TNotifyEvent read FonDo write FonDo;
property onFinished: TNotifyEvent read FonFinished write FonFinished;
property CharSet: string read FCharset write FCharset;
property numCharacters: Integer read FNumChars write FNumChars;
end;
implementation
{ TBruteThread }
procedure TBruteThread.execute;
var
loop: integer;
tmpstr: string;
begin
if FNumChars <= 0 then
begin
{$IFNDEF TINY}
raise exception.create('invalid Numchars');
{$ENDIF}
{$IFDEF TINY}
exit;
{$ENDIF}
end;
if FCharSet = '' then
begin
{$IFNDEF TINY}
raise exception.create('Charset is blank');
{$ENDIF}
{$IFDEF TINY}
exit;
{$ENDIF}
end;
init;
while (not terminated) do
begin
if incbrute(1) > FNumChars then
break;
loop := 0;
bruteresult := '';
while loop < FNumChars do
begin
inc(loop);
if charcount[loop] = #0 then
break;
// speed optimization
tmpstr := BruteResult;
BruteResult := tmpstr + charcount[loop];
end;
{$IFNDEF OPTIMIZE}
inc(Brutecount);
{$ENDIF}
if assigned(onDo) then
onDo(Self);
end;
if assigned(onFinished) then
onFinished(Self);
end;
{-----------------------------------------------------------------------------
Procedure: incBrute
Arguments: posi: integer
Result: integer
Purpose: Recurive
Description:
This function brutes
Copyright 2002 by Stewart Moss
All rights reserved.
-----------------------------------------------------------------------------}
function TBruteThread.incBrute(posi: integer): integer;
var
tmpint: integer;
bufferpos: integer;
begin
result := posi;
bufferpos := pos(charcount[posi], FCharset);
charcount[posi] := FCharset[bufferpos + 1];
if FCharset[Bufferpos] = maxchar then
begin
charcount[posi] := minchar;
tmpint := incBrute(posi + 1);
if tmpint > FnumChars then
result := tmpint;
end;
end;
procedure TBruteThread.init;
var
loop: integer;
begin
FCharSet := StringBubbleSort(FCharset);
minchar := FCharset[1];
maxChar := FCharset[length(FCharset)];
imaxchar := ord(MaxChar);
charcount := '';
for loop := 1 to FNumChars do
begin
charcount := charcount + #0;
end;
{$IFNDEF OPTIMIZE}
Brutecount := 0;
{$ENDIF}
end;
function TBruteThread.StringBubbleSort(StrIn: string): string;
var
i, j: Integer;
temp: Char;
tmplen: integer;
begin
tmplen := length(StrIn);
for i := 1 to tmplen do
for j := 1 to tmplen do
if strIn[i] < StrIn[j] then
begin
temp := StrIn[i];
StrIn[i] := StrIn[j];
StrIn[j] := temp;
end;
Result := strIn;
end;
end.
2007. január 30., kedd
How to format the cell borders of an Excel spreadsheet
Problem/Question/Abstract:
How to format the cell borders of an Excel spreadsheet
Answer:
Various ways of setting borders on a worksheet (WS):
{ ... }
var
Rng: OleVariant;
LeftEdge: Border;
{ ... }
WS.Range['A5', 'D5'].Borders.Item[xlEdgeTop].Weight := xlThick;
WS.Range['A5', 'D5'].Borders.Item[xlEdgeTop].Color := clYellow;
WS.Range['A5', 'D5'].Borders.Item[xlEdgeBottom].Linestyle := xlDouble;
WS.Range['A5', 'D5'].Borders.Item[xlEdgeBottom].Color := clYellow;
{ ... }
{ ... }
WS.Evaluate('B6, C6, D6, E6, F6').Borders.Item[xlEdgeLeft].Line
style := xlContinuous;
Rng := WS.Range['A1', 'A1'];
Rng.BorderAround(xlContinuous, xlThin, Color := clFuchsia);
LeftEdge := WS.Range['B2', 'B5'].Borders.Item[xlEdgeLeft];
LeftEdge.Linestyle := xlContinuous;
LeftEdge.Weight := 3;
LeftEdge.Color := clLime;
{ ... }
2007. január 29., hétfő
How to convert a TMemoryStream to an OLE variant and vice versa
Problem/Question/Abstract:
How to convert a TMemoryStream to an OLE variant and vice versa
Answer:
function MemoryStreamToOleVariant(Strm: TMemoryStream): OleVariant;
var
Data: PByteArray;
begin
Result := VarArrayCreate([0, Strm.Size - 1], varByte);
Data := VarArrayLock(Result);
try
Strm.Position := 0;
Strm.ReadBuffer(Data^, Strm.Size);
finally
VarArrayUnlock(Result);
end;
end;
function OleVariantToMemoryStream(OV: OleVariant): TMemoryStream;
var
Data: PByteArray;
Size: integer;
begin
Result := TMemoryStream.Create;
try
Size := VarArrayHighBound(OV, 1) - VarArrayLowBound
(OV, 1) + 1;
Data := VarArrayLock(OV);
try
Result.Position := 0;
Result.WriteBuffer(Data^, Size);
finally
VarArrayUnlock(OV);
end;
except
Result.Free;
Result := nil;
end;
end;
2007. január 28., vasárnap
Print an HTML file using TWebBrowser
Problem/Question/Abstract:
How to print an HTML file using TWebBrowser
Answer:
Solve 1:
var
I, O: OleVariant;
begin
I := 0;
WebBrowser1.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, I, O);
end;
Solve 2:
uses
ActiveX
procedure PrintWebBrowser(WB: TWebBrowser);
var
OleCommandTarget: IOleCommandTarget;
Command: TOleCmd;
Success: HResult;
begin
if not Assigned(WB) then
Exit;
if not Assigned(WB.Document) then
Exit;
{Get reference to IOleCommandTarget}
WB.Document.QueryInterface(IOleCommandTarget, OleCommandTarget);
{Check if printing is currently possible}
Command.cmdID := OLECMDID_PRINT;
if OleCommandTarget.QueryStatus(nil, 1, @Command, nil) <> S_OK then
begin
{Something went wrong ...}
Exit;
end;
if (Command.cmdf and OLECMDF_ENABLED) <> 0 then
begin
{Print}
Success := OleCommandTarget.Exec(nil, OLECMDID_PRINT,
OLECMDEXECOPT_DONTPROMPTUSER, EmptyParam, EmptyParam);
case Success of
S_OK: ; {Everything's fine}
OLECMDERR_E_CANCELED: ShowMessage('Aborted by user');
else
ShowMessage('Error');
end;
end
else
begin
{Printing not possible}
end;
end;
Solve 3:
After navigating to a page with TWebBrowser you may want to print it. Well, the Microsoft Internet Explorer control can do that, show a print preview dialog, and even a page setup dialog.
var
vaIn, vaOut: OleVariant; // Needed in all examples
{ ...}
// Printing without the Printer dialog
WebBrowser1.ControlInterface.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER,
vaIn, vaOut);
// Print with the Printer dialog
WebBrowser1.ControlInterface.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER,
vaIn, vaOut);
// Show the Print Preview dialog
WebBrowser1.ControlInterface.ExecWB(OLECMDID_PRINTPREVIEW,
OLECMDEXECOPT_DONTPROMPTUSER, vaIn, vaOut);
// Show the Page Setup dialog
WebBrowser1.ControlInterface.ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_PROMPTUSER,
vaIn, vaOut);
2007. január 27., szombat
How to restrict the number of lines in a TMemo
Problem/Question/Abstract:
How can I get the following to happen with a memo: I would like to make it start purging lines from the top when a line is added at the bottom after I have 1024 lines.
Answer:
You can do the following (I am making the example for TCustomMemo to make this more general).
TLimitedMemo = class(TCustomMemo)
private
fChanging: Boolean;
protected
procedure Change; override;
public
constructor Create(AOwner: TComponent); override;
end;
procedure TLimitedMemo.Change;
var
i: Integer;
begin
if fChanging then
Exit;
inherited;
with Lines do
try
BeginUpdate;
if Count > 5 then
begin
fChanging := True;
for i := 0 to Count - 6 do
Delete(0);
fChanging := False;
end;
finally
EndUpdate;
end;
end;
constructor TLimitedMemo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fChanging := False;
end;
In this case, this memo is allowing 5 lines(you can change it at your will).
2007. január 26., péntek
How to change the position of the dropdown list of a TComboBox
Problem/Question/Abstract:
I have been able to find out how to increase the width of a combo box drop down so that it is wide enough to read the text. However if my combo box is positioned on the right hand side of a form when there is a particularly wide list the scroll bar and the list gets cut off on the edge of the screen. Is there a way to change the position the dropdown list?
Answer:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, stdctrls, Unit2;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
ComboBox1: TComboBox;
procedure Button1Click(Sender: TObject);
procedure ComboBox1DropDown(Sender: TObject);
private
{ Private declarations }
procedure WMUser(var msg: TMessage); message WM_USER;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to 20 do
combobox1.Items.add(StringofChar(Chr(Ord('A') + i), Random(50) + 10));
combobox1.Perform(CB_SETDROPPEDWIDTH, combobox1.Width * 2, 0)
end;
function EnumProc(wnd: HWND; var wndresult: HWND): BOOL; stdcall;
var
classname: array[0..63] of Char;
begin
Result := True;
GetClassname(wnd, classname, sizeof(classname));
if SameText(classname, 'ComboLBox') then
begin
Result := false;
wndresult := wnd;
end;
end;
procedure TForm1.ComboBox1DropDown(Sender: TObject);
var
wnd: HWND;
r: Trect;
w: Integer;
begin
wnd := 0;
EnumThreadWindows(GetCurrentThreadID, @EnumProc, integer(@wnd));
if wnd <> 0 then
begin
PostMessage(handle, WM_USER, wnd, 0);
end
else
memo1.lines.add('Window not found');
end;
procedure TForm1.WMUser(var msg: TMessage);
var
wnd: HWND;
r: Trect;
w: Integer;
begin
wnd := msg.wparam;
GetWindowRect(wnd, r);
if r.Right > Screen.width then
begin
w := r.right - r.Left;
MoveWindow(wnd, Screen.Width - w, r.top, w, r.Bottom - r.Top, true);
end;
memo1.lines.add(format('Wnd: %x, r: (%d,%d,%d,%d)', [wnd, r.left, r.top, r.right, r.bottom]));
end;
initialization
randomize;
end.
2007. január 25., csütörtök
TNCCanvas - write on a form's non-client area
Problem/Question/Abstract:
TNCCanvas - write on a form's non-client area
Answer:
This canvas gives you access to a form's none-client (NC) area and can be used to create a window with a personal frame style:
TNCCanvas = class(TCanvas)
private
FDeviceContext: HDC;
FWindowHandle: HWnd;
function GetWindowRect: TRect;
protected
procedure CreateHandle; override;
procedure FreeHandle;
public
constructor Create(aWindow: hWnd);
destructor Destroy; override;
property WindowRect: TRect read GetWindowRect;
end;
{ TNCCanvas - Object }
constructor TNCCanvas.Create(aWindow: hWnd);
begin
inherited Create;
FWindowHandle := aWindow;
end;
destructor TNCCanvas.Destroy;
begin
FreeHandle;
inherited Destroy;
end;
procedure TNCCanvas.CreateHandle;
begin
if FWindowHandle = 0 then
inherited CreateHandle
else
begin
if FDeviceContext = 0 then
FDeviceContext := GetWindowDC(FWindowHandle);
Handle := FDeviceContext;
end;
end;
procedure TNCCanvas.FreeHandle;
begin
Handle := 0;
if FDeviceContext <> 0 then
begin
ReleaseDC(FWindowHandle, FDeviceContext);
FDeviceContext := 0;
end;
end;
function TNCCanvas.GetWindowRect: TRect;
begin
winProcs.GetWindowRect(FWindowHandle, Result);
with Result do
begin
Right := Pred(Right - Left);
Bottom := Pred(Bottom - Top);
Left := 0;
Top := 0;
end;
end;
2007. január 24., szerda
Extracting both the small and the large icon from a file
Problem/Question/Abstract:
Extracting both the small and the large icon from a file
Answer:
The Windows help files only document ExtractIcon which extracts the large icon from an EXE (DLL, etc.).
There is an undocumented function ExtractIconEx which retrieves both the small and the large icon as shown below.
procedure TForm1.FormPaint(Sender: TObject);
var
LargeIcon: HIcon;
SmallIcon: HIcon;
IconCount: Integer;
i: Integer;
FileName: PChar;
begin
// draw a stripe with all large icons contained in the file
// and below of that a stripe with all small icons.
FileName := 'C:\WinNT\RegEdit.exe';
IconCount := ExtractIconEx(FileName, -1, LargeIcon, SmallIcon, 0);
for i := 0 to Pred(IconCount) do
begin
ExtractIconEx(FileName, i, LargeIcon, SmallIcon, 1);
DrawIcon(Canvas.Handle, 5 + i * 36, 5, LargeIcon);
DrawIconEx(Canvas.Handle, 5 + i * 36, 50, SmallIcon,
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
end;
end;
2007. január 23., kedd
Replacement for the C ternary conditional operator "?"
Problem/Question/Abstract:
Replacement for the C ternary conditional operator "?"
Answer:
Solve 1:
Original functions by Project JEDI Code Library (JCL).
It's better(read faster) than use Variant Type.
Ps.: Portuguese comments.
//*******************************************************
// Declara??o
// Substitui??o do operador "?" em C
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart: Boolean): Boolean;
overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Byte): Byte;
overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Cardinal):
Cardinal; overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Char): Char;
overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Extended):
Extended; overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Integer):
Integer; overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Pointer): Pointer; overload;
function Iff(const Condition: Boolean; const TruePart, FalsePart:
string): string; overload;
{$IFDEF SUPPORTS_INT64}
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Int64): Int64; overload;
{$ENDIF SUPPORTS_INT64}
//*******************************************************
// Fun?�es
// Substitui??o do operador "?" em C
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Boolean): Boolean; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Byte): Byte; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Cardinal): Cardinal; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Char): Char; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Extended): Extended; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Integer): Integer; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Pointer): Pointer; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
function Iff(const Condition: Boolean; const TruePart, FalsePart:
string): string; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
//*******************************************************
{$IFDEF SUPPORTS_INT64}
function Iff(const Condition: Boolean; const TruePart, FalsePart:
Int64): Int64; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
{$ENDIF SUPPORTS_INT64}
Solve 2:
Delphi 6+ has the following functions:
function IfThen(AValue: Boolean; const ATrue: Integer; const AFalse: Integer = 0):
Integer; overload;
function IfThen(AValue: Boolean; const ATrue: Int64; const AFalse: Int64 = 0): Int64;
overload;
function IfThen(AValue: Boolean; const ATrue: Double; const AFalse: Double = 0.0):
Double; overload;
function IfThen(AValue: Boolean; const ATrue: string; const AFalse: string =
''): string; overload;
2007. január 22., hétfő
Free a parent form when its child gets closed or freed
Problem/Question/Abstract:
I am using a TPageControl and show some forms on its pages. So, whenever I want to show a form I create a new page on the TPageControl for that form and then displat the form in that page. Now I want free that page when the user closes the form sitting on it. I tried using the form's OnClose or OnDestroy events to free the parent tabsheet of the form but I get an access violation.
Answer:
Solve 1:
It is difficult enough to destroy a control from an event handler of that control, trying to destroy its parent adds even more problems to that. The best way to handle this is to leave the destruction of the tabsheet to a neutral 3rd party, in this case the form holding the pagecontrol. In the embedded forms OnClose you post (via postmessage) a custom message to the form holding the pagecontrol and then hide the embedded form. The host form then destroys the tabsheet and that also destroys the embedded form. Posting the message delays the action long enough to allow any code in the embedded form to complete safely. Example:
{Unit for the embedded form}
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
const
UM_KILLCONTROL = WM_USER + 666;
type
TUMKillControl = record
msg: Cardinal;
control: TControl;
unused: LPARAM;
result: LRESULT;
end;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
public
end;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
close
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
action := caHide;
PostMessage(GetParentForm(self).Handle, UM_KILLCONTROL, Integer(parent), 0);
end;
end.
{Unit for the host form}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls, unit2;
type
TForm1 = class(TForm)
StatusBar: TStatusBar;
Button1: TButton;
PageControl1: TPageControl;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure UMKillControl(var msg: TUMKillControl); message UM_KILLCONTROL;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
tab: TTabSheet;
begin
tab := TTabSheet.Create(self);
tab.PageControl := pagecontrol1;
with TForm2.create(self) do
begin
borderstyle := bsNone;
parent := tab;
tab.caption := caption;
align := alclient;
show;
end;
end;
procedure TForm1.UMKillControl(var msg: TUMKillControl);
begin
msg.control.Free;
end;
end.
Solve 2:
As long as the child form is not "owned" by the tabsheet on which it is parented (being owned by the form which owns the PageControl is OK), you can do this:
{ ... }
type
TfNastyChild = class(TForm)
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
fKillParent: TWinControl;
public
{ Public declarations }
destructor Destroy; override;
end;
implementation
{$R *.dfm}
procedure TfNastyChild.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if (Parent is TTabsheet) and (Owner <> Parent) then
begin
Hide;
fKillParent := Parent;
Parent := nil;
end;
action := caFree;
end;
destructor TfNastyChild.Destroy;
begin
if assigned(fKillParent) and not (csDestroying in fKillParent.ComponentState) then
fKillParent.Free;
inherited;
end;
2007. január 21., vasárnap
Freeing a TList
Problem/Question/Abstract:
Freeing a TList
Answer:
If there is any possibility of the freeing code being called re-entrantly, make sure to store a nil in the list before freeing the object; your code should look like this:
// original from Ray Lischner
for i := 0 to List.Count - 1 do
begin
TempNode := List[i];
List[i] := nil;
TempNode.Free;
end;
List.Free;
2007. január 20., szombat
How to find out program path
Problem/Question/Abstract:
How to find out program path
Answer:
function Path_App: string;
var
x: string;
y: string;
begin
x := ParamStr(0);
y := ExtractFileName(ParamStr(0));
Result := copy(x, 0, length(x) - length(y));
end;
2007. január 19., péntek
How to detect a color within a range of pixels around the mouse cursor
Problem/Question/Abstract:
I need to search for a pixel of a given TColor around the mouse cursor position. My main problem is that I have to search in a range of pixels around the center of the mouse's actual position starting with one pixel.
Answer:
function FindPixelOnCanvas(
canvas: TCanvas; {canvas to find pixel on}
const startAt: TPoint; {start position}
tolerance: Integer; {pixel range to check}
color: TColor; {color to look for}
const rect: TRect; {dimension of canvas}
var foundPos: TPoint {returns last position tested}
): Boolean; {returns true if color found, false if not}
var
i, k, n: Integer;
begin
Result := False;
for n := 0 to tolerance do
for i := -n to +n do
for k := -n to +n do
if (Abs(i) = n) or ((Abs(k) = n) then
begin
foundPos := Point(startAt.X + i, startAt.Y + k);
if PtInRect(foundPos, rect) and (Canvas.Pixels[foundPos.X, foundPos.Y] = color) then
begin
Result := True;
Exit;
end;
end;
end;
2007. január 18., csütörtök
How to do a locate on a non-indexed field
Problem/Question/Abstract:
How to do a locate on a non-indexed field
Answer:
The following function can be added to your to your unit and called as follows:
Locate(Table1, Table1LName, 'Beman');
Table1 is your table component, Table1LName is TField you've add with the fields editor (double click on the table component) and 'Beman' is the name you want to find.
{Locate will find sValue in a non-indexed table}
function Locate(const oTable: TTable; const oField: TField; const sValue: string): Boolean;
var
bmPos: TBookMark;
bFound: Boolean;
begin
Locate := False;
bFound := False;
if not oTable.Active then
Exit;
if oTable.FieldDefs.IndexOf(oField.FieldName) < 0 then
Exit;
bmPos := oTable.GetBookMark;
with oTable do
begin
DisableControls;
First;
while not EOF do
if oField.AsString = sValue then
begin
Locate := True;
bFound := True;
Break;
end
else
Next;
end;
if (not bFound) then
oTable.GotoBookMark(bmPos);
oTable.FreeBookMark(bmPos);
oTable.EnableControls;
end;
2007. január 17., szerda
Add a bitmap to a menu item
Problem/Question/Abstract:
Add a bitmap to a menu item
Answer:
Just follow these steps:
Create a Picture and store it as a file 'img.bmp'.
Use the SetMenuItemBitmaps API call to connect the Picture to the Menu with these parameters:
MenuItemFile is the name given to the horizontal Menuitem, e.g. "File", "Edit", "Help"
0,1 ... is the position of the item on which you want to place the bitmap. (start counting with 0)
The first of the two bitmap handles is the one for the bitmap displayed for the unchecked menuitem
The second bitmap handle is the one for the checked menuitem. They may be the same.
All this can by coded in the .Create of a form.
Try to make the picture not to large, or it will not be displayed completely. Only the right-top of the bitmap will be displayed.
Finally, here's the code:
var
Bmp1: TPicture;
...
Bmp1 := TPicture.Create;
Bmp1.LoadFromFile('.\img.bmp');
SetMenuItemBitmaps(MenuItemTest.Handle, 0, MF_BYPOSITION,
Bmp1.Bitmap.Handle, Bmp1.Bitmap.Handle);
...
Don't forget to free the bitmap Bmp1 e.g. when you destroy your form.
2007. január 16., kedd
How to force a TListBox to set a horizontol scrollbar if an entry is being cropped
Problem/Question/Abstract:
I have a listbox on a form which contains a list of stuff of varying widths. In some situations, they all fit in the box, in some cases the entries are too long to fit and their right end gets cropped. Is there some way to force the listbox to set a horizontol scrollbar if and only if an entry is being cropped? I can brute force set the ScrollWidth property to 1000, but this puts a scrollbar in place all the time. I only want a scrollbar if it's necessary.
Answer:
{ ... }
listbox.Scrollwidth := CalcMaxWidthOfStrings(listbox.Items, listbox.font);
{ ... }
function CalcMaxWidthOfStrings(aList: TStrings; aFont: TFont): Integer;
var
max, n, i: Integer;
canvas: TCanvas;
begin
Assert(Assigned(aList));
Assert(Assigned(aFont));
canvas := TCanvas.Create;
try
canvas.Handle := CreateDC('DISPLAY', nil, nil, nil);
try
Canvas.Font := aFont;
max := 0;
for i := 0 to aList.Count - 1 do
begin
n := Canvas.TextWidth(aList[i]);
if n > max then
max := n;
end;
Result := max;
finally
DeleteDC(canvas.Handle);
canvas.Handle := 0;
end;
finally
canvas.free;
end;
end;
2007. január 15., hétfő
Various color conversion routines
Problem/Question/Abstract:
Is there a routine that can take a hex color value and convert it to a Delphi formatted value like: "Cornsilk1 Cornsilk1 255 248 220 #FFF8DC" into $00DCF8FF ?
Answer:
Solve 1:
function Swap32(aLong: Longint): Longint; assembler;
asm
BSWAP eax
end;
function HexColorToColor(HexColor: string): TColor;
{input: '#FFF8DC' -> output $DCF8FF as TColor, use IntTohex to convert output to string again if needed}
begin
Assert(Length(hexcolor)) > 1;
Assert(hexcolor[1] = '#');
hexcolor[1] := '$';
Result := Swap32(StrToInt(hexcolor));
end;
Solve 2:
The safest way to convert this is to use the following:
{ ... }
var
r, g, b: string;
begin
r := Copy(HexValue, 2, 2);
g := {... same, but for the GG part}
b := {...same, but for the BB part}
{Finally}
DelphiColor := RGB(STrToInt('$' + R), STrToInt('$' + G), STrToInt('$' + B));
end;
Note: You should never shift the RGB data manually as the bit order is different in various screen modes. The first two bytes you refered to as $00 is the alpha channel, or color intensity. Packages like Graphics32 that support this feature normally allows you to manipulate this directly, where $FF is normal color, while $00 is completely invisible. But all of this can be avoided by using the example above. Try it out, you will see what i mean.
Solve 3:
A HTML color string has the format #RRGGBB, the color values are coded as two digit hexadecimal numbers. Delphi's TColor is an Integer value. If the bits 24..31 = 0 then the value describes a RGB color.
RGB color:
Bit
0.. 7: red
8..15: green
16..23: blue
24..31: 0
Source code:
{exchange red and blue color values}
function ByteSwapColor(Color: TColor): TColor; assembler;
asm
BSWAP EAX
SHR EAX, 8
end;
resourcestring
SIsNotAHTMLColorValue = '%s is not a HTML color value';
procedure ConvertHTMLtoRGBColor(HTMLColor: string; var Color: TColor): Boolean;
begin
Result := False;
if Length(HTMLColor) <> 7 then
Exit;
if HTMLColor[1] <> '#' then
Exit;
HTMLColor[1] := '$';
Color := StrToIntDef(HTMLColor, -1);
Result := (0 <= Color) and (Color <= $FFFFFF);
if Result then
Color := ByteSwapColor(Color);
end;
function HTMLtoRGBColor(const HTMLColor: string): TColor;
begin
if not ConvertHTMLtoRGBColor(HTMLColor, Result) then
raise EConvertError.CreateFmt(SIsNotAHTMLColorValue, [HTMLColor]);
end;
function RGBtoHTMLColor(Color: TColor): string;
begin
Color := RGBColor(Color);
Color := ByteSwapColor(Color);
Result := Format('#%.6x', [Color]);
end;
Solve 4:
function HTMLToDelphiColor(S: string): TColor;
var
Red, Green, Blue: LongInt;
begin
Red := StrToInt('$' + Copy(S, 1, 2));
Green := StrToInt('$' + Copy(S, 3, 2));
Blue := StrToInt('$' + Copy(S, 5, 2));
Result := (Blue shl 16) + (Green shl 8) + Red;
end;
function ColorToHTMLHex(Color: TColor): string;
begin
Result := IntToHex(ColorToRGB(Color), 6);
Result := '#' + Copy(Result, 5, 2) + Copy(Result, 3, 2) + Copy(Result, 1, 2);
end;
2007. január 14., vasárnap
How to really do before and after processing on web requests using borlands web module
Problem/Question/Abstract:
Do you not know where to put CoInitialize or CoUnitialize?
The problem with Borlands web module architech is that there is no true after dispatch method. Yes, there is one but it only gets called only if your response was handled by an action item and will not get called if your response was sent by an action item. I have a complete and simple solution for this problem that will allow you to execute code before an after handling a response.. always.
Answer:
First a code listing, then an explanation.
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
PrevBeforeDispatchMethod: THTTPMethodEvent;
begin
Handled := True;
{
******* Do initialization code here such as CoInitialize
}
try
PrevBeforeDispatchMethod := BeforeDispatch;
try
BeforeDispatch := nil;
{
****** Process anything before calling Action Items here
}
DispatchAction(Request, Response);
finally
BeforeDispatch := PrevBeforeDispatchMethod;
end;
finally
{
******* Do cleanup here such as CoUnInitialize
}
end;
end;
Now, to explain.
You need to create an OnBeforeDispatch event, then paste the above code into it. This bypasses Borlands behavior in a way.
This is what goes on behind the scenes....
Borlands web application calls your web modules DispatchAction method, the dispatch action event then calls your BeforeDispatch event if you have assigned one... now for the trick, if your BeforeDispatch event handles the request then no action item will be called. I take advantage of that behavior. The Dispatach event will be called 2 times, once by the web application and a second time by the above code... to prevent recursion I set the BeforeDispatch event to nil. Because I set handled to true, the first call made by the web application will not call any action items... remember if the BeforeDispatch event Sets handled to true then the DispatchAction event will not call action items. Therefore my BeforeDispatch event calls the action items through a second call to DispatchAction... DispatchAction will not call my BeforeDispatch event because I set it to nil before calling DispatchAction.
There is one problem that can occur, AfterDispatch will be called 2 times. If you are not using the AfterDispatch event then you have no problems.
I suggest you not use the AfterDispatch event handler or write some code to prevent your code executing 2 times.
2007. január 13., szombat
Delete our own application
Problem/Question/Abstract:
How to delete our own application
Answer:
Solve 1:
This solution comes from my idea of doing a installer program. InstallShield(C) and others just uses that solution.
Do not use a Batch file, instead use a small (about 25K) console application that just takes one parameter (the file
that must be deleted) and waits until your application unloads and/or EXE file is unlocked. Just run it from your application before exiting.
program DelFile;
{$APPTYPE CONSOLE}
uses SysUtils, Windows;
begin
if (ParamCount = 0) then
Exit;
repeat
Sleep(10);
until (DeleteFile(PChar(ParamStr(1))));
end.
To do a better work, the DelFile.Exe file should be put during the installation of the program into the Windows\Temp folder and runned from there, specifing the complete path of the file that must be deleted.
PRO:
The batch file can be modified and can be done to not delete your file.
VS:
You must hide a program in the Windows\Temp folder of a user, that's not good.
Solve 2:
You can use the registry key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Runonce
to add a command that will be runned only once, and your application will be deleted next type your Windows will be restarted.
Key name: "DeleteMyOwnApplicationFile"
Key value: "c:\windows\temp\delfile.exe c:\myapp\myapp.exe"
PRO:
The next time Windows starts, you application will be surelly not
loaded, also if it's in the Statup folder.
VS:
You must hide a program in the Windows\Temp folder of a
user, that's not good.
The system must be rebooted.
Solve 3:
Always using, the key registry, don't use your delfile.exe, but:
Key name: "DeleteMyOwnApplicationFile"
Key value: "del c:\myapp\myapp.exe"
...using the DOS "Del" command.
PRO:
You don't need to hide programs.
VS:
The system must be restarted.
2007. január 12., péntek
Create a Treeview with Keys from the Registry
Problem/Question/Abstract:
Anyone have any sample code on how to load a TreeView with registry keys, i want to load the
KEY_CURRENT_USER\\Software key, and i want all the subkeys to load in the treeview too.
Answer:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, Registry;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
procedure FillRegBranch(rootkey: hkey; parentkey: string; ParentNode: TTreeNode);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
Node: TTreeNode;
begin
TreeView1.Items.Clear;
TreeView1.Items.BeginUpdate;
Node := TreeView1.Items.AddChild(nil, 'Borland');
FillRegBranch(HKEY_Local_Machine, 'Software\Borland', Node);
TreeView1.Items.EndUpdate;
end;
procedure tForm1.FillRegBranch(rootkey: hkey; parentkey: string; ParentNode:
TTreeNode);
var
Cnt: Integer;
StList: TStrings;
Node: tTreeNode;
Registry: TRegistry;
begin
Registry := TRegistry.Create;
try
Registry.RootKey := rootkey;
if Registry.OpenKey(parentkey, false) then
begin
StList := tStringlist.Create;
try
Registry.GetKeyNames(StList);
for Cnt := 0 to StList.count - 1 do
begin
Node := TreeView1.Items.addChild(ParentNode, StList.Strings[cnt]);
if Registry.HasSubKeys then
FillRegBranch(rootkey, parentkey + '\' + StList.Strings[cnt], node);
end;
finally
StList.Free;
end;
end;
finally
Registry.Free;
end;
end;
end.
2007. január 11., csütörtök
An Edit Control with AutoComplete Capabilities
Problem/Question/Abstract:
Microsoft�s AutoComplete can be used in a Delphi Application in a Friendly way with the following component
Answer:
// Implements a TCustomEdit with AutoComplete Capabilities
// Author: Jorge Abel Ayala Marentes
// Created: 15/Oct/2000
// Last Modification: 21/Nov/2000
unit U_AutoCompleteEdit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, StrTools, ShlIntf, ActiveX, ComObj;
type
TSearchListChangeEvent = procedure of object;
TAutoCompleteEdit = class(TCustomEdit)
private
FSearchListChange: TSearchListChangeEvent;
FAutoComplete: IAutoComplete2;
FStrings: IUnknown;
FStringList: TStrings;
procedure SetFStringList(const Value: TStrings);
protected
procedure SearchListChange;
public
constructor Create(AOwner: TComponent); override;
//Needed to init AutoComplete when the component is first Loaded
procedure Loaded; override;
destructor Destroy; override;
procedure SetAutoComplete;
published
property AutoSelect;
property AutoSize;
property BorderStyle;
property CharCase;
property HideSelection;
property MaxLength;
property ParentColor;
property Text;
property OnChange;
property SearchList: TStrings read FStringList write SetFStringList;
property OnSearchListChange: TSearchListChangeEvent read FSearchListChange
write FSearchListChange;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Sitio Web', [TAutoCompleteEdit]);
end; //end of Register
{ TAutoCompleteEdit }
constructor TAutoCompleteEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Parent := TWinControl(AOwner);
FStringList := TStringList.Create;
SearchListChange;
end; //end of TAutoCompleteEdit.Create
destructor TAutoCompleteEdit.Destroy;
begin
FStringList.Free;
inherited;
end; //end of TAutoCompleteEdit.Destroy
//Updated: Last version didt�nt work because the searchlist wasn�t
//initializaed when the component was loaded :)
procedure TAutoCompleteEdit.Loaded;
begin
inherited;
if FStringList.Count > 0 then
SetAutoComplete;
end; //end of TAutoCompleteEdit.Loaded
procedure TAutoCompleteEdit.SearchListChange;
begin
if Assigned(FSearchListChange) then
FSearchListChange;
end; //end of TAutoCompleteEdit.SearchListChange
procedure TAutoCompleteEdit.SetAutoComplete;
begin
FAutoComplete := CreateComObject(CLSID_AutoComplete) as IAutoComplete2;
FStrings := TEnumString.Create(FStringList) as IUnknown;
OleCheck(FAutoComplete.SetOptions(ACO_AUTOSUGGEST
or ACO_AUTOAPPEND or ACO_UPDOWNKEYDROPSLIST or ACO_USETAB));
OleCheck(FAutoComplete.Init(Self.Handle, FStrings, nil, nil));
end; //end of TAutoCompleteEdit.SetAutoComplete
procedure TAutoCompleteEdit.SetFStringList(const Value: TStrings);
begin
SearchList.Assign(Value);
SetAutoComplete;
SearchListChange;
end; //end of TAutoCompleteEdit.SetFStringList
end.
You can download the complete component.
Please note that AutoComplete can only be used if you have Sell32.dll verson 4.7 or above, I think that if you install IE 5.0 or above you won�t have any troble, there are still some improvements I can think of, but please any feedback will be apreciated, let me kwnow your ideas.
Component Download: AutoCompleteEdit.zip
2007. január 10., szerda
Giving a MDI window a background image/tile
Problem/Question/Abstract:
How do I give my MDI window a background image or tile?
Answer:
This is a handy trick I found somewhere:
Put an image called Image1 on your main form.
Add the following routine to your main form:
Make sure you have the following variables in your main form object:
FClientInstance: TFarProc;
FPrevClientProc: TFarProc;
{ MDI Background code }
procedure TMainForm.ClientWndProc(var Message: TMessage);
var
Dc: hDC;
Row: Integer;
Col: Integer;
begin
with Message do
case Msg of
WM_ERASEBKGND:
begin
Dc := TWMEraseBkGnd(Message).Dc;
// Tile Image on DC
for Row := 0 to ClientHeight div Image1.Picture.Height do
for Col := 0 to ClientWidth div Image1.Picture.Width do
BitBlt(Dc,
Col * Image1.Picture.Width,
Row * Image1.Picture.Height,
Image1.Picture.Width,
Image1.Picture.Height,
Image1.Picture.Bitmap.Canvas.Handle,
0,
0,
SRCCOPY);
Result := 1;
end;
else // Pass on other msg's
Result := CallWindowProc(FPrevClientProc,
ClientHandle,
Msg,
wParam,
lParam);
end;
end;
And put this in your mainform OnShow event:
// MDI background tiles stuff, chain in de WndProc.
FClientInstance := MakeObjectInstance(ClientWndProc);
FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FClientInstance));
You now have a background!
2007. január 9., kedd
Connect/Disconnect network drives
Problem/Question/Abstract:
How do I map a drive letter to a network resource?
Answer:
//we could use the standard dialog to have the user do it
procedure TForm1.Button1Click(Sender: TObject);
begin
WNetConnectionDialog(Handle, RESOURCETYPE_DISK)
end;
//we can use the same to connect a printer
procedure TForm1.Button1Click(Sender: TObject);
begin
WNetConnectionDialog(Handle, RESOURCETYPE_PRINT)
end;
//or we can do this by code
procedure TForm1.Button2Click(Sender: TObject);
var
NetResource: TNetResource;
begin
{ fill out TNetResource record structure }
NetResource.dwType := RESOURCETYPE_DISK;
NetResource.lpLocalName := 'S:';
NetResource.lpRemoteName := '\\myserver\public';
NetResource.lpProvider := '';
{ map our network drive using our TNetResource record structure }
if (WNetAddConnection2(NetResource,
'', {Password (if needed) or empty}
'', {User name (if needed) or empty}
CONNECT_UPDATE_PROFILE) <> NO_ERROR) then
raise Excepcion.Create('unable to map drive')
//there are other constants to check the error
//ERROR_ACCESS_DENIED, ERROR_ALREADY_ASSIGNED, etc
end;
//to disconnect it simply...
procedure TForm1.Button2Click(Sender: TObject);
begin
if WNetCancelConnection2('S:', 0, TRUE) <> NO_ERROR then
raise Exception.create('Error disconnecting map drive');
//of course there are also some other constants to check why the error
//occurred: ERROR_DEVICE_IN_USE, ERROR_NOT_CONNECTED, etc
end;
2007. január 8., hétfő
OpenGL I: Hello World
Problem/Question/Abstract:
Most people know Delphi as a RAD tool to create database applications, Delphi programmers know that with Delphi you can do EVERYTHING
Answer:
There's quite some people out there doing great efforts to promote OpenGL and DirectX technologies with Delphi;
I will mention this, as I got the base code (and fixed it a little bit) from here:
http://nehe.gamedev.net/
In this article I will show you the base code to create fast and small Delphi-OpenGL applications
I would like to comment that graphics programming is not easy, you will need some knowledge about math and a lot of reading, is like learning a new languaje (a hard one)
First, we will be using no forms (to make application small) and therefore obviously no components (we're going to do this as real programmers =o) )
Our application will consist of the "project source" and one unit In our unit we are just going to create a record to hold some of the application values and some simple constants that are explained in detail here's the "header" of our unit (sorry, no classes or objects):
type
TGLWindow = record
Active: Boolean;
//Window Active Flag (False is minimized, so we don't draw stuff when minimized)
ExitGame: Boolean; //The main loop is based on this variable
Keys: array[0..255] of Bool; //Array Used For The Keyboard Routine
Fullscreen: Boolean; //Fullscreen Flag
MouseLButton: Integer;
MouseRButton: Integer; //Left or right buttons pressed? (0 or 1)
MouseX: Integer;
MouseY: Integer;
MouseZ: Integer;
//Used when right button is pressed (up and down move in and out in the Z axis)
end;
{ All User Variables Here }
var
GS: TGLWindow;
const
POS_X = 100; //Position of window (only when NOT in fullscren mode)
POS_Y = 100;
RES_X = 640; //Resolution
RES_Y = 480;
RES_BITS = 16; //16 bits resolution
WIN_TITLE = 'My Game'; //Title for our window
Then from our unit we need to export this function:
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nCmdShow:
Integer): integer; stdcall;
and we will also need these variables (private to our unit, so after implementation):
var
h_RC: HGLRC; //Permanent Rendering Context
h_DC: HDC; //Private GDI Device Context
h_Wnd: HWND; //Holds Our Window Handle
This function basically does everything, initializes the window, draws our stuff, process messages, and when you're done destroys the window. Ok, now we need all the procedures to initialize, process messages, etc...
Here are the functions and some explanations (I'll just list the functions and later will put the actual implementation of each one)
function DrawGLScene(): Bool; { All Rendering Done Here }
procedure ReSizeGLScene(const Width: GLsizei; Height: GLsizei);
{ Resize and Initialize The GL Window }
function InitGL(const Width, Height: Glsizei): Bool;
{ All Setup For OpenGL Goes Here }
//WndProc handles all the messages coming to our window
function WndProc(hWnd: HWND; //Handle For The Window
message: UINT; //Message For This Window
wParam: WPARAM; //Additional Message Information
lParam: LPARAM): //Additional Message Information
LRESULT; stdcall;
{in the CreateWindow we do:
- Register the class window
- Create the window
- Get a Device Context (DC)
- Create a Rendering Context (RC) }
function CreateGLWindow(Title: PChar; PosX, PosY: Integer; const Width,
Height, Bits: Integer; const FullScreenFlag: Bool): Bool; stdcall;
{In the KillWindow we do (obviously the opposite of the CreateWindow and in reverse order):
- Restore the display settings (we need to do this even if something else fails)
- Delete the Rendering Context (RC)
- Release the Device Context (DC)
- Destroy the Window
- Unregister the class window }
procedure KillGLWindow; { Properly Kill the Window }
//WinMain is the actual Main Program (gets called from the actual Main.dpr)
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar;
nCmdShow: Integer): integer; stdcall;
That's all you will need to get started, here's the implementation of these procedures/functions, take a good look at WinMain and WndProc, they show some good stuff even for not graphic applications, you could use them to create small programs that do not require windows...
oh... and one last thing... the "hello world" program in OpenGL won't even show the words "Hello World" (WHAAAT??)
the thing is that outputing text to the screen in OpenGL is a little advanced and I will show it to you in later articles (if you are interested that is). The purpose of this article is to show you the base code and hopefully you will get to understand it and then from there we can concentrate in the OpenGL stuff, so... I will just show you how to create a simple rectangle =o (,but don't be dissapointed, as I say OpenGL is not easy and you won't be creating the next QUAKE in the next 3 months, first you need to create a rectangle, a triangle... a circle ...and theeeen... eventually you will get there (if you really persist)
with no more preambule, here's the code:
//---------------------------------------------------------//
// //
// Original Copyrights: Daniel Vivas //
// daniel@vivas.com.br //
// //
// Main Game Unit //
// Ported To Delhi By: Bryce TeBeest //
// Assistance Provided By: JP Krauss //
// //
// Taken from Jeff Molofi (NEHE) WebSite //
// http://nehe.gamedev.net //
// //
// Some fixes and comments by: EberSys //
//---------------------------------------------------------//
unit oglMain;
interface
uses
Classes,
Messages,
Windows,
OpenGL;
type
TGLWindow = record
Active: Boolean;
//Window Active Flag (False is minimized, so we don't draw stuff when minimized)
ExitGame: Boolean; //The main loop is based on this variable
Keys: array[0..255] of Bool; //Array Used For The Keyboard Routine
Fullscreen: Boolean; //Fullscreen Flag
MouseLButton: Integer;
MouseRButton: Integer; //Left or right buttons pressed? (0 or 1)
MouseX: Integer;
MouseY: Integer;
MouseZ: Integer;
//Used when right button is pressed (up and down move in and out in the Z axis)
end;
{ All User Variables Here }
var
GS: TGLWindow;
const
POS_X = 100; //Position of window (only when NOT in fullscren mode)
POS_Y = 100;
RES_X = 640; //Resolution
RES_Y = 480;
RES_BITS = 16; //16 bits resolution
WIN_TITLE = 'My Game'; //Title for our window
{-----------------------------------------------------------}
{ Public Procedures: }
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nCmdShow:
Integer): integer; stdcall;
{-----------------------------------------------------------}
implementation
var
h_RC: HGLRC; //Permanent Rendering Context
h_DC: HDC; //Private GDI Device Context
h_Wnd: HWND; //Holds Our Window Handle
{-----------------------------------------------------------}
function DrawGLScene(): Bool; { All Rendering Done Here }
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); //Clear Screen and Depth Buffer
glLoadIdentity(); //Reset The View (move to 0, 0, 0)
glTranslatef(0.0, 0.0, -6.0); // Move Right 1.5 Units And Into The Screen 6.0
glColor3f(0.0, 0.5, 0.5);
glBegin(GL_QUADS); // Draw A Quad
glVertex3f(-0.5, 0.5, 0.0); // Top Left
glVertex3f(0.5, 0.5, 0.0); // Top Right
glVertex3f(0.5, -0.5, 0.0); // Bottom Right
glVertex3f(-0.5, -0.5, 0.0); // Bottom Left
glEnd();
DrawGLScene := True
end;
procedure ReSizeGLScene(const Width: GLsizei; Height: GLsizei);
{ Resize and Initialize The GL Window }
var
fWidth, fHeight: GLFloat;
begin
if (Height = 0) then //Prevent Divide by Zero
Height := 1; //Be Setting Height at One
glViewport(0, 0, Width, Height);
//Reset The Current Viewport and Perspective Transformation
glMatrixMode(GL_PROJECTION); //Select The Projection Matrix
glLoadIdentity(); //Reset The Project Matrix
fWidth := Width;
fHeight := Height;
gluPerspective(45.0, fWidth / fHeight, 0.1, 100);
//Calculate the Aspect Ratio of the Window
glMatrixMode(GL_MODELVIEW); //Select The ModelView Matrix
end;
{ All Setup For OpenGL Goes Here }
function InitGL(const Width, Height: Glsizei): Bool;
var
fWidth, fHeight: GLfloat;
begin
glClearColor(0.0, 0.0, 0.0, 0.0); //Black Background
glClearDepth(1.0); //Depth Buffer Setup
glDepthFunc(GL_LESS); //Text
glEnable(GL_DEPTH_TEST); //Enables Depth Testing
glShadeModel(GL_SMOOTH); //Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //reset the View (move to 0, 0, 0)
fWidth := Width;
fHeight := Height;
gluPerspective(45.0, fWidth / fHeight, 0.1, 100);
//Calculate Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW)
end;
//WndProc handles all the messages coming to our window
function WndProc(hWnd: HWND; //Handle For The Window
message: UINT; //Message For This Window
wParam: WPARAM; //Additional Message Information
lParam: LPARAM): //Additional Message Information
LRESULT; stdcall;
begin
if message = WM_SYSCOMMAND then
case wParam of //Check System Calls
SC_SCREENSAVE, SC_MONITORPOWER:
//Screensaver Trying To Start, Monitor Trying To Enter Powersave
begin
Result := 0;
exit
end
end;
case message of //Tells Windows We Want To Check Message
WM_ACTIVATE:
begin
if (Hiword(wParam) = 0) then //Check Minimization State
GS.Active := True
else
GS.Active := False; //when Active is False we don't draw anything
Result := 0;
end;
WM_CLOSE: //Did we get a close message?
begin
PostQuitMessage(0); //Send A Quit Message
Result := 0; //Return To The Message Loop
end;
WM_KEYDOWN: //Is A Key Being Held Down?
begin
GS.Keys[wParam] := True;
Result := 0; //Return To The Message Loop
end;
WM_KEYUP: //Is A Key Being Released?
begin
GS.Keys[wParam] := False;
Result := 0;
end;
WM_SIZE: //Resize scene
begin
ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); //LoWord=Width, HighWord=Height
Result := 0;
end;
WM_LBUTTONDOWN: //(mouse) Left button pressed
begin
ReleaseCapture(); //Need Them Here, Because If Mouse Moves Off
SetCapture(h_Wnd); //Window and Returns, It Needs To Reset Status
GS.MouseLButton := 1;
GS.MouseX := LOWORD(lParam);
GS.MouseY := HIWORD(lParam);
end;
WM_LBUTTONUP: //(mouse) Left button released
begin
ReleaseCapture();
GS.MouseLButton := 0;
GS.MouseX := 0;
GS.MouseY := 0;
Result := 0;
end;
WM_RBUTTONDOWN: //(mouse) Right button pressed
begin
ReleaseCapture();
SetCapture(h_Wnd);
GS.MouseRButton := 1;
GS.MouseZ := HIWORD(lParam);
Result := 0;
end;
WM_RBUTTONUP: //(mouse) Right button released
begin
ReleaseCapture();
GS.MouseRButton := 0;
Result := 0
end
else
{ Pass All Unhandled Messages TO DefWinProc }
Result := DefWindowProc(hWnd, message, wParam, lParam)
end //case message of
end;
{In the KillWindow we do (obviously the opposite of the CreateWindow and in reverse order):
- Restore the display settings (we need to do this even if something else fails)
- Delete the Rendering Context (RC)
- Release the Device Context (DC)
- Destroy the Window
- Unregister the class window }
procedure KillGLWindow; { Properly Kill the Window }
begin
if (GS.FullScreen) then
begin //Are We In FullScreen Mode?
ChangeDisplaySettings(devmode(nil^), 0); //Switch Back To The Desktop
ShowCursor(True); //Show The Mouse Pointer
end;
if (h_RC <> 0) and not (wglDeleteContext(h_RC)) then //Are We Able To Delete The Rc?
begin
MessageBox(0, 'Release of Rendering Context failed.', ' Shutdown Error', MB_OK or
MB_ICONERROR);
h_RC := 0 //Set Rendering Context To Null
end;
if (h_DC <> 0) and (releaseDC(h_Wnd, h_DC) = 0) then
//Are We Able To Release The Device Context?
begin
MessageBox(0, 'Release of Device Context failed.', ' Shutdown Error', MB_OK or
MB_ICONERROR);
h_Dc := 0; //Set Dc To Null
end;
if (h_Wnd <> 0) and not (destroywindow(h_Wnd)) then
//Are We Able To Destroy The Window?
begin
MessageBox(0, 'Could not release hWnd.', ' Shutdown Error', MB_OK or
MB_ICONERROR);
h_Wnd := 0; //Set hWnd To Null
end;
UnregisterClass('OpenGL', hInstance)
end;
{in the CreateWindow we do:
- Register the class window
- Create the window
- Get a Device Context (DC)
- Create a Rendering Context (RC) }
function CreateGLWindow(Title: PChar; PosX, PosY: Integer; const Width, Height, Bits:
Integer; const FullScreenFlag: Bool): Bool; stdcall;
var
PixelFormat: GLUint; //Holds The Result After Searching For A Match
WC: TWndClass; //Windows Class Structure
dwExStyle: DWord; //Extended Windows Style
dwStyle: DWord; //Window Style
PFD: PixelFormatDescriptor; //Tells Windows How We Want Things To Be
dmScreenSettings: DevMode; //Device Mode
h_Instance: hInst; //Holds The Instance Of The Application
begin
h_Instance := GetModuleHandle(nil); //Grab An Instance For Our Window
GS.Fullscreen := FullScreenFlag; //Set The Global FullScreen Flag
with WC do //can't use parentesis on "with" when using packed records
begin
Style := CS_HREDRAW or CS_VREDRAW or CS_OWNDC;
//ReDraw On Size -- Own DC For Window
lpfnWndProc := @WndProc; //WndProc Handles The Messages
cbClsExtra := 0; //No Extra Window Data
cbWndExtra := 0; //No Extra Window Data
hInstance := h_Instance; //Set The Instance
hIcon := LoadIcon(0, IDI_WINLOGO); //Load The Default Icon
hCursor := LoadCursor(0, IDC_ARROW); //Load The Arrow Pointer
hbrBackground := 0; //No BackGround Required For OpenGL
lpszMenuName := nil; //We Don't Want A Menu
lpszClassname := 'OpenGL'; //Set The Class Name
end;
if (RegisterClass(WC) = 0) then //Attempt To Register The Class Window
begin
MessageBox(0, 'Failed To Register The Window Class.', 'Error', MB_OK or
MB_ICONERROR);
CreateGLWindow := False;
exit
end;
if (GS.FullScreen) then
begin
ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
//Make Sure Memory's Availiable
with dmScreenSettings do //don't use parentesis on "with" when using packed records
begin
dmSize := SizeOf(dmScreenSettings); //Size Of The DevMode Structure
dmPelsWidth := Width; //Selected Screen Width
dmPelsHeight := Height; //Selected Screen Height
dmBitsPerPel := Bits; //Selected Bits Per Pixel
dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
//Try to Set Selected Mode
end;
if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) <>
DISP_CHANGE_SUCCESSFUL) then
if (MessageBox(0,
'This Fullscreen Mode Is Not Supported. Use Windowed Mode Instead?',
WIN_TITLE,
MB_YESNO or MB_ICONEXCLAMATION) = IDYES) then
GS.Fullscreen := False //Select Windowed Mode
else
begin
{ Show Message Box To Let User Know Program Is Ending }
MessageBox(0, 'Program Will Now Close.', 'Error', MB_OK or MB_ICONERROR);
CreateGLWindow := False; //Return False
Exit
end
end;
if (GS.Fullscreen) then //If Still In FullScreen Mode
begin
dwExStyle := WS_EX_APPWINDOW; //Entended Window Style
dwStyle := WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; //Window Style
ShowCursor(False);
PosX := 0; //reset these to zero
PosY := 0
end
else
begin
dwExStyle := WS_EX_APPWINDOW or WS_EX_WINDOWEDGE; //Extended Window Style
dwStyle := WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
//Windows Style
end;
h_Wnd := CreateWindowEx(dwExStyle, //Extends Style For The Window
'OpenGL', //Class Name
Title, //Window Title
dwStyle, //Window Style
PosX, PosY, //Window Position
Width, Height, //Selected Width and Height
0, //No Parent Window
0, //No Menu
hInstance, //Instance
nil); //Don't Pass Anything To WM_CREATE
if (h_Wnd = 0) then
begin //If The Window Creation Failed
KillGLWindow(); //Reset The Display
MessageBox(0, 'Window Creation Error.', 'Error', MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow := False;
exit;
end;
with PFD do //don't use parentesis on "with" when using packed records
begin //Tells Window How We Want Things To Be
nSize := SizeOf(PIXELFORMATDESCRIPTOR); //Size Of This Pixel Format Descriptor
nVersion := 1; //Version Number (?)
dwFlags := PFD_DRAW_TO_WINDOW //Format Must Support Window
or PFD_SUPPORT_OPENGL //Format Must Support OpenGL
or PFD_DOUBLEBUFFER; //Must Support Double Buffering
iPixelType := PFD_TYPE_RGBA; //Request An RGBA Format
cColorBits := Bits; //Select Our Color Depth
cRedBits := 0; //Color Bits Ignored
cRedShift := 0;
cGreenBits := 0;
cGreenShift := 0;
cBlueBits := 0;
cBlueShift := 0;
cAlphaBits := 0; //No Alpha Buffer
cAlphaShift := 0; //Shift Bit Ignored
cAccumBits := 0; //No Accumulation Buffer
cAccumRedBits := 0; //Accumulation Bits Ignored
cAccumGreenBits := 0;
cAccumBlueBits := 0;
cAccumAlphaBits := 0;
cDepthBits := 16; //16 Bit Z-Buffer (Depth Buffer)
cStencilBits := 0; //No Stencil Buffer
cAuxBuffers := 0; //No Auxilary Buffer
iLayerType := PFD_MAIN_PLANE; //Main Drawing Layer
bReserved := 0; //Reserved
dwLayerMask := 0; //Layer Masks Ignored
dwVisibleMask := 0;
dwDamageMask := 0;
end;
h_DC := GetDC(h_Wnd); //Try Getting a Device Context
if (h_DC = 0) then // Did We Get Device Context For The Window?
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t create a GL device context.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
PixelFormat := ChoosePixelFormat(h_Dc, @pfd);
// Finds The Closest Match To The Pixel Format We Set Above
if (PixelFormat = 0) then //Did We Find A Matching Pixelformat?
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t Find A Suitable PixelFormat.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
if not (SetPixelFormat(h_Dc, PixelFormat, @pfd)) then
begin //Are We Able To Set The Pixelformat?
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t set PixelFormat.', 'Error', MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
h_RC := wglCreateContext(h_DC); //Are We Able To create a Rendering Context?
if (h_RC = 0) then
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t create a GL rendering context.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
if not (wglMakeCurrent(h_DC, h_RC)) then
//Are We Able To Activate The Rendering Context?
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t activate the GL rendering context.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
ShowWindow(h_Wnd, SW_SHOW); //Show The Window
SetForegroundWindow(h_Wnd); //Slightly Higher Priority
SetFocus(h_Wnd); //Set Keyboard Focus To The Window
ReSizeGLScene(Width, Height); //Set Up Our Perspective Gl Screen
if not (InitGl(Width, Height)) then
//Do all the initialization here (load textures, etc)
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Initialization Failed.', 'Error', MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
CreateGLWindow := True //Succes
end;
//WinMain is Main Program (gets called from the actual Main.dpr)
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nCmdShow:
Integer): integer; stdcall;
var
msg: TMsg;
begin
if MessageBox(0, 'Would You Like To Run In FullScreen Mode?', 'Start FullScreen',
MB_YESNO or MB_ICONQUESTION) = idNo then
GS.Fullscreen := False
else
GS.Fullscreen := True;
if not (CreateGLWindow(WIN_TITLE, POS_X, POS_Y, RES_X, RES_Y, RES_BITS,
GS.Fullscreen)) then
begin //Could We Create The OpenGL Window?
Result := 0;
exit
end;
while not (GS.ExitGame) do //Main Game Loop
begin
if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then //Is There A Message?
begin
if (msg.message = WM_QUIT) then //Have We Received A Quit Message?
GS.ExitGame := True
else
begin
TranslateMessage(msg); //Translate Message
DispatchMessage(msg); //Dispatch the Message
end
end
else
{//No messages, so keep rendering our game} if (GS.Active) and not (DrawGLScene())
then //here's where all the fun happens
GS.ExitGame := True
else
SwapBuffers(h_DC); //Not Time To Quit Yet
//Check for keyboard input here
if (GS.Keys[VK_ESCAPE]) then //Time To Quit
begin
GS.Keys[VK_ESCAPE] := False;
GS.ExitGame := True;
end
else if (GS.Keys[VK_F1]) then //Toggle FullScreen Mode
begin
GS.Keys[VK_F1] := False;
KillGLWindow(); //Kill Our Current Window
GS.Fullscreen := not GS.Fullscreen; //Toggle Our Fullscreen Flag
//Recreate Our Window
if not CreateGLWindow(WIN_TITLE, POS_X, POS_Y, RES_X, RES_Y, RES_BITS,
GS.Fullscreen) then
Result := 0;
end
end; //While not GS.ExitGame
{ End of the Game }
KillGLWindow(); //Shutdown
Result := msg.wParam
end;
end.
{and here's the code for the "project source"}
program prjShell;
uses
oglMain in 'oglMain.pas';
begin
GS.Active := True;
WinMain(hInstance, hPrevInst, CmdLine, CmdShow);
end.
2007. január 7., vasárnap
Determine if a Unicode string is Baltic or Russian
Problem/Question/Abstract:
How to determine if a Unicode string is Baltic or Russian
Answer:
procedure TForm1.SetReadableText(const ws: WideString);
var
s: string;
pch: PChar;
i, CodePage, Charset: Integer;
begin
CodePage := 1252;
Charset := ANSI_CHARSET;
pch := PChar(PWideChar(ws));
for i := 0 to length(ws) - 1 do
begin
if ord(pch[2 * i + 1]) = 1 then
begin
CodePage := 1257;
Charset := BALTIC_CHARSET;
break;
end;
if ord(pch[2 * i + 1]) = 4 then
begin
CodePage := 1251;
Charset := RUSSIAN_CHARSET;
break;
end;
end;
setlength(s, 2 * length(ws));
setlength(s, WideCharToMultiByte(CodePage, 0, PWideChar(ws), length(ws),
PChar(s), length(s), nil, nil));
Edit1.Font.Charset := Charset;
Edit1.Text := s;
end;
2007. január 6., szombat
Create a scrollbox that lets you disable automatic scrolling
Problem/Question/Abstract:
How to create a scrollbox that lets you disable automatic scrolling
Answer:
unit MyScrollBox;
interface
uses
SysUtils, Classes, Controls, Forms;
type
TMyScrollBox = class(TScrollBox)
private
FEnableScrollInView: Boolean;
protected
procedure AutoScrollInView(AControl: TControl); override;
public
constructor Create(AOwner: TComponent); override;
published
property EnableScrollInView: Boolean read FEnableScrollInView
write FEnableScrollInView default True;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyScrollBox]);
end;
procedure TMyScrollBox.AutoScrollInView(AControl: TControl);
begin
if FEnableScrollInView then
inherited AutoScrollInView(AControl);
end;
constructor TMyScrollBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FEnableScrollInView := True;
end;
end.
2007. január 5., péntek
Fill a polygon
Problem/Question/Abstract:
How to fill a polygon
Answer:
Below is a demo application with all code inside for drawing and hit-testing polygons. It uses an algorithm which searches for intersections between each scanline (or Y coordinate) with polygon vertices. It is not optimized (though it's quite fast) and it's also universal. It fills all types of polygons, not just concave, or similar. Filling style is equivalent to WINDING comparing to GDI and cannot be changed so far. The slowest part of polygon filling is it's rasterization, also called the polygon scan conversion where polygon has to be transformed into regions that needs to be filled. This can be speed up by caching previously calculated fill ranges. You can do that yourself or you can use TPolygon object that is included. It caches ranges by itself. Note that caching will only work if points do not change (cache is discarded on each point change) but for hit-testing you don't need to use caching because ranges for only one scanline are calculates and not for whole polygon (except if you use TPolygon object where all ranges are precalculated).
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
SpinEdit1: TSpinEdit;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure SpinEdit1Change(Sender: TObject);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
type
{Stores a fill range which is equal to a scanline but there can be many fill ranges for one X coordinate}
TRange = packed record
X: Integer;
Count: Word;
end;
TRangeList = array of TRange;
TRangeListArray = array of TRangeList;
{TPolygon class represents a polygon. It containes points that define a polygon and
caches fill range list for fast polygon filling.}
TPolygon = class
private
FPoints: array of TPoint;
FStartY: Integer;
FRangeList: TRangeListArray;
function GetCount: Integer;
procedure SetCount(AValue: Integer);
function GetPoint(Index: Integer): TPoint;
procedure SetPoint(Index: Integer; APoint: TPoint);
protected
{Initializes range list}
procedure RangeListNeeded;
function GetFillRange(Y: Integer): TRangeList;
public
constructor Create;
destructor Destroy; override;
procedure AssignPoints(APoints: array of TPoint);
procedure Offset(dx, dy: Integer);
property Count: Integer read GetCount write SetCount;
property Points[Index: Integer]: TPoint read GetPoint write SetPoint;
end;
{Returns fill range list for specified Y coordinate. It calculates intersection
points with specified scanline (at Y coordinates).}
procedure Polygon_GetFillRange(const Points: array of TPoint; Y: Integer;
out ARangeList: TRangeList);
{Returns bounds of polygon}
function Polygon_GetBounds(const Points: array of TPoint): TRect;
{Returns True if point lies inside polygon}
function Polygon_PtInside(const Points: array of TPoint; Pt: TPoint): Boolean;
implementation
{$R *.dfm}
type
pRangeItem = ^TRangeItem;
TRangeItem = record
X: Integer;
Up: Boolean;
Next: pRangeItem;
end;
procedure Polygon_GetFillRange(const Points: array of TPoint; Y: Integer;
out ARangeList: TRangeList);
var
{first item in list}
AItem: pRangeItem;
procedure AddIntersection(X: Integer; Up: Boolean);
var
p, p2, Prev: pRangeItem;
begin
New(p);
Prev := nil;
p^.X := X;
p^.Up := Up;
p^.Next := nil;
if Assigned(AItem) then
begin
{insert into sorted position}
p2 := AItem;
while Assigned(p2) do
begin
if p2^.X > X then
begin
if Assigned(Prev) then
begin
Prev^.Next := p;
p^.Next := p2;
Break;
end
else
begin
p^.Next := p2;
AItem := p;
Break;
end;
end;
if p2^.Next = nil then
begin
{add to the end}
p2^.Next := p;
Break;
end;
Prev := p2;
p2 := p2^.Next;
end;
end
else
AItem := p;
end;
var
i, X, X0, Cnt: Integer;
LastDirection: Boolean;
p: pRangeItem;
begin
if Length(Points) = 0 then
Exit;
AItem := nil;
Cnt := 0;
for i := 0 to Length(Points) - 2 do
begin
if ((Points[i].Y > Y) and (Points[i + 1].Y <= Y)) or ((Points[i].Y <= Y) and
(Points[i + 1].Y > Y)) then
if Points[i + 1].Y <> points[i].Y then
begin
X := Round(Points[i].X + ((Points[i + 1].X - Points[i].X) *
(Y - Points[i].Y) / (Points[i + 1].Y - points[i].Y)));
AddIntersection(X, Points[i + 1].Y > Points[i].Y);
Inc(Cnt);
end;
end;
{close polygon}
i := Length(Points) - 1;
if ((Points[i].Y > Y) and (Points[0].Y <= Y)) or ((Points[i].Y <= Y) and (Points[0].Y
> Y)) then
if Points[0].Y <> points[i].Y then
begin
X := Round(Points[i].X + ((Points[0].X - Points[i].X) * (Y - Points[i].Y) /
(Points[0].Y - points[i].Y)));
AddIntersection(X, Points[0].Y > Points[i].Y);
Inc(Cnt);
end;
p := AItem;
{calculate fill ranges}
i := 1; {use as acumulative direction counter}
SetLength(ARangeList, Cnt);
Cnt := 0; {number of range items in array}
if Assigned(AItem) then
begin
LastDirection := AItem^.Up; {init last direction}
X0 := AItem^.X;
AItem := AItem^.Next;
end;
while Assigned(AItem) do
begin
if AItem^.Up = LastDirection then
begin
Inc(i);
if i = 1 then
X0 := AItem^.X; {init start position}
end
else
begin
Dec(i);
if i = -1 then
X0 := AItem^.X; {init start position}
end;
if i = 0 then
begin
ARangeList[Cnt].X := X0;
ARangeList[Cnt].Count := AItem^.X - X0;
Inc(Cnt);
LastDirection := AItem^.Up;
end;
AItem := AItem^.Next;
end;
{shrink list}
SetLength(ARangeList, Cnt);
{delete internal range list}
while Assigned(p) do
begin
AItem := p;
p := p^.Next;
Dispose(AItem);
end;
end;
function Polygon_GetBounds(const Points: array of TPoint): TRect;
var
i: Integer;
begin
Result := Rect(0, 0, 0, 0);
for i := 0 to Length(Points) - 1 do
begin
if i = 0 then
Result := Rect(Points[i].X, Points[i].Y, Points[i].X, Points[i].Y)
else
begin
if Points[i].X < Result.Left then
Result.Left := Points[i].X;
if Points[i].Y < Result.Top then
Result.Top := Points[i].Y;
if Points[i].X > Result.Right then
Result.Right := Points[i].X;
if Points[i].Y > Result.Bottom then
Result.Bottom := Points[i].Y;
end;
end;
Result.Right := Result.Right + 1;
Result.Bottom := Result.Bottom + 1;
end;
function Polygon_PtInside(const Points: array of TPoint; Pt: TPoint): Boolean;
var
RL: TRangeList;
i: Integer;
begin
Result := False;
Polygon_GetFillRange(Points, Pt.Y, RL);
for i := 0 to Length(RL) - 1 do
begin
Result := (Pt.X >= RL[i].X) and (Pt.X < RL[i].X + RL[i].Count);
if Result then
Exit;
end;
end;
{TPolygon}
procedure TPolygon.AssignPoints(APoints: array of TPoint);
begin
SetLength(FRangeList, 0);
SetLength(FPoints, Length(APoints));
Move(APoints, FPoints, Length(APoints) * SizeOf(TPoint));
{clear cache}
SetLength(FRangeList, 0);
end;
constructor TPolygon.Create;
begin
SetLength(FPoints, 0);
SetLength(FRangeList, 0);
FStartY := 0;
end;
destructor TPolygon.Destroy;
begin
SetLength(FPoints, 0);
SetLength(FRangeList, 0);
end;
function TPolygon.GetCount: Integer;
begin
Result := Length(FPoints);
end;
function TPolygon.GetFillRange(Y: Integer): TRangeList;
begin
RangeListNeeded;
SetLength(Result, 0);
if (Y >= FStartY) and (Y < Length(FPoints) + FStartY) then
Result := FRangeList[Y];
end;
function TPolygon.GetPoint(Index: Integer): TPoint;
begin
Result := FPoints[Index];
end;
procedure TPolygon.Offset(dx, dy: Integer);
var
i, j: Integer;
begin
RangeListNeeded;
FStartY := FStartY + dy;
for i := 0 to Length(FRangeList) - 1 do
for j := 0 to Length(FRangeList[i]) - 1 do
Inc(FRangeList[i][j].X, dx);
end;
procedure TPolygon.RangeListNeeded;
var
R: TRect;
Y, i: Integer;
begin
if Length(FPoints) <> Length(FRangeList) and Length(FPoints) then
begin
SetLength(FRangeList, Length(FPoints));
R := Polygon_GetBounds(FPoints);
i := 0;
for Y := R.Top to R.Bottom do
begin
Polygon_GetFillRange(FPoints, Y, FRangeList[i]);
Inc(i);
end;
end;
end;
procedure TPolygon.SetCount(AValue: Integer);
begin
SetLength(FPoints, AValue);
{Clear cache on point list change}
SetLength(FRangeList, 0);
end;
procedure TPolygon.SetPoint(Index: Integer; APoint: TPoint);
begin
FPoints[Index] := APoint;
{Clear cache if a point changes}
SetLength(FRangeList, 0);
end;
var
APoints: array of TPoint;
AColor: TColor = clBlack;
APtInside: Boolean = False;
procedure FillPolygon(ACanvas: TCanvas; APoints: array of TPoint);
var
i, j: Integer;
R: TRect;
ARangeList: TRangeList;
begin
ACanvas.Pen.Color := AColor;
{Find polygon bounds because we only need to calculate fill-ranges from
top to bottom value of rectangle}
R := Polygon_GetBounds(APoints);
for i := R.Top to R.Bottom do
begin
Polygon_GetFillRange(APoints, i, ARangeList);
{Since there can be many fill ranges for one Y, function returns a list of all}
for j := 0 to Length(ARangeList) - 1 do
begin
{fill pixels inside range}
{so far I'll just draw a line with GDI but this part can be substituted with your own draw function}
ACanvas.MoveTo(ARangeList[j].X, i);
ACanvas.LineTo(ARangeList[j].X + ARangeList[j].Count, i);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to Length(APoints) - 1 do
APoints[i] := Point(Random(ClientWidth), Random(ClientHeight));
Repaint;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
Randomize;
SetLength(APoints, SpinEdit1.Value);
for i := 0 to Length(APoints) - 1 do
APoints[i] := Point(Random(ClientWidth), Random(ClientHeight));
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
FillPolygon(Canvas, APoints);
end;
procedure TForm1.SpinEdit1Change(Sender: TObject);
var
i: Integer;
begin
SetLength(APoints, SpinEdit1.Value);
for i := 0 to Length(APoints) - 1 do
APoints[i] := Point(Random(ClientWidth), Random(ClientHeight));
Repaint;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Polygon_PtInside(APoints, Point(X, Y)) then
begin
if not APtInside then
begin
Caption := 'Inside: YES';
AColor := clRed;
APtInside := True;
Repaint;
end;
end
else
begin
if APtInside then
begin
Caption := 'Inside: NO';
AColor := clBlack;
APtInside := False;
Repaint;
end;
end;
end;
end.
{main.dfm}
object Form1: TForm1
Left = 290
Top = 153
Width = 783
Height = 540
Caption = 'Inside: NO'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnMouseMove = FormMouseMove
OnPaint = FormPaint
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 168
Top = 12
Width = 54
Height = 13
Caption = 'Point count'
end
object Button1: TButton
Left = 8
Top = 8
Width = 145
Height = 25
Caption = 'Randomize points'
TabOrder = 0
OnClick = Button1Click
end
object SpinEdit1: TSpinEdit
Left = 232
Top = 8
Width = 73
Height = 22
MaxValue = 0
MinValue = 0
TabOrder = 1
Value = 8
OnChange = SpinEdit1Change
end
end
2007. január 4., csütörtök
Deleting all records in a table/deleting a table in a database without SQL
Problem/Question/Abstract:
How can we delete all the records in a table without using a SQL statement?
How can we delete a table in a database without using a SQL statement?
Answer:
In Delphi, all versions, we have a component called TTable. We can use a method of that component to delete all the records in a table. Note that we should have privilege on that database to delete records in that table.
We can use the following code to do that:
with Table1 do
begin
Active := False;
DatabaseName := 'dbname';
TableName := 'tablename';
EmptyTable;
end;
The important thing to note here is that we need to set the Table’s active property to false before calling the EmptyTable method.
If we try to empty the table when the table is open (i.e Active is True), then we will be getting an EDBEngineError exception like the following:
Table cannot be opened for exclusive use.
Also we can use another method of the TTable component to delete the table itself provided we have privileges on that database to do that.
The following code will do that:
with Table1 do
begin
Active := False;
DatabaseName := 'dbname';
TableName := 'tablename';
DeleteTable;
end;
Here also the table must be closed (setting the Active property to false) before calling the DeleteTable method. Otherwise the method will throw an exception.
If we perform this operation on an open table, you will be getting an EDatabaseError exception like the following:
Table1 : Cannot perform this operation on a open dataset.
It’s always easy to call a method of a component in Delphi; but we should remember some important things before we call that method.
Here with these two methods above, we need to keep in mind two things before calling them:
We should have enough privileges to do that operation on a database where the table resides.
We need to close the table by either calling the TTable’s close method or setting the Ttable’s active property to false.
2007. január 3., szerda
Create an autorun CD
Problem/Question/Abstract:
This is not exactly a Delphi trick, but as a programmer (using any language) some time you might need this
Answer:
what you have to do is just create a text file with the notepad (or whatever) with the following text:
[autorun]
OPEN=myprogram.EXE
ICON=myicon.ICO
of course myprogram.exe is the application that you want to "autorun" and the icon will be the icon for the CD when you put it in that's it, now just save your text file with the name: AutoRun.INF in the root of the CD and you've got your self an autorun CD
This is not exactly a Delphi trick, but as a programmer (using any language) some time you might need this
Answer:
what you have to do is just create a text file with the notepad (or whatever) with the following text:
[autorun]
OPEN=myprogram.EXE
ICON=myicon.ICO
of course myprogram.exe is the application that you want to "autorun" and the icon will be the icon for the CD when you put it in that's it, now just save your text file with the name: AutoRun.INF in the root of the CD and you've got your self an autorun CD
2007. január 1., hétfő
Read MS-SQL Error Logs via SQL-DMO into TStrings
Problem/Question/Abstract:
Functions to load a StringList with MS-SQL Server Error Logs via SQL-DMO. MS-SQL DMO is a COM/OLE object that can do many things, in this article we just read the error logs off the server.
There a two overloaded functions, one for Windows Authentication, and another for SQL Authentication. The function returns true if successful. The default log number is 0 (Current Log).
// Windows Authentication
function SqlErrorLog(AStrings: TStrings;
const ASqlServer: string;
ALogNumber: integer = 0): boolean; overload;
// SQL Authentication
function SqlErrorLog(AStrings: TStrings;
const ASqlServer, AUserName, APassword: string;
ALogNumber: integer = 0): boolean; overload;
Example
// Load memo using Default Log 0 and Windows Authentication
if SqlErrorLog(Memo1.Lines, 'BusServer1') then
.....
// Load memo using Log 3 and SQL Authentication
if SqlErrorLog(Memo1.Lines, 'BusServer', 'harry', 'mypass', 3) then
..
Answer:
uses ComObj, Variants; {Variants is for Delphi 7}
// =====================================================
// PRIMITIVE Load MS SQL Server Error Log Function
// =====================================================
function _SqlErrorLog(AObject: OleVariant;
AStrings: TStrings;
const ASqlServer: string;
ALogNumber: integer): boolean;
var
oLog: OleVariant;
bResult: boolean;
i: integer;
begin
try
AObject.Connect(ASqlServer);
try
AStrings.BeginUpdate;
oLog := AObject.ReadErrorLog(ALogNumber);
for i := 1 to oLog.Rows do
AStrings.Add(oLog.GetColumnString(i, 1));
oLog := Unassigned;
finally
AStrings.EndUpdate;
end;
AObject.Disconnect;
bResult := true;
except
bResult := false;
end;
AObject := Unassigned;
Result := bResult;
end;
// =====================================================
// Get SQL Server Log using Windows Authentication
// =====================================================
function SqlErrorLog(AStrings: TStrings;
const ASqlServer: string;
ALogNumber: integer = 0): boolean; overload;
var
oDMO: OleVariant;
bResult: boolean;
begin
AStrings.Clear;
try
oDMO := CreateOleObject('SQLDMO.SQLServer');
oDMO.LoginSecure := true;
bResult := _SqlErrorLog(oDMO, AStrings, ASqlServer, ALogNumber);
except
bResult := false;
end;
Result := bResult;
end;
// =====================================================
// Get SQL Server Log using SQL Authentication
// =====================================================
function SqlErrorLog(AStrings: TStrings;
const ASqlServer, AUserName, APassword: string;
ALogNumber: integer = 0): boolean; overload;
var
oDMO: OleVariant;
bResult: boolean;
begin
AStrings.Clear;
try
oDMO := CreateOleObject('SQLDMO.SQLServer');
oDMO.LoginSecure := false;
oDMO.Login := AUserName;
oDMO.Password := APassword;
bResult := _SqlErrorLog(oDMO, AStrings, ASqlServer, ALogNumber);
except
bResult := false;
end;
Result := bResult;
end;
Functions to load a StringList with MS-SQL Server Error Logs via SQL-DMO. MS-SQL DMO is a COM/OLE object that can do many things, in this article we just read the error logs off the server.
There a two overloaded functions, one for Windows Authentication, and another for SQL Authentication. The function returns true if successful. The default log number is 0 (Current Log).
// Windows Authentication
function SqlErrorLog(AStrings: TStrings;
const ASqlServer: string;
ALogNumber: integer = 0): boolean; overload;
// SQL Authentication
function SqlErrorLog(AStrings: TStrings;
const ASqlServer, AUserName, APassword: string;
ALogNumber: integer = 0): boolean; overload;
Example
// Load memo using Default Log 0 and Windows Authentication
if SqlErrorLog(Memo1.Lines, 'BusServer1') then
.....
// Load memo using Log 3 and SQL Authentication
if SqlErrorLog(Memo1.Lines, 'BusServer', 'harry', 'mypass', 3) then
..
Answer:
uses ComObj, Variants; {Variants is for Delphi 7}
// =====================================================
// PRIMITIVE Load MS SQL Server Error Log Function
// =====================================================
function _SqlErrorLog(AObject: OleVariant;
AStrings: TStrings;
const ASqlServer: string;
ALogNumber: integer): boolean;
var
oLog: OleVariant;
bResult: boolean;
i: integer;
begin
try
AObject.Connect(ASqlServer);
try
AStrings.BeginUpdate;
oLog := AObject.ReadErrorLog(ALogNumber);
for i := 1 to oLog.Rows do
AStrings.Add(oLog.GetColumnString(i, 1));
oLog := Unassigned;
finally
AStrings.EndUpdate;
end;
AObject.Disconnect;
bResult := true;
except
bResult := false;
end;
AObject := Unassigned;
Result := bResult;
end;
// =====================================================
// Get SQL Server Log using Windows Authentication
// =====================================================
function SqlErrorLog(AStrings: TStrings;
const ASqlServer: string;
ALogNumber: integer = 0): boolean; overload;
var
oDMO: OleVariant;
bResult: boolean;
begin
AStrings.Clear;
try
oDMO := CreateOleObject('SQLDMO.SQLServer');
oDMO.LoginSecure := true;
bResult := _SqlErrorLog(oDMO, AStrings, ASqlServer, ALogNumber);
except
bResult := false;
end;
Result := bResult;
end;
// =====================================================
// Get SQL Server Log using SQL Authentication
// =====================================================
function SqlErrorLog(AStrings: TStrings;
const ASqlServer, AUserName, APassword: string;
ALogNumber: integer = 0): boolean; overload;
var
oDMO: OleVariant;
bResult: boolean;
begin
AStrings.Clear;
try
oDMO := CreateOleObject('SQLDMO.SQLServer');
oDMO.LoginSecure := false;
oDMO.Login := AUserName;
oDMO.Password := APassword;
bResult := _SqlErrorLog(oDMO, AStrings, ASqlServer, ALogNumber);
except
bResult := false;
end;
Result := bResult;
end;
Feliratkozás:
Bejegyzések (Atom)