应用程序的唯一性

程序启动后,如果再次启动程序,不会出现2个程序,或者实现如Notepad++已打开一个文件,再打开另外一个文件,则追加在Notepad++界面上。

使用codeproject上别人编写的一个类,加在程序启动时即可。

 

sinstance.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*
Module : SINSTANCE.H
Purpose: Defines the interface for an MFC wrapper class to do instance checking
Created: PJN / 29-07-1998
 
Copyright (c) 1996 - 2008 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com)
 
All rights reserved.
 
Copyright / Usage Details:
 
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
when your product is released in binary form. You are allowed to modify the source code in any way you want
except you cannot modify the copyright details at the top of each module. If you want to distribute source
code with your application, then you are only allowed to distribute versions released by the author. This is
to maintain a single distribution point for the source code.
 
*/
 
 
/// Macros / Defines //
 
#pragma once
 
#ifndef __SINSTANCE_H__
#define __SINSTANCE_H__
 
#ifndef CSINGLEINSTANCE_EXT_CLASS
#define CSINGLEINSTANCE_EXT_CLASS
#endif
 
#ifndef CSINGLEINSTANCE_EXT_API
#define CSINGLEINSTANCE_EXT_API
#endif
 
 
Includes /
 
#ifndef __AFXMT_H__
#pragma message("To avoid this message, please put afxmt.h in your pre compiled header (normally stdafx.h)")
#include <afxmt.h>
#endif
 
 
Classes //
 
class  CSINGLEINSTANCE_EXT_CLASS CInstanceChecker
{
public :
//Constructors / Destructors
     CInstanceChecker( const  CString& sUniqueName);
     virtual  ~CInstanceChecker();
 
//General functions
     void  ActivateChecker();
     BOOL  TrackFirstInstanceRunning();
     BOOL  PreviousInstanceRunning();
     HWND  ActivatePreviousInstance( LPCTSTR  lpCmdLine = NULL,  ULONG_PTR  dwCopyDataItemData = 0,  DWORD  dwTimeout = 30000);
     void  QuitPreviousInstance( int  nExitCode = 0);
 
protected :
//Virtual methods
     virtual  CString GetMMFFilename();
     virtual  HWND  GetWindowToTrack();
 
//Standard non-virtual methods
   void  ReleaseLock();
 
//Data
     CMutex       m_instanceDataMutex;
     CMutex       m_executeMutex;
     CSingleLock* m_pExecuteLock;
   CString      m_sName;
};
 
#endif //__SINSTANCE_H__

  sinstance.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/*
Module : SINSTANCE.CPP
Purpose: Defines the implementation for an MFC wrapper classe
          to do instance checking
Created: PJN / 29-07-1998
History: PJN / 25-03-2000 Neville Franks made the following changes. Contact nevf@getsoft.com, www.getsoft.com
                           1. Changed #pragma error to #pragma message. Former wouldn't compile under VC6
                           2. Replaced above #pragma with #include
                           3. Added TrackFirstInstanceRunning(), MakeMMFFilename()
          PJN / 27-03-2000 1. Fixed a potential handle leak where the file handle m_hPrevInstance was not being
                           closed under certain circumstances.
                           Neville Franks made the following changes. Contact nevf@getsoft.com, www.getsoft.com
                           2. Split PreviousInstanceRunning() up into separate functions so we
                           can call it without needing the MainFrame window.
                           3. Changed ActivatePreviousInstance() to return hWnd.
          PJN / 15-05-2000 1. Serialized access to all of the CSingleInstance class to prevent race conditions
                           which can occur when you app is programatically spawned.
          PJN / 17-05-2000 1. Updated sample to show how the code can be used for dialog based apps.
          PJN / 01-01-2001 1. Added a number of asserts to CInstanceChecker::ActivatePreviousInstance
                           2. Now includes copyright message in the source code and documentation.
          PJN / 15-01-2001 1. Now allows multiple calls to PreviousInstanceRunning without ASSERTing
                           2. Removed some unnecessary VERIFY's from ActivatePreviousInstance
                           3. Made the MMF filename used modifiable via a virtual function GetMMFFilename
                           4. Made the window to track modifiable via a virtual function GetWindowToTrack
                           5. Made destructor virtual since the introduction of other virtual functions in the class
                           6. Removed a number of unnecessary verifies
                           7. Changed the meaning of the return value from TrackFirstInstanceRunning
          PJN / 17-06-2001 1. Moved most of the code from CInstanceChecker::CInstanceChecker to
                           CInstanceChecker::ActivateChecker. This allows client code to turn on or off the instance
                           checking code easily. Thanks to Anders Rundegren for this addition.
          PJN / 31-08-2001 1. made the name of the mutex which the class uses to serialize access to itself a paramter
                           to the constructor. That way multiple independent apps do not block each other while
                           they are calling into the CSingleInstance class. Thanks to Eugene Shmelyov for spotting
                           this problem.
          PJN / 23-03-2002 1. Provided a QuitPreviousInstance method. Thanks to Jon Bennett for providing this.
          PJN / 30-10-2002 1. The name of the internal memory mapped file is now based on the Mutex name rather than
                           the application name. An example: a client was writing a webcam application and wanted it to
                           run with multiple configuration for multiple camera support. So the app can run multiple times
                           as long as a special configuration is given on the command line. But for that configuration
                           only one instance is allowed. Using the application name for the memory mapped file was tying
                           the single instance to the app rather than the unique mutex name. Thanks to Frank Fesevur for
                           this nice update.
          PJN / 06-02-2003 1. Was missing a call to ReleaseLock in CInstanceChecker::ActivatePreviousInstance. Thanks to
                           Pierrick Ingels for reporting this problem.
          PJN / 09-05-2004 1. Updated the copyright details.
                           2. Extended CInstanceChecker::ActivatePreviousInstance to now allow the command line of the
                           second app to be passed to the original app. By default the parameter is NULL, meaning that
                           you get the original behaviour which just activates the previous instance. To respond to this
                           information you should add the following to your mainfrm module:
 
                           mainfrm.h
 
                           afx_msg LRESULT OnCopyData(WPARAM, LPARAM);
 
 
                           mainfrm.cpp
 
                           LRESULT CMyFrameWnd::OnCopyData(WPARAM wParam, LPARAM lParam)
                           {
                             COPYDATASTRUCT* pCDS = reinterpret_cast<COPYDATASTRUCT*>(lParam);
                             TCHAR* pszCmdLine = static_cast<TCHAR*>(pCDS->lpData);
                             if (pszCmdLine)
                             {
                               //DO SOMETHING with pszCmdLine here such as call AfxGetApp()->OpenDocumentFile(pszCmdLine);
                             }
                             return TRUE;
                           }
 
                           Also hook up your onCopyData to the windows message map using
 
                           ON_MESSAGE(WM_COPYDATA, OnCopyData)
 
 
                           Thanks to Ingo H. de Boer for providing this nice update.
 
                           3. Following a discussion on the Codeproject.com discussion forum for CSingleInstance on what
                           exactly a single instance app means (See http://www.codeproject.com/cpp/csingleinst.asp?msg=766108#xx1103xx),
                           Daniel Lohmann has produced a simple function called CreateUniqueName which given a number of
                           settings as flags, will produce a name which is unique. This code can be downloaded at
                           http://www.losoft.de/code/SingleInstance.zip. You can then use this name in the constructor for
                           CInstanceChecker. The concept of a single instance app is complicated by the concept of Window stations
                           and desktops as used by NT Services and Windows Terminal Services. In addition you might want to allow your
                           program to be run once per user.
          PJN / 30-05-2005 1. Fix for a crash where CWnd::GetLastActivePopup can sometimes return a NULL pointer. Thanks to
                           Dominik Reichl for reporting this bug.
          PJN / 07-07-2006 1. Updated copyright details.
                           2. Addition of CSINGLEINSTANCE_EXT_CLASS and CSINGLEINSTANCE_EXT_API which allows the class to be easily used
                           in an extension DLL.
                           3. Removed derivation from CObject as it was not really needed.
                           4. Updated the documentation to use the same style as the web site.
                           5. Code now uses newer C++ style casts instead of C style casts.
                           6. Fixed a number of level 4 warnings in the sample app.
                           7. Updated code to compile cleanly using VC 2005.
          PJN / 17-03-2007 1. Updated copyright details.
                           2. Optimized _INSTANCE_DATA constructor code
                           3. Reworked how the method CInstanceChecker::GetMMFFilename creates the name of the memory mapped filename
                           the code requires for sharing. Now the main instance name appears before the hard coded string. This
                           ensures that the CInstanceChecker class works correctly for terminal sessions i.e. kernel objects prefixed
                           with the value "Local\". Thanks to Mathias Berchtold for reporting this issue.
                           4. Updated the sample app code to clean compile on VC 2005
                           5. QuitPreviousInstance now uses GetLastActivePopup API to ensure it posts the WM_QUIT message to the
                           correct window of the previous instance.
          PJN / 02-02-2008 1. Updated copyright details
                           2. Removed VC 6 style classwizard comments from the sample apps code
                           3. Updated ActivatePreviousInstance method to support Win64 compliant data
                           4. ActivatePreviousInstance now takes a "dwTimeout" parameter which it now uses internally as the timeout when
                           calling SendMessageTimeout instead of SendMessage. The code now uses SendMessageTimeout instead of SendMessage
                           to ensure we do not hang if the previous instance itself is hung. Thanks to Paul Shore for suggesting this
                           update.
                           5. Updated the sample apps to clean compile on VC 2008
 
Copyright (c) 1996 - 2008 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com)
 
All rights reserved.
 
Copyright / Usage Details:
 
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
when your product is released in binary form. You are allowed to modify the source code in any way you want
except you cannot modify the copyright details at the top of each module. If you want to distribute source
code with your application, then you are only allowed to distribute versions released by the author. This is
to maintain a single distribution point for the source code.
 
*/
 
 
/  Includes  //
 
