579 lines
26 KiB
C#
579 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Drawing;
|
|
using System.Xml;
|
|
using System.Windows.Forms;
|
|
using System.Text.RegularExpressions;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Runtime.Serialization;
|
|
using System.IO;
|
|
|
|
namespace EduNetworkBuilder
|
|
{
|
|
[Serializable]
|
|
public class NetTest
|
|
{
|
|
public string sHost = "";
|
|
public string dHost = "";
|
|
public Color WrongColor = Color.Red;
|
|
public NetTestType TheTest = NetTestType.NeedsDefaultGW;
|
|
public bool TaskWasDone = false;
|
|
public int PacketNumber = -1;
|
|
|
|
public NetTest(string srcHost, string dstHost, NetTestType tTest)
|
|
{
|
|
sHost = srcHost;
|
|
dHost = dstHost;
|
|
TheTest = tTest;
|
|
SetInitialDoneState();
|
|
}
|
|
|
|
public NetTest(NetTest FromWhat)
|
|
{
|
|
sHost = FromWhat.sHost;
|
|
dHost = FromWhat.dHost;
|
|
TheTest = FromWhat.TheTest;
|
|
SetInitialDoneState();
|
|
}
|
|
|
|
public NetTest(XmlNode theNode)
|
|
{
|
|
foreach (XmlNode Individual in theNode.ChildNodes)
|
|
{
|
|
XmlNodeType myNodetype = Individual.NodeType;
|
|
if (myNodetype == XmlNodeType.Element)
|
|
{
|
|
switch (Individual.Name.ToLower())
|
|
{
|
|
case "sourcehost":
|
|
case "shost":
|
|
sHost = Individual.InnerText;
|
|
break;
|
|
case "desthost":
|
|
case "dhost":
|
|
dHost = Individual.InnerText;
|
|
break;
|
|
case "thetest":
|
|
TheTest = NB.ParseEnum<NetTestType>(Individual.InnerText);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SetInitialDoneState();
|
|
}
|
|
|
|
public void SetInitialDoneState()
|
|
{
|
|
switch (TheTest)
|
|
{
|
|
case NetTestType.LockAll:
|
|
case NetTestType.LockDHCP:
|
|
case NetTestType.LockGateway:
|
|
case NetTestType.LockIP:
|
|
case NetTestType.LockNic:
|
|
case NetTestType.LockRoute:
|
|
case NetTestType.LockInterfaceVLAN:
|
|
case NetTestType.LockNicVLAN:
|
|
case NetTestType.LockVLANNames:
|
|
case NetTestType.LockVLANsOnHost:
|
|
TaskWasDone = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Save(XmlWriter writer)
|
|
{
|
|
writer.WriteStartElement("nettest");
|
|
writer.WriteElementString("shost", sHost);
|
|
writer.WriteElementString("dhost", dHost);
|
|
writer.WriteElementString("thetest", TheTest.ToString());
|
|
writer.WriteEndElement();
|
|
}
|
|
|
|
public void UpdateValuesFromAnother(NetTest WhatFrom)
|
|
{
|
|
dHost = WhatFrom.dHost;
|
|
sHost = WhatFrom.sHost;
|
|
TheTest = WhatFrom.TheTest;
|
|
WrongColor = WhatFrom.WrongColor;
|
|
}
|
|
|
|
public bool Equals(NetTest CompareTo)
|
|
{
|
|
if (sHost != CompareTo.sHost) return false;
|
|
if (dHost != CompareTo.dHost) return false;
|
|
if (TheTest != CompareTo.TheTest) return false;
|
|
if (WrongColor != CompareTo.WrongColor) return false;
|
|
return true;
|
|
}
|
|
|
|
public bool Edit()
|
|
{
|
|
NetTestEditor nte = new NetTestEditor(this);
|
|
NetTest copy = new NetTest(this);
|
|
nte.ShowDialog();
|
|
if (copy.Equals(this)) return false;
|
|
return true;
|
|
}
|
|
|
|
private string TestDescription(NetTestVerbosity amount)
|
|
{
|
|
string toreturn = "";
|
|
switch(amount)
|
|
{
|
|
case NetTestVerbosity.basic:
|
|
if(TheTest != NetTestType.ReadContextHelp)
|
|
toreturn = NB.Translate("NT_TstDscriptProblem");
|
|
else
|
|
toreturn = NB.Translate("_ReadContext");
|
|
break;
|
|
case NetTestVerbosity.hints:
|
|
switch (TheTest)
|
|
{
|
|
case NetTestType.NeedsDefaultGW:
|
|
toreturn = NB.Translate("NT_TstDscriptGteway");
|
|
break;
|
|
// case NetTestType.NeedsPingToHost:
|
|
// toreturn = "Cannot ping";
|
|
// break;
|
|
case NetTestType.NeedsRouteToNet:
|
|
toreturn = NB.Translate("NT_TstDscriptRout");
|
|
break;
|
|
case NetTestType.NeedsLocalIPTo:
|
|
toreturn = NB.Translate("NT_TstDscriptIP");
|
|
break;
|
|
case NetTestType.NeedsLinkToDevice:
|
|
toreturn = NB.Translate("NT_TstDiscriptConnect");
|
|
break;
|
|
case NetTestType.SuccessfullyArps:
|
|
toreturn = NB.Translate("NT_TstDiscriptARP");
|
|
break;
|
|
case NetTestType.SuccessfullyDHCPs:
|
|
toreturn = NB.Translate("NT_TstDiscriptDHCPIP");
|
|
break;
|
|
case NetTestType.SuccessfullyTraceroutes:
|
|
toreturn = NB.Translate("NT_TstDiscriptTraceroute");
|
|
break;
|
|
case NetTestType.SuccessfullyPings:
|
|
case NetTestType.SuccessfullyPingsAgain:
|
|
toreturn = NB.Translate("NT_TstDiscriptPing");
|
|
break;
|
|
case NetTestType.HelpRequest:
|
|
toreturn = NB.Translate("NT_TstDiscriptHelp");
|
|
break;
|
|
case NetTestType.FailedPing:
|
|
toreturn = NB.Translate("NT_TstDiscriptPingHost");
|
|
break;
|
|
case NetTestType.DHCPServerEnabled:
|
|
toreturn = NB.Translate("NT_TstDiscriptDHCP");
|
|
break;
|
|
case NetTestType.LockAll:
|
|
toreturn = NB.Translate("NT_TstDiscriptLock");
|
|
break;
|
|
case NetTestType.LockDHCP:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockDHCP");
|
|
break;
|
|
case NetTestType.LockIP:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockIP");
|
|
break;
|
|
case NetTestType.LockNic:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockNIC");
|
|
break;
|
|
case NetTestType.LockRoute:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockRout");
|
|
break;
|
|
case NetTestType.LockGateway:
|
|
toreturn = NB.Translate("NT_TstDiscriptGteway");
|
|
break;
|
|
case NetTestType.LockVLANNames:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockVLANNames");
|
|
break;
|
|
case NetTestType.LockVLANsOnHost:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockVLAN");
|
|
break;
|
|
case NetTestType.LockNicVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockNicVLAN");
|
|
break;
|
|
case NetTestType.LockInterfaceVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockInterfaceVLAN");
|
|
break;
|
|
case NetTestType.NeedsTaggedVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptNeedsTaggedVLAN");
|
|
break;
|
|
case NetTestType.NeedsUntaggedVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptNeedsUntaggedVLAN");
|
|
break;
|
|
case NetTestType.NeedsForbiddenVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptNeedsForbiddenVLAN");
|
|
break;
|
|
case NetTestType.ReadContextHelp:
|
|
toreturn = NB.Translate("_ReadContext");
|
|
break;
|
|
}
|
|
break;
|
|
case NetTestVerbosity.full:
|
|
switch (TheTest)
|
|
{
|
|
case NetTestType.NeedsDefaultGW:
|
|
toreturn = NB.Translate("NT_TstDiscriptGteway2");
|
|
break;
|
|
// case NetTestType.NeedsPingToHost:
|
|
// toreturn = "Cannot ping host:";
|
|
// break;
|
|
case NetTestType.NeedsRouteToNet:
|
|
toreturn = NB.Translate("NT_TstDiscriptRout2");
|
|
break;
|
|
case NetTestType.NeedsLocalIPTo:
|
|
toreturn = NB.Translate("NT_TstDiscriptIP2");
|
|
break;
|
|
case NetTestType.NeedsLinkToDevice:
|
|
toreturn = NB.Translate("NT_TstDiscriptLink");
|
|
break;
|
|
case NetTestType.SuccessfullyArps:
|
|
toreturn = NB.Translate("NT_TstDiscriptARP2");
|
|
break;
|
|
case NetTestType.SuccessfullyDHCPs:
|
|
toreturn = NB.Translate("NT_TstDiscriptDHCPIP2");
|
|
break;
|
|
case NetTestType.SuccessfullyTraceroutes:
|
|
toreturn = NB.Translate("NT_TstDiscriptTraceroute2");
|
|
break;
|
|
case NetTestType.SuccessfullyPings:
|
|
case NetTestType.SuccessfullyPingsAgain:
|
|
toreturn = NB.Translate("NT_TstDiscriptPing2");
|
|
break;
|
|
case NetTestType.HelpRequest:
|
|
toreturn = NB.Translate("NT_TstDiscriptHelp2");
|
|
break;
|
|
case NetTestType.FailedPing:
|
|
toreturn = NB.Translate("NT_TstDiscriptPingFail");
|
|
break;
|
|
case NetTestType.DHCPServerEnabled:
|
|
toreturn = NB.Translate("NT_TstDiscriptDHCP2");
|
|
break;
|
|
case NetTestType.LockAll:
|
|
toreturn = NB.Translate("NT_TstDiscriptLock") + ":";
|
|
break;
|
|
case NetTestType.LockDHCP:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockDHCP") + ":";
|
|
break;
|
|
case NetTestType.LockIP:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockIP") + ":";
|
|
break;
|
|
case NetTestType.LockNic:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockNIC") + ":";
|
|
break;
|
|
case NetTestType.LockRoute:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockRout") + ":";
|
|
break;
|
|
case NetTestType.LockGateway:
|
|
toreturn = NB.Translate("NT_TstDiscriptGteway") + ":";
|
|
break;
|
|
case NetTestType.LockVLANNames:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockVLANNames") + ":";
|
|
break;
|
|
case NetTestType.LockVLANsOnHost:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockVLAN") + ":";
|
|
break;
|
|
case NetTestType.LockNicVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockNicVLAN") + ":";
|
|
break;
|
|
case NetTestType.LockInterfaceVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptLockInterfaceVLAN") + ":";
|
|
break;
|
|
case NetTestType.NeedsTaggedVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptNeedsTaggedVLAN") + ":";
|
|
break;
|
|
case NetTestType.NeedsUntaggedVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptNeedsUntaggedVLAN") + ":";
|
|
break;
|
|
case NetTestType.NeedsForbiddenVLAN:
|
|
toreturn = NB.Translate("NT_TstDiscriptNeedsForbiddenVLAN") + ":";
|
|
break;
|
|
case NetTestType.ReadContextHelp:
|
|
toreturn = NB.Translate("_ReadContext");
|
|
break;
|
|
}
|
|
break;
|
|
case NetTestVerbosity.none:
|
|
toreturn = "";
|
|
break;
|
|
}
|
|
return toreturn;
|
|
}
|
|
|
|
public string GetDescription(NetTestVerbosity amount)
|
|
{
|
|
string toreturn = "";
|
|
if(TheTest == NetTestType.ReadContextHelp)
|
|
{
|
|
return TestDescription(amount) + " " + sHost;
|
|
}
|
|
switch(amount)
|
|
{
|
|
case NetTestVerbosity.basic:
|
|
toreturn = sHost + " " + TestDescription(amount);
|
|
break;
|
|
case NetTestVerbosity.hints:
|
|
toreturn = sHost + " " + TestDescription(amount);
|
|
break;
|
|
case NetTestVerbosity.full:
|
|
toreturn = sHost + " " + TestDescription(amount) + " " + dHost;
|
|
break;
|
|
case NetTestVerbosity.none:
|
|
toreturn = "";
|
|
break;
|
|
}
|
|
return toreturn;
|
|
}
|
|
|
|
|
|
public bool TestPiecesExist()
|
|
{
|
|
Network theNet = NB.GetNetwork();
|
|
NetworkDevice Source = theNet.GetDeviceFromName(sHost);
|
|
NetworkDevice Dest = theNet.GetDeviceFromName(dHost);
|
|
if (Source == null) return false;
|
|
if (TheTest == NetTestType.DHCPServerEnabled) return true; //dest does not matter
|
|
if (Dest == null) return false;
|
|
return true;
|
|
}
|
|
|
|
public bool ColorItemsIfNeeded(bool ChangeColor)
|
|
{
|
|
bool WasDone = TaskWasDone;
|
|
if(!TestComplete())
|
|
{
|
|
if(TheTest == NetTestType.ReadContextHelp && ChangeColor)
|
|
{
|
|
BuilderWindow myWin = NB.GetBuilderWin();
|
|
if(myWin != null)
|
|
{
|
|
Control ctl = myWin.GetControlNamed(sHost);
|
|
if (ctl == null) return false;
|
|
ctl.BackColor = WrongColor;
|
|
}
|
|
return true;
|
|
}
|
|
if (TheTest == NetTestType.HelpRequest && dHost == "?Button" && ChangeColor)
|
|
{
|
|
BuilderWindow myWin = NB.GetBuilderWin();
|
|
if (myWin != null)
|
|
{
|
|
Control ctl = myWin.GetControlNamed("btnHelp");
|
|
if (ctl == null) return false;
|
|
if(ctl.BackColor != WrongColor)
|
|
ctl.BackColor = WrongColor;
|
|
}
|
|
return true;
|
|
}
|
|
if (TheTest == NetTestType.HelpRequest && dHost == "ViewButton" && ChangeColor)
|
|
{
|
|
BuilderWindow myWin = NB.GetBuilderWin();
|
|
if (myWin != null)
|
|
{
|
|
Control ctl = myWin.GetControlNamed("cbViewTitles");
|
|
if (ctl == null) return false;
|
|
if (ctl.BackColor != WrongColor)
|
|
ctl.BackColor = WrongColor;
|
|
}
|
|
return true;
|
|
}
|
|
Network theNet = NB.GetNetwork();
|
|
NetworkDevice Source = theNet.GetDeviceFromName(sHost);
|
|
if(Source!= null)
|
|
{
|
|
if (ChangeColor)
|
|
{
|
|
Source.BackgroundColor = WrongColor;
|
|
Source.IsDirty = true; //Make sure we re-draw it
|
|
}
|
|
return true; //We have a test that is not completed
|
|
}
|
|
return true;
|
|
}
|
|
if(WasDone == false)
|
|
{
|
|
//We just solved it for the first time
|
|
TaskWasDone = true;
|
|
NB.PlaySound(NBSoundType.success);
|
|
NB.MarkToUpdate();
|
|
}
|
|
return false; //No need to color anything
|
|
}
|
|
|
|
public void SetDone(int PacketID = -1)
|
|
{
|
|
if(TaskWasDone == false)
|
|
{
|
|
if (TheTest == NetTestType.FailedPing || TheTest == NetTestType.SuccessfullyArps
|
|
|| TheTest == NetTestType.SuccessfullyDHCPs || TheTest == NetTestType.SuccessfullyPings
|
|
|| TheTest == NetTestType.SuccessfullyPingsAgain || TheTest == NetTestType.SuccessfullyTraceroutes)
|
|
PacketNumber = PacketID; //Track the packetID of the first packet to complete the task
|
|
NB.PlaySound(NBSoundType.success);
|
|
NB.MarkToUpdate();
|
|
}
|
|
TaskWasDone = true;
|
|
}
|
|
|
|
|
|
public string GetInterfaceFromVLANInterfaceRequirement()
|
|
{
|
|
Match result = Regex.Match(dHost, @"(?<interface>[A-z:0-9]+) - (?<id>\d+)");
|
|
if (result.Groups["interface"] != null) return result.Groups["interface"].Value;
|
|
return "";
|
|
}
|
|
|
|
public int GetIDFromVLANInterfaceRequirement()
|
|
{
|
|
int answer = -1;
|
|
Match result = Regex.Match(dHost, @"(?<interface>[A-z:0-9]+) - (?<id>\d+)");
|
|
if (result.Groups["id"] != null) int.TryParse( result.Groups["id"].Value, out answer);
|
|
return answer;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// See if the test has been solved
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool TestComplete()
|
|
{
|
|
Network theNet = NB.GetNetwork();
|
|
NetworkDevice Source = theNet.GetDeviceFromName(sHost);
|
|
NetworkDevice Dest = theNet.GetDeviceFromName(dHost);
|
|
IPAddress gw;
|
|
IPAddress tAddr;
|
|
|
|
switch(TheTest)
|
|
{
|
|
case NetTestType.NeedsDefaultGW:
|
|
if (Source == null) return false; //Unable to do it. Do not count it against them.
|
|
if (Dest == null) return false; //Unable to do it. Do not count it against them.
|
|
|
|
gw = Source.GetGateway();
|
|
if(Dest.HasIPAddress(gw))
|
|
{
|
|
//It has the IP. Is it local to the source?
|
|
if(Source.LocalNic(gw) != null)
|
|
{
|
|
//The default gw is set to the IP of the dest
|
|
//The IP address chosen is "local" to the source host. So it should ping between them
|
|
return true;
|
|
}
|
|
}
|
|
return false; //Something is not set right.
|
|
case NetTestType.NeedsRouteToNet:
|
|
if (Source == null) return false; //Unable to do it. Do not count it against them.
|
|
tAddr = theNet.DNSLookup(Source,dHost);
|
|
if (tAddr == null || tAddr.GetIPString == NB.ZeroIPString)
|
|
tAddr = new IPAddress(dHost);
|
|
if (Source.HasRouteMatching(tAddr))
|
|
{
|
|
IPAddress route = Source.RouteMatching(tAddr);
|
|
if (Source.LocalNic(new IPAddress(route.GetGateway.ToIpString())) == null)
|
|
return false; //The gateway specified is not local to the device. We cannot get to it
|
|
return true;
|
|
}
|
|
return false; //if we get here, it has failed somehow
|
|
case NetTestType.NeedsLocalIPTo:
|
|
if (Source == null) return false; //Unable to do it. Do not count it against them.
|
|
if (Dest == null) return false; //Unable to do it. Do not count it against them.
|
|
tAddr = Source.LocalDeviceIP(Dest);
|
|
IPAddress dAddress = Dest.LocalDeviceIP(Source);
|
|
if (Dest.HasIPAddress(tAddr)) return false; //They gave the same address to the source that the dest has.
|
|
if (!theNet.HasUniqueIP(tAddr, Source)) return false; //Verify we have not given the IP to someone else
|
|
if (tAddr != null &&
|
|
tAddr.GetIPString != NB.ZeroIPString)
|
|
{
|
|
if(dAddress != null & dAddress.GetMask == tAddr.GetMask)
|
|
return true;
|
|
}
|
|
return false; //Something is not set right.
|
|
case NetTestType.NeedsLinkToDevice:
|
|
if (Source == null) return false; //Unable to do it. Do not count it against them.
|
|
if (Dest == null) return false; //Unable to do it. Do not count it against them.
|
|
if (Source.HasLinkTo(dHost)) return true;
|
|
return false; //Something is not set right.
|
|
case NetTestType.SuccessfullyArps:
|
|
case NetTestType.SuccessfullyDHCPs:
|
|
case NetTestType.SuccessfullyPings:
|
|
case NetTestType.SuccessfullyPingsAgain:
|
|
case NetTestType.SuccessfullyTraceroutes:
|
|
case NetTestType.HelpRequest:
|
|
case NetTestType.ReadContextHelp:
|
|
case NetTestType.FailedPing:
|
|
return TaskWasDone; //This variable will tell us if these tests have been done.
|
|
case NetTestType.DHCPServerEnabled:
|
|
if (Source == null) return false; //Unable to do it. Do not count it against them.
|
|
//dHost is either "true" or "false"
|
|
string srvr = Source.GetIsDHCPServer().ToString().ToLower();
|
|
string shouldbe = dHost.ToLower();
|
|
if (srvr == shouldbe)
|
|
return true;
|
|
break;
|
|
case NetTestType.NeedsForbiddenVLAN:
|
|
case NetTestType.NeedsTaggedVLAN:
|
|
case NetTestType.NeedsUntaggedVLAN:
|
|
if (Source == null) return false; //Unable to do it. Do not count it against them.
|
|
string ifname = GetInterfaceFromVLANInterfaceRequirement();
|
|
if (ifname == "") return false; //unable to do it.
|
|
string nicname = Regex.Replace(ifname, @":\w*","");
|
|
int id = GetIDFromVLANInterfaceRequirement();
|
|
//Find the nic
|
|
NetworkCard nic = Source.NicFromName(nicname);
|
|
if (nic == null) return false; //no matching nic
|
|
//find the interface
|
|
NetworkInterface nif = nic.InterfaceFromName(ifname);
|
|
if (nif == null) return false; //no matchint interface
|
|
|
|
//find the entry for the vlan
|
|
VLANInfo vi = nif.GetVLANInfo(id);
|
|
if (vi == null) return false; //No such vlan or something.
|
|
if (TheTest == NetTestType.NeedsForbiddenVLAN && vi.Tag == VLANTagType.Forbidden) return true;
|
|
if (TheTest == NetTestType.NeedsUntaggedVLAN && vi.Tag == VLANTagType.Untagged) return true;
|
|
if (TheTest == NetTestType.NeedsTaggedVLAN && vi.Tag == VLANTagType.Tagged) return true;
|
|
break;
|
|
case NetTestType.LockAll:
|
|
case NetTestType.LockDHCP:
|
|
case NetTestType.LockIP:
|
|
case NetTestType.LockNic:
|
|
case NetTestType.LockRoute:
|
|
case NetTestType.LockGateway:
|
|
case NetTestType.LockInterfaceVLAN:
|
|
case NetTestType.LockNicVLAN:
|
|
case NetTestType.LockVLANsOnHost:
|
|
case NetTestType.LockVLANNames:
|
|
return true; //Nothing to solve. We just lock it so it cannot be changed.
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
public static T Clone<T>(T source)
|
|
{
|
|
if (!typeof(T).IsSerializable)
|
|
{
|
|
throw new ArgumentException(NB.Translate("NC_CloneSerialzable"), NB.Translate("_source"));
|
|
}
|
|
|
|
// Don't serialize a null object, simply return the default for that object
|
|
if (Object.ReferenceEquals(source, null))
|
|
{
|
|
return default(T);
|
|
}
|
|
|
|
IFormatter formatter = new BinaryFormatter();
|
|
Stream stream = new MemoryStream();
|
|
using (stream)
|
|
{
|
|
formatter.Serialize(stream, source);
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
return (T)formatter.Deserialize(stream);
|
|
}
|
|
}
|
|
}
|
|
} |