2007. április 30., hétfő
How to copy records to the same table
Problem/Question/Abstract:
I need to copy a record in a dBase table to the same table and just change a value or two. I know that I can copy the hard way read all the fields into a record then write it back out.
Answer:
Solve 1:
var
SourceQueryFieldName: string;
begin
QueryDestination.Open;
QuerySource.Open;
QueryDestination.Insert;
for FieldLoop := 0 to QuerySource.FieldCount - 1 do
begin
SourceQueryFieldName := DataBaseQuerySource.Fields[FieldLoop].FieldName;
try
QueryDestination[SourceQueryFieldName] := QuerySource[SourceQueryFieldName];
except
{Field not Found}
end;
end;
QueryDestination.Post;
QueryDestination.Close;
QuerySource.Close;
end;
Solve 2:
I actually prefer code that reads each field and writes it to the new record like this:
procedure CopyRecord(tbl: TTable);
var
I: Integer;
tblTmp: TTable;
begin
blTmp := TTable.Create(nil);
try
tblTmp.DatabaseName := tbl.DatabaseName;
tblTmp.TableName := tbl.TableName;
tblTmp.Open;
ttblTmp.GotoCursor(Src);
tbl.Insert;
try
for I := 0 to T.FieldCount - 1 do
tbl.Fields[I].Assign(tblTmp.Fields[I]);
except
tbl.Cancel;
raise;
end;
finally
tblTmp.Free;
end;
end;
But you can also do it like this:
procedure CopyRecord(const FromTable: TTable);
begin
dbiInsertRecord(FromTable.Handle, dbiNoLock, FromTable.ActiveBuffer);
end;
2007. április 29., vasárnap
Kill a task
Problem/Question/Abstract:
Kill a task using only the .exe name
Answer:
This little function closes all applications with the same .exe-name.
Example:
KillTask('notepad.exe');
KillTask('iexplore.exe');
Working on Win9x/2k, but apparently not on WinNT systems (never tried on my own)
uses
Tlhelp32, Windows, SysUtils;
function KillTask(ExeFileName: string): integer;
const
PROCESS_TERMINATE = $0001;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
result := 0;
FSnapshotHandle := CreateToolhelp32Snapshot
(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle,
FProcessEntry32);
while integer(ContinueLoop) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeFileName))
or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeFileName))) then
Result := Integer(TerminateProcess(OpenProcess(
PROCESS_TERMINATE, BOOL(0),
FProcessEntry32.th32ProcessID), 0));
ContinueLoop := Process32Next(FSnapshotHandle,
FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
2007. április 28., szombat
Application.Terminate or Halt() ?
Problem/Question/Abstract:
Should I use Application.Terminate or Halt() ? And what are the differences?
Answer:
Application.Terminate closes the main window and that way the application in a clean fashion. Halt() shuts down right away, e.g. memory may not be freed, tables are not closed and so on.
For D4: Halt can cause AV's on an NT system, usually with Runtime error 216, if DLL's are involved also 217 - Application.Terminate on the other hand cleans without AV.
But: Halt worked with D2, even the cleaning of the memory did work....
Then again, if you write a console application, you have to use halt(), since there probably is no Application object.
2007. április 27., péntek
Preventing the Debugger from stepping into VCL source
Problem/Question/Abstract:
Preventing the Debugger from stepping into VCL source
Answer:
Does your debugger step into the VCL source code and you want to disable this?
Or are you in the opposite situation, you need to step into through the VCL source code?
Here are some pointers (for Delphi 5) what you should look at.
The following steps will stop the debugger from stepping into the VCL:
Go to menu 'Project | Options' and there click on tab 'Compiler'. Then uncheck the 'Use Debug DCUs' option under 'Debugging'. This is a project-specific setting.
Also check to be sure your Library path just points to LIB, not LIB\DEBUG. This is an environment option and will affect all projects.
Remove the VCL source directories from the Search Path. This is also a project specific setting. Choose under 'Project | Options' the tab 'Directories/Conditionals'. Delphi 5 allows to remove directories comfortably from the search path. The VCL source directories will look like
$(DELPHI)\Source\Vcl
There are probably be other directories below $(DELPHI)\Source, which you may want to remove as well. E.g. \RTL\
Happy Debugging!
2007. április 26., csütörtök
Disable the main form while a dialog box is shown
Problem/Question/Abstract:
I'm trying to set up a "Please Wait" box. I want it to be modal in the sense that my main form is deactivated while I have this box showing. But, in the function that displays the "Please Wait" box, I want the code to continue rather than stall, waiting for the box to close.
Answer:
{ ... }
WaitBox.Show; {shows your WaitBox}
Enabled := false; {disables the whole main form (Self.Enabled)}
Application.ProcessMessages; {let the two forms update themselves}
try
{ ... Do next steps }
finally
Enabled := true;
WaitBox.Hide;
end;
Note that the WaitBox must be visible before you can disable the main form. You needn't disable each single component. With this construction you can easily add a Cancel-Button to your WaitBox. Set a public property (CancelPressed) to TRUE if the Cancel Button is pressed and you can do something like this:
{ ... }
repeat
{ next steps }
Application.ProcessMessages;
until
WaitBox.CancelPressed
{ ... }
2007. április 25., szerda
Close all the open IE windows
Problem/Question/Abstract:
How can I close all the open internet explorer windows currently open?
Answer:
Use this to close all open IE windows.
var
IExplorer: Thandle;
begin
IExplorer := FindWindow('' IEFrame '', nil);
if IExplorer <> 0 then
SendMessage(IExplorer, WM_SYSCOMMAND, SC_CLOSE, 0);
note instead of SC_CLOSE
SC_MINIMIZE can be used to minimize all ie windows
SC_MAXIMIZE can be used to maximize all ie windows
This could be used on a button click or in a timer for example.
2007. április 24., kedd
How to tab between fields displayed in a TDBGrid
Problem/Question/Abstract:
Inside a DBGrid I would like to tab to the next field on each line (record) in the DBGrid, but when I tab the focus jumps to the last record. How do I get this to work right? Visible fields: LineNo, Code, Qty, Description, Price, Taxable, Extended (price). I want to tab to each or at least from Code to Qty to Price.
Answer:
procedure TYourForm.FormKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then { if it's an enter key }
if not (ActiveControl is TDBGrid) then { if not on a TDBGrid }
begin
Key := #0; { eat enter key }
Perform(WM_NEXTDLGCTL, 0, 0); { move to next control }
end
else if (ActiveControl is TDBGrid) then { if it is a TDBGrid }
with TDBGrid(ActiveControl) do
if selectedindex < (fieldcount - 1) then { increment the field }
selectedindex := selectedindex + 1
else
selectedindex := 0;
end;
2007. április 23., hétfő
Using custom cursors
Problem/Question/Abstract:
How can I use custom cursors in my application?
Answer:
To use custom cursors in your application you have to follow these steps:
1. Create the cursors and save them in a resource file. You can use the Image Editor that comes with Delphi for this purpose.
2. In the interface section of any unit of your project declare the constants to refer to your cursors in code. This is not required, but it will improve the readability of your code, so it is higly recommended.
These constans must be possitive integers (0 and negative values are reserved for the standard cursors). For example:
const
crFinger = 1;
crPower = 2;
3. In the initialization section of this unit, or anywhere in your project before you attempt to use your cursors, you have to load the cursors from the resource file. For example:
{$R Cursors.res}
Screen.Cursors[crFinger] := LoadCursor(hInstance, 'FINGER');
Screen.Cursors[crPower] := LoadCursor(hInstance, 'POWER');
Here we assumed "Cursors.res" is the resource file where you saved your cursors, and that FINGER and POWER are the names you saved them under.
This is it. You can use these cursors in the same you would use the predefined cursors. For example:
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Cursor := crPower;
Label1.Cursor := crFinger;
end;
You can also set the Cursor and DragCursor properties of a component at design-time using the Object Inspector. The only drawback is that you can't use the constants names (for example crFinger and crPower) but their values (for example 1 and 2).
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2007. április 22., vasárnap
Compare two records in the same table
Problem/Question/Abstract:
Is there any way to compare two records from the same table to know which fields are different on each record? Both my field are the same except that some field may differ, and I want to know which one is different.
Answer:
I would create a second TTable and then compare using the Fields array. Give the following function a TTable and a field position to start the compare. If it returns false bgnFld will reflect the first field where values are not the same. (This code has not been tested):
function compairFields( t: TTable; var bgnFld: Integer ): Boolean;
var
t2: TTable;
cntr: Integer;
begin
try
t2 := ttable.create(nil)
with T do
begin
t2.gotoCurrent(t); {synchronize tables}
for cntr := bgnfld to FieldCount - 1 do
begin
result := (fieldscntr] = T2.fields[cntr]);
if not result then
Break;
end;
end;
finally
t2.free;
end;
end;
or use two TTables one pointing to each of the two records. Then:
for I := 0 to Table1.FieldCount - 1 do
if Table1.Fields[I].Value <> Table2.Fields[I].Value then
...
2007. április 21., szombat
How to assign a new path to a TTable at runtime
Problem/Question/Abstract:
How to assign a new path to a TTable at runtime
Answer:
Use a TDataBase with a custom, application-specific alias. Set the Alias property to an empty string, select a DriverName, and insert the string 'PATH=C:\MYPATH' into the Params property.
Now any TTable etc. of your project can see an alias of the name you choose for the DataBaseName property of TDataBase. At runtime you can assign a new path at a single place. You have to re-open the tables, however. Like this:
procedure AssignDBDir(ADataBase: TDataBase; const ADir: string);
begin
with ADataBase do
if (Params.Count = 0) or (Params[0] <> 'PATH=' + ExtractFilePath(AFileName)) then
begin
if Connected then
Close; {closes all tables as well}
DriverName := 'STANDARD'; {clears any alias as well}
Params.Clear;
Params.Add('PATH=' + ADir);
Open; {reopen tables here}
end;
end;
2007. április 20., péntek
Differentiate between a Windows shutdown and a user's close request
Problem/Question/Abstract:
How can I differentiate between a Windows shutdown and a user's close request (Alt + F4 / titlebar close icon / file menu + close item / etc.) so that I can bypass the OnCloseQuery logic during a shutdown?
Answer:
Windows sends a WM_QUERYENDSESSION message to the main window of your application. The default processing for that invokes your CloseQuery method, which (in your logged out case) replies "No". So you need to watch for the WM_QUERYENDSESSION message and set a flag for your CloseQuery method. Give the form a flag and method like so:
FShuttingDown: Boolean;
procedure WMQueryEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;
procedure TForm1.WMQueryEndSession(var Msg: TMessage);
begin
{Tell CloseQuery it's a shutdown operation}
FShuttingDown := True;
{Let the default stuff happen to see if we can otherwise close}
inherited;
end;
Then in your CloseQuery event handler do:
if FShuttingDown then
CanClose := True
else
CanCLose := {User if "logged in"};
It is possible for the shutdown to be aborted by another application, however. So you need to watch for the WM_ENDSESSION message that gets sent telling you if you really are going to shut down:
procedure WMEndSession(var Msg: TMessage); message WM_ENDSESSION;
procedure TForm1.WMEndSession(var Msg: TMessage);
begin
{Clear the flag if the shutdown was aborted}
FShuttingDown := Msg.WParam <> 0;
end;
2007. április 19., csütörtök
Parsing the Words in a Sentencee
Problem/Question/Abstract:
How can I parse the words in a sentence?
Answer:
This week's tip is some code that actually accomplishes something very simple: parsing the words of a sentence. I've been hanging out in the newsgroups and in CompuServe forum and ran across several question regarding what's the best way to do this, so I came up with a simple procedure to do it. I've seen a lot of people use arrays and such, but the problem with using arrays is that they're of fixed size (though in a previous tip, I showed how to make runtime resizeable arrays). A better way to store the words of a string is to use a TStringList object.
A TStringList is essentially an array of strings (or objects) that can be resized dynamically at runtime. Since memory is allocated and deallocated in the background, you don't have to worry about those operations when using one. All you have to worry about is adding or deleting elements. Each item in a TStringList is referenced by its Strings property, much in the way you reference an array element. Let's say you want to know what the value of the fifth element in a string list. You'd write something like the following:
x := MyStringList.Strings[4];
I forgot to mention that TStringLists are zero-based, so the first element in the TStringList is always numbered '0.' So how can you use it to parse a sentence? Well, let's look at the code below:
function FillList(sentnc: string; {Input string}
var sList: TStringList; {String List to add values to}
clearList: Boolean) {Clear list before adding?}
: Boolean; {Return value}
var
str, wrd: string;
I: Word;
begin
{Initialize vars}
Result := True;
str := sentnc;
wrd := '';
{Check to see if the string passed is blank}
if (Length(sentnc) = 0) then
begin
MessageDlg('Passed an empty string', mtError, [mbOk], 0);
Result := False;
Exit;
end;
{Clear the list if wanted and the count of values is > 0}
if clearList and (sList.Count > 0) then
repeat
sList.Delete(0);
until
sList.Count = 0;
while (Pos(' ', str) > 0) do {Do this while you find}
begin {spaces in the sentence}
wrd := Copy(str, 1, Pos(' ', str) - 1); {Get the word from the string}
sList.Add(wrd); {Add the word to the TStringList}
str := Copy(str, Pos(' ', str) + 1, {Redefine the sentence by cutting}
Length(str) - Length(wrd) + 1); {off the first word}
end;
if (Length(str) > 0) then {This is important, because you never}
sList.Add(str); {know if there's anything left in the sentence.}
end;
The function above takes a string input called sentnc and uses the Pos and Copy functions to successively cut off the first word of the phrase and load it into a string list. You'll notice that I've added a couple of tests: 1) to test whether the input is blank; 2) to see if the program should empty the list before adding items to the list. You'll also notice that I have the TStringList object passed by reference as a formal parameter of the function. This is so that any string list can be passed into the function to accept a phrase. However, besides the extra checking stuff, the real workhorse of the function is the while loop. Follow the commenting to the right of the code to see what's going on.
To employ this function, you'd have to create a TStringList object then call the function. Look at the code below:
procedure TForm1.FormCreate(Sender: TObject);
begin
strList := TStringList.Create;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
{Fill the list}
if FillList(Edit1.Text, strList, True) then
begin
repeat
ListBox1.Items.Delete(0);
until
ListBox1.Items.Count = 0;
for I := 0 to strList.Count - 1 do
ListBox1.Items.Add(strList.Strings[I]);
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
strList.Free;
end;
The code above was taken from a form I built to test the FillList function. In the FormCreate, I create and initialize the TStringList object. In a pushbutton click event, I read the contents of a TEdit then call the function. The resultant load is then read into a list box that I dropped on the form. FormClose destroys the TStringList. Granted, this is a rather simple way of employing the string list, but there are numerous ways in which to use this neat little object.
2007. április 18., szerda
Find whole words within a string
Problem/Question/Abstract:
Does anyone know of a function that finds a whole word within a string as in the search and replace options?
Answer:
{Function FindWord
Parameters:
pattern: word to search for
text: text to search
caseSensitive: determines whether search is case sensitive or not. Default is not case-sensitive.
startAt: first character to search, default is 1.
Returns:
The start of the first instance of the word, or 0, if the word was not found or only as part of larger words. A word in this context is any sequence of alphanumeric characters delimited by non-alphanumeric characters.
Error Conditions: none
Created: 18.05.99 by P. Below}
function FindWord(pattern, text: string; caseSensitive: Boolean = false; startAt:
Integer = 1): Integer;
var
offset, endOfPattern: Integer;
begin
Result := 0;
if Length(text) = 0 then
exit;
if Length(pattern) = 0 then
begin
{By definition a pattern of length 0 is always found}
result := 1;
Exit;
end;
if not caseSensitive then
begin
pattern := AnsiLowerCase(pattern);
text := AnsiLowerCase(text);
end;
endOfPattern := startAt + Length(pattern);
for offset := startAt to Length(text) - Length(pattern) + 1 do
begin
if pattern[1] = text[offset] then
begin
if ((offset = 1) or not IsCharAlphaNumeric(text[offset - 1])) and ((endOfPattern
> Length(text)) or not IsCharAlphaNumeric(text[endOfPattern]))
and (StrLComp(@text[offset], @pattern[1], Length(pattern)) = 0) then
begin
Result := offset;
exit;
end;
end;
Inc(endOfPattern);
end;
end;
2007. április 17., kedd
How to get the scan code of keyboards
Problem/Question/Abstract:
How to get the scan code of keyboards
Answer:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;
type
TKeyInfo = packed record
KeyDown: Boolean;
VirtualKeyCode: WORD;
RepeatCount: Word;
VirtualScanCode: Byte;
ExtendedKey: Boolean;
ContextCode: Boolean;
PreviousState: Boolean;
AsciiChar: Char;
ControlKeyState: DWORD;
end;
TForm1 = class(TForm)
private
procedure WMKeyDown(var Message: TMessage); message WM_KEYDOWN;
function GetKeyInfo(var Message: TMessage): TKeyInfo;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.WMKeyDown(var Message: TMessage);
var
KeyInfo: TKeyInfo;
begin
KeyInfo := GetKeyInfo(Message);
KeyInfo.KeyDown := True;
ShowMessage('ScanCode: ' + IntToStr(KeyInfo.VirtualScanCode));
end;
function TForm1.GetKeyInfo(var Message: TMessage): TKeyInfo;
function IsWinNT: Boolean;
begin
Result := (GetVersion < $80000000);
end;
const
AltMask = $20000000;
var
KeyBoardState: TKeyBoardState;
LowerKeyData: WORD;
UpperKeyData: WORD;
begin
ZeroMemory(@Result, SizeOf(TKeyInfo));
GetKeyBoardState(KeyBoardState);
LowerKeyData := LOWORD(Message.LParam);
UpperKeyData := HIWORD(Message.LParam);
Result.VirtualKeyCode := WORD(Message.WParam);
Result.RepeatCount := LowerKeyData;
Result.VirtualScanCode := UpperKeyData and $FF;
Result.ExtendedKey := Boolean((UpperKeyData and KF_EXTENDED) shr 8);
Result.ContextCode := Boolean((UpperKeyData and KF_ALTDOWN) shr 13);
Result.PreviousState := Boolean((UpperKeyData and KF_REPEAT) shr 14);
Result.KeyDown := not Boolean((UpperKeyData and KF_UP) shr 15);
ToAscii(Result.VirtualKeyCode, Result.VirtualScanCode, KeyBoardState,
@Result.AsciiChar, 0);
Result.ControlKeyState := (((KeyBoardState[VK_LCONTROL] and 128) shr 7) *
LEFT_CTRL_PRESSED) or (((KeyBoardState[VK_RCONTROL] and 128)
shr 7) * RIGHT_CTRL_PRESSED) or (((KeyBoardState[VK_LMENU] and
128) shr 7) * LEFT_ALT_PRESSED) or (((KeyBoardState[VK_RMENU]
and 128) shr 7) * RIGHT_ALT_PRESSED) or ((KeyBoardState
[VK_CAPITAL] and 1) * CAPSLOCK_ON) or ((KeyBoardState
[VK_NUMLOCK] and 1) * NUMLOCK_ON) or ((KeyBoardState
[VK_SCROLL] and 1) * SCROLLLOCK_ON) or ((((KeyBoardState
[VK_LSHIFT] or KeyBoardState[VK_RSHIFT]) and 128) shr 7) *
SHIFT_PRESSED) or (Integer(Result.ExtendedKey) * ENHANCED_KEY);
if (not IsWinNT) then
begin
if (((Result.ControlKeyState and LEFT_CTRL_PRESSED) or (Result.ControlKeyState and
RIGHT_CTRL_PRESSED)) = 0) and ((KeyBoardState[VK_CONTROL] and 128) <> 0) then
Result.ControlKeyState := Result.ControlKeyState or RIGHT_CTRL_PRESSED;
if (((Result.ControlKeyState and LEFT_ALT_PRESSED) or (Result.ControlKeyState and
RIGHT_ALT_PRESSED)) = 0) and ((KeyBoardState[VK_MENU] and 128) <> 0) then
Result.ControlKeyState := Result.ControlKeyState or RIGHT_ALT_PRESSED;
end;
end;
end.
2007. április 16., hétfő
Interesting API calls part I : detecting simultaneous keystrokes
Problem/Question/Abstract:
How can I detect if more than one key is pressed at the same time?
Answer:
You can use Windows API to detect multiple keystrokes. The name of the function that give us this facility is GetKeyState. The higher order bit show us the state of the key we pass as parameter to the function. On this sample I detect the states of the arrow keys, spacebar, shift keys and ESC. This sample can detect up to four keystrokes at the same time, but have some limitations, due to hardware limitations (i think). This will detect UP + RIGHT + SPACE but won�t detect UP + LEFT + SPACE. I may be wrong, but this happens because of keyboards pins design. That�s why I included the SHIFT state, because SHIFT keys will be detected with any combination of arrow keys. To run this sample place on a blank form a Label and a Button. Click the button to start, press ESC to stop.
{****************************************************************
* Multiple keystrokes detection using Windows API *
* Source written by Rafael Cotta (rcotta.geo@yahoo.com) *
* July 26th, 2001 *
****************************************************************}
// To run this sample, create a blank form, and place a label
// and a timer on it.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
function GetKeysPressed(): Cardinal;
var
Form1: TForm1;
implementation
{$R *.DFM}
{
Each bit on the function result represents
the state of a key, so
0000 0001 = UP
0000 0010 = DOWN
0000 0100 = LEFT
0000 1000 = RIGHT
0001 0000 = SPACE BAR
0010 0000 = ESC
0100 0000 = SHIFT
}
function GetKeysPressed(): Cardinal;
var
dwRet: Cardinal;
begin
dwRet := 0;
if ((GetKeyState(VK_UP) and $10000000) > 0) then
dwRet := dwRet + 1;
if ((GetKeyState(VK_DOWN) and $10000000) > 0) then
dwRet := dwRet + 2;
if ((GetKeyState(VK_LEFT) and $10000000) > 0) then
dwRet := dwRet + 4;
if ((GetKeyState(VK_RIGHT) and $10000000) > 0) then
dwRet := dwRet + 8;
if ((GetKeyState(32) and $10000000) > 0) then
dwRet := dwRet + 16; // SpaceBar
if ((GetKeyState(27) and $10000000) > 0) then
dwRet := dwRet + 32; // ESC
if ((GetKeyState(VK_SHIFT) and $10000000) > 0) then
dwRet := dwRet + 64; // ESC
GetKeysPressed := dwRet;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
dwKeys: Cardinal;
begin
dwKeys := 0;
// While ESC is not pressed
while ((dwKeys and 32) = 0) do
begin
Application.ProcessMessages;
dwKeys := GetKeysPressed;
Label1.Caption := 'Keys pressed : ';
if ((dwKeys and 1) > 0) then
Label1.Caption := Label1.Caption + ' UP';
if ((dwKeys and 2) > 0) then
Label1.Caption := Label1.Caption + ' DOWN';
if ((dwKeys and 4) > 0) then
Label1.Caption := Label1.Caption + ' LEFT';
if ((dwKeys and 8) > 0) then
Label1.Caption := Label1.Caption + ' RIGHT';
if ((dwKeys and 16) > 0) then
Label1.Caption := Label1.Caption + ' SPACE';
if ((dwKeys and 32) > 0) then
Label1.Caption := Label1.Caption + ' ESC';
if ((dwKeys and 64) > 0) then
Label1.Caption := Label1.Caption + ' SHIFT';
end;
end;
end.
2007. április 15., vasárnap
How to change the ReadyMessage of HP-LaserJet printers
Problem/Question/Abstract:
How to change the ReadyMessage of HP-LaserJet printers with a LCD display?
Answer:
This works only on HP-LaserJet printers, which have a two line 16 character LCD display. F.i. on a HP LaserJet 4000, HP LaserJet 5000 (N). In this LCD-display you normally find messages like: READY, or PAPER OUT IN BIN 3 or something like that. With this small routine you can alter the ready message into your own. It will stay there until you switch the printer off (or change the ready message with this program). The message should be no longer than two lines of 16 characters each. Remember that it will be truncated after 16 characters, the rest of the line will be on the next line of 16 characters on the LCD display.
//----------------------------------------------------------------------
// This routine is published by me before...
procedure PrintRawStr(const S: ANSIString);
Uses
Printers, WinSpool, Dialogs;
var
Handle: THandle;
dwN: DWORD;
diDocInfo1: TDocInfo1;
bP: BYTE;
sDefaultPrinter: string;
begin
sDefaultPrinter := '';
if Printer.Printers.Count > 0 then
begin
sDefaultPrinter := Printer.Printers[Printer.PrinterIndex];
//uses Printers, get default printer
bP := Pos(' on ', sDefaultPrinter);
if bP > 0 then
sDefaultPrinter := Copy(sDefaultPrinter, 1, bP - 1);
end;
if Length(S) = 0 then
Exit;
if not OpenPrinter(PChar(sDefaultPrinter), Handle, nil) then
begin
case GetLastError of
87: ShowMessage('Printer name does not exists.');
else
ShowMessage('Error ' + IntToStr(GetLastError)); // Uses Dialogs
end;
Exit;
end;
with diDocInfo1 do
begin
pDocName := PChar('My Print Job'); // Visible in the spooler window
pOutputFile := nil;
pDataType := 'RAW';
end;
StartDocPrinter(Handle, 1, @diDocInfo1);
StartPagePrinter(Handle);
WritePrinter(Handle, PChar(S), Length(S), dwN);
EndPagePrinter(Handle);
EndDocPrinter(Handle);
ClosePrinter(Handle);
end;
//----------------------------------------------------------------------
procedure ChangeLaserReadyMessage(S: string);
const
InitStr: string = #27 + '%-12345X@PJL RDYMSG DISPLAY="';
ExitStr: string = '"' + #13 + #10 + #27 + '%-12345X' + #13 + #10;
begin
PrintRawStr(InitStr + S + ExitStr);
end;
//----------------------------------------------------------------------
2007. április 14., szombat
How to call the help file for the application's help file
Problem/Question/Abstract:
How to call the help file for the application's help file
Answer:
Application.HelpCommand(HELP_HELPONHELP, 0);
2007. április 13., péntek
How to change the default highlight color of a TListBox
Problem/Question/Abstract:
Can anyone tell me how to change the default highlight color used in a TListBox? I need it to be clAqua instead of the standard Navy as the text in the listbox is made other colors in the OwnerDraw and you can't read some of them with the Navy selection color.
Answer:
Solve 1:
Check the 'State' parameter in the DrawItem event. It lets you know if the item is selected. If it is then use a different brush color.
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
if odSelected in State then
ListBox1.Canvas.Brush.Color := clAqua;
ListBox1.Canvas.FillRect(Rect);
ListBox1.Canvas.TextOut(Rect.Left, Rect.Top, ListBox1.Items[Index]);
end;
Solve 2:
Set Style := lbOwnerDrawFixed and OnDrawItem := ListBoxDrawItem; . Remove the last line from the example if you want to have the focus rectangle.
procedure TListBox.ListBoxDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
State: TOwnerDrawState);
begin
if (odSelected in State) then
Canvas.Brush.Color := clBlue
else
Canvas.Brush.Color := Color;
Canvas.FillRect(Rect);
Canvas.Font := Font;
SetTextAlign(Canvas.Handle, TA_LEFT or TA_TOP or TA_NOUPDATECP);
ExtTextOut(Canvas.Handle, Rect.Left + 2, Rect.Top + 1, ETO_CLIPPED or ETO_OPAQUE, @Rect,PChar(Items[Index]), Length(Items[Index]), nil);
if (odSelected in State) then
DrawFocusRect(Canvas.Handle, Rect);
end;
2007. április 12., csütörtök
Retrieve if a given folder is empty
Problem/Question/Abstract:
Ever needed to check if a folder is empty or not? That's my way of doing. I think it's really fast, but not bench marked yet.
Answer:
uses
FileCtrl, SysUtils;
function IsEmptyFolder(fld: string): boolean;
var
sr: tsearchrec;
r: integer;
begin
fld := IncludeTrailingBackSlash(fld);
result := false;
if (DirectoryExists(fld)) then
begin
result := true;
r := findfirst((fld + '*.*'), faAnyFile, sr);
while ((r = 0) and (result)) do
begin
// Revision 2:
// checks for system folders "." and ".." that always exists
// inside an empty folder.
if ((SR.Attr and faDirectory) <> 0) then
begin
if ((sr.name <> '.') and (sr.name <> '..')) then
result := false;
end
else
result := false;
r := findnext(sr);
end;
// Revision 1:
// this prevents compiler by using the API defined in windows unit,
// that will raise a compiler error like this:
// [Error]:Incompatible types: 'Cardinal' and 'TSearchRec'
sysutils.findclose(sr);
end;
end;
2007. április 11., szerda
Code completion reports a non-existing error
Problem/Question/Abstract:
So I have a component which simply refuses to use code completion, always reporting that there are errors in the source code, however the component compiles absolutely fine. What is the reason?
Answer:
Do you have any conditional defines? If you do, code completion wont work.
(At least not on Delphi 4 Update Pack 3.)
2007. április 10., kedd
How to get the font of the active title bar
Problem/Question/Abstract:
How do you get hold of the font for the active title bar (after it's been set in the Appearence tab in the display properties)? What API call is needed?
Answer:
function GetCaptionFont(afont: TFont);
var
ncMetrics: TNonClientMetrics;
begin
assert(assigned(afont));
ncMetrics.cbSize := sizeof(TNonClientMetrics);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(TNonClientMetrics), @ncMetrics,
0);
afont.Handle := CreateFontIndirect(ncMetrics.lfCaptionFont);
end;
The TNonClientMetrics structure also contains information on other fonts used in the non-client area information:
lfCaptionFont: Font used in regular captions
lfSmCaptionFont: Font used in small captions
lfMenuFont: Font used in menus
lfStatusFont: Font used in status bars
lfMessageFont: Font used in message boxes
2007. április 9., hétfő
How to make the [Enter] key behave like the [Tab] key
Problem/Question/Abstract:
How to make the [Enter] key behave like the [Tab] key
Answer:
Solve 1:
You need to trap the keystroke and set up your own response to it. Try this: (Note: This will not work within a DBGrid, since the next field is not a separate object.)
procedure TMainForm.FormCreate(Sender: TObject);
begin
keyPreview := true; {To turn the event "ON"}
end;
procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
Key := #0;
PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
end;
end;
Solve 2:
Use this code for example for an TEdit's OnKeyPress event:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
SelectNext(Sender as TWinControl, True, True);
Key := #0;
end;
end;
This causes Enter to behave like tab. Now, select all controls on the form you'd like to exhibit this behavior (not Buttons) and go to the Object Inspector and set their OnKeyPress handler to EditKeyPress. Now, each control you selected will process Enter as Tab. If you'd like to handle this at the form (as opposed to control) level, reset all the controls OnKeyPress properties to blank, and set the form's OnKeyPress property to EditKeyPress. Then, change Sender to ActiveControl and set the form's KeyPreview property to true:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
SelectNext(ActiveControl as tWinControl, True, True);
Key := #0;
end;
end;
This will cause each control on the form (that can) to process Enter as Tab.
Solve 3:
Handle this in the OnKeyPress event. The form's KeyPreview property must be set to true.
procedure TFrmEnterTab.FormKeyPress(Sender: TObject; var Key: Char);
begin
if Key = Chr(VK_RETURN) then
begin
if GetKeyState(VK_SHIFT) < 0 then
SelectNext(ActiveControl, false, true)
else
SelectNext(ActiveControl, true, true);
Key := #0;
end;
end;
2007. április 8., vasárnap
How to create a TCollection
Problem/Question/Abstract:
How do I create a Collection myself? I need a Collection of Items with ArticelNr, Name, Price, etc.. Each item should be in the collection of ResellItems.
Answer:
First, you need to create your ResellItem class based upon TCollectionItem:
TResellItem = class(TCollectionItem)
private
fArticleNr: integer;
fName: string;
fPrice: double;
public
property ArticleNr: integer read fArticleNr write fArticleNr;
property Name: string read fName write fName;
property Price: double read fPrice write fPrice;
end;
The next step is optional although I generally find it useful (self-documents for a start): create a descendent of TCollection:
TResellCollection = class(TCollection)
end;
Now you need to declare an instance somewhere:
var
ResellCollection: TResellCollection;
The constructor for TCollection takes a TCollectionItem class parameter:
ResellCollection: TResellCollection.Create(TResellItem);
Having created our collection (don't forget to free it afterwards) then adding items becomes:
var
item: TResellItem;
item := ResellCollection.Add as TResellItem;
item.ArticleNr := aNumber;
item.Name := aName;
item.Price := aPrice;
I generally wrap the above code into a TResellCollection method, say AddResellItem, thus allowing:
ResellCollection.AddResellItem(aNumber, aName, aPrice);
Accessing items afterwards is done like this:
item := ResellCollection.Items[index] as TResellItem;
The syntax above can be simplified by adding a new default array property to TResellCollection, thus encapsulating the messy casting:
property ResellItem[index: integer]: TResellItem read GetResellItem; default;
The GetResellItem method required looks like this:
function TResellCollection.GetResellItem(index: integer): TResellItem;
begin
Result := Items[index] as TResellItem;
end;
Now you can say things like:
item := ResellCollection[0];
ResellCollection[1].Name := newname;
Just in case you have got a bit lost with the changes, the interface and implementation of TResellCollection now look like this:
TResellCollection = class(TCollection)
private
function GetResellItem(index: integer): TResellItem;
public
procedure AddResellItem(aNumber: integer; const aName: string; aPrice: Double);
property ResellItem[index: integer]: TResellItem read GetResellItem;
default;
end;
procedure TResellCollection.AddResellItem(aNumber: integer; const aName: string;
aPrice: Double);
var
item: TResellItem;
begin
item := Add as TResellItem;
item.ArticleNr := aNumber;
item.Name := aName;
item.Price := aPrice;
end;
function TResellCollection.GetResellItem(index: integer): TResellItem;
begin
Result := Items[index] as TResellItem;
end;
2007. április 7., szombat
How to put forms into a DLL
Problem/Question/Abstract:
How to put forms into a DLL
Answer:
Create a new Project. In between the word "type" and the "TForm1 = class..." put:
TMyProc = procedure(App: TApplication; Scr: TScreen); stdcall;
In the "private" area of "TForm1" add:
MyDLLHandle: THandle;
ShowMyModuleForm: TMyProc;
Add a "Form1.OnCreate" event and add this code to it:
MyDLLHandle := LoadLibrary('Project2.DLL');
if MyDLLHandle <> 0 then
@ShowMyModuleForm := GetProcAddress(MyDLLHandle, 'ShowMyForm')
else
ShowMyModuleForm := nil;
Add a "Form1.OnDestroy" event and add this code to it:
if Assigned(ShowMyModuleForm) then
ShowMyModuleForm := nil;
if MyDLLHandle <> 0 then
FreeLibrary(MyDLLHandle);
MyDLLHandle := 0;
Now drop a "TButton" onto the form and add an "OnClick" event with this code:
if (MyDLLHandle <> 0) and (Assigned(ShowMyModuleForm)) then
ShowMyModuleForm(Application, Screen);
That's all for the EXE side. Now in the "Project Manager" right click the "ProjectGroup1" (Top Node) and select "Add new project" from the popup menu. The add "New Items" dialog comes up and under the "New" tab double click "DLL". In the Project2.DLL source under the "uses" clause insert:
{$R *.res}
procedure ShowMyForm(App: TApplication; Scr: TScreen); stdcall;
var
a: TForm2;
begin
Application := App;
Screen := Scr;
a := TForm2.Create(Application.MainForm);
{"Application.MainForm" could also be "nil" or any valid value}
a.ShowModal;
a.Free;
end;
exports
ShowMyForm;
end.
Add to the "uses" clause "Forms". Now in the "Project Manager" right click on the "Project2.dll" and add a form and the class name should be named "TForm2". In the "Implementation" area of the form put:
var
OldApp: TApplication;
OldScr: TScreen;
initialization
OldApp := Application;
OldScr := Screen;
finalization
Screen := OldScr;
Application := OldApp;
end.
Put a "TButton" on the new form and set the "ModalResult" to "mrOk". Compile the "dll" and the "exe". There's a sample form in DLL.
If you already have a form made that you want in the DLL just use it instead of "TForm2". Make sure the "initialization" and "finalization" code is in one, and only one, of the forms you put into the DLL. Without that code you may get unexpected results.
2007. április 6., péntek
Accessing the Windows Registry
Problem/Question/Abstract:
How can I simply save and get data from the Windows Registry? The purpose of this article is to introduce GetRegistryData and SetRegistryData as an alternative to TRegistry, making it easy to read and write values from and to the Windows Registry, allowing developers to access the registry in a practical way.
Answer:
What is the Registry?
It is where Windows stores many of its configuration options and also allows applications to access this data as well as save their own data. If you want to take a look at the registry, just execute the REGEDIT.EXE application located in the Windows directory. Be careful not to change anything or you could end up ruining your installation! Now, the data in the registry is stored in a tree structure. There are many roots (many trees):
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_PERFORMANCE_DATA
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA
Each root can have values and keys. The values are data stored under item names (right panel of RegEdit). Keys can have values and other keys, forming a tree structure (left panel of RegEdit).
For example, the tree HKEY_CURRENT_USER has many keys, like AppEvents, Control Panel, Identities, Network, Software, etc. Each key may have sub-keys. For example, Control Panel has some sub-keys, like Accessibility, Appearance, Colors, Cursors, Desktop, International, etc. All keys have at least one value (the first value in the right panel of RegEdit), which is the default value (the name of the value is the empty string), not necessarily set. A key may have more values. For example, let's see the key Control Panel\Colors under HKEY_CURRENT_USER. Appart from the default value, it has values like ActiveBorder, ActiveTitle, AppWorkspace, Background, etc. In turn, each "value" has a "data" (the actual value, sort to speak). For example, the data of the value ActiveTitle would be "0 0 128" (may be different if you are not using the standard Windows colors).
TRegistry
Before getting into GetRegistryData and SetRegistryData, let's see how we would accomplish the same tasks the hard way, using TRegistry.
The TRegistry class is declared in the Registry unit, so you will have to add this unit to the Uses clause of the unit or program where you want to use it. To access a value in the registry, first you should create an object of this class, assign the root to its RootKey property (the values are defined in the Windows unit) and then try to open a key with the OpenKey function method, which will return True if successful. Then you can read (with the ReadXxxx functions) or write (with the WriteXxxx procedures) the values of the open key and, after that, you should close the key with CloseKey. When you are done with the registry, you should free the registry object you created. Let's see an example of how to obtain the name of the processor in our computer:
procedure TForm1.Button1Click(Sender: TObject);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKey('\Hardware\Description\System'
+ '\CentralProcessor\0', False) then
begin
ShowMessage(Reg.ReadString('Identifier'));
Reg.CloseKey;
end; // if
Reg.Free;
end;
You can see another example in the article Determining the associated application. Of course, there are many more things you can do with the registry, like creating and deleting keys and values...
The TRegistryIniFile class makes it simpler for applications to write and read their configuration information to and from the registry, while TRegistry operates at a lower level.
GetRegistryData
To simplify reading a data value from the registry you can use the following function that can read any data type from the registry and returns it as a variant (string or integer). The function performs exception handling.
uses Registry;
function GetRegistryData(RootKey: HKEY; Key,
Value: string): variant;
var
Reg: TRegistry;
RegDataType: TRegDataType;
DataSize, Len: integer;
s: string;
label
cantread;
begin
Reg := nil;
try
Reg := TRegistry.Create(KEY_QUERY_VALUE);
Reg.RootKey := RootKey;
if Reg.OpenKeyReadOnly(Key) then
begin
try
RegDataType := Reg.GetDataType(Value);
if (RegDataType = rdString) or
(RegDataType = rdExpandString) then
Result := Reg.ReadString(Value)
else if RegDataType = rdInteger then
Result := Reg.ReadInteger(Value)
else if RegDataType = rdBinary then
begin
DataSize := Reg.GetDataSize(Value);
if DataSize = -1 then
raise Exception.Create(SysErrorMessage(ERROR_CANTREAD));
SetLength(s, DataSize);
Len := Reg.ReadBinaryData(Value, PChar(s)^, DataSize);
if Len <> DataSize then
raise Exception.Create(SysErrorMessage(ERROR_CANTREAD));
Result := s;
end
else
raise Exception.Create(SysErrorMessage(ERROR_CANTREAD));
except
s := ''; // Deallocates memory if allocated
Reg.CloseKey;
raise;
end;
Reg.CloseKey;
end
else
raise Exception.Create(SysErrorMessage(GetLastError));
except
Reg.Free;
raise;
end;
Reg.Free;
end;
Sample Call
ShowMessage(GetRegistryData(HKEY_LOCAL_MACHINE,
'\Hardware\Description\System\CentralProcessor\0', 'Identifier'));
SetRegistryData
To simplify writing a data value to the registry you can use the following procedure that can write any data type to the registry. The procedure performs exception handling.
uses Registry;
procedure SetRegistryData(RootKey: HKEY; Key, Value: string;
RegDataType: TRegDataType; Data: variant);
var
Reg: TRegistry;
s: string;
begin
Reg := nil;
try
Reg := TRegistry.Create(KEY_WRITE);
Reg.RootKey := RootKey;
if Reg.OpenKey(Key, True) then
begin
try
if RegDataType = rdUnknown then
RegDataType := Reg.GetDataType(Value);
if RegDataType = rdString then
Reg.WriteString(Value, Data)
else if RegDataType = rdExpandString then
Reg.WriteExpandString(Value, Data)
else if RegDataType = rdInteger then
Reg.WriteInteger(Value, Data)
else if RegDataType = rdBinary then
begin
s := Data;
Reg.WriteBinaryData(Value, PChar(s)^, Length(s));
end
else
raise Exception.Create(SysErrorMessage(ERROR_CANTWRITE));
except
Reg.CloseKey;
raise;
end;
Reg.CloseKey;
end
else
raise Exception.Create(SysErrorMessage(GetLastError));
except
Reg.Free;
raise;
end;
Reg.Free;
end;
Sample Call
SetRegistryData(HKEY_LOCAL_MACHINE,
'\Software\Microsoft\Windows\CurrentVersion',
'RegisteredOrganization', rdString, 'Latium Software');
You can find another example of SetRegistryData in my article Making an application run automatically when Windows starts.
Component Download: http://www.latiumsoftware.com/download/delphi-2.zip
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2007. április 5., csütörtök
DataSet -> Strings -> DataSet
Problem/Question/Abstract:
DataSet -> Strings -> DataSet
Answer:
///////Begin Source
function StrLeft(const mStr: string; mDelimiter: string): string;
begin
Result := Copy(mStr, 1, Pos(mDelimiter, mStr) - 1);
end; { StrLeft }
function ListCount(mList: string; mDelimiter: string = ','): Integer;
var
I, L: Integer;
begin
Result := 0;
if mList = '' then
Exit;
L := Length(mList);
I := Pos(mDelimiter, mList);
while I > 0 do
begin
mList := Copy(mList, I + Length(mDelimiter), L);
I := Pos(mDelimiter, mList);
Inc(Result);
end;
Inc(Result);
end; { ListCount }
function ListValue(mList: string; mIndex: Integer; mDelimiter: string = ','): string;
var
I, L, K: Integer;
begin
L := Length(mList);
I := Pos(mDelimiter, mList);
K := 0;
Result := '';
while (I > 0) and (K <> mIndex) do
begin
mList := Copy(mList, I + Length(mDelimiter), L);
I := Pos(mDelimiter, mList);
Inc(K);
end;
if K = mIndex then
Result := StrLeft(mList + mDelimiter, mDelimiter);
end; { ListValue }
function DataSetToText(mDataSet: TDataSet; mStrings: TStrings;
mDelimiter: string = #9): Boolean;
var
vBookmark: string;
I: Integer;
S: string;
begin
Result := False;
if (not Assigned(mDataSet)) or (not mDataSet.Active) or
(not Assigned(mStrings)) then
Exit;
mStrings.Text :=
StringReplace(Trim(mDataSet.FieldList.Text), #13#10, mDelimiter, [rfReplaceAll]);
vBookmark := mDataSet.Bookmark;
mDataSet.DisableControls;
try
mDataSet.First;
while not mDataSet.Eof do
begin
S := '';
for I := 0 to mDataSet.FieldList.Count - 1 do
S := S + mDelimiter + mDataSet.FieldList[I].AsString;
Delete(S, 1, Length(mDelimiter));
mStrings.Add(S);
mDataSet.Next;
end;
finally
mDataSet.Bookmark := vBookmark;
mDataSet.EnableControls;
end;
Result := True;
end; { DataSetToText }
function TextToDataSet(mStrings: TStrings; mDataSet: TDataSet;
mDelimiter: string = #9): Boolean;
var
I, J, C: Integer;
vFieldNames: string;
begin
Result := False;
if (not Assigned(mDataSet)) or (not mDataSet.Active) or
(mStrings.Count <= 0) then
Exit;
vFieldNames := mStrings[0];
C := ListCount(vFieldNames, mDelimiter);
for I := 1 to mStrings.Count - 1 do
try
mDataSet.Append;
for J := 0 to C - 1 do
if mDataSet.FieldList.IndexOf(ListValue(vFieldNames, J, mDelimiter)) >= 0 then
mDataSet[ListValue(vFieldNames, J, mDelimiter)] :=
ListValue(mStrings[I], J, mDelimiter);
mDataSet.Post;
except
Exit;
end;
Result := True;
end; { TextToDataSet }
///////End Source
///////Begin Demo
procedure TForm1.Button1Click(Sender: TObject);
begin
DataSetToText(Table1, Memo1.Lines);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
TextToDataSet(Memo1.Lines, Table1);
end;
///////End Demo
2007. április 4., szerda
On which storage is you application?
Problem/Question/Abstract:
Sometimes, when you develop a software you need to disable the execution of the code from certain types of media, for example, if your application uses a database file, you can't write on it if it's located on a CR-ROM.
How to manage this in a easy way? There's the solution.
Answer:
Just write down these short routines:
function IsOnHDD: boolean;
begin
result := GetDriveType(pChar(uppercase(copy(ParamStr(0), 1, 3)))) = DRIVE_FIXED;
end;
function IsOnCD: boolean;
begin
result := GetDriveType(pChar(uppercase(copy(ParamStr(0), 1, 3)))) = DRIVE_CDROM;
end;
function IsOnRemoveable: boolean;
begin
result := GetDriveType(pChar(uppercase(copy(ParamStr(0), 1, 3)))) = DRIVE_REMOVABLE;
end;
The use them in your project file (.DPR) like this:
program Project1;
uses
Windows, // Added manually
SysUtils, // Added manually
Dialogs, // Added manually
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
function IsOnCD: boolean;
begin
result := GetDriveType(pChar(uppercase(copy(ParamStr(0), 1, 3)))) = DRIVE_CDROM;
end;
begin
Application.Initialize;
if IsOnCD then
begin
ShowMessage('This program cannot be executed from a CD-ROM drive.');
Application.Terminate;
end
else
begin
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
This program will not start if located on a CD-ROM. And no other code than the necessary one will be executed.
Christian Cristofori
2007. április 3., kedd
How to turn the CapsLock on and off
Problem/Question/Abstract:
How to turn the CapsLock on and off
Answer:
procedure SetLockKey(vcode: Integer; down: Boolean);
begin
if Odd(GetAsyncKeyState(vcode)) <> down then
begin
keybd_event(vcode, MapVirtualkey(vcode, 0), KEYEVENTF_EXTENDEDKEY, 0);
keybd_event(vcode, MapVirtualkey(vcode, 0), KEYEVENTF_EXTENDEDKEY
or KEYEVENTF_KEYUP, 0);
end;
end;
Call by:
SetLockKey(VK_CAPITAL, True); {caps lock down}
2007. április 2., hétfő
Draw lines over a TStringGrid
Problem/Question/Abstract:
I have a TStringGrid with objects put in big coloumns of 4 normal columns. How can I draw a black line from top to bottom over the gray line that the grid itself draws?
Answer:
Handle the OnDrawCell event for the grid. If the cell you are asked to draw is in the column in question you draw the part of the line that crosses the cell:
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
if (aCol = 1) and not (gdFixed in State) then
begin
with (sender as tstringgrid).canvas do
begin
Pen.Color := clBlack;
Pen.Width := 2;
Pen.Style := psSolid;
MoveTo(rect.right - 1, rect.top);
Lineto(rect.right - 1, rect.bottom);
end;
end;
end;
2007. április 1., vasárnap
How to draw transparent text on a bitmap
Problem/Question/Abstract:
How to draw transparent text on a bitmap
Answer:
The following example demonstrates drawing transparent text on the canvas of a TBitmap:
procedure TForm1.Button1Click(Sender: TObject);
var
OldBkMode: integer;
begin
Image1.Picture.Bitmap.Canvas.Font.Color := clBlue;
OldBkMode := SetBkMode(Image1.Picture.Bitmap.Canvas.Handle, TRANSPARENT);
Image1.Picture.Bitmap.Canvas.TextOut(10, 10, 'Hello');
SetBkMode(Image1.Picture.Bitmap.Canvas.Handle, OldBkMode);
end;
Feliratkozás:
Bejegyzések (Atom)