Single-instance applications

来源:互联网 发布:tag标签自动获取seo 编辑:程序博客网 时间:2024/06/06 18:21

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 toListing 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 aWinMain() function. WinMain() is the entry point for all Windows GUI applications. TheWinMain() for a standard VCL GUI application contains code that initializes theApplication object, creates any forms in the project’s auto-create list, and callsApplication->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 theApplication 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 examinehPrevInstance and see if it contained a valid instance handle (indicating a previously running instance of the program). If it was0, there was no previous instance. In 32-bit Windows, hPrevInstance is still aWinMain() 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() orEnumWindows() 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 fromWinMain().

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 callSetForegroundWindow() 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 useEnumWindows() 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 theWM_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 theWM_COPYDATA message. This message is specifically designed to allow one application to send data to another application. When you send aWM_COPYDATA message, you pass the handle of the window sending the message in theWPARAM, 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 thecbData 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 theWM_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 toWinMain(). Note that C++Builder does not assign variable names to theWinMain() parameters so you will have to add the variable name to the function header (seeListing B). I set the cbData member to the length of the command line text and thelpData member to the address of the command line (cmdLine is achar*). 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 theCOPYDATASTRUCT instance in the LPARAM (the cast is necessary becauseLPARAM is an int). To see this code in its proper context, see theWinMain() 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 theWM_COPYDATA message. The code in this method extracts the command line from theCOPYDATASTRUCT data and either prints or opens a file. The WmCopyData() method is shown inListing 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 aString 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 temporaryTRichEdit 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 hdrstopUSERES("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;}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 家里路由器网速一会快一会慢怎么办 用快看影视下载电影网速太慢怎么办 苹果手机下载东西网速特别慢怎么办 网上买重庆时时彩输了很多钱怎么办 找不到自己在哪个平台借过钱怎么办 九游账号绑定手机之前绑定的怎么办 九游充过钱的游戏忘了游戏名怎么办 百度网盘密码忘了申诉不了怎么办 手机号被别人注册了百度账号怎么办 快手被盗找回时出来重置密码怎么办 魅族账号密码和密保都忘记了怎么办 vivo账号的密保问题忘了怎么办 oppo账号密保问题忘了怎么办 小米手机刷了机忘了账号密码怎么办 忘了小米账号的密码是多少怎么办 千牛账号在手机上被限制登录怎么办 违规的千牛账号被限制登录了怎么办 苹果id和锁屏密码忘记了怎么办 感应门的编程密码忘记了怎么办 交易猫买的号被找回了怎么办 uc上我的小说看不了怎么办 微信零钱忘记密码没有银行卡怎么办 九游平台冻结提不了现怎么办 计算机考试报名登录名忘记了怎么办 云顶扑克提现怎么提不出来怎么办 微信正在下载一直0kb怎么办 守望先锋运行时出现意外错误怎么办 信用卡暂停使用怎么办还能恢复吗 新刷乳胶漆墙面一碰一个坑怎么办 夏天开空调冻着了头疼打喷嚏怎么办 桑蚕丝衣服被沐浴露退了色怎么办 空间被别人知道了密码登录了怎么办 三星手机显示解析包出现问题怎么办 三星手机下载解析包出现问题怎么办 两万的流动大棚给整坏了怎么办 劲舞团抽奖领了一样的衣服怎么办 win系统ps界面字体太小怎么办 任何网页都变成监控登录界面怎么办 微信启动录音的尝试被拒绝怎么办 微信传到电脑的文件打不开怎么办 转转网账号出租时遇到防沉迷怎么办