Single-instance applications

Single-instance applications


by Kent Reisdorph

Some applications are built to allow users to run as many instances of the application as the user desires. Other applications allow only a single instance of the application to be run. The VCL model does not contain a pre-built method of allowing only a single instance of an application. This article will show you how to create applications that only allow a single instance.

This article also shows how to pass information from a second instance of an application to the first instance. Consider the case where your application is already running and the user double-clicks a file associated with your application in Explorer (file associations are discussed in the article, "Using file associations"). In that case you would want to prevent a second instance from starting, but yet load the file the user double-clicked in the original instance of the application. This article will explain how to handle that situation.

The example program for this article is the same as the example program for the article, "Using file associations." I will refer you to Listing A in that article at points in this article.

Preventing a second instance from running

An application that allows only a single instance requires you to go where you might never have been before—to the project source file. The project source contains a WinMain() function. WinMain() is the entry point for all Windows GUI applications. The WinMain() for a standard VCL GUI application contains code that initializes the Application object, creates any forms in the project’s auto-create list, and calls Application->Run() to start the application. You can view the project source by choosing Project|View Source in C++Builder 4, or View|Project Source in C++Builder 1 and 3. In most VCL applications you never have to look at the project source. When preventing a second instance of an application from running, though, you need to execute code before the VCL gets a chance to initialize the Application object.

In the days of 16-bit Windows, detecting a previous instance was easy. The WinMain() function contains a parameter called hPrevInstance. You only had to examine hPrevInstance and see if it contained a valid instance handle (indicating a previously running instance of the program). If it was 0, there was no previous instance. In 32-bit Windows, hPrevInstance is still a WinMain() parameter, but it is always 0.

Preventing a second instance, then, requires you to use some global mechanism to detect that an instance of the application is already running. By global I mean that the mechanism must be available to any Windows application. You can detect an existing instance of an application in one of several ways. One way is to use FindWindow() or EnumWindows() to locate a previous instance. Another, more reliable way, is to use a mutex.

Using a mutex

The term mutex comes from the words "mutually exclusive." A mutex is a synchronization object typically used to insure that two or more threads do not attempt to simultaneously access shared memory. Using a mutex is relatively straightforward. When used in this context, the mutex is used in the WinMain() function as follows:

?Attempt to read the mutex. If the mutex does not exist then this is the first instance of the application.

?Create the mutex if it does not already exist.

?Release the mutex after Application->Run() returns. This only happens when the application closes.

?If the mutex exists then this is a second instance of the application. Terminate the second instance by returning from WinMain().

The following code is the simplest WinMain() that can be written given the above steps:

WINAPI WinMain(
  HINSTANCE, HINSTANCE, LPSTR, int)
{
  try {
    // Try to open the mutex.
    HANDLE hMutex = OpenMutex(
      MUTEX_ALL_ACCESS, 0, "MyApp1.0");

    if (!hMutex)
      // Mutex doesn’t exist. This is
      // the first instance so create
      // the mutex.
      hMutex = 
        CreateMutex(0, 0, "MyApp1.0");
    else
      // The mutex exists so this is the
      // the second instance so return.
      return 0;

    Application->Initialize();
    Application->CreateForm(
      __classid(TForm1), &Form1);
    Application->Run();

    // The app is closing so release
    // the mutex.
    ReleaseMutex(hMutex);
  }
  catch (Exception &exception) {
    Application->
      ShowException(&exception);
  }
  return 0;
}

Note that the calls to OpenMutex() and CreateMutex() specify a mutex name in their final parameters. The mutex name must be unique or you may end up opening a mutex that belongs to someone else. It is up to you to decide what constitutes a unique name, but any reasonable combination of your application name and version should suffice.

Bringing the application to the front

As I said, the previous WinMain() shows the simplest code that will prevent a second instance of the application from running. In most cases, though, you will want to bring the running instance of the application to the front before terminating the second instance. This can be accomplished with only two additional lines of code:

if (!hMutex)
  hMutex = CreateMutex(0, 0, "MyApp1.0");
else {
  HWND hWnd = FindWindow(
    0, "File Association Example");
  SetForegroundWindow(hWnd);
  return 0;
}

First I use FindWindow() to obtain the window handle of the first instance of the application. Next, I call SetForegroundWindow() to bring the first instance to the top of all other applications. If your application’s title bar changes based on the file currently open, you may have to use EnumWindows() to get the window handle of the running instance.

Passing data to the initial instance

When writing Windows applications, you should always try to anticipate how your customers will use (or abuse) your application. If you have a file association for your application then your users may double-click a document file in Explorer to launch your application. If an instance of the application is already running when that happens, you should bring the application to the top and load the file the user double-clicked. This requires a bit of work to implement, as you must pass the path and file name of the file to the first instance of the application.

Passing data from one application to another in 32-bit Windows is not necessarily straightforward. This is because Windows prevents a process from accessing data owned by another process. In order to pass data from the second instance of the application to the first instance, you must implement some type of shared memory scheme. As with many tasks in Windows, this can be accomplished in many ways. You might use a memory mapped file, a named pipe, or a mailslot. You might even be tempted to write a file to disk that the initial instance can read (although I would consider that approach a hack). Another approach is to use the WM_COPYDATA message.

Using the WM_COPYDATA message

Perhaps the simplest way of getting data from the second instance to the first instance is by using the WM_COPYDATA message. This message is specifically designed to allow one application to send data to another application. When you send a WM_COPYDATA message, you pass the handle of the window sending the message in the WPARAM, and a pointer to a COPYDATASTRUCT in the LPARAM. COPYDATASTRUCT is a simple structure:

typedef struct tagCOPYDATASTRUCT {
  DWORD dwData;
  DWORD cbData;
  PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;

The dwData member can be used if you are only passing 32 bits of data to the second instance. If you need to pass a block of memory to the second instance, you set the cbData member to the size of the memory block, and the lpData member to the address of the memory block.

Windows will guarantee that the data sent in the COPYDATASTRUCT will exist until after the WM_COPYDATA message has been carried out. As such, you must use SendMessage() to send a WM_COPYDATA message. You cannot use PostMessage(). Here is the code I use to pass the command line from the second instance of the example application to the first instance:

if (strlen(cmdLine) != 0) {
  COPYDATASTRUCT cds;
  cds.cbData = strlen(cmdLine) + 1;
  cds.lpData = cmdLine;
  SendMessage(hWnd, 
    WM_COPYDATA, 0, (LPARAM)&cds);
}

In this code, cmdLine is the command line passed to the application by Windows. The command line is passed in the third parameter to WinMain(). Note that C++Builder does not assign variable names to the WinMain() parameters so you will have to add the variable name to the function header (see Listing B). I set the cbData member to the length of the command line text and the lpData member to the address of the command line (cmdLine is a char*). After that, I send the WM_COPYDATA message to the first instance’s window handle. Remember, I had previously obtained the window handle to the first instance when I brought the application to the foreground. In this case I am not interested in the WPARAM so I set it to 0. I send the address of the COPYDATASTRUCT instance in the LPARAM (the cast is necessary because LPARAM is an int). To see this code in its proper context, see the WinMain() function in Listing B.

Naturally, the application must have code to catch the WM_COPYDATA message and to take appropriate action when the message is received. Let’s look at that now.

Handling the WM_COPYDATA message

The example program’s WmCopyData() method is the message handler for the WM_COPYDATA message. The code in this method extracts the command line from the COPYDATASTRUCT data and either prints or opens a file. The WmCopyData() method is shown in Listing A of the previous article.

The WmCopyData() method has a TWMCopyData reference as its parameter. This makes it easy to extract the command line:

String S = 
  (char*)Message.CopyDataStruct->lpData;

I simply cast the lpData member to a char* and assign the result to a String object. I now have the command line that was passed to the second instance of the application. At that point I parse the command line to see if I am printing or if I should open the file passed in the command line.

If you examine the WmCopyData() method you will see that I use a temporary TRichEdit object to print the contents of the file. I do this so that I can leave the text in the application’s RichEdit control intact during printing.

Conclusion

Creating an application that only allows a single instance to run can be challenging at first. This is especially true if your application has a file association. Your users can run your application in many ways, and that always leads to complications. Properly handling a single-instance application is easy if you follow the guidelines in this article.

Listing A: FileAssociation.cpp

#include <vcl.h>
#pragma hdrstop
USERES("FileAssociation.res");
USEFORM("MainU.cpp", Form1);

WINAPI WinMain(
  HINSTANCE, HINSTANCE, LPSTR cmdLine, int)
{
  try {
    // Try to open the mutex.
    HANDLE hMutex = OpenMutex(
      MUTEX_ALL_ACCESS, 0, "MyApp1.0");

    // If hMutex is 0 then the mutex doesn't exist.
    if (!hMutex)
      hMutex = CreateMutex(0, 0, "MyApp1.0");
    else {
      // This is a second instance. Bring the 
      // original instance to the top.
      HWND hWnd = FindWindow(
        0, "File Association Example");
      SetForegroundWindow(hWnd);

      // Command line is not empty. Send the 
      // command line in a WM_COPYDATA message.
      if (strlen(cmdLine) != 0) {
        COPYDATASTRUCT cds;
        cds.cbData = strlen(cmdLine);
        cds.lpData = cmdLine;
        SendMessage(
          hWnd, WM_COPYDATA, 0, (LPARAM)&cds);
      }

      return 0;
    }

    Application->Initialize();
    Application->CreateForm(
      __classid(TForm1), &Form1);
    Application->Run();

    ReleaseMutex(hMutex);
  }
  catch (Exception &exception) {
    Application->ShowException(&exception);
  }
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值