2005. augusztus 31., szerda

Check if a TForm has already been created


Problem/Question/Abstract:

How to check if a TForm has already been created

Answer:

Solve 1:

You just need to provide a little code to manage the lifetime of the form. Something like this (which assumes there should only ever be one instance of the TForm1 class):

interface

procedure ShowForm;
procedure CloseForm;

implementation

var
  Form: TForm1;

procedure ShowForm;
begin
  if (Form <> nil) then
    Form.Show
  else
  begin
    Form := TForm1.Create(Application);
    Form.Show;
  end;
end;

You can only check for nil the first time, because once you create and free the form it won't be nil again unless you make it nil in code!

procedure CloseForm;
begin
  Form.Free;
  Form := nil;
end;

Then simply call these help functions whenever you want to show or close the form.


Solve 2:

You can use the FindWindow API function, but for checking if a form has been created within an application, this is easier:

function IsFormCreated(Form: TCustomForm): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 0 to Screen.FormCount - 1 do
  begin
    if Screen.Forms[i] = Form then
    begin
      Result := True;
      Break;
    end;
  end;
end;


Solve 3:

Know whether a form already exist before I dynamically create it

function IsFormOpen(const FormName: string): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := Screen.FormCount - 1 downto 0 do
    if (Screen.Forms[i].Name = FormName) then
    begin
      Result := True;
      Break;
    end;
end;

First check, if the Form (here Form2) is open. If not, create it.

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not IsFormOpen('Form2') then
    Form2 := TForm2.Create(Self);

  Form2.Show
end;

{ For MDI Children }

function IsMDIChildOpen(const AFormName: TForm; const AMDIChildName: string): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := Pred(AFormName.MDIChildCount) downto 0 do
    if (AFormName.MDIChildren[i].Name = AMDIChildName) then
    begin
      Result := True;
      Break;
    end;
end;

First check, if the MDI Child is open. If not, create it.

procedure TForm1.Button2Click(Sender: TObject);
begin
  if not IsMDIChildOpen(Form1, 'MyMDIChild') then
    MyMDIChild := TMyMDIChild.Create(Self);
  MyMDIChild.Show;
  MyMDIChild.BringToFront;
end;


2005. augusztus 30., kedd

Convert a Unicode string to a normal string


Problem/Question/Abstract:

I have an application that reads data from a server via winsock. The data sent are in Unicode format and I need to parse out the constituent strings and display in a ListView. They are sent as C strings so the data looks like this: array of chars#0array of chars#0array of chars#0#0. Since the 'array of chars' is actually an array of widechars it also contains #0 bytes in the msb of the character. I tried StringReplace(Intext, #0, '', [rfReplaceAll]); but it does not convert, maybe it cannot go past the first #0 in the input string?

Answer:

Yes. What you need to do here is work with PWideChars. It would have helped, of course, to post a bit more specific information, e.g. what the type of Intext is. Anyway, all you need is a way to get the address of the first widechar in the data. Assuming intext is a String (even though it contains widechars) the process would look like this:

procedure SplitServerWidecharList(const intext: string; list: TStrings);
var
  p: PWideChar;
begin
  Assert(Assigned(list));
  list.Clear;
  if intext <> '' then
  begin
    p := PWideChar(@intext[1]); {points to first widechar}
    while p^ <> #0000 do
    begin
      {Convert this widestring to Ansi and store it}
      list.add(WidecharToString(p));
      {Find end of this widestring}
      while p^ <> #0000 do
        Inc(p);
      {Hop to start of the next one  }
      Inc(p);
    end;
  end;
end;

Can you be sure of the byte order of the received Unicode characters? The code above assumes little-endian byte order, if the data comes in in big-endian byte order you would have to swap the bytes in every widechar before you could process it as above.

2005. augusztus 29., hétfő

Find how many (or is) users are connected to Access DB


Problem/Question/Abstract:

How to find how many (or is) users are connected to Access DB

Answer:

You can do it by using ADO components. Put TADOConnection and TADODataSet components on the form and name it ADOConnection1 and ADODataSet1. Information about users will be handle in ADODataSet1 then ADOConnection1 become active. More about ADO schemas you can find in MSDN.

ADOConnection1.OpenSchema(siProviderSpecific, emptyParam,
  '{947bb102-5d43-11d1-bdbf-00c04fb92675}', ADODataSet1);

2005. augusztus 28., vasárnap

Simple way to rotate region


Problem/Question/Abstract:

Simple function returning rotated region. Pprocedure doing the same with source region.

Answer:

It's the simple function returning new region rotated to the angle that you want around the source region. Source region doesn't change.

The second procedure does the same with source region without creating new region.

I hope this will be useful.
Comments are provided along the code

function _RotateRgn(ARgn: HRGN; ADegree: Real): HRGN;
var
  wXFORM: XFORM; // transformation structure, see Windows API
  kRgnD: DWord; // count of RGNDATA structures in region
  RgnData: PRgnData; // pointer to region data
  Rt: TRect;
  kX, kY: Integer;
begin
  if (ARgn = 0) or (ADegree = 0) then
    Exit;

  // Get region's surrounding rectangular
  GetRgnBox(ARgn, Rt);

  // Move source region so that the centre of its surrounding rectangular
  // goes to the left top corner of a window
  kX := Rt.Left + (Rt.Right - Rt.Left) div 2;
  kY := Rt.Top + (Rt.Bottom - Rt.Top) div 2;
  OffsetRgn(ARgn, -kX, -kY);

  // Fill XFORM according to task (rotate region)
  FillChar(wXFORM, SizeOf(wXFORM), #0);
  wXFORM.eM11 := Cos(ADegree / 180 * pi);
  wXFORM.eM12 := -Sin(ADegree / 180 * pi);
  wXFORM.eM21 := -wXFORM.eM12;
  wXFORM.eM22 := wXFORM.eM11;

  // Prepare buffer to store region data
  kRgnD := GetRegionData(ARgn, 0, nil);
  GetMem(RgnData, SizeOf(RGNDATA) * kRgnD);
  // ..and fill the buffer with region's data
  GetRegionData(ARgn, kRgnD, RgnData);
  // ..move source region to its initial position
  OffsetRgn(ARgn, kX, kY);

  // Create output region using data in the buffer and transformation wXFORM
  Result := ExtCreateRegion(@wXFORM, kRgnD, RgnData^);
  // Move output region on a place of source region
  OffsetRgn(Result, kX, kY);
  FreeMem(RgnData);
end;

procedure _RotateRgnEx(var ARgn: HRGN; ADegree: Real);
var
  wXFORM: XFORM; // transformation structure, see Windows API
  kRgnD: DWord; // count of RGNDATA structures in region
  RgnData: PRgnData; // pointer to region data
  Rt: TRect;
  kX, kY: Integer;
begin
  if (ARgn = 0) or (ADegree = 0) then
    Exit;

  // Get region's surrounding rectangular
  GetRgnBox(ARgn, Rt);

  // Move source region so that the centre of its surrounding rectangular
  // goes to the left top corner of a window
  kX := Rt.Left + (Rt.Right - Rt.Left) div 2;
  kY := Rt.Top + (Rt.Bottom - Rt.Top) div 2;
  OffsetRgn(ARgn, -kX, -kY);

  // Fill XFORM according to task (rotate region)
  FillChar(wXFORM, SizeOf(wXFORM), #0);
  wXFORM.eM11 := Cos(ADegree / 180 * pi);
  wXFORM.eM12 := -Sin(ADegree / 180 * pi);
  wXFORM.eM21 := -wXFORM.eM12;
  wXFORM.eM22 := wXFORM.eM11;

  // Prepare buffer to store region data
  kRgnD := GetRegionData(ARgn, 0, nil);
  GetMem(RgnData, SizeOf(RGNDATA) * kRgnD);
  // ..and fill the buffer with region's data
  GetRegionData(ARgn, kRgnD, RgnData);
  // ..delete source region
  DeleteObject(ARgn);

  // Create new region using data in the buffer and transformation wXFORM
  ARgn := ExtCreateRegion(@wXFORM, kRgnD, RgnData^);
  // Move output region to the origin place
  OffsetRgn(ARgn, kX, kY);
  FreeMem(RgnData);
end;

2005. augusztus 27., szombat

CNPJ and CPF Validation


Problem/Question/Abstract:

How to validade CNPJ or CPF

Answer:

In Brazil every people has a ID called CPF(Cadastro de pessoa fisica) and every company has a ID called CNPJ(Cadastro nacional de pessoa juridica). Some times we need to validate those IDs.

//Validade CPF

function ChkCPF(const cCPF: string): boolean;
  function LimpaString(const StrNumerica: string): string;
  var
    i: integer;
    valor: string;
  begin
    valor := StrNumerica;
    for i := 1 to length(valor) do
      if not (valor[i] in ['0'..'9']) then
        Delete(valor, i, 1);
    LimpaString := valor;
  end;

  function CharToInt(cNum: char): integer;
  begin
    CharToInt := Ord(cNum) - 48;
  end;

  function DigiSum(N: integer): integer;
  var
    value: integer;
  begin
    value := N mod 10 + N div 10;
    if value >= 10 then
      value := DigiSum(value);
    DigiSum := value;
  end;
var
  i, soma, multiplo: integer;
  CPF: string;
begin
  ChkCPF := false;
  CPF := LimpaString(cCPF);
  if Length(CPF) <> 11 then
    exit;
  soma := 0;
  for i := 9 downto 1 do
  begin
    soma := soma + CharToInt(CPF[i]) * (11 - i);
  end;
  multiplo := soma mod 11;
  if multiplo <= 1 then
    multiplo := 0
  else
    multiplo := 11 - multiplo;
  if (multiplo <> CharToInt(CPF[10])) then
    exit;
  soma := 0;
  for i := 10 downto 1 do
  begin
    soma := soma + CharToInt(CPF[i]) * (12 - i);
  end;
  multiplo := soma mod 11;
  if multiplo <= 1 then
    multiplo := 11;
  ChkCPF := CharToInt(CPF[11]) = (11 - multiplo);
end;

//Validade CNPJ

function ChkCNPJ(const cCNPJ: string): boolean;
  function LimpaString(const StrNumerica: string): string;
  var
    i: integer;
    valor: string;
  begin
    valor := StrNumerica;
    for i := 1 to length(valor) do
      if not (valor[i] in ['0'..'9']) then
        Delete(valor, i, 1);
    LimpaString := valor;
  end;

  function CharToInt(cNum: char): integer;
  begin
    CharToInt := Ord(cNum) - 48;
  end;

  function DigiSum(N: integer): integer;
  var
    value: integer;
  begin
    value := N mod 10 + N div 10;
    if value >= 10 then
      value := DigiSum(value);
    DigiSum := value;
  end;
var
  i, soma, mult: integer;
  CGC: string;
begin
  ChkCNPJ := false;
  CGC := LimpaString(cCNPJ);
  if Length(CGC) <> 14 then
    exit;
  soma := 0;
  mult := 2;
  for i := 12 downto 1 do
  begin
    soma := soma + CharToInt(CGC[i]) * mult;
    mult := mult + 1;
    if mult > 9 then
      mult := 2;
  end;
  mult := soma mod 11;
  if mult <= 1 then
    mult := 0
  else
    mult := 11 - mult;
  if mult <> CharToInt(CGC[13]) then
    exit;
  soma := 0;
  mult := 2;
  for i := 13 downto 1 do
  begin
    soma := soma + CharToInt(CGC[i]) * mult;
    mult := mult + 1;
    if mult > 9 then
      mult := 2;
  end;
  mult := soma mod 11;
  if mult <= 1 then
    mult := 0
  else
    mult := 11 - mult;
  ChkCNPJ := mult = CharToInt(CGC[14]);
end;

2005. augusztus 26., péntek

Check if the mouse pointer is over or close to a line on a TCanvas


Problem/Question/Abstract:

I am painting a line on a canvas with LineTo. How can I determine if the mouse pointer is over that line or not.

Answer:

This will give you the distance to the line and you can decide in your code how close (in pixels) you want the user to be to the line before selecting it. It takes into account the end points as well, so when the user is past the end points on the extension of the line it is not triggered.

MinDistPointLine calculates the minimum distance of a point to a line. P is the point, the line is between points A and B. It is based on the distance of P to the parametrised point Q = (1 - q) A + qB where 0 <= q <= 1. The distance PQ is sqrt(((1 - q) Ax + qBx - Px)^2 + (... Y term) ). Differentiating gives dPQ / dq = 2((Bx - Ax) q + (Ax - Px))(Bx - Ax) + (... Y term). dPQ / dq must be zero for minimum so q = (Px - Ax)(Bx - Ax) + (Py - Ay)(By - Ay) / ((Bx - Ax)^2 + (By - Ay)^2)

function MinDistPointLine(Px, Py, Ax, Ay, Bx, By: double): double;

implementation

function PointToPointDist(Ax, Ay, Bx, By: double): double;
begin
  Result := sqrt(sqr(Bx - Ax) + sqr(By - Ay));
end;

function MinDistPointLine(Px, Py, Ax, Ay, Bx, By: double): double;
var
  q: double;
begin
  if (Ax = Bx) and (Ay = By) then
  begin
    {Point to point}
    Result := PointToPointDist(Px, Py, Ax, Ay);
  end
  else
  begin
    {Minimum}
    q := ((Px - Ax) * (Bx - Ax) + (Py - Ay) * (By - Ay)) / (sqr(Bx - Ax) + sqr(By - Ay));
    {Limit q to 0 <= q <= 1}
    if q < 0 then
      q := 0;
    if q > 1 then
      q := 1;
    {Distance}
    Result := PointToPointDist(Px, Py, (1 - q) * Ax + q * Bx, (1 - q) * Ay + q * By);
  end;
end;

2005. augusztus 25., csütörtök

How to connect or disconnect all tables in a datamodule


Problem/Question/Abstract:

How to connect or disconnect all tables in a datamodule

Answer:

This should work:

for i := 0 to pred(components.count) do
  if (components[i] is TDataSet) then
    (components[i] as TDataSet).Active := not (components[i] as TDataSet).Active;

2005. augusztus 24., szerda

Disable hints in a TTreeView


Problem/Question/Abstract:

How to disable hints in a TTreeView

Answer:

Solve 1:

If you have installed the Internet Explorer 4.0 or high, in TTreeView component always displaying a hint for cutted items. It's useful but sometimes prevents and irritates (at least, me).

But there is a simple way to switch off this feature:

const
  TVS_NOTOOLTIPS = $0080;
begin
  SetWindowLong(yourTreeView.Handle, GWL_STYLE,
    GetWindowLong(yourTreeView.Handle, GWL_STYLE) xor TVS_NOTOOLTIPS);
end;


Solve 2:

const
  {Treeview has no standard way of disabling tooltips}
  TVS_NOTOOLTIPS = $00000080;
  TVS_UNDERLINE = $00000200;

{Disable the hint window of the treeview, Underline items}
SetWindowLong(tvApplications.Handle, GWL_STYLE, GetWindowLong(tvApplications.Handle,
  GWL_STYLE) or TVS_NOTOOLTIPS or TVS_UNDERLINE);

2005. augusztus 23., kedd

How to Backup and Restore the content of a TreeView


Problem/Question/Abstract:

How can I backup (save) and the restore (load) the content of my TreeView to a file?

Answer:

Use the following two procedures to Backup and Restore the content of your TreeView:

procedure TForm1.BackupTreeView(MyTree: TTReeView; ToFile: string);
begin
  with TFileStream.Create(ToFile, fmCreate) do
  try
    WriteComponent(MyTree);
  finally
    Free;
  end;
end;

procedure TForm1.RestoreTreeView(MyTree: TTReeView; FromFile: string);
begin
  with TFileStream.Create(FromFile, fmOpenRead) do
  try
    MyTree.Clear;
    ReadComponent(MyTree);
  finally
    Free;
  end;

end;

This approach will not keep any data associated with the nodes, you need take care about that separately. The only thing it will do is preserve the tree structure and node names. You also will not be able to restore the treeview to any other component than original one (say to the other form) without risking to screw up everything.

2005. augusztus 22., hétfő

How to determine the size of an executable from its exe header


Problem/Question/Abstract:

How to determine the size of an executable from its exe header

Answer:

This code does not work with 16 bit executables. It assumes that the application is trying to find the size of itself, but could easily be modified to find the size of any 32bit exe loaded into a stream.

{$IFDEF VER100}

{TImageDosHeader isn't defined in Delphi 3 so here's an an abbreviated structure definition}
type
  PImageDosHeader = ^TImageDosHeader;
  TImageDosHeader = packed record
    e_ignore: packed array[0..29] of WORD;
    _lfanew: Longint;
  end;

{$ENDIF}

function GetExeSize: cardinal;
var
  p: PChar;
  i, NumSections: integer;
begin
  result := 0;
  {hInstance is actually a pointer to the exe's image base in memory}
  p := pointer(hinstance);
  inc(p, PImageDosHeader(p)._lfanew);
  inc(p, sizeof(dword));
  NumSections := PImageFileHeader(p).NumberOfSections;
  inc(p, sizeof(TImageFileHeader) + sizeof(TImageOptionalHeader));
  for i := 1 to NumSections do
  begin
    with PImageSectionHeader(p)^ do
      if PointerToRawData + SizeOfRawData > result then
        result := PointerToRawData + SizeOfRawData;
    inc(p, sizeof(TImageSectionHeader));
  end;
end;

2005. augusztus 21., vasárnap

How to remove a menu or submenu at runtime


Problem/Question/Abstract:

How to remove a menu or submenu at runtime

Answer:

Try these two procedures, i.e. RemoveMenu(form1.handle, 0) to remove the first menu:


procedure RemoveMenu(hwndMain: THandle; MenuIndex: Integer);
var
  h: HMenu;
begin
  h := GetMenu(hwndMain);
  if h > 0 then
    DeleteMenu(h, MenuIndex, MF_BYPOSITION);
end;

procedure RemoveSubmenu(hwndMain: THandle; MenuIndex, SubmenuIndex: Integer);
var
  h: HMenu;
begin
  h := GetMenu(hwndMain);
  if h > 0 then
    DeleteMenu(GetSubmenu(h, MenuIndex), SubmenuIndex, MF_BYPOSITION);
end;

2005. augusztus 20., szombat

How to allow only one instance of an application


Problem/Question/Abstract:

I use Delphi 6 to make an application. Everytime I run the executable, an instance of my application starts up (of course). Is there any way to detect at runtime if another instance of the same application is running and switch control to the original window instead of making a new one?

Answer:

Solve 1:

Include the following unit in your code:

unit MultInst;

interface

const
  MI_QUERYWINDOWHANDLE = 1;
  MI_RESPONDWINDOWHANDLE = 2;
  MI_ERROR_NONE = 0;
  MI_ERROR_FAILSUBCLASS = 1;
  MI_ERROR_CREATINGMUTEX = 2;

  {Call this function to determine if error occurred in startup. Value will be one or
  more of the MI_ERROR_* error flags.}

function GetMIError: Integer;

implementation

uses
  Forms, Windows, SysUtils;

const
  UniqueAppStr = 'DDG.I_am_the_Eggman!';

var
  MessageId: Integer;
  WProc: TFNWndProc;
  MutHandle: THandle;
  MIError: Integer;

function GetMIError: Integer;
begin
  Result := MIError;
end;

function NewWndProc(Handle: HWND; Msg: Integer; wParam, lParam: Longint): Longint;
  stdcall;
begin
  Result := 0;
  {If this is the registered message...}
  if Msg = MessageID then
  begin
    case wParam of
      MI_QUERYWINDOWHANDLE:
        {A new instance is asking for main window handle in order to focus the
                                main window, so normalize app and send back message with main window handle.}
        begin
          if IsIconic(Application.Handle) then
          begin
            Application.MainForm.WindowState := wsNormal;
            Application.Restore;
          end;
          PostMessage(HWND(lParam), MessageID, MI_RESPONDWINDOWHANDLE,
            Application.MainForm.Handle);
        end;
      MI_RESPONDWINDOWHANDLE:
        {The running instance has returned its main window handle, so we need to
                          focus it and go away.}
        begin
          SetForegroundWindow(HWND(lParam));
          Application.Terminate;
        end;
    end;
  end
    {Otherwise, pass message on to old window procedure}
  else
    Result := CallWindowProc(WProc, Handle, Msg, wParam, lParam);
end;

procedure SubClassApplication;
begin
  {We subclass Application window procedure so that Application.OnMessage
  remains available for user.}
  WProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC,
    Longint(@NewWndProc)));
  {Set appropriate error flag if error condition occurred}
  if WProc = nil then
    MIError := MIError or MI_ERROR_FAILSUBCLASS;
end;

procedure DoFirstInstance;
{This is called only for the first instance of the application}
begin
  {Create the mutex with the (hopefully) unique string}
  MutHandle := CreateMutex(nil, False, UniqueAppStr);
  if MutHandle = 0 then
    MIError := MIError or MI_ERROR_CREATINGMUTEX;
end;

procedure BroadcastFocusMessage;
{This is called when there is already an instance running.}
var
  BSMRecipients: DWORD;
begin
  {Prevent main form from flashing}
  Application.ShowMainForm := False;
  {Post message to try to establish a dialogue with previous instance}
  BSMRecipients := BSM_APPLICATIONS;
  BroadCastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
    @BSMRecipients, MessageID, MI_QUERYWINDOWHANDLE, Application.Handle);
