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 WilliamsAt 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 Villacawrote: >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
| Вернуться в корень Архива |