Windows
  1. ???? Data segment too large ????
  2. Read the Run Minimized Checkbox
  3. Cannot properly minimize a form on startup
  4. Shared memory in a DLL with Delphi 2.0
  5. Shell_NotifyIcon
  6. How do I make completely invisible main forms??
  7. HELP !! STAY-ON-TOP !!!
  8. Hiding Windows 95 Taskbar
  9. A Catch-a-Maximize Command Question
  10. How do you detect Windows version?
  11. How can I change the wallpaper programmatically?
  12. Path / directory name for 'My Computer'
  13. Determining which font (Large or Small) is in use
  14. Large/Small Fonts?
  15. How can I restore a window to its last state when I run it again?
  16. How: to determine name of StartUp group
  17. Finding Boot Drive
  18. How to make a window system modal ?
  19. Sending Keystrokes/Text to a Window...
  20. Windows Messages Basics
  21. Buttons in Win95 task bar
  22. Control Panel
  23. Associate filetype [extension)
  24. Hide Start Button[NEW]

???? Data segment too large ????

From: info@sundialservices.com (Sundial Services)

In article <325B6BAB.7096@hec.unil.ch> Jean-Luc Nicoulin <Jean-Luc.Nicoulin@hec.unil.ch> writes:

I get the message 'Data segment too large'. What is the matter and what
can I do to solve the problem ?

In a Windows 3.1 application, three major data-areas share one(!) 64K memory segment: t he stack, the Windows 'local heap', and the data-segment containing all globals and initialized constants. This area can become used up extremely quickly.

Consequently, W3.1 applications store almost -nothing- in the stack or in a global. Rather, they store -pointers- to them. This is why, in Delphi, 'an object is a pointer.'

What you have to do is to comb your application looking for large globals and to move those into a common data block which your program allocates on startup (as an object) and destroys when done. You can see a lot about what's in the data segment by setting the 'linker map' to 'detailed' and looking for the 'DATA' segment. Whatever's in that segment is going to be trying to occupy space in the lower part of that 64K-memory segment.

Read the Run Minimized Checkbox

Solution 1

Paul S. Knapp wrote:
>
> Does anyone know how to make as Delphi app read the Run Minimized
> checkbox in the Windows 3.1 or 3.11 StartUp Group.
>
> I cannot get my application to run minimized when this checkbox is
> checked.  I would like to be able to give my users the option of
> starting the application in their startup group, in either maximized or
> minimized mode.  Every Windows application I have ever used is able to
> start in the mode selected by the checkbox in the Startup Group. I
> assume Delphi applications should be able to as well, but so far I a
> haven't found a way.
>
> Thanks in advance for your advice
> Paul Knapp

Hi Paul!

Use WinProcs unit and after created main form add call ShowWindow.

You can use HInstance, HPrevInst, CmdShow and CmdLine global variables.


program Project1;

uses
  WinProcs, {*** use WinProcs}
  Forms,
  Unit1 in 'UNIT1.PAS' {Form1};

{$R *.RES}

begin
  Application.CreateForm(TForm1, Form1);
  ShowWindow(Form1.handle, CmdShow);
  Application.Run;
end.

Solution 2

From: Ken Kyler <ken@kyler.com>

Here's and example, it's taken from

Rubenking, Neil (1996). _Delphi Programming Problem Solver_. Foster City, CA: IDG Books. ISBN:1-56884-795-5.


unit Unit1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs;

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

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  SUI : TStartupInfo ;
begin
  if CmdShow = SW_SHOWDEFAULT then
  begin
    GetStartupInfo( SUI ) ;
    ShowHow := SUI.wShowWindow ;
  end
  else
    ShowHow := CmdShow ;

  if ShowHow = SW_SHOWMAXIMIZED then
    WindowState := wsMaximized ;
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
  case ShowHow of
    SW_SHOWMINIMIZED,
    SW_MINIMIZE,
    SW_SHOWMINNOACTIVE :
      Application.Minimize ;
  end ;
end;

end.

Solution 3

From: a.viebke@berlin.snafu.de (Andreas Viebke)

This works with NT 4 and Delphi 2.01. It took me one and a half hours to find out: Make your project code look like this:


  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Form1.Show;
    Application.Minimize;
    Application.Run;
  end.

It seems to be important that neither OnCreate nor OnShow is disturbed by a procedure that changes a window's state.

Cannot properly minimize a form on startup

From: abeldup@unison.co.za (Abel du Plessis)

I need to start my form minimized, unfortunetly it doesn't work. When I set the WindowState property of the main form
to wsMinimized and run it, the form minimizes onto Win95 desktop instead of the taskbar how it properly should.

Does anyone know how to fix this bug?
There was an article in The Delphi Magazine, Issue 19, March 1997 - the Delphi Clinic section which explained the problem.
Here is my adaptation of the fix:
unit Foobar;

interface

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

implementation

procedure TfrmUVChannel.FormCreate(Sender: TObject);
begin
     //Assign a temporary event handler for when the app gets restored
     Application.OnRestore := DoRestore;
     Application.Minimize;
end;

procedure TfrmFoobar.DoRestore(Sender: TObject);
begin
     Application.ShowMainForm := True;
     //Restore the application
     Perform(wm_SysCommand, sc_Restore, 0);
     //Ensure all components draw properly
     Show;
     //Disconnect this event handler so it will not be called again
     Application.OnRestore := nil;
end;

initialization
   //Hide the minimized main form for now
   Application.ShowMainForm := False;

end.

Shared memory in a DLL with Delphi 2.0

From: johnnysc@ix.netcom.com (John Crane)