#include "stdafx.h"
#include "sinstance.h"
 
 
/// Defines / Macros //
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
 
/ Implementation //
 
//struct which is put into shared memory
struct  CWindowInstance
{
   HWND  hMainWnd;
};
 
//Class which is used as a static to ensure that we
//only close the file mapping at the very last chance
class  _SINSTANCE_DATA
{
public :
//Constructors / Destructors
   _SINSTANCE_DATA();
   ~_SINSTANCE_DATA();
 
protected :
//Member variables
   HANDLE  hInstanceData;
 
   friend  class  CInstanceChecker;
};
 
_SINSTANCE_DATA::_SINSTANCE_DATA() : hInstanceData(NULL)
{
}
 
_SINSTANCE_DATA::~_SINSTANCE_DATA()
{
   if  (hInstanceData != NULL)
   {
     ::CloseHandle(hInstanceData);
     hInstanceData = NULL;
   }
}
 
static  _SINSTANCE_DATA g_sinstanceData;
 
 
CInstanceChecker::CInstanceChecker( const  CString& sUniqueName) : m_executeMutex(FALSE, sUniqueName)
{
   //Hive away the unique name as we will also be using it for the name of the memory mapped file
   m_sName = sUniqueName;
 
   //Only one object of type CInstanceChecker should be created
   ASSERT(g_sinstanceData.hInstanceData == NULL);
   m_pExecuteLock = NULL;
}
 
void  CInstanceChecker::ActivateChecker()
{
   ASSERT(m_pExecuteLock == NULL);
 
   //Ensure there is only ever one CInstanceChecker instance
   //active at any one time throughout the system
   m_pExecuteLock =  new  CSingleLock(&m_executeMutex, TRUE);
}
 
CInstanceChecker::~CInstanceChecker()
{
   //Free up the instance lock
   ReleaseLock();
}
 
