One instance of an application only- How to post the
Mike Blaszczak -- mikeblas@nwlink.com
Monday, January 13, 1997
[Mini-digest: 4 responses]
At 23:25 1/12/97 -0200, Ari Villaca wrote:
>Environment: MSVC 4.0/Win95
>I have an application which is supposed to have only one instance running.
>I know how to activate the 1st instance of the application when a 2nd one
>is started by the user.
>But the solution I have just do it, ie, activates the 1st instance. If the
>user has used Explorer to click on a saved application document name in
>order to open that document, the document won't be opened.
>How can I post the 2nd instance creation message arguments ( maybe the
>CWinApp::m_lpCmdLine ) to the 1st instance already running?
>Any pointer to related documentation or solution is greatelly appreciated.
My book outlines a technique for doing this that doesn't involve posting
messages, which has several disadvantages. The idea is to spawn a
new thread that does nothing but sleep on the object which is being
used to make the application instances run exlusively. If the object
becomes signalled, the waiting thread can react by doing what ever it
wants to within the answering application.
If you need to pass data from one application to the other, you
might do so by using shared memory or named pipes.
.B ekiM
http://www.nwlink.com/~mikeblas/
Why does the "new" Corvette look like a 1993 RX-7?
These words are my own. I do not speak on behalf of Microsoft.
-----From: Jim Lawson Williams
At 11:25 PM 12/01/97 -0200, "Ari Villaca" wrote:
>Environment: MSVC 4.0/Win95
>
>I have an application which is supposed to have only one instance running.
>I know how to activate the 1st instance of the application when a 2nd one
>is started by the user.
> //determines if another window with our class name exists
> HWND hWnd;
> hWnd = ::FindWindow( ( LPCSTR ) csClassName, NULL );
> if ( hWnd )
> {
> AfxMessageBox( "The application is already running..." );
>
You might consider something along the lines of the following (requires afxadv.h):
//allocate memory for the text, and a handle to go with it:
m_HGLOBALpath = GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE,512);
//tie that to the CSharedFile to store the pathname:
m_pSharedFileToPass->SetHandle(m_HGLOBALpath, /*BOOL bAllowGrow=*/ TRUE);
//store the pathname in the memory file:
m_pSharedFileToPass->Write(lpPathBuf,nCount);
//identify the original thread:
DWORD firstThreadID = GetWindowThreadProcessId(hWnd,NULL);
//post that message!
PostThreadMessage( firstThreadID,myPathNameMsg,
(WPARAM)m_HGLOBALpath,
(LPARAM)nCount);//or perhaps sender's threadID
and, per PSS ID Number: Q142415, catch the posted message through
PreTranslateMessage(MSG* pMsg);
and terminate the posting process either by return post, or by polling the shared file at
(say) 1 sec. intervals for a NULL string.
Hope this is what you seek, blunders excepted!
Regards,
Jim LW
>From the BBC's "Barchester Chronicles":
"I know that ultimately we are not supposed to understand.
But I also know that we must try."
-- the Reverend Septimus Harding, crypt-analyst, clog-dancer, C++ programmer
-----From: Kostya Sebov
> Environment: MSVC 4.0/Win95
>
> I have an application which is supposed to have only one instance running.
> I know how to activate the 1st instance of the application when a 2nd one
> is started by the user.
>
> But the solution I have just do it, ie, activates the 1st instance. If th=
> e
> user has used Explorer to click on a saved application document name in
> order to open that document, the document won't be opened.
>
> How can I post the 2nd instance creation message arguments ( maybe the
> CWinApp::m_lpCmdLine ) to the 1st instance already running?=20
>
> The code for limiting the application to one instance is:
>
> Bool CMyApp::InitInstance() {
> ....
> ....
> ....
> SetRegistryKey( "MyCompanyName" );
> csClassName =3D GetProfileString( "Initialization", "ClassName", "None" =
> );
>
> //determines if another window with our class name exists
> HWND hWnd;
> hWnd =3D ::FindWindow( ( LPCSTR ) csClassName, NULL );
> if ( hWnd )
> {
> AfxMessageBox( "The application is already running..." );
>
> SetForegroundWindow( hWnd );
> CWnd* PrevWindow =3D CWnd::FromHandle( hWnd );
> PrevWindow->SetForegroundWindow();
>
> //does it have a popup?
> CWnd* ChildWnd =3D PrevWindow->GetLastActivePopup();
>
> //bring the main window to the top
> PrevWindow->BringWindowToTop();
>
> //if iconic, restore the main window
> if ( PrevWindow->IsIconic() )
> PrevWindow->ShowWindow( SW_RESTORE );
>
> //if there are popups, bring them along too
> if ( PrevWindow !=3D ChildWnd )
> ChildWnd->BringWindowToTop();
>
> //here seems to be the place to post the 2nd instance creation argument=
> s
> (maybe m_lpCmdLine )
> //to the 1st instance, so they are processed
> //for now, send a File/New message
> PrevWindow->PostMessage( WM_COMMAND, ID_FILE_NEW );
> =09
>
> //
> return FALSE;
> }
>
> ....
> ....
> ....
> }
>
> Any pointer to related documentation or solution is greatelly appreciated=
> .=20
>
> Regards,
>
> Ari
>
> -------------------------------------------------------------------------=
> ---
> ---
> Ari de Moura Villa=E7a
> e-Mail: villaca@correionet.com.br
>
> Av. Moraes Sales, 987 Apto.113 fone: +55 19 232-2440
> 13010-001 - Campinas, SP fax: +55 19 232-2440
> Brasil
> -------------------------------------------------------------------------=
> ---
> ---
>
>
Look at the WORDPAD sample on the Visual's CD.
The basic idea is using atoms for passing a string when Sending the message to
the original instance.
HTH,
---
Kostya Sebov.
----------------------------------------------------------------------------
Tel: (38 044) 266-6387 | Fax: (38 044) 266-6195 | E-mail: sebov@is.kiev.ua
-----From: Ted
To the Moderator: If nobody else points it out, please point out to Ari=20
that this isn't perfect-the vagaries of multiple threads/processes=20
imply that a second process could be launched at the exact moment this=20
process is launched, which means that the code will be executing in=20
parallel, the FindWindow() call fails for each, and you now have two=20
instances running. (Mike will probably point this out, but if he=20
doesn't, then you can include this in the mini-digest.)
----------
From: Ari Villaca[SMTP:villaca@correionet.com.br]
Sent: Sunday, January 12, 1997 5:26 PM
To: MFC-L
Subject: One instance of an application only- How to post the creation=20
message from the 2nd to the 1st instance
Environment: MSVC 4.0/Win95
I have an application which is supposed to have only one instance=20
running.
I know how to activate the 1st instance of the application when a 2nd=20
one
is started by the user.
But the solution I have just do it, ie, activates the 1st instance. If=20
the
user has used Explorer to click on a saved application document name in
order to open that document, the document won't be opened.
How can I post the 2nd instance creation message arguments ( maybe the
CWinApp::m_lpCmdLine ) to the 1st instance already running?=20
The code for limiting the application to one instance is:
Bool CMyApp::InitInstance() {
....
....
....
SetRegistryKey( "MyCompanyName" );
csClassName =3D GetProfileString( "Initialization", "ClassName", "None" =
);
//determines if another window with our class name exists
HWND hWnd;
hWnd =3D ::FindWindow( ( LPCSTR ) csClassName, NULL );
if ( hWnd )
{
AfxMessageBox( "The application is already running..." );
SetForegroundWindow( hWnd );
CWnd* PrevWindow =3D CWnd::FromHandle( hWnd );
PrevWindow->SetForegroundWindow();
//does it have a popup?
CWnd* ChildWnd =3D PrevWindow->GetLastActivePopup();
//bring the main window to the top
PrevWindow->BringWindowToTop();
//if iconic, restore the main window
if ( PrevWindow->IsIconic() )
PrevWindow->ShowWindow( SW_RESTORE );
//if there are popups, bring them along too
if ( PrevWindow !=3D ChildWnd )
ChildWnd->BringWindowToTop();
//here seems to be the place to post the 2nd instance creation=20
arguments
(maybe m_lpCmdLine )
//to the 1st instance, so they are processed
//for now, send a File/New message
PrevWindow->PostMessage( WM_COMMAND, ID_FILE_NEW );
=09
//
return FALSE;
}
....
....
....
}
Any pointer to related documentation or solution is greatelly=20
appreciated.=20
Regards,
Ari
----------------------------------------------------------------------- =
-----
---
Ari de Moura Villa=E7a
e-Mail: villaca@correionet.com.br
Av. Moraes Sales, 987 Apto.113 fone: +55 19 232-2440
13010-001 - Campinas, SP fax: +55 19 232-2440
Brasil
----------------------------------------------------------------------- =
-----
---
Gforce -- gforce@liquidaudio.com
Wednesday, January 15, 1997
I have a similar problem, I have an app that should only have one
instance running at any time,
I send the command line to the first instance with no problems, Problem
arises if the
user double clicks on a file associated with my app and a second
instance tries to launch, sends
command line info (path and filename) to the first instances, the first
instances acts on the
command line but gets a sharing volation error attempting to open the
file...
Any ideas ?, thanks in advance.
t 23:25 1/12/97 -0200, Ari Villaca wrote:
>Environment: MSVC 4.0/Win95
>I have an application which is supposed to have only one instance
running.
>I know how to activate the 1st instance of the application when a 2nd
one
>is started by the user.
>But the solution I have just do it, ie, activates the 1st instance. If
the
>user has used Explorer to click on a saved application document name in
>order to open that document, the document won't be opened.
>How can I post the 2nd instance creation message arguments ( maybe the
>CWinApp::m_lpCmdLine ) to the 1st instance already running?
>Any pointer to related documentation or solution is greatelly
appreciated.
My book outlines a technique for doing this that doesn't involve
posting messages, which has several disadvantages. The idea is to
spawn a new thread that does nothing but sleep on the object which is
being used to make the application instances run exlusively. If the
object becomes signalled, the waiting thread can react by doing what
ever it wants to within the answering application.
If you need to pass data from one application to the other, you might do
so by using shared memory or named pipes.
Jim Lawson Williams -- jimlw@mail.ccur.com.au
Sunday, February 02, 1997
G'day!
I'm requesting reviews of a (well outside Microsoft guidelines) approach to=
=20
SharedMemory originally sparked by this problem:
At 11:25 PM 12/01/97 -0200, Ari Villaca wrote:
>Environment: MSVC 4.0/Win95
>
>I have an application which is supposed to have only one instance running.
>I know how to activate the 1st instance of the application when a 2nd one
>is started by the user.
>
>But the solution I have just do it, ie, activates the 1st instance. If the
>user has used Explorer to click on a saved application document name in
>order to open that document, the document won't be opened.
>
>How can I post the 2nd instance creation message arguments ( maybe the
>CWinApp::m_lpCmdLine ) to the 1st instance already running?=20
>
>The code for limiting the application to one instance is:
>
>Bool CMyApp::InitInstance() {
>....
>....
>....
> SetRegistryKey( "MyCompanyName" );
> csClassName =3D GetProfileString( "Initialization", "ClassName", "None" );
>
> //determines if another window with our class name exists
> HWND hWnd;
> hWnd =3D ::FindWindow( ( LPCSTR ) csClassName, NULL );
Marko K=F6nig and Mats Manhav=
=20
suggested FileMapping as the solution, and independent of ClassName. I=20
proposed a CSharedFile, which would do the job here because csClassName can=
=20
be resolved. However, as a general solution I like it less because it will=
=20
always require fiddling window ClassNames somehow-or-other -- either in=20
the "real" windows, or perhaps a "dummy" per Mike B.'s "Revolutionary=20
Guide..." Chapter 11.=20
Without a ClassName, no ::FindWindow(), no window-handle, no=20
GetWindowThreadProcessId, and ultimately, the CSharedFile can't be shared=20
without passing the requisite HGLOBAL. Sadly there's no direct correlation=
=20
between FileMapping and SharedFiles: stuff a string into m_strFileName, and=
=20
the SharedFile forgets about Memory and thinks "disk"!
Hence the experimental code following: treating a buffer associated with one
handle as if it belonged to another. I've ended up posting the lot so=20
there's less chance of misunderstanding what I've been doing.
Why all this trouble to pass one simple string? Anyone who thinks that this=
=20
is a sledge-hammer approach to nut-cracking is right. It's really a=20
test-bed for more complex exchanges between loosely-coupled processes. The=
=20
"navigation by programmer" required in FileMapping is just too clumsy for my=
=20
taste, but at the momemt CSharedFile is too limited.
Criticism, and suggestions on how not to walk the razor's edge, welcomed.
Regards,
Jim LW
/////////////////////////////////////////////////////////////////////////
// Fred is a bog-standard MDI project
CFredApp::CFredApp()
:m_oNoDupes("The One and Only Fred")
/*
where Fred.h includes
=09
CNoDupes m_oNoDupes;//ensures but 1 instance
*/
{
}
// CFredApp initialization
BOOL CFredApp::InitInstance()
{
// Standard initialization, changed between //>>>> and //<<<<
//
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//No new doc. here...
if (cmdInfo.m_nShellCommand =3D=3D CCommandLineInfo::FileNew)
cmdInfo.m_nShellCommand =3D CCommandLineInfo::FileNothing;
if (!m_oNoDupes.OkToProceed(&cmdInfo))
return FALSE;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Dispatch commands specified on the command line
}
BOOL CFredApp::PreTranslateMessage(MSG* pMsg)=20
{
if (pMsg->hwnd =3D=3D NULL)
{
//then it's not from around here:
if (m_oNoDupes.PreTranslateMessage(pMsg))
return TRUE;
}
return CWinApp::PreTranslateMessage(pMsg);
}
/////////////////////////////////////////////////////////////////////////
// NoDupes.h : header file, ex-Class Wizard, with thanks to CStatic
//
#include
#include "GlobalMemoryFile.h"
#define SHMEMSIZE 512
#define PARANOIA 0xdeadface
#define AVAILABLE 0xcafef00d
#define UNAVAILABLE 0xfeedbabe
/////////////////////////////////////////////////////////////////////////
// CNoDupes=20
class CNoDupes : public CObject
{
// Construction
public:
CNoDupes(LPCSTR WhoIsMe);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CNoDupes)
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CNoDupes();
protected:
BOOL m_bFileIsMine;//who created this?
CString m_sErrMsg; //dump this compound msg. on error
CString m_sFileName;
LPCSTR m_psMe; //from App, used to create mutex & m_aHash
UINT m_nNoDupesMsg; //our inter-process message
CMutex m_mutex; //to lock "if there's no m_aHash, create one"
ATOM m_aHash; //Global atom for this application
CGlobalMemoryFile m_sharedFile; //for the interchange
void HandOffCommand(CCommandLineInfo* pCmd);
//posts our bits to original copy =20
void DumpError(int nErr);
BOOL RestoreViewIfIconic(LPTSTR lpStr); //for doc.s
public:
BOOL OkToProceed(CCommandLineInfo* pCmd);
//called by App. to check m_aHash
};
/////////////////////////////////////////////////////////////////////////
// NoDupes.cpp : implementation file, derived from CStatic via //ClassWizard
//
// The first copy of the App. receives any file-name parameter passed.
//
#include "stdafx.h"
#include "NoDupes.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] =3D __FILE__;
#endif
////////////////////////////////////////////////////////////////////////////=
/
// CNoDupes
CNoDupes::CNoDupes(LPCSTR WhoIsMe)
:m_sErrMsg("Unable to establish 'Duplicate Applications' protocol:\n"),
m_psMe(WhoIsMe),
m_sFileName("Q:\\NoDupes.QQQ"),
m_mutex(FALSE,WhoIsMe),
m_sharedFile(/*nGrowBytes=3D*/ SHMEMSIZE),
m_bFileIsMine(FALSE)
{
}
CNoDupes::~CNoDupes()
{
if (m_bFileIsMine)
{
//clean up meticulously:
int result;
do=20
{
result =3D GlobalDeleteAtom(m_aHash);
}
while (result =3D=3D 0);
}
}
BOOL CNoDupes::OkToProceed(CCommandLineInfo* pCmd)
{
CString sNoDupes("S");//a single char. should suffice
//Set up our registered message --
//if we're the 1st, this is what we'll get,
//otherwise it's what we'll send
m_nNoDupesMsg =3D RegisterWindowMessage(m_psMe);
if (m_nNoDupesMsg =3D=3D 0)
{
m_sErrMsg+=3D"error in creation";
AfxMessageBox(m_sErrMsg);
return FALSE;
}
//Establish whether we're the 1st:
//
//Sadly, there seems to be no way to "test and set" in a single=20
//operation. Hence the need to lock the separate testing & =20
//setting.
//Gain control, then check whether or not we are to create --
//or to use -- the pseudo-disk:
m_mutex.Lock();
//the string to generate the atom must differ from the mutex
sNoDupes +=3D m_psMe;
m_aHash =3D GlobalFindAtom((LPCTSTR)sNoDupes);
if (m_aHash !=3D 0)
{
//then We Are Not Alone!
HandOffCommand(pCmd);//give 'em to the original
//relinquish control:
m_mutex.Unlock();
//as an exception may have set m_bFileIsMine for =
=20
//cleanup...
return FALSE;
}
//we are #1!
CWinThread* threadObj =3D AfxGetThread();
SetLastError(0);//perhaps overcautious
try
{
//Set up the "you are #2" test:
m_aHash =3D GlobalAddAtom(sNoDupes);
if (m_aHash =3D=3D 0)
{
//something is drastically wrong!
m_sErrMsg+=3D"results are contradictory";
AfxThrowUserException();
}
//do the equivalent of "Open, Create"
if =20
(!m_sharedFile.Allocate(m_sFileName,(DWORD)SHMEMSIZE))
{
m_sErrMsg+=3D"not enough memory";
AfxThrowUserException();
} =09
//now set up the data for any subsequent copies:
m_sharedFile.Write(&threadObj->m_nThreadID,sizeof(int));
int i; =09
i =3D PARANOIA;
m_sharedFile.Write(&i,sizeof(int));
i =3D AVAILABLE;
m_sharedFile.Write(&i,sizeof(int));
m_bFileIsMine =3D TRUE;
}
catch (CException* ex)
{
TCHAR szCause[255];
ex->GetErrorMessage(szCause, 255);
m_sErrMsg +=3D " -- ";
m_sErrMsg +=3D szCause;
AfxMessageBox(m_sErrMsg);
ex->Delete();
}
//relinquish control:
m_mutex.Unlock();
return m_bFileIsMine;
}
void CNoDupes::HandOffCommand(CCommandLineInfo* pCmd)
{
//NB: executed with m_mutex locked
int nErr;
//Prepare to store the file-id portion of the command in the =20
//memory file:
int lenFileName =3D pCmd->m_strFileName.GetLength();
try
{
if (!m_sharedFile.Assign(m_sFileName))
//an "Open, Existing"
{
nErr=3DGetLastError();
if (nErr !=3D ERROR_ALREADY_EXISTS)
//the file from the prime run =20
{
DumpError(nErr);
m_sErrMsg+=3D"problem with shared file";
AfxThrowUserException();
}
}
int firstThreadID, //where to post our "Look at the =
=20
//shared-file!" message
i; //integer I/O buffer
m_sharedFile.Read(&firstThreadID,sizeof(int));
//Do Paranoia checks:
m_sharedFile.Read(&i,sizeof(int));
if ( i !=3D PARANOIA)
{
m_sErrMsg+=3D"invalid shared file";
AfxThrowUserException();
}
m_sharedFile.Read(&i,sizeof(int));//flag field
if ( i !=3D AVAILABLE)
{
m_sErrMsg+=3D"prime copy stalled";
AfxThrowUserException();
}
//Reposition for output
m_sharedFile.Seek(sizeof(int)+sizeof(int),CFile::begin);
i=3DUNAVAILABLE;
m_sharedFile.Write(&i,sizeof(int));//flag field=20
//now the real data:
m_sharedFile.Write(&lenFileName,sizeof(int));
LPCSTR lpStr =3D =
=20
pCmd->m_strFileName.GetBuffer(lenFileName);
m_sharedFile.Write(lpStr,lenFileName);
pCmd->m_strFileName.ReleaseBuffer();
//post that message!
if (!PostThreadMessage( firstThreadID,m_nNoDupesMsg,
(WPARAM)AVAILABLE,0))
{
m_sErrMsg +=3D"can't post message";
AfxThrowUserException();
}
=09
//OK. If we got this far, the message has gone.
//Give the original copy 12 seconds to process it,
//checking every 300 millisec.s
BOOL bCleared=3DFALSE;
int j;
for (i=3D0;i<40;i++)
{
Sleep(300);
//Reposition for input =20
=
m_sharedFile.Seek(sizeof(int)+sizeof(int),CFile::begin);
m_sharedFile.Read(&j,sizeof(int));
//now check flag:
if (j=3D=3DAVAILABLE)
{
//flag has been set by the other end:
bCleared=3DTRUE;
break;
}
}
if (!bCleared)
{
m_sErrMsg +=3D"no response";
AfxThrowUserException();
}
}
catch (CFileException* ex)
{
ex->Delete();
}
catch (CException* ex)
{
TCHAR szCause[255];
ex->GetErrorMessage(szCause, 255);
m_sErrMsg +=3D " -- ";
m_sErrMsg +=3D szCause;
AfxMessageBox(m_sErrMsg);
ex->Delete();
m_bFileIsMine =3D TRUE; //set for unconditional clean-up
}
}
void CNoDupes::DumpError(int nErr)
{
LPVOID lpMsgBuf;
FormatMessage(=20
FORMAT_MESSAGE_ALLOCATE_BUFFER =20
| FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
nErr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),=20
// Default language
(LPTSTR) &lpMsgBuf,
0,
NULL);
m_sErrMsg+=3D(LPTSTR) lpMsgBuf;
AfxMessageBox(m_sErrMsg);
LocalFree(lpMsgBuf);
}
////////////////////////////////////////////////////////////////////////////=
/
// CNoDupes message handlers
BOOL CNoDupes::PreTranslateMessage(MSG* pMsg)=20
{
if (pMsg->message!=3Dm_nNoDupesMsg)
return FALSE;
//else we must act on it:
//Get the control data:
UINT nIDThread,
nParanoia,
nAvailability,
lenFileName;
CString strFileName;
try
{
m_sharedFile.Seek(0,CFile::begin);//necessary!
m_sharedFile.Read(&nIDThread, sizeof(int));
m_sharedFile.Read(&nParanoia, sizeof(int));
m_sharedFile.Read(&nAvailability, sizeof(int));
m_sharedFile.Read(&lenFileName, sizeof(int));
if ( nIDThread !=3D AfxGetThread()->m_nThreadID
||nParanoia !=3D PARANOIA
||nAvailability !=3D UNAVAILABLE)
{
m_sErrMsg+=3D"shared memory is corrupt";
AfxThrowUserException();
}
//Get to the foreground before we ask for a window for =20
//any new doc.
CWnd* mainWnd =3D AfxGetThread()->GetMainWnd();
if (mainWnd->IsIconic())
mainWnd->ShowWindow(SW_RESTORE);
else
{
mainWnd->SetForegroundWindow();
mainWnd->BringWindowToTop();
}
if (lenFileName !=3D 0)
{
LPTSTR lpStr =3D =20
=
strFileName.GetBufferSetLength(lenFileName);
m_sharedFile.Read(lpStr,lenFileName);
ASSERT(AfxIsValidString(lpStr));
TRACE("Opening %s\n",strFileName);
AfxGetApp()->OpenDocumentFile(strFileName); =09
//but if it's already resident, but Iconic, nowt =
=20
//will have happened..
BOOL bRestored =3D RestoreViewIfIconic(lpStr);
strFileName.ReleaseBuffer();
if (!bRestored)
{
m_sErrMsg+=3D"view-check failed";
AfxThrowUserException();
}
}
//OK, now signal success -- reposition & set flag: =
=20
m_sharedFile.Seek(sizeof(int)+sizeof(int),CFile::begin);
int i =3D AVAILABLE;
m_sharedFile.Write(&i,sizeof(int));
}
catch (CException* ex)
{
TCHAR szCause[255];
ex->GetErrorMessage(szCause, 255);
m_sErrMsg +=3D " -- ";
m_sErrMsg +=3D szCause;
AfxMessageBox(m_sErrMsg);
ex->Delete();
}
return TRUE;
}
BOOL CNoDupes::RestoreViewIfIconic(LPTSTR lpStr)
{
CDocument* pDoc;
CView* pView;
CFrameWnd* pFrame;=09
CWnd* pSomeWnd =3D AfxGetMainWnd();
ASSERT_VALID(pSomeWnd);
=09
pDoc =3D ((CFrameWnd*)pSomeWnd)->GetActiveDocument();
if (pDoc =3D=3D NULL)
{
//then hopefully this is an MDI application:
pFrame =3D ((CMDIFrameWnd*)pSomeWnd)->MDIGetActive();
pDoc =3D pFrame->GetActiveDocument();
if (pDoc =3D=3D NULL)
return FALSE;
}
CString strPath =3D pDoc->GetPathName();
if (lpStr !=3D strPath)
return FALSE;//VERRRRY strange...
=09
POSITION pos =3D pDoc->GetFirstViewPosition();
while (pos !=3D NULL)
{
pView =3D pDoc->GetNextView(pos);
pFrame =3D pView->GetParentFrame();
if (pFrame->IsIconic())
pFrame->ShowWindow(SW_RESTORE);
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////
// GlobalMemoryFile.h : header file
//
#include
#define PSEUDO_SECTOR 256
////////////////////////////////////////////////////////////////////////////=
/
// CGlobalMemoryFile
class CGlobalMemoryFile : public CSharedFile
{
DECLARE_DYNAMIC(CGlobalMemoryFile)
// Construction, stage 1:
public:
CGlobalMemoryFile(UINT nGrowBytes =3D PSEUDO_SECTOR,
UINT nAllocFlags=3D GMEM_DDESHARE | GMEM_MOVEABLE);
// Attributes
protected:
HGLOBAL m_hGlobalMemory;//GlobalAlloc(), GlobalFree()
UCHAR* m_lpGlobal; //store/restore CMemFile::m_lpBuffer
HANDLE m_hFileMap; //CreateFileMapping(),MapViewOfFile()
LPVOID m_lpMapAddress; //[Un]MapViewOfFile(),=20
//=3D=3D> CMemFile::m_lpBuffer
public:
// Operations
public:
// Construction, stage 2:
BOOL Allocate(LPCTSTR lpszFileName,
DWORD nBytes =3D PSEUDO_SECTOR);//~=3D Open,Create
BOOL Assign (LPCTSTR lpszFileName); //~=3D Open Existing
// Overrides
// Implementation
public:
virtual ~CGlobalMemoryFile();
};
////////////////////////////////////////////////////////////////////////////=
/
// GlobalMemoryFile.cpp : implementation file
//
#include "stdafx.h"
#include "GlobalMemoryFile.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] =3D __FILE__;
#endif
////////////////////////////////////////////////////////////////////////////=
/
// CGlobalMemoryFile
CGlobalMemoryFile::CGlobalMemoryFile(UINT nGrowBytes, UINT nAllocFlags)
:CSharedFile(nAllocFlags,nGrowBytes),
m_hGlobalMemory(NULL), //handle for CSharedFile
m_hFileMap(NULL), //handle for SDK File-mapping
m_lpMapAddress(NULL) //^working I/O buffer
{
}
CGlobalMemoryFile::~CGlobalMemoryFile()
{
if (m_lpMapAddress !=3D NULL)
UnmapViewOfFile(m_lpMapAddress);
if (m_hFileMap !=3D NULL)
CloseHandle(m_hFileMap);
if (m_hGlobalMemory !=3D NULL)
{
m_lpBuffer =3D m_lpGlobal;//put it back...
GlobalFree(m_hGlobalMemory);
}
}
BOOL CGlobalMemoryFile::Allocate(LPCTSTR lpszFileName,DWORD nBytes)
{
ASSERT(AfxIsValidString(lpszFileName));
int nErr;=09
=20
m_strFileName =3D lpszFileName;
m_hGlobalMemory =3D ::GlobalAlloc(m_nAllocFlags, nBytes);
if (m_hGlobalMemory =3D=3D NULL)
return FALSE;
m_lpGlobal =3D m_lpBuffer;//to put it back at dtor time...
SetHandle(m_hGlobalMemory, /*BOOL bAllowGrow=3D*/ TRUE);
m_hFileMap =3D CreateFileMapping(
(HANDLE) 0xFFFFFFFF, //the paging file
/*LPSECURITY_ATTRIBUTES =3D */ NULL,
PAGE_READWRITE,
0,GlobalSize(m_hGlobalMemory),
lpszFileName);
=09
nErr =3D GetLastError();
if (nErr =3D=3D 0)
{
m_lpMapAddress =3D MapViewOfFile(
m_hFileMap,
FILE_MAP_ALL_ACCESS, // Read/write permission=20
0, // Max. object size.=20
0, // Size of hFile.=20
0); // Map entire file.
nErr =3D GetLastError();
}
if (nErr =3D=3D 0)
{
m_lpBuffer =3D (UCHAR*)m_lpMapAddress;
return TRUE;
}
else
return FALSE;
}
BOOL CGlobalMemoryFile::Assign(LPCTSTR lpszFileName)
{
ASSERT(AfxIsValidString(lpszFileName));
m_strFileName =3D lpszFileName;
//m_hGlobalMemory =3D ::GlobalAlloc(m_nAllocFlags, nBytes);
m_hGlobalMemory =3D =20
::GlobalAlloc(m_nAllocFlags,(DWORD)PSEUDO_SECTOR);
if (m_hGlobalMemory =3D=3D NULL)
return FALSE;
m_lpGlobal =3D m_lpBuffer;
SetHandle(m_hGlobalMemory, /*BOOL bAllowGrow=3D*/ TRUE);
HANDLE hFileMap =3D OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
/*BOOL bInheritHandle =3D */FALSE,
lpszFileName);
int nErr =3D GetLastError();
if (nErr =3D=3D 0)
{
m_lpMapAddress =3D (UCHAR*)MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS, // Read/write permission=20
0, // Max. object size.=20
0, // Size of hFile.=20
0); // Map entire file.
nErr =3D GetLastError();
}
if (nErr =3D=3D 0)
{
m_lpBuffer =3D (UCHAR*)m_lpMapAddress;
return TRUE;
}
else
return FALSE;
}
IMPLEMENT_DYNAMIC(CGlobalMemoryFile, CSharedFile)
////////////////////////////////////////////////////////////////////////////
>From the BBC's "Barchester Chronicles":
"I know that ultimately we are not supposed to understand.
But I also know that we must try."
-- the Reverend Septimus Harding,=20
tax-consultant, crypt-analyst, clog-dancer, C++ programmer
Jim Lawson Williams -- jimlw@mail.ccur.com.au
Thursday, February 06, 1997
At 09:15 AM 3/02/97 -0500, you wrote:
>What we do is:
> - register a window class
> - override OnDDECommand in the App class
> - use CWnd::FindWindow to find a previous invocation
> - use DDE to send the file name to open (or any other
> sort of message) to the already running application.
>
>The OnDDECommand routine in the first invocation receives
>the string sent by the second invocation, and then you
>can process it any way you wish.
>
>What is outlined below seems needlessly complicated, unless
>I am missing something. Registering and using a window class
>is not a big deal - we register the class in InitInstance,
>and override CMainFrame::PreCreateWindow, where we set the
>window's class when the frame gets created.
>
>-charlie way
> cway@viatech-inc.com
>
You're absolutely right re a simple file-name. Let me repeat what I
posted earlier:
left
Why all this trouble to pass one simple string? Anyone
who thinks that this is a sledge-hammer approach to
nut-cracking is right. It's really a test-bed for more
complex exchanges between loosely-coupled processes. The
"navigation by programmer" required in FileMapping is
just too clumsy for my taste, but at the moment
CSharedFile is too limited.
Message-exchange is fine for simple protocols. As
Peter.Walker@ubs.com said,
a WM_COPYDATA could be made to do the job; to suit me, with the addition
of a
< to tie off loose ends. Note in particular the recommendation that
SendMessage() be used, not PostMessage(). If you ignore that, you
obviously risk modifying the data before the recipient has a chance to
use them. The sender is suspended until the receiver accepts the
message. Not a problem with shipping a file-name to a primary copy as in
the instance with which I elected to experiment. A serious one if the
sender is supposed to be responsive, and
cannot "go to sleep" for an unpredictable amount of time.
Of course response-time is ultimately limited by the reponsiveness of the
receiver/server, but in the system I'm concerned with all efforts needs
must be made to smooth out that response-time, recognizing differences
between "put" and "get" requests.
Your suggestion of CWinApp::OnDDECommand() would also open the nominated
document in this selected case, and could do a few more things besides.
Doing anything more complex via the SDK functions leaves me confused.
I've just re-read the stuff on XTYP_ADVDATA, XTYP_ADVREQ, XTYP_ADVSTART,
XTYP_ADVSTOP, XTYP_CONNECT, XTYP_CONNECT_CONFIRM,
XTYP_DISCONNECT,XTYP_ERROR, XTYP_EXECUTE, XTYP_MONITOR, XTYP_POKE,
XTYP_REGISTER, XTYP_REQUEST, XTYP_UNREGISTER, XTYP_WILDCONNECT,
XTYP_XACT_COMPLETE, and after re-studying those 16 functions I'm still
none the wiser in terms of what I would need to do to approximate
multiple queue-management in good old FORTRAN named COMMON, or
equivalent. Or, indeed, if I really could obtain totally asynchronous
execution via DDE.
Multi-threading is not the total answer since it assumes the "client"
must be integrated with the "server" in a single process. That is, any
new client cannot be developed as a separate project, and must be
integrated with the original source -- inflexible, and not what I seek.
For "futures" I need separate processes, with "run-time" access to the
particular server.
Forget about the file-name exchange in the test-bed. My only interest is
in the CSharedFile buffer-pointer-fiddling, or some viable alternatve.
If I can reliably manipulate a (comparartively large) area with "lock",
"read", and "write", then I can manage multiple complex queues of "things
to do".
Regards,
Jim LW
>From the BBC's "Barchester Chronicles":
"I know that ultimately we are not supposed to understand.
But I also know that we must try."
-- the Reverend Septimus Harding,
tax-consultant, crypt-analyst, clog-dancer, C++ programmer
| Вернуться в корень Архива
|