/********************************************************************************************* * Copyright 2004 - Volian Enterprises, Inc. All rights reserved. * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE * ------------------------------------------------------------------------------ * $Workfile: VEConn.cs $ $Revision: 3 $ * $Author: Kathy $ $Date: 1/31/05 11:04a $ * * $History: VEConn.cs $ * * ***************** Version 3 ***************** * User: Kathy Date: 1/31/05 Time: 11:04a * Updated in $/LibSource/VENetwork * Fix B2005-005 (connection & delete directory errors) * * ***************** Version 2 ***************** * User: Jsj Date: 11/12/04 Time: 10:34a * Updated in $/LibSource/VENetwork * Save user's Temp dir path * * ***************** Version 1 ***************** * User: Kathy Date: 7/27/04 Time: 8:44a * Created in $/LibSource/VENetwork *********************************************************************************************/ using System; using System.IO; using System.Collections; using System.Windows; using System.Windows.Forms; using System.ComponentModel; using System.Text; using Utils; using VlnStatus; namespace VENetwork { // The following enum is used to set/check the mode for which the dat file connection // was made (i.e. when the file was opened. // NWModes: // exclusive, hold, write public enum NWModes {XCLUDMODE=1, HOLDMODE=2, WRITMODE=3}; // This class manages the connection options for multi-user support, i.e. creation, // connecting to and removing dat files at the system, plant and procedure set levels. public class VEConnection { private VELock m_Lock; private string m_Path; private string CurDir; private long m_Position; private NWModes m_Mode; private FileStream datFs; private UserRunTime usrRunTime; const int ProcessRecLen = 140; private VETempFile TempFile; public VEConnection(VELock ilck, UserRunTime iusrRT) { TempFile = null; datFs = null; m_Mode = 0; m_Lock = ilck; usrRunTime = iusrRT; SetUserTempDirPath(); if (m_Lock.LockType == LockTypes.None) return; string tmp_path = ilck.Path; CurDir = tmp_path.Substring(0,tmp_path.LastIndexOf("\\")+1); // save directory // path is same as lock file except extension is dat not lck. m_Path = tmp_path.Substring(0,tmp_path.Length-3) + "dat"; } ~ VEConnection() { // update the record as inactive & close the file (this gets called on // exit of the program, rather than changing active data in the tree view). Exit(); } public int GetVfwMode() { //given the current mode, get the required filemode to send back to // vfw. The values were taken from Borland include file // define BASEMODE (O_NOINHERIT|O_NOCRITERR) // 0x80 0x2000 // define XCLUDMODE (BASEMODE|O_COMMIT|O_CREAT|O_DENYALL|O_RDWR) // 0x4000 0x0100 0x10 0x4 // define HOLDMODE (BASEMODE|O_DENYNONE|O_RDONLY) // 0x40 0x1 // define WRITMODE (BASEMODE|O_COMMIT|O_DENYWRITE|O_RDWR) // 0x4000 0x20 0x4 int BASEMODE = 0x80|0x2000; if(m_Mode==NWModes.HOLDMODE) return (BASEMODE|0x40|0x1); else if (m_Mode==NWModes.XCLUDMODE) return (BASEMODE|0x4000|0x0100|0x10|0x4); else if (m_Mode==NWModes.WRITMODE) return (BASEMODE|0x4000|0x20|0x4); else return (0); } // if file open is done from vfw, need to open file using that mode, // get mode from input. public NWModes GetModeFromVfwMode(int vfwMode) { int BASEMODE = 0x80|0x2000; if (vfwMode==(BASEMODE|0x40|0x1)) return NWModes.HOLDMODE; else if (vfwMode==(BASEMODE|0x4000|0x0100|0x10|0x4)) return NWModes.XCLUDMODE; else if (vfwMode==(BASEMODE|0x4000|0x20|0x4)) return NWModes.WRITMODE; return (NWModes) 0; } [Description("File offset")]public long FileOffset { get{return m_Position;} } // reads the dat file for this level, and creates a list of ACTIVE users, i.e. // those users whose record's status is set to active. public ArrayList GetUsers() { ArrayList retval = new ArrayList(); if (datFs!=null) { // read records in the .dat file and see if any are active. long howbig = datFs.Length; int howmany = (int)(howbig/ProcessRecLen) - 1; // the first record is a dummy for (int rec=1;rec<=howmany;rec++) { UserData usrd = new UserData(datFs, rec); if (usrd.UserStatus==(byte)Utils.UserCStatus.PRACTIVE) retval.Add(usrd); else usrd = null; } } return retval; } // display the monitor users dialog public void MonitorUsers(int lbstr) { ArrayList al = GetUsers(); // temporarily do separate dialog. eventually, this will be a pane with a list // on the main window MonUsrDlg dlg = new MonUsrDlg(al,lbstr); dlg.ShowDialog(); } // determines whether there are active users, other than myself, currently in this // level of data public bool HasActiveUsers() { int cntactive=0; // read records in the .dat file and see if any are active. long howbig = datFs.Length; int howmany = (int)(howbig/ProcessRecLen) - 1; // the first record is a dummy for (int rec=1;rec<=howmany;rec++) { UserData usrd = new UserData(datFs, rec); // if active record and it does not match my user name, there is another // user in. return true. if (usrd.UserStatus==(byte)Utils.UserCStatus.PRACTIVE) { cntactive++; if (usrd.UserName.ToUpper()!=usrRunTime.myUserData.UserName.ToUpper())return true; } } // if there is more than one active user that is current user, i.e. multiple // sessions, return true if (cntactive>1)return true; return false; } // This method will attempt to open the *process.dat file in a read/write mode so // that a process record can be added. private bool OpenRW() { bool retval=true; VlnSpinner spn=null; try { spn = new VlnSpinner(2,50,"- Opening Process File ",m_Path,true,true,false); datFs=null; while(spn.SpinnerWait(datFs!=null)) { datFs = new FileStream(m_Path,FileMode.Open,FileAccess.ReadWrite,FileShare.Read); } // take this chance to do some cleanup in the procedure set directory if (m_Lock.LockType == LockTypes.ProcSet) DoProcCleanup(); datFs.Seek(0L,SeekOrigin.End); m_Position = datFs.Position; usrRunTime.myUserData.UserStatus= (byte)Utils.UserCStatus.PRACTIVE; usrRunTime.myUserData.Write(datFs); m_Mode=NWModes.WRITMODE; datFs.Flush(); } catch (Exception e) { //MessageBox.Show(e.Message,"Error on connecting to data - no lock"); retval = false; } spn.Dispose(); return retval; } // used upon expand/select an item in tree - open the connection to the // the dat file. The reenter flag is used when the user is already in at this // level, but a change in lock state may have occurred. public bool Enter(bool reenter) { // for locktype.none, this level has no lock - so just say that // enter succeeded. if (m_Lock.LockType == LockTypes.None) return true; bool attchtemp = false; bool success=false; VlnSpinner spin = new VlnSpinner(2,10,"- Connecting to Data ",m_Path,true,true,false); while( spin.SpinnerWait( false ) ) // spin until break or return { // if not reenter refresh the lock. if reenter, the locking logic refreshed // the lock. if (!reenter)m_Lock.Refresh(); // if it's locked, see if it is locked by me and make sure there isn't an // active connection by another user or by myself from another window or network // process if (m_Lock.LockStatus == Status.Locked || m_Lock.LockStatus == Status.LockPending) { // if this is a procedure set lock, also check that the correct temp // file is attached, i.e. the one that is from the locking process. if(m_Lock.LockType==LockTypes.ProcSet) { if (TempFile==null)TempFile = new VETempFile(this.CurDir); attchtemp=TempFile.AttachSpecificTemp(usrRunTime.myUserData.UserNetworkID,m_Lock.UserProcess); //usrRunTime.myUserData.UserProcess); // if failed to connect, but a lock was pending, reset the lock type to // locked by other. if(m_Lock.LockStatus == Status.LockPending && !attchtemp) m_Lock.LockStatus = Status.LockedByOther; } if (m_Lock.LockType != LockTypes.ProcSet || attchtemp) { // try to get exclusive access to it (if I don't already have it) // first step is to exit from current connect if (m_Mode !=0 && m_Mode != NWModes.XCLUDMODE) Exit(); // try to establish a new exclusive connection. If a new exclusive connection // cannot be made, then a connection must exist from another process (either myself, // or another user if pending lock). // If I'm other user, this should be treated like a nolock for me, i.e. I can // view data but not modify it. if (m_Mode == 0) { try { datFs = new FileStream(m_Path,FileMode.Create,FileAccess.ReadWrite,FileShare.None); if(m_Lock.LockType == LockTypes.ProcSet) { // these files are only used at the procedure set level DeleteFiles("*.own",0); // Cleanup ownership files DeleteFiles("*.wrq",0); // Cleanup write request files } usrRunTime.myUserData.UserProcess=m_Lock.UserProcess; // truncate file datFs.SetLength(0L); m_Mode=NWModes.XCLUDMODE; // write out a dummy record with the status set to inactive. usrRunTime.myUserData.UserStatus= (byte)Utils.UserCStatus.PRINACTIVE; usrRunTime.myUserData.Write(datFs); m_Position = datFs.Position; usrRunTime.myUserData.UserStatus= (byte)Utils.UserCStatus.PRACTIVE; usrRunTime.myUserData.Write(datFs); success=true; m_Lock.LockStatus = Status.Locked; // in case it was pending datFs.Flush(); break; } // if an ioexception occurs, the connect is open either by me // or another user. Open a non-exlusive connection. catch (IOException) { // if open by another user, reopen as it was. Else return for // view only mode. spin.Dispose(); bool opn = OpenRW(); if (!opn) { m_Lock.LockStatus = Status.LockedByOther; return false; } datFs.Close(); datFs = new FileStream(m_Path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite); success=true; m_Mode=NWModes.HOLDMODE; break; } catch (Exception e) { spin.Dispose(); m_Lock.LockStatus = Status.LockedByOther; MessageBox.Show(e.Message, "Connecting to data failed - lock exists."); return false; } } else { m_Lock.LockStatus = Status.Locked; success=true; break; // already connected. } } else { success = false; break; } } // Handle case where there is no lock or the current user is the lock owner, // but they're already connected from another run (either windows session or // network session) if (m_Lock.LockStatus == Status.NoLock) { // check if already connected, i.e. a filestream exists, or that I'm not // changing modes (the mode change occurs when going from locked to unlocked) if (datFs != null && this.m_Mode!=NWModes.XCLUDMODE) { success=true; break; } else { // if at the procedure set level, need to connect to a temp directory // as well. usrRunTime.myUserData.UserProcess=""; if(m_Lock.LockType==LockTypes.ProcSet && TempFile==null) { TempFile = new VETempFile(this.CurDir); bool got_tmp = TempFile.FindAvailableTempDir(usrRunTime.myUserData.UserNetworkID); if (got_tmp==false) { TempFile = null; spin.Dispose(); return false; } usrRunTime.myUserData.UserProcess=TempFile.TempNum; } // try to get exclusive access to dat file (see following commentary) try { // if this was open with exclusive, exit (this would be the // case where an unlock occurred. if (reenter || m_Mode == NWModes.XCLUDMODE) Exit(); // if can't get exclusive, an exception is thrown. just continue // on from there, i.e. the file is being used. // if we get exclusive access, cleanup the procset directory (if // this is a procset level enter) and then clean out the dat file datFs = new FileStream(m_Path,FileMode.Create,FileAccess.ReadWrite,FileShare.None); if(m_Lock.LockType == LockTypes.ProcSet) { // these files are only used at the procedure set level DeleteFiles("*.own",0); // Cleanup ownership files DeleteFiles("*.wrq",0); // Cleanup write request files } // truncate file datFs.SetLength(0L); m_Mode=NWModes.XCLUDMODE; // write out a dummy record with the status set to inactive. usrRunTime.myUserData.UserStatus= (byte)Utils.UserCStatus.PRINACTIVE; usrRunTime.myUserData.Write(datFs); datFs.Close(); } // if it is an IOException, it means the user could not get exclusive access, // just continue on, opening with read/write access to add process record // and then reopen in hold mode. catch (IOException) { } // Catch a real error. catch (Exception e) { MessageBox.Show(e.Message, "connect->enter failed"); spin.Dispose(); return false; } // Attempt to open the file in Read/write mode to add an active process record bool opn = OpenRW(); if (!opn)return false; // now close the process file & open in hold mode. datFs.Close(); datFs = new FileStream(m_Path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite); success=true; m_Mode=NWModes.HOLDMODE; break; } } if (m_Lock.LockStatus == Status.LockedByOther) { success = false; if (datFs!=null)Exit(); break; } } spin.Dispose(); // end of spinnerwait while loop // if successful connection & at the procedure set level, write out number // of bytes to the temp directory file (temps\userid_.p##) to flag which // connection this is associated with. Also, if this is a reenter (locked state change) // then the tempfile is already the correct length, don't write out bytes. if (success==true && m_Lock.LockType==LockTypes.ProcSet && TempFile!=null && !reenter) { TempFile.WriteBytes((int)(m_Position/140)); } SetUserTempDirPath(); return success; } // if len is !0, try to delete files of the specified length. // zero length files are normally in transition and will be // deleted by their owners. public void DeleteFiles(string tmp, int len) { DirectoryInfo di = new DirectoryInfo(Directory.GetCurrentDirectory()); FileInfo [] fis = di.GetFiles(tmp); foreach (FileInfo fi in fis) { if (len==0 || len==fi.Length) fi.Delete(); } } // cleanup temp files and active record flags in the dat files. private void DoProcCleanup() { // read records in the .dat file and do check for those that are active. long howbig = datFs.Length; int howmany = (int)(howbig/ProcessRecLen); for (int rec=1;rec