2008. augusztus 31., vasárnap

Copy your form to the clipboard


Problem/Question/Abstract:

Put the form contents in the clipboard.

Answer:

Add 'Clipbrd' in the uses of your form
Put this code in the OnClick of a TButton

procedure TForm1.Button1Click(Sender: TObject);
var bitmap:tbitmap;
begin
  bitmap:=tbitmap.create;
  bitmap.width:=clientwidth;
  bitmap.height:=clientheight;
  try
    with bitmap.Canvas do
      CopyRect (clientrect,canvas,clientrect);
    clipboard.assign(bitmap);
  finally
    bitmap.free;
  end;
end;

2008. augusztus 30., szombat

How to convert a cursor file into a bitmap


Problem/Question/Abstract:

How to convert a cursor file into a bitmap

Answer:

procedure TForm1.Button1Click(Sender: TObject);
var
  hCursor: LongInt;
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  Bitmap.Width := 32;
  Bitmap.Height := 32;
  hCursor := LoadCursorFromFile('test.cur');
  DrawIcon(Bitmap.Canvas.Handle, 0, 0, hCursor);
  Bitmap.SaveToFile('test.bmp');
  Bitmap.Free;
end;

2008. augusztus 29., péntek

Launch a Windows open file dialog without using the standard Delphi dialog component


Problem/Question/Abstract:

Is there an API call or other to launch a Windows open file dialog without using the standard delphi dialog component? I call a function "Hooklock1" from:

keyHook := SetWindowsHookEx(WH_KEYBOARD, HookLock1, HInstance, 0);

In this function no references to delphi global variables or components seem to work, I need to call an open dialog box to specify a filename. Any ideas?

Answer:

procedure TForm1.OpenApiClick(Sender: TObject);
var
  OpenFile: TOpenFileName;
begin
  with OpenFile do
  begin
    lStructSize := SizeOf(TOpenFilename);
    hInstance := SysInit.HInstance;
    hWndOwner := {Application.} Handle;
    lpstrFilter := 'Text Files (*.txt)' + Chr(0) + '*.txt' + Chr(0) +
                'All Files (*.*)' + Chr(0) + '*.*' + Chr(0);
    nFilterIndex := 1;
    {create a buffer for the file}
    nMaxFile := 255;
    lpstrFile := PChar(StringOfChar(' ', nMaxFile - 1));
    {create a buffer for the file title}
    nMaxFileTitle := 255;
    lpstrFileTitle := PChar(StringOfChar(' ', nMaxFileTitle - 1));
    {set the initial directory}
    lpstrInitialDir := 'C:\';
    lpstrTitle := 'Open a file, please';
    Flags := OFN_EXPLORER;
  end;
  if not GetOpenFileName(OpenFile) then
    Exit;
end;

2008. augusztus 28., csütörtök

ISAPI Filter Header Files


Problem/Question/Abstract:

In regard to my article "Writing a simple ISAPI Filter for IIS". here you can find the whole translation of the ISAPI Filter Header file HttpFilt.h.

Answer:

Some time ago I wrote an article about ISAPI Filters. Back then I promised to publish the whole translation of the ISAPI Header files. Somehow I never got around to do them, so finally, here they are.

For questions on how to use this definitions refer to my old article, please "Writing a simple ISAPI Filter for IIS".

Some of the definitions may have changed, due to my personal development during the last months, most, however should work as expected. I hope to submit this header translations for the Delphi-Jedi project some time soon.

NOTE: I know that Borland has some of the definitions in the unit Isapi2.pas, however, there are some important changes since version 2. This version depends upon version 4 from Microsoft.

There you go:

{************************************************************************}
{                                                                        }
{       Borland Delphi Runtime Library                                   }
{       HTTP Filter interface unit                                       }
{                                                                        }
{ Portions created by Microsoft are                                      }
{ Copyright (C) 1997 Microsoft Corporation.                              }
{ All Rights Reserved.                                                   }
{                                                                        }
{ The original file is: httpext.h, released 27 January 1999.             }
{ The original Pascal code is: httpext.pas, released 29 April 2002.      }
{ The initial developer of the Pascal code is Daniel Wischnewski         }
{ (daniel@wischnewski.tv).                                               }
{                                                                        }
{ Portions created by Daniel Wischnewski are                             }
{ Copyright (C) 2002 Daniel Wischnewski.                                 }
{                                                                        }
{       Obtained through:                                                }
{                                                                        }
{       Joint Endeavour of Delphi Innovators (Project JEDI)              }
{                                                                        }
{ The contents of this file are used with permission, subject to         }
{ the Mozilla Public License Version 1.1 (the "License"); you may        }
{ not use this file except in compliance with the License. You may       }
{ obtain a copy of the License at                                        }
{ http://www.mozilla.org/MPL/MPL-1.1.html                                }
{                                                                        }
{ Software distributed under the License is distributed on an            }
{ "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or         }
{ implied. See the License for the specific language governing           }
{ rights and limitations under the License.                              }
{                                                                        }
{************************************************************************}

unit HttpFilt;

{$WEAKPACKAGEUNIT}

interface

{$HPPEMIT ''}
{$HPPEMIT '#include "httpfilt.h"'}
{$HPPEMIT '#include "windows.h"'}
{$HPPEMIT ''}

uses
  Windows;

type
  PVOID = Pointer;
{$EXTERNALSYM PVOID}
  LPVOID = Pointer;
{$EXTERNALSYM LPVOID}
  LPSTR = PChar;
{$EXTERNALSYM LPSTR}

  //
  //  Current version of the filter spec is 4.0
  //
const
  HTTP_FILTER_REVISION = WORD(0) + DWORD(4 shl 16);
{$EXTERNALSYM HTTP_FILTER_REVISION}
  SF_MAX_USERNAME = 256 + 1;
{$EXTERNALSYM SF_MAX_USERNAME}
  SF_MAX_PASSWORD = 256 + 1;
{$EXTERNALSYM SF_MAX_PASSWORD}
  SF_MAX_AUTH_TYPE = 32 + 1;
{$EXTERNALSYM SF_MAX_AUTH_TYPE}
  SF_MAX_FILTER_DESC_LEN = 256 + 1;
{$EXTERNALSYM SF_MAX_FILTER_DESC_LEN}

  //
  //  These values can be used with the pfnSFCallback function supplied in
  //  the filter context structure
  //

const
  //
  //  Sends a complete HTTP server response header including
  //  the status, server version, message time and MIME version.
  //
  //  Server extensions should append other information at the end,
  //  such as Content-type, Content-length etc followed by an extra
  //  '\r\n'.
  //
  //  pData - Zero terminated string pointing to optional
  //      status string (i.e., "401 Access Denied") or NULL for
  //      the default response of "200 OK".
  //
  //  ul1 - Zero terminated string pointing to optional data to be
  //      appended and set with the header.  If NULL, the header will
  //      be terminated with an empty line.
  //
  SF_REQ_SEND_RESPONSE_HEADER = 0;
  //
  //  If the server denies the HTTP request, add the specified headers
  //  to the server error response.
  //
  //  This allows an authentication filter to advertise its services
  //  w/o filtering every request.  Generally the headers will be
  //  WWW-Authenticate headers with custom authentication schemes but
  //  no restriction is placed on what headers may be specified.
  //
  //  pData - Zero terminated string pointing to one or more header lines
  //      with terminating '\r\n'.
  //
  SF_REQ_ADD_HEADERS_ON_DENIAL = 1;
  //
  //  Only used by raw data filters that return SF_STATUS_READ_NEXT
  //
  //  ul1 - size in bytes for the next read
  //
  SF_REQ_SET_NEXT_READ_SIZE = 2;
  //
  //  Used to indicate this request is a proxy request
  //
  //  ul1 - The proxy flags to set
  //      0x00000001 - This is a HTTP proxy request
  //
  //
  SF_REQ_SET_PROXY_INFO = 3;
  //
  //  Returns the connection ID contained in the ConnID field of an
  //  ISAPI Application's Extension Control Block.  This value can be used
  //  as a key to cooridinate shared data between Filters and Applications.
  //
  //  pData - Pointer to DWORD that receives the connection ID.
  //
  SF_REQ_GET_CONNID = 4;
  //
  // Used to set a SSPI security context + impersonation token
  // derived from a client certificate.
  //
  // pData - certificate info ( PHTTP_FILTER_CERTIFICATE_INFO )
  // ul1 - CtxtHandle*
  // ul2 - impersonation handle
  //
  SF_REQ_SET_CERTIFICATE_INFO = 5;
  //
  // Used to get an IIS property
  // as defined in SF_PROPERTY_IIS
  //
  // ul1 - Property ID
  //
  SF_REQ_GET_PROPERTY = 6;
  //
  // Used to normalize an URL
  //
  // pData - URL to normalize
  //
  SF_REQ_NORMALIZE_URL = 7;
  //
  // Disable Notifications
  //
  // ul1 - notifications to disable
  //
  SF_REQ_DISABLE_NOTIFICATIONS = 8;
type
  SF_REQ_TYPE = DWORD;
{$EXTERNALSYM SF_REQ_TYPE}
  TSF_REQ_TYPE = SF_REQ_TYPE;

const
  SF_PROPERTY_SSL_CTXT = 0;
  SF_PROPERTY_INSTANCE_NUM_ID = 1;
type
  SF_PROPERTY_IIS = DWORD;
  TSF_PROPERTY_IIS = SF_PROPERTY_IIS;

  //
  //  These values are returned by the filter entry point when a new request is
  //  received indicating their interest in this particular request
  //
const
  //
  //  The filter has handled the HTTP request.  The server should disconnect
  //  the session.
  //
  SF_STATUS_REQ_FINISHED = $8000000;
  //
  //  Same as SF_STATUS_FINISHED except the server should keep the TCP
  //  session open if the option was negotiated
  //
  SF_STATUS_REQ_FINISHED_KEEP_CONN = $8000001;
  //
  //  The next filter in the notification chain should be called
  //
  SF_STATUS_REQ_NEXT_NOTIFICATION = $8000002;
  //
  //  This filter handled the notification.  No other handles should be
  //  called for this particular notification type
  //
  SF_STATUS_REQ_HANDLED_NOTIFICATION = $8000003;
  //
  //  An error occurred.  The server should use GetLastError() and indicate
  //  the error to the client
  //
  SF_STATUS_REQ_ERROR = $8000004;
  //
  //  The filter is an opaque stream filter and we're negotiating the
  //  session parameters.  Only valid for raw read notification.
  //
  SF_STATUS_REQ_READ_NEXT = $8000005;
type
  SF_STATUS_TYPE = DWORD;
  TSF_STATUS_TYPE = SF_STATUS_TYPE;

  //
  //  pvNotification points to this structure for all request notification types
  //
type
  TGetServerVariable = function(var pfc {: THTTP_FILTER_CONTEXT}; lpszVariableName:
    LPSTR; lpvBuffer: LPVOID; var lpdwSize: DWORD): BOOL; stdcall;
  TAddResponseHeaders = function(var pfc {: THTTP_FILTER_CONTEXT}; lpszHeaders: LPSTR;
    dwReserved: DWORD): BOOL; stdcall;
  TWriteClient = function(var pfc {: THTTP_FILTER_CONTEXT}; Buffer: LPVOID; var
    lpdwBytes: DWORD; dwReserved: DWORD): BOOL; stdcall;
  TAllocMem = function(var pfc {: THTTP_FILTER_CONTEXT}; cbSize: DWORD; dwReserved:
    DWORD): Pointer; stdcall;
  TServerSupportFunction = function(var pfc {: THTTP_FILTER_CONTEXT}; sfReq:
    SF_REQ_TYPE; pData: PVOID; ul1: DWORD; ul2: DWORD): BOOL; stdcall;

  PHTTP_FILTER_CONTEXT = ^THTTP_FILTER_CONTEXT;
{$EXTERNALSYM PHTTP_FILTER_CONTEXT}
  _HTTP_FILTER_CONTEXT = packed record
    cbSize: DWORD;
    //
    //  This is the structure revision level.
    //
    Revision: DWORD;
    //
    //  Private context information for the server.
    //
    ServerContext: PVOID;
    ulReserved: DWORD;
    //
    //  TRUE if this request is coming over a secure port
    //
    fIsSecurePort: BOOL;
    //
    //  A context that can be used by the filter
    //
    pFilterContext: PVOID;
    //
    //  Server callbacks
    //
    GetServerVariable: TGetServerVariable;
    AddResponseHeaders: TAddResponseHeaders;
    WriteClient: TWriteClient;
    AllocMem: TAllocMem;
    ServerSupportFunction: TServerSupportFunction;
  end;
{$EXTERNALSYM _HTTP_FILTER_CONTEXT}
  THTTP_FILTER_CONTEXT = _HTTP_FILTER_CONTEXT;

  //
  //  This structure is the notification info for the read and send raw data
  //  notification types
  //
type
  PHTTP_FILTER_RAW_DATA = ^THTTP_FILTER_RAW_DATA;
{$EXTERNALSYM PHTTP_FILTER_RAW_DATA}
  _HTTP_FILTER_RAW_DATA = packed record
    //
    //  This is a pointer to the data for the filter to process.
    //
    pvInData: PVOID;
    cbInData: DWORD; // Number of valid data bytes
    cbInBuffer: DWORD; // Total size of buffer
    dwReserved: DWORD;
  end;
{$EXTERNALSYM _HTTP_FILTER_RAW_DATA}
  THTTP_FILTER_RAW_DATA = _HTTP_FILTER_RAW_DATA;

  //
  //  This structure is the notification info for when the server is about to
  //  process the client headers
  //
type
  TGetHeader = function(var pfc: _HTTP_FILTER_CONTEXT; lpszName: LPSTR; lpvBuffer:
    LPVOID; var lpdwSize: DWORD): BOOL; stdcall;
  TSetHeader = function(var pfc: _HTTP_FILTER_CONTEXT; lpszName: LPSTR; lpszValue:
    LPSTR): BOOL; stdcall;
  TAddHeader = function(var pfc: _HTTP_FILTER_CONTEXT; lpszName: LPSTR; lpszValue:
    LPSTR): BOOL; stdcall;

  PHTTP_FILTER_PREPROC_HEADERS = ^THTTP_FILTER_PREPROC_HEADERS;
{$EXTERNALSYM PHTTP_FILTER_PREPROC_HEADERS}
  _HTTP_FILTER_PREPROC_HEADERS = packed record
    //
    //  For SF_NOTIFY_PREPROC_HEADERS, retrieves the specified header value.
    //  Header names should include the trailing ':'.  The special values
    //  'method', 'url' and 'version' can be used to retrieve the individual
    //  portions of the request line
    //
    GetHeader: TGetHeader;
    //
    //  Replaces this header value to the specified value.  To delete a header,
    //  specified a value of '\0'.
    //
    SetHeader: TSetHeader;
    //
    //  Adds the specified header and value
    //
    AddHeader: TAddHeader;
    HttpStatus: DWORD; // New in 4.0, status for SEND_RESPONSE
    dwReserved: DWORD; // New in 4.0
  end;
{$EXTERNALSYM _HTTP_FILTER_PREPROC_HEADERS}
  THTTP_FILTER_PREPROC_HEADERS = _HTTP_FILTER_PREPROC_HEADERS;

type
  HTTP_FILTER_SEND_RESPONSE = _HTTP_FILTER_PREPROC_HEADERS;
{$EXTERNALSYM HTTP_FILTER_SEND_RESPONSE}
type
  PHTTP_FILTER_SEND_RESPONSE = ^_HTTP_FILTER_PREPROC_HEADERS;
{$EXTERNALSYM PHTTP_FILTER_SEND_RESPONSE}

  //
  //  Authentication information for this request.
  //
type
  PHTTP_FILTER_AUTHENT = ^THTTP_FILTER_AUTHENT;
{$EXTERNALSYM PHTTP_FILTER_AUTHENT}
  _HTTP_FILTER_AUTHENT = packed record
    //
    //  Pointer to username and password, empty strings for the anonymous user
    //
    //  Client's can overwrite these buffers which are guaranteed to be at
    //  least SF_MAX_USERNAME and SF_MAX_PASSWORD bytes large.
    //
    pszUser: PChar;
    cbUserBuff: DWORD;
    pszPassword: PChar;
    cbPasswordBuff: DWORD;
  end;
{$EXTERNALSYM _HTTP_FILTER_AUTHENT}
  THTTP_FILTER_AUTHENT = _HTTP_FILTER_AUTHENT;

  //
  //  Indicates the server is going to use the specific physical mapping for
  //  the specified URL.  Filters can modify the physical path in place.
  //
type
  PHTTP_FILTER_URL_MAP = ^THTTP_FILTER_URL_MAP;
{$EXTERNALSYM PHTTP_FILTER_URL_MAP}
  _HTTP_FILTER_URL_MAP = packed record
    pszURL: PChar;
    pszPhysicalPath: PChar;
    cbPathBuff: DWORD;
  end;
{$EXTERNALSYM _HTTP_FILTER_URL_MAP}
  THTTP_FILTER_URL_MAP = _HTTP_FILTER_URL_MAP;

  //
  //  Bitfield indicating the requested resource has been denied by the server due
  //  to a logon failure, an ACL on a resource, an ISAPI Filter or an
  //  ISAPI Application/CGI Application.
  //
  //  SF_DENIED_BY_CONFIG can appear with SF_DENIED_LOGON if the server
  //  configuration did not allow the user to logon.
  //
const
  SF_DENIED_LOGON = $00000001;
{$EXTERNALSYM SF_DENIED_LOGON}
  SF_DENIED_RESOURCE = $00000002;
{$EXTERNALSYM SF_DENIED_RESOURCE}
  SF_DENIED_FILTER = $00000004;
{$EXTERNALSYM SF_DENIED_FILTER}
  SF_DENIED_APPLICATION = $00000008;
{$EXTERNALSYM SF_DENIED_APPLICATION}

  SF_DENIED_BY_CONFIG = $00010000;
{$EXTERNALSYM SF_DENIED_BY_CONFIG}

type
  PHTTP_FILTER_ACCESS_DENIED = ^THTTP_FILTER_ACCESS_DENIED;
{$EXTERNALSYM PHTTP_FILTER_ACCESS_DENIED}
  _HTTP_FILTER_ACCESS_DENIED = packed record
    pszURL: PChar; // Requesting URL
    pszPhysicalPath: PChar; // Physical path of resource
    dwReason: DWORD; // Bitfield of SF_DENIED flags
  end;
{$EXTERNALSYM _HTTP_FILTER_ACCESS_DENIED}
  THTTP_FILTER_ACCESS_DENIED = _HTTP_FILTER_ACCESS_DENIED;

  //
  //  The log information about to be written to the server log file.  The
  //  string pointers can be replaced but the memory must remain valid until
  //  the next notification
  //
type
  PHTTP_FILTER_LOG = ^THTTP_FILTER_LOG;
{$EXTERNALSYM PHTTP_FILTER_LOG}
  _HTTP_FILTER_LOG = packed record
    pszClientHostName: PChar;
    pszClientUserName: PChar;
    pszServerName: PChar;
    pszOperation: PChar;
    pszTarget: PChar;
    pszParameters: PChar;
    dwHttpStatus: DWORD;
    dwWin32Status: DWORD;
    dwBytesSent: DWORD; // IIS 4.0 and later
    dwBytesRecvd: DWORD; // IIS 4.0 and later
    msTimeForProcessing: DWORD; // IIS 4.0 and later
  end;
{$EXTERNALSYM _HTTP_FILTER_LOG}
  THTTP_FILTER_LOG = _HTTP_FILTER_LOG;

  //
  //  Notification Flags
  //
  //  SF_NOTIFY_SECURE_PORT
  //  SF_NOTIFY_NONSECURE_PORT
  //
  //      Indicates whether the application wants to be notified for transactions
  //      that are happenning on the server port(s) that support data encryption
  //      (such as PCT and SSL), on only the non-secure port(s) or both.
  //
  //  SF_NOTIFY_READ_RAW_DATA
  //
  //      Applications are notified after the server reads a block of memory
  //      from the client but before the server does any processing on the
  //      block.  The data block may contain HTTP headers and entity data.
  //
  //
  //
const
  SF_NOTIFY_SECURE_PORT = $00000001;
{$EXTERNALSYM SF_NOTIFY_SECURE_PORT}
  SF_NOTIFY_NONSECURE_PORT = $00000002;
{$EXTERNALSYM SF_NOTIFY_NONSECURE_PORT}

  SF_NOTIFY_READ_RAW_DATA = $00008000;
{$EXTERNALSYM SF_NOTIFY_READ_RAW_DATA}
  SF_NOTIFY_PREPROC_HEADERS = $00004000;
{$EXTERNALSYM SF_NOTIFY_PREPROC_HEADERS}
  SF_NOTIFY_AUTHENTICATION = $00002000;
{$EXTERNALSYM SF_NOTIFY_AUTHENTICATION}
  SF_NOTIFY_URL_MAP = $00001000;
{$EXTERNALSYM SF_NOTIFY_URL_MAP}
  SF_NOTIFY_ACCESS_DENIED = $00000800;
{$EXTERNALSYM SF_NOTIFY_ACCESS_DENIED}
  SF_NOTIFY_SEND_RESPONSE = $00000040;
{$EXTERNALSYM SF_NOTIFY_SEND_RESPONSE}
  SF_NOTIFY_SEND_RAW_DATA = $00000400;
{$EXTERNALSYM SF_NOTIFY_SEND_RAW_DATA}
  SF_NOTIFY_LOG = $00000200;
{$EXTERNALSYM SF_NOTIFY_LOG}
  SF_NOTIFY_END_OF_REQUEST = $00000080;
{$EXTERNALSYM SF_NOTIFY_END_OF_REQUEST}
  SF_NOTIFY_END_OF_NET_SESSION = $00000100;
{$EXTERNALSYM SF_NOTIFY_END_OF_NET_SESSION}

  //
  //  Filter ordering flags
  //
  //  Filters will tend to be notified by their specified
  //  ordering.  For ties, notification order is determined by load order.
  //
  //  SF_NOTIFY_ORDER_HIGH - Authentication or data transformation filters
  //  SF_NOTIFY_ORDER_MEDIUM
  //  SF_NOTIFY_ORDER_LOW  - Logging filters that want the results of any other
  //                      filters might specify this order.
  //
const
  SF_NOTIFY_ORDER_HIGH = $00080000;
{$EXTERNALSYM SF_NOTIFY_ORDER_HIGH}
  SF_NOTIFY_ORDER_MEDIUM = $00040000;
{$EXTERNALSYM SF_NOTIFY_ORDER_MEDIUM}
  SF_NOTIFY_ORDER_LOW = $00020000;
{$EXTERNALSYM SF_NOTIFY_ORDER_LOW}
  SF_NOTIFY_ORDER_DEFAULT = SF_NOTIFY_ORDER_LOW;
{$EXTERNALSYM SF_NOTIFY_ORDER_DEFAULT}

  SF_NOTIFY_ORDER_MASK = (SF_NOTIFY_ORDER_HIGH or
    SF_NOTIFY_ORDER_MEDIUM or
    SF_NOTIFY_ORDER_LOW);
{$EXTERNALSYM SF_NOTIFY_ORDER_MASK}

  //
  //  Filter version information, passed to GetFilterVersion
  //
type
  PHTTP_FILTER_VERSION = ^THTTP_FILTER_VERSION;
{$EXTERNALSYM PHTTP_FILTER_VERSION}
  _HTTP_FILTER_VERSION = packed record
    //
    //  Version of the spec the server is using
    //
    dwServerFilterVersion: DWORD;
    //
    //  Fields specified by the client
    //
    dwFilterVersion: DWORD;
    lpszFilterDesc: array[0..SF_MAX_FILTER_DESC_LEN - 1] of Char;
    dwFlags: DWORD;
  end;
{$EXTERNALSYM _HTTP_FILTER_VERSION}
  THTTP_FILTER_VERSION = _HTTP_FILTER_VERSION;

  //
  //  A filter DLL's entry point looks like this.  The return code should be
  //  an SF_STATUS_TYPE
  //
  //  NotificationType - Type of notification
  //  pvNotification - Pointer to notification specific data
  //

  //    function GetFilterVersion(var pVer: THTTP_FILTER_VERSION): BOOL; stdcall;
  //    function HttpFilterProc(var pfc: THTTP_FILTER_CONTEXT; NotificationType: DWORD; pvNotification: Pointer): DWORD; stdcall;
  //    function TerminateFilter(dwFlags: DWORD): BOOL; stdcall;

implementation

end.

2008. augusztus 27., szerda

How to change colours in a 16 colour bitmap


Problem/Question/Abstract:

How to change colors in a 16 colour bitmap

Answer:

I found the solution which works best, although the final version will have the colors computed rather than hard coded. The bitmap I am using actually only uses the first 4 colors in the palette for shades of grey.

procedure TForm1.SetWallpaperPalette;
type
  RGBQUAD = packed record
    rgbBlue: Byte;
    rgbGreen: Byte;
    rgbRed: Byte;
    rgbReserved: Byte;
  end;
var
  NewColors: array[1..4] of RGBQUAD;
begin
  FillChar(NewColors, SizeOf(NewColors), 0);
  with NewColors[1] do
  begin
    rgbBlue := 0;
    rgbGreen := $C6;
    rgbRed := $C6;
  end;
  with NewColors[2] do
  begin
    rgbBlue := 0;
    rgbGreen := $CE;
    rgbRed := $CE;
  end;
  with NewColors[3] do
  begin
    rgbBlue := 0;
    rgbGreen := $D6;
    rgbRed := $D6;
  end;
  with NewColors[4] do
  begin
    rgbBlue := 0;
    rgbGreen := $DE;
    rgbRed := $DE;
  end;
  if Assigned(SpeedBar1.Wallpaper.Bitmap) then
    SetDibColorTable(SpeedBar1.Wallpaper.Bitmap.Canvas.Handle, 0, 4, NewColors);
end;

2008. augusztus 26., kedd

Data Entry: Automagically Moving the Cursor


Problem/Question/Abstract:

How can I get the cursor to advance to the next form field after a field has been filled with the maximum number of characters?

Answer:

Hmm... sounds like a serial number or SSN entry thing to me. Whatever the case, the best way I've found to handle this is to use the OnChange method of the TEdit to determine the length of the text being entered at runtime. Then if the text has reached a specified length, call Self.ActiveControl to move to a specific control.

For example, I have an application that requires that the user enter a social security number separated by three fields. Instead of having the user press Tab to move from field to field, I trap the OnChange event handler to determine the length of the text and move to the next edit control if the text for the field in question reaches a certain size. Here's the code for my social security number entry:

procedure PatientForm.edSSNBegChange(Sender: TObject);
begin
  case TEdit(Sender).Tag of
    100: if Length(TEdit(Sender).Text) = 3 then
        ActiveControl := edSSNMid;
    101: if Length(TEdit(Sender).Text) = 2 then
        ActiveControl := edSSNEnd;
    102: if Length(TEdit(Sender).Text) = 4 then
        ActiveControl := Button1;
  end;
end;

On the form where this method lives, I have three edit boxes called edSSNBeg, edSSNMid and edSSNEnd to signify the first, middle, and end parts of a social security number. As you may know, a social security number is defined as a three-digit front plus a two-digit middle and a four-digit end. The code above captures this quite well.

Notice that I use the Tag property for the edit boxes. Each one has a specific tag value so the procedure can determine which edit box is currently active, because TEdit(Sender) doesn't tell you much. So by using the Tag property, I can save myself a lot of coding by using a case statement.

With respect to what decides whether the cursor should move, each case condition checks for the length of the current edit box's text using the Length property. If has reached the size specified, the cursor is moved to the next field.

Why does this work? OnChange fires after a change has been made to an edit box. So this scheme works because the change has already been made and you can readily measure the text size.

2008. augusztus 25., hétfő

AVL-tree generic classes


Problem/Question/Abstract:

Balanced binary tree (AVL-tree) generic classes.

Answer:

AVLtrees unit implement fast insertion, replacing, deletion and search of item (complexity is O*log(N)).
It contains low level functions, low level class and user-friendly classes.
Most of functions are implemented on BASM (inline assembler).

You may use TStringKeyAVLtree, TIntegerKeyAVLtree classes directly or declare descedants from one of these.

Example for more complex way - declaring own classes:

type
  TMyItem = class;

  TMyCollection = class(TStringKeyAVLtree)
  protected
    function GetItems(AKey: string): TMyItem;
  public
    constructor Create;
    function Add(const AKey, ADescription: string): TMyItem;
    property Items[AKey: string]: TMyItem read GetItems;
  end;

  TMyItem = class(TStringKeyAVLtreeNode)
  protected
    FDescription: string;
  public
    property FileName: string read FKey;
      // for your convinience, FKey is defined in base class
    property Desciption: string read FDescription write FDescription;
  end;

constructor TMyCollection.Create;
begin
  inherited Create(TMyItem); // class of items
end;

function TMyCollection.Add(const AKey, ADescription: string): TMyItem;
begin
  Result := TMyItem(inherited Add(AKey));
  if Result = nil then
    raise Exception.Create('Item ''' + AKey + ''' already exists');
  Result.Description := ADescription;
end;

function GetItems(AKey: string): TMyItem;
begin
  Result := TMyItem(Find(AKey));
end;

See also little sample supplied with unit.

See Dr.D.E.Knuth "Art of Computer Programming" for more information about balanced trees.
For implementation of item deletion I use an article "An Iterative Algorithm for Deletion from AVL-Balanced Binary Trees" by Ben Pfaff , http://www.msu.edu/user/pfaffben/avl

AVLtrees unit (sources) is available on my homepage http://www.mtgroup.ru/~alexk/


2008. augusztus 24., vasárnap

Create a GUID at runtime


Problem/Question/Abstract:

Create a GUID at runtime

Answer:

You may need to create a GUID at runtime. One reason could be to simply have a unique number that identifies each workstation.By the way the last group of digits in the GUID generated on a computer is its network card number (if any is present).

Delphi does not have the needed definitions, here they are:

function CoCreateGuid(pGUID: TGUID): longint; external 'OLE32.DLL';

procedure TForm1.FormCreate(Sender: TObject);
var
  udtGUID: TGUID;
  lResult: longint;
begin
  lResult := CoCreateGuid(udtGUID);
  // see definition of TGUID in Delphi's online help
  // udtGUID.D4 = network card's number
end;

2008. augusztus 23., szombat

How to create a print preview


Problem/Question/Abstract:

I am trying to put together a simple print preview program that can be used by other programs. I am using the panel component as my printing cavas and I am having troubles equating the screen and page ratios. Does anyone have any ideas on how I could simplify this task or point me in the direction of a good book or give some example code?

Answer:

unit printpreview;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    PreviewPaintbox: TPaintBox;
    Label1: TLabel;
    Label2: TLabel;
    LeftMarginEdit: TEdit;
    TopMarginEdit: TEdit;
    Label3: TLabel;
    Label4: TLabel;
    RightMarginEdit: TEdit;
    Label5: TLabel;
    BottomMarginEdit: TEdit;
    ApplyMarginsButton: TButton;
    OrientationRGroup: TRadioGroup;
    Label6: TLabel;
    ZoomEdit: TEdit;
    ZoomUpDown: TUpDown;
    procedure LeftMarginEditKeyPress(Sender: TObject; var Key: Char);
    procedure FormCreate(Sender: TObject);
    procedure PreviewPaintboxPaint(Sender: TObject);
    procedure ApplyMarginsButtonClick(Sender: TObject);
  private
    { Private declarations }
    PreviewText: string;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses printers;

{$R *.DFM}

procedure TForm1.LeftMarginEditKeyPress(Sender: TObject; var Key: Char);
begin
  if not (Key in ['0'..'9', #9, DecimalSeparator]) then
    Key := #0;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  S: string;

  procedure loadpreviewtext;
  var
    sl: TStringList;
  begin
    sl := TStringList.Create;
    try
      sl.Loadfromfile(Extractfilepath(application.exename) + 'printpreview.pas');
      PreviewText := sl.Text;
    finally
      sl.free
    end;
  end;

begin
  {Initialize the margin edits with a margin of 0.75 inch}
  S := FormatFloat('0.00', 0.75);
  LeftMarginEdit.Text := S;
  TopMarginEdit.Text := S;
  RightMarginEdit.Text := S;
  BottomMarginEdit.Text := S;
  {Initialize the orientation radio group}
  if Printer.Orientation = poPortrait then
    OrientationRGroup.ItemIndex := 0
  else
    OrientationRGroup.ItemIndex := 1;
  {load test text for display}
  LoadPreviewtext;
end;

procedure TForm1.PreviewPaintboxPaint(Sender: TObject);
var
  pagewidth, pageheight: Double; {printer page dimension in inch}
  printerResX, printerResY: Integer; {printer resolution in dots/inch}
  minmarginX, minmarginY: Double; {nonprintable margin in inch}
  outputarea: TRect; {print area in 1/1000 inches}
  scale: Double; {conversion factor, pixels per 1/1000 inch}

  procedure InitPrintSettings;
    function GetMargin(S: string; inX: Boolean): Double;
    begin
      Result := StrToFloat(S);
      if InX then
      begin
        if Result < minmarginX then
          Result := minmarginX;
      end
      else
      begin
        if Result < minmarginY then
          Result := minmarginY;
      end;
    end;
  begin
    printerResX := GetDeviceCaps(printer.handle, LOGPIXELSX);
    printerResY := GetDeviceCaps(printer.handle, LOGPIXELSY);
    pagewidth := GetDeviceCaps(printer.handle, PHYSICALWIDTH) / printerResX;
    pageheight := GetDeviceCaps(printer.handle, PHYSICALHEIGHT) / printerResY;
    minmarginX := GetDeviceCaps(printer.handle, PHYSICALOFFSETX) / printerResX;
    minmarginY := GetDeviceCaps(printer.handle, PHYSICALOFFSETY) / printerResY;
    outputarea.Left := Round(GetMargin(LeftMarginEdit.Text, true) * 1000);
    outputarea.Top := Round(GetMargin(TopMarginEdit.Text, false) * 1000);
    outputarea.Right := Round((pagewidth - GetMargin(RightMarginEdit.Text, true)) *
      1000);
    outputarea.Bottom := Round((pageheight - GetMargin(BottomMarginEdit.Text, false))
      * 1000);
  end;

  procedure ScaleCanvas(Canvas: TCanvas; widthavail, heightavail: Integer);
  var
    needpixelswidth, needpixelsheight: Integer;
    {dimensions of preview at current zoom factor in pixels}
    orgpixels: TPoint;
    {origin of preview in pixels}
  begin
    {set up a coordinate system for the canvas that uses 1/1000 inch as unit,
    honors the zoom factor and maintains the MM_TEXT orientation of the
    coordinate axis (origin in top left corner, positive Y axis points down}
    scale := Screen.PixelsPerInch / 1000;
    {Apply zoom factor}
    scale := scale * StrToInt(Zoomedit.text) / 100;
    {figure out size of preview}
    needpixelswidth := Round(pagewidth * 1000 * scale);
    needpixelsheight := Round(pageheight * 1000 * scale);
    if needpixelswidth >= widthavail then
      orgpixels.X := 0
    else
      orgpixels.X := (widthavail - needpixelswidth) div 2;
    if needpixelsheight >= heightavail then
      orgpixels.Y := 0
    else
      orgpixels.Y := (heightavail - needpixelsheight) div 2;
    {change mapping mode to MM_ISOTROPIC}
    SetMapMode(canvas.handle, MM_ISOTROPIC);
    {move viewport origin to orgpixels}
    SetViewportOrgEx(canvas.handle, orgpixels.x, orgpixels.y, nil);
    {scale the window}
    SetViewportExtEx(canvas.handle, Round(1000 * scale), Round(1000 * scale), nil);
    SetWindowExtEx(canvas.handle, 1000, 1000, nil);
  end;

begin
  if OrientationRGroup.ItemIndex = 0 then
    Printer.Orientation := poPortrait
  else
    Printer.Orientation := poLandscape;
  InitPrintsettings;
  with Sender as TPaintBox do
  begin
    ScaleCanvas(Canvas, ClientWidth, ClientHeight);
    {specify font height in 1/1000 inch}
    Canvas.Font.Height := Round(font.height / font.pixelsperinch * 1000);
    {paint page white}
    Canvas.Brush.Color := clWindow;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(Rect(0, 0, Round(pagewidth * 1000), Round(pageheight * 1000)));
    {draw the text}
    DrawText(canvas.handle, PChar(PreviewText), Length(PreviewText),
      outputarea, DT_WORDBREAK or DT_LEFT);
    {Draw thin gray lines to mark borders}
    Canvas.Pen.Color := clGray;
    Canvas.Pen.Style := psSolid;
    Canvas.Pen.Width := 10;
    with Canvas do
    begin
      MoveTo(outputarea.left - 100, outputarea.top);
      LineTo(outputarea.right + 100, outputarea.top);
      MoveTo(outputarea.left - 100, outputarea.bottom);
      LineTo(outputarea.right + 100, outputarea.bottom);
      MoveTo(outputarea.left, outputarea.top - 100);
      LineTo(outputarea.left, outputarea.bottom + 100);
      MoveTo(outputarea.right, outputarea.top - 100);
      LineTo(outputarea.right, outputarea.bottom + 100);
    end;
  end;
end;

procedure TForm1.ApplyMarginsButtonClick(Sender: TObject);
begin
  PreviewPaintbox.Invalidate;
end;

end.


---------------------------------------------------------------------------------
{
object Form1: TForm1
  Left = 192
  Top = 128
  Width = 696
  Height = 480
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = ANSI_CHARSET
  Font.Color = clWindowText
  Font.Height = -15
  Font.Name = 'Arial'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 120
  TextHeight = 17
  object Panel1: TPanel
    Left = 503
    Top = 0
    Width = 185
    Height = 453
    Align = alRight
    TabOrder = 0
    object Label1: TLabel
      Left = 8
      Top = 8
      Width = 92
      Height = 17
      Caption = 'Margins (inch)'
    end
    object Label2: TLabel
      Left = 8
      Top = 45
      Width = 24
      Height = 17
      Caption = 'Left'
    end
    object Label3: TLabel
      Left = 8
      Top = 77
      Width = 25
      Height = 17
      Caption = 'Top'
    end
    object Label4: TLabel
      Left = 8
      Top = 109
      Width = 34
      Height = 17
      Caption = 'Right'
    end
    object Label5: TLabel
      Left = 8
      Top = 141
      Width = 47
      Height = 17
      Caption = 'Bottom'
    end
    object Label6: TLabel
      Left = 8
      Top = 261
      Width = 64
      Height = 17
      Caption = 'Zoom (%)'
    end
    object LeftMarginEdit: TEdit
      Left = 60
      Top = 40
      Width = 100
      Height = 25
      TabOrder = 0
      OnKeyPress = LeftMarginEditKeyPress
    end
    object TopMarginEdit: TEdit
      Left = 60
      Top = 72
      Width = 100
      Height = 25
      TabOrder = 1
      OnKeyPress = LeftMarginEditKeyPress
    end
    object RightMarginEdit: TEdit
      Left = 60
      Top = 104
      Width = 100
      Height = 25
      TabOrder = 2
      OnKeyPress = LeftMarginEditKeyPress
    end
    object BottomMarginEdit: TEdit
      Left = 60
      Top = 136
      Width = 100
      Height = 25
      TabOrder = 3
      OnKeyPress = LeftMarginEditKeyPress
    end
    object ApplyMarginsButton: TButton
      Left = 24
      Top = 304
      Width = 137
      Height = 25
      Caption = 'Apply'
      TabOrder = 4
      OnClick = ApplyMarginsButtonClick
    end
    object OrientationRGroup: TRadioGroup
      Left = 8
      Top = 176
      Width = 161
      Height = 65
      Caption = 'Orientation'
      Items.Strings = (
        'Portrait'
        'Landscape')
      TabOrder = 5
    end
    object ZoomEdit: TEdit
      Left = 80
      Top = 256
      Width = 40
      Height = 25
      TabOrder = 6
      Text = '50'
    end
    object ZoomUpDown: TUpDown
      Left = 120
      Top = 256
      Width = 17
      Height = 25
      Associate = ZoomEdit
      Min = 0
      Increment = 10
      Position = 50
      TabOrder = 7
      Wrap = False
    end
  end
  object Panel2: TPanel
    Left = 0
    Top = 0
    Width = 503
    Height = 453
    Align = alClient
    Font.Charset = ANSI_CHARSET
    Font.Color = clWindowText
    Font.Height = -17
    Font.Name = 'Times New Roman'
    Font.Style = []
    ParentFont = False
    TabOrder = 1
    object PreviewPaintbox: TPaintBox
      Left = 1
      Top = 1
      Width = 501
      Height = 451
      Align = alClient
      OnPaint = PreviewPaintboxPaint
    end
  end
end
}
---------------------------------------------------------------------------------

2008. augusztus 22., péntek

How to change the color of the selection in a TCheckListBox


Problem/Question/Abstract:

How to change the color of the selection in a TCheckListBox

Answer:

If you want to change the color of the selection so that it's always the Color property, this code will work:

procedure TForm1.CheckListBox1DrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
  with Control as TCheckListBox do
  begin
    { Use the default colors regardless of selection status }
    Canvas.Font.Color := Font.Color;
    Canvas.Brush.Color := Color;
    { Erase everything there at the moment }
    Canvas.FillRect(Rect);
    { Draw the text }
    Canvas.TextOut(Rect.Left + 2, Rect.Top, Items[Index]);
  end;
end;

2008. augusztus 21., csütörtök

How to check if the caret position in a TMemo is between two HTML tags


Problem/Question/Abstract:

I need a string procedure that checks whether the caret position is between the tags '<>' , and if this is true, the contents of the tag are placed in a string for further processing.

Answer:

Something like this:

function FindTextBetweenTags(const S: string; caretpos: Integer): string;
var
  nStart, nEnd: Integer;
begin
  Result := EmptyStr;
  {caretpos is 0-based, string indices are 1-based}
  Inc(caretpos);
  {move backwards from caretpos util we find a '<'}
  nstart := caretpos;
  while ((nstart > 0) and (S[nstart]) <> '<') do
    Dec(nstart);
  if nstart = 0 then
    Exit;
  {move to first char after '<'}
  Inc(nstart);
  if S[nstart] = '>' then
    Exit; {empty tag}
  {move forward until we find a '>'}
  nend := nstart;
  while (nend <= Length(S)) and (S[nend] <> '>') do
    Inc(nend);
  if (nend > Length(S)) or (nend <= caretpos) then
    Exit;
  Result := Copy(S, nstart, nend - nstart);
end;

You would call it like

tagstring := FindtextBetweentags(memo1.text, memo1.selstart);

2008. augusztus 20., szerda

How to execute a method by name


Problem/Question/Abstract:

I have a reference to a TMethod (with its Data and Code pointers) and would like to execute the associated method. Does anyone know how I can do this?

Answer:

Here is an example that executes a method by name. Note that the method to be called (in the example that is SomeMethod) must be declared as published, otherwise MethodAddress wil return nil.


{ ... }
type
  PYourMethod = ^TYourMethod;
  TYourMethod = procedure(S: string) of object;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  ExecMethodByName('SomeMethod');
end;

procedure TMainForm.ExecMethodByName(AName: string);
var
  PAddr: PYourMethod;
  M: TMethod;
begin
  PAddr := MethodAddress(AName);
  if PAddr <> nil then
  begin
    M.Code := PAddr;
    M.Data := Self;
    TYourMethod(M)('hello');
  end;
end;

procedure TMainForm.SomeMethod(S: string);
begin
  ShowMessage(S);
end;

2008. augusztus 19., kedd

Add items to the Windows Explorer right-click menu


Problem/Question/Abstract:

How do I add some items to Explorer's right-click menu, which appears when the user right-clicks on a certain file type?

Answer:

procedure add_context_menu;
type
  extns = (wav, png, bmp, jpg);
const
  ext_names: array[extns] of string = ('.wav', '.png', '.bmp', '.jpg');
var
  ext: extns;
  reg: TRegistry;
  name: string;
  command: string;
begin
  reg := TRegistry.Create;
  reg.RootKey := HKEY_CLASSES_ROOT;
  {Build the command string we want to store}
  command := '"' + Application.ExeName + '" "%1"';
  {Loop over extensions we can handle}
  for ext := wav to jpg do
  begin
    {See if this extension is already known in HKEY_CLASSES_ROOT}
    if reg.OpenKeyReadOnly('\' + ext_names[ext]) then
    begin
      name := reg.ReadString(''); {Get the name of this type}
      if name <> '' then
        {If not blank, open this type's shell key, but don't create it}
        if reg.OpenKey('\' + name + '\shell', False) then
          {Try to create a new key called "APTprocess". Note that for Delphi5 we
          need to set the access explicitly}
          reg.Access := KEY_READ or KEY_WRITE;
      if reg.OpenKey('APTprocess', True) then
      begin
        {The default value will be displayed in the context menu}
        reg.WriteString('', '&APT process');
        {So now open the command key, creating it if required}
        reg.Access := KEY_READ or KEY_WRITE;
        if reg.OpenKey('command', True) then
          {and write the command string as the default value}
          reg.WriteString('', command);
      end;
    end;
  end;
  reg.Free;
end;

2008. augusztus 18., hétfő

How to delete a line from a TRichEdit


Problem/Question/Abstract:

How do you delete a line from a TRichEdit at the current cursor point?

Answer:

uses
  richedit; {for EM_EXLINEFROMCHAR}

var
  lineindex: Integer;
  { ... }

with richedit1 do
begin
  lineindex := perform(EM_EXLINEFROMCHAR, 0, SelStart);
  SelStart := perform(EM_LINEINDEX, lineindex, 0);
  SelLength := perform(EM_LINEINDEX, lineindex + 1, 0) - SelStart;
  SelText := '';
end;