2010. február 14., vasárnap

How to remove the scrollbar of a TListBox


Problem/Question/Abstract:

I want to remove the scrollbar of a TListBox and control scrolling with a separate scrollbar. Anyone has an idea how to remove it?

Answer:

This requires a somewhat dubious hack. Derive a new component from TListBox, like this:


type
  TNoVScrolllistbox = class(TListBox)
  private
    procedure WMNCCalcSize(var msg: TMessage); message WM_NCCALCSIZE;
  end;

procedure TNoVScrolllistbox.WMNCCalcSize(var msg: TMessage);
var
  style: Integer;
begin
  style := GetWindowLong(handle, GWL_STYLE);
  if (style and WS_VSCROLL) <> 0 then
    SetWindowLong(handle, GWL_STYLE, style and not WS_VSCROLL);
  inherited;
end;


This technique works for nearly any control that uses the standard window scrollbars.

2010. február 13., szombat

Create a balloon-shaped tooltip


Problem/Question/Abstract:

How to create a balloon-shaped tooltip

Answer:

Solve 1:

You could show a ToolTip control, which would have the appearance of a cartoon "balloon", with rounded corners and a stem pointing to the item. Also, there could be a multiline text and a caption with an icon. But in order to see this, be sure that there are Version 5.80 of Comctl32.dll and version 5.0 of Shlwapi.dll installed on your machine. Below is the code which would force the tooltip to show itself.

{ ... }
var
  FTTHandle: THandle;

const
  TTM_SETTITLE = $0420;
  TTS_BALLOON = $040;

procedure TForm1.SpeedButton2Click(Sender: TObject);
var
  ti: TOOLINFO;
  XRect: TRect;
begin
  FTTHandle := CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nil, WS_POPUP or
    TTS_NOPREFIX or TTS_BALLOON, 0, 0, 0, 0, Handle, 0, Application.Handle, nil);
  ti.cbSize := sizeof(TOOLINFO);
  ti.uFlags := TTF_SUBCLASS or TTF_DI_SETITEM;
  ti.hwnd := Handle;
  ti.hinst := Application.Handle;
  ti.uId := 0;
  ti.lpszText := 'First line' + #$0D#$0A + 'Second line' + #$0D#$0A + {...}
                + 'Last Line';
  {ti.lpszText := LPSTR_TEXTCALLBACK;}
  XRect := ClientRect;
  ti.rect.left := XRect.left;
  ti.rect.top := XRect.top;
  ti.rect.right := XRect.right;
  ti.rect.bottom := XRect.bottom;
  SendMessage(FTTHandle, TTM_ADDTOOL, 0, integer(@ti));
  SendMessage(FTTHandle, TTM_SETTITLE, 1, integer(PChar('Title')));
  SendMessage(FTTHandle, TTM_SETMAXTIPWIDTH, 0, 100);
  SendMessage(FTTHandle, TTM_SETTIPBKCOLOR, clMoneyGreen, 0);
  SendMessage(FTTHandle, TTM_SETTIPTEXTCOLOR, clNavy, 0);
end;

Basically, you could even perform some custom painting on the tooltip's surface. In order to do this add a WM_NOTIFY message handler to the form and handle the NM_CUSTOMDRAW notification. Below is an example:

{ ... }
type
  TForm1 = class(TForm)
    SpeedButton2: TSpeedButton;
    procedure SpeedButton2Click(Sender: TObject);
  protected
    procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY;
  end;

  { ... }

procedure TForm1.WMNotify(var Message: TWMNotify);
var
  XCanvas: TCanvas;
  XRect: TRect;