end;

procedure InitInstance;
begin
  SubClassApplication; {hook application message loop}
  MutHandle := OpenMutex(MUTEX_ALL_ACCESS, False, UniqueAppStr);
  if MutHandle = 0 then
    {Mutex object has not yet been created, meaning that no previous instance
                has been created.}
    DoFirstInstance
  else
    BroadcastFocusMessage;
end;

initialization
  MessageID := RegisterWindowMessage(UniqueAppStr);
  InitInstance;
finalization
  {Restore old application window procedure}
  if WProc <> nil then
    SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(WProc));
  if MutHandle <> 0 then
    CloseHandle(MutHandle); {Free mutex}
end.


Solve 2:

The simplest way to do this is to make the following changes to your dpr where TForm1 is the name of your main form.

program Project1;

uses
  Forms, Windows, Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin
  if FindWindow('TForm1', nil) <> 0 then
  begin
    SetForegroundWindow(FindWindow('TForm1', nil));
    Exit;
  end;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

2005. augusztus 19., péntek

Making Secondary Forms Independent of the Main Form


Problem/Question/Abstract:

In my application, I want to be able to iconize the main form and still leave the secondary forms displaying on the desktop. Likewise, I want to be able to select secondary forms without the main form popping up. How can I do this?

Answer:

Recently a user asked me about this, and I had to do a bit of experimentation before I finally figured it out. And the solution to this problem is actually so simple, you'll scream (actually, I did all the screaming myself). But it's not something that's necessarily easy to find out nor intuitive (maybe it is for some, but it wasn't for me). But before I give you the solution, let's discuss the concept that's behind it.

