2007. szeptember 14., péntek
How to modify the color of a TCheckBox
Problem/Question/Abstract:
How to modify the color of a TCheckBox
Answer:
I would do the drawing in the CN_DRAWITEM message handler. Below is the code of such a checkbox:
{ ... }
type
TMyCheckBox = class(TCheckBox)
protected
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
procedure CMEnabledchanged(var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure SetChecked(Value: Boolean); override;
procedure SetButtonStyle;
public
constructor Create(AOwner: TComponent); override;
end;
{ ... }
constructor TMyCheckBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle - [csDoubleClicks];
end;
procedure TMyCheckBox.CNDrawItem(var Message: TWMDrawItem);
var
XCanvas: TCanvas;
XCaptionRect, XGlyphRect: TRect;
procedure xxDrawBitMap(ACanvas: TCanvas);
const
xx_h = 13;
xx_w = 13;
var
xxGlyph: TBitmap;
xxX, xxY, xxStepY, xxStepX: integer;
begin
xxGlyph := TBitmap.Create;
try
xxGlyph.Handle := LoadBitmap(0, PChar(OBM_CHECKBOXES));
xxY := XGlyphRect.Top + (XGlyphRect.Bottom - XGlyphRect.Top - xx_h) div 2;
xxX := 2;
xxStepX := 0;
xxStepY := 0;
if Enabled then
begin
case State of
cbChecked:
xxStepX := xxStepX + xx_w;
cbGrayed:
xxStepX := xxStepX + xx_w * 3;
end;
end
else if State = cbChecked then
xxStepX := xxStepX + xx_w * 3
else
xxStepX := xxStepX + xx_w * 2;
ACanvas.CopyRect(Rect(xxX, xxY, xxX + xx_w, xxY + xx_h), xxGlyph.Canvas,
Rect(xxStepX, xxStepY, xx_w + xxStepX, xx_h + xxStepY));
finally
xxGlyph.Free;
end;
end;
procedure xxDrawCaption;
var
xXFormat: longint;
begin
xXFormat := DT_VCENTER + DT_SINGLELINE + DT_LEFT;
xXFormat := DrawTextBiDiModeFlags(xXFormat);
DrawText(Message.DrawItemStruct.hDC, PChar(Caption),
length(Caption), XCaptionRect, xXFormat);
end;
begin
XGlyphRect := Message.DrawItemStruct.rcItem;
XGlyphRect.Right := 20;
XCaptionRect := Message.DrawItemStruct.rcItem;
XCaptionRect.Left := XGlyphRect.Right;
XCanvas := TCanvas.Create;
try
XCanvas.Handle := Message.DrawItemStruct.hDC;
XCanvas.Brush.Style := bsClear;
xxDrawBitMap(XCanvas);
xxDrawCaption;
finally
XCanvas.Free;
end;
end;
procedure TMyCheckBox.CMEnabledchanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TMyCheckBox.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_Transparent;
end;
procedure TMyCheckBox.CreateWnd;
begin
inherited CreateWnd;
SetButtonStyle;
end;
procedure TMyCheckBox.SetChecked(Value: Boolean);
begin
inherited SetChecked(Value);
Invalidate;
end;
procedure TMyCheckBox.SetButtonStyle;
const
BS_MASK = $000F;
var
Style: Word;
begin
if HandleAllocated then
begin
Style := BS_CHECKBOX or BS_OWNERDRAW;
if GetWindowLong(Handle, GWL_STYLE) and BS_MASK <> Style then
SendMessage(Handle, BM_SETSTYLE, Style, 1);
end;
end;
2007. szeptember 13., csütörtök
Painting the Form Menu bar
Problem/Question/Abstract:
When i uses programs like the game freecell that comes with windows 9x i see that there is a text in the menu bar tht tells me how many cards left me.
How can i make something like that in my programs ?
Answer:
Well, First of all we need to put a main menu component on our form.
Now set the OwnerDraw property to true.
If you have an item that you wish to paint by yourself, now is the time to create it and to make the OnDrawItem.
In this line you put also this line:
{... }
ACanvas.TextOut(1, ARect.Top + 1, 'I''m in the MainMenuDrawbar');
{... }
Note, If you need to use a changed variable you can do it from another function and ll you need to do afther the change is to call the API function DrawMenuBar.
If you are using Delphi 2,3 Use the Messages WM_MESUREITEM and the message WM_DRAWITEM to make this effect.
2007. szeptember 12., szerda
How to print bitmaps and controls placed on a TPanel
Problem/Question/Abstract:
I have placed several images and assorted graphic controls on a TPanel. Now I want to print it. My problem is that the panel does not have a canvas property. Somehow I should be able to manipulate the "graphics" on the panel. What I thought might work is to do a screen capture of the panel area, but I am not sure what the function calls are. Does anybody have any ideas? I want to be able to scale the image and print it to a specific part of the page.
Answer:
The form has a canvas. You can create a new bitmap the same size as your panel and then use CopyRect to copy the panel and its content from the form to this in- memory bitmap. Then you can print the in-memory bitmap. Here's an example:
procedure TFormPrintWindows.ButtonPrintPanelClick(Sender: TObject);
var
Bitmap: TBitmap;
FromLeft, FromTop, PrintedWidth, PrintedHeight: Integer;
begin
Printer.BeginDoc;
try
Bitmap := TBitmap.Create;
try
Bitmap.Width := Panel1.Width;
Bitmap.Height := Panel1.Height;
Bitmap.PixelFormat := pf24bit; {Avoid palettes}
{Copy the panel area from the form into a separate bitmap}
Bitmap.Canvas.CopyRect(Rect(0, 0, Bitmap.Width, Bitmap.Height),
FormPrintWindows.Canvas, Rect(Panel1.Left, Panel1.Top, Panel1.Left +
Panel1.Width - 1, Panel1.Top + Panel1.Height - 1));
{Assumes 10% left, right and top margin}
{Assumes bitmap aspect ratio > ~0.75 for portrait mode}
PrintedWidth := MulDiv(Printer.PageWidth, 80, 100); {80%}
PrintedHeight := MulDiv(PrintedWidth, Bitmap.Height, Bitmap.Width);
FromLeft := MulDiv(Printer.PageWidth, 10, 100); {10%}
FromTop := MulDiv(Printer.PageHeight, 10, 100); {10%}
PrintBitmap(Printer.Canvas, Rect(FromLeft, FromTop, FromLeft + PrintedWidth,
FromTop + PrintedHeight), Bitmap);
finally
Bitmap.Free
end;
finally
Printer.EndDoc
end;
end;
2007. szeptember 11., kedd
Can your video handle 16, 256, 32768, 16777216, or more colors
Problem/Question/Abstract:
Can your video handle 16, 256, 32768, 16777216, or more colors?
Answer:
You can use WIN API function GetDeviceCaps() to calculate the number of colors supported by the current video mode. To make it even easier to use, here's a function that will simply return the number of maximum simultaneous colors current video device can handle:
function GetColorsCount: integer;
var
h: hDC;
begin
Result := 0;
try
h := GetDC(0);
Result :=
1 shl
(
GetDeviceCaps(h, PLANES) *
GetDeviceCaps(h, BITSPIXEL)
);
finally
ReleaseDC(0, h);
end;
end;
2007. szeptember 10., hétfő
How to use TCollection and TCollectionItem
Problem/Question/Abstract:
Has anyone out there attempted to use TCollection and TCollectionItem? What I am trying to do is mimic what the Columns Editor does in the TDBGrid for the TStringGrid component. This is the first time that I have made a component that needs properties and sub-properties. I am not sure how to go about this.
Answer:
This one worked for me:
unit ggImgLst;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Dialogs,
ExtCtrls, Dsgnintf; {, jpeg;}
type
TAboutProperty = class(TPropertyEditor)
private
protected
public
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
function GetName: string; override;
function GetValue: string; override;
end;
TggImageListPropertyEditor = class(TPersistent);
TggImageListProperty = class(TClassProperty);
TggImageSizes = (ggSmall, ggMedium, ggLarge);
{TggImageSize = set of TggImageSizes;}
TggImage = class;
TggImageList = class;
TggImage = class(TCollectionItem)
private
FSize: TggImageSizes;
FPicture: TPicture;
FName: string;
function GetDisplayName: string; override;
procedure SetPicture(Value: TPicture);
public
constructor Create(Collection: TCollection); override;
destructor destroy; override;
published
property Size: TggImageSizes read FSize write FSize;
property Name: string read FName write FName;
property Picture: TPicture read FPicture write SetPicture;
end;
TggImageClass = class of TggImage;
TggImages = class(TCollection)
private
FggImageList: TggImageList;
FggImageListPropertyEditor: TggImageListPropertyEditor;
function GetImage(Index: Integer): TggImage;
procedure SetImage(Index: Integer; Value: TggImage);
protected
function GetOwner: TPersistent; override;
public
constructor create(ggImageList: TggImageList; ggImageClass: TggImageClass);
function Add: TggImage;
property ggImageList: TggImageList read FggImageList;
property Items[Index: Integer]: TggImage read GetImage write SetImage; default;
published
end;
TggImageList = class(TComponent)
private
FAbout: TAboutProperty;
FImages: TggImages;
procedure WriteImages(Writer: TWriter);
procedure ReadImages(Reader: TReader);
procedure SetImages(Value: TggImages);
protected
function CreateImages: Tggimages; dynamic;
procedure DefineProperties(Filer: TFiler); override;
public
constructor Create(AOwner: TComponent); override;
function GetImageNameList: TStringList;
function GetPicture(PictureName: string): TPicture;
published
property About: TAboutProperty read FAbout write FAbout;
property Images: TggImages read FImages write SetImages;
end;
procedure Register;
implementation
uses
jpeg;
{ggImage}
constructor TggImage.Create(Collection: TCollection);
var
ggImageList: TggImageList;
begin
FPicture := TPicture.Create;
ggImageList := nil;
if assigned(Collection) and (Collection is TggImages) then
ggImageList := Tggimages(Collection).ggImageList;
if assigned(ggImageList) then
inherited Create(Collection);
end;
destructor TggImage.Destroy;
begin
FPicture.Free;
inherited Destroy;
end;
procedure TggImage.SetPicture(Value: TPicture);
begin
FPicture.Assign(Value);
end;
function TggImage.GetDisplayName: string;
begin
Result := Name;
if Result = '' then
Result := inherited GetDisplayName;
end;
{TggImages}
function TggImages.GetImage(Index: Integer): TggImage;
begin
Result := TggImage(inherited Items[Index]);
end;
procedure TggImages.SetImage(Index: Integer; Value: TggImage);
begin
Items[Index].Assign(Value);
end;
constructor TggImages.Create(ggImageList: TggImageList;
ggImageClass: TggImageClass);
begin
inherited Create(ggImageClass);
FggImageList := ggImageList;
FggImageListPropertyEditor := TggImageListPropertyEditor.Create;
end;
function TggImages.GetOwner: TPersistent;
begin
Result := FggImageList;
end;
function TggImages.Add: TggImage;
begin
Result := TggImage(inherited Add);
end;
{ggImageList}
procedure TggImageList.WriteImages(Writer: TWriter);
begin
Writer.WriteCollection(Images);
end;
procedure TggImageList.ReadImages(Reader: TReader);
begin
Images.Clear;
Reader.ReadValue;
Reader.ReadCollection(Images);
end;
procedure TggImageList.DefineProperties(Filer: TFiler);
begin
Filer.DefineProperty('ggImages', ReadImages, WriteImages, Filer.Ancestor < > nil);
end;
procedure TggImageList.SetImages(Value: TggImages);
begin
Images.Assign(Value);
end;
function TggImageList.CreateImages: TggImages;
begin
Result := TggImages.Create(Self, TggImage);
end;
function TggImageList.GetImageNameList: TStringList;
var
I: Integer;
begin
Result := TStringList.Create;
for I := 0 to Self.Images.Count - 1 do
Result.Add(Self.Images.Items[I].Name);
end;
function TggImageList.GetPicture(PictureName: string): TPicture;
var
I: Integer;
begin
I := 0;
Result := nil;
PictureName := uppercase(Picturename);
while I <= Self.Images.Count - 1 do
begin
if PictureName = uppercase(Self.Images.Items[I].Name) then
begin
Result := Self.Images.Items[I].Picture;
I := Self.Images.Count;
end
else
Inc(I);
end;
end;
constructor TggImageList.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FImages := CreateImages;
end;
{TAboutProperty}
procedure TAboutProperty.Edit;
begin
MessageBox(0, PChar('TggImageList component' + #13 + #13 + 'by Geurts Guido -
guido.geurts@advalvas.be ' + #13 + ' 10 / 03 / 1999'),
PChar('The GuidoG utilities present...'), MB_OK);
end;
function TAboutProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog, paReadOnly];
end;
function TAboutProperty.GetName: string;
begin
Result := 'About';
end;
function TAboutProperty.GetValue: string;
begin
Result := GetStrValue;
end;
{Non class related procedures and functions:}
procedure register;
begin
RegisterComponents('GuidoG', [TggImageList]);
RegisterPropertyEditor(TypeInfo(TggImageListPropertyEditor), TGGImages,
'Images', TGGImageListProperty);
RegisterPropertyEditor(TypeInfo(TAboutProperty), TggImageList, 'About',
TAboutProperty);
end;
end.
2007. szeptember 9., vasárnap
How to access a single object in a metafile
Problem/Question/Abstract:
How to access a single object in a metafile
Answer:
Below is an example of getting metafile information and enumerating each metafile record :
function MyEnhMetaFileProc(DC: HDC; {handle to device context}
lpHTable: PHANDLETABLE; {pointer to metafile handle table}
lpEMFR: PENHMETARECORD; {pointer to metafile record}
nObj: integer; {count of objects}
TheForm: TForm1): integer; stdcall;
begin
{draw the metafile record}
PlayEnhMetaFileRecord(dc, lpHTable^, lpEMFR^, nObj);
{set to zero to stop metafile enumeration}
result := 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyMetafile: TMetafile;
lpENHMETAHEADER: PENHMETAHEADER; {extra metafile info}
lpENHMETAHEADERSIZE: DWORD;
NumMetaRecords: DWORD;
begin
{Create a metafile}
MyMetafile := TMetafile.Create;
with TMetafileCanvas.Create(MyMetafile, 0) do
try
Brush.Color := clRed;
Ellipse(0, 0, 100, 100);
Ellipse(100, 100, 200, 200);
Ellipse(200, 200, 300, 300);
Ellipse(300, 300, 400, 400);
Ellipse(400, 400, 500, 500);
Ellipse(500, 500, 600, 600);
finally
Free;
end;
{we might as well get some extra metafile info}
lpENHMETAHEADERSIZE := GetEnhMetaFileHeader(MyMetafile.Handle, 0, nil);
NumMetaRecords := 0;
if (lpENHMETAHEADERSIZE > 0) then
begin
GetMem(lpENHMETAHEADER, lpENHMETAHEADERSIZE);
GetEnhMetaFileHeader(MyMetafile.Handle, lpENHMETAHEADERSIZE, lpENHMETAHEADER);
{Here is an example of getting number of metafile records}
NumMetaRecords := lpENHMETAHEADER^.nRecords;
{enumerate the records}
EnumEnhMetaFile(Canvas.Handle, MyMetafile.Handle, @MyEnhMetaFileProc, self,
Rect(0, 0, 600, 600));
FreeMem(lpENHMETAHEADER, lpENHMETAHEADERSIZE);
end;
MyMetafile.Free;
end;
2007. szeptember 8., szombat
Reference a column of a TDBGrid by name instead of integer index
Problem/Question/Abstract:
Is there a way in TDBGrid to reference a column by name rather than by integer index? Right now I am using "ListGrd.Columns[ 5 ]" (for example) to access a particular column but that is dangerous if moving columns is enabled. Can I reference a column by a column name instead?
Answer:
function TForm1.ColumnByFieldName(AGrid: TDBGrid; const AFieldName: string): TColumn;
var
I: Integer;
begin
for I := 0 to AGrid.Columns.Count - 1 do
begin
Result := AGrid.Columns[I];
if AnsiCompareText(Result.FieldName, AFieldName) = 0 then
Exit;
end;
raise Exception.Create(AGrid.Name + ', ' + AFieldName);
end;
2007. szeptember 7., péntek
How to create a countdown timer (2)
Problem/Question/Abstract:
All I want to do is to read the system time of the computer, then read it again a little later, and compare the times. I want hours, minutes, seconds and milliseconds. I have looked into TimeStamp and GetSystemTime, but I just can't get it to work. What should I do?
Answer:
var
T0, T1: TDateTime;
ElapsedSeconds: Double;
begin
T0 := Now;
{ ... }
T1 := Now;
ElapsedSeconds := 86400.0 * (T1 - T0);
end;
2007. szeptember 6., csütörtök
How to put the content of a TStringGrid into an Excel range
Problem/Question/Abstract:
How to put the content of a TStringGrid into an Excel range
Answer:
{ ... }
var
ArrV: Variant;
Cell: Range;
{ ... }
ArrV := VarArrayCreate([0, NumRows, 0, NumCols], varOleStr);
for Row := 0 to NumRows do
for Col := 0 to NumCols do
ArrV[Row, Col] := StringGrid1.Cells[Col, Row];
Cell := Excel.ActiveCell;
WS.Range[Cell, Cell.Offset[NumRows, NumCols]].Value := ArrV;
{ ... }
2007. szeptember 5., szerda
Retrieve a list of available BDE language drivers
Problem/Question/Abstract:
How can I retrieve a list of available BDE language drivers?
Answer:
The following Delphi procedure returns a formatted list of available BDE language drivers. The procedure can be called as shown in the buttonclick method below.
Add BDE and DBTables to your unit's uses clause.
procedure GetLdList(Lines: TStrings);
var
hCur: hDBICur;
LD: LDDesc;
cnt: integer;
begin
// get a cursor to the in-mem table containing language
// driver information...
cnt := 0;
check(dbiinit(nil));
Check(DbiOpenLdList(hCur));
try
while (DbiGetNextRecord(hCur, dbiNOLOCK, @LD, nil) = DBIERR_NONE) do
begin
cnt := cnt + ;
Lines.Add(format('%4d %-6s%- 0s %- 0s%5s %- 0s %- 0s', [cnt, 'Name:', LD.szName,
'Code Page:', IntToStr(LD.iCodePage), 'Description:', LD.szDesc]));
end;
finally Check(DbiCloseCursor(hCur));
check(dbiexit);
end;
end;
procedure TForm.Button Click(Sender: TObject);
begin
getldlist(memo.lines);
end;
end.
2007. szeptember 4., kedd
How to compare two strings and measure the percentage they match
Problem/Question/Abstract:
Does anyone know how or does anyone know of a good procedure to match two strings? What I want is a % match between two strings. Something like Hart and Harts are 80% equal.
Answer:
uses
math;
function IsStrMatch(s1, s2: string): Double;
var
i, iMin, iMax, iSameCount: Integer;
begin
iMax := Max(Length(s1), Length(s2));
iMin := Min(Length(s1), Length(s2));
iSameCount := -1;
for i := 0 to iMax do
begin
if i > iMin then
break;
if s1[i] = s2[i] then
Inc(iSameCount)
else
break;
end;
if iSameCount > 0 then
Result := (iSameCount / iMax) * 100
else
Result := 0.00;
end;
2007. szeptember 3., hétfő
How to create non-selectable separator lines in a TComboBox
Problem/Question/Abstract:
How to create non-selectable separator lines in a TComboBox
Answer:
Note that the Combobox1.Style is csOwnerDrawvariable.
procedure TForm1.FormCreate(Sender: TObject);
begin
with combobox1 do
begin
items.add('Item 1');
items.add('Item 2');
items.addObject('Item 3', Pointer(1));
Perform(CB_SetItemHeight, 2, ItemHeight + 5);
items.add('Item 4');
items.add('Item 5');
end;
end;
procedure TForm1.ComboBox1MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
begin
Height := (Control as TCombobox).Itemheight;
end;
procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
needsSeparator: Boolean;
begin
with Control as TCombobox do
begin
needsSeparator := Assigned(Items.Objects[index]) and not (odComboBoxEdit in State);
if needsSeparator then
Rect.Bottom := Rect.Bottom - 5;
Canvas.FillRect(Rect);
Canvas.TextRect(Rect, Rect.Left + 2, Rect.Top, Items[index]);
if needsSeparator then
begin
Rect.Top := Rect.Bottom;
Rect.Bottom := Rect.Bottom + 5;
Canvas.Brush.Color := color;
Canvas.Pen.Color := font.Color;
Canvas.Pen.Style := psSolid;
Canvas.Fillrect(Rect);
Canvas.MoveTo(rect.left, rect.top + 2);
Canvas.LineTo(rect.right, rect.top + 2);
end;
end;
end;
2007. szeptember 2., vasárnap
Get filepath from shortcut
Problem/Question/Abstract:
How to obtain the linked file from a shortcut
Answer:
uses ShellAPI;
function ExeFromLink(const linkname: string): string;
var
FDir,
FName,
ExeName: PChar;
z: integer;
begin
ExeName := StrAlloc(MAX_PATH);
FName := StrAlloc(MAX_PATH);
FDir := StrAlloc(MAX_PATH);
StrPCopy(FName, ExtractFileName(linkname));
StrPCopy(FDir, ExtractFilePath(linkname));
z := FindExecutable(FName, FDir, ExeName);
if z > 32 then
Result := StrPas(ExeName)
else
Result := '';
StrDispose(FDir);
StrDispose(FName);
StrDispose(ExeName);
end;
2007. szeptember 1., szombat
How to catch windows keystrokes and pass them to an assigned event
Problem/Question/Abstract:
How to catch windows keystrokes and pass them to an assigned event
Answer:
For those interested, here's a keyboard hook component that catches windows keystrokes and passes them to an assigned event.
unit KeyboardHook;
{
By William Egge
Sep 20, 2002
egge@eggcentric.com
http://www.eggcentric.com
This code may be used/modified however you wish.
}
interface
uses
Windows, Classes;
type
TCallbackThunk = packed record
POPEDX: Byte;
MOVEAX: Byte;
SelfPtr: Pointer;
PUSHEAX: Byte;
PUSHEDX: Byte;
JMP: Byte;
JmpOffset: Integer;
end;
{See windows help on KeyboardProc or press F1 while your cursor is on "KeyboardProc"}
TKeyboardCallback = procedure(code: Integer; wparam: WPARAM; lparam: LPARAM) of
object;
TKeyboardHook = class(TComponent)
private
{ Private declarations }
FHook: HHook;
FThunk: TCallbackThunk;
FOnCallback: TKeyboardCallBack;
function CallBack(code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;
procedure SetOnCallback(const Value: TKeyboardCallBack);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property OnCallback: TKeyboardCallBack read FOnCallback write SetOnCallback;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('EggMisc', [TKeyboardHook]);
end;
{ TKeyboardHook }
function TKeyboardHook.CallBack(code: Integer; wparam: WPARAM; lparam: LPARAM):
LRESULT;
begin
if Code < 0 then
Result := CallNextHookEx(FHook, Code, wparam, lparam)
else
begin
if Assigned(FOnCallback) then
FOnCallback(Code, wParam, lParam);
Result := 0;
end;
end;
constructor TKeyboardHook.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FThunk.POPEDX := $5A;
FThunk.MOVEAX := $B8;
FThunk.SelfPtr := Self;
FThunk.PUSHEAX := $50;
FThunk.PUSHEDX := $52;
FThunk.JMP := $E9;
FThunk.JmpOffset := Integer(@TKeyboardHook.Callback) - Integer(@FThunk.JMP) - 5;
FHook := SetWindowsHookEx(WH_KEYBOARD, TFNHookProc(@FThunk), 0, MainThreadID);
end;
destructor TKeyboardHook.Destroy;
begin
UnhookWindowsHookEx(FHook);
inherited;
end;
procedure TKeyboardHook.SetOnCallback(const Value: TKeyboardCallBack);
begin
FOnCallback := Value;
end;
end.
Feliratkozás:
Bejegyzések (Atom)