577 lines
19 KiB
C#

/*********************************************************************************************
* 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<howmany;rec++)
{
UserData usrd = new UserData(datFs, rec);
if (usrd.UserStatus==(byte)Utils.UserCStatus.PRACTIVE)
{
VETempFile tfile = new VETempFile(CurDir);
string tfilename = CurDir + "\\" + tfile.MakeTempName(usrd.UserNetworkID,usrd.UserProcess,'P');
FileInfo fi = new FileInfo(tfilename);
bool tfile_exists = fi.Exists;
int len=0;
if (tfile_exists) len = (int) fi.Length;
// now check if it is an active process (versus an active
// record left in dat file by a crashed process).
// Do this by checking:
// if the record points to the active process file
// or the process file doesn't exist
// or it's length doesn't match the process number
if (usrd.UserStatus == (byte)Utils.UserCStatus.PRACTIVE)
{
if ((usrd.UserNetworkID.ToUpper()==usrRunTime.myUserData.UserNetworkID.ToUpper() &&
usrd.UserProcess==usrRunTime.myUserData.UserProcess) ||
!tfile_exists || len!=rec)
{
datFs.Seek(-ProcessRecLen,SeekOrigin.Current);
usrd.UserStatus = (byte)Utils.UserCStatus.PRINACTIVE;
usrd.Write(datFs);
DeleteFiles("*.own",rec);
DeleteFiles("*.wrq",rec);
}
}
}
}
}
public long Seek(long offset, int fromwhere)
{
long skpos = 0L;
if(datFs!=null)
{
// fromwhere: 0=beginning;1=current;2=end. Map these from 16-bit code
// to .net 32-bit code.
SeekOrigin sorig;
if (fromwhere==0)
sorig = SeekOrigin.Begin;
else if (fromwhere==1)
sorig = SeekOrigin.Current;
else
sorig = SeekOrigin.End;
skpos = datFs.Seek(offset, sorig);
}
return skpos;
}
public Int16 GetProcRecBuff(Int16 size, ref byte [] bt)
{
int retval = datFs.Read(bt,0,(int)size);
return (Int16)retval;
}
public void Close()
{
datFs.Close();
m_Mode = 0;
datFs=null;
}
public Int16 Open(Int16 vfwmode)
{
Int16 retval = 1;
try
{
if (m_Mode!=0) Close();
m_Mode = GetModeFromVfwMode(vfwmode);
if(m_Mode==NWModes.HOLDMODE)
datFs = new FileStream(m_Path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
else if(m_Mode==NWModes.WRITMODE)
datFs = new FileStream(m_Path,FileMode.Open,FileAccess.ReadWrite,FileShare.Read);
else if (m_Mode==NWModes.XCLUDMODE)
datFs = new FileStream(m_Path,FileMode.Create,FileAccess.ReadWrite,FileShare.None);
}
catch
{
retval = -1;
}
return retval;
}
// purpose: close the connection to the dat file.
public void Exit()
{
// if datFs is null, either we have a lock type of none or the destructor
// is being called & an exit may have been done, if collapse occurred
if (datFs==null) return; // datFs not set if lock type was none.
byte stat=(byte)Utils.UserCStatus.PRINACTIVE;
usrRunTime.myUserData.UserStatus=stat;
datFs.Close();
datFs=null;
VlnSpinner spin = new VlnSpinner(2,25,"- Exiting from Connection ",m_Path,true,true,false);
while(spin.SpinnerWait(datFs!=null))
{
datFs = new FileStream(m_Path,FileMode.Open,FileAccess.ReadWrite,FileShare.Read);
}
spin.Dispose();
datFs.Seek(m_Position,SeekOrigin.Begin);
BinaryWriter bw = new BinaryWriter(datFs);
bw.Write(stat);
bw.Close();
datFs.Close();
if (TempFile!=null)
{
TempFile.CloseTempProc();
TempFile = null;
}
datFs=null;
m_Mode=0;
}
public void SetUserTempDirPath()
{
if (m_Lock.LockStatus == Status.NoLock && TempFile != null)
{
// no lock set, use temp directory
char [] backslash = {'\\'};
usrRunTime.TempDirPath = TempFile.TemporaryDirectoryName.TrimEnd(backslash);
}
else // lock set, don't use temp directory
usrRunTime.TempDirPath = null;
}
}
}