All windowed controls have a parent of some sort; that is, some control that maintains visual control (ie. display) over it. Main forms of an application all point to the Application as their parent. Likewise, by default, secondary forms point to the main form of the application for parentage. But the neat thing about creating windowed objects in Delphi (though you need to be careful with some controls) is that you can change the parentage of a control to isolate its visual control, essentially giving it independence from its default parent. Okay, so how do you do it? You might think that you can reset parentage at FormCreate, but that's not the right place to do it. The only way to do this is before the window gets created in the first place, and that place is in the CreateParams procedure.

I've discussed CreateParams in previous articles, so I won't go into details about it, though I will brush over what it does. CreateParams is an inherited procedure that wraps the WinAPI functions CreateWindow and CreateWindowEx that are responsible for a window's initial appearance. It's a convenient way to set display parameters. With it, we can change the a variable parameter called Params that is a TCreateParams structure (you should look this structure up in the online help) to affect a number of different things about a form. One of the fields in the TCreateParams structure is WndParent. This parameter specifies the handle of the window that controls the display of the window being created. By changing this parameter to point another window handle (hint, hint), we can change the default parentage.

So now it's a matter of deciding what window is going to be the secondary form's new parent. In this case, whenever we want to make a secondary form independent of the main form, we're essentially turning it into its own mini-application without creating a new EXE. So it's best to choose a parent that's at the highest order in the system. That window is Windows' Desktop Window. Fortunately we have a way of getting its handle by using the WinAPI call GetDesktopWindow, which returns the handle of the Desktop.

Okay, we've covered all the bases. Now you're going to kill me for belaboring the point. Here's the code:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm2 = class(TForm)
  private
    { Private declarations }
    //override the CreateParams procedure for any child forms you want to
    //make independent of the main form
    procedure CreateParams(var Params: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

//Here's the implementation of CreateParams

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params); //Don't ever forget to do this!!!
  Params.WndParent := GetDesktopWindow;
end;

end.

Insanely simple, huh? Sorry I took so long to lead up to it, but while the solution was simple, I just couldn't get away from explaining at least a bit of background information to help those who aren't familiar with the internal workings of the WinAPI. In any case, HAVE AT IT!!!

2005. augusztus 18., csütörtök

Shutdown, Reboot, Logoff, Monitor Off and Suspend mode functions


Problem/Question/Abstract:

The way to make your computer to sleep, reboot or shutdown. It also have the code to force shutdown and force reboot. To try this example you need seven buttons. The Suspend Mode is a magic sendkey that I triped over and it force the computer in to suspend mode.

Answer:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    btnShutDown: TButton;
    btnReboot: TButton;
    btnLogOff: TButton;
    btnForceDown: TButton;
    btnForceReboot: TButton;
    btnMonitorOff: TButton;
    btnSuspend: TButton;
    procedure btnLogOffClick(Sender: TObject);
    procedure btnShutDownClick(Sender: TObject);
    procedure btnRebootClick(Sender: TObject);
    procedure btnForceDownClick(Sender: TObject);
    procedure btnForceRebootClick(Sender: TObject);
    procedure TimerEx1Timer(Sender: TObject);
    procedure btnMonitorOffClick(Sender: TObject);
    procedure btnSuspendClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.btnLogOffClick(Sender: TObject);
begin
  if ExitWindowsEx(EWX_LOGOFF, 1) = False then
    ShowMessage('Uable to comply !');
end;

procedure TForm1.btnShutDownClick(Sender: TObject);
begin
  if ExitWindowsEx(EWX_SHUTDOWN, 1) = False then
    ShowMessage('Uable to comply !');
end;

procedure TForm1.btnRebootClick(Sender: TObject);
begin
  if ExitWindowsEx(EWX_REBOOT, 1) = False then
    ShowMessage('Uable to comply !');
end;

procedure TForm1.btnForceDownClick(Sender: TObject);
begin
  if ExitWindowsEx(EWX_SHUTDOWN + EWX_FORCE, 1) = False then
    ShowMessage('Uable to comply !');
end;

procedure TForm1.btnForceRebootClick(Sender: TObject);
begin
  if ExitWindowsEx(EWX_REBOOT + EWX_FORCE, 1) = False then
    ShowMessage('Uable to comply !');
end;

procedure TForm1.TimerEx1Timer(Sender: TObject);
begin
  if ExitWindowsEx(EWX_SHUTDOWN + EWX_FORCE, 1) = False then
    ShowMessage('Uable to comply !');
end;

procedure TForm1.btnMonitorOffClick(Sender: TObject);
begin
  SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, 0);
end;

procedure TForm1.btnSuspendClick(Sender: TObject);
begin
  Keybd_event(8, 0, 0, 0); //I don't remember what I was doing when I found this.
  Keybd_event(95, 0, 0, 0);
end;

end.

2005. augusztus 17., szerda

How to place a TComboBox on the Windows Taskbar


Problem/Question/Abstract:

How to place a TComboBox on the Windows Taskbar

Answer:

FindWindow('Shell_TrayWnd', nil) will get you the handle of the taskbar, you can then use this handle to manipulate the taskbar. For example, to move a combobox1 to the taskbar do:

{ ... }
Combobox1.Left := 0;
Windows.SetParent(Combobox1.Handle, FindWindow('Shell_TrayWnd', nil));
{ ... }

2005. augusztus 16., kedd

Opening and Closing a CD Tray


Problem/Question/Abstract:

How can I open and close the tray on a CD-ROM drive?

Answer:

Most of you are probably familiar with the TMediaPlayer component. It's a nice multi-purpose component for multimedia. But it has one failing and that is its inability to close a CD-ROM drive tray if it's open. And unfortunately for us, there's no way to manipulate methods or properties of TMediaPlayer to enable this functionality. So what we have to do is use the Windows API; in particular, we'll be using the MMSystem.pas file.

One thing to note: We can use Windows API function calls solely, but TMediaPlayer does some internal handling that we don't need to worry about if we employ the component. So this example makes use of the TMediaPlayer.

Just follow these steps:

Start a new project and drop a TMediaPlayer and a TButton on it.
Add a "MMSystem" declaration to the uses statement of your form.
Set AutoOpen to True on the TMediaPlayer. Set the DeviceType property to dtCDAudio. You might want to consider disabling the btEject option from EnabledButtons property since we'll be handling that functionality in code.
One thing I use this for is for data CD's in some applications, so I also set the Visible property to False and just let my button do the opening and closing of the tray.
Finally, add the following code to the button's OnClick event:

procedure TForm1.Button2Click(Sender: TObject);
begin
  with MediaPlayer1 do
    if (MediaPlayer1.Mode = mpOpen) then
      mciSendCommand(MediaPlayer1.DeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0)
    else
      mciSendCommand(MediaPlayer1.DeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0);
end;

Notice we use the function mciSendCommand. This is the "Swiss Army Knife" of the MMSystem unit. In Windows, everything's controlled by messages. With respect to device control, mciSendCommand is very similar to a window's WndProc in that it acts as a message dipatcher. Just supply the device, the message type, message flags, and message parameters, and you're on your way. For more detailed information, I suggest you look in the help file.

2005. augusztus 15., hétfő

How to check which control previously had focus


Problem/Question/Abstract:

Is there any way to tell within the OnEnter handler of Control2, which other control just passed the focus to Control2?

Answer:

type
  TForm1 = class(TForm)
    { ... }
  public
    LastControl: TComponent;
  end;

procedure TForm1.Edit1OnEnter(Sender: TObject);
begin
  if Assigned(LastControl) then
    { do whatever you want with the previous control }
    LastControl := Sender as TComponent;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  LastControl := nil;
end;

2005. augusztus 14., vasárnap

Printing a TForm


Problem/Question/Abstract:

Printing a TForm

Answer:

If you try to print a Delphi form with the Print() method, it will print but the page is blank.
Instead use the following method.

  
procedure TForm1.PrintForm;
var
  DC: HDC;
  isDcPalDevice: Bool;
  MemDC: HDC;
  MemBitmap: HBITMAP;
  OldMemBitmap: HBITMAP;
  hDibHeader: THandle;
  pDibHeader: Pointer;
  hBits: THandle;
  pBits: Pointer;
  ScaleX: Double;
  ScaleY: Double;
  pPal: PLOGPALETTE;
  pal: HPALETTE;
  OldPal: HPALETTE;
  i: Integer;
begin
  {Get the screen dc}
  DC := GetDC(0);
  {Create a compatible dc}
  MemDC := CreateCompatibleDC(DC);
  {create a bitmap}
  MemBitmap := CreateCompatibleBitmap(DC, Self.Width, Self.Height);
  {select the bitmap into the dc}
  OldMemBitmap := SelectObject(MemDC, MemBitmap);

  {Lets prepare to try a fixup for broken video drivers}
  isDcPalDevice := False;
  if GetDeviceCaps(DC, RASTERCAPS) and RC_PALETTE = RC_PALETTE then
  begin
    GetMem(pPal, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)));
    FillChar(pPal^, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)),
      #0);
    pPal^.palVersion := $300;
    pPal^.palNumEntries := GetSystemPaletteEntries(DC, 0, 256, pPal^.palPalEntry);
    if pPal^.palNumEntries <> 0 then
    begin
      pal := CreatePalette(pPal^);
      OldPal := SelectPalette(MemDC, pal, False);
      isDcPalDevice := True
    end
    else
      FreeMem(pPal, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)));
  end;
  {copy from the screen to the memdc/bitmap}
  BitBlt(MemDC, 0, 0, Self.Width, Self.Height, DC, Self.Left, Self.Top, SRCCOPY);

  if isDcPalDevice = True then
  begin
    SelectPalette(MemDC, OldPal, False);
    DeleteObject(pal);
  end;
  {unselect the bitmap}
  SelectObject(MemDC, OldMemBitmap);
  {delete the memory dc}
  DeleteDC(MemDC);
  {Allocate memory for a DIB structure}
  hDibHeader := GlobalAlloc(GHND, SizeOf(TBITMAPINFO) + (SizeOf(TRGBQUAD) *
    256));
  {get a pointer to the alloced memory}
  pDibHeader := GlobalLock(hDibHeader);

  {fill in the dib structure with info on the way we want the DIB}
  FillChar(pDibHeader^, SizeOf(TBITMAPINFO) + (SizeOf(TRGBQUAD) *
    256), #0);
  PBITMAPINFOHEADER(pDibHeader)^.biSize := SizeOf(TBITMAPINFOHEADER);
  PBITMAPINFOHEADER(pDibHeader)^.biPlanes := 1;
  PBITMAPINFOHEADER(pDibHeader)^.biBitCount := 8;
  PBITMAPINFOHEADER(pDibHeader)^.biWidth := Self.Width;
  PBITMAPINFOHEADER(pDibHeader)^.biHeight := Self.Height;
  PBITMAPINFOHEADER(pDibHeader)^.biCompression := BI_RGB;

  {find out how much memory for the bits}
  GetDIBits(DC, MemBitmap, 0, Self.Height, nil, TBITMAPINFO(pDibHeader^),
    DIB_RGB_COLORS);

  {Alloc memory for the bits}
  hBits := GlobalAlloc(GHND, PBITMAPINFOHEADER(pDibHeader)^.BiSizeImage);

  {Get a pointer to the bits}
  pBits := GlobalLock(hBits);

  {Call fn again, but this time give us the bits!}
  GetDIBits(DC, MemBitmap, 0, Self.Height, pBits, PBitmapInfo(pDibHeader)^,
    DIB_RGB_COLORS);

  {Lets try a fixup for broken video drivers}
  if isDcPalDevice = True then
  begin
    for i := 0 to (pPal^.palNumEntries - 1) do
    begin
      PBitmapInfo(pDibHeader)^.bmiColors[i].rgbRed := pPal^.palPalEntry[i].peRed;
      PBitmapInfo(pDibHeader)^.bmiColors[i].rgbGreen := pPal^.palPalEntry[i].peGreen;
      PBitmapInfo(pDibHeader)^.bmiColors[i].rgbBlue := pPal^.palPalEntry[i].peBlue;
    end;
    FreeMem(pPal, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)));
  end;
  {Release the screen dc}
  ReleaseDC(0, DC);
  {Delete the bitmap}
  DeleteObject(MemBitmap);

  {Start print job}
  Printer.BeginDoc;

  {Scale print size }
  ScaleX := Self.Width * 3;
  ScaleY := Self.Height * 3;

  {
  if Printer.PageWidth < Printer.PageHeight then
  begin
    ScaleX := Printer.PageWidth;
    ScaleY := Self.Height*(Printer.PageWidth/Self.Width);
  end
  else
  begin
    ScaleX := Self.Width*(Printer.PageHeight/Self.Height);
    ScaleY := Printer.PageHeight;
  end;
  }

  {Just incase the printer drver is a palette device}
  isDcPalDevice := False;
  if GetDeviceCaps(Printer.Canvas.Handle, RASTERCAPS) and RC_PALETTE = RC_PALETTE then
  begin
    {Create palette from dib}
    GetMem(pPal, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)));
    FillChar(pPal^, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)),
      #0);
    pPal^.palVersion := $300;
    pPal^.palNumEntries := 256;
    for i := 0 to (pPal^.palNumEntries - 1) do
    begin
      pPal^.palPalEntry[i].peRed := PBitmapInfo(pDibHeader)^.bmiColors[i].rgbRed;
      pPal^.palPalEntry[i].peGreen := PBitmapInfo(pDibHeader)^.bmiColors[i].rgbGreen;
      pPal^.palPalEntry[i].peBlue := PBitmapInfo(pDibHeader)^.bmiColors[i].rgbBlue;
    end;
    pal := CreatePalette(pPal^);
    FreeMem(pPal, SizeOf(TLOGPALETTE) + (255 * SizeOf(TPALETTEENTRY)));
    OldPal := SelectPalette(Printer.Canvas.Handle, pal, False);
    isDcPalDevice := True
  end;
  {send the bits to the printer}
  StretchDiBits(Printer.Canvas.Handle, 0, 0, Round(ScaleX), Round(ScaleY),
    0, 0, Self.Width, Self.Height, pBits, PBitmapInfo(pDibHeader)^,
    DIB_RGB_COLORS, SRCCOPY);

  {Just incase you printer drver is a palette device}
  if isDcPalDevice = True then
  begin
    SelectPalette(Printer.Canvas.Handle, OldPal, False);
    DeleteObject(pal);
  end;
  {Clean up allocated memory}
  GlobalUnlock(hBits);
  GlobalFree(hBits);
  GlobalUnlock(hDibHeader);
  GlobalFree(hDibHeader);

  {end the print job}
  Printer.EndDoc;
end;

2005. augusztus 13., szombat

How to iterate through both a master and a detail table


Problem/Question/Abstract:

My tables need to have master-detail relationship, but I still want to iterate through all the records in a table (detail...). My tables are:

Associate- employee name, leads, supervisor, etc...
Quality Score- associate name, etc...
Productivity Score- associate name, etc...

Obviously, these will be linked via the associate name. However, I cannot programmatically iterate through all the records after being linked. What I want to do is get the average quality score, productivity score, etc... for the team lead and supervisor. Any ideas?

Answer:

You can do something like:

{ ... }
MasterTable.First;
while not MasterTable.EOF do
begin
  while not DetailTable.EOF do
  begin
    { do your stuff }
    DetailTable.Next;
  end;
  MasterTable.Next;
end;

2005. augusztus 12., péntek

Detect an HTTP proxy


Problem/Question/Abstract:

Detect an HTTP proxy

Answer:

If you write a core http client, e.g. from socket level, you may need to detect whether there is an http proxy used. This includes the name of the proxy server and the port number it operates on. Such proxy servers are often used where a firewall is installed.

Luckily IE is installed on many Windows systems, and IE puts this information in the registry under

\Software\Microsoft\Windows\CurrentVersion\Internet Settings

The following procedure GetProxy retrieves the host name, port number and whether the proxy is enabled. You can use it as shown in the FormCreate() event handler.

Note: The value ProxyEnable should be a DWord, but sometimes it may be stored as binary or as a string, depending on the version of IE that the user has installed. The code below evaluates the type and reads it appropriately.


unit fProxy;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Registry;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

function GetProxy(var Host: string; var Port: integer; var ProxyEnabled: boolean): boolean;
var
  s: string;
  p: integer;
begin
  with TRegistry.Create do
  begin
    RootKey := HKEY_CURRENT_USER;
    ProxyEnabled := false;
    s := '';
    OpenKey('\Software\Microsoft\Windows\CurrentVersion\Internet Settings', True);
    if ValueExists('ProxyServer') then
      s := ReadString('ProxyServer');

    if s <> '' then
    begin
      p := pos(':', s);
      if p = 0 then
        p := length(s) + 1;
      Host := copy(s, 1, p - 1);
      try
        Port := StrToInt(copy(s, p + 1, 999));
      except
        Port := 80;
      end;

      ProxyEnabled := true;
    end;

    if ValueExists('ProxyEnable') then
    begin
      case GetDataType(sProxyEnable) of
        rdString,
          rdExpandString:
          begin
            sPortTmp := AnsiLowerCase(ReadString(sProxyEnable));
            ProxyEnabled := true;
            if pos(' ' + sPortTmp + ' ', ' yes true t enabled 1 ') > 0 then
              ProxyEnabled := true
            else if pos(' ' + sPortTmp + ' ', ' no false f none disabled 0 ') > 0 then
              ProxyEnabled := false
          end;
        rdInteger:
          begin
            ProxyEnabled := ReadBool(sProxyEnable);
          end;
        rdBinary:
          begin
            ProxyEnabled := true;
            ReadBinaryData(sProxyEnable, ProxyEnabled, 1);
          end;
      end;
    end;

    Free;
  end;

  Result := s <> '';
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Host: string;
  Port: integer;
  ProxyEnabled: boolean;
const
  YesNo: array[false..true] of string = (' not ', '');
begin
  // get proxy information
  if GetProxy(Host, Port, ProxyEnabled) then
    ShowMessage(Format('Your proxy is %s on port %d, it is%s enabled.', [Host, Port, YesNo[ProxyEnabled]]))
  else
    ShowMessage('No proxy detected');
end;

end.

2005. augusztus 11., csütörtök

Read the content of Internet Explorer's "Favourites" folder


Problem/Question/Abstract:

I would like to read the Properties of all entries in 'My Favorites' folder which contains all the web sites (URLs) addresses used by Internet Explorer. I would like to read the addresses and save them in a database. It's easier to do in Netscape because Netscape uses an HTML file that can be parsed easily.

Answer:

Solve 1:

The favourites folder is not very special. It just has some additions to the visibility. This function gets the location of it (no final backslash):

uses
  ShlObj;

function FavouritesPath: string;
var
  FilePath: array[0..MAX_PATH] of char;
begin
  SHGetSpecialFolderPath(0, FilePath, CSIDL_FAVORITES, false);
  Result := FilePath;
end;

Then you can traverse this folder and search for all *.url files. This is done by FindFirst/ FindNext. Use a recursive procedure if you want the subfolders, too.

To get the shortcut from these files, you can use this function (BTW: This is how TIniFile reads a string):

function GetInternetShortCut(const Filename: string): string;
var
  Buffer: array[0..2047] of Char;
begin
  SetString(Result, Buffer, GetPrivateProfileString('InternetShortcut',
    PChar('URL'), nil, Buffer, SizeOf(Buffer), PChar(Filename)));
end;

Example:

GetInternetShortcut(FavouritesPath + '\OneOfMyFavourites.url')


Solve 2:

I'm not sure if SHGetSpecialFolderPath is available on all Win32 platforms. Alternatively, you can use:

function FavoritesPath: string;
var
  FilePath: array[0..MAX_PATH] of Char;
  IDL: PItemIDList;
begin
  Result := '';
  if Succeeded(SHGetSpecialFolderLocation(0, CSIDL_FAVORITES, IDL)) then
    if SHGetPathFromIDList(IDL, FilePath) then
      Result := FilePath;
end;

2005. augusztus 10., szerda

How to jump directly to the WinHelp search dialog


Problem/Question/Abstract:

How to jump directly to the WinHelp search dialog

Answer:

procedure TForm1.HelpSearch(Sender: TObject);
var
  HelpMacro: pchar;
begin
  HelpMacro := 'Search()';
  with Application do
  begin
    Application.HelpContext(1);
    HelpCommand(HELP_COMMAND, longint(HelpMacro));
  end;
end;

2005. augusztus 9., kedd

Get the published properties of an persistent object


Problem/Question/Abstract:

How to get the published properties of an persistent object / Using the pPropInfo-Pointer and the RTTI of Delphi

Answer:

The TypeInfo unit of Delphi declares several types and functions that gives you easy access to the puplished properties of an object and other informations.
You can obtain a list of the published properties of a class and get the name an type of each property.

The TypeInfo funtion returns a pointer to a type information record. The TypInfo unit declares a real type, that is, a pointer to a TTypeInfo record :

PTypeInfo = ^TTypeInfo;
TTypeInfo = record
  Kind: TTypeKind;
  Name: ShortString;
end;

The TTypeKind datatype describes the Datatype , returned by the GetTypeData function.

TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
  tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
  tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray);
TTypeKinds = set of TTypeKind;

Well ... for our first step to access the objects published properties we need to use the PPropInfo-pointer.

PPropInfo = ^TPropInfo;
TPropInfo = packed record
  PropType: PPTypeInfo;
  GetProc: Pointer;
  SetProc: Pointer;
  StoredProc: Pointer;
  Index: Integer;
  Default: Longint;
  NameIndex: SmallInt;
  Name: ShortString;
end;

To clarify it, please take a look at this example :

function GetFontSize(Obj: TPersistent): Integer;
{
in this Procedure we want to get the pPropInfo-pointer - pointing
on the Font-Property from an arbitrary TPersistent-Class.
The return-value in this instance will be the font-size ( if the font
property exists , if not -> the return value will be -1 )
}
var
  PropInfo: PPropInfo;
begin
  RESULT := -1;
  // Get the PPropInfo-Pointer for Font of the TPersistent obj
  PropInfo := GetPropInfo(Obj, 'Font');
  // At first we will find out if the property FONT exists
  if PropInfo = nil then
    EXIT; // The Property doesn't exists
  {
    TFont is not an ordinal-Type - therefore will have to control if
    Typekind of the TypeInfo-Class is set to tkClass
  }
  if PropInfo.PropType^.Kind <> tkClass then
    EXIT; // property isn't a tkClass type
  {
    now, we now that the TypeKind of die PropInfo-pointer is a class .
    last but not least we will use the GetObjectProp, the return-value
    of this function is a TObject. Subsequently, we will use this object as
    a TFont to get the Size value.
  }
  RESULT := ((GetObjectProp(Obj, PropInfo)) as TFont).Size;
end;

But to get the complete list of all properties of a TPersistent-Class we will need the pPropList-Type . This type is a simple pointer-array and the magic key to all Property-Informations and their structures.

Take a look at this :

procedure TForm1.Button1Click(Sender: TObject);

const
  tkOrdinal = [tkEnumeration, tkInteger, tkChar, tkSet, tkWChar]; //Filter

begin
  {
    in this method of the mainform-class we are seeking for all ordinal-type
    properties of the edit1-component. The from the GetPropertyList method
    returned list of all properties will be written into the Listbox1. You can
    replace the obj parameter with an arbitrary TObject ( but usually TPersistent
    objects ).
    For another filter please take a look at the TTypeKinds-set.
  }
  GetPropertyList(Edit1, ListBox1.Items, tkOrdinal);
end;

procedure GetPropertyList(Obj: TObject; List: TStrings; Filter: TTypeKinds);
var
  PropList: pPropList;
  count, i: Integer;
begin
  List.Clear;
  // Here we'll get the count of the given properties, ...
  Count := GetPropList(Obj.ClassInfo, Filter, nil);
  // ...and create room for the PropList,...
  GetMem(PropList, Count * SizeOf(PPropInfo));
  // ...get the Proplist-Data,...
  GetPropList(Obj.ClassInfo, Filter, PropList);
  // ...and write the property-names into the StringList
  for i := 0 to Count - 1 do
    List.Add(Proplist[i].Name);
end;

2005. augusztus 8., hétfő

Get the CRC


Problem/Question/Abstract:

How to get the CRC value

Answer:

implementation
const
  Table: array[0..255] of LongInt = ($00000000, $77073096, $EE0E612C, $990951BA,
    $076DC419, $706AF48F, $E963A535, $9E6495A3, $0EDB8832, $79DCB8A4, $E0D5E91E,
    $97D2D988, $09B64C2B, $7EB17CBD, $E7B82D07, $90BF1D91, $1DB71064, $6AB020F2,
    $F3B97148, $84BE41DE, $1ADAD47D, $6DDDE4EB, $F4D4B551, $83D385C7, $136C9856,
    $646BA8C0, $FD62F97A, $8A65C9EC, $14015C4F, $63066CD9, $FA0F3D63, $8D080DF5,
    $3B6E20C8, $4C69105E, $D56041E4, $A2677172, $3C03E4D1, $4B04D447, $D20D85FD,
    $A50AB56B, $35B5A8FA, $42B2986C, $DBBBC9D6, $ACBCF940, $32D86CE3, $45DF5C75,
    $DCD60DCF, $ABD13D59, $26D930AC, $51DE003A, $C8D75180, $BFD06116, $21B4F4B5,
    $56B3C423, $CFBA9599, $B8BDA50F, $2802B89E, $5F058808, $C60CD9B2, $B10BE924,
    $2F6F7C87, $58684C11, $C1611DAB, $B6662D3D, $76DC4190, $01DB7106, $98D220BC,
    $EFD5102A, $71B18589, $06B6B51F, $9FBFE4A5, $E8B8D433, $7807C9A2, $0F00F934,
    $9609A88E, $E10E9818, $7F6A0DBB, $086D3D2D, $91646C97, $E6635C01, $6B6B51F4,
    $1C6C6162, $856530D8, $F262004E, $6C0695ED, $1B01A57B, $8208F4C1, $F50FC457,
    $65B0D9C6, $12B7E950, $8BBEB8EA, $FCB9887C, $62DD1DDF, $15DA2D49, $8CD37CF3,
    $FBD44C65, $4DB26158, $3AB551CE, $A3BC0074, $D4BB30E2, $4ADFA541, $3DD895D7,
    $A4D1C46D, $D3D6F4FB, $4369E96A, $346ED9FC, $AD678846, $DA60B8D0, $44042D73,
    $33031DE5, $AA0A4C5F, $DD0D7CC9, $5005713C, $270241AA, $BE0B1010, $C90C2086,
    $5768B525, $206F85B3, $B966D409, $CE61E49F, $5EDEF90E, $29D9C998, $B0D09822,
    $C7D7A8B4, $59B33D17, $2EB40D81, $B7BD5C3B, $C0BA6CAD, $EDB88320, $9ABFB3B6,
    $03B6E20C, $74B1D29A, $EAD54739, $9DD277AF, $04DB2615, $73DC1683, $E3630B12,
    $94643B84, $0D6D6A3E, $7A6A5AA8, $E40ECF0B, $9309FF9D, $0A00AE27, $7D079EB1,
    $F00F9344, $8708A3D2, $1E01F268, $6906C2FE, $F762575D, $806567CB, $196C3671,
    $6E6B06E7, $FED41B76, $89D32BE0, $10DA7A5A, $67DD4ACC, $F9B9DF6F, $8EBEEFF9,
    $17B7BE43, $60B08ED5, $D6D6A3E8, $A1D1937E, $38D8C2C4, $4FDFF252, $D1BB67F1,
    $A6BC5767, $3FB506DD, $48B2364B, $D80D2BDA, $AF0A1B4C, $36034AF6, $41047A60,
    $DF60EFC3, $A867DF55, $316E8EEF, $4669BE79, $CB61B38C, $BC66831A, $256FD2A0,
    $5268E236, $CC0C7795, $BB0B4703, $220216B9, $5505262F, $C5BA3BBE, $B2BD0B28,
    $2BB45A92, $5CB36A04, $C2D7FFA7, $B5D0CF31, $2CD99E8B, $5BDEAE1D, $9B64C2B0,
    $EC63F226, $756AA39C, $026D930A, $9C0906A9, $EB0E363F, $72076785, $05005713,
    $95BF4A82, $E2B87A14, $7BB12BAE, $0CB61B38, $92D28E9B, $E5D5BE0D, $7CDCEFB7,
    $0BDBDF21, $86D3D2D4, $F1D4E242, $68DDB3F8, $1FDA836E, $81BE16CD, $F6B9265B,
    $6FB077E1, $18B74777, $88085AE6, $FF0F6A70, $66063BCA, $11010B5C, $8F659EFF,
    $F862AE69, $616BFFD3, $166CCF45, $A00AE278, $D70DD2EE, $4E048354, $3903B3C2,
    $A7672661, $D06016F7, $4969474D, $3E6E77DB, $AED16A4A, $D9D65ADC, $40DF0B66,
    $37D83BF0, $A9BCAE53, $DEBB9EC5, $47B2CF7F, $30B5FFE9, $BDBDF21C, $CABAC28A,
    $53B39330, $24B4A3A6, $BAD03605, $CDD70693, $54DE5729, $23D967BF, $B3667A2E,
    $C4614AB8, $5D681B02, $2A6F2B94, $B40BBE37, $C30C8EA1, $5A05DF1B, $2D02EF8D);

type
  buffer = array[1..65521] of byte; {largest buffer that can be
  allocated on heap }
var
  i: WORD;
  q: ^buffer;

