2007. október 30., kedd
TPicture and non-standard image file name extensions
Problem/Question/Abstract:
Can we use TPicture.LoadFromFile() for loading images while their file extension isn't recognized by TPicture? For example if BMP files have been renamed to *.img.
Answer:
If you know the format and it is indeed a bitmap then you can force it to work - see the first piece of code below.
You can even register your own extension with the second piece of code.
begin
// force it to be treated as a bitmap:
Image1.Picture.Bitmap.LoadFromFile('APicture.img');
// register your IMG extension application-wide to be treated as a bitmap:
Image1.Picture.RegisterFileFormat('img', 'Bitmap file', TBitmap);
Image1.Picture.LoadFromFile('APicture.img');
end.
2007. október 29., hétfő
How to change the directory while a TOpenDialog is open
Problem/Question/Abstract:
I have created an extra TSpeedButton on my TOpenDialog and would like to change the active directory if the user clicks it. If I change the current directory, nothing happens.
Answer:
{ ... }
hDlg := GetForeGroundWindow;
repeat
{Sending the path. Try until the dialog has received it}
SendDlgItemMessage(hDlg, 1152, WM_SETTEXT, 0, Integer(PChar(sDir)));
L := SendDlgItemMessage(hDlg, 1152, WM_GETTEXTLENGTH, 0, 0);
Application.ProcessMessages;
if Application.Terminated then
Exit;
until
L <> 0;
{And now click OK}
SendMessage(hDlg, WM_COMMAND, 1 + BN_CLICKED * $10000, GetDlgItem(hDlg, 1));
{Clear}
SendDlgItemMessage(hDlg, 1152, WM_SETTEXT, 0, Integer(PChar('')));
{ ... }
2007. október 28., vasárnap
Change the hint display delay
Problem/Question/Abstract:
How to change the hint display delay
Answer:
Solve 1:
I usually use a procedure like this, so it�ll calculate the needed time to display any hint:
procedure TDM.DoShowHint(var HintStr: string; var CanShow: Boolean; var HintInfo:
THintInfo);
begin
CanShow := not ExibirHints;
{Global variable the user configured to display the hint or not}
HintInfo.HideTimeout := Length(HintStr) * 50;
{Calculate the size of the string to wait a certain time}
end;
Solve 2:
Put a TApplicationEvents component and play with the OnShowHint event handler:
procedure TForm1.ApplicationEvents1ShowHint(var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
begin
{Check here the HintInfo class, eg.
if HintInfo.HintControl = MyControl then
HintInfo.ReshowTimeout := ...
HintInfo.HideTimeout := ... }
end;
2007. október 27., szombat
Finding a substring in a TStrings
Problem/Question/Abstract:
How to find a substring in a TStrings
Answer:
The IndexOf function in TStrings is great because it lets you quickly get the index of Item that holds the string in question. Unfortunately, it doesn't work for sub-strings. In that case, I've put together a neat little function called IndexOfSubString where you pass in the TStrings descendant you want to search on and a search value, and it'll return the index. Check it out:
{Purpose : Binary search algorithm for a
TStrings object. Finds the first
occurence of any substring within
a TStrings object or descendant}
function IndexOfSubString(List: TStrings; SubString: string): Integer;
var
I,
LowIdx,
HighIdx: Integer;
Found: boolean;
begin
Found := false;
Result := -1;
{This type of search uses the first half
of the TStrings list, so initialize the
LowIdx and HighIdx to the first and approximate
half of the list, respectively.}
LowIdx := 0;
HighIdx := List.Count div 2;
{Note that Found and the LowIdx are used
as conditionals. It's obvious why Found
is used, but less apparent why LowIdx is
used instead of HighIdx. The reason for
this is that the way I've set it up here,
HighIdx will never exceed (List.Count - 2),
whereas LowIdx can equal (List.Count - 1)
by nature of the assignment
if Found remains false after the for loop.}
while not Found and (LowIdx < (List.Count - 1)) do
begin
for I := LowIdx to HighIdx do
if (Pos(SubString, List[I]) > 0) and
not Found then
begin
Found := true;
Result := I;
end;
if not Found then
begin
LowIdx := HighIdx + 1;
HighIdx := HighIdx + ((List.Count - HighIdx) div 2);
end;
end;
end;
2007. október 26., péntek
How to detect if a point lies on a polyline
Problem/Question/Abstract:
I would like to know if a point is in a polyline (not polygon). Any code?
Answer:
The main procedure is called ExploreLine. In this procedure Fst and Lst may be two consecutively points in the polyline. Srch is the point searched.
{ ... }
const {global}
BigM = 1000000;
function Pend(Pi, Pf: TPoint): Real;
begin
if (Pf.X = Pi.X) then
Result := BigM {for a vertical line}
else
Result := (Pf.Y - Pi.Y) / (Pf.X - Pi.X);
end;
function Dist(Pi, Pf: TPoint): Real;
begin
Result := sqrt(sqr(Pi.Y - Pf.Y) + sqr(Pi.X - Pf.X))
end;
function CalcPoint(Pi, Pf: TPoint; d: Word): TPoint;
var
k, m: Real; { k=d / (1 + m2)� }
begin
m := Pend(Pi, Pf);
k := d / (Sqrt(1 + Sqr(m)));
if ((Pf.X - Pi.X) < 0) then
begin
Result.X := Pi.X - Round(k);
Result.Y := Pi.Y - Round(m * k);
end
else
begin
Result.X := Pi.X + Round(k);
Result.Y := Pi.Y + Round(m * k);
end;
end;
function ExploreLine(Srch, Fst, Lst: TPoint): Boolean;
var
p: Word;
Any: TPoint;
lim, dis: Real;
begin
lim := Dist(Lst, Fst);
p := 1;
Any := Fst;
repeat
Result := TestPoint(Srch, Any);
dis := Dist(Any, Fst);
Any := CalcPoint(Fst, Lst, Rad * p);
Inc(p);
until
(Result)rr(dis >= lim);
end;
2007. október 25., csütörtök
How to display the value of a field in the hint of a TDBGrid
Problem/Question/Abstract:
Does someone have a code snippet showing how I can display a field value from the TDBGrid row where the mouse pointer has stayed long enough to invoke the hint?
Answer:
This is the way I would do it:
{ ... }
if not VarIsNull(table1['PARTNO']) then
dbGrid1.hint := table1['PARTNO'];
I found that you have to check to see if there is data in the field before you use it, also you would need to convert to a string inttostr(table1['PARTNO']) if needed.
2007. október 24., szerda
How to get a list of all subdirectories in the current folder
Problem/Question/Abstract:
How to get a list of all subdirectories in the current folder
Answer:
Enumerating all folders in a subfolder/ directory:
procedure EnumFolders(root: string; folders: TStrings);
procedure Enum(dir: string);
var
SR: TSearchRec;
ret: Integer;
begin
if dir[length(dir)] <> '\' then
dir := dir + '\';
ret := FindFirst(dir + '*.*', faDirectory, SR);
if ret = 0 then
try
repeat
if ((SR.Attr and faDirectory) < > 0) and (SR.Name <> '.') and
(SR.Name <> '..') then
begin
folders.add(dir + SR.Name);
Enum(dir + SR.Name);
end;
ret := FindNext(SR);
until
ret <> 0;
finally
FindClose(SR)
end;
end;
begin
Folders.Clear;
if root < > emptyStr then
Enum(root);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumFolders('C:\windows', memo1.lines);
end;
2007. október 23., kedd
How to tell when to time-out an application
Problem/Question/Abstract:
How can I determine that a user has been inactive for a certain length of time so that I may exit the application?
Answer:
This is an interesting question because it introduces one form of security for an application, and that is time- sensitive, user activity-based processing. This type of "awareness" in a program can usually be found in dialup programs (CompuServe, MSN), but they also reside in security log-ins, in which if a response is not made within a discrete period of time, the program will close and you'll have to start all over again.
In Delphi, this is pretty easy to implement. What you're about to see is not the prettiest solution in the world, but it works.
Here's how to implement user activity-based time-sensitivity in your programs:
1. In the main form of your application, set the KeyPreview property to True so the form will see keystrokes before any other components (you'll see why when you see the code for the OnKeyDown method). And in the FormCreate method, write the following code:
procedure TForm1.FormCreate(Sender: TObject);
begin
TimeOut := 0;
end;
2. Drop a Timer component on the form and set its interval to 1000 milliseconds (the default).
3. Switch to the editor. Under the implementation section, declare the following const and var:
const
MaxTimeOutValue = 300; {This is 300 seconds, or five minutes}
var
TimeOut: Integer; {This will be incremented by the Timer}
4. Write the following procedure and declare in the private section of your form:
procedure TForm1.ResetTimeOut;
begin
� TimeOut := 0;
end;
5. Open up the OnKeyDown method for your form and put the following code:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
ResetTimeOut;
end;
This calls ResetTheTimer which, in turn, resets the TimeOut variable to zero, then disables and re- enables the Timer. The reason we do form-level processing of the keystrokes is so that no matter which component the user is typing in, keystrokes are always picked up by the form. Otherwise, you'd have to add this code to every component, and if you have a lot on your form ... yikes! I'd rather not think about it
6. In the OnTimer event of the Timer, put the following code:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Inc(TimeOut);
if TimeOut = MaxTimeOutValue then
begin
Timer1.Enabled := False;
Close;
end;
end;
This increments the TimeOut variable and compares it against the MaxTimeOutValue. If TimeOut equals MaxTimeOutValue, the timer is disabled and Close is called.
So how does this all work?
When the user presses a key while the form is running, TimeOut is reset to 0. This means that if the user is constantly typing, there's no way TimeOut can ever reach MaxTimeOutValue. However, once the user stops typing, because the timer is always enabled, TimeOut will be incremented every second, and will eventually reach the value equivalent to MaxTimeOutValue.
This isn't pretty, but it works.
2007. október 22., hétfő
Set the level of transparency for a TForm
Problem/Question/Abstract:
I want to create a form that has some degree of transparency. I know that in Windows 2000 SDK there is a very good resource to do that (SetLayeredWindowAttributes), but this one is not implemented in Windows.pas. I tried to import directly from user32.dll, and I even could find the value for some constants (WS_EX_LAYERED) with non documented value (MS C++.net), but at the end, I got some weird messages of "invalid variant type conversion" when trying to use this function. Does somebody have any example written in Delphi using this function?
Answer:
Solve 1:
{ ... }
const
WS_EX_LAYERED = $80000;
LWA_COLORKEY = 1;
LWA_ALPHA = 2;
type
TSetLayeredWindowAttributes = function(
hwnd: HWND; {handle to the layered window}
crKey: TColor; {specifies the color key}
bAlpha: byte; {value for the blend function}
dwFlags: DWORD {action}
): BOOL; stdcall;
procedure TfBaseSplash.FormCreate(Sender: TObject);
var
Info: TOSVersionInfo;
F: TSetLayeredWindowAttributes;
begin
inherited;
Info.dwOSVersionInfoSize := SizeOf(Info);
GetVersionEx(Info);
if (Info.dwPlatformId = VER_PLATFORM_WIN32_NT) and (Info.dwMajorVersion >= 5) then
begin
F := GetProcAddress(GetModulehandle(user32), 'SetLayeredWindowAttributes');
if Assigned(F) then
begin
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
GWL_EXSTYLE) or WS_EX_LAYERED);
F(Handle, 0, Round(255 * 80 / 100), LWA_ALPHA);
end;
end;
end;
Solve 2:
Make sure you check that the OS supports it. Here's how I do it:
function ALLOWALPHA: Boolean;
type
TSetLayeredWindowAttributes = function(hwnd: HWND; crKey: LongInt; bAlpha: Byte;
dwFlags: LongInt): LongInt; stdcall;
var
FhUser32: THandle;
SetLayeredWindowAttributes: TSetLayeredWindowAttributes;
begin
AllowAlpha := False;
FhUser32 := LoadLibrary('USER32.DLL');
if FhUser32 <> 0 then
begin
@SetLayeredWindowAttributes := GetProcAddress(FhUser32,
'SetLayeredWindowAttributes');
if @SetLayeredWindowAttributes <> nil then
begin
FreeLibrary(FhUser32);
Result := TRUE;
end
else
begin
FreeLibrary(FhUser32);
Result := False;
end;
end;
end;
2007. október 21., vasárnap
Creating a roll-up form
Problem/Question/Abstract:
How can I create a form that will roll up; that is, a form that when clicked will reduce its height to nothing but the title bar?
Answer:
I have seen a demo of a commercially available component in DCU form that does this and it's pretty slick. Because it's a component, the implementation is really nice. Just drop it in and you're off.
What I'm showing here does the pretty much the same thing, but is coded directly into the form. I did this because building a component would have required more event-handling code than I cared to perform. However, with Delphi 2.0's Object Repository, it's a very simple to add a form with this functionality into it and use it over and over again.
Let's look at the code, then discuss it:
unit testmain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, enhimage, StdCtrls, Printers, rollup, Buttons, ShellAPI;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FOldHeight: Integer;
procedure WMNCRButtonDown(var Msg: TWMNCRButtonDown); message WM_NCRBUTTONDOWN;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
FOldHeight := ClientHeight;
end;
procedure TForm1.WMNCRButtonDown(var Msg: TWMNCRButtonDown);
var
I: Integer;
begin
if (Msg.HitTest = HTCAPTION) then
if (ClientHeight = 0) then
begin
I := 0;
while (I < FOldHeight) do
begin
I := I + 40;
if (I > FOldHeight) then
I := FOldHeight;
ClientHeight := I;
Application.ProcessMessages;
end;
end
else
begin
FOldHeight := ClientHeight;
I := ClientHeight;
while (I > 0) do
begin
I := I - 40;
if (I < 0) then
I := 0;
ClientHeight := I;
Application.ProcessMessages;
end;
end;
end;
end.
First, by way of synopsis, the roll-up/down occurs in response to a WM_NCRBUTTONDOWN message firing off and the WMNCRButtonDown procedure handling the message, telling the window to roll up/down depending upon the height of the client area. WM_NCRBUTTONDOWN fires whenever the right mouse button is clicked in a "non-client" area, such as a border, menu or, for our purposes, the caption bar of a form. (The client area of a window is the area within the border where most of the interesting activity usually occurs. In general, the Windows API restricts application code to drawing only within the client area.)
Delphi encapsulates the WM_NCRBUTTONDOWN in a TWMNCRButtonDown type, which is actually an assignment from a TWMNCHitMessage type that has the following structure:
type
TWMNCHitMessage = record
Msg: Cardinal;
HitTest: Integer;
XCursor: SmallInt;
YCursor: SmallInt;
Result: Longint;
end;
Table 1 below discusses the parameters of the TWMCHitMessage structure in more detail:
Table 1 - TWMNCHitMessage record fields
Parameter
Type
Description
Msg
Cardinal
Each Windows message has an integer value which is its assigned ID
HitTest
Integer
This is a constant value that is returned by an internal Windows callback function that specifies the area on window when the message fired. Look in the Win32 Developer's Reference under WM_NCRBUTTONDOWN for values of nHitTest. For our purposes, we'll use HTCAPTION as the test value.
XCursor
SmallInt
This is the X value of the cursor position relative to the top left corner of the window
YCursor
SmallInt
This is the Y value of the cursor position relative to the top left corner of the window
Result
LongInt
The result value of WM_NCRBUTTONDOWN. Should be 0 if the application handled the message.
Now that you know about the message, let's look more closely at the code.
It's easy to create message wrappers in Delphi to deal with messages that aren't handled by an object by default. Since a right-click on the title bar of a form isn't handled by default, I had to create a wrapper. The procedure procedure WMNCRButtonDown(var Msg : TWMNCRButtonDown); message WM_NCRBUTTONDOWN; is the wrapper I created. All that goes on in the procedure is the following:
If the value of the message's HitTest field is equal to HTCAPTION (which means a right-click on the caption bar) then,
If height of the form's client area is equal to 0 then
1. Roll the form down
else if the height of the form's client area is not equal to 0 then
1. Roll the form up
In order to make this work, I had to create a variable called FOldHeight and set its value at FormCreate whenever the form was to be rolled up. FOldHeight is used as a place for the form to remember what size it was before it was re-sized to 0. When a form is to be rolled up, FOldHeight is immediately set to the current ClientHeight, which means you can interactively set the form's size, and the function will always return the form's ClientHeight to what it was before you rolled it up.
So what use is this? Well, sometimes I don't want to iconize a window; I just want to get it out of the way so I can see what's underneath. Having the capability to roll a form up to its title bar makes it a lot easier to see underneath a window without iconizing it, then having to Alt-tab back to it. (If you are familiar with the Macintosh platform, the System 7.5 environment offers a very similar facility called a "window shade," and makes a roll-up sound when the shade goes up.)
On an ending note, my good friend and colleague, Peter Jagielski, gave me a challenge: to create the effect of imploding and exploding windows. About eight years ago, he wrote an article in the DB Advisor for doing exploding and imploding windows in Paradox for DOS. When he saw the code for this, he smugly said, "That's pretty slick, but can you do exploding and imploding windows like I did?" How could I pass up that challenge? So be on the lookout for an example of exploding and imploding windows in a future tip.
By the way, a big thanks goes to Keith Bartholomess of TeamBorland for turning me on to the WM_NCRBUTTONDOWN message. I wouldn't have been able to write the event code without Keith pointing this message out to me.
2007. október 20., szombat
Convert a number to text
Problem/Question/Abstract:
How to convert a number to text?
Answer:
Here a code to covert a Number (Real) to string:
function RealToTxt(Amount: Real): string;
var
Num: LongInt;
Fracture: Integer;
function Num2Str(Num: LongInt): string;
const
hundred = 100;
thousand = 1000;
million = 1000000;
billion = 1000000000;
begin
if Num >= billion then
if (Num mod billion) = 0 then
Num2Str := Num2Str(Num div billion) + ' Billion'
else
Num2Str := Num2Str(Num div billion) + ' Billion ' +
Num2Str(Num mod billion)
else if Num >= million then
if (Num mod million) = 0 then
Num2Str := Num2Str(Num div million) + ' Million'
else
Num2Str := Num2Str(Num div million) + ' Million ' +
Num2Str(Num mod million)
else if Num >= thousand then
if (Num mod thousand) = 0 then
Num2Str := Num2Str(Num div thousand) + ' Thousand'
else
Num2Str := Num2Str(Num div thousand) + ' Thousand ' +
Num2Str(Num mod thousand)
else if Num >= hundred then
if (Num mod hundred) = 0 then
Num2Str := Num2Str(Num div hundred) + ' Hundred'
else
Num2Str := Num2Str(Num div hundred) + ' Hundred ' +
Num2Str(Num mod hundred)
else
case (Num div 10) of
6, 7, 9: if (Num mod 10) = 0 then
Num2Str := Num2Str(Num div 10) + 'ty'
else
Num2Str := Num2Str(Num div 10) + 'ty-' +
Num2Str(Num mod 10);
8: if Num = 80 then
Num2Str := 'Eighty'
else
Num2Str := 'Eighty-' + Num2Str(Num mod 10);
5: if Num = 50 then
Num2Str := 'Fifty'
else
Num2Str := 'Fifty-' + Num2Str(Num mod 10);
4: if Num = 40 then
Num2Str := 'Forty'
else
Num2Str := 'Forty-' + Num2Str(Num mod 10);
3: if Num = 30 then
Num2Str := 'Thirty'
else
Num2Str := 'Thirty-' + Num2Str(Num mod 10);
2: if Num = 20 then
Num2Str := 'Twenty'
else
Num2Str := 'Twenty-' + Num2Str(Num mod 10);
0, 1: case Num of
0: Num2Str := 'Zero';
1: Num2Str := 'One';
2: Num2Str := 'Two';
3: Num2Str := 'Three';
4: Num2Str := 'Four';
5: Num2Str := 'Five';
6: Num2Str := 'Six';
7: Num2Str := 'Seven';
8: Num2Str := 'Eight';
9: Num2Str := 'Nine';
10: Num2Str := 'Ten';
11: Num2Str := 'Eleven';
12: Num2Str := 'Twelve';
13: Num2Str := 'Thirteen';
14: Num2Str := 'Fourteen';
15: Num2Str := 'Fifteen';
16: Num2Str := 'Sixteen';
17: Num2Str := 'Seventeen';
18: Num2Str := 'Eightteen';
19: Num2Str := 'Nineteen'
end
end
end {Num2Str};
begin
Num := Trunc(Amount);
Fracture := Round(1000 * Frac(Amount));
if Num > 0 then
Result := Num2Str(Num) + ' and ';
if Fracture > 0 then
Result := Result + IntToStr(Fracture) + '/1000'
else
Result := Result + '000/1000';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
form1.Caption := realtotxt(123);
end;
2007. október 19., péntek
How to hide and show icons on the Windows Desktop
Problem/Question/Abstract:
I have seen code to hide the taskbar, but is there an API call or Delphi Code to hide the whole desktop except the applications you want to show?
Answer:
Not a single call, you would have to do an enumWindows and hide whatever windows you do not want to show. This hides and shows the icons on the desktop, for example:
procedure TForm1.Button1Click(Sender: TObject);
const
b: Boolean = false;
var
wnd: HWND;
begin
wnd := FindWindow('progman', nil);
if wnd <> 0 then
begin
if b then
ShowWindow(wnd, SW_SHOW)
else
ShowWindow(wnd, SW_HIDE);
b := not b;
end
else
showmessage('Desktop not found');
end;
2007. október 18., csütörtök
The Classes vs Object declaration
Problem/Question/Abstract:
99.9% (if not 100%) of the times we are using the Class declaration even when we do not need it.
Answer:
First of all lets clear what are the diffrences between the two type of declaration.
Class declaration is actully like the TObject object. There isn't any diffrence between the 2 declarations:
type
TMyClass1 = class
{.......}
end;
and the second declaration:
type
TMyClass2 = class(TObject)
{ ....... }
end;
If you will notice in delphi (in Delphi 3 and above) when you press on CTRL+Space you will find in the first declaration the same functions like the second declaration.
And if you will look in the delphi help file, you will find that Class word created for components !!!!
If you will declare this:
type
TMyObject = object
{ ....... }
end;
You will not find any function or property inside of it, if you did not declare of it.
In the Delphi help file, they are writing that the Object declaration can not receave any properties.
TheObject reserve word is very good for OOP of functions, like let say a CODEC (Encryption and Decryption) of the same thing, we can build it in OOP without the need of a component or a memory allocations (although we can).
2007. október 17., szerda
How to get the printer margins
Problem/Question/Abstract:
Does anybody know how to get, programmatically, the location of the canvas on the piece of paper that emerges from the printer? i.e. the size of the top, left, right and bottom margins?
Answer:
procedure TPrtPvw.GetOffsetPrinter;
var
pt: TPoint;
tmpAncho, tmpAlto: longint;
begin
Escape(hPrinter, GETPRINTINGOFFSET, 0, nil, @pt);
gOffSetLeft := pt.X;
gOffSetTop := pt.Y;
Escape(hPrinter, GETPHYSPAGESIZE, 0, nil, @pt);
tmpAncho := pt.X;
tmpAlto := pt.Y;
gOffSetRight := tmpAncho - gOffSetLeft - Printer.PageWidth;
gOffSetBottom := tmpAlto - gOffSetTop - Printer.PageHeight;
end;
2007. október 16., kedd
How to detect if a system is set to Large Font
Problem/Question/Abstract:
How to detect if a system is set to Large Font
Answer:
Returns True if small fonts are set, False if using Large Fonts:
function SmallFonts: boolean;
var
DC: HDC;
begin
DC := GetDC(0);
{LOGPIXELSX will be 120, if large fonts are in use}
result := (GetDeviceCaps(DC, LOGPIXELSX) = 96);
ReleaseDC(0, DC);
end;
2007. október 15., hétfő
Change the Windows start button bitmap
Problem/Question/Abstract:
Change your windows start button image
Answer:
{ define Global vars }
var
Form1: TForm1;
StartButton: hWnd;
OldBitmap: THandle;
NewImage: TPicture;
{ put the Code in the OnCreate event of your form }
procedure TForm1.FormCreate(Sender: TObject);
begin
NewImage := TPicture.create;
NewImage.LoadFromFile('C:\Windows\Circles.BMP');
StartButton := FindWindowEx(FindWindow('Shell_TrayWnd', nil), 0, 'Button', nil);
OldBitmap := SendMessage(StartButton, BM_SetImage, 0, NewImage.Bitmap.Handle);
end;
{ OnDestroy-Event }
procedure TForm1.FormDestroy(Sender: TObject);
begin
SendMessage(StartButton, BM_SetImage, 0, OldBitmap);
NewImage.Free;
end;
2007. október 14., vasárnap
Sending an email from Delphi using MAPI
Problem/Question/Abstract:
Sending an email from Delphi using MAPI
Answer:
If you do not want to rely on Outlook to send an email but you know that MAPI is installed, then you can also send mails with the following handy routine SendMailMAPI(). You need to add unit MAPI to your uses clause.
Note that MAPI is not always installed with Windows.
program MAPIMail;
uses
MAPI;
function SendMailMAPI(const Subject, Body, FileName, SenderName, SenderEMail,
RecepientName, RecepientEMail: string): Integer;
var
message: TMapiMessage;
lpSender,
lpRecepient: TMapiRecipDesc;
FileAttach: TMapiFileDesc;
SM: TFNMapiSendMail;
MAPIModule: HModule;
begin
FillChar(message, SizeOf(message), 0);
with message do
begin
if (Subject <> '') then
begin
lpszSubject := PChar(Subject)
end;
if (Body <> '') then
begin
lpszNoteText := PChar(Body)
end;
if (SenderEMail <> '') then
begin
lpSender.ulRecipClass := MAPI_ORIG;
if (SenderName = '') then
begin
lpSender.lpszName := PChar(SenderEMail)
end
else
begin
lpSender.lpszName := PChar(SenderName)
end;
lpSender.lpszAddress := PChar('SMTP:' + SenderEMail);
lpSender.ulReserved := 0;
lpSender.ulEIDSize := 0;
lpSender.lpEntryID := nil;
lpOriginator := @lpSender;
end;
if (RecepientEMail <> '') then
begin
lpRecepient.ulRecipClass := MAPI_TO;
if (RecepientName = '') then
begin
lpRecepient.lpszName := PChar(RecepientEMail)
end
else
begin
lpRecepient.lpszName := PChar(RecepientName)
end;
lpRecepient.lpszAddress := PChar('SMTP:' + RecepientEMail);
lpRecepient.ulReserved := 0;
lpRecepient.ulEIDSize := 0;
lpRecepient.lpEntryID := nil;
nRecipCount := 1;
lpRecips := @lpRecepient;
end
else
begin
lpRecips := nil
end;
if (FileName = '') then
begin
nFileCount := 0;
lpFiles := nil;
end
else
begin
FillChar(FileAttach, SizeOf(FileAttach), 0);
FileAttach.nPosition := Cardinal($FFFFFFFF);
FileAttach.lpszPathName := PChar(FileName);
nFileCount := 1;
lpFiles := @FileAttach;
end;
end;
MAPIModule := LoadLibrary(PChar(MAPIDLL));
if MAPIModule = 0 then
begin
Result := -1
end
else
begin
try
@SM := GetProcAddress(MAPIModule, 'MAPISendMail');
if @SM <> nil then
begin
Result := SM(0, Application.Handle, message, MAPI_DIALOG or
MAPI_LOGON_UI, 0);
end
else
begin
Result := 1
end;
finally
FreeLibrary(MAPIModule);
end;
end if Result <> 0 then
begin
MessageDlg('Error sending mail (' + IntToStr(Result) + ').', mtError, [mbOk],
0)
end;
end;
end.
2007. október 13., szombat
Copy from the active control to the clipboard
Problem/Question/Abstract:
Copy from the active control to the clipboard
Answer:
Use the following piece of code for this.
Note: You may trigger the menu open event and enable/disable the copy/paste menu items depending on the type of control that is active.
procedure CopyButtonClick(Sender: TObject);
begin
if ActiveControl is TMemo then
TMemo(ActiveControl).CopyToClipboard;
if ActiveControl is TDBMemo then
TDBMemo(ActiveControl).CopyToClipboard;
if ActiveControl is TEdit then
TEdit(ActiveControl).CopyToClipboard;
if ActiveControl is TDBedit then
TDBedit(ActiveControl).CopyToClipboard;
end;
procedure PasteButtonClick(Sender: TObject);
begin
if ActiveControl is TMemo then
TMemo(ActiveControl).PasteFromClipboard;
if ActiveControl is TDBMemo then
TDBMemo(ActiveControl).PasteFromClipboard;
if ActiveControl is TEdit then
TEdit(ActiveControl).PasteFromClipboard;
if ActiveControl is TDBedit then
TDBedit(ActiveControl).PasteFromClipboard;
end;
2007. október 12., péntek
Handle Excel through OLE Automation
Problem/Question/Abstract:
Handle Excel through OLE Automation
Answer:
The example below shows how to create and control an embedded Excel object. In case of Delphi 3, you need to use unit OleAuto, in Delphi 5 you have to use ComObj instead.
A good additional source is here.
uses
OleAuto; // Delphi 3
ComObj; // Delphi 5
var
vExcel: variant;
procedure TForm1.Button1Click(Sender: TObject);
begin
vExcel := CreateOleObject('Excel.Application');
vExcel.Workbooks.Add;
vExcel.ActiveWorkbook.Worksheets(1).Range('A1').Value := 'Hello World';
vExcel.Visible := True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if not VarIsEmpty(vExcel) then
vExcel.Quit;
end;
2007. október 11., csütörtök
Categories in the Object Inspector
Problem/Question/Abstract:
In Delphi 5 the object inspector allow us to view properties and events by categories. We can instruct Delphi which category the properties and events of our components belongs to and even to create our own categories.
Answer:
1. To instruct Delphi which category a property belongs to:
We have to do that in the Register procedure. The function RegisterPropertyInCategory has four overloaded versions. This is one of them. In this version we instruct Delphi to assign the property "Version" of our component "TMyButton" to the "TMiscellaneousCategory" standard category.
procedure Register;
begin
RegisterComponents('Samples', [TMyButton]);
RegisterPropertyInCategory(TMiscellaneousCategory, TMyButton, 'Version');
end;
Search Delphi help for more information about other overloaded versions of this function.
2.To create our own category:
We have to create a new class and derive it from TPropertyCategory or one of the existing categories (for example TMiscellaneousCategory). Then, we need to override the Name class function. The result value is the name shown by the object inspector.
interface
TMyCategory = class(TPropertyCategory)
public
class function Name: string; override;
end;
implementation
class function TMyCategory.Name: string;
begin
Result := 'My Category';
end;
Then we could use our new category.
procedure Register;
begin
RegisterComponents('Samples', [TMyButton]);
RegisterPropertyInCategory(TMyCategory, TMyButton, 'Version');
end;
You can also use RegisterPropertiesInCategory to register more than one property with one category at a time.
2007. október 10., szerda
How to determine if a formatted disk is in a drive
Problem/Question/Abstract:
I am trying to create a backup-routine for one of my applications. For that purpose I need a routine to test if there is a formatted disk in the disk-drive.
Answer:
There are two routines in the Object Pascal Language that can be used to determine if a formatted diskette is in a drive, as both return the same results if there is not a diskette in the drive. "DiskFree" and "DiskSize". You need to disable Windows error handling before using them or else Windows will display an error window and cause the functions to return invalid results.
procedure TForm1.Button1Click(Sender: TObject);
var
emode: word;
begin
emode := SetErrorMode(SEM_FAILCRITICALERRORS);
edit1.text := IntToStr(Diskfree(1));
SetErrorMode(emode);
end;
If DiskFree returns "-1" then there is not a formatted diskette in the drive.
2007. október 9., kedd
Convert color value into gray-scaled color value
Problem/Question/Abstract:
How can I convert the color value into gray-scaled value?
Answer:
If you want to convert a colored image into same gray scaled, then you must convert the color of the each pixel by the next schema:
function RgbToGray(Source: TColor): TColor;
var
Target: Byte;
begin
Target := Round((0.30 * GetRValue(Source)) +
(0.59 * GetGValue(Source)) +
(0.11 * GetBValue(Source)));
Result := RGB(Target, Target, Target);
end;
2007. október 8., hétfő
How to add text completion capability to a TComboBox
Problem/Question/Abstract:
How to add text completion capability to a TComboBox
Answer:
Solve 1:
The Netscape Communicator location box, The Windows 98 'Run' dialog, and other programs, have implemented a very user friendly feature known commonly as text completion. This document describes how to add similar functionality to a TComboBox. The most elegant and reusable way to add this functionality is by descending from TComboBox and overriding the ComboWndProc to handle the WM_KEYUP message. By adding a new property 'TextCompletion', the functionality can be toggled to act like a regular TComboBox. Below is the component unit that implements text completion in a TComboBox. This unit can be installed as is.
unit CompletingComboBox;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TCompletingComboBox = class(TComboBox)
private
FTextCompletion: Boolean;
function GetTextCompletion: Boolean;
procedure SetTextCompletion(const Value: Boolean);
protected
{override the WndProc() so that we can trap KeyUp events}
procedure ComboWndProc(var Message: TMessage; ComboWnd: HWnd;
ComboProc: Pointer); override;
public
{Public declarations}
published
property TextCompletion: Boolean read GetTextCompletion write SetTextCompletion;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Standard', [TCompletingComboBox]);
end;
{TCompletingComboBox}
function TCompletingComboBox.GetTextCompletion: Boolean;
begin
Result := fTextCompletion;
end;
procedure TCompletingComboBox.SetTextCompletion(const Value: Boolean);
begin
fTextCompletion := Value;
end;
procedure TCompletingComboBox.ComboWndProc(var Message: TMessage;
ComboWnd: HWnd; ComboProc: Pointer);
var
rc, len: Integer;
begin
inherited;
case Message.Msg of
WM_KEYUP:
begin
{test to see if its a character that should not be processed}
if (Message.WParam <> 8) and (Message.WParam <> VK_DELETE) and
(Message.WParam <> VK_SHIFT) and (FTextCompletion = True) then
begin
{Use CB_FINDSTRING to locate the string in the Items property}
rc := Perform(CB_FINDSTRING, -1, Integer(PChar(Caption)));
{if its in there then add the new string to the Text and select the portion that wasn't typed in by the user}
if rc <> CB_ERR then
begin
{store the length of the current string}
len := Length(Text);
{set the new string}
ItemIndex := rc;
{highlight the rest of the text that was added}
SelStart := len;
SelLength := Length(Text) - len;
{return 0 to signify that the message has been handled}
Message.Result := 0;
end;
end;
end;
end;
end;
end.
Solve 2:
Performing autocompletion in a combobox:
procedure TForm1.ComboBox1Change(Sender: TObject);
var
oldpos: Integer;
item: Integer;
begin
with Sender as TComboBox do
begin
oldpos := selstart;
item := Perform(CB_FINDSTRING, -1, lparam(Pchar(text)));
if item >= 0 then
begin
onchange := nil;
text := items[item];
selstart := oldpos;
sellength := gettextlen - selstart;
onchange := combobox1change;
end;
end;
end;
procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: Char);
var
oldlen: Integer;
begin
if key = #8 then
with sender as TComboBox do
begin
oldlen := sellength;
if selstart > 0 then
begin
selstart := selstart - 1;
sellength := oldlen + 1;
end;
end;
end;
2007. október 7., vasárnap
Detect the full path and file name of where the DLL is running from?
Problem/Question/Abstract:
How can I detect (from a dynamic link library) the full path and file name of where the DLL is running from?
Answer:
The following example demonstrates a dll function that will detect the full path of where the dll was loaded from.
Example:
uses Windows;
procedure ShowDllPath stdcall;
var
TheFileName: array[0..MAX_PATH] of char;
begin
FillChar(TheFileName, sizeof(TheFileName), #0);
GetModuleFileName(hInstance, TheFileName, sizeof(TheFileName));
MessageBox(0, TheFileName, 'The DLL file name is:', mb_ok);
end;
2007. október 6., szombat
Create a SystemDSN with Delphi-5
Problem/Question/Abstract:
How to create a ODBC SystemDSN with Delphi?
Answer:
This example shows one way to load the ODBC Administrator's DLL (ODBCCP32.DLL) to create an Access MDB file and ODBC DSN pointing at it. Note that it assumes current directory for both the DLL and the MDB, but the DLL will be found if in the WinSys directory which is where it normally is anyway.
Similar operation applies to most driver types, with some modifications. eg: Access requires the MDB file to exist so you can hook the DSN to it.
Note also that the "CREATE_DB" call is an Access special (MS Jet Engine) and has other variants like COMPACT_DB and REPAIR_DB. For a full list see either the Jet Engine Programmers Guide or the MSDN and search for "CREATE_DB".
const
ODBC_ADD_DSN = 1; // Add data source
ODBC_CONFIG_DSN = 2; // Configure (edit) data source
ODBC_REMOVE_DSN = 3; // Remove data source
ODBC_ADD_SYS_DSN = 4; // add a system DSN
ODBC_CONFIG_SYS_DSN = 5; // Configure a system DSN
ODBC_REMOVE_SYS_DSN = 6; // remove a system DSN
type
TSQLConfigDataSource = function(hwndParent: HWND;
fRequest: WORD;
lpszDriver: LPCSTR;
lpszAttributes: LPCSTR): BOOL; stdcall;
procedure Form1.FormCreate(Sender: TObject);
var
pFn: TSQLConfigDataSource;
hLib: LongWord;
strDriver: string;
strHome: string;
strAttr: string;
strFile: string;
fResult: BOOL;
ModName: array[0..MAX_PATH] of Char;
srInfo: TSearchRec;
begin
Windows.GetModuleFileName(HInstance, ModName, SizeOf(ModName));
strHome := ModName;
while (strHome[length(strHome)] <> '\') do
Delete(strHome, length(strHome), 1);
strFile := strHome + 'TestData.MDB'; // Test Access Rights (Axes = Access)
hLib := LoadLibrary('ODBCCP32'); // load from default path
if (hLib <> NULL) then
begin
@pFn := GetProcAddress(hLib, 'SQLConfigDataSource');
if (@pFn <> nil) then
begin
// force (re-)create DSN
strDriver := 'Microsoft Access Driver (*.mdb)';
strAttr := Format('DSN=TestDSN' + #0 +
'DBQ=%s' + #0 +
'Exclusive=1' + #0 +
'Description=Test Data' + #0 + #0,
[strFile]);
fResult := pFn(0, ODBC_ADD_SYS_DSN, @strDriver[1], @strAttr[1]);
if (fResult = false) then
ShowMessage('Create DSN (Datasource) failed!');
// test/create MDB file associated with DSN
if (FindFirst(strFile, 0, srInfo) <> 0) then
begin
strDriver := 'Microsoft Access Driver (*.mdb)';
strAttr := Format('DSN=TestDSN' + #0 +
'DBQ=%s' + #0 +
'Exclusive=1' + #0 +
'Description=Test Data' + #0 +
'CREATE_DB="%s"'#0 + #0,
[strFile, strFile]);
fResult := pFn(0, ODBC_ADD_SYS_DSN, @strDriver[1], @strAttr[1]);
if (fResult = false) then
ShowMessage('Create MDB (Database file) failed!');
end;
FindClose(srInfo);
end;
FreeLibrary(hLib);
end
else
begin
ShowMessage('Unable to load ODBCCP32.DLL');
end;
end;
2007. október 5., péntek
How to play a beep without consuming system ressources
Problem/Question/Abstract:
How can I play a beep without consuming system ressources? It's easy. Use the code below.
Answer:
procedure SpecialBeep;
asm
mov al,7
int 29h
end;
How to play a beep without consuming system ressources
Problem/Question/Abstract:
How can I play a beep without consuming system ressources? It's easy. Use the code below.
Answer:
procedure SpecialBeep;
asm
mov al,7
int 29h
end;
2007. október 4., csütörtök
How to draw an underline on a Listview Caption
Problem/Question/Abstract:
Can you underline the caption of a ListView Item?
Answer:
To draw an Underline on a Listview Caption the same like the HotTrack function in Delphi 6 in Delphi 3 you must call an API function.
In the Uses Clausse inpelement the CommCtrl unit.
Then you set the following code in the MouseMove property of your ListView.
procedure TfrmMain.lvwMainMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
const
LVS_EX_UNDERLINEHOT = $00000800;
LVS_EX_INFOTIP = $00000400;
var
AItem: TListItem;
Styles: DWord;
begin
//This line is a VCL Bugfix for the ListView
Styles := LVS_EX_INFOTIP;
AItem := lvwMain.GetItemAt(X, Y);
if not Assigned(AItem) then
begin
lvwMain.Cursor := crArrow;
end
else
begin
lvwMain.Cursor := crHandPoint;
Styles := Trunc(Styles + LVS_EX_UNDERLINEHOT - LVS_EX_CHECKBOXES -
LVS_EX_FULLROWSELECT);
ListView_SetExtendedListViewStyle(lvwMain.Handle, Styles);
end;
end;
When you goes with your mouse over an ListView Item there will be an underline drawed under the caption of the Item.
Because the value that exists in the Styles variabele allso enables checkboxes and rowselect add the following lines under the Styles lines and above the SetExtendedListViewStyle.
Styles := Styles - LVS_EX_CHECKBOXES;
Styles := Styles - LVS_EX_TRACKSELECT;
This will fix the bug of the Checkboxes and TrackSelecting.
2007. október 3., szerda
How to determine if a method is of type TNotifyEvent
Problem/Question/Abstract:
If I am given a TPersistent object, and a method name, is there a way to determine if the name is an event of TNotifyEvent type? For example, given a TPersistent lMyObj and an event name, "OnDataChanged", how can I determine if OnDataChanged is a TNotifyEvent?
Answer:
function IsNotifyEvent(Sender: TObject; const Event: string): Boolean;
var
PropInfo: PPropInfo;
Method: TNotifyEvent;
begin
Result := False;
PropInfo := GetPropInfo(Sender.ClassInfo, Event);
if not Assigned(PropInfo) then
Exit;
if PropInfo.PropType^.Kind <> tkMethod then
Exit;
Method := TNotifyEvent(GetMethodProp(Sender, PropInfo));
Result := Assigned(Method);
end;
2007. október 2., kedd
Fastest way to search a string in a file
Problem/Question/Abstract:
Fastest way to search a string in a file
Answer:
The function below returns position of substring in file, or -1 if such substring can not be found.
function PosInFile(Str, FileName: string): integer;
var
Buffer: array[0..1023] of char;
BufPtr, BufEnd: integer;
F: file;
Index: integer;
Increment: integer;
c: char;
function NextChar: char;
begin
if BufPtr >= BufEnd then
begin
BlockRead(F, Buffer, 1024, BufEnd);
BufPtr := 0;
Form1.ProgressBar1.Position := FilePos(F);
Application.ProcessMessages;
end;
Result := Buffer[BufPtr];
Inc(BufPtr);
end;
begin
Result := -1;
AssignFile(F, FileName);
Reset(F, 1);
Form1.ProgressBar1.Max := FileSize(F);
BufPtr := 0;
BufEnd := 0;
Index := 0;
Increment := 1;
repeat
c := NextChar;
if c = Str[Increment] then
Inc(Increment)
else
begin
Inc(Index, Increment);
Increment := 1;
end;
if Increment = (Length(Str) + 1) then
begin
Result := Index;
Break;
end;
until BufEnd = 0;
CloseFile(F);
Form1.ProgressBar1.Position := 0;
end;
2007. október 1., hétfő
Add database aliases to BDE at run time
Problem/Question/Abstract:
Add database aliases to BDE at run time
Answer:
This function that will let you add database aliases to BDE (Borland Database engine) during run time:
uses
DBIProcs, DBITypes;
procedure AddBDEAlias(sAliasName, sAliasPath, sDBDriver: string);
var
h: hDBISes;
begin
DBIInit(nil);
DBIStartSession('dummy', h, '');
DBIAddAlias(nil, PChar(sAliasName), PChar(sDBDriver),
PChar('PATH:' + sAliasPath), True);
DBICloseSession(h);
DBIExit;
end;
{ Sample call to create an alias called WORK_DATA that }
{ points to the C:\WORK\DATA directory and uses the }
{ DBASE driver as the default database driver: }
AddBDEAlias('WORK_DATA', 'C:\WORK\DATA', 'DBASE');
Feliratkozás:
Bejegyzések (Atom)