2008. szeptember 30., kedd
Un-registering an OLE server
Problem/Question/Abstract:
How do you get rid of a "registered" ServerName if you don't want to use it any longer?
Answer:
Run the executable with the /UNREGSERVER flag:
MYSERVER.EXE /UNREGSERVER
This is the standard way of unregistering a self-registering OLE automation server.
2008. szeptember 29., hétfő
Create a full screen form without auto-hiding the Windows Taskbar
Problem/Question/Abstract:
How do I set my form to display in full screen? No title bar, no borders, and it goes over the task bar (not by setting the task bar to "Auto Hide").
Answer:
Emulating full screen mode:
private {in form declaration}
procedure WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
procedure TForm1.WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo);
begin
inherited;
with msg.MinMaxInfo^.ptMaxTrackSize do
begin
X := GetDeviceCaps(Canvas.handle, HORZRES) + (Width - ClientWidth);
Y := GetDeviceCaps(Canvas.handle, VERTRES) + (Height - ClientHeight);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
const
Rect: TRect = (Left: 0; Top: 0; Right: 0; Bottom: 0);
FullScreen: Boolean = False;
begin
FullScreen := not FullScreen;
if FullScreen then
begin
Rect := BoundsRect;
SetBounds(Left - ClientOrigin.X, Top - ClientOrigin.Y,
GetDeviceCaps(Canvas.handle, HORZRES)
+ (Width - ClientWidth), GetDeviceCaps(Canvas.handle, VERTRES) + (Height -
ClientHeight));
{Label2.caption := IntToStr(GetDeviceCaps( Canvas.handle, VERTRES ));}
end
else
BoundsRect := Rect;
end;
2008. szeptember 28., vasárnap
How to create a system wide windows hook
Problem/Question/Abstract:
How to create a system wide windows hook
Answer:
The following example demonstrates creating a system wide windows hook under Win32. The example provides both the code for the system hook dll and an example application. The hook function that we will create will also demonstrate advanced coding techniques such as sharing global memory across process boundaries using memory mapped files, sending messages from the key hook function back to the originating application, and dynamic loading of a dll at runtime.
The example keyboard hook that we create will keep a count of the number of keystrokes a user enters on the keyboard. Further, we will demonstrate trapping the enter key, and passing a message back to the application that initiated the keyboard hook each time the enter key is pressed. Finally, we will demonstrate trapping the left arrow key and instead of letting it through to the current application, we will instead replace it with a right arrow keystroke. (Note: that this can cause much confusion to a unsuspecting user).
Adding a hook to the windows system involves calling the Windows API function SetWindowsHookEx() and passing it the type of hook you wish to install, and address of the hook function you are installing. System wide hook functions are required to reside in a dynamic link library, since they must be mapped into each process on the system. The SetWindowsHookEx() function adds your hook function into the Windows "hook chain", returning a handle (or id) of the hook you are installing. You will use this handle to identify your hook to windows, and to remove your hook when you are done trapping the keyboard.
The Windows "hook chain" is a linked list of functions that Windows uses to keep track of all the installed hooks, allowing multiple hooks to be installed at any given time. Occasionally, Windows will ask your hook function to call the next hook in the chain, allowing all the hooks an opportunity to function. When we do call the next hook in the chain, we will need to identify ourselves by passing the handle of our hook function to the next hook.
Creating a Windows hook requires special handling under Win32, since the dll must be mapped (on the fly) into the process space of every application that receives keystrokes. Normally, this is not an issue, however, when operating inside a keyhook procedure, global variables (such as your hook handle) must be preserved while the dll is mapped into other process spaces. Under Win16, this would not be a program, since dlls had a single data segment that was shared across all process mappings. Under Win32, each mapping of the dll receives its own data segment. This means that as the dll that contains the keyboard hook is mapped into each process that receives keystrokes, it receives a new data segment, and new unitialized variables with it. This is a problem, since global variables (such as your hook handle) must be preserved across process mappings. To solve this problem, we will take advantage of Win32's ability to memory map variables from the system paging file.
Each time our dll is mapped into a process, the DllMain() function in our dll will be called by windows, with a parameter flag indicating the reason for the call. When we receive the DLL_PROCESS_ATTACH flag (indicating our dll is getting mapped into a different process), we will create a file mapping to the system paging file and get a pointer to our memory mapped variables. When we receive the DLL_PROCESS_DETACH flag (indicating our dll is getting un-mapped from a process), we will free our file mapping of the system paging file. The variables we will need to keep track of (and have access to from both the dll and the application that originally loaded the keyboard hook) are placed in a record structure called THookRec. The THookRec structure has the following fields:
TheHookHandle:
The handle (id) of the Keyboard hook that we set. We will need access to this variable during the execution of the keyhook function, to identify ourselves to windows when we are asked to call the next hook in the hook chain. We will also need access to this variable when we remove our hook. Finally, the originating application that will receive the messages from our hook function can access this variable to see if and when the hook is active.
TheAppWinHandle:
While this variable is not used in our example dll or application, it is a starting place for adding additional messaging capabilities between the hook function and your application that initiates the hook. It can also be useful for determining if the hook is functioning while mapped into the context of the initiating application.
TheCtrlWinHandle:
This variable will hold the handle to a button control in our initiating application. We will use this handle to send messages from the keyboard hook function to the button control. Every time the enter key is pressed, we will send a WM_KEYDOWN and a WM_KEYUP message to the button and a key value of 0 (zero). We will trap the OnKeyDown event in the button control, and keep count of the number of times the user presses the enter key.
TheKeyCount:
This variable will keep track of the total number of key presses made by the user. Obviously our keyhook will need access to this variable to increment its value, and the originating application that will receive the messages from our hook function will want to access this variable to display real time results.
The DLL contains the following functions:
MapFileMemory:
Creates a system paging file mapping object and initializes a pointer to our mapping variable of type THookRec.
UnMapFileMemory:
Frees the system paging file mapping object and mapping variable created by the MapFileMemory() function.
GetHookRecPointer:
An exported function that returns a pointer to the mapping variable created by the MapFileMemory() function. The initiating application can both set and examine this memory block, and effectively share memory that is used by our hook function during the time the hook function is operating in the context of another process space.
KeyBoardProc:
The actual hook function. This function receives both keydown, and keyup messages as well as a message from windows indicating we should call the next hook in the windows "hook chain". This function increments TheKeyCount field of the memory mapped THookRec structure if the keystroke we are processing is a keyup message. If the key being processed is the enter key, we will fire the OnKeyDown event of the window provided in "TheCtrlWinHandle" field of the memory mapped THookRec structure. Finally, if the left arrow key is pressed, we will swallow the keystroke, and instead send a right arrow key stroke to the application. Note that the following variables and initializing code has been included in this function for your convience. The variables have been commented out in the code (as not to compile). To use them, simply remove the comments in the code:
IsAltPressed {Determines if the Alt key is currently down}
IsCtrlPressed {Determines if the Control key is currently down}
IsShiftPressed {Determines if the Shift key is currently down}
StartKeyBoardHook:
An exported function that allows the application to initiate installing the keyboard hook.
StopKeyBoardHook:
An exported function that allows the application to initiate removing the keyboard hook.
DllEntryPoint:
The main entry point into our dll, allowing us to know when our dll is being mapped in, and out of, different application's address space.
Delphi Hook DLL Example:
library TheHook;
uses
Windows, Messages, SysUtils;
{Define a record for recording and passing information process wide}
type
PHookRec = ^THookRec;
THookRec = packed record
TheHookHandle: HHOOK;
TheAppWinHandle: HWND;
TheCtrlWinHandle: HWND;
TheKeyCount: DWORD;
end;
var
hObjHandle: THandle; {Variable for the file mapping object}
lpHookRec: PHookRec; {Pointer to our hook record}
procedure MapFileMemory(dwAllocSize: DWORD);
begin
{Create a process wide memory mapped variable}
hObjHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, dwAllocSize,
'HookRecMemBlock');
if (hObjHandle = 0) then
begin
MessageBox(0, 'Hook DLL', 'Could not create file map object', MB_OK);
exit;
end;
{Get a pointer to our process wide memory mapped variable}
lpHookRec := MapViewOfFile(hObjHandle, FILE_MAP_WRITE, 0, 0, dwAllocSize);
if (lpHookRec = nil) then
begin
CloseHandle(hObjHandle);
MessageBox(0, 'Hook DLL', 'Could not map file', MB_OK);
exit;
end;
end;
procedure UnMapFileMemory;
begin
{Delete our process wide memory mapped variable}
if (lpHookRec <> nil) then
begin
UnMapViewOfFile(lpHookRec);
lpHookRec := nil;
end;
if (hObjHandle > 0) then
begin
CloseHandle(hObjHandle);
hObjHandle := 0;
end;
end;
function GetHookRecPointer: pointer stdcall;
begin
{Return a pointer to our process wide memory mapped variable}
result := lpHookRec;
end;
{The function that actually processes the keystrokes for our hook}
function KeyBoardProc(Code: integer; wParam: integer; lParam: integer): integer;
stdcall;
var
KeyUp: bool;
{Remove comments for additional functionability ... :
IsAltPressed: bool;
IsCtrlPressed: bool;
IsShiftPressed: bool;
}
begin
result := 0;
case Code of
HC_ACTION:
begin
{We trap the keystrokes here}
{Is this a key up message?}
KeyUp := ((lParam and (1 shl 31)) <> 0);
{Remove comments for additional functionability ... :
{Is the Alt key pressed}
if ((lParam and (1 shl 29)) <> 0) then
begin
IsAltPressed := TRUE;
end
else
begin
IsAltPressed := FALSE;
end;
{Is the Control key pressed}
if ((GetKeyState(VK_CONTROL) and (1 shl 15)) <> 0) then
begin
IsCtrlPressed := TRUE;
end
else
begin
IsCtrlPressed := FALSE;
end;
{if the Shift key pressed}
if ((GetKeyState(VK_SHIFT) and (1 shl 15)) <> 0) then
begin
IsShiftPressed := TRUE;
end
else
begin
IsShiftPressed := FALSE;
end;
}
{If KeyUp then increment the key count}
if (KeyUp <> FALSE) then
begin
Inc(lpHookRec^.TheKeyCount);
end;
case wParam of
{Was the enter key pressed?}
VK_RETURN:
begin
{if KeyUp}
if (KeyUp <> FALSE) then
begin
{Post a bogus message to the window control in our app}
PostMessage(lpHookRec^.TheCtrlWinHandle, WM_KEYDOWN, 0, 0);
PostMessage(lpHookRec^.TheCtrlWinHandle, WM_KEYUP, 0, 0);
end;
{If you wanted to swallow the keystroke then return -1, else if you want
to allow the keystroke then return 0}
result := 0;
exit;
end; {VK_RETURN}
{If the left arrow key is pressed then lets play a joke!}
VK_LEFT:
begin
{if KeyUp}
if (KeyUp <> FALSE) then
begin
{Create a UpArrow keyboard event}
keybd_event(VK_RIGHT, 0, 0, 0);
keybd_event(VK_RIGHT, 0, KEYEVENTF_KEYUP, 0);
end;
{Swallow the keystroke}
result := -1;
exit;
end; {VK_LEFT}
end; {case wParam}
{Allow the keystroke}
result := 0;
end; {HC_ACTION}
HC_NOREMOVE:
begin
{This is a keystroke message, but the keystroke message has not been removed
from the message queue, since an application has called PeekMessage()
specifying PM_NOREMOVE}
result := 0;
exit;
end;
end; {case code}
if (Code < 0) then
{Call the next hook in the hook chain}
result := CallNextHookEx(lpHookRec^.TheHookHandle, Code, wParam, lParam);
end;
procedure StartKeyBoardHook stdcall;
begin
{If we have a process wide memory variable and the hook has not already been set...}
if ((lpHookRec <> nil) and (lpHookRec^.TheHookHandle = 0)) then
begin
{Set the hook and remember our hook handle}
lpHookRec^.TheHookHandle := SetWindowsHookEx(WH_KEYBOARD, @KeyBoardProc,
hInstance, 0);
end;
end;
procedure StopKeyBoardHook stdcall;
begin
{If we have a process wide memory variable and the hook has already been set...}
if ((lpHookRec <> nil) and (lpHookRec^.TheHookHandle <> 0)) then
begin
{Remove our hook and clear our hook handle}
if (UnHookWindowsHookEx(lpHookRec^.TheHookHandle) <> FALSE) then
begin
lpHookRec^.TheHookHandle := 0;
end;
end;
end;
procedure DllEntryPoint(dwReason: DWORD);
begin
case dwReason of
Dll_Process_Attach:
begin
{If we are getting mapped into a process, then get a pointer to our
process wide memory mapped variable}
hObjHandle := 0;
lpHookRec := nil;
MapFileMemory(sizeof(lpHookRec^));
end;
Dll_Process_Detach:
begin
{If we are getting unmapped from a process then, remove the pointer to
our process wide memory mapped variable}
UnMapFileMemory;
end;
end;
end;
exports
KeyBoardProc name 'KEYBOARDPROC',
GetHookRecPointer name 'GETHOOKRECPOINTER',
StartKeyBoardHook name 'STARTKEYBOARDHOOK',
StopKeyBoardHook name 'STOPKEYBOARDHOOK';
begin
{Set our Dll's main entry point}
DLLProc := @DllEntryPoint;
{Call our Dll's main entry point}
DllEntryPoint(Dll_Process_Attach);
end.
Application notes:
The test application we have created demonstrates loading the dll that contains the keyboard hook, installing the keyboard hook, displaying the total keystroke count and the number of times the enter key has been pressed (in real time), uninstalling the keyboard hook and unloading the dll.
The application code starts out by defining a form containing two labels, a button, and timer component. Once we install our hook function, we will start the timer, and upon every timer event, we will display in label1 the total number of keystrokes that have been entered by the user since the hook was set. The hook will also fire the button's OnKeyDown event each time the enter key is pressed, giving us the opportunity to display the total number of times the enter key has been pressed in the caption of label2.
After the form is defined, we then define the THookRec structure in the same manner as it is defined in the hook dll. Other variables we will use include: a handle variable used for loading the hook dll, and three function pointer variables used to call the GetHookRecPointer(), StartKeyBoardHook(), and StopKeyBoardHook() functions. Finally we define a pointer to a THookRec structure used to access the memory mapped variables used by the hook function, a variable to keep track of the number of times the enter key is pressed, and a variable used to indicate the success of loading the dll, getting its functions, and setting the hook.
The application logic goes something like this:
On form create, we will initialize our form's components, attempt to dynamically load the hook dll, and get the address of the GetHookRecPointer(), StartKeyBoardHook(), and StopKeyBoardHook() functions located in the hook dll. If we are successful, we will retrieve a pointer to THookRec structure used by the hook dll, we will then initialize structure, adding the handle of the button control so the keyboard hook will know which window control to call when the enter key is pressed. We will then attempt to start the keyboard hook. If we are successful, at setting the hook, we can then start the timer.
On form destroy, if we where previously successful in installing the windows hook and loading the hook dll, we will now uninstall the windows hook, and unload the KeyHook dll.
On the timer's timer event, we will simply display the total number of key presses in the form's label1 caption by accessing the KeyHook dll's THookRec structure.
On the Buttons KeyDown event, if the key value passed is zero we increment our EnterKeyCount variable and display the total number of times the enter key has been pressed by accessing the KeyHook dll's THookRec structure.
Delphi TestApp Example:
unit TestHk1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,
ExtCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Timer1: TTimer;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{Functions prototypes for the hook dll}
type
TGetHookRecPointer = function: pointer stdcall;
type
TStartKeyBoardHook = procedure stdcall;
type
TStopKeyBoardHook = procedure stdcall;
{The record type filled in by the hook dll}
type
THookRec = packed record
TheHookHandle: HHOOK;
TheAppWinHandle: HWND;
TheCtrlWinHandle: HWND;
TheKeyCount: DWORD;
end;
{A pointer type to the hook record}
type
PHookRec = ^THookRec;
var
hHookLib: THandle; {A handle to the hook dll}
GetHookRecPointer: TGetHookRecPointer; {Function pointer}
StartKeyBoardHook: TStartKeyBoardHook; {Function pointer}
StopKeyBoardHook: TStopKeyBoardHook; {Function pointer}
LibLoadSuccess: bool; {If the hook lib was successfully loaded}
lpHookRec: PHookRec; {A pointer to the hook record}
EnterKeyCount: DWORD; {An internal count of the Enter Key}
procedure TForm1.FormCreate(Sender: TObject);
begin
{Set our initial variables}
Timer1.Enabled := FALSE;
Timer1.Interval := 1000;
Label1.Caption := '0 Keys Logged';
Label2.Caption := '0 Enter Keys Logged';
EnterKeyCount := 0;
lpHookRec := nil;
LibLoadSuccess := FALSE;
@GetHookRecPointer := nil;
@StartKeyBoardHook := nil;
@StopKeyBoardHook := nil;
{Try to load the hook dll}
hHookLib := LoadLibrary('THEHOOK.DLL');
{If the hook dll was loaded successfully}
if hHookLib <> 0 then
begin
{Get the function addresses}
@GetHookRecPointer := GetProcAddress(hHookLib, 'GETHOOKRECPOINTER');
@StartKeyBoardHook := GetProcAddress(hHookLib, 'STARTKEYBOARDHOOK');
@StopKeyBoardHook := GetProcAddress(hHookLib, 'STOPKEYBOARDHOOK');
{Did we find all the functions we need?}
if ((@GetHookRecPointer <> nil) and (@StartKeyBoardHook <> nil) and
(@StopKeyBoardHook <> nil)) then
begin
LibLoadSuccess := TRUE;
{Get a pointer to the hook record}
lpHookRec := GetHookRecPointer;
{Were we successfull in getting a ponter to the hook record}
if (lpHookRec <> nil) then
begin
{Fill in our portion of the hook record}
lpHookRec^.TheHookHandle := 0;
lpHookRec^.TheCtrlWinHandle := Button1.Handle;
lpHookRec^.TheKeyCount := 0;
{Start the keyboard hook}
StartKeyBoardHook;
{Start the timer if the hook was successfully set}
if (lpHookRec^.TheHookHandle <> 0) then
begin
Timer1.Enabled := TRUE;
end;
end;
end
else
begin
{We failed to find all the functions we need}
FreeLibrary(hHookLib);
hHookLib := 0;
@GetHookRecPointer := nil;
@StartKeyBoardHook := nil;
@StopKeyBoardHook := nil;
end;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{Did we load the dll successfully?}
if (LibLoadSuccess = TRUE) then
begin
{Did we sucessfully get a pointer to the hook record?}
if (lpHookRec <> nil) then
begin
{Did the hook get set?}
if (lpHookRec^.TheHookHandle <> 0) then
begin
Timer1.Enabled := FALSE;
StopKeyBoardHook;
end;
end;
{Free the hook dll}
FreeLibrary(hHookLib);
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
{Display the number of keystrokes logged}
Label1.Caption := IntToStr(lpHookRec^.TheKeyCount) + ' Keys Logged';
end;
procedure TForm1.Button1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
{Process message sent from hook dll and display number of time the enter key was pressed}
if (Key = 0) then
begin
Inc(EnterKeyCount);
Label2.Caption := IntToStr(EnterKeyCount) + ' Enter Keys Logged';
end;
end;
end.
2008. szeptember 27., szombat
How to turn menu accelerators on and off
Problem/Question/Abstract:
How can I turn off menu accelerators? It's not done with MenuItem.enabled:=false. I need that because I have set the accelerators 'Delete' and 'Insert' to menu items, but in some situations those keys need to be "free" for other purposes (when the user is editing an EditBox, in my case).
Answer:
Your message implies, but doesn't state, that you are using standard windows shortcuts for Delete (Del key) and Insert (Ins key) features. You want to use them as the standard when the active control is an edit box but not under other circumstances.
I had a similar situation and resolved the problem using a TActionList. Use the OnExecute event to determine if you are using your own or windows processing. Unfortunately, the windows processing doesn't occur automatically and you have to handle the action yourself. Here's my solution for Delete:
procedure TfrmBuildQuery.alExpressionExecute(Action: TBasicAction; var Handled:
Boolean);
begin
if Action = aDelete then
if eValue.Focused then
{This is the edit control I needed to have windows-like changes}
begin
Handled := True; {Stops it from going to the delete action}
with eValue do
begin
{If no selection, then select the char to the right of the cursor.}
if SelLength = 0 then
SelLength := 1;
{Do the delete}
SelText := '';
end;
end
else
Handled := False
else
Handled := False;
end;
2008. szeptember 26., péntek
How to draw dotted or dashed lines using a pen with a width greater than 1
Problem/Question/Abstract:
How to draw dotted or dashed lines using a pen with a width greater than 1
Answer:
I once got around this silly limitation by writing a wrapper procedure that would figure out where each dot or dash belonged, and draw many tiny line segments using a solid pen. One of the parameters to the procedure was a string indicating the pattern, which looked like morse code (any arrangement of dots and dashes is allowed).
However, the procedure is ugly. It will handle polylines, and if a polyline vertex falls right in the middle of a dash, then the dash will bend around the corner correctly.
The LineTo method of TCanvas cannot reliably render dashed lines more than one pixel wide. This procedure provides a workaround.
{Copyright (c) 1996 G. Williams}
procedure PlotDashedLine(const Canvas: TCanvas; const Vertices: array of TPoint;
const Pattern: string; const DashLength: Integer);
var
PenDown: Boolean;
Index: Integer;
procedure PlotTo(const Position: TPoint);
begin
with Canvas, Position do
if (PenDown) then
LineTo(X, Y)
else
MoveTo(X, Y);
end;
function Advance(const Distance: Integer): Boolean;
var
DistanceRemaining: Single;
DistanceToNextVertex: Single;
begin
Result := false;
DistanceRemaining := Distance;
DistanceToNextVertex := PointDist(Canvas.PenPos, Vertices[Index]);
while (DistanceRemaining > DistanceToNextVertex) do
begin
DistanceRemaining := DistanceRemaining - DistanceToNextVertex;
PlotTo(Vertices[Index]);
Inc(Index);
if (Index > High(Vertices)) then
Exit;
DistanceToNextVertex := PointDist(Canvas.PenPos, Vertices[Index]);
end;
with Canvas.PenPos do
if (FltEqual(DistanceToNextVertex, 0)) then
PlotTo(Vertices[Index])
else
PlotTo(Point(Round(X + DistanceRemaining / DistanceToNextVertex * (Vertices[Index].X - X)), Round(Y + DistanceRemaining / DistanceToNextVertex * (Vertices[Index].Y - Y))));
Result := true;
end;
var
PatternIndex: Integer;
OldPenStyle: TPenStyle;
begin
OldPenStyle := Canvas.Pen.Style;
Canvas.Pen.Style := psSolid;
Canvas.MoveTo(Vertices[0].X, Vertices[0].Y);
PatternIndex := 1;
Index := 1;
while (true) do
begin
PenDown := true;
case Pattern[PatternIndex] of
'.':
if not (Advance(0)) then
Break;
'-':
if not (Advance(DashLength)) then
Break;
else
ShowError('');
end;
PenDown := false;
if not (Advance(DashLength)) then
Break;
Inc(PatternIndex);
if (PatternIndex > Length(Pattern)) then
PatternIndex := 1;
end;
Canvas.Pen.Style := OldPenStyle;
end;
2008. szeptember 25., csütörtök
Detect the current URL in Internet Explorer
Problem/Question/Abstract:
Detect the current URL in Internet Explorer
Answer:
You can quickly retrieve the current Internet Explorer's URL with DDE.
There are a few problems with this technique:
multiple instances of IE could be open - you have no control to which one you connect
one instance with multiple windows could be open (created with IE's menu File | New | Window). Again, you have no control which one you connect to.
Ultimately, you could use EnumChildWindows() calls to check for any open IE window. This could would have to be customized for each new IE release..
uses
DDEMan;
// TForm type declaration...
procedure TForm1.Button1Click(Sender: TObject);
var
DDE: TDDEClientConv;
begin
DDE := TDDEClientConv.Create(self);
if DDE.SetLink('IExplore', 'WWW_GetWindowInfo') then
Memo1.Lines.Add(DDE.RequestData('0xFFFFFFFF,sURL,sTitle'));
DDE.Free;
end;
2008. szeptember 24., szerda
Embedding files as resources in a Delphi executable
Problem/Question/Abstract:
This article attempts to explain how to include files inside a Delphi application as different kinds of resources, and how to manage them.
Answer:
It is possible to embed any kind of file in an executable using resource files (*.RES). Certain kinds of resources are recognized by the API and can be used directly. Others are simply taken as binary data and its up to you to use them. In this article we will see examples of both kinds.
To create the resource file we start with the source file (*.RC), for example named RESOURCES.RC, which is a simple text file that contains the resource entries (name, class and file):
sample_bmp
BITMAP
sample.bmp
sample_ico
ICON
sample.ico
sample_cur
CURSOR
sample.cur
sample_ani
ANICURSOR
sample.ani
sample_jpg
JPEG
sample.jpg
sample_wav
WAVE
sample.wav
sample_txt
TEXT
sample.txt
The names of the resources (sample_bmp, sample_ico, etc.) are arbitrary. The kind of resource may be one recognized by the APIs (BITMAP, ICON, CURSOR) or arbitrary (JPEG, WAVE, TEXT). The file names specify the files that will be included in the .RES file (and later in the .EXE).
Now we have to compile the .RC file to produce the .RES file. For that we can use the Borland Resource Compiler (brcc32.exe) that you can probably find in Delphi's BIN folder. It's a simple command-line utility that expects the name of the source file as parameter:
C:\DELPHI\P0025>brcc32 resources
Borland Resource Compiler Version 5.40
Copyright (c) 1990, 1999 Inprise Corporation. All rights reserved.
C:\DELPHI\P0025>_
To instruct the linker to embed the resource file in the executable, we use the resource file directive ($R or $RESOURCE) in our Pascal ource code:
{$R resources.res}
Loading the resources in your application is easy for the "recongnized" resources like BITMAP, ICON and CURSOR since the Windows API provides functions (LoadBitmap, LoadIcon and LoadCursor respectively) to get handles for these elements, that for example we can assign to the Handle property of the corresponding object:
Image1.Picture.Bitmap.Handle := LoadBitmap(hInstance, 'sample_bmp');
Icon.Handle := LoadIcon(hInstance, 'sample_ico');
Screen.Cursors[1] := LoadCursor(hInstance, 'sample_cur');
For more alternatives when loading image resources, see the API LoadImage.
Other resources are little bit more difficult to manage. Let's start with JPEG images. The following function uses TResourceStream to load the resource as a stream that will be loaded into a TJPEGImage object:
function GetResourceAsJpeg(const resname: string): TJPEGImage;
var
Stream: TResourceStream;
begin
Stream := TResourceStream.Create(hInstance, ResName, 'JPEG');
try
Result := TJPEGImage.Create;
Result.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
Example:
var
Jpg: TJPEGImage;
begin
// ...
Jpg := GetResourceAsJpeg('sample_jpg');
Image2.Picture.Bitmap.Assign(Jpg);
Jpg.Free;
// ...
end;
For WAV files we need a pointer to the resource loaded in memory, and for a text file we need to load a resource in a string. We can do it using TResourceStream, but let's see an example using the API:
function GetResourceAsPointer(ResName: pchar; ResType: pchar;
out Size: longword): pointer;
var
InfoBlock: HRSRC;
GlobalMemoryBlock: HGLOBAL;
begin
InfoBlock := FindResource(hInstance, resname, restype);
if InfoBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
size := SizeofResource(hInstance, InfoBlock);
if size = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
GlobalMemoryBlock := LoadResource(hInstance, InfoBlock);
if GlobalMemoryBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
Result := LockResource(GlobalMemoryBlock);
if Result = nil then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
function GetResourceAsString(ResName: pchar; ResType: pchar): string;
var
ResData: PChar;
ResSize: Longword;
begin
ResData := GetResourceAsPointer(resname, restype, ResSize);
SetString(Result, ResData, ResSize);
end;
Sample calls:
var
sample_wav: pointer;
procedure TForm1.FormCreate(Sender: TObject);
var
size: longword;
begin
{...}
sample_wav := GetResourceAsPointer('sample_wav', 'wave', size);
Memo1.Lines.Text := GetResourceAsString('sample_txt', 'text');
end;
Once we have the wave resource loaded into memory we can play it as many times as we want by using the API sndPlaySound declared in the MMSystem unit:
procedure TForm1.Button1Click(Sender: TObject);
begin
sndPlaySound(sample_wav, SND_MEMORY or SND_NODEFAULT or SND_ASYNC);
end;
There are some resources (like fonts and animated cursors) that can't be used from memory. We necessarily have to save these resources to a temporary disk file and load them from there. The following function saves a resource to a file:
procedure SaveResourceAsFile(const ResName: string; ResType: pchar;
const FileName: string);
begin
with TResourceStream.Create(hInstance, ResName, ResType) do
try
SaveToFile(FileName);
finally
Free;
end;
end;
The following function makes use of the previous one to save a resource in a temporary file:
function SaveResourceAsTempFile(const ResName: string;
ResType: pchar): string;
begin
Result := CreateTempFile;
SaveResourceAsFile(ResName, ResType, Result);
end;
The discussion of the function CreateTempFile falls beyond the scope of this article and its implementation can be seen in the example attached to the newsletter.
The following function makes use of SaveResourceAsTempFile to save an animated-cursor resource to a temporary file, then it loads the cursor from the file with LoadImage and finally deletes the temporary file. The function returns the handle returned by LoadImage:
function GetResourceAsAniCursor(const ResName: string): HCursor;
var
CursorFile: string;
begin
CursorFile := SaveResourceAsTempFile(ResName, 'ANICURSOR');
Result := LoadImage(0, PChar(CursorFile), IMAGE_CURSOR, 0,
0, LR_DEFAULTSIZE or LR_LOADFROMFILE);
DeleteFile(CursorFile);
if Result = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
Sample call:
Screen.Cursors[1] := GetResourceAsAniCursor('sample_ani');
Form1.Cursor := 1;
Well, that's it. I hope you find it useful. You can find more information about resource files in the MSDN Library.
FAQ
1. I was trying to acces some resources like a textfile for example or some variables, but i want to be able to change them and access them at any time. Is this possible??? How?
In some Windows there is an API to update resources in an executable, but the operating system won't allow you to write to an executable file that is running... Basically, take resources as read-only. The first time you can extract them from the executable, but if you need to write to them and preserve their values, I'm afraid that from them on you'll have to use separate files.
2. However I would like to point out something, sndPlaySound is reported as a windows 3.1 api wich of course is great since this makes a program degrade gracefully when run on older windows, but the windows api recommends instead the use of playsound (defined in MMSYSTEM I think) wich even has a SND_RESOURCE flag (yep direct playing from a resource)
PlaySound can play a resource, it's true, however sndPlaySond is not a Windows 3.1 API but a Windows NT 3.1 API. It's a 32-bit function and it doesn't degrade the system's performance as you suggest (actually it's very likely that it is implemented as a call to PlaySound). In certain contexts, for example a game that plays a sound too often, it won't hurt if the resource is loaded in memory instead of playing it from the file.
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2008. szeptember 23., kedd
How to get the Icon of a CD-ROM
Problem/Question/Abstract:
I want to write an File explorer, but how can I get the Icon of a CD-ROM? The problem is it can be stored as an ICO File or as a Win32 Resource into an Executable File.
Answer:
With the following Code you can get the Icon of a CD inserted in your CD-ROM Drive. It opens the icon which is specified in the "AutoRun.inf" of your CD. It's unimportant weather the icon is saved as a file or as a resource in a Win32 Executable File (*.EXE or *.DLL). If the "AutoRun.inf" doesn't exist, the function will stop themself.
function GetCDIcon(Drive: Char): TICon;
var
ico: TIcon;
ini: TIniFile;
s, p: string;
i, j: integer;
begin
Result := nil;
if GetDriveType(PChar(Drive + ':\')) <> DRIVE_CDROM then
exit;
//Abort if "AutoRun.inf" doesn't exists.
if FileExists(Drive + ':\autorun.inf') = False then
exit;
//Open the "AutoRun.inf"
ini := TIniFile.create(Drive + ':\autorun.inf');
ico := TIcon.create;
try
//Read the filename
s := ini.ReadString('Autorun', 'ICON', '');
//Abort if there is no icon specified
if s = '' then
exit;
//load the icon from a file
if FileExists(s) then
ico.LoadFromFile(s);
if FileExists(Drive + ':\' + s) then
ico.LoadFromFile(Drive + ':\' + s);
//Load the icon from a Win32 resource
if (FileExists(s) = False) and (FileExists(Drive + ':\' + s) = False) then
begin
for j := (Pos(',', s) + 1) to Length(s) do
begin
p := p + s[j];
end;
i := strtoint(p);
for j := Length(s) downto (Pos(',', s)) do
Delete(s, j, Length(s));
if FileExists(s) = False then
s := Drive + ':\' + s;
ico.handle := ExtractIcon(hinstance, PChar(s), i);
end;
Result := ico;
finally
ini.free;
end;
end;
Here an example how to use this function:
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.picture.assign(GetCDIcon('F'));
end;
2008. szeptember 22., hétfő
How to determine the font that is used in a menu
Problem/Question/Abstract:
How would I get the font used in menus? I want to setup a TFont that is the same.
Answer:
Returned font becomes callers property and must be freed by it!
function GetMenuFont: TFont;
var
ncMetrics: TNonClientMetrics;
begin
ncMetrics.cbSize := sizeof(TNonClientMetrics);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(TNonClientMetrics), @ncMetrics,
0);
Result := TFont.Create;
Result.Handle := CreateFontIndirect(ncMetrics.lfMenuFont);
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
2008. szeptember 21., vasárnap
Scroll a TForm through code
Problem/Question/Abstract:
I'm using a form with a TPaintBox element that exceeds the size of the form, so the form has two scrollbars. I want the user to be able to scroll the form using the keyboard (with cursor keys). How can I perform scrolling programmatically? I tried using the TForm.ScrollBy method, but the results are a bit strange.
Answer:
Do not use ScrollBy, instead send WM_VSCROLL messages to the form to make it do the work for you.
procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
VK_DOWN: {scroll down}
begin
Perform(WM_VSCROLL, SB_LINEDOWN, 0);
Perform(WM_VSCROLL, SB_ENDSCROLL, 0);
end;
VK_UP: {scroll up}
begin
Perform(WM_VSCROLL, SB_LINEUP, 0);
Perform(WM_VSCROLL, SB_ENDSCROLL, 0);
end;
end;
end;
If you use ScrollBy you also have to manually adjust the scrollbar position since it only scrolls the windows client area, completely independent of any scrollbar.
2008. szeptember 20., szombat
Create an appointment in MS Outlook
Problem/Question/Abstract:
How to create an appointment in MS Outlook
Answer:
Today I want to continue a serie of tips for MS Outlook automatization from Delphi.
If you want to create a new appointment, you can use a code sample below:
uses ComObj;
procedure CreateNewAppointment;
const
olAppointmentItem = $00000001;
olImportanceLow = 0;
olImportanceNormal = 1;
olImportanceHigh = 2;
{to find a default Contacts folder}
function GetCalendarFolder(folder: OLEVariant): OLEVariant;
var
i: Integer;
begin
for i := 1 to folder.Count do
begin
if (folder.Item[i].DefaultItemType = olAppointmentItem) then
begin
Result := folder.Item[i];
break
end
else
Result := GetCalendarFolder(folder.Item[i].Folders);
end;
end;
var
outlook, ns, folder, appointment: OLEVariant;
begin
{initialize an Outlook}
outlook := CreateOLEObject('Outlook.Application');
{get MAPI namespace}
ns := outlook.GetNamespace('MAPI');
{get a default Contacts folder}
folder := GetCalendarFolder(ns.Folders);
{if Contacts folder is found}
if not VarIsNull(folder) then
begin
{create a new item}
appointment := folder.Items.Add(olAppointmentItem);
{define a subject and body of appointment}
appointment.Subject := 'new appointment';
appointment.Body := 'call me tomorrow';
{duration: 10 days starting from today}
appointment.Start := Now();
appointment.End := Now() + 10; {10 days for execution}
appointment.AllDayEvent := 1; {all day event}
{set reminder in 20 minutes}
appointment.ReminderMinutesBeforeStart := 20;
appointment.ReminderSet := 1;
{set a high priority}
appointment.Importance := olImportanceHigh;
{to save an appointment}
appointment.Save;
{to display an appointment}
appointment.Display(True);
{to print a form}
appointment.PrintOut;
end;
{to free all used resources}
folder := UnAssigned;
ns := UnAssigned;
outlook := UnAssigned
end;
2008. szeptember 19., péntek
How to determine if a property has inherited from a particular class
Problem/Question/Abstract:
How can I propogate through an object's published properties and extract an actual class reference in order to perform operations like: if GiveProperty is TSomeObjectType then do something?
Answer:
function GetFontProp(anObj: TObject): TFont;
var
PInfo: PPropInfo;
begin
{Try to get a pointer to the property information for a
property with the name 'Font'.
TObject.ClassInfo returns a pointer to the RTTI table, which we need to pass
to GetPropInfo}
PInfo := GetPropInfo(anObj.ClassInfo, 'font');
Result := nil;
if PInfo <> nil then
{found a property with this name, check if it has the correct type}
if (PInfo^.Proptype^.Kind = tkClass) and
GetTypeData(PInfo^.Proptype^)^.ClassType.InheritsFrom(TFont) then
Result := TFont(GetOrdProp(anObj, PInfo));
end;
2008. szeptember 18., csütörtök
Auto Hide Form
Problem/Question/Abstract:
Auto Hide Form
Answer:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
FAnchors: TAnchors;
procedure WMMOVING(var Msg: TMessage); message WM_MOVING;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Math;
procedure TForm1.WMMOVING(var Msg: TMessage);
begin
inherited;
with PRect(Msg.LParam)^ do
begin
Left := Min(Max(0, Left), Screen.Width - Width);
Top := Min(Max(0, Top), Screen.Height - Height);
Right := Min(Max(Width, Right), Screen.Width);
Bottom := Min(Max(Height, Bottom), Screen.Height);
FAnchors := [];
if Left = 0 then
Include(FAnchors, akLeft);
if Right = Screen.Width then
Include(FAnchors, akRight);
if Top = 0 then
Include(FAnchors, akTop);
if Bottom = Screen.Height then
Include(FAnchors, akBottom);
Timer1.Enabled := FAnchors <> [];
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled := False;
Timer1.Interval := 200;
FormStyle := fsStayOnTop;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
const
cOffset = 2;
var
vHandle: THandle;
begin
vHandle := WindowFromPoint(Mouse.CursorPos);
while (vHandle <> 0) and (vHandle <> Handle) do
vHandle := GetParent(vHandle);
if vHandle = Handle then
begin
if akLeft in FAnchors then
Left := 0;
if akTop in FAnchors then
Top := 0;
if akRight in FAnchors then
Left := Screen.Width - Width;
if akBottom in FAnchors then
Top := Screen.Height - Height;
end
else
begin
if akLeft in FAnchors then
Left := -Width + cOffset;
if akTop in FAnchors then
Top := -Height + cOffset;
if akRight in FAnchors then
Left := Screen.Width - cOffset;
if akBottom in FAnchors then
Top := Screen.Height - cOffset;
end;
end;
end.
2008. szeptember 17., szerda
How to get TRichEdit to use the RichEd20.dll
Problem/Question/Abstract:
Is there any easy way to get TRichEdit to use RichEd20.dll? We need to use the latest RTF v3, not the 1.0 currently supported by TRichEdit.
Answer:
It's better to use a third-party wrapper, but you can try to trick the standard TRichEdit. Override CreateParams and call CreateSubClass(Params, RICHEDIT_CLASS) there. The RICHEDIT_CLASS constant designates a Rich Edit version 2.0 and higher control. Also, you should call the LoadLibrary('RICHED20.DLL') function to verify which version of Rich Edit is installed. Check the code listed below for details. There is additional code to suppress exceptions, which the RichEdit strings object generates, after the new string was inserted.
{ ... }
type
TMyRichEdit20 = class(TRichEdit)
protected
FMax: integer;
FSelection: TCharRange;
procedure EMExSetSel(var Message: TMessage); message EM_EXSETSEL;
procedure EMReplaceSel(var Message: TMessage); message EM_REPLACESEL;
function GetSelStart: integer; override;
procedure CreateParams(var Params: TCreateParams); override;
public
constructor Create(AOwner: TComponent); override;
end;
{ ... }
constructor TMyRichEdit20.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FMax := 0;
FSelection.cpMin := 0;
FSelection.cpMax := 0;
end;
procedure TMyRichEdit20.EMExSetSel(var Message: TMessage);
var
ISel: integer;
XSel: ^TCharRange absolute ISel;
begin
inherited;
ISel := Message.LParam;
FSelection := XSel^;
end;
procedure TMyRichEdit20.EMReplaceSel(var Message: TMessage);
begin
inherited;
FMax := FSelection.cpMax + length(PChar(Message.LParam));
end;
function TMyRichEdit20.GetSelStart: Integer;
begin
if FMax = 0 then
Result := inherited GetSelStart
else
begin
Result := FMax;
FMax := 0;
end;
end;
var
FRichEditModule: THandle;
procedure TMyRichEdit20.CreateParams(var Params: TCreateParams);
begin
if FRichEditModule = 0 then
begin
FRichEditModule := LoadLibrary('RICHED20.DLL');
if FRichEditModule <= HINSTANCE_ERROR then
FRichEditModule := 0;
end;
inherited CreateParams(Params);
CreateSubClass(Params, RICHEDIT_CLASS);
end;
{ ... }
initialization
finalization
if FRichEditModule <> 0 then
FreeLibrary(FRichEditModule);
end.
2008. szeptember 16., kedd
How to adjust the width of THeaderControl sections automatically, when the first section is resized
Problem/Question/Abstract:
I have THeaderControl with 4 sections. When I resize the first section, I need the remaining 3 sections to resize proportionally to their size to ensure that they don't go out of visibility (I mean that the last section end position always remains the same). I tried to use OnSectionResize or OnSectionTrack events, but without success. Can anyone help?
Answer:
procedure TForm1.ResizeMyHeader();
const
MIN_COL_WIDTH = 10;
var
iColWidth: Integer;
iColExtra: Integer;
iSection: Integer;
begin
if HeaderControl1.Sections.Count > 1 then
begin
iColWidth := HeaderControl1.Width;
Dec(iColWidth, HeaderControl1.Sections[0].Width);
iColWidth := (iColWidth div (HeaderControl1.Sections.Count - 1));
iColExtra := HeaderControl1.Width - (HeaderControl1.Sections[0].Width +
(HeaderControl1.Sections.Count - 1) * iColWidth);
{resize the 2nd to the last columns}
if iColWidth >= MIN_COL_WIDTH then
begin
for iSection := 1 to HeaderControl1.Sections.Count - 2 do
begin
HeaderControl1.Sections[iSection].Width := iColWidth;
end;
HeaderControl1.Sections[HeaderControl1.Sections.Count - 1].Width := iColWidth + iColExtra;
end;
end;
end;
procedure TForm1.HeaderControl1_OnSectionResize(HeaderControl: THeaderControl;
Section: THeaderSection);
begin
Self.ResizeMyHeader;
end;
procedure TForm1.Form_OnResize(Sender: TObject);
begin
Self.ResizeMyHeader;
end;
2008. szeptember 15., hétfő
Create a *.jpg image from a TRichEdit
Problem/Question/Abstract:
How to create a *.jpg image from a TRichEdit
Answer:
uses
RichEdit;
procedure RichEditToJPEG(const aRichEdit: TRichEdit; const aJPEGImage: TJPEGImage);
var
lBMP: TBitmap;
liStyle: Integer;
liExStyle: Integer;
lR: TRect;
liTwipsPerPixel: Integer;
lfrFormatRange: TFormatRange; { Defined in RichEdit }
begin
lBMP := TBitmap.Create;
try
lBMP.Height := aRichEdit.Height;
lBMP.Width := aRichEdit.Width;
{ Paint the richedit control's border }
aRichEdit.PaintTo(lBMP.Canvas.Handle, 0, 0);
{ Store the richedit's window styles }
liStyle := GetWindowLong(aRichEdit.Handle, GWL_STYLE);
liExStyle := GetWindowLong(aRichEdit.Handle, GWL_EXSTYLE);
{ Get canvas rect and adjust for the richedit's border if necessary }
lR := lBMP.Canvas.ClipRect;
if ((liStyle and WS_BORDER) <> 0) or ((liExStyle and WS_EX_CLIENTEDGE) <> 0) then
begin
Inc(lR.Left, GetSystemMetrics(SM_CXEDGE));
Inc(lR.Top, GetSystemMetrics(SM_CYEDGE));
Dec(lR.Right, lR.Left);
Dec(lR.Bottom, lR.Top);
end;
{ Adjust richedit's border by another pixel }
InflateRect(lR, -1, -1);
{ We need twips to calculate sizes }
liTwipsPerPixel := 1400 div Screen.PixelsPerInch;
{ Fill the TFormatRange record }
with lfrFormatRange do
begin
hdc := lBMP.Canvas.Handle;
hdcTarget := lBMP.Canvas.Handle;
{ Convert the coordinates to twips }
rc := Rect(lR.Left * liTwipsPerPixel, lR.Top * liTwipsPerPixel,
lR.Right * liTwipsPerPixel, lR.Bottom * liTwipsPerPixel);
rcPage := rc;
chrg.cpMin := 0;
chrg.cpMax := -1;
end;
aRichEdit.Perform(EM_FORMATRANGE, 0, 0);
aRichEdit.Perform(EM_FORMATRANGE, 0, DWORD(@lfrFormatRange));
aRichEdit.Perform(EM_FORMATRANGE, 1, DWORD(@lfrFormatRange));
aRichEdit.Perform(EM_FORMATRANGE, 0, 0);
aJPEGImage.Assign(lBMP);
finally
lBMP.Free;
end;
end;
2008. szeptember 14., vasárnap
Keep your IF ... ELSE conditions reduced to a minimum
Problem/Question/Abstract:
Make simple conditions easier to read and mantain
Answer:
Sometimes you have conditions like
if condition then
Result := what_ever
else
Result := something;
Or, even worst, sometimes you have several conditions, like
if condition1 then
Result1 := what_ever1
else
Result1 := something1;
if condition2 then
Result2 := what_ever2
else
Result2 := something2;
if conditionN then
ResultN := what_everN
else
ResultN := somethingN;
Woundt it be much easy to write something like
Result := IFF(condition, result_when_condition_is_true,
result_when_condition_is_false);
What I propose here is a simple function that will reduce simple conditions into a single line, by receiving a condition and the values to return when the condition is true or false.
function IFF(C: Boolean; T, F: Variant): Variant;
begin
if C then
Result := T
else
Result := F;
end;
Since the variables are variant type, you can pass any data type you want, like in these examples:
// will return 'Correct', since the condition is true
MyStr = IFF(TRUE, 'Correct', 'Incorrect');
// will return 'Incorrect', since the condition is false
MyStr = IFF(TALSE, 'Correct', 'Incorrect');
// will return X if X > Y, otherwise returns Y
MyInt = IFF(X > Y, X, Y);
// will return TRUE, since TRUE or FALSE is TRUE
MyBool = IFF((TRUE or FALSE), TRUE, FALSE);
// will return 0, since TRUE and FALSE is FALSE
MyInt = IFF((TRUE and FALSE), 1, 0);
// is MyStr has a lenght grater then 0, returns its lenght, otherwise returns 0
MyInt = IFF(Lenght(MyStr) > 0, Lenght(MyStr), 0);
// if 'Address:' is present on MyStr, it will return the lenght of the string, otherwise will return the string 'Not Found!'
MyVar = IFF(Pos('Address:', MyStr) > 0, Length(MyStr), 'Not found!');
// if x is smaller or equal to 1, it returns X, otherwise it returns the multiplication of X by its predecessor
MyInt = IFF(X <= 1, X, X * (X - 1));
I've been using this funtion for a while and noticed that the code is easier to read and maintain.
2008. szeptember 13., szombat
Create and Manage Modal and Modeless forms in a DLL
Problem/Question/Abstract:
Displaying and using forms from a DLL can be difficult if you don't know the way. Fortunately enough, Delphi is quite flexible and the managing forms from a DLL is quite easy.
Answer:
The first thing you will need a Handle to the application’s main window. Assuming that the application is running on top, you can get the window’s handle using GetActiveWindow, like this:
MainApplicationHandle := GetActiveWindow;
In order to be able to take advantage of Delphi’s window controlling features, however, you need a window control, not a window handle. You can get a window control uising FindControl (Controls Unit)
fWinControl := FindControl(MainApplicationHandle);
fWinControl is assumed to be of TwinControl type..
I have found it useful to create the Modal (or modeless window) since the begiinning:
MyForm := TMyForm.create(fWinControl);
The three items can be placed confortably in the initialization section of the DLL:
MainApplicationHandle := GetActiveWindow;
fWinControl := FindControl(MainApplicationHandle);
MyForm := TMyForm.create(fWinControl);
Finally, when you need the Form, you just show it, but be carfeul to redraw it if something changed within it:
MyForm.Repaint;
winresult := MyForm.showmodal
(winresult is defined to be of typo longint) You just have to be sure that your form includes wither buttons that produce a modal result (by setting ModalResult to something else than mrNone)
If you’d rather prefer a modeless window (for example, to display a progress bar) , you can use the following approach:
MyForm.Visible := true;
while {some condition set} do
begin
{change something in the window}
MyForm.repaint;
end;
MyForm.Hide;
2008. szeptember 12., péntek
How select first item in ListView with use key TAB
Problem/Question/Abstract:
When I hand over focus with key TAB to ListView, ListView not select first item. How on it?
Answer:
We have two ListView components. First component with name ListView1 contains two events.
procedure TForm1.ListView1Enter(Sender: TObject);
begin
HookHandle := SetWindowsHookEx(WH_KEYBOARD, @MsgHook, 0, GetCurrentThreadID);
end;
procedure TForm1.ListView1Exit(Sender: TObject);
begin
UnHookWindowsHookEx(HookHandle);
end;
Second component with name ListView2 is activate with procedure MsgHook.
function MsgHook(Code: Integer; WParam: WPARAM; LParam: LPARAM): Integer; stdcall;
const
KeyTAB: Integer = 983041;
begin
if (Code = HC_ACTION) then
begin
if LParam = KeyTAB then
begin
frmMain.lsvCCM.SetFocus;
SendMessage(Form1.ListView2.Handle, WM_KEYDOWN, VK_DOWN, 1);
Result := -1;
Exit;
end;
end;
Result := CallNextHookEx(HookHandle, Code, WParam, LParam);
end;
2008. szeptember 11., csütörtök
How to identify detail tables linked to a master table
Problem/Question/Abstract:
How can I retrieve the name of the detail tables of some master table? How can I know if a table has a detail table linked? Is there any property or function in the table or query to get the details they have?
Answer:
One way to identify linked detail tables is to scan the form or data module's components array:
for I := 0 to Pred(Component.Count) do
if Components[I] is TTable then
if TTable(Components[I]).DataSource <> nil then
{ do whatever }
2008. szeptember 10., szerda
List of Exceptions
Problem/Question/Abstract:
List of Exceptions
Answer:
Below you find a list of exception that was automatically generated from the C++ Builder 5 include files, using a Python script.
| Exception
| | AxisException
| | BarException
| | ChartException
| | | PieException
| | DBChartException
| | EAbort
| | EAbstractError
| | EAssertionFailed
| | EBitsError
| | ECommonCalendarError
| | | EDateTimeError
| | | EMonthCalError
| | EComponentError
| | EConvertError
| | EDBEditError
| | EDLLLoadError
| | EDatabaseError
| | | EDBEngineError
| | | ENoResultSet
| | EExternal
| | | EAccessViolation
| | | EControlC
| | | EExternalException
| | | EIntError
| | | | EDivByZero
| | | | EIntOverflow
| | | | ERangeError
| | | EMathError
| | | | EInvalidArgument
| | | | EInvalidOp
| | | | EOverflow
| | | | EUnderflow
| | | | EZeroDivide
| | | EPrivilege
| | | EStackOverflow
| | EHeapException
| | | EInvalidPointer
| | | EOutOfMemory
| | | | EOutOfResources
| | EIBError
| | EInOutError
| | EIntfCastError
| | EInvalidCast
| | EInvalidContainer
| | EInvalidGraphic
| | EInvalidGraphicOperation
| | EInvalidGridOperation
| | EInvalidInsert
| | EInvalidOperation
| | EListError
| | EMCIDeviceError
| | EMenuError
| | EOleCtrlError
| | EOleError
| | | EOleException
| | | EOleSysError
| | | | EOleException
| | | EOleSysError
| | | | EOleException
| | EOleError
| | | EOleException
| | | EOleSysError
| | | | EOleException
| | | EOleSysError
| | | | EOleException
| | EOutlineError
| | EPackageError
| | EParserError
| | EPrinter
| | EPropReadOnly
| | EPropWriteOnly
| | EPropertyError
| | EQRError
| | ERegistryException
| | EResNotFound
| | ESockError
| | | EAbortError
| | ESocketError
| | EStreamError
| | | EFCreateError
| | | EFOpenError
| | | EFilerError
| | | | EClassNotFound
| | | | EInvalidImage
| | | | EMethodNotFound
| | | | EReadError
| | | | EWriteError
| | EStringListError
| | EThread
| | ETreeViewError
| | EVariantError
| | EWin32Error
| | FTPException
| | FormExcept
| | HTTPException
| | LegendException
| | NNTPError
| | UDPSockError
2008. szeptember 9., kedd
How to get the physical caret position in a TMemo, TEdit or TRichEdit
Problem/Question/Abstract:
How to get the caret position of a Memo or RichEdit control? I don't mean any character position expressed in row and column, I mean it in pixels!
Answer:
You get the caret position in pixels (client relative) from an edit, memo or richedit control by sending it a EM_POSFROMCHAR message. The message parameters are different for a TRichEdit and TMemo/ TEdit.
{TRichEdit}
var
pt: TPoint;
begin
with richedit1 do
begin
Perform(messages.EM_POSFROMCHAR, WPARAM(@pt), selstart);
label1.caption := Format('(%d, %d)', [pt.x, pt.y]);
end;
end;
{TMemo and TEdit}
var
r: LongInt;
begin
with memo1 do
begin
r := Perform(messages.EM_POSFROMCHAR, selstart, 0);
if r >= 0 then
begin
label1.caption := IntToStr(HiWord(r));
label2.caption := IntToStr(LoWord(r));
end;
end;
end;
The win32.hlp entries for this message are really messed up, on older versions they only showed the memo variant, on newer (e.g. the one that comes with D5) they show only the richedit variant.
2008. szeptember 8., hétfő
Get FullPath Application of Any Object
Problem/Question/Abstract:
You want get app fullpath of any Object?
Answer:
uses psAPI;
procedure GetAppName(hWindow: THandle; var Buffer: string);
{
hWindow = Handle of the Object that you want know the path.
Buffer = variable that receive the fullpath.
}
var
dPID: dWord;
hHandle: THandle;
begin
GetWindowThreadProcessId(hWindow, @dPID); // Get PID of Object.
SetLength(Buffer, MAX_PATH); // Set Length of Buffer.
hHandle := OpenProcess(// Get Handle of Process.
PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
true,
dPID
);
if GetModuleFileNameEx(// Check and get App FullPath Name.
hHandle,
0,
PChar(Buffer),
MAX_PATH
) > 0 then
SetLength(Buffer, StrLen(PChar(Buffer))); // If not reset Length of Buffer.
end;
2008. szeptember 7., vasárnap
Converting a group of images from a TImageList into one single bitmap
Problem/Question/Abstract:
I have a program that adds images to a TImageList at runtime. What I would like to do is save the images to a file so that it another program can load in the images to its TImageList.
Answer:
The best way to load a TImageList from a resource is to pack all images into one bitmap and load them all in one go. For this you need the bitmap, of course.
So, create a new project, drop a TImageList on the form and add the icons to it at design-time, as usual. Add a handler for the forms OnCreate event and do it like in the little program ImageListConverter below.
The result is a 'strip' bitmap with all images in the list, here saved as 'c:\temp\images.bmp'.
Open this bitmap in MSPaint and save it again under the same name as a 256 or 16 color bitmap, it will usually have a higher color depth since the VCL creates bitmaps with the color depth of your current video mode by default. The 'transparent' color for this bitmap is clOlive, since that is what we filled the bitmap with before painting the images on it transparently.
The next step is to add this bitmap to a resource file and add the resource to your project. You can do that with the image editor as usual or create a RC file and add it to your project group (requires D5). The RC file would contain a line like
IMAGES1 BITMAP c:\temp\images.bmp
You can now load this resource into your projects imagelist with
ImageList2.ResInstLoad(HInstance, rtBitmap, 'IMAGES1', clOlive);
Note that the width and height setting of the imagelist has to be the same as the one you saved the images from, otherwise the bitmap will not be partitioned correctly.
program ImageListConverter;
procedure ImageList2Bitmap(anImageList: TImageList; const sBMPFile: string);
var
Bmp: TBitmap;
i: Integer;
begin
Bmp := TBitmap.Create;
try
Bmp.Width := anImageList.Width * ImageList1.Count;
Bmp.Height := anImageList.Height;
with Bmp.Canvas do
begin
Brush.Color := clOlive;
Brush.Style := bsSolid;
FillRect(ClipRect);
end;
for i := 0 to anImageList.Count - 1 do
anImageList.Draw(Bmp.Canvas, i * anImageList.Width, 0, i);
Bmp.SaveToFile(sBMPFile);
finally
Bmp.Free
end;
end;
begin
ImageList2Bitmap(myImageList1, 'c:\temp\images.bmp');
end.
2008. szeptember 6., szombat
Checking if a Windows feature exists - Hide your app in the Task List
Problem/Question/Abstract:
Some Windows API functions may or may not be present in your Windows version, but detecting the Windows version is not the best way to know if a function is present since it may yield a false negative if the user updated a DLL and the update includes the new function...
Answer:
To check if an API function exists, we have to load the DLL library where it is supposed to reside (calling the API LoadLibrary) and then we have to get the address of the function (calling the API GetProcAddress) which is finally used to call it. If GetProcAddress returns Nil, then the function isn't present, and if it returns a value other than Nil, then the function is present, buy we have to take into account that it isn't necessarily implemented (it may be just a placeholder, and if we call it, we will get the error code ERROR_CALL_NOT_IMPLEMENTED).
In the following example we implement a function called RegisterAsService which tries to call the API RegisterServiceProcess to register/unregister our application as a service. The function returns True if successful.
function RegisterAsService(Active: boolean): boolean;
const
RSP_SIMPLE_SERVICE = 1;
RSP_UNREGISTER_SERVICE = 0;
type
TRegisterServiceProcessFunction =
function(dwProcessID, dwType: Integer): Integer; stdcall;
var
module: HMODULE;
RegServProc: TRegisterServiceProcessFunction;
begin
Result := False;
module := LoadLibrary('KERNEL32.DLL');
if module <> 0 then
try
RegServProc := GetProcAddress(module, 'RegisterServiceProcess');
if Assigned(RegServProc) then
if Active then
Result := RegServProc(0, RSP_SIMPLE_SERVICE) = 1
else
Result := RegServProc(0, RSP_UNREGISTER_SERVICE) = 1;
finally
FreeLibrary(module);
end;
end;
Notice that registering our application as a service has the side-effect of hiding our application in the Task List (in Windows Task Manager).
Sample calls:
procedure TForm1.FormCreate(Sender: TObject);
begin
RegisterAsService(true);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
RegisterAsService(false);
end;
I guess this works only for win 9X-Me is there any way a similar move may be made under win NT/2000?
I believe for Windows NT/2000, the application must be designed to be a Windows NT/2000 service...
Perhaps you can try running your EXE with SRVANY.EXE, which runs any application as a service. Of course apps run this way won't be able to take advantage of the special operating system features available to services, but for the sake of hiding your app in the Task List, maybe it's enough, I don't know...
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2008. szeptember 5., péntek
How to add all paper bins of an active printer to a TListBox
Problem/Question/Abstract:
How is it possible to add all paperbins from the active printer in a listbox (like in the PrinterSetupDialog)?
Answer:
uses
WinSpool;
procedure GetBinnames(sl: TStrings);
type
TBinName = array[0..23] of Char;
TBinNameArray = array[1..High(Integer) div Sizeof(TBinName)] of TBinName;
PBinnameArray = ^TBinNameArray;
TBinArray = array[1..High(Integer) div Sizeof(Word)] of Word;
PBinArray = ^TBinArray;
var
Device, Driver, Port: array[0..255] of Char;
hDevMode: THandle;
i, numBinNames, numBins, temp: Integer;
pBinNames: PBinnameArray;
pBins: PBinArray;
begin
Printer.PrinterIndex := -1;
Printer.GetPrinter(Device, Driver, Port, hDevmode);
numBinNames := WinSpool.DeviceCapabilities(Device, Port, DC_BINNAMES, nil, nil);
numBins := WinSpool.DeviceCapabilities(Device, Port, DC_BINS, nil, nil);
if numBins <> numBinNames then
begin
raise Exception.Create('DeviceCapabilities reports different number of bins and ' + 'bin names!');
end;
if numBinNames > 0 then
begin
pBins := nil;
GetMem(pBinNames, numBinNames * Sizeof(TBinname));
GetMem(pBins, numBins * Sizeof(Word));
try
WinSpool.DeviceCapabilities(Device, Port, DC_BINNAMES, Pchar(pBinNames), nil);
WinSpool.DeviceCapabilities(Device, Port, DC_BINS, Pchar(pBins), nil);
sl.clear;
for i := 1 to numBinNames do
begin
temp := pBins^[i];
sl.addObject(pBinNames^[i], TObject(temp));
end;
finally
FreeMem(pBinNames);
if pBins <> nil then
FreeMem(pBins);
end;
end;
end;
Called like this:
GetBinnames(listbox1.items);
2008. szeptember 4., csütörtök
How to define a custom event
Problem/Question/Abstract:
Does anyone have a good example for creating a custom event? I want a TDataModule subclass to fire a custom event, and then a TForm subclass to detect this event and perform some action.
Answer:
First you define the event and create an event property on the datamodule to refer to this event:
TCustomEvent = procedure(Sender: TObject; Var1: integer; Var2: string) of object;
TMyDataModule = class(TDataModule)
private
{ Private declarations }
FCustomEvent: TCustomEvent;
public
{ Public declarations }
property CustomEvent: TCustomEvent read FCustomEvent write FCustomEvent;
end;
Next, in some event handler on the form (like the OnCreate) you create a method that matches the event definition and then you programmatically assign it to the datamodule's event property:
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure CustomEvent(Sender: TObject; Var1: Integer; Var2: string);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.DFM}
procedure TForm1.CustomEvent(Sender: TObject; Var1: Integer; Var2: string);
begin
ShowMessage('Hello');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyDataModule.CustomEvent := CustomEvent;
end;
end.
2008. szeptember 3., szerda
Comment out large amount of source code
Problem/Question/Abstract:
How to comment out large amount of source code
Answer:
You can use { } or (* *) or // [Delphi 2.x+] to comment out code in Delphi. But, if you have a large amount of code that you want to comment out, none of the above operators may help, depending on whether you have already used those operators in your code. Here's a definitive way to comment out code:
{$IFDEF False}
// your commented code goes here
{$ENDIF}
2008. szeptember 2., kedd
How to XOR a font on a bitmap canvas
Problem/Question/Abstract:
How to XOR a font on a bitmap canvas
Answer:
Write the text to a (temporary) bitmap and copyrect that to the image:
procedure XORText(sheet: TCanvas; x, y: Integer; text: string);
var
bmp: TBitmap;
r1, r2: TRect;
begin
bmp := TBitmap.create;
try
with sheet do
begin
bmp.Width := textWidth(text);
bmp.height := textheight(text);
r1 := rect(0, 0, bmp.Width, bmp.Height);
r2 := Rect(x, y, x + bmp.Width, y + bmp.Height);
bmp.canvas.font.assign(font);
bmp.canvas.brush.color := clBlack;
bmp.Canvas.fillrect(r1);
bmp.canvas.brush.style := bsClear;
bmp.canvas.textout(0, 0, text);
copymode := cmSrcInvert;
copyrect(r2, bmp.canvas, r1);
end;
finally
bmp.free;
end;
end;
2008. szeptember 1., hétfő
Migration InterBase 5.5 to 6.0
Problem/Question/Abstract:
A certain stored procedure caused me to get this error message:
ISC ERROR CODE: 335544321
ISC ERROR MESSAGE:
arithmetic exception, numeric overflow, or string truncation
Answer:
I found that a variable of type char(18) was assigned to another variable of type char(10). Since the data was never (?) longer than 8 characters, this worked fine up to version 5.5. Seems that IB 6.0 handles strings different. It is likely that a delcaration as VARCHAR instead of CHAR would help also. (See part 1 below)
Another necessary change was a type cast where IB 5.5 did an implicit conversion.
// part 1
declare variable v1 char(10);
declare variable v2 char(18);
..
v1 = v2; // generates the error in IB 6.0
// part 2
declare variable vchardate char(18);
declare variable vdatedate date;
vchardate = '1996-Jan-15';
vdatedate = vchardate; // generates error in IB 6.0
vdatedate = cast(vchardate as DATE);
Feliratkozás:
Bejegyzések (Atom)