procedure CalcCRC32(p: pointer; nbyte: Word; var CRCvalue: LongInt);
{The following is a little cryptic (but executes very quickly). The algorithm is as follows:
1. exclusive-or the input byte with the low-order portion of the CRC register to get an INDEX
2. shift the CRC register eight bits to the right
3. exclusive-or the CRC register with the contents of Table[INDEX]
4. repeat steps 1 through 3 for all bytes}

var
  i: Word;
begin
  q := p;
  for i := 1 to nBYTE do
    CRCvalue := (CRCvalue shr 8) xor
      Table[q^[i] xor (CRCvalue and $000000FF)]
end {CalcCRC32};

procedure CalcFileCRC32(FromName: string; var CRCvalue: LongInt; var IOBuffer:
  pointer; BufferSize: Word; var TotalBytes: LongInt; var error: WORD);
var
  BytesRead: integer;
  FromFile: file;

begin
  FileMode := 0; {Turbo default is 2 for R/W; 0 is for R/O}
  CRCValue := $FFFFFFFF;
  Assign(FromFile, FromName);
{$I-}Reset(FromFile, 1);
{$I+}Error := IOResult;
  if error = 0 then
  begin
    TotalBytes := 0;
    repeat BlockRead(FromFile, IOBuffer^, BufferSize, BytesRead);
      CalcCRC32(IOBuffer, BytesRead, CRCvalue);
      Inc(TotalBytes,
        BytesRead)
    until BytesRead = 0;
    Close(FromFile)
  end;
  CRCvalue := not CRCvalue
end {CalcFileCRC32};

2005. augusztus 7., vasárnap

Store the HTML source code of a TWebBrowser document into a string


Problem/Question/Abstract:

How can I access the HTML content of a TWebBrowser object? I tried to use OLECMDID_SAVEAS to save it as a file first and then access it afterwards. But it always asks for the directory and file name for this file.

Answer:

Use the following function to store the HTML source code to a string (e.g. a TStringStream):

procedure SaveDocumentSourceToStream(Document: IDispatch; Stream: TStream);
var
  PersistStreamInit: IPersistStreamInit;
  StreamAdapter: IStream;
begin
  {Delete stream content}
  Stream.Size := 0;
  Stream.Position := 0;
  {IPersistStreamInit - get document interface}
  if Document.QueryInterface(IPersistStreamInit, PersistStreamInit) = S_OK then
  begin
    {Use stream adapter to get the IStream Interface to our stream}
    StreamAdapter := TStreamAdapter.Create(Stream, soReference);
    {Save data from document into stream}
    PersistStreamInit.Save(StreamAdapter, False);
    {Destroy stream adapter. Optional, as it would happen anyway}
    StreamAdapter := nil;
  end;
end;

2005. augusztus 6., szombat

How to load and scale a JPEGImage into a TImage


Problem/Question/Abstract:

How to load and scale a JPEGImage into a TImage

Answer:

{ ... }
Image1.Picture.Graphic := nil;
try
  Image1.Picture.Graphic := nil;
  Image1.Picture.LoadFromFile(jpegfile);
except
  on EInvalidGraphic do
    Image1.Picture.Graphic := nil;
end;
if Image1.Picture.Graphic is TJPEGImage then
begin
  TJPEGImage(Image1.Picture.Graphic).Scale := Self.Scale;
  TJPEGImage(Image1.Picture.Graphic).Performance := jpBestSpeed;
end;

2005. augusztus 5., péntek

How to open or close all datasets in on the form


Problem/Question/Abstract:

How to open or close all datasets in on the form

Answer:

Open datasets: OpenDataSet(MyForm);

procedure OpenDataSet(FormName: TForm);
var
  I: Integer;
begin
  for I := FormName.ComponentCount - 1 downto 0 do
    if (FormName.Components[I] is TADOTable) then
    begin
      (FormName.Components[I] as TADOTable).Open;
    end;
end;

Close datasets: CloseDataSet(MyForm);

procedure CloseDataSet(FormName: TForm);
var
  I: Integer;
begin
  for I := FormName.ComponentCount - 1 downto 0 do
    if (FormName.Components[I] is TADOTable) then
    begin
      (FormName.Components[I] as TADOTable).Close;
    end;
end;

2005. augusztus 4., csütörtök

How to get a list of all windows on the Desktop and their handles


Problem/Question/Abstract:

How to get a list of all windows on the Desktop and their handles

Answer:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure GetWins;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  WindowList: TList;

implementation

{$R *.DFM}

function GetWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
begin
  Result := True;
  WindowList.Add(Pointer(Handle));
end;

procedure TForm1.GetWins;
var
  TopWindow, CurrentWindow: HWND;
  Dest: array[0..80] of char;
  ClassName: array[0..80] of char;
  i: Integer;
begin
  try
    WindowList := TList.Create;
    TopWindow := Handle;
    EnumWindows(@GetWindows, Longint(@TopWindow));
    CurrentWindow := TopWindow;
    for i := 0 to WindowList.Count - 1 do
    begin
      CurrentWindow := GetNextWindow(CurrentWindow, GW_HWNDNEXT);
      GetWindowText(CurrentWindow, Dest, sizeof(Dest) - 1);
      GetClassName(CurrentWindow, ClassName, sizeof(ClassName) - 1);
      if StrLen(Dest) > 0 then
        Memo1.Lines.Add(Dest + ' = ' + ClassName + ' - ' + IntToStr(CurrentWindow));
    end;
  finally
    WindowList.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetWins;
end;

end.

2005. augusztus 3., szerda

How to generate a circle through three points


Problem/Question/Abstract:

How to generate a circle through three points

Answer:

Let the three given points be a, b, c. Use _0 and _1 to represent x and y coordinates. The coordinates of the center p = (p_0,p_1) of the circle determined by a, b, and c are:



A = b_0 - a_0;
B = b_1 - a_1;
C = c_0 - a_0;
D = c_1 - a_1;

E = A * (a_0 + b_0) + B * (a_1 + b_1);
F = C * (a_0 + c_0) + D * (a_1 + c_1);

G = 2.0 * (A * (c_1 - b_1) - B * (c_0 - b_0));

p_0 = (D * E - B * F) / G;
p_1 = (A * F - C * E) / G;



If G is zero then the three points are collinear and no finite-radius circle through them exists. Otherwise, the radius of the circle is:



r^2 = (a_0 - p_0)^2 + (a_1 - p_1)^2

2005. augusztus 2., kedd

Invert the color of a TEdit


Problem/Question/Abstract:

I have a TEdit control and there are two color properties I wish to set: TEdit.Color and TEdit.Font.Color. I did something like

TEdit.Color := clBlack
TEdit.Font.Color := TEdit.Color xor $7FFFFFFF;

but TEdit.Font.Color doesn't show as contrast of TEdit.Color. Can anybody advise on that. I wish to make the TEdit.Color and TEdit.Font.Color always contrast to each other. My application allows the user to change the TEdit.color at runtime.

Answer:

Try this one:

function InverseColor(color: TColor): TColor;
var
  rgb_: TColorRef;

  function Inv(b: Byte): Byte;
  begin
    if b > 128 then
      result := 0
    else
      result := 255;
  end;

begin
  rgb_ := ColorToRgb(color);
  rgb_ := RGB(Inv(GetRValue(rgb_)), Inv(GetGValue(rgb_)), Inv(GetBValue(rgb_)));
  Result := rgb_;
end;

2005. augusztus 1., hétfő

How to launch the Windows Control Panel


Problem/Question/Abstract:

How to launch the Windows Control Panel

Answer:

This is quite useful if you are writing a shell replacement.


This will launch the control panel:

WinExec('rundll32 shell32.dll, Control_RunDLL', SW_SHOW);

Launch display properties:

WinExec('rundll32 shell32.dll, Control_RunDLL desk.cpl, , 0', SW_SHOW);