begin
  inherited;
  if integer(Message.NMHdr.hwndFrom) = integer(FTTHandle) then
  begin
    case Message.NMHdr.code of
      TTN_POP:
        begin
          {do something here, when tooltip hides}
        end;
      TTN_SHOW:
        begin
          {do something here, when tooltip show itself}
        end;
      TTN_NEEDTEXT:
        begin
          PTOOLTIPTEXT(Message.NMHdr).lpszText := 'some text...';
          {here you could set new text to the tooltip, but only in
                                        case you've specified a LPSTR_TEXTCALLBACK constant
                                        in the lpszText identifier, in the SpeedButton2Click method}
        end;
      NM_CUSTOMDRAW:
        begin
          with PNMCustomDraw(Message.NMHdr)^ do
          begin
            if dwDrawStage = CDDS_PREPAINT then
            begin
              Message.Result := CDRF_NOTIFYPOSTPAINT;
            end
            else if dwDrawStage = CDDS_POSTPAINT then
            begin
              XCanvas := TCanvas.Create;
              try
                XCanvas.Handle := hdc;
                XRect := PNMCustomDraw(Message.NMHdr)^.rc;
                XRect.Left := XRect.Right - 40;
                XRect.Bottom := XRect.Top + 30;
                XCanvas.Brush.Color := clBlue;
                XCanvas.FillRect(RECT(XRect.Left, XRect.Top, XRect.Right,
                                                                        XRect.Top + 15));
                XCanvas.Brush.Color := clYellow;
                XCanvas.FillRect(RECT(XRect.Left, XRect.Top + 15,
                                                                        XRect.Right, XRect.Top + 30));
                XCanvas.Brush.Color := clBlack;
                XCanvas.FrameRect(XRect);
              finally
                XCanvas.Free;
              end;
            end;
          end;
        end;
    end;
  end;
end;


Solve 2:

unit Unit1;

interface

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

const
  TTS_BALLOON = $40;
  TTM_SETTITLE = (WM_USER + 32);

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

var
  Form1: TForm1;
  hTooltip: Cardinal;
  ti: TToolInfo;
  buffer: array[0..255] of char;

implementation

{$R *.dfm}

procedure CreateToolTips(hWnd: Cardinal);
begin
  hToolTip := CreateWindowEx(0, 'Tooltips_Class32', nil, TTS_ALWAYSTIP or TTS_BALLOON,
    Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT), hWnd, 0, hInstance, nil);
  if hToolTip <> 0 then
  begin
    SetWindowPos(hToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or
      SWP_NOSIZE or SWP_NOACTIVATE);
    ti.cbSize := SizeOf(TToolInfo);
    ti.uFlags := TTF_SUBCLASS;
    ti.hInst := hInstance;
  end;
end;

procedure AddToolTip(hwnd: dword; lpti: PToolInfo; IconType: Integer; Text, Title:
  PChar);
var
  Item: THandle;
  Rect: TRect;
