How many users opening a BE .mdb?

FuzMic

DataBase Tinker
Local time
Today, 09:45
Joined
Sep 13, 2006
Messages
744
Good Day Guys

Need quicky codes to find out if i am the only FE user opening a linked BE .mdb. Much obliged!! :cool:
 
Last edited:
Create a access log table within the BackEnd DB to keep track of who and which computer has logged into the Database. Date and time would be a good idea as well.

It's now a simple matter of the FrontEnd to read the table so as to indicate whatever you like. It all depends upon what you are logging.

.
 
Thanks mate
What if i want check the BE .mdf, can i create a log too?
:)
 
MDF? or MDB? ... two completely separate things ...

If its and MDB and you just want to see who else is connected to the database ... I would suggest that you look at JETs UserRoster feature ...

http://support.microsoft.com/?id=198755

Which is the core of the utility I have attached to this post, which can also be found at
http://www.utteraccess.com/forums/showflat.php?Cat=&Board=48&Number=728080

You can also check out an other utility that is an LDB viewer in addition to a UserRoster viewer ...
http://www.mvps.org/access/modules/mdl0055.htm


Hope that helps ...
 

Attachments

Straight to the point!!

Much Appreciate Brent:). Your 'to the point' reply certainly enlighten this yokel tinker.

Just one 'tiny' (haha) question, what's the significance of 947bb.... in the following code.
Set rs = cn.OpenSchema(adSchemaProviderSpecific, _
, "{947bb102-5d43-11d1-bdbf-00c04fb92675}")

It is good to be aware that there is of the utility (MSLDBUSR.DLL) that enables us to programmatically retrieve relevant info from the .ldb file; got to tinker through this too.

Meanwhile i am going through your codes in the WhoIsConnected.mdb to move up my learning curve with your tips. Thanks again.


As to CyberLnx suggestion, after some tinkering, i read your suggestion as follow:
i have to create a table eg tblLog in the BE .mdb or BE .mdf and whenever a user conects to the BEs, entry is made to the tblLog. By checking on this tblLog, user will know how many users have logged in. Is this what you suggest? Cheers.
 
Last edited:
As to CyberLnx suggestion, after some tinkering, i read your suggestion as follow:
i have to create a table eg tblLog in the BE .mdb or BE .mdf and whenever a user conects to the BEs, entry is made to the tblLog. By checking on this tblLog, user will know how many users have logged in. Is this what you suggest? Cheers.

Basically, Yes. Let's say you have a Form which is either bound to or accesses a specific table or tables. When the Form is opened, code would check the log table in the BE database to see say three specific conditions (fields):

- A FormAccessed Field which would store the name of the Form currently opened in the Front End Database;

- A UserLogInDateTime Field which indicates the Date and Time a specific user accessed the BE for that specific Form in the FE;

- A UserLogOutDateTime Field which indicates the Date and Time a specific user Logged out of the BE for that specific Form in the FE. In other words, when the Form was closed.

Of couse, the Log record would contain several other Fields such as UserName, ComputerMACAddress, etc. but something like the three shown above should be enough to determine if it is OK or not to access the BE for that specific Form.

When a FE Form is first opened the BE Log Table is checked. The Table is searched to located the last record where the name contained within the FormAccessed Field matches the name of the Form just opened. If a last record is found and the contents of the FormAccessed field matches the name of the Form opened then the UserLogOutDateTime Field is checked in the very same record. If it's NULL then there is someone in the BE using the very same Form you are. A message is displayed indicating as such and the Form closes.

If it was found that the UserLogOutDateTime Field is not NULL then obviously a logout time was applied an no one is accessing the BE using the same Form you are. At this point you know it's OK to access the BE table(s) related to that Form and code now INSERTS as new record INTO the Log table filling in all the pertinent fields but specifically the FormAccess Field, the UserLogInDateTime Field, and the UserLogOutDateTime Field.

Code should placed into a Forms' OnOpen event that should Call a Function procedure which makes these checks. If the check fails then the Forms' On Open event CANCEL property is set to True and the Form ultimately closes denying access. If the Check proves to not fail then a new login Record is added. When the Form is Closed then the Log Record for the current Form is Updated. In other words, the Date and Time the Form is closed is placed into the UserLogOutDateTime field within the Log Table. Your Front-End Form's OnOpen event code may look like this:

Code:
Private Sub Form_Open(Cancel As Integer)
   Cancel = Not CheckBEtoForm(Me.name)
End Sub

Here you can see that the function we are calling to make the Log Table check is the CheckBEtoForm Function which will return a boolean result - True if no one is already using the Form and false if someone is using the form. Because the CheckBEtoForm Function returns TRUE is it is clear the use the Form, we don't want to apply that to the Cancel property....we want the oposite so we use the NOT statement to reverse the reuslt since if CANCEL = TRUE, the Form would close. Here is the CheckBEtoForm Function:

Code:
Public Function CheckBEtoForm(ByVal FrmName As String) As Boolean
   Dim Result As Long, StrgSQL As String
   Dim ComputerUserLoginName As String, ComputerName As String
   Dim MacAddress As String
   
   Result = Nz(DMax("[LogID]", "[LogTable]", "[FormAccessed]='" & FrmName & "' AND [UserLogOutDateTime] IS NULL"), 0)
   If Result > 0 Then
      MsgBox "The Back-End Database is already in use for this Form. Please try again later."
      CheckBEtoForm = False: Exit Function
   Else
      ComputerUserLoginName = VBA.Environ("USERNAME")
      ComputerName = VBA.Environ("COMPUTERNAME")
      MacAddress = GetMACAddress("-")
      StrgSQL = "INSERT INTO LogTable (FormAccessed,UserName,ComputerName," & _
                "MACAddress,UserLogInDateTime) VALUES ('" & FrmName & "','" & _
                ComputerUserLoginName & "','" & ComputerName & "','" & _
                MacAddress & "',#" & Now() & "#);"
      CurrentDb.Execute StrgSQL
      CheckBEtoForm = True
   End If
End Function

You would want to place this CheckBEtoForm Function into a Database Code Module - not a Form Code Module. You need to do it this way so that the Function is available to all Forms within the Front-End Database. Also notice how the Name of the Form is passed to the CheckBEtoForm Function within the Forms' OnOopen event where it's initially Called. This is required so that it knows what Form to look for within the Log Table.

If you look closely at the CheckBEtoForm Function code, you can see all the Log Table Fields in use. In each Log record there are seven (7) specific Fields (in the following order):

1) LogID (AutoNumber - Primary Key)
2) FormAccessed (Text DataType - 50)
3) UserName (Text DataType - 50)
4) ComputerName (Text DataType - 50)
5) MACAddress (Text DataType - 25)
6) UserLogInDateTime (Date/Time DataType)
7) UserLogOutDateTime (Date/Time DataType)

You'll notice that this Log Table also supports the storage of the MAC (Medium Access Control) Address of the current Network Card that is attached to the computer being Logged. So it stores the Form Name being opened, the Computers Logged In User Name, the Computer Name itself, and also the MAC address of the Network Card attached to that computer. To get the MAC address, we use another Function conveniently named GetMACAddress. The character passed to the GetMACAddress Function is the delimiter used to seperate the MAC Address members. The Minus (or dash) character is used here, you may want to use a period ot whatever. It's up to you.

The GetMACAddress Function is originally writen by "Randy Birch" and utilizes several Windows API functions and yet another function named MakeMacAddress. Here is the Code:

Code:
Option Explicit

'===========================================================
'This section of code was originally writen by: Randy Birch
'===========================================================

Private Const NCBASTAT As Long = &H33
Private Const NCBNAMSZ As Long = 16
Private Const HEAP_ZERO_MEMORY As Long = &H8
Private Const HEAP_GENERATE_EXCEPTIONS As Long = &H4
Private Const NCBRESET As Long = &H32

Private Type NET_CONTROL_BLOCK  'NCB
   ncb_command    As Byte
   ncb_retcode    As Byte
   ncb_lsn        As Byte
   ncb_num        As Byte
   ncb_buffer     As Long
   ncb_length     As Integer
   ncb_callname   As String * NCBNAMSZ
   ncb_name       As String * NCBNAMSZ
   ncb_rto        As Byte
   ncb_sto        As Byte
   ncb_post       As Long
   ncb_lana_num   As Byte
   ncb_cmd_cplt   As Byte
   ncb_reserve(9) As Byte 'Reserved, must be 0
   ncb_event      As Long
End Type

Private Type ADAPTER_STATUS
   adapter_address(5) As Byte
   rev_major         As Byte
   reserved0         As Byte
   adapter_type      As Byte
   rev_minor         As Byte
   duration          As Integer
   frmr_recv         As Integer
   frmr_xmit         As Integer
   iframe_recv_err   As Integer
   xmit_aborts       As Integer
   xmit_success      As Long
   recv_success      As Long
   iframe_xmit_err   As Integer
   recv_buff_unavail As Integer
   t1_timeouts       As Integer
   ti_timeouts       As Integer
   Reserved1         As Long
   free_ncbs         As Integer
   max_cfg_ncbs      As Integer
   max_ncbs          As Integer
   xmit_buf_unavail  As Integer
   max_dgram_size    As Integer
   pending_sess      As Integer
   max_cfg_sess      As Integer
   max_sess          As Integer
   max_sess_pkt_size As Integer
   name_count        As Integer
End Type
   
Private Type NAME_BUFFER
   name        As String * NCBNAMSZ
   name_num    As Integer
   name_flags  As Integer
End Type

Private Type ASTAT
   adapt          As ADAPTER_STATUS
   NameBuff(30)   As NAME_BUFFER
End Type

Private Declare Function Netbios Lib "netapi32" _
   (pncb As NET_CONTROL_BLOCK) As Byte
     
Private Declare Sub CopyMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
  (hpvDest As Any, ByVal _
   hpvSource As Long, ByVal _
   cbCopy As Long)
     
Private Declare Function GetProcessHeap Lib "kernel32" () As Long

Private Declare Function HeapAlloc Lib "kernel32" _
  (ByVal hHeap As Long, _
   ByVal dwFlags As Long, _
   ByVal dwBytes As Long) As Long
     
Private Declare Function HeapFree Lib "kernel32" _
  (ByVal hHeap As Long, _
   ByVal dwFlags As Long, _
   lpMem As Any) As Long

Public Function GetMACAddress(sDelimiter As String) As String
  'retrieve the MAC Address for the network controller
  'installed, returning a formatted string
   Dim tmp As String
   Dim pASTAT As Long
   Dim NCB As NET_CONTROL_BLOCK
   Dim AST As ASTAT
   Dim cnt As Long

  'The IBM NetBIOS 3.0 specifications defines four basic
  'NetBIOS environments under the NCBRESET command. Win32
  'follows the OS/2 Dynamic Link Routine (DLR) environment.
  'This means that the first NCB issued by an application
  'must be a NCBRESET, with the exception of NCBENUM.
  'The Windows NT implementation differs from the IBM
  'NetBIOS 3.0 specifications in the NCB_CALLNAME field.
   NCB.ncb_command = NCBRESET
   Call Netbios(NCB)
   
  'To get the Media Access Control (MAC) address for an
  'ethernet adapter programmatically, use the Netbios()
  'NCBASTAT command and provide a "*" as the name in the
  'NCB.ncb_CallName field (in a 16-chr string).
   NCB.ncb_callname = "*               "
   NCB.ncb_command = NCBASTAT
   
  'For machines with multiple network adapters you need to
  'enumerate the LANA numbers and perform the NCBASTAT
  'command on each. Even when you have a single network
  'adapter, it is a good idea to enumerate valid LANA numbers
  'first and perform the NCBASTAT on one of the valid LANA
  'numbers. It is considered bad programming to hardcode the
  'LANA number to 0 (see the comments section below).
   NCB.ncb_lana_num = 0
   NCB.ncb_length = Len(AST)
   
   pASTAT = HeapAlloc(GetProcessHeap(), _
                      HEAP_GENERATE_EXCEPTIONS Or _
                      HEAP_ZERO_MEMORY, _
                      NCB.ncb_length)
   If pASTAT <> 0 Then
      NCB.ncb_buffer = pASTAT
      Call Netbios(NCB)
      CopyMemory AST, NCB.ncb_buffer, Len(AST)
     'convert the byte array to a string
      GetMACAddress = MakeMacAddress(AST.adapt.adapter_address(), sDelimiter)
      HeapFree GetProcessHeap(), 0, pASTAT
    Else
      Debug.Print "memory allocation failed!"
      Exit Function
   End If
End Function


Private Function MakeMacAddress(b() As Byte, sDelim As String) As String
   Dim cnt As Long
   Dim buff As String
   
   On Local Error GoTo MakeMac_error
 
  'so far, MAC addresses are
  'exactly 6 segments in size (0-5)
   If UBound(b) = 5 Then
      'concatenate the first five values
     'together and separate with the
     'delimiter char
      For cnt = 0 To 4
         buff = buff & Right$("00" & Hex(b(cnt)), 2) & sDelim
      Next
      'and append the last value
      buff = buff & Right$("00" & Hex(b(5)), 2)
    End If  'UBound(b)
   MakeMacAddress = buff
   
MakeMac_exit:
   Exit Function
   
MakeMac_error:
   MakeMacAddress = "(error building MAC address)"
   Resume MakeMac_exit
End Function

You should place the above code into either the same Database Code Module that the CheckBEtoForm Function was placed or into a seperate Database Code Module (Not a Form Code Module).

Another step that needs to be done is applying the current Date and Time to the UserLogOutDateTime field within the Log Table when the Form is closed. To do this we create create yet another Public procedure. This will merely be a Sub-Procedure, we'll just call it FormLogOut and call it from the Front-End Forms' OnUnload event. Like this:

Code:
Private Sub Form_Unload(Cancel As Integer)
   Call FormLogOut
End Sub

Here is the FormLogOut Sub procdure. It should be placed into the same Database Code Module as the CheckBEtoForm Function and also be declared as public:

Code:
Public Sub FormLogOut(ByVal FrmName As String)
   Dim Result As Long, StrgSQL As String
   Dim ComputerUserLoginName As String, ComputerName As String
   Dim MacAddress As String
   
   ComputerUserLoginName = VBA.Environ("USERNAME")
   ComputerName = VBA.Environ("COMPUTERNAME")
   MacAddress = GetMACAddress("-")
   
   Result = DLookup("[LogID]", "[LogTable]", "[FormAccessed]='" & FrmName & _
                 "' AND [UserName]='" & ComputerUserLoginName & _
                 "' AND [ComputerName]='" & ComputerName & "' AND [MACAddress]='" & _
                 MacAddress & "' AND [UserLogOutDateTime] IS NULL")
   
   StrgSQL = "UPDATE [LogTable] SET [UserLogOutDateTime]=#" & Now() & _
             "# WHERE [LogID]=" & Result & ";"
                
   CurrentDb.Execute StrgSQL
End Sub

In the examples above we've used the name of a Form in the Log Table. This should really be extended a little further and also include BE Table names in use by that Form since more than one Form can utilize the same Table or Tables. The Table names in use should be the items actually checked. You have enough now I think to implement that yourself. More Error Trapping should also be implemented

Hope this helps somewhat.....for someone.

.
 
Great Effort & InDepth

Much appreciate CyberLynx for the time & effort, i will certainly dwell on your suggestion to leap frog.

If i may indulge further, an alternative approach is try to lock or open the BE .mdb EXCLUSIVELY so that i can do some major processing on it.

I was trying with
Set db = DBEngine(0).OpenDatabase("\\path\BE_db.mdb", True).

Err#1. If the BE_db.mdb is already shared opened by a user, a error will occur with the codes above; thus indicating that it is in use.

However, i face a hurdle like this:
If i have opened a form in the FE.mdb which is bounded to a linked table from the BE_db.mdb, the above "set db=.." will not show an error similar to line Err#1 above.

Why is that so? Is there another view of open the BE_db.mdb exclusively? Just trying to self taught.
 
Hello FuzMic ...

The number sequence in

>> Set rs = cn.OpenSchema(adSchemaProviderSpecific, _
, "{947bb102-5d43-11d1-bdbf-00c04fb92675}") <<

Is described this way in the ADO help ...

The GUID for a provider-schema query not defined by the OLE DB specification. This parameter is required if QueryType is set to adSchemaProviderSpecific; otherwise, it is not used.

Yeah ... it helped me a lot too! ... Anyway, OLE DB providers (Click Here for a summary of what OLE DB is) keep track of (or know how to extract) information (Schema) regarding the data that is being exposed by the provider. The OLE DB specifications provide a list of Schema information that *should* be accessible through the OLE DB provider. Many OLE DB providers have their OWN schema they want to make accessible. So the OLE DB provider has to basically name the schema that is being exposed, and that name is the GUID string you see. So the JET OLE DB provider can provide the "Standardized" schema information (ie: Tables, Relationships, etc), as well as is own custom schema information (the UserRoster). The user roster is identified with that huge GUID string ... so when ADO requests Schema information with .OpenSchema, it tells OLE DB that it whats the custom Schema information named "{947bb102-5d43-11d1-bdbf-00c04fb92675}" ... the OLE DB fetches that information and gives it back to ADO, so we can then use that info however we want to.

Does that help?
 
Simple Software Solutions

Here is a function I use to count the number of concurrent users

Code:
Function CountConnectionInUse()
'If first time install data path wil not exist in registry
If GetSysDataPath = False Then
    Exit Function
End If
'check to see if the target database exists.
If Dir(SysDataPath & "\icatsdata.MDB") = "" Then
    MsgBox "Cannot find the source database." & vbCrLf & vbCrLf & "Unable to connect to " & UCase(SysDataPath) & "\icatsdata.MDB. Please check that the file exists in the specified location. " & vbCrLf & vbCrLf & IIf(Left(SysDataPath, 1) <> "C", "Check network connection via My Computer.", "") & vbCrLf & vbCrLf & "Application will terminate once you click the Ok button.", vbCritical + vbOKOnly, "Cannot detect database"
    End ' teminate application
End If
    
Dim BumsOnSeats As Integer
Dim szMaxUsers As Integer
    szMaxUsers = 15
    
    Dim cn As ADODB.Connection
    Set cn = New ADODB.Connection
    ' Open the connection
    With cn
      .Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
         "Data Source=" & SysDataPath & "\icatsdata.mdb"
    End With
    'You have to take off 1 user as you are part of the count
    BumsOnSeats = CountUsers(cn) - 1
    Set cn = Nothing
    If BumsOnSeats >= szMaxUsers Then
        If szMaxUsers > 1 Then
            MsgBox "There are aready " & szMaxUsers & " users logged into the application." & vbCrLf & vbCrLf & "Please wait while another user logs out before attempting to log in again.", vbExclamation + vbOKOnly, "Maximum number of concurrent users reached."
        Else
            MsgBox "There is aready 1 user logged into the application." & vbCrLf & vbCrLf & "Please wait while this user logs out before attempting to log in again.", vbExclamation + vbOKOnly, "Maximum number of concurrent users reached."
        End If
        
        End ' teminate application
    End If
    
        
    
   
End Function



Code:
Function CountUsers(cn As ADODB.Connection)
    Dim rs          As ADODB.Recordset


    ' The user roster is exposed as a provider-specific schema rowset
    ' in the Jet 4.0 OLE DB provider.  You have to use a GUID to
    ' reference the schema, as provider-specific schemas are not
    ' listed in ADO's type library for schema rowsets

    Set rs = cn.OpenSchema(adSchemaProviderSpecific, _
    , "{947bb102-5d43-11d1-bdbf-00c04fb92675}")

    CountUsers = 0
    With rs
        Do Until .EOF
            CountUsers = CountUsers + 1
            .MoveNext
        Loop
    End With
     
    rs.Close
    Set rs = Nothing


End Function

I use this when the number of installations exceeds the number of license keys issued. So if the application is installed on 20 pcs and they have only bought 5 licenses then only 5 pcs can gain access to backend at the same time.


CodeMaster::cool:
 
Thanks mates
Currently out of town, will digest all the goodies in a few weeks time. Cheers.
 

Users who are viewing this thread

Back
Top Bottom