void  CInstanceChecker::ReleaseLock()
{
   if  (m_pExecuteLock)
   {
     delete  m_pExecuteLock;
     m_pExecuteLock = NULL;
  
}
 
//Track the first instance of our App.
//return TRUE on success, else FALSE
BOOL  CInstanceChecker::TrackFirstInstanceRunning()
{
   //If a previous instance is running, just return prematurely
   if  (PreviousInstanceRunning())
     return  FALSE;
 
   //If this is the first instance then copy in our info into the shared memory
 
   //First create the MMF
   int  nMMFSize =  sizeof (CWindowInstance);
   g_sinstanceData.hInstanceData = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, nMMFSize, GetMMFFilename());
   if  (g_sinstanceData.hInstanceData == NULL)
   {
     TRACE(_T( "Failed to create the MMF even though this is the first instance, you might want to consider overriding GetMMFFilename()\n" ));
     return  FALSE;
   }
 
   //Open the MMF
   CWindowInstance* pInstanceData =  static_cast <CWindowInstance*>(MapViewOfFile(g_sinstanceData.hInstanceData, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, nMMFSize));
   ASSERT(pInstanceData != NULL);    //Opening the MMF should work
 
   //Lock the data prior to updating it
   CSingleLock dataLock(&m_instanceDataMutex, TRUE);
   pInstanceData->hMainWnd = GetWindowToTrack();
   UnmapViewOfFile(pInstanceData);
 
   //Since this will be the last function that will be called
   //when this is the first instance we can release the lock
   ReleaseLock();
 
   return  TRUE;
}
 
// Returns TRUE if a previous instance of the App is running.
BOOL  CInstanceChecker::PreviousInstanceRunning()
{
   //Try to open the MMF first to see if we are the second instance
   HANDLE  hPrevInstance = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, GetMMFFilename());
   BOOL  bPreviousInstance = (hPrevInstance != NULL);
   if  (hPrevInstance)
     CloseHandle(hPrevInstance);
 
   return  bPreviousInstance;
}
 
CString CInstanceChecker::GetMMFFilename()
{
   CString sMMF(m_sName);
   sMMF += _T( "_CInstanceChecker_MMF" );
   return  sMMF;
}
 
HWND  CInstanceChecker::GetWindowToTrack()
{
   //By default the window tracked will be the standard AfxGetMainWnd()
   ASSERT(AfxGetMainWnd() != NULL);  //Did you forget to set up the mainfrm in InitInstance ?
   return  AfxGetMainWnd()->GetSafeHwnd();
}
 
//Activate the Previous Instance of our Application.
HWND  CInstanceChecker::ActivatePreviousInstance( LPCTSTR  lpCmdLine,  ULONG_PTR  dwCopyDataItemData,  DWORD  dwTimeout)
{
   //What will be the return value from this function (assume the worst)
   HWND  hWindow = NULL;
 
   //Try to open the previous instances MMF
   HANDLE  hPrevInstance = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, GetMMFFilename());
   if  (hPrevInstance)
   {
     //Open up the MMF
     int  nMMFSize =  sizeof (CWindowInstance);
     CWindowInstance* pInstanceData =  static_cast <CWindowInstance*>(MapViewOfFile(hPrevInstance, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, nMMFSize));
     if  (pInstanceData)  //Opening the MMF should work
     {
       //Lock the data prior to reading from it
       CSingleLock dataLock(&m_instanceDataMutex, TRUE);
 
       //activate the old window
       ASSERT(pInstanceData->hMainWnd);  //Something gone wrong with the MMF
       hWindow = pInstanceData->hMainWnd;
       if  (hWindow)
       {
         CWnd wndPrev;
         wndPrev.Attach(hWindow);
         CWnd* pWndChild = wndPrev.GetLastActivePopup();
 
         //Restore the focus to the previous instance and bring it to the foreground
         if  (wndPrev.IsIconic())
           wndPrev.ShowWindow(SW_RESTORE);
 
         if  (pWndChild)
           pWndChild->SetForegroundWindow();
 
         if  (lpCmdLine)
        
           //Send the current apps command line to the previous instance using WM_COPYDATA
           COPYDATASTRUCT cds;
           cds.dwData = dwCopyDataItemData;
           DWORD  dwCmdLength =  static_cast < DWORD >(_tcslen(lpCmdLine) + 1);
           cds.cbData = dwCmdLength *  sizeof ( TCHAR );
           TCHAR * pszLocalCmdLine =  new  TCHAR [dwCmdLength];  //We use a local buffer so that we can specify a constant parameter
                                                            //to this function
         #if (_MSC_VER >= 1400)
           _tcscpy_s(pszLocalCmdLine, dwCmdLength, lpCmdLine);
         #else                                                
           _tcscpy(pszLocalCmdLine, lpCmdLine);
         #endif
           cds.lpData = pszLocalCmdLine;
           CWnd* pMainWindow = AfxGetMainWnd();
           HWND  hSender = NULL;
           if  (pMainWindow)
             hSender = pMainWindow->GetSafeHwnd();
 
           //Send the message to the previous instance. Use SendMessageTimeout instead of SendMessage to ensure we
           //do not hang if the previous instance itself is hung
           DWORD_PTR  dwResult = 0;
           if  (SendMessageTimeout(hWindow, WM_COPYDATA,  reinterpret_cast < WPARAM >(hSender),  reinterpret_cast < LPARAM >(&cds),
                                  SMTO_ABORTIFHUNG, dwTimeout, &dwResult) == 0)
           {
             //Previous instance is not responding to messages
             hWindow = NULL;
           }
 
           //Tidy up the heap memory we have used
           delete  [] pszLocalCmdLine;
         }
 
         //Detach the CWnd we were using
         wndPrev.Detach();
       }
 
       //Unmap the MMF we were using
       UnmapViewOfFile(pInstanceData);
     }
 
     //Close the file handle now that we
     CloseHandle(hPrevInstance);
 
     //When we have activate the previous instance, we can release the lock
     ReleaseLock();
   }
 
   return  hWindow;
}
 
void  CInstanceChecker::QuitPreviousInstance( int  nExitCode)
{
   //Try to open the previous instances MMF
   HANDLE  hPrevInstance = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, GetMMFFilename());
     if  (hPrevInstance)
     {
         // Open up the MMF
         int  nMMFSize =  sizeof (CWindowInstance);
         CWindowInstance* pInstanceData =  static_cast <CWindowInstance*>(MapViewOfFile(hPrevInstance, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, nMMFSize));
         if  (pInstanceData != NULL)  //Opening the MMF should work
         {
           // Lock the data prior to reading from it
           CSingleLock dataLock(&m_instanceDataMutex, TRUE);
 
           //activate the old window
           ASSERT(pInstanceData->hMainWnd);  //Something gone wrong with the MMF?
           HWND  hWnd = pInstanceData->hMainWnd;
 
       //Ask it to exit
       HWND  hChildWnd = GetLastActivePopup(hWnd);
       PostMessage(hChildWnd, WM_QUIT, nExitCode, 0);
       }
 
     //Close the file handle now that we
     CloseHandle(hPrevInstance);
   }
}

  windows MFC下使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
在InitInstance()中添加:
//Check for the previous instance as soon as possible
   <strong>CInstanceChecker instanceChecker(_T( "CInstanceChecker Example FrameWnd App" ));</strong>
   <strong>instanceChecker.ActivateChecker();</strong>
  <strong>  if  (instanceChecker.PreviousInstanceRunning())</strong>
   {
     CCommandLineInfo cmdInfo;
     ParseCommandLine(cmdInfo);
 
       AfxMessageBox(_T( "Previous version detected, will now restore it" ), MB_OK);
    <strong> instanceChecker.ActivatePreviousInstance(cmdInfo.m_strFileName);</strong>
       return  FALSE;
   }
 
...
 
   //Standard MFC stuff, create the mainframe
   CFrameWnd* pMainFrm =  new  CMyFrameWnd();
   if  (!pMainFrm->Create(NULL, m_pszAppName))
     return  FALSE;
   pMainFrm->ShowWindow(SW_SHOW);
   m_pMainWnd = pMainFrm;
 
   // If this is the first instance of our App then track it so any other instances can find us
   <strong>instanceChecker.TrackFirstInstanceRunning();
 
 
     
</strong>

  如需实现像Notepad++追加在程序后面的效果,可在CFrameWnd中添加:

添加WM_COPYDATA消息响应

1
2
3
4
5
6
7
8
9
10
11
12
LRESULT  CMyFrameWnd::OnCopyData( WPARAM  /*wParam*/ LPARAM  lParam)
{
   COPYDATASTRUCT* pCDS =  reinterpret_cast <COPYDATASTRUCT*>(lParam);
   TCHAR * pszCmdLine =  static_cast < TCHAR *>(pCDS->lpData);
   if  (pszCmdLine)
   {
     CString sMsg;
     sMsg.Format(_T( "Another instance passed us the command line: %s" ), pszCmdLine);
     AfxMessageBox(sMsg);
   }
   return  TRUE;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值