CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Feb 2002
    Posts
    3,788

    [RESOLVED]: Only one CDaoDatabase object.

    I have a CDaoDatabase that I open in InitInstance. I'm using this function to return the only CDaoDatabase object I'm using throughout the application.
    Code:
     CDaoDatabase * GetOpenDB(){return &m_DB;};
    My program will be used from a network and I need to ensure that one and only one user can get access to the database at one time, that is as long as one user has the application open, no other users will get access to it.

    Ideas?

    Thanks!
    Last edited by Alin; March 26th, 2007 at 12:03 PM. Reason: resolved

  2. #2
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,397

    Re: Only one CDaoDatabase object.

    Try to Open your database for exclusive (nonshared) access (with the second parameter of CDaoDatabase::Open set to TRUE).

  3. #3
    Join Date
    Aug 2002
    Location
    Cluj-Napoca,Romania
    Posts
    3,496

    Re: Only one CDaoDatabase object.

    One way would be to use a table in that database or in a different one where to keep a flag marking if the database is in use or not. An app would check/change that flag before working with the DB. There is one nasty scenario with this solution: the machine currently locking the database looses its network connection ( or the app crashes).

    A better solution would be to have a helper service running on the machine hosting the DB (or any other machine in that network for that matter) Any client app would connect to it ( using TCP/IP or perhaps COM) and that service in turn would "ping" the client app currently holding the DB lock. If the client does not reply in a given amount of time, the DB lock will be released.

    Hope it helps.

    Edit: not familiar with DAO and the MFC implementation, but if it works, Victor's idea is painfully simple.
    Har Har

  4. #4
    Join Date
    Feb 2002
    Posts
    3,788

    Re: Only one CDaoDatabase object.

    Quote Originally Posted by VictorN
    Try to Open your database for exclusive (nonshared) access (with the second parameter of CDaoDatabase::Open set to TRUE).
    victor, i have this:
    Code:
    try
    {
    	m_DB.Open("vacation.mdb", TRUE);
    }
    
    catch(CDBException *e)
    {
    	AfxMessageBox(e->m_strError, MB_ICONEXCLAMATION);
        e->Delete();
    }
    catch(CDaoException *e)
    {
    	AfxMessageBox("The database is already in use. \
    				\nThe database cannot be upated by more than one user at a time. \
    				\nPlease notity the other users to close the application and try again. \
    				\n\nThe application will now exit.", MB_ICONERROR);
    	e->Delete();
    
    	return FALSE;
    }
    while this works well within the same instance of this application, it does not work when multiple instances of the application are opened from different computers.

  5. #5
    Join Date
    Feb 2002
    Posts
    3,788

    Re: Only one CDaoDatabase object.

    Padex, thanks for that, I'll think about it. The thing is though that I'd like to keep things as simple as possible, without involving COM or other technologies, if that's possible.

  6. #6
    Join Date
    Apr 2003
    Location
    kathmandu, nepal
    Posts
    1,570

    Re: Only one CDaoDatabase object.

    Maybe you can extend the CDaoDatabase class. It has it's Open member virtual and try to open the actual database file in share deny mode which should work for multiple applications I think.

    EDIT: I wonder why it doesn't work for mutiple instances of the app as suggested by Victor
    Last edited by miteshpandey; March 26th, 2007 at 10:56 AM.
    If there is no love sun won't shine

  7. #7
    Join Date
    Feb 2002
    Posts
    3,788

    Re: Only one CDaoDatabase object.

    i don't know wht it isn't working for multimple instances, maybe initinstance isn't the place to open the DB?

    this is my InitInstance.
    Code:
    BOOL CTestApp::InitInstance()
    {
    .....................
    	try
    	{
    		m_DB.Open("vacation.mdb", TRUE);
    	}
    	
    	catch(CDBException *e)
        {
    		AfxMessageBox(e->m_strError, MB_ICONEXCLAMATION);
            e->Delete();
        }
    	catch(CDaoException *e)
    	{
    		AfxMessageBox("The database is already in use. \
    					\nThe database cannot be upated by more than one user at a time. \
    					\nPlease notity the other users to close the application and try again. \
    					\n\nThe application will now exit.", MB_ICONERROR);
    		e->Delete();
    
    		return FALSE;
    	}
    
    	if (m_DB.IsOpen())
    	{
    		// Create mutex
    		m_hAlreadyOpened = ::CreateMutex(NULL, TRUE, "DatabaseMutex");
    
    		CString str = "";
    		DWORD dwErr = ::GetLastError();
    
    		switch(dwErr)
    		{
    			case ERROR_SUCCESS:
    				// I always get this message if I open the application from 2 different computers.
    				AfxMessageBox("No instance running.");
    			break;
    
    			case ERROR_ALREADY_EXISTS:
    				// I would expect this message will be displayed if a second user tries opening it.
    				// It never gets here when opened from 2 different computers, but it gets here
    				// if I try to open the application from the same computer twice.
    				str = "The database is already opened by another user.\nPlease notify the other user to close the application and try again.";
    				AfxMessageBox(str);
    			return FALSE;
    
    			default:
    			// Failed to create mutex by unknown reason
    			return FALSE;
    		}
    	}
    ...............
    	return TRUE;
    }

  8. #8
    Join Date
    Apr 2003
    Location
    kathmandu, nepal
    Posts
    1,570

    Re: Only one CDaoDatabase object.

    Maybe it's becuase you use different user accounts from the two computers. Not sure if Mutexes are able to prevent or not multiple instances for multiple user accounts.
    If there is no love sun won't shine

  9. #9
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,397

    Re: Only one CDaoDatabase object.

    Quote Originally Posted by Alin
    victor, i have this:
    Code:
    try
    {
    	m_DB.Open("vacation.mdb", TRUE);
    }
    ...
    while this works well within the same instance of this application, it does not work when multiple instances of the application are opened from different computers.
    In this case your database name is wrong (not for use in the network!)

    I had a similar problem in my recent project: I had to forbid simalteneous access on the same records of DB tables/queries. I have done it the following way:
    • each application instance needing to access some record first tries to create the file (in some predefined shared directory) with the name/extension depending on the record to access and some additional information about User and App instance written into this file;
    • the file is created with the read/write access for owner and FILE_SHARE_READ access for other (frankly, it is created in two steps: first - with only GENERIC_READ flag to be able to read the data from the file if it already exists, second - with GENERIC_READ | GENERIC_WRITE flags);
    • if the file already exists (some other App instance has created it and is currently working with the same record) - then sorry, record is locked by other - please, try later;
      note, that if the application created some file crashes the file stays existsing, but now any other user/application will be able to delete/overwrite it!

  10. #10
    Join Date
    Feb 2002
    Posts
    3,788

    Re: Only one CDaoDatabase object.

    thanks Victor, i'll try that.

  11. #11
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Post Re: Only one CDaoDatabase object.

    You can also use a named mutex with name starting with "Global\".

    See the SingleInstance.h file in this post for a small wrapper class that limits an app to a single instance.

    You just need to declare an instance in your app class, initialize in the ctor an perform the check in OnInitInstance:


    Code:
     
    
    CMyApp::CMyApp()
    : m_SingleInstance( _T("Global\\MyAppMutex") )
    {
    }
    BOOL CMyApp::InitInstance()
    {
      CWinApp::InitInstance();
     
      //
      // Verify this is the first instance of the program. 
      //
     
      if( S_OK != m_SingleInstance.CheckFirstInstance( ) )
      {
    	// TODO: Display other user error here
    	return FALSE;
      }
     
      /*
       * Other InitInstance code
       */
    }
    One advantage of a named mutex over using a file is that Windows will handle the bookkeeping for you. That is with a file, you'll need to manage whether the file existed from a previous instance and other code to handle failure cases, etc. With a mutex, windows handles the cleanup for you.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured