2009. március 31., kedd
How to check if Active Desktop is enabled
Problem/Question/Abstract:
How to check if Active Desktop is enabled
Answer:
uses
ComObj, ShlObj, ActiveX;
{Check if Active Desktop is enabled - Option 1}
function IsActiveDeskTopOn: Boolean;
var
h: HWND;
begin
h := FindWindow('Progman', nil);
h := FindWindowEx(h, 0, 'SHELLDLL_DefView', nil);
h := FindWindowEx(h, 0, 'Internet Explorer_Server', nil);
Result := h <> 0;
end;
{Check if Active Desktop is enabled - Option 2}
function IsActiveDesktopEnable: Boolean;
const
CLSID_ActiveDesktop: TGUID = '{75048700-EF1F-11D0-9888-006097DEACF9}';
var
ActiveDesk: IActiveDesktop;
ComponentsOpt: TComponentsOpt;
hr: HRESULT;
dwReserved: DWORD;
begin
ZeroMemory(@ComponentsOpt, SizeOf(TComponentsOpt));
ComponentsOpt.dwSize := SizeOf(TComponentsOpt);
hr := CoCreateInstance(CLSID_ActiveDesktop, nil, CLSCTX_INPROC_SERVER,
CLSID_ActiveDesktop, ActiveDesk);
if SUCCEEDED(hr) then
begin
hr := ActiveDesk.GetDesktopItemOptions(ComponentsOpt, dwReserved);
{ActiveDesk._Release;}
end;
Result := ComponentsOpt.fActiveDesktop;
end;
And here is how to activate the Active Desktop:
procedure TForm1.Button1Click(Sender: TObject);
const
CLSID_ActiveDesktop: TGUID = '{75048700-EF1F-11D0-9888-006097DEACF9}';
var
ActiveDesk: IActiveDesktop;
ComponentsOpt: TComponentsOpt;
begin
ActiveDesk := CreateComObject(CLSID_ActiveDesktop) as IActiveDesktop;
with ActiveDesk do
begin
ComponentsOpt.dwSize := SizeOf(ComponentsOpt);
GetDesktopItemOptions(ComponentsOpt, 0);
ComponentsOpt.fActiveDesktop := True;
SetDesktopItemOptions(ComponentsOpt, 0);
ApplyChanges(AD_APPLY_ALL);
end;
end;
2009. március 30., hétfő
How to print a TScrollBox that contains controls generated at runtime
Problem/Question/Abstract:
How to print a TScrollBox that contains controls generated at runtime
Answer:
If this is some kind of custom control you developed yourself teach it to print itself. In fact it may be able to do that already using the PaintTo method. The main problem here is scaling. If your control uses device units (pixels) as measures instead of some device-independent unit like mm or inches you will need to scale the printer.canvas before you pass it to a controls PaintTo method. Scaling the printer canvas to the screen resolution is pretty straightforward. Here is an older example that you can tailor to your needs.
Print all of a forms client area, even if parts are not visible. The form will clip the output to the visible area if you try to output it to a canvas using using the forms paintto method. But one can print the controls on it individually and that is not clipped:
procedure TForm1.Button1Click(Sender: TObject);
var
c: TControl;
i: Integer;
topX, topY: Integer;
begin
printer.begindoc;
try
{ Scale printer to screen resolution. }
SetMapMode(printer.canvas.handle, MM_ANISOTROPIC);
SetWindowExtEx(printer.canvas.handle, GetDeviceCaps(canvas.handle, LOGPIXELSX),
GetDeviceCaps(canvas.handle, LOGPIXELSY), nil);
SetViewportExtEx(printer.canvas.handle, GetDeviceCaps(printer.canvas.handle, LOGPIXELSX),
GetDeviceCaps(printer.canvas.handle, LOGPIXELSY), nil);
topX := 10;
topY := 10;
for i := 0 to controlcount - 1 do
begin
c := controls[i];
if c is TWinControl then
TWinControl(c).paintto(printer.canvas.handle, c.left + topX, c.top + topy);
end;
finally
printer.enddoc;
end;
end;
The problem here is that this only prints TWinControl descendents, if you have TLabels or TImages on the form they are not printed. The solution is to put everything on the form onto a single top level TPanel. This panel is *not* aligned to alClient, it has its left and top set to 0 and its width and height is such that all controls fit on it. The code above then prints this panel unclipped and the panel prints any non-TWinControls on it.
The usual caveats for PaintTo apply: not all controls will implement this method properly (a Windows limitation). Bitmaps on the form may not appear on the printer if the printer is not able to print device-dependent bitmaps for the screen. It may be advisable to first paint the form to a properly sized tBitmaps canvas (you can omit all the scaling stuff for that since the bitmap resolution is the same as the screens) and then print the bitmap as a device independent bitmap using StretchDIBits.
2009. március 29., vasárnap
How to change property values (RTTI)
Problem/Question/Abstract:
I use setpropvalue() function to set property values. It works fine if I set properties that are on first level (i.e.. button1.caption, button1.name,..), but it fails if I want to set properties like button1.font.name or radiogroup.items.text.
Answer:
function GetProperty(AControl: TPersistent; AProperty: string): PPropInfo;
var
i: Integer;
props: PPropList;
typeData: PTypeData;
begin
Result := nil;
if (AControl = nil) or (AControl.ClassInfo = nil) then
Exit;
typeData := GetTypeData(AControl.ClassInfo);
if (typeData = nil) or (typeData^.PropCount = 0) then
Exit;
GetMem(props, typeData^.PropCount * SizeOf(Pointer));
try
GetPropInfos(AControl.ClassInfo, props);
for i := 0 to typeData^.PropCount - 1 do
begin
with Props^[i]^ do
if (Name = AProperty) then
result := Props^[i];
end;
finally
FreeMem(props);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
propInfo: PPropInfo;
begin
PropInfo := GetProperty(Button1.Font, 'Name');
if PropInfo <> nil then
SetStrProp(Button1.Font, PropInfo, 'Arial');
end;
2009. március 28., szombat
How to communicate with a com port through RS232
Problem/Question/Abstract:
I want to develop a device that communicates through RS232 with the Com1 port. I know the port is connected to IRQ4 and I know the IO address of the 8250 status and data registers. I know how to do this mission in DOS (interrupt vector), but what I do not know is how to do something like this with Window 98 system and Delphi as a programming platform.
Answer:
The DOS solution is not recommended and will not work under NT anyway. The following unit has a class for the RS232 communication:
unit ComPort;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TComPort = class(TObject)
private
ComID, ComError: Integer;
DcbOld: TDCB;
CommTimeoutsOld: TCommTimeouts;
protected
public
function Open(PortNo: integer): boolean;
procedure Close;
function Config(Baudrate: DWORD; ByteSize, StopBits, Parity: Byte): boolean;
function ReadBlock(var Ch: array of char; BlockSize: dword): integer;
function ReadChar(var Ch: char): boolean;
function WriteBlock(var Ch: array of char; BlockSize: dword): boolean;
function WriteChar(Ch: char): boolean;
procedure Purge;
function Error: integer;
constructor Create;
published
end;
const
cpReadError = 1;
cpWriteError = 2;
cpOpenError = 3;
implementation
constructor TComPort.Create;
begin
inherited Create;
ComID := -1;
end;
function TComPort.Open(PortNo: integer): boolean;
var
CommTimeouts: TCommTimeouts;
Port: string;
begin
Port := 'COM' + IntToStr(PortNo);
ComID := CreateFile(pChar(Port), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);
if ComID <> -1 then
begin
GetCommState(ComID, DcbOld);
GetCommTimeouts(ComID, CommTimeoutsOld);
CommTimeouts.ReadIntervalTimeout := 1;
CommTimeouts.ReadTotalTimeoutMultiplier := 1;
CommTimeouts.ReadTotalTimeoutConstant := 1;
CommTimeouts.WriteTotalTimeoutMultiplier := 10;
CommTimeouts.WriteTotalTimeoutConstant := 10;
SetCommTimeouts(ComID, CommTimeouts);
ComError := 0;
end
else
ComError := cpOpenError;
Result := (ComID <> -1)
end;
procedure TComPort.Close;
begin
if ComID <> -1 then
begin
SetCommState(ComID, DcbOld);
SetCommTimeouts(ComID, CommTimeoutsOld);
CloseHandle(ComID);
end;
ComID := -1;
end;
function TComPort.Config(Baudrate: DWORD; ByteSize, StopBits, Parity: Byte): boolean;
var
Dcb: TDCB;
begin
if ComID <> -1 then
begin
GetCommState(ComID, Dcb);
Dcb.Baudrate := Baudrate;
Dcb.ByteSize := ByteSize;
Dcb.StopBits := StopBits;
Dcb.Parity := Parity;
SetCommState(ComID, Dcb);
end
else
ComError := cpOpenError;
Result := (ComID <> -1)
end;
function TComPort.ReadBlock(var Ch: array of char; BlockSize: dword): integer;
var
rdBlockSize: dword;
begin
Result := 0;
if ComID <> -1 then
begin
rdBlockSize := BlockSize;
if not ReadFile(ComID, Ch, BlockSize, rdBlockSize, nil) then
begin
GetLastError;
ComError := cpReadError;
end
else
Result := rdBlockSize;
end
else
ComError := cpOpenError;
end;
function TComPort.ReadChar(var Ch: char): boolean;
var
BlockSize: dword;
begin
Result := False;
if ComID <> -1 then
begin
if not ReadFile(ComID, Ch, 1, BlockSize, nil) then
begin
GetLastError;
ComError := cpReadError;
end
else
Result := (BlockSize = 1);
end
else
ComError := cpOpenError;
end;
function TComPort.WriteBlock(var Ch: array of char; BlockSize: dword): boolean;
var
W: dword;
begin
Result := False;
if ComID <> -1 then
begin
if not WriteFile(ComID, Ch, BlockSize, W, nil) then
begin
GetLastError;
ComError := cpWriteError;
end
else
Result := (BlockSize = W)
end
else
ComError := cpOpenError;
end;
function TComPort.WriteChar(Ch: char): boolean;
var
W: dword;
begin
Result := False;
if ComID <> -1 then
begin
if not WriteFile(ComID, Ch, 1, W, nil) then
begin
GetLastError;
ComError := cpWriteError;
end
else
Result := (W = 1)
end
else
ComError := cpOpenError;
end;
procedure TComPort.Purge;
begin
if ComID <> -1 then
begin
PurgeComm(ComID, Purge_TXABORT);
PurgeComm(ComID, Purge_RXABORT);
PurgeComm(ComID, Purge_TXCLEAR);
PurgeComm(ComID, Purge_RXCLEAR);
end
else
ComError := cpOpenError;
end;
function TComPort.Error: integer;
begin
Result := ComError;
ComError := 0;
end;
end.
And this is how you use this class:
{ ... }
var
ComPort: TComPort;
In the Form1.OnCreate event:
ComPort := TComPort.Create;
ComPort.Open(1); {for COM1}
So now you can use ComPort.Config (see in Win32 API SetCommState for the Config parameter)
ComPort.WriteBlock
ComPort.WriteChar
ComPort.ReadBlock
ComPort.ReadChar
etc.
In the Form1.OnClose event:
ComPort.Close;
ComPort.Free;
2009. március 27., péntek
Hiding/showing the Windows taskbar
Problem/Question/Abstract:
Hiding/showing the Windows taskbar
Answer:
Use these functions to hide or show the Windows taskbar programmatically from your Delphi application:
procedure hideTaskbar;
var
wndHandle: THandle;
wndClass: array[0..50] of Char;
begin
StrPCopy(@wndClass[0], 'Shell_TrayWnd');
wndHandle := FindWindow(@wndClass[0], nil);
ShowWindow(wndHandle, SW_HIDE); // This hides the taskbar
end;
procedure showTaskbar;
var
wndHandle: THandle;
wndClass: array[0..50] of Char;
begin
StrPCopy(@wndClass[0], 'Shell_TrayWnd');
wndHandle := FindWindow(@wndClass[0], nil);
ShowWindow(wndHandle, SW_RESTORE); // This restores the taskbar
end;
2009. március 26., csütörtök
How can i reboot my windows
Problem/Question/Abstract:
How can i reboot my windows?
Answer:
To Reboot your computer you just have to insert the next code:
ExitWindowsEx(EWX_FORCE and EWX_REBOOT);
This line of code while cause a LogOff in Windows 2000 and Windows XP.
If you need to ShutDown the line of code is:
ExitWindowsEx(EWX_FORCE and EWX_SHUTDOWN);
For more informaction about this, just write ExitWindowsEx in your program and then press F1 and will appear the help related with this function.
2009. március 25., szerda
Convert a Delphi form (from file) to text and vice versa
Problem/Question/Abstract:
Convert your Delphi form from .dfm format to text and vice versa
Answer:
use this function to convert:
Example (DFM->TXT): ConvertFormToText('unit1.dfm');
Example (TXT->DFM): ConvertTextToForm('unit1.txt');
uses
SysUtils;
function ConvertFormToText(SourceFileName: string): boolean;
var
InputStream, OutputStream: TFileStream;
DestFileName: string;
begin
result := true;
{ change the file extension to .txt }
DestFileName := ChangeFileExt(SourceFileName, '.txt');
{ Create a file stream for the specified file }
InputStream := TFileStream.Create(SourceFileName, fmOpenRead);
OutputStream := TFileStream.Create(DestFileName, fmCreate);
{ convert }
try
try
ObjectResourceToText(InputStream, OutputStream);
except
on EStreamError do
Result := False;
end
finally
{ free memory }
InputStream.Free;
OutputStream.Free;
end;
end;
function ConvertTextToForm(SourceFileName: string): boolean;
var
InputStream, OutputStream: TFileStream;
DestFileName: string;
begin
result := true;
DestFileName := ChangeFileExt(SourceFileName, '.dfm');
InputStream := TFileStream.Create(SourceFileName, fmOpenRead);
OutputStream := TFileStream.Create(DestFileName, fmCreate);
try
try
ObjectTextToResource(InputStream, OutputStream);
except
on EStreamError do
result := false;
end
finally
InputStream.Free;
OutputStream.Free;
end;
end;
2009. március 24., kedd
How to implement string pattern matching with wildcards
Problem/Question/Abstract:
How to implement string pattern matching with wildcards
Answer:
There are many times when you need to compare two strings, but want to use wild cards in the match - all last names that begin with 'St', etc.
This function takes two strings and compares them. The first string can be anything, but should not contain pattern characters (* or ?). The pattern string can have as many of these pattern characters as you want. For example: MatchStrings('David Stidolph','*St*') would return True.}
function MatchStrings(source, pattern: string): Boolean;
var
pSource: array[0..255] of Char;
pPattern: array[0..255] of Char;
function MatchPattern(element, pattern: PChar): Boolean;
function IsPatternWild(pattern: PChar): Boolean;
var
t: Integer;
begin
Result := StrScan(pattern, ' * ') <> nil;
if not Result then
Result := StrScan(pattern, ' ? ') <> nil;
end;
begin
if 0 = StrComp(pattern, ' * ') then
Result := True
else if (element^ = Chr(0)) and (pattern^ <> Chr(0)) then
Result := False
else if element^ = Chr(0) then
Result := True
else
begin
case pattern^ of
' * ':
if MatchPattern(element, @pattern[1]) then
Result := True
else
Result := MatchPattern(@element[1], pattern);
' ? ':
Result := MatchPattern(@element[1], @pattern[1]);
else
if element^ = pattern^ then
Result := MatchPattern(@element[1], @pattern[1])
else
Result := False;
end;
end;
end;
begin
StrPCopy(pSource, source);
StrPCopy(pPattern, pattern);
Result := MatchPattern(pSource, pPattern);
end;
2009. március 23., hétfő
How to map a variant OLEObject to an interface
Problem/Question/Abstract:
There seems to be no way to map the variant OLEObject to an interface (in our case Word 2000) or even cast it so at design time.
Answer:
Yes there is. The OleContainer's OleObject property holds the document as an IDispatch, and you can just cast to the interface you want. For example:
{ ... }
Doc: _Document;
{ ... }
OleContainer1.CreateObjectFromFile(Path, False);
OleContainer1.DoVerb(ovShow);
Doc := IDispatch(OleContainer1.OleObject) as _Document;
2009. március 22., vasárnap
How to detect the regional settings of a system
Problem/Question/Abstract:
How to detect the regional settings of a system
Answer:
Here is some sample code to get the language's abbreviated name, e.g.: ENU.
{ ... }
var
Buffer: PChar;
Size: integer;
begin
Size := GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, nil, 0);
GetMem(Buffer, Size);
try
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, Buffer, Size);
Result := string(Buffer);
finally
FreeMem(Buffer);
end;
end;
This code gets the current Currency Symbol from Windows' regional settings:
function GetCurrencySymbol: string;
var
Res: Cardinal;
begin
Res := GetLocaleInfo(GetUserDefaultLCID, LOCALE_SCURRENCY, nil, 0);
SetLength(Result, Res);
Res := GetLocaleInfo(GetUserDefaultLCID, LOCALE_SMONDECIMALSEP, PChar(Result), Res);
if Res = 0 then
RaiseLastOSError;
end;
2009. március 21., szombat
Paint a TBitmap in disabled state
Problem/Question/Abstract:
How can I make an image to appear enabled and disabled (i.e going from original to disabled-grey and back)? I know that TImage doesn't support this feature, so how would I implement this?
Answer:
Everyone from you saw that standard TSpeedButton allow to show a loaded glyph in "disabled" state when your original glyph will be converted into gray-scheme.
Sometimes to create similar bitmap is useful not only for TSpeedButton.
You can use the next my CreateDisabledBitmap procedure where such "disabled" bitmap (Destination parameter) will be created from your original bitmap (Source).
procedure CreateDisabledBitmap(Source, Destination: TBitmap);
const
ROP_DSPDxax = $00E20746;
var
DDB, MonoBmp: TBitmap;
IWidth, IHeight: Integer;
IRect: TRect;
begin
IWidth := Source.Width;
IHeight := Source.Height;
Destination.Width := IWidth;
Destination.Height := IHeight;
IRect := Rect(0, 0, IWidth, IHeight);
Destination.Canvas.Brush.Color := clBtnFace;
Destination.Palette := CopyPalette(Source.Palette);
MonoBmp := nil;
DDB := nil;
try
MonoBmp := TBitmap.Create;
DDB := TBitmap.Create;
DDB.Assign(Source);
DDB.HandleType := bmDDB;
{ Create a disabled version }
with MonoBmp do
begin
Assign(Source);
HandleType := bmDDB;
Canvas.Brush.Color := clBlack;
Width := IWidth;
if Monochrome then
begin
Canvas.Font.Color := clWhite;
Monochrome := False;
Canvas.Brush.Color := clWhite;
end;
Monochrome := True;
end;
with Destination.Canvas do
begin
Brush.Color := clBtnFace;
FillRect(IRect);
Brush.Color := clBtnHighlight;
SetTextColor(Handle, clBlack);
SetBkColor(Handle, clWhite);
BitBlt(Handle, 1, 1, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
Brush.Color := clBtnShadow;
SetTextColor(Handle, clBlack);
SetBkColor(Handle, clWhite);
BitBlt(Handle, 0, 0, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
end;
finally
DDB.Free;
MonoBmp.Free;
end;
Source.Dormant;
end;
Sample of use:
procedure TfrmMain.ButtonClick(Sender: TObject);
var
Destination: TBitmap;
begin
Destination := TBitmap.Create;
try
CreateDisabledBitmap(Image1.Picture.Bitmap, Destination);
Image2.Picture.Bitmap.Assign(Destination);
finally
Destination.Free
end
end;
where Image1 is TImage where you have an original bitmap and TImage2 will a container for created disabled bitmap.
2009. március 20., péntek
How to play system sounds
Problem/Question/Abstract:
Is there any way to play system default sounds (like MessageBeep(mb_IconError) or mb_IconQuestion or these)? I need a way to play all the default sounds set in the system control (e.g. "new mail", "start windows" etc.).
Answer:
If you want to play the sound associated with "Empty Recycle Bin" then call the following procedure. Using the Key Name -> PlayRegisteredSound("EmptyRecycleBin") and Window's takes care of the rest.
procedure PlayRegisteredSound(SoundKeyName: string);
begin
{ call win32 api procedure PlaySound() }
PlaySound(PChar(SoundKeyName), 0, SND_APPLICATION or SND_NODEFAULT or
SND_ASYNC or SND_NOWAIT);
end;
Follow the RegKey by examining the keys beneath HKEY_CURRENT_USER\AppEvents\Schemes. Here you'll find where the wav files are registered and changed by the Sounds applet in the Control Panel.
2009. március 19., csütörtök
Copy a file with or without a progressbar
Problem/Question/Abstract:
How to COPY a file with or without a progressbar
Answer:
function FileCopy(const SourceFile, TargetFile: string): Boolean; overload;
function FileCopy(const SourceFile, TargetFile: string; PB: TProgressBar): Boolean;
overload;
function FileCopy(const SourceFile, TargetFile: string): Boolean;
begin
Result := FileCopy(SourceFile, TargetFile, nil);
end;
function FileCopy(const SourceFile, TargetFile: string; PB: TProgressBar):
Boolean;
const
BlockSize = 1024 * 16;
var
FSource, FTarget: Integer;
BRead, Bwrite: Word;
Buffer: Pointer;
begin
Result := False;
FSource := FileOpen(SourceFile, fmOpenRead + fmShareDenyNone); { Open Source }
if FSource >= 0 then
try
if Assigned(PB) then
begin
PB.Position := 0;
pb.Min := 0;
pb.Max := (FileSeek(FSource, 0, 2));
if (pb.Max > 2048) then
pb.Step := pb.Max div 2048
else
pb.Step := pb.Max;
FileSeek(FSource, 0, 0);
end;
FTarget := FileCreate(TargetFile); { Open Target }
try
getmem(Buffer, BlockSize);
try
FileSeek(FSource, 0, soFromBeginning);
repeat
BRead := FileRead(FSource, Buffer^, BlockSize);
if assigned(PB) then
PB.StepIt;
BWrite := FileWrite(FTarget, Buffer^, Bread);
if assigned(PB) then
PB.StepIt;
until (Bread = 0) or (Bread <> BWrite);
if Bread = Bwrite then
begin
Result := True;
if assigned(PB) then
PB.Position := PB.Max;
end;
finally
freemem(Buffer, BlockSize);
end;
FileSetDate(FTarget, FileGetDate(FSource));
finally
FileClose(FTarget);
end;
finally
FileClose(FSource);
end;
end;
2009. március 18., szerda
How to reach a graphic field without using a TDBImage
Problem/Question/Abstract:
How to reach a graphic field without using a TDBImage
Answer:
var
Pic: TPicture;
begin
Pic := TPicture.Create;
dm.tbDeviceTypes.first;
while not dm.tbDeviceTypes.EOF do
begin
Pic.Assign(dm.tbDeviceTypesFreePic);
ImageList1.AddMasked(Pic.BitMap, ClWhite);
dm.tbDeviceTypes.next;
end;
end;
2009. március 17., kedd
How can I set the bar color of a TProgressbar?
Problem/Question/Abstract:
This code will show you, how you can set the bar color of a TProgressbar.
Answer:
procedure SetBarColor(Component: TProgressBar; Color: TColor);
begin
SendMessage(Component.Handle, 1033, 0, Color);
end;
2009. március 16., hétfő
How to retrieve all available TBrushStyle values as a list of strings
Problem/Question/Abstract:
I need to get a list of strings (like a StringList) with the possible values for a TBrushStyle property (bsSolid, bsClear, bsHorizontal, for example). I want to build a ComboBox with this options. How can I set the property Items of my ComboBox directly with all the values from the enumerated type TBrushStyle? My ComboBox will be alike the Property Editor for this type.
Answer:
You can use runtime type information (RTTI) to do that. Below is an example:
uses
{...}, TypInfo
procedure BrushStylesAsStrings(AList: TStrings);
var
a: integer;
pInfo: PTypeInfo;
pEnum: PTypeData;
begin
AList.Clear;
pInfo := PTypeInfo(TypeInfo(TBrushStyle));
pEnum := GetTypeData(pInfo);
with pEnum^ do
begin
for a := MinValue to MaxValue do
AList.Add(GetEnumName(pInfo, a));
end;
end;
2009. március 15., vasárnap
Read the content of an Excel object embedded in a Word document
Problem/Question/Abstract:
I have an Excel object in a Word document. I want to read the content of the Excel object from my Delphi program. How can I do that?
Answer:
{ ... }
var
AWordApplication: WordApplication;
AWordDocument: WordDocument;
AWorkBook: ExcelWorkBook;
AWorkSheet: ExcelWorkSheet;
AInlineShape: InlineShape;
AFileName: OleVariant;
TrueParam: OleVariant;
begin
AWordApplication := CoWordApplication.Create;
try
FalseParam := False;
AFileName := 'c:\wordexcel.doc';
AWordDocument := AWordApplication.Documents.Open(AFileName, EmptyParam,
EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam);
AInlineShape := AWordDocument.InlineShapes.Item(1);
AInlineShape.Activate;
AWorkBook := AWordDocument.InlineShapes.Item(1).OLEFormat.Object_ as
ExcelWorkBook;
AWorkSheet := AWorkBook.ActiveSheet as ExcelWorkSheet;
ShowMessage(AWorkSheet.Cells.Item[2, 1].Text);
finally
AWordApplication.Quit(FalseParam, EmptyParam, EmptyParam);
AWordApplication := nil;
AWordDocument := nil;
end;
end;
2009. március 14., szombat
How to check if a drive is ready
Problem/Question/Abstract:
How can I check if there is a disk in the "A" drive without an error message box telling you that it is not ready?
Answer:
The following function accepts a drive letter as a parameter, and it will return a boolean value that indicates whether or not there is a disk in the drive.
function DiskInDrive(Drive: Char): Boolean;
var
ErrorMode: word;
begin
{make it upper case}
if Drive in ['a'..'z'] then
Dec(Drive, $20);
{make sure it's a letter}
if not (Drive in ['A'..'Z']) then
raise EConvertError.Create('Not a valid drive ID');
{turn off critical errors}
ErrorMode := SetErrorMode(SEM_FailCriticalErrors);
try
{ drive 1 = a, 2 = b, 3 = c, etc.}
if DiskSize(Ord(Drive) - $40) = -1 then
Result := False
else
Result := True;
finally
{restore old error mode}
SetErrorMode(ErrorMode);
end;
end;
2009. március 13., péntek
The future of the BDE
Problem/Question/Abstract:
After Borland's official announcement regarding the future of the BDE, I contacted (and was contacted by) many Delphi developers who are currently using the BDE to learn about their future plans regarding data access...
Answer:
After Borland's official announcement regarding the future of the BDE, I contacted (and was contacted by) many Delphi developers who are currently using the BDE to learn about their future plans regarding data access.
For local databases, the BDE will keep being used, although a discrete minority are seriously considering switching to a BDE alternative in the short term (mainly Interbase accessed thru IBX or dbExpress, and third-party data access components).
For server databases, the scenario changes radically. Among those who are still using previous Delphi versions, many are not likely to upgrade, so they'll keep using the BDE + SQL Links all they can, while almost all the rest are considering mainly dbExpress, ADO and ADO.Net, but developers showed their concern about these alternatives:
dbExpress is not as "universal" as SQL Links, meaning there are missing drivers for some important database servers (like Microsoft SQL Server). About dbExpress being faster than the BDE, this is not true for small queries beause there's no caching mechanism (since there is no front layer like the BDE), so the metadata gets downloaded in every query. I'd like to credit Vasilis Devletoglou for sharing his findings about the inner workings of dbExpress with us. Finally, when one used a technology for many years, sometimes it's a bit difficult not be a bit conservative and consider new technologies as "beta". We all know dbExpress arrived here to stay, but many developers percieve it's still "green" and needs further development.
ADO and ADO.Net don't conform the expectations of Delphi programmers in performance and/or features, and it can't be ignored the fact that most programmers would rather prefer to use a Borland solution.
In conclusion, the only ones that are happy here seem to be those who switched to a BDE alternative some time ago... :-)
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2009. március 12., csütörtök
How to create a TPanel with scrollbars
Problem/Question/Abstract:
I want to create a component that has scrollbars (vertical/ horizontal). I tried to get the tricks from TCustomGrid but it doesn't work when I try to set a range/ position value to one of the scrollbars.
Answer:
This example uses an interposer class for convenience (mine, I just wanted to avoid the hassle of creating and installing a proper component for this example) but you should be able to adapt it for a proper component.
{ Example for fitting a panel with scrollbars }
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls, ExtCtrls;
type
TPanel = class(Extctrls.TPanel)
private
procedure WMVScroll(var msg: TWMSCROLL); message WM_VSCROLL;
procedure WMHScroll(var msg: TWMSCROLL); message WM_HSCROLL;
procedure WMGetDlgCode(var msg: TWMGetDlgCode); message WM_GETDLGCODE;
procedure HandleScrollbar(var msg: TWMSCROLL; bar: Integer);
protected
procedure CreateParams(var params: TCreateParams); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
end;
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TPanel }
procedure TPanel.CreateParams(var params: TCreateParams);
begin
inherited;
params.Style := params.Style or WS_VSCROLL or WS_HSCROLL;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
si: TScrollInfo;
begin
si.cbSize := Sizeof(TScrollInfo);
si.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
si.nMin := 0;
si.nMax := 3 * panel1.clientheight;
si.nPage := panel1.clientheight div 2;
si.nPos := 0;
SetScrollInfo(panel1.handle, SB_VERT, si, true);
si.nMax := 2 * panel1.clientwidth;
si.nPage := panel1.clientwidth div 2;
SetScrollInfo(panel1.handle, SB_HORZ, si, true);
end;
procedure TPanel.HandleScrollbar(var msg: TWMSCROLL; bar: Integer);
var
si: TScrollInfo;
begin
msg.result := 0;
si.cbSize := Sizeof(TscrollInfo);
si.fMask := SIF_ALL;
GetScrollInfo(Handle, bar, si);
si.fMask := SIF_POS;
{ For simplicities sake we use 1/10 of the page size as small scroll
increment and the page size as large scroll increment }
case msg.ScrollCode of
SB_TOP: si.nPos := si.nMin;
SB_BOTTOM: si.nPos := si.nMax;
SB_LINEUP: Dec(si.nPos, si.nPage div 10);
SB_LINEDOWN: Inc(si.nPos, si.nPage div 10);
SB_PAGEUP: Dec(si.nPos, si.nPage);
SB_PAGEDOWN: Inc(si.nPos, si.nPage);
SB_THUMBTRACK, SB_THUMBPOSITION: si.nPos := msg.Pos;
SB_ENDSCROLL: Exit;
end;
si.fMask := SIF_POS;
if si.nPos < si.nMin then
si.nPos := si.nMin;
if si.nPos > si.nMax then
si.nPos := si.nMax;
SetScrollInfo(Handle, bar, si, true);
{ Fire a scroll notification off here to allow client to scroll content of panel }
end;
procedure TPanel.KeyDown(var Key: Word; Shift: TShiftState);
procedure Scroll(scrollcode, message: Cardinal);
begin
Perform(message, scrollcode, 0);
end;
const
scrollkind: array[Boolean] of Cardinal = (WM_VSCROLL, WM_HSCROLL);
begin
inherited;
{ Ignoring shift state for arrow keys here for simplicities sake }
case Key of
VK_UP: Scroll(SB_LINEUP, WM_VSCROLL);
VK_LEFT: Scroll(SB_LINEUP, WM_HSCROLL);
VK_DOWN: Scroll(SB_LINEDOWN, WM_VSCROLL);
VK_RIGHT: Scroll(SB_LINEDOWN, WM_HSCROLL);
VK_NEXT: Scroll(SB_PAGEDOWN, scrollkind[ssCtrl in Shift]);
VK_PRIOR: Scroll(SB_PAGEUP, scrollkind[ssCtrl in Shift]);
VK_HOME: Scroll(SB_TOP, scrollkind[ssCtrl in Shift]);
VK_END: Scroll(SB_BOTTOM, scrollkind[ssCtrl in Shift]);
end;
Key := 0;
end;
procedure TPanel.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited;
if (Button = mbLeft) and CanFocus and not Focused then
SetFocus;
end;
procedure TPanel.WMGetDlgCode(var msg: TWMGetDlgCode);
begin
msg.result := DLGC_WANTARROWS;
end;
procedure TPanel.WMHScroll(var msg: TWMSCROLL);
begin
HandleScrollbar(msg, SB_HORZ);
end;
procedure TPanel.WMVScroll(var msg: TWMSCROLL);
begin
HandleScrollbar(msg, SB_VERT);
end;
end.
2009. március 11., szerda
How to force a form to stay iconized
Problem/Question/Abstract:
How to force a form to stay iconized
Answer:
If your application should be iconized from the beginning, set the for property WindowState to wsMinimized.
To keep a form iconized (and prevent a restore), add the following handler to your form class definition:
// e.g. the private section
procedure WMQueryOpen(var Msg: TWMQueryOpen); message WM_QUERYOPEN;
implementation
// ..
procedure TForm1.WMQueryOpen(var Msg: TWMQueryOpen);
begin
Msg.Result := 0;
end;
2009. március 10., kedd
Draw a grid on a TPaintBox
Problem/Question/Abstract:
How to draw a grid on a TPaintBox
Answer:
procedure TForm1.DrawPaintBoxGrid(distance: Integer);
var
xpos, ypos: Integer;
begin
PaintBox1.Canvas.Brush.Color := clBlack;
ypos := 0;
xpos := 0;
while ypos < PaintBox1.Height do
begin
ypos := ypos + distance;
PaintBox1.Canvas.MoveTo(0, ypos);
PaintBox1.Canvas.LineTo(PaintBox1.Width, ypos);
while xpos < PaintBox1.Width do
begin
xpos := xpos + distance;
PaintBox1.Canvas.MoveTo(xpos, 0);
PaintBox1.Canvas.LineTo(xpos, PaintBox1.Height);
end;
end;
end;
2009. március 9., hétfő
Make a TPanel look like the title bar of a window
Problem/Question/Abstract:
I have a good reason why I cannot use forms. So as an alternative I'm using two panels to mimic a simple form, i.e. with no windows icons (close, minimize etc.). One is aligned to the top to pose like the window title bar and the other to client. I want to paint the top panel like a typical title bar. How can I do this?
Answer:
There is an API function named DrawCaption, you can use it to draw the caption bar. Drop a client-aligned TPaintbox on your fake caption panel and do the drawing in the paintboxes OnPaint handler.
procedure TPLabBaseChildform.CaptionPaint(Sender: TObject);
const
activeFlags: array[Boolean] of DWORD = (0, DC_ACTIVE);
begin
with Sender as TPaintbox do
DrawCaption(self.handle, canvas.handle, clientrect, activeFlags[FActive] or
DC_TEXT or DC_GRADIENT);
end;
To draw other elements as well (beside the icon, which DrawCaption can handle) you use DrawFrameControl instead.
2009. március 8., vasárnap
How to convert the content of a TRichEdit into a bitmap
Problem/Question/Abstract:
Does anyone know of a component or a few lines of code, that could turn the contents of a TRichText field (WIN32 RTF) into a bitmap?
Answer:
Add this in the unit your are developing:
uses
RichText;
{For this demo add a RichEdit and an Image Control set the RichEdit change event to the lower code}
procedure
OutputRTFToBmp(RichHolder: TRichEdit; ImageHolder: TBitmap; itemwidth, itemheight: real);
var
Range: TFormatRange;
TextBoundary: TRect;
begin
{Setup the Height and Width of our output}
ImageHolder.width := round(itemwidth * screen.PixelsPerInch);
ImageHolder.height := round(itemheight * screen.PixelsPerInch);
{Set the Size of the Rich Edit}
textboundary := rect(0, 0, round(itemwidth * 1440), round(itemheight * 1440));
{Set the Range record}
range.hdc := ImageHolder.Canvas.handle;
range.hdctarget := ImageHolder.Canvas.handle;
range.rc := textboundary;
range.rcpage := textboundary;
{Start at character zero}
range.chrg.cpMin := 0;
{Display all Characters}
range.chrg.cpMax := -1;
{Ask RTF to Draw}
Sendmessage(RichHolder.handle, EM_FORMATRANGE, 1, longint(@range));
{Cleanup RTF Cache}
sendmessage(RichHolder.handle, EM_FORMATRANGE, 0, 0);
end;
procedure TForm1.RichEdit1Change(Sender: TObject);
begin
OutputRTFToBmp(RichEdit1, Image1.picture.bitmap, 2, 2);
{Display new stuff, this will flicker so you will have to double buffer}
image1.refresh;
end;
I use it on Metafiles then you can scale it also.
2009. március 7., szombat
How to do a backward search in a TRichEdit
Problem/Question/Abstract:
How to do a backward search in a TRichEdit
Answer:
Solve 1:
This is how to find text searching backwards:
function FindPreviousInstanceOfSubstring(substr, S: string; startAt: Integer):
Integer;
var
i: Integer;
ch: Char;
begin
ch := substr[1];
i := startAt;
Result := 0; {assume we fail}
while i >= 1 do
begin
if S[i] = ch then
begin
if AnsiCompareStr(substr, Copy(S, i, Length(substr))) = 0 then
begin
{found an instance}
Result := i;
Break;
end;
end;
Dec(i);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
richedit1.selstart := FindPreviousInstanceOfSubstring
(edit1.text, richedit1.text, richedit1.gettextlen) - 1;
richedit1.sellength := Length(edit1.text);
end;
Solve 2:
{Function FindTextBackwards
Parameters:
findWhat: text to find
inString: string to find it in
startAt : character index to start at (1-based)
caseSensitive: determines whether search is case-sensitive
words: if true the characters immediately surrounding a found location must not be alphanumeric
Returns: character index (1-based) of first character of a found location, or 0,
if the text was not found.
Description:
Performs a simple sequential search for a string in a larger string, starting at the specified
position and working towards the start of the string.
Error Conditions: none
Created: 27.02.99 by P. Below}
function FindTextBackwards(findWhat, inString: string; startAt: Integer;
caseSensitive, words: Boolean): Integer;
var
i, patternlen, findpos: Integer;
lastchar, firstchar: Char;
begin
Result := 0; {assume failure}
patternlen := Length(findWhat);
{Do a few sanity checks on the parameters}
if (patternlen = 0) or (startAt < patternlen) or (Length(inString) < patternlen)
then
Exit;
if not caseSensitive then
begin
{convert both strings to lower case}
findWhat := AnsiLowercase(findWhat);
inString := AnsiLowercase(inString);
end;
i := startAt;
lastchar := findWhat[patternlen];
firstchar := findWhat[1];
while (Result = 0) and (i >= patternlen) do
begin
if inString[i] = lastchar then
begin
findPos := i - patternlen + 1;
if inString[findPos] = firstchar then
begin
{We have a candidate. Compare the substring of length patternlen
starting at findPos with findWhat.
With AnsiStrLComp we can do that without having to copy the substring to
a temp string first.}
if AnsiStrLComp(@findWhat[1], @inString[findPos], patternlen) = 0 then
begin
{We have a match!}
Result := findPos;
if words then
begin
{Check the characters surrounding the hit.
For the hit to constitute a word they must not be alphanumeric.}
if (findPos > 1) and IsCharAlphanumeric(inString[findPos - 1]) then
begin
{Not a match after all}
Result := 0;
end
else
begin
if (i < Length(inString)) and IsCharAlphanumeric(inString[i + 1]) then
begin
{Not a match after all}
Result := 0;
end;
end;
end;
end;
end;
end;
Dec(i);
end;
end;
Here's how to use it:
procedure TForm1.Button1Click(Sender: TObject);
var
findPos: Integer;
begin
findPos := FindTextBackwards(findEdit.Text, richedit1.Text, richedit1.selstart + 1,
caseCheckbox.checked, wordsCheckbox.checked);
if findPos > 0 then
begin
with richedit1 do
begin
selstart := findPos - 1;
sellength := findEdit.GetTextLen;
perform(em_scrollcaret, 0, 0);
setfocus;
end;
end
else
showmessage('Text not found');
end;
2009. március 6., péntek
Easy way to compare dates
Problem/Question/Abstract:
Did you know that you can easily compare dates by using the "EncodeDate()"?
Answer:
Here are some examples:
uses
SysUtils;
{...}
if (Date > EncodeDate(1997, 1, 1)) then
begin
{ display "this program has expired" }
end;
{...}
if (EncodeDate(1997, 1, 1) > EncodeDate(1996, 1, 1)) then
begin
{...}
end;
2009. március 5., csütörtök
How to use MAPI to auto-send new mail
Problem/Question/Abstract:
How can I do ShellExecute(nil, 'open', 'mailto:abc@123.com', nil, nil, sw_shownormal) and auto-send new mail?
Answer:
You'd better try MapiSendMail. This uses the unit MAPI, if MAPI is configured correctly, this works quite fine.
uses
Mapi;
{ ...}
var
MapiMessage: TMapiMessage;
MapiFileDesc: PMapiFileDesc;
MError: Cardinal;
FNStr: string;
R, i: Integer;
begin
FNStr := AttachedFileName;
if R <> mrOK then
exit;
MapiFileDesc := New(PMapiFileDesc);
try
MapiFileDesc.lpszPathName := PChar(FNStr);
MapiFileDesc.lpszFileName := '';
with MapiMessage do
begin
ulReserved := 0;
lpszSubject := nil;
lpszNoteText := '';
lpszMessageType := nil;
lpszDateReceived := nil;
lpszConversationID := nil;
flFlags := 0;
lpOriginator := nil;
nRecipCount := 0;
lpRecips := nil;
nFileCount := 1;
lpFiles := MapiFileDesc;
end;
MError := MapiSendMail(0, 0, MapiMessage, MAPI_DIALOG or MAPI_LOGON_UI or
MAPI_NEW_SESSION, 0);
if MError <> 0 then
MessageDlg(SSendError, mtError, [mbOK], 0);
finally
Dispose(MapiFileDesc);
end;
end;
2009. március 4., szerda
Scroll my control without flicker effect
Problem/Question/Abstract:
Scroll my control without flicker effect
Answer:
The easiest way to scroll the elements of a control is to force a complete repaint of the control. Unfortunately this produces the flicker effect. You may use
InvalidateRect(MyControl.Handle, nil, FALSE);
(important: last parameter = FALSE) to cause a complete redraw without erasing the background.
The best way to reduce this flickering is to use the ScrollWindow or ScrollWindowEx Windows API function. Look them up in your Win32.HLP file.
Another source of flickering can be from Windows using two messages to paint: WM_PAINT and WM_ERASEBKGND.
You may want to intercept all of the WM_ERASEBKGND messages and do all of your painting, including the background, in response to WM_PAINT messages in the Paint method:
type
� TMyComponent = class(TWinControl)
// ..
� protected
��� procedure WMEraseBkgnd(var message: TWMEraseBkgnd);
����� message WM_ERASEBKGND;
// ..
�
end;
// ..
procedure TBMyComponent.WMEraseBkgnd(var message: TWMEraseBkgnd);
begin
message.Result := 0
end;
2009. március 3., kedd
How to zoom a polygon
Problem/Question/Abstract:
How can I zoom a polygon? Using SetWorldTransform or how?
Answer:
Here's one possible way:
{ ... }
type
TPolygon = array of TPoint;
procedure ZoomPolygon(var Polygon: TPolygon; const Center: TPoint; const Scale: Double);
var
I: Integer;
begin
for I := 0 to High(Polygon) do
begin
Polygon[I].X := Round(Scale * (Polygon[I].X - Center.X) + Center.X);
Polygon[I].Y := Round(Scale * (Polygon[I].Y - Center.Y) + Center.Y);
end;
end;
2009. március 2., hétfő
Splitting a string in an dynamic array
Problem/Question/Abstract:
A function that splits a string in parts separated by a substring and returns the parts in a dynamic string array
Answer:
The following functions split a string in parts separated by a substring and return the parts in a dynamic string array:
interface
type
TStringArray = array of string;
function Split(const str: string;
const separator: string = ','): TStringArray;
function AnsiSplit(const str: string;
const separator: string = ','): TStringArray;
implementation
uses sysutils;
function Split(const str: string;
const separator: string): TStringArray;
// Returns an array with the parts of "str" separated by "separator"
var
i, n: integer;
p, q, s: PChar;
begin
SetLength(Result, Occurs(str, separator) + 1);
p := PChar(str);
s := PChar(separator);
n := Length(separator);
i := 0;
repeat
q := StrPos(p, s);
if q = nil then
q := StrScan(p, #0);
SetString(Result[i], p, q - p);
p := q + n;
inc(i);
until q^ = #0;
end;
function AnsiSplit(const str: string;
const separator: string): TStringArray;
// Returns an array with the parts of "str" separated by "separator"
// ANSI version
var
i, n: integer;
p, q, s: PChar;
begin
SetLength(Result, AnsiOccurs(str, separator) + 1);
p := PChar(str);
s := PChar(separator);
n := Length(separator);
i := 0;
repeat
q := AnsiStrPos(p, s);
if q = nil then
q := AnsiStrScan(p, #0);
SetString(Result[i], p, q - p);
p := q + n;
inc(i);
until q^ = #0;
end;
Example:
procedure TForm1.Button1Click(Sender: TObject);
var
a: TStringArray;
i: integer;
begin
a := Split('part1,part2,part3');
for i := 0 to Length(a) - 1 do
begin // Will show three dialogs
ShowMessage(a[i]); // 'part1', 'part2', 'part3'
end;
end;
You can see an example using a StringList instead of a dynamic array in a separate article "Splitting a string in a string list".
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2009. március 1., vasárnap
How to intercept the maximize command
Problem/Question/Abstract:
How to intercept the maximize command
Answer:
If you want to restrict your window's maximum size (or minimum size, for that matter), you may try to intercept WM_SYSCOMMAND and check for the value of wParam.
More elegant is to intercept WM_GETMINMAXINFO, as the following example shows:
type
TMyForm = class(TForm)
procedure _WM_GETMINMAXINFO(var mmInfo: TWMGETMINMAXINFO); message
wm_GetMinMaxInfo;
end;
//..
procedure TMyForm._WM_GETMINMAXINFO(var mmInfo: TWMGETMINMAXINFO);
begin
with mmInfo.minmaxinfo^ do
begin
// allow at most half of the screen, and position it in the middle
ptmaxposition.x := Screen.Width div 4;
ptmaxposition.y := Screen.Height div 4;
ptmaxsize.x := Screen.Width div 2;
ptmaxsize.y := Screen.Height div 2;
end;
end;
end.
Feliratkozás:
Bejegyzések (Atom)