Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents
Bookmark and Share

Part I - Foundations
  Chapter 1 Delphi 7 and Its IDE
  Chapter 2 The Delphi Programming Language
  Chapter 3 The Run-Time Library
  Chapter 4 Core Library classes
  Chapter 5 Visual Controls
  Chapter 6 Building the User Interface
  Chapter 7 Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 The Architecture of Delphi Applications
  Chapter 9 Writing Delphi Components
  Chapter 10 Libraries and Packages
  Chapter 11 Modeling and OOP Programming (with ModelMaker)
  Chapter 12 From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 Delphi's Database Architecture
  Chapter 14 Client/Server with dbExpress
  Chapter 15 Working with ADO
  Chapter 16 Multitier DataSnap Applications
  Chapter 17 Writing Database Components
  Chapter 18 Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 Internet Programming: Sockets and Indy
  Chapter 20 Web Programming with WebBroker and WebSnap
  Chapter 21 Web Programming with IntraWeb
  Chapter 22 Using XML Technologies
  Chapter 23 Web Services and SOAP
  Chapter 24 The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 Delphi for .NET Preview: The Language and the RTL
       
  Appendix A Extra Delphi Tools by the Author
  Appendix B Extra Delphi Tools from Other Sources
  Appendix C Free Companion Books on Delphi
       
  Index    
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

 
Previous Section Next Section

Checking for a Previous Instance of an Application

One form of multitasking is the execution of two or more instances of the same application. Any application can generally be executed by a user in more than one instance, and it needs to be able to check for a previous instance already running, in order to disable this default behavior and allow for one instance at most. This section demonstrates several ways of implementing such a check, allowing me to discuss some interesting Windows programming techniques.

Looking for a Copy of the Main Window

To find a copy of the main window of a previous instance, use the FindWindow API function and pass it the name of the window class (the name used to register the form's window type, or WNDCLASS, in the system) and the caption of the window for which you are looking. In a Delphi application, the name of the WNDCLASS window class is the same as the Object Pascal name for the form's class (for example, TForm1). The result of the FindWindow function is either a handle to the window or zero (if no matching window was found).

The main code of your Delphi application should be written so that it will execute only if the FindWindow result is zero:

var
  Hwnd: THandle;
begin
  Hwnd := FindWindow ('TForm1', nil);
  if Hwnd = 0 then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else
    SetForegroundWindow (Hwnd)
end.

To activate the window of the application's previous instance, you can use the SetForegroundWindow function, which works for windows owned by other processes. This call produces its effect only if the window passed as parameter hasn't been minimized. When the main form of a Delphi application is minimized, it is hidden, and for this reason the activation code has no effect.

Unfortunately, if you run a program that uses the FindWindow call just shown from within the Delphi IDE, a window with that caption and class may already exist: the design-time form. Thus, the program won't start even once. However, it will run if you close the form and its corresponding source code file (closing only the form simply hides the window), or if you close the project and run the program from the Windows Explorer. Consider also that having a form called Form1 is quite likely to not work as expected, as many Delphi applications might have a form with the same name. This will be fixed in the following versions of the code.

Using a Mutex

A completely different approach is to use a mutex, or mutual exclusion object. This is a typical Win32 approach, commonly used for synchronizing threads. Here you will use a mutex to synchronize two different applications or, to be more precise, two instances of the same application.

Once an application has created a mutex with a given name, it can test whether this object is already owned by another application by calling the WaitForSingleObject Windows API function. If the mutex has no owner, the application calling this function becomes the owner. If the mutex is already owned, the application waits until the time-out (the function's second parameter) elapses. It then returns an error code.

To implement this technique, you can use the following project source code:

var
  hMutex: THandle;
begin
  HMutex := CreateMutex (nil, False, 'OneCopyMutex');
  if WaitForSingleObject (hMutex, 0) <> wait_TimeOut then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
end.

If you run this example twice, you'll see that it creates a new, temporary copy of the application (the icon appears in the Taskbar) and then destroys it when the time-out elapses. This approach is certainly more robust than the previous one, but it lacks a feature: How do you enable the existing instance of the application? You still need to find its form, but you can use a better technique.

Searching the Window List

When you want to search for a specific main window in the system, you can use the EnumWindows API functions. Enumeration functions are peculiar in Windows, because they usually require another function as a parameter. These enumeration functions require a pointer to a function (often described as a callback function) as parameter. This function is applied to each element of the list (in this case, the list of main windows), until the list ends or the function returns False. Here is the enumeration function from the OneCopy example:

function EnumWndProc (hwnd: THandle; Param: Cardinal): Bool; stdcall;
var
  ClassName, WinModuleName: string;
  WinInstance: THandle;
begin
  Result := True;
  SetLength (ClassName, 100);
  GetClassName (hwnd, PChar (ClassName), Length (ClassName));
  ClassName := PChar (ClassName);
  if ClassName = TForm1.ClassName then
  begin
    // get the module name of the target window
    SetLength (WinModuleName, 200);
    WinInstance := GetWindowLong (hwnd, GWL_HINSTANCE);
    GetModuleFileName (WinInstance,
      PChar (WinModuleName), Length (WinModuleName));
    WinModuleName := PChar(WinModuleName); // adjust length
    // compare module names
    if WinModuleName = ModuleName then
    begin
      FoundWnd := Hwnd;
      Result := False; // stop enumeration
    end;
  end;
end;

This function, which is called for each nonchild window of the system, checks the name of each window's class, looking for the name of the TForm1 class. When it finds a window with this string in its class name, it uses GetModuleFilename to extract the name of the executable file of the application that owns the matching form. If the module name matches that of the current program (which was extracted previously with similar code), you can be sure that you have found a previous instance of the same program. Here is how you can call the enumerated function:

var
  FoundWnd: THandle;
  ModuleName: string;
begin
  if WaitForSingleObject (hMutex, 0) <> wait_TimeOut then
    ...
  else
  begin
    // get the current module name
    SetLength (ModuleName, 200);
    GetModuleFileName (HInstance, PChar (ModuleName), Length (ModuleName));
    ModuleName := PChar (ModuleName); // adjust length
    // find window of previous instance
    EnumWindows (@EnumWndProc, 0);

Handling User-Defined Window Messages

I've mentioned that the SetForegroundWindow call doesn't work if the main form of the program has been minimized. Now you can solve this problem. You can ask the form of another application—the previous instance of the same program, in this case—to restore its main form by sending it a user-defined window message. You can then test whether the form is minimized and post a new user-defined message to the old window. Here is the code; in the OneCopy program, it follows the last fragment shown in the preceding section:

if FoundWnd <> 0 then
begin
  // show the window, eventually
  if not IsWindowVisible (FoundWnd) then
    PostMessage (FoundWnd, wm_App, 0, 0);
  SetForegroundWindow (FoundWnd);
end;

The PostMessage API function sends a message to the message queue of the application that owns the destination window. In the form's code, you can add a special function to handle this message:

public
  procedure WMApp (var msg: TMessage); message wm_App;
   
procedure TForm1.WMApp (var msg: TMessage);
begin
  Application.Restore;
end;
Note 

The program uses the wm_App message rather than the wm_User message; some system windows use wm_User, so there is no guarantee that other applications or the system won't send this message. That's why Microsoft introduced wm_App for messages that are restricted to the application's interpretation.


 
Previous Section Next Section


 


 


Copyright © 2004-2020 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide
   Facebook     Twitter