begin
  Item := hWnd;
  if (Item <> 0) and (GetClientRect(Item, Rect)) then
  begin
    lpti.hwnd := Item;
    lpti.Rect := Rect;
    lpti.lpszText := Text;
    SendMessage(hToolTip, TTM_ADDTOOL, 0, Integer(lpti));
    FillChar(buffer, sizeof(buffer), #0);
    lstrcpy(buffer, Title);
    if (IconType > 3) or (IconType < 0) then
      IconType := 0;
    SendMessage(hToolTip, TTM_SETTITLE, IconType, Integer(@buffer));
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateToolTips(Form1.Handle);
  AddToolTip(Memo1.Handle, @ti, 1, Memo1.Lines.GetText, 'Memo Text');
end;

end.

2010. február 12., péntek

Accept files dragged over an application


Problem/Question/Abstract:

If you have an application that works with files, you probably want that users would be able to drag and drop files over your application to open them.

Answer:

For your application be able to accept files when dropped over it, you need to tell windows that your application can accept files. To do this, you have two options:

Make use of Params.ExStyle
  
To be able to accept files without much trouble you have just to override the protected the protected procedure CreateParams and write the following:

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  // Register the window to be able to accept dropped files
  Params.ExStyle := Params.ExStyle or WS_EX_ACCEPTFILES;
end;

Use API function DragAcceptFiles
  
The other option is use DragAcceptFiles API function. This way you can control if your application can accept dropped files or not without the need to change Params.ExStyle

Both options of activation activate the WM_DROPFILES windows message, and it's here that you'll have to process the dropped files.

The following routine gives the essential part of what you need to do:

procedure TForm1.WMDropFiles(var Message: TWMDropFiles);
// The WM_DROPFILES message is sent when the user releases the left mouse button
//    while the cursor is in the window of an application that has registered
//    itself as a recipient of dropped files.
var
  FNumFiles: Integer;
  i: Integer;
  BufSize: Integer;
  FFilePath: array of char;
  FFileName: string;

begin
  // How many files were dropped ?
  FNumFiles := DragQueryFile(Message.Drop, $FFFFFFFF, nil, 0);
  // Process all files in the list
  for i := 0 to FNumFiles - 1 do
  begin
    // Get the buffer size to old the filename
    BufSize := DragQueryFile(Message.Drop, i, nil, 0);
    // Get filename. This filename is a null-terminated string.
    SetLength(FFilePath, BufSize + 1);
    DragQueryFile(Message.Drop, i, PChar(FFilePath), BufSize + 1);
    // Check if the dropped file extension can be accepted
    FFileName := ExtractFileName(PChar(FFilePath));

    // DO WHATEVER YOU NEED
  end;
  // The DragFinish function releases memory that Windows allocated for use in
  //    transferring filenames to the application.
  DragFinish(Message.Drop);
end;

Attached is a project sample. It's a small file text viewer and it implement's a bit more than the above, as you can see in the following screen shot.

2010. február 11., csütörtök

Detect a form movement


Problem/Question/Abstract:

How to detect a form movement

Answer:

Solve 1:

type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure WMEXITSIZEMOVE(var Message: TMessage); message WM_EXITSIZEMOVE;
    procedure WMENTERSIZEMOVE(var Message: TMessage); message WM_ENTERSIZEMOVE;

implementation

procedure TForm1.WMENTERSIZEMOVE(var Message: TMessage);
begin
  Form1.Caption := 'Starting moving and sizing';
end;

procedure TForm1.WMEXITSIZEMOVE(var Message: TMessage);
begin
  Form1.Caption := 'Finished moving and sizing';
end;


Solve 2:

Handle the WM_MOVING or WM_WINDOWPOSCHANGING message from windows, i.e.:

{ Private declarations }

procedure WMWINDOWPOSCHANGING(var msg: TWMWINDOWPOSCHANGING);
  message WM_WINDOWPOSCHANGING;

procedure TForm1.WMWINDOWPOSCHANGING(var msg: TWMWINDOWPOSCHANGING);
var
  r: TRect;
begin
  if ((SWP_NOMOVE or SWP_NOSIZE) and msg.WindowPos^.flags) <> (SWP_NOMOVE
    or SWP_NOSIZE) then
  begin
    { Window is moved or sized, get usable screen area }
    { Do something here }
  end;
  inherited;
end;

2010. február 10., szerda

Copy a file using a TFileStream


Problem/Question/Abstract:

How to copy a file using a TFileStream

Answer:

{ ... }
type
  TFileCopyUpdateEvent = procedure(const SrcFile, DestFile: string;
    CurrentPos, MaxSize: Integer) of object;

function Min(Val1, Val2: Integer): Integer;
begin
  Result := Val1;
  if Val2 < Val1 then
    Result := Val2;
end;

{SrcFile and DestFile are the fully qualified filenames to the files to copy function}

MyFileCopy(SrcFile, DestFile: TFilename; OnUpdate: TFileCopyUpdateEvent = nil):
  Boolean;
const
  StreamBuf = 4096;
var
  Src, Dst: TFileStream;
  BufCount: Integer;
begin
  Src := nil;
  Dst := nil; {prevents .Free problems on exception}
  {allow everyone else any access}
  Src := TFileStream.Create(SrcFile, fmOpenRead or fmShareDenyNone);
  if FileExists(DestFile) then
    {this could cause an error if a user has the file open}
    Dst := TFileStream.Create(DestFile, fmOpenWrite or fmShareExclusive)
  else
    Dst := TFileStream.Create(DestFile, fmCreate or fmShareExclusive);
  try
    while Dst.Position < Dst.Size do
    begin
      BufCount := Min(StreamBuf, Dst.Size - Dst.Position);
      Src.CopyFrom(Dst, BufCount);
      if Assigned(OnUpdate) then {report progress every 4k}
        OnUpdate(SrcFile, DestFile, Dst.Position, Dst.Size);
    end;
  finally
    Src.Free;
    Dst.Free;
  end;
end;

2010. február 9., kedd

Extract swf from Flash Projector (EXE)


Problem/Question/Abstract:

How to extract swf from Flash Projector

Answer:

procedure ExeToSWF(ExeFile, aSWF: string);
var
  p: pointer;
  f: file;
  sz,
    swfsize: integer;
const
  SWF_FLAG: integer = $FA123456;
begin
  if not fileexists(ExeFile) then
  begin
    messagebox(Application.Handle, pchar('File not found'), pchar('Error'),
      MB_ICONERROR);
    exit;
  end;
  assignfile(f, ExeFile);
  reset(f, 1);
  seek(f, filesize(f) - (2 * sizeof(integer)));
  blockread(f, sz, sizeof(integer));
  if sz <> swf_flag then
  begin
    messagebox(Application.Handle, pchar('Not a valid Projector Exe'), pchar('Error'),
      MB_ICONERROR);
    closefile(f);
    exit;
  end;
  blockread(f, swfsize, sizeof(integer));
  seek(f, filesize(f) - (2 * sizeof(integer)) - swfsize);
  getmem(p, swfsize);
  blockread(f, p^, swfsize);
  closefile(f);
  assignfile(f, aSWF);
  rewrite(f, 1);
  blockwrite(f, p^, swfsize);
  closefile(f);
  freemem(p, swfsize);
  messagebox(Application.Handle, pchar('SWF Extracted'), pchar('Succes'),
    MB_ICONINFORMATION);
end;

Example:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExeToSWF('C:Desktopflash.exe', 'C:Desktopf.swf');
end;

end.

2010. február 8., hétfő

Undocumented: Delphi Visual Component Library Access License


Problem/Question/Abstract:

Undocumented: Delphi Visual Component Library Access License

Answer:

The SysUtils.pas unit contains some very interesting routines that are used by the VCL components to check if the correct version of Delphi is beeing used to compile the code (e.g. C/S components won't run if compiled with the Pro compiler).

Here are the functions:

function GDAL: LongWord;

Get Delphi Access License. Retreives the access licences resource. It checks if it is valid, if not an exception with the message 'Application is not licensed to use this feature' is raised.
The returned value is the decrypted first Access Licence (AL1).

procedure RCS;

Perform a check to see there is a Delphi Client Server licence. An exception is raised if the license is not valid.

procedure RPR;

Perform a check to see there is a Delphi Pro licence. An exception is raised if the license is not valid.

Other non exposed functions are:

function AL1(const P): LongWord;
function AL2(const P): LongWord;

These two functions return the decrypted value of the license value specified by P.

procedure ALV;

Raises an Access Licence Violation exception.

function ALR: Pointer;

Access License Resource loader. Returns a pointer to the loaded access license. An exception is raised if the resource is not found.

2010. február 7., vasárnap

Convert short file names to long ones


Problem/Question/Abstract:

How do I convert a short (alias) filename or directory name into its long equivalent?

Answer:

Solve 1:

{Parameters:

shortname:
File name or path to convert. This can be a fully qualified file name or a path relative to the current directory. It can contain long and / or short forms for the names.

Returns:
Fully qualified filename using the long names for all elements of the path.

Description:
Recursively uses FindFirst to find the long names for the path elements.

Error Conditions:
Will raise an exception if any part of the path was not found.

Created:
15.01.98 14:09:26 by Peter Below}

function GetLongFilename(shortname: string): string;

  function GetL(shortname: string): string;
  var
    srec: TSearchRec;
  begin
    { Lob off the last element of the passed name. If we received only a root name,
    e.g. c:\, ExtractFileDir returns the path unchanged. }
    Result := ExtractFileDir(shortname);
    if (Result <> shortname) then
    begin
      { We still have an unconverted path element. So convert the last one in
                  the current shortname and combine the resulting long name with what we get
                        by calling ourselves recursively with the rest of the path. }
      if FindFirst(shortname, faAnyfile, srec) = 0 then
      try
        Result := GetL(Result) + '\' + srec.Name;
      finally
        FindClose(srec);
      end
      else
        raise Exception.CreateFmt('Path %s does not exist!', [shortname]);
    end
    else
      { Only the root remains. Remove the backslash since the caller will add it
                  back anyway. }
      Delete(Result, length(result), 1);
  end;

begin
  { Create fully qualified path and pass it to the converter. }
  Result := GetL(ExpandFilename(shortname));
end;


Solve 2:

{Get LFN from 8.3}

function GetLongPathName(const PathName: string): string;
var
  Drive: string;
  Path: string;
  SearchRec: TSearchRec;
begin
  if PathName = '' then
    Exit;
  Drive := ExtractFileDrive(PathName);
  Path := Copy(PathName, Length(Drive) + 1, Length(PathName));
  if (Path = '') or (Path = '\') then
  begin
    Result := PathName;
    if Result[Length(Result)] = '\' then
      Delete(Result, Length(Result), 1);
  end
  else
  begin
    Path := GetLongPathName(ExtractFileDir(PathName));
    if FindFirst(PathName, faAnyFile, SearchRec) = 0 then
    begin
      Result := Path + '\' + SearchRec.FindData.cFileName;
      FindClose(SearchRec);
    end
    else
      Result := Path + '\' + ExtractFileName(PathName);
  end;
end;


Solve 3:

You could try the following. It should work on Win95 and above.

unit WhateverYouWantToCallIt;

interface

function LongPathFromShort(const ShortPath: string): string;

implementation

uses
  Windows, SysUtils, ActiveX, ShlObj;

function LongPathFromShort(const ShortPath: string): string;
var
  iAttributes: Cardinal;
  iEaten: Cardinal;
  IntfDesktop: IShellFolder;
  IntfMalloc: IMalloc;
  pItemList: PItemIDList;
  sFile: WideString;
  szFile: array[0..MAX_PATH] of Char;
begin
  Result := ShortPath;
  if not FileExists(ShortPath) then
    Exit;
  if Succeeded(SHGetDesktopFolder(IntfDesktop)) then
  begin
    sFile := ShortPath;
    iAttributes := 0;
    if Succeeded(IntfDesktop.ParseDisplayName(0, nil, POleStr(sFile),
      iEaten, pItemList, iAttributes)) then
    begin
      SHGetPathFromIDList(pItemList, szFile);
      Result := szFile;
      SHGetMalloc(IntfMalloc);
      IntfMalloc.Free(pItemList)
    end
  end
end;

end.


Solve 4:

GetFullPathName converts a relative path to an absolute path. You can use GetLongPathName, but this requires Win98 and later or Win2k and later.

function GetLongName(const APath: string): string;
var
  Buffer: array[0..MAX_PATH] of Char;
  Required: Integer;
begin
  Required := GetLongPathName(PChar(APath), Buffer, Length(Buffer));
  if Required > MAX_PATH then {Buffer too small}
  begin
    SetLength(Result, Required - 1);
    GetLongPathName(PChar(APath), Pointer(Result), Required);
  end
  else if Required = 0 then {Error}
    Result := APath
  else
    SetString(Result, Buffer, Required);
end;

For an ANSI only function you can reduce the above to:

function GetLongName(const APath: AnsiString): AnsiString;
var
  Buffer: array[0..MAX_PATH] of AnsiChar;
  Required: Integer;
begin
  Required := GetLongPathNameA(PChar(APath), Buffer, Length(Buffer));
  SetString(Result, Buffer, Required);
end;

If you need to support for Win95 or WinNT, you can use this function:

function GetLongPathName(Path: string): string;
var
  I: Integer;
  SearchHandle: THandle;
  FindData: TWin32FindData;
  IsBackSlash: Boolean;
begin
  Path := ExpandFileName(Path);
  Result := ExtractFileDrive(Path);
  I := Length(Result);
  if Length(Path) <= I then {only drive}
    Exit;
  if Path[I + 1] = '\' then
  begin
    Result := Result + '\';
    Inc(I);
  end;
  Delete(Path, 1, I);
  repeat
    I := Pos('\', Path);
    IsBackSlash := I > 0;
    if not IsBackSlash then
      I := Length(Path) + 1;
    SearchHandle := FindFirstFile(PChar(Result + Copy(Path, 1, I - 1)), FindData);
    if SearchHandle <> INVALID_HANDLE_VALUE then
    begin
      try
        Result := Result + FindData.cFileName;
        if IsBackSlash then
          Result := Result + '\';
      finally
        Windows.FindClose(SearchHandle);
      end;
    end
    else
    begin
      Result := Result + Path;
      Break;
    end;
    Delete(Path, 1, I);
  until Length(Path) = 0;
end;

2010. február 6., szombat

Gauge in a StatusBar


Problem/Question/Abstract:

How to put a component in a statusbar?

Answer:

uses Gauges
  { ... }

  public
  Gauge1: TGauge;
private
{ ... }

procedure TForm1.FormCreate(Sender: TObject);
begin
  Gauge1 := TGauge.Create(StatusBar1); //Ctreate it on the statusbar
  Gauge1.Parent := StatusBar1; //Parent Winodow
  Gauge1.Height := StatusBar1.Height - 6; //Height
  Gauge1.Top := 4; //Top
  Gauge1.BackColor := clSilver; //Make it cool
  Gauge1.BorderStyle := bsNone; //Border
  Gauge1.ForeColor := clRed; //Color of Gauge
  Gauge1.Left := StatusBar1.Width div 2; //Left Position
  Gauge1.Progress := 0; //Progress
end;

2010. február 5., péntek

Subclassing Non Delphi Windows


Problem/Question/Abstract:

How to subclass non Delphi windows

Answer:

Every window has a procedure associated with it that recieves all the messages that are sent to it. To subclass a window means to replace the procedure associated with the window by another user defined procedure. The main use of subclassing windows is to customize how a window works.

The handle of the procedure associated with the window can be got by calling the GetWindowLong function.

hproc: TFarproc;
hproc := TFarProc(GetWindowLong(hwnd, GWL_WNDPROC));

if hwnd is the handle of the function then hproc is the handle of the procedure associated with the window.

Now define a procedure that will replace the original procedure associated with the window.

For example see the code below.

type
  TForm1 = class(TForm)
  private
    hproc: TFarproc;
  protected
    procedure WndProc(var msg: TMessage);
  end;

Here the procedure WndProc will replace the original window procedure.

To replace the original procedure with the procedure WndProc you have to first call the function 'MakeObjectInstance'  defined in forms unit which converts a member procedure(Here WndProc is member of TForm1 class) to a standard procedure.

This is because the WndProc procedure is a member function and Windows does not understand class member functions.  Class member functions have a "self" pointer as a hidden first parameter which uniquely identifies a class object (keep in mind that an object is a specific instantiation of a class). The API callback does not know how to pass "self".

To convert the member procedure to a standard procedure call the MakeObjectInstance function as shown below.

fproc: TFarProc;
fproc := MakeObjectInstance(WndProc);

after you have done this

call the SetWindowlong function to replace the procedure of the window as shown below

SetWindowlong(hwnd, GWL_WNDPROC, longword(fcurProc));

Now all messages that are sent to the window will be intercepted by the WndProc procedure

The messages that are not handled by the WndProc can be sent to the original procedure by using the CallWindowProc function.

Here is how to call the function.

procedure TForm1.DlgProc(var msg: Tmessage);
begin
  case msg.msg of
    WM_SIZE:
      begin
        //user defined code
      end;
    WM_PAINT:
      begin
        //user defined code
      end;
  end;
  //all unhandled messages are sent to original procedure. Here  hproc is the  
        //handle to the original window procedure.
  msg.result := CallWindowProc(hproc, hwnd, msg.msg, msg.wparam, msg.lparam);
end;

once you have finshed you have to destroy the handle created by the MakeObjectInstance(WndProc) function by calling the 'FreeObjectInstance' function which is also defined in the forms unit. You usually call this function when the form is closed. Example of how to call the function is shown below.

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeObjectInstance(fproc);
end;

2010. február 4., csütörtök

Enumerate MS-SQL Servers via SQL-DMO into TStrings

Problem/Question/Abstract:

Function to load a StringList with MS-SQL Servers on a Network via SQL-DMO. MS-SQL DMO is a COM/OLE object that can do many things, in this article we just enumerate the SQL Servers on a Network. "List SQL servers on the network" by Tommy Andersen deals with this issue by using WinSock and a comment supplies a solution using CoApplication.Create. This article solves the issue by using CreateOleObject('SQLDMO.SQLServer'). The function returns true if successful.

// Declaration

function EnumSqlServers(AStrings: TStrings): boolean;

// Eg.
EnumSqlServers(Memo1.Lines);

Answer:

uses ComObj, Variants; {Variants is for Delphi 7}

// ====================================================
// Load SQL Servers on a Network into a string list
// ====================================================

function EnumSqlServers(AStrings: TStrings): boolean;
var
oDmo, oApp, oServers: OleVariant;
bResult: boolean;
i: integer;
begin
AStrings.Clear;

try
oDMO := CreateOleObject('SQLDMO.SQLServer');
oApp := oDMO.Application;
oServers := oApp.ListAvailableSQLServers;

try
AStrings.BeginUpdate;
for i := 1 to oServers.Count do
AStrings.Add(oServers.Item(i));
finally
AStrings.EndUpdate;
end;

bResult := true;
except
bResult := false;
end;

oServers := Unassigned;
oApp := Unassigned;
oDMO := Unassigned;

Result := bResult;
end;


2010. február 3., szerda

Where is Delphi installed? What is $(DELPHI)?


Problem/Question/Abstract:

Where is Delphi installed? What is $(DELPHI)?

Answer:

In Delphi, when specifying search paths, you can use $(delphi) to refer to Delphi's installation directory.
If your are writing an installation program, you may be interested in how to find out, where / which version of Delphi is installed.

It is in the registry at:
HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Delphi\3.0
or
HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Delphi\4.0

2010. február 2., kedd

Adding a EXE file into yours and running it


Problem/Question/Abstract:

How can I put an EXE file into my application and run it?

Answer:

1) Create file "hearts.rc"

2) Insert following text in that file:
   "TESTFILE EXEFILE \Mshearts.exe"
   Mshearts.exe -> Mshearts from Windows

3) Compile this file with brcc32.exe, which is located in your
   "\Borland\Delphi\Bin" directory by writing
   following command: "brcc32 -32 \hearts.rc"

Now you should have file "hearts.RES". How to add it to your application:

1) Copy "hearts.RES" to your project directory

