2010/03/16

[轉載] C++ Builder: Use a mutex to achieve synchronization

在網路上有看到一篇:C++ Builder中引用API函数禁止应用程序多次启动
看得不是很明白
於是找到另一篇,C++ Builder Developer's Journal / Use a mutex to achieve synchronization
但編排不是很好,所以我有稍微做了一下程式碼的排版,其它內容一字不變地轉載:
January 1998
Use a mutex to achieve synchronization
by Sam Azer

Over the past few months, a few people on the CPB-Thread listserv have asked how to prevent more than one instance of an application from being started. (To subscribe to CPB-Thread, visit www.cobb.com/cpb.) Several people have worked very hard on a variety of answers to this question. It seems, though, that the simplest solution is one suggested by members of Borland's TeamB on their news server: a named mutex. As you know, Windows 95 provides a number of built-in functions for running multiple tasks and threads within a task. Accordingly, a group of Synchronization functions is available to ensure that your tasks and threads are able to work together effectively.

One of the more common problems in a multitasking environment is resource sharing. If you're using a resource--for example, a serial port--you don't want somebody else to start using it before you're finished. The standard solution to this problem is for you to get a mutual exclusion lock (mutex) on the resource. If you're able to get the lock, you can use the resource. If, while trying to lock the resource, you find that somebody else has it--you must wait.

In this article, we'll build a small and very simple function to return true if an instance of a program is already running. To do this, we'll use a simple named mutex. (You can download the code from this article at www.cobb.com/cpb.) Let's start by looking at how you can use a mutex.


Using a named mutex
A mutex is a very simple object. It has a name, access rights, and a state. The name must be unique among all mutex, semaphore, and file-mapping objects. In most cases, the access rights will be MUTEX_ALL_ACCESS. The state can be Owned or Not Owned. If you create a resource that can be used by only one caller at a time, you'll probably also create a mutex for it. In this case, your callers try to open the mutex to see if the resource exists. Once they have a valid handle to the mutex, they use wait functions to gain ownership of the resource and start using it. Wait functions sleep until a mutex isn't owned, then take ownership of it and use the resource. (You can optionally place a time limit on a wait function.) Eventually, the caller will release the mutex, marking it not owned and available to the next caller. In this way, all callers wait to take ownership of the resource when they need it and release it when they're finished. If a caller has no further need for a resource, the mutex handle can be closed.

Once a mutex exists, any tasks trying to create it again will get a handle to it and an error already exists error. If a task ends, all the mutex handles that it owns are closed. When all tasks have closed their handles to a mutex, it's destroyed. Therefore, a single call to the CreateMutex() function is all you need to find out if an instance of an application is already running!

Using CreateMutex()

The CreateMutex() function takes only three parameters: a pointer to a security descriptor, a flag to request immediate ownership, and the name of the mutex to create. It returns a HANDLE. It ignores the pointer to the security descriptor under Windows 95. Under Windows NT, you can get default security settings by passing a NULL pointer. The mutex name is simply any unique null-terminated string (up to MAXPATH characters in length). If the returned HANDLE is null, something undesirable happened. In any case, GetLastError() should return zero to indicate that the mutex was created. A value of ERROR_ALREADY_EXISTS indicates that the mutex already exists. Use SysErrorString() to translate the other error codes into English.

By now you've probably realized that there really isn't much to it--you can very easily detect an existing instance of an application using this method. Listing A shows the source code for one way to do it.

Listing A: Detecting another instance of an application

//--------------------------------------------
#include
#pragma hdrstop
//--------------------------------------------
USEFORM("OneOnlyForm.cpp", Form1);
USERES("OneOnly.res");
//--------------------------------------------
// the name of the mutex for this program
const char *MutexName = "OneOnlyDemo";
//--------------------------------------------
HANDLE CheckInstance( const char *Name )
{
// 1st: create mutex. Request ownership.
HANDLE Mutex = CreateMutex(NULL,true,Name);
// Next, error result - should be zero
int r = GetLastError();
// if r != 0, probably ERROR_ALREADY_EXISTS
if ( r != 0 )
return 0; // disaster or another instance
return Mutex; // else, return the handle.
}
//--------------------------------------------
WINAPI WinMain (HINSTANCE,HINSTANCE,LPSTR, int)
{
HANDLE Mutex = CheckInstance( MutexName );
if ( !Mutex )
{
MessageBox(HInstance, "Another Instance is running!", "Sorry", MB_OK );
ReleaseMutex( Mutex );
return 1;
};

try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1),&Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
//--------------------------------------------

Wrap up
Do a keyword search for Synchronization in the Win32 (Platform) SDK Help files for detailed information on the functions available for synchronizing--they're quite handy. In many cases the code presented in this article is enough to prevent problems from occurring between two instances of a simple database program. However, for MDI applications you'll probably want to send a message to the original instance. Watch for an article on inter-process communications in a future issue of C++Builder Developer's Journal.

沒有留言:

張貼留言