Sharing Memory Mapped Files... Check out the following code:


var
  HMapping: THandle;
  PMapData: Pointer;

const
  MAPFILESIZE = 1000;

procedure OpenMap;
var
  llInit: Boolean;
  lInt: Integer;
begin
  HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, MAPFILESIZE, pchar('MY MAP NAME GOES HERE'));
  // Check if already exists
  llInit := (GetLastError() <> ERROR_ALREADY_EXISTS);
  if (hMapping = 0) then begin
    ShowMessage('Can''t Create Memory Map');
    Application.Terminate;
    exit;
  end;
  PMapData := MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  if PMapData = nil then begin
    CloseHandle(HMapping);
    ShowMessage('Can''t View Memory Map');
    Application.Terminate;
    exit;
  end;
  if (llInit) then begin
    // Init block to #0 if newly created
    memset(PMapData, #0, MAPFILESIZE);
  end;
end;

procedure CloseMap;
begin
  if PMapData <> nil then begin
    UnMapViewOfFile(PMapData);
  end;
  if HMapping <> 0 then begin
    CloseHandle(HMapping);
  end;
end;

Any two or more applications or DLLs may obtain pointers to the same physical block of memory this way. PMapData will point to a 1000 byte buffer in this example, this buffer being initialized to #0's the first time in. One potential problem is synchronizing access to the memory. You may accomplish this through the use of mutexes. Here's an example of that:


{ Call LockMap before writing (and reading?) to the memory mapped file.  Be sure to call UnlockMap immediately when done updating. }

var
  HMapMutex: THandle;

const
  REQUEST_TIMEOUT = 1000;

function LockMap:Boolean;
begin
  Result := true;
  HMapMutex := CreateMutex(nil, false, pchar('MY MUTEX NAME GOES HERE'));
  if HMixMutex = 0 then begin
    ShowMessage('Can''t create map mutex');
    Result := false;
  end else begin
    if WaitForSingleObject(HMapMutex,REQUEST_TIMEOUT) = WAIT_FAILED then begin
      // timeout
      ShowMessage('Can''t lock memory mapped file');
      Result := false;
    end;
  end;
end;

procedure UnlockMap;
begin
  ReleaseMutex(HMixMutex);
  CloseHandle(HMixMutex);
end;

Please excuse my unnecessary begin..end's. I come from a Clipper background, and I prefer to see my logic blocks capped off with end's - easier to follow.

Shell_NotifyIcon

From: "Neil Clayton" <100101.602@compuserve.com>

Rainer Perl <Rainer.Perl@iddc.via.at> wrote in article

> I have a question to the Shell_NotifyIcon function:
> I can add an icon to the taskbar
> I can modify an icon
> I can delete an icon.
> What I can't do: I can't receive Messages from the Icon!!

To receive messages you must add the NIF_MESSAGE flag to your notify structure and tell it what message to send and to which window. This is the code that I use:


procedure TMainForm.UpdateTaskBar;            // update the win95 taskbar icon area
var
  NotifyData: TNotifyIconData;

begin
  With NotifyData do                                            // set up the data structure
  begin
    cbSize             := SizeOf(TNotifyIconData);
    Wnd                := MyForm.Handle;
    uID                  := 0;
    uFlags             := NIF_ICON or NIF_MESSAGE or NIF_TIP;   // ... the aspects to modify ...
    uCallbackMessage := WM_MY_MESSAGE;                         // ... the message to send back to us ...
    hIcon              := hMyIcon;
    szTip              := 'Tool Tip To Display';           // ... and the tool tip
  end;
  Shell_NotifyIcon(dwMessage, @NotifyData);                     // now do the update
end;

WM_MYMESSAGE is a user defined message. Usually defined as:


const
  WM_MYMESSAGE = WM_USER + <some number - can be zero>;

How do I make completely invisible main forms??

From: gt6298d@prism.gatech.edu (Chris Randall)

"J.J. Bakker" <J.J.Bakker@stud.rgl.ruu.nl> wrote:
>Does anyone know the answer, I', trying to make an app that has an icon in the notification area with a popupmenu. However the application is still visible on the taskbar. 
Using Application.ShowMainForm:=False; is not enough.
>

I have run into the same problem but found the answer. This little bit of code works great.


procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnMinimize:=AppMinimize;
  Application.OnRestore:=AppMinimize;
  Application.Minimize;
  AppMinimize(@Self);
end;

procedure TMainForm.AppMinimize(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;

HELP !! STAY-ON-TOP !!!

From: "James D. Rofkar" <jim_rofkar%lotusnotes1@instinet.com>

JAAD wrote:
>
> I want to make a Delphi-Form to REALLY stay on top, But not only within it own  application (thats simpel)
       No I want it to stay on top even when I am using  for instance EXCEL.
>

Try using the Windows API function SetWindowPos(). Something like...


   with MyForm do
      SetWindowPos(Handle,
                   HWND_TOPMOST,
                   Left,
                   Top,
                   Width,
                   Height,
                   SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);

You may need to call this function in your Form's OnShow(), OnDeactivate(), and OnActivate() event handlers.

Hiding Windows 95 Taskbar

From: "James D. Rofkar" <jim_rofkar%lotusnotes1@instinet.com>

Robert Copier wrote:
> Is there a way to hide the Windows 95 statusbar when i start my application made in delphi 2.01. When the user close the application the statusbar must become visible again.

I'm guessing you're referring to the Windows 95 taskbar and system tray window, and not a statusbar. The answer: Sure you can! And what a cool idea! Here's how:

  1. First declare a variable of type HWND to store the Window handle of the Windows 95 taskbar.
          TForm1 = class(TForm)
             ...
          private
             hTaskBar: HWND;
             ...
          end;
    

  2. In your main form's OnCreate() event handler, place some code that resembles:
          hTaskBar := FindWindow('Shell_TrayWnd', nil);
          ShowWindow(hTaskBar, SW_HIDE);
    

  3. Finally, in your main form's OnDestroy() event handler, code something like:
          ShowWindow(hTaskBar, SW_SHOW);

"Earl F. Glynn" <EarlGlynn@WorldNet.att.net>


  PROCEDURE HideWin95TaskBar;
    VAR
      WindowHandle:  hWnd;
  BEGIN
    {Hide the Windows 95 Taskbar}
    WindowHandle := FindWindow('Shell_TrayWnd', '');
    IF   WindowHandle <> 0
    THEN ShowWindow(WindowHandle, SW_HIDE)
  END {HideWin95TaskBar};


  PROCEDURE ShowWin95TaskBar;
    VAR
      WindowHandle:  hWnd;
  BEGIN
    {Allow the Windows 95 Taskbar to appear}
    WindowHandle := FindWindow('Shell_TrayWnd', '');
    IF   WindowHandle <> 0
    THEN ShowWindow(WindowHandle, SW_RESTORE)
  END {ShowWin95TaskBar};

A Catch-a-Maximize Command Question

From: "Chami" <72223.10@compuserve.com>

> I need to have a form in my application that zooms to half of
> the screen when the Maximize button is pressed, not to full
> screen.
>

you could handle the WM_GETMINMAXINFO message from your form.

for example, add the following declaration to the protected section of your form (interface):


procedure _WM_GETMINMAXINFO( var mmInfo : TWMGETMINMAXINFO );  message wm_GetMinMaxInfo;

then define (implementation) the above message handler as follows (TForm1 being the name of your form of course):


procedure TForm1._WM_GETMINMAXINFO( var mmInfo : TWMGETMINMAXINFO );
begin
        // set the position and the size of your form when maximized:
        with mmInfo.minmaxinfo^ do
        begin
                ptmaxposition.x := Screen.Width div 4;
                ptmaxposition.y := Screen.Height div 4;

                ptmaxsize.x     := Screen.Width div 2;
                ptmaxsize.y     := Screen.Height div 2;
        end;
end;

How do you detect Windows version?

From: mdiluglio@aol.com

Check the API help In the WINPROCS unit try the function GetVersion: LongInt;

The GetVersion function retrieves the current version numbers of the Windows and MS-DOS operation systems.

NOTE there is a error in the orginal API documentation, the major/minor Win version are reversed!

As I have used it:

Windows 3.1 show up as 3.1, WIN95 shows up as 3.95

Note For windows 95 and higher use the function GetVersionEx

How can I change the wallpaper programmatically?

Solution 1

The following code comes from Loyds Help File (it can be found on most delphi web pages). I haven't tried it but I will use it in one of my apps as soon as I get the bitmap from the client. let me know if it works for you.


unit Unit1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs;

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

var
  Form1: TForm1;
  Bitmap: TBitmap;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromFile('C:\WINDOWS\cars.BMP');
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  X, Y, W, H: LongInt;
begin
  with Bitmap do begin
    W := Width;
    H := Height;
  end;
  Y := 0;
  while Y < Height do begin
    X := 0;
    while X < Width do begin
      Canvas.Draw(X, Y, Bitmap);
      Inc(X, W);
    end;
    Inc(Y, H);
  end;
end;

end.

Solution 2

From: "Dirk Faber " <d.j.faber@student.utwente.nl>

Rob Wilson <wilson@pelops.compd.com> wrote
> Does anyone know how I can change the wallpaper at runtime using a
> filename that I specifiy?

procedure ChangeWallpaper(bitmap: string);       {bitmap contains filename: *.bmp}

var pBitmap : pchar;

begin
 bitmap:=bitmap+#0;
 pBitmap:=@bitmap[1];
 SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, pBitmap, SPIF_UPDATEINIFILE);
end;

> Also, is there a way of saving it to the INI file for next session?
  1. add inifiles to the uses list.
  2. create an inifile with a texteditor like this:
    [LastUsedBitmap]
    LUBitmap= c:\mybitmap.bmp
    

  3. use a procedure like this: (supposed the inifile is like above, and is named c:\Bitmap.ini)
    procedure WriteToIniFile(bitmap : string);
    
    var MyIniFile : TInifile;
    
    begin
     MyIniFile := Tinifile.Create( 'c:\Bitmap.ini' );
     MyIniFile.WriteString( 'LastUsedBitmap', 'LUBitmap', bitmap);
     MyIniFile.Free;
    end;
    
    procedure ReadFromIniFile(var bitmap: string);
    
    var MyIniFile : TInifile;
    
    begin
      MyIniFile := Tinifile.Create( 'c:\Bitmap.ini' );
      bitmap:= MyIniFile.ReadString('LastUsedBitmap', 'LUBitmap');
      MyIniFile.Free;
    end;
    

Path / directory name for 'My Computer'

Christian Piene Gundersen <j.c.p.gundersen@jusstud.uio.no>

This is a rather complicated matter, so if it isn't vital to your application, I suggest that you spend your time better than digging into it. However, I'll try to point you in the right direction.

The windows 32 operating system is based on a shell which uses virtual folders, like 'my computer', 'desktop' and 'recycle bin'. Some of these folders are part of the physical file system. That is they have a corresponding directory in the file system. This is the case with 'desktop' and 'recycle bin'. These directories can be used as InitialDir for the TOpenDialog, but first you have to get their physical location, which can be different on different computers. To get the physical location of these folders, you have to use some special API calls (see example below). Other folders, like 'my computer' and 'printers' are not part of the file system - they are only virtual. I've noticed that you can browse to these folders using the TOpenDialog, but I don't think they can be used as InitialDir.

Virtual folders are (a bit simplified) of the type SHITEMID (item identifier). They are normally accessed using pointers to item identifiers list (PIDL). To get the PIDL of a special folder, you can use the SHGetSpecialFolder function. The physical location of the corresponding directory can then be obtained by passing the PIDL to the GetPathFromIDList function. If the folder is part of the file system, the function will return the path as a string (which can be used as InitialDir). But if you want the OpenDialog to start in a folder that is only virtual (like 'my computer'), you'll have to make it accept a PIDL as InitialDir, which I don't think it will. My guess is that the TOpenDialog uses PIDLs when browsing, but only accepts physical directories as InitialDir.

Here is an example that shows how to get the 'recent documents' path and use it as InitialDir:


procedure TForm1.Button1Click(Sender: TObject);
var
PIDL: Pointer;
Path: LPSTR;
const
CSIDL_RECENT = $0008;
begin
Path := StrAlloc(MAX_PATH);
SHGetSpecialFolderLocation(Handle, CSIDL_RECENT, @PIDL);
if SHGetPathFromIDList(PIDL, Path) then // returns false if folder isn't
part of file system
  begin
  OpenDialog1.InitialDir := Path;
  OpenDialog1.Execute;
  end;
StrDispose(Path);
end;

I think you'll have to write a wrapper for these API calls. They are found in shell32.dll. The best advice I can give you if you want to dig into this is to study the ShlObj.h file. I don't program in C myself, but I found it very useful.

Some constants you may need:


  CSIDL_DESKTOP            = $0000;
  CSIDL_PROGRAMS           = $0002;
  CSIDL_CONTROLS           = $0003;
  CSIDL_PRINTERS           = $0004;
  CSIDL_PERSONAL           = $0005;
  CSIDL_STARTUP            = $0007;
  CSIDL_RECENT             = $0008;
  CSIDL_SENDTO             = $0009;
  CSIDL_BITBUCKET          = $000a;
  CSIDL_STARTMENU          = $000b;
  CSIDL_DESKTOPDIRECTORY   = $0010;
  CSIDL_DRIVES             = $0011;  // My Computer
  CSIDL_NETWORK            = $0012;
  CSIDL_NETHOOD            = $0013;
  CSIDL_FONTS              = $0014;
  CSIDL_TEMPLATES          = $0015;

Determining which font (Large or Small) is in use

"Greg Peterson" <maxint@cwnet.com>

Try this:
FUNCTION SmallFonts : BOOLEAN;
{returns TRUE if  small fonts are set, FALSE if using Large Fonts }
VAR
  DC : HDC; { used to check for number of colors available }
BEGIN
  DC := GetDC(0);
  Result :=   (GetDeviceCaps(DC, LOGPIXELSX) = 96); 
  { LOGPIXELSX will = 120 if large fonts are in use }
  ReleaseDC(0, DC);
END;

Large/Small Fonts?

Gene Eighmy <eighmy@scott.net>

> 
> When my programs run on systems with small fonts, I
> often get strange output.  Labels too small to hold all
> the text, leaving the right, or the bottom, unshown, for
> instance.  StringGrid's which don't align as expected.
> 
Try this. This will rescale both the form size and also reform small vs. large fonts. Call it in Form.FormCreate. Hope this helps.


unit geScale;

interface
uses Forms, Controls;

procedure geAutoScale(MForm: TForm);

implementation
Type
TFooClass = class(TControl); { needed to get at protected }
                               { font property }


procedure geAutoScale(MForm: TForm);
const
     cScreenWidth :integer = 800;
     cScreenHeight:integer = 600;
     cPixelsPerInch:integer= 96;
     cFontHeight:integer   = -11;  {Design-time value of From.Font.Height}

var
  i: integer;

begin
     {
     IMPORTANT!! : Set Scaled Property of TForm to FALSE with Object Inspector.

     The following routine will scale the form such that it looks the same
     regardless of the screen size or pixels per inch.  The following section
     determines if the screen width differs from the design-time screen size.
     If it differs, Scaled is set true and component positions are rescaled such
     that they appear in the same screen location as the design-time location.
     }
     if (Screen.width &;lt> cScreenWidth)or(Screen.PixelsPerInch <> cPixelsPerInch) then
     begin
          MForm.scaled := TRUE;
          MForm.height := MForm.height * screen.Height DIV cScreenHeight;
          MForm.width  := MForm.width  * screen.width DIV cScreenWidth;
          MForm.ScaleBy(screen.width, cScreenWidth);

     end;

     {
      This section determines if the run-time font size differs from the design-
      time font size.  If the run-time pixelsperinch differs form the design-time
      pixelsperinch, the fonts must be rescaled in order for the form to appear
      as designed.  Scaling is calculated as the ratio of the design-time font.height
      to run-time font.height.  Font.size will not work as it may equal the design-
      time value yet appear physically larger crowding and overrunning other
      components.    For instance, a form designed in 800x600 small fonts
      has a font.size of 8.  When you run the form on in 800x600 large fonts,
      font.size is also 8 but the text is noticably larger than when run in small
      font mode. This scaling will make them both appear to be the same size.
     }

     if (Screen.PixelsPerInch <> cPixelsPerInch) then
     begin

         for i := MForm.ControlCount - 1 downto 0 do
              TFooClass(MForm.Controls[i]).Font.Height :=
               (MForm.Font.Height div cFontHeight) *
                 TFooClass(MForm.Controls[i]).Font.Height;

     end;

end;

end.

How can I restore a window to its last state when I run it again?

A: Here is WindowRestorer - a window size and state restorer

DESCRIPTION: Ever notice how professional programs seem to remember in what condition and location you left them and their child windows? Ever notice how most RAD apps don't? You can take that ragged edge off your program with this unit. It Allows apps to save the location, size, and state of windows so that when the user reopens them, they will look as the user left them.

USE: Put WINRSTOR in the uses of clause of your main form and any forms that will be saving or restoring their own state, size, or location. (If you will be doing all the saving and restoring using WinSaveChildren and WinRestoreChildren from the main form, you only need reference it in the main form's uses clause.)

In MainForm.Create, initialize the global WinRestorer object as follows (it's already declared in this file, but needs to be allocated):


	GlobalWinRestorer := TWinRestorer.create( Application, TRUE, WHATSAVE_ALL); 

Which is the same as:
	GlobalWinRestorer := TWinRestorer.create( Application, TRUE, [location, size, state]); 

Then, in MainForm.Destroy, deallocate the global WinRestorer object as follows:
GlobalWinRestorer.free; 

A good place to save a form's status is in the queryclose event or else attached to a button or menu item. I usually create an item in the File Menu captioned 'Save &Workspace' which does:
	GlobalWinRestorer.SaveChildren(Self, [default]); 

And under main form's Close event I put:
	GlobalWinRestorer.SaveWin(Self, [WHATSAVE_ALL]); 

I have tended to restore the children's status in their own show events like this:
	GlobalWinRestorer.RestoreWin(Self, [default]); 

though I am moving toward putting in the main form's show event:
	GlobalWinRestorer.RestoreWin(Self, [default]); 
	GlobalWinRestorer.RestoreChildren(Self, [default]);

HINTS: If you set TForm.Position to poScreenCenter or anything fancy, this unit won't do what you expect. poDesigned seems to work fairly well. I could have raised an exception if you try to set top and left of a poScreenCentere'd form, but then you have to be careful using WinRestoreChildren. I opted not to check the position property and leave that up to individual developers.


unit WinRstor;

INTERFACE

USES SysUtils, Forms;

TYPE {=============================================================}


{------------------------------------------------------------------
Windows restorer object class and related types.
-------------------------------------------------------------------}
EWinRestorer = class( Exception);
TWhatSave = (default, size, location, state);
STWhatSave = set of TWhatSave;
TWinRestorer = class(TObject)
 protected
  mIniFile: string;
  mIniSect: string[80];
  mIsInitialized: boolean;
  mDefaultWhat: STWhatSave;
 public
  constructor Create( TheApp: TApplication;

    LocalDir: boolean; DefaultWhatSave: STWhatSave);
    {If localDir is true, ini dir is the app dir.  Else, ini dir is the windows dir.}
  procedure SaveWin(TheForm: TForm; What: STWhatSave);
  procedure SaveChildren(TheMDIForm: TForm; What: STWhatSave);
  procedure RestoreWin( TheForm: TForm; What: STWhatSave);
  procedure RestoreChildren(TheMDIForm: TForm; What: STWhatSave);
  property IniFileName: string  read mIniFile;
end;

CONST
  WHATSAVE_ALL = [size, location, state];


VAR
GlobalWinRestorer: TWinRestorer;

IMPLEMENTATION

Uses IniFiles;

constructor TWinRestorer.create;
var fname, path: string[100];
begin
  inherited create;
{Calculate ini file name}
  if default in DefaultWhatSave then
    raise EWinRestorer.create(
     'Attempt to initialize default window position paramaters with set ' +
     ' containing [default] item.  ' +
     'Default params may contain only members of [size, location, state].  ')
  else mDefaultWhat := DefaultWhatSave;

  fname := ChangeFileExt( ExtractFileName( TheApp.exeName), '.INI');
  if LocalDir then begin {parse out path and add to file name}
    path := ExtractFilePath(TheApp.exeName);
    if path[length(path)] <> '\' then
      path := path + '\';
    fname := path + fname;
  end;
{fill object fields}
  mIniFile := fname;
  mIniSect := 'WindowsRestorer';
{It'd be nice to write some notes to a section called [WinRestorer Notes]}
end;

procedure TWinRestorer.RestoreWin;

var FormNm, SectionNm: string[80];   ini: TIniFile;
  n,l,t,w,h: integer; {Left, Top Width, Height}
begin
  ini := TIniFile.create( mIniFile);
  TRY
    SectionNm := mIniSect;
    FormNm := TheForm.classname;
    if default in What then What := mDefaultWhat;
{Update Window State if Necessary}
    if state in What then
      n := ini.ReadInteger( SectionNm, FormNm + '_WindowState', 0);
      case  n of
        1:   TheForm.WindowState := wsMinimized;
        2:  TheForm.WindowState := wsNormal;

        3:   TheForm.WindowState := wsMaximized;
      end;
{Update Size and Location if necessary.}
    with TheForm do begin l:=left; t:=top; h:=height; w:=width; end; {Save current vals.}
    if size in What then begin
      w := ini.ReadInteger( SectionNm, FormNm + '_Width', w);
      h := ini.ReadInteger( SectionNm, FormNm + '_Height', h);
    end;
    if location in What then begin
      t := ini.ReadInteger( SectionNm, FormNm + '_Top', t);
      l := ini.ReadInteger( SectionNm, FormNm + '_Left', l);

    end;
    TheForm.SetBounds(l,t,w,h);
  FINALLY
    ini.free;
  END;
end;

procedure TWinRestorer.RestoreChildren;
var i: integer;
begin
  if TheMDIForm.formstyle <> fsMDIForm then
    raise EWinRestorer.create('Attempting to save window sizes of children for a non MDI parent window.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      RestoreWin( TheMDIForm.MDIChildren[i], what);
end;

procedure TWinRestorer.SaveWin;
var FormNm, SectionNm: string[80];   w : STWhatsave; ini: TIniFile;

begin
  ini := TIniFile.create( mIniFile);
  TRY
    SectionNm := mIniSect;
    FormNm := TheForm.ClassName;
    if default in What then w := mDefaultWhat else w := mDefaultWhat;
    if size in w then begin
      ini.WriteInteger( SectionNm, FormNm + '_Width', TheForm.Width);
      ini.WriteInteger( SectionNm, FormNm + '_Height', TheForm.Height);
    end;
    if location in w then begin
      ini.WriteInteger( SectionNm, FormNm + '_Top', TheForm.Top);
      ini.WriteInteger( SectionNm, FormNm + '_Left', TheForm.Left);

    end;
    if state in w then
      case TheForm.WindowState of
        wsMinimized:   ini.WriteInteger( SectionNm, FormNm + '_WindowState', 1);
        wsNormal:     ini.WriteInteger( SectionNm, FormNm + '_WindowState', 2);
        wsMaximized:   ini.WriteInteger( SectionNm, FormNm + '_WindowState', 3);
      end;
  FINALLY
    ini.free;
  END;
end;

procedure TWinRestorer.SaveChildren;
var i: integer;
begin
  if TheMDIForm.formstyle <> fsMDIForm then
    raise EWinRestorer.create('Attempting to restore window sizes of children for a non MDI parent window.')

  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      SaveWin( TheMDIForm.MDIChildren[i], what);
end;

INITIALIZATION
END.

{ This code came from Lloyd's help file! }

How: to determine name of StartUp group

From: Allan Carlton <zephyr@athene.co.uk>

In each language version of Windows the 'StartUp' folder has als a different name.
Is there any way to determine the correct name of this folder ??
There is an entry in the registry under:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Start Menu

Which might give you the info you need

Finding Boot Drive

From: "HIKI Takehito" <f8498008@ca.aif.or.jp>
Is there a function or API call to find the boot drive?
I found it in the Registry.


 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup

"BootDir" value may be BootDrive.

How to make a window system modal ?

From: "Eric Lawrence" <deltagrp@keynetcorp.net>


SetSystemModalWindow(Form1.handle);

Sending Keystrokes/Text to a Window...

From: "David Zajac" <dzajac@HiWAAY.net> Hope this helps:
unit Unit1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    AppInst: THandle;
    AppWind: THandle;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses ShellAPI;
 

procedure SendShift(H: HWnd; Down: Boolean);
var vKey, ScanCode, wParam: Word;
    lParam: longint;
begin
  vKey:= $10;
  ScanCode:= MapVirtualKey(vKey, 0);
  wParam:= vKey or ScanCode shl 8;
  lParam:= longint(ScanCode) shl 16 or 1;
  if not(Down) then lParam:= lParam or $C0000000;
  SendMessage(H, WM_KEYDOWN, vKey, lParam);
end;

procedure SendCtrl(H: HWnd; Down: Boolean);
var vKey, ScanCode, wParam: Word;
    lParam: longint;
begin
  vKey:= $11;
  ScanCode:= MapVirtualKey(vKey, 0);
  wParam:= vKey or ScanCode shl 8;
  lParam:= longint(ScanCode) shl 16 or 1;
  if not(Down) then lParam:= lParam or $C0000000;
  SendMessage(H, WM_KEYDOWN, vKey, lParam);
end;

procedure SendKey(H: Hwnd; Key: char);
var vKey, ScanCode, wParam: Word;
    lParam, ConvKey: longint;
    Shift, Ctrl: boolean;
begin
  ConvKey:= OemKeyScan(ord(Key));
  Shift:= (ConvKey and $00020000) <> 0;
  Ctrl:= (ConvKey and $00040000) <> 0;
  ScanCode:= ConvKey and $000000FF or $FF00;
  vKey:= ord(Key);
  wParam:= vKey;
  lParam:= longint(ScanCode) shl 16 or 1;
  if Shift then SendShift(H, true);
  if Ctrl then SendCtrl(H, true);
  SendMessage(H, WM_KEYDOWN, vKey, lParam);
  SendMessage(H, WM_CHAR, vKey, lParam);
  lParam:= lParam or $C0000000;
  SendMessage(H, WM_KEYUP, vKey, lParam);
  if Shift then SendShift(H, false);
  if Ctrl then SendCtrl(H, false);
end;

function EnumFunc(Handle: HWnd; TF: TForm1): Bool; Far;
begin
  TF.AppWind:= 0;
  if GetWindowWord(Handle, GWW_HINSTANCE) = TF.AppInst then
    TF.AppWind:= Handle;
  result:= (TF.AppWind = 0);
end;

procedure TForm1.Button1Click(Sender: TObject);
var Text: Array[0..255] of char;
begin
  AppInst:= ShellExecute(Handle, 'open', 'notepad.exe', nil, '', SW_NORMAL);
  EnumWindows(@EnumFunc, longint(self));
  AppWind:= GetWindow(AppWind, GW_CHILD);
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
  SendKey(AppWind, 'T');
  SendKey(AppWind, 'e');
  SendKey(AppWind, 's');
  SendKey(AppWind, 't');
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if AppWind <> 0 then SendKey(AppWind, Key);
end;

end.

Windows Messages Basics

Can anybody out there send me some basic stuff about Windows Messages
related to Delphi. All this WM_*** stuff is getting on my nerves, since I
can't understand it.
[Jim Stanley, Jim.Stanley@jacobs.com]

All the Windows messages are listed in the Windows API help in your Delphi help topics. (I'm using D1, assume the same for future versions).

The WM_ (and other) messages are essential to the way Windows works. You're well aware that Delphi is primarily an *event-driven* system; all those OnKeyPress, OnThis, OnThat methods. If you have the VCL source code, you'll find in there somewhere that those event handler methods are designed to *receive* particular Windows messages (and there are some threads in here showing how you can subclass a component and "teach" it to respond to other messages as well). Windows is constantly sending out those messages in response to actions performed by the user, and it's the business of the Delphi app (and of all Windows apps) to intercept them and handle them in ways you decide. Delphi puts a wrapper over most of the message system by creating the event handlers for components described above.

In addition to recieving those messages, you can also *send* them as well. There are a couple of ways to work this: check out SendMessage and PostMessage (both native Win API functions), as well as the Delphi Perform method. The first two require you to use the Handle parameter of the component you're sending the message to, while Perform is a method belonging to that component. The messages go into the standard Windows message queue and are processed like every other message.

Here's a trivial example: I want (for some bizarre reason) to insert a 'y' character whenever I type a '4' in a TMemo. [Think of automatically inserting a begin-end block or a closing parenthesis.) Now I could do a lot with the Memo's Lines property, but that gets pretty complex. A much simpler way of going about it is:


procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = '4' then
    SendMessage(Memo1.Handle, WM_CHAR, Word('y'), 0);
end;

Another example is something we were doing here at Jacobs that used a lot of combo boxes. We wanted them to automatically drop down when the user pressed a key, which is (unfortunately) not standard behavior. Here's what we did:
procedure TFormEffortRates.ComboBoxMaterialKeyDown(Sender: TObject; var
Key: Word;
  Shift: TShiftState);
var iShowing : integer;

{ other code, then... }
    begin
      { This tells you whether the combo is already dropped }
      iShowing := SendMessage((Sender as TComboBox).Handle, CB_GETDROPPEDSTATE, 0, 0);
      if iShowing = 0 then
        { drop the combo box }
        SendMessage((Sender as TComboBox).Handle, CB_SHOWDROPDOWN, 1,0);
    end;

Another good example is getting the line and column from a TMemo. You have to go into the API to do it. Here's a (not particularly efficient - no flames please!!) method of determining them:
function TMDIChild.GetMemoColumn(const TheMemo : TMemo) : integer;
begin
  Result := TheMemo.SelStart -
    (SendMessage(TheMemo.Handle, EM_LINEINDEX,
    GetMemoLine(TheMemo), 0));
end;

function TMDIChild.GetMemoLine(const TheMemo : TMemo) : integer;
begin
  Result := SendMessage(TheMemo.Handle, EM_LINEFROMCHAR,
TheMemo.SelStart, 0);
end;

Again, all these messages can be found in your API help. The instructions for using them are a little vague, but I'm sure everyone will be glad to help you should you need it.

In short, API messages provide you with a way to fine-tune your applications to respond in exactly the way you want them to. I would consider it an advanced Delphi topic, but it sounds like one you're more than ready for.

Buttons in Win95 task bar

Can anyone tell me of a way or a component or whatever else that will allow
delphi 2 or 3 to place a button on the task bar much like what PowerDesk
2.0 Toolbar does.
[Joolz@emarkt.com]

Here are the code snipits to do just that!


// This needs to be in your public declarations @ the top of the pas file
procedure TForm1.IconCallBackMessage( var Mess : TMessage ); message WM_USER
+ 100;


procedure TForm1.FormCreate(Sender: TObject);
var
   nid : TNotifyIconData;
begin
     with nid do
     begin
           cbSize := SizeOf( TNotifyIconData );
           Wnd := Form1.Handle;
           uID := 1;
           uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
           uCallbackMessage := WM_USER + 100;
           hIcon := Application.Icon.Handle;
           szTip := 'This is the hint!';
     end;
     Shell_NotifyIcon( NIM_ADD, @nid );
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
   nid : TNotifyIconData;
begin
     with nid do
     begin
           cbSize := SizeOf( TNotifyIconData );
           Wnd := Form1.Handle;
           uID := 1;
           uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
           uCallbackMessage := WM_USER + 100;
           hIcon := Application.Icon.Handle;
           szTip := 'This is the hint!';
// All the above is probably not needed.
     end;
     Shell_NotifyIcon( NIM_DELETE, @nid );
end;

procedure TForm1.IconCallBackMessage( var Mess : TMessage );
var
   sEventLog : String;
begin
     case Mess.lParam of
// Do whatever you wish here. For example popup up a menu on a right click.
          WM_LBUTTONDBLCLK  : sEventLog := 'Left Double Click';
          WM_LBUTTONDOWN    : sEventLog := 'Left Down';
          WM_LBUTTONUP      : sEventLog := 'Left Up';
          WM_MBUTTONDBLCLK  : sEventLog := 'M Dbl';
          WM_MBUTTONDOWN    : sEventLog := 'M D';
          WM_MBUTTONUP      : sEventLog := 'M U';
          WM_MOUSEMOVE      : sEventLog :=  'movement';
          WM_MOUSEWHEEL     : sEventLog := 'Wheel';
          WM_RBUTTONDBLCLK  : sEventLog := 'r dbl';
          WM_RBUTTONDOWN    : sEventLog := 'r down';
          WM_RBUTTONUP      : sEventLog := 'r up';
     end;
end;

Control Panel

From: "Hiki Takehito" <takeone@pop06.odn.ne.jp>

>Anyone know how to put a Delphi application inside the Control Panel?
If you use Delphi3, add Cpl unit at dpr file.

I show you a sample code. -----------


library Project1; {Change "program" to "library"}

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

{$R *.RES}

procedure ExecuteApp;
 begin
  Application.Initialize;
  Application.CreateForm(TForm1,Form1);
  Application.Run;
end;

{A callback function to export at Control Panel}
function CPlApplet(hwndCPl: THandle; uMsg: DWORD;
                 lParam1, lParam2: LongInt):LongInt;stdcall;
var
  NewCplInfo:PNewCplInfo;
begin
  Result:=0;
  case uMsg of
   {Initialization.Return True.}
   CPL_INIT:
    Result:=1;
   {Number of Applet.}
   CPL_GETCOUNT:
    Result:=1;
   {Transporting informations of this Applet to the Control Panel.}
   CPL_NEWINQUIRE:
    begin
     NewCplInfo:=PNewCplInfo(lParam2);
     with NewCplInfo^ do
     begin
      dwSize:=SizeOf(TNewCplInfo);
      dwFlags:=0;
      dwHelpContext:=0;
      lData:=0;
      {An icon to display on Control Panel.}
      hIcon:=LoadIcon(HInstance,'MAINICON');
      {Applet name}
      szName:='Project1';
      {Description of this Applet.}
      szInfo:='This is a test Applet.';
      szHelpFile:='';
     end;
   end;
   {Executing this Applet.}
   CPL_DBLCLK:
    ExecuteApp;

   else Result:=0;
  end;
end;

{Exporting the function of CplApplet}
exports
  CPlApplet;

begin
end.
To use this, change the extention from "dll" to "cpl". And put into the System folder.

Applet means a piece of Control Panel.Display,Fonts,Mouse,System are all Applets.

Associate filetype [extension)

From: Jeremy Collins <jem@jcollins.demon.co.uk>

Basically, you need to add two keys to the registry under HKEY_CLASSES_ROOT. Say your extension in ".ext", then the first key you add is the extension itself:


HKEY_CLASSES_ROOT\
  .ext\
and set the "default" string value of this key to an "internal name" for your file type - for example MyApp.Document:
HKEY_CLASSES_ROOT\
  .ext\
     Default = "MyApp.Document"
You then create another key with this name:
HKEY_CLASSES_ROOT\
  MyApp.Document\
Create a sub-key of this called "shell", a sub-key of *this* called "open" and a further sub-key of "open" called "command". The default value uder this key is the location and name of your your application folled by "%1" which represents the filename parameter that Windows will pass to your executable:
HKEY_CLASSES_ROOT\
  MyApp.Document\
    shell\
      open\
        command\
          Default = "C:\myapp\myapp.exe %1"
You can do this in code with the TRegistry object, or use InstallShield, which can make registry changes for you. I'd advise doing both, in case the user trashes your registry entry. From: "Rodney E Geraghty" &tt;gerarod@ibm.net>

The easiest way I've found to do this is to modify the Extensions section of the win.ini file that is located in the Windows directory. This also works under Win 95 and will update the registry automatically under Win95. Look at the extensions section of the win.ini to see the format you have to use. Put IniFiles in your uses clause and then use something like this:


var
  INIFile: TIniFile;
begin
  try
    INIFile := TInifile.Create('WIN.INI');
    INIFile.WriteString('Extensions','txt','c:\windows\notepad.exe ^.txt');
  finally
    INIFile.Free;
  end;
end;
This would associate *.txt files with Windows Notepad. If you had an app named MyApp in the c:\MyApps directory and your extension was *.MAP then you would change it like this:
var
  INIFile: TIniFile;
begin
  try
    INIFile := TInifile.Create('WIN.INI');
    INIFile.WriteString('Extensions','map','c:\myapps\myapp.exe ^.map');
  finally
    INIFile.Free;
  end;
end;
This will work in both Win 3.11 and Win 95 and saves you from having to modify the Reqistry under Win 95. Not sure about Win NT (or Win95b) since I don't have a test machine available. Note that this is only the first part of the solution though since it will open the associated application but it won't load the file you clicked. To do this you have to read ParamStr(1), which would hold the full path of the file you clicked, and run the file name through your file opening routine.

Hide Start Button[NEW]

From: "Carsten Paasch" <cpaasch@cww.de>

Use this proc. to hide the start button:


procedure hideStartbutton(visi:boolean);
  Var
    Tray, Child : hWnd;
    C : Array[0..127] of Char;
    S : String;
  Begin
    Tray := FindWindow('Shell_TrayWnd', NIL);
    Child := GetWindow(Tray, GW_CHILD);
    While Child <> 0
          do Begin
               If GetClassName(Child, C, SizeOf(C)) > 0
                  Then Begin
                         S := StrPAS(C);
                         If UpperCase(S) = 'BUTTON'
                            then begin
                                   // IsWindowVisible(Child)
                                   startbutton_handle:=child;
                                   If Visi
                                      then ShowWindow(Child, 1)
                                      else ShowWindow(Child, 0);
                                 end;
                       End;
               Child := GetWindow(Child, GW_HWNDNEXT);
             End;
  End;



Please email me and tell me if you liked this page.