2) Add ShellApi to USES
   To global vars add "Hearts:String";
   Next add to project:

{$R HEARTS.RES}

function GetTempDir: string;
var
  Buffer: array[0..MAX_PATH] of Char;
begin
  GetTempPath(Sizeof(Buffer) - 1, Buffer);
  result := StrPas(Buffer);
end;

procedure ExtractRes(ResType, ResName, ResNewName: string);
var
  Res: TResourceStream;
begin
  Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
  try
    Res.SavetoFile(ResNewName);
  finally
    Res.Free;
  end;
end;

procedure ShellExecute_AndWait(FileName: string);
var
  exInfo: TShellExecuteInfo;
  Ph: DWORD;
begin
  FillChar(exInfo, Sizeof(exInfo), 0);
  with exInfo do
  begin
    cbSize := Sizeof(exInfo);
    fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
    Wnd := GetActiveWindow();
    ExInfo.lpVerb := 'open';
    lpFile := PChar(FileName);
    nShow := SW_SHOWNORMAL;
  end;
  if ShellExecuteEx(@exInfo) then
  begin
    Ph := exInfo.HProcess;
  end
  else
  begin
    ShowMessage(SysErrorMessage(GetLastError));
    exit;
  end;
  while WaitForSingleObject(ExInfo.hProcess, 50) <> WAIT_OBJECT_0 do
    Application.ProcessMessages;
  CloseHandle(Ph);
end;

3) Add a Button on the form and to its OnClick event put this:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExtractRes('EXEFILE', 'TESTFILE', Hearts);
  if FileExists(Hearts) then
  begin
    ShellExecute_AndWait(Hearts);
    ShowMessage('Hearts finished');
    DeleteFile(Hearts);
  end;
end;

4) To OnCreate event of form put this:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Hearts := GetTempDir + 'Hearts_FROM_RES.EXE';
end;

5) Run program and click the button

2010. február 1., hétfő

How to trap your own hot keys


Problem/Question/Abstract:

How to trap your own hot keys

Answer:

Windows has many default hot keys that your interface takes advantage of. However, you sometimes need to add your own hot keys to your form. How do you trap the hot keys when the user enters them?

To solve this problem, first set your form KeyPreview property to True. Next, add this line of code to your form's OnKeyDown event handler:


if (ssCtrl in Shift) and (chr(Key) in ['A', 'a']) then
  ShowMessage('Ctrl-A');


The OnKeyDown event will trap the keystrokes and perform the specified code in response.