577 lines
19 KiB
C#
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;
|
|
|
|
}
|
|
|
|
}
|
|
}
|