Introduction
I recently changed my home network, basically I added a server to connect the network to the Internet. Unfortunately where I live we can't get broadband Internet, so the Internet connection server needs to dial into our ISP. I needed a simple way to connect, disconnect and see how long the connection has been up. Normally I would use terminal services to establish and view the connection, but since my wife also uses the Internet I needed another simpler way, one that even my wife can use. I built this web application, that displays the current connection's statistics or shows the phonebook entries so that the user can connect.
Using the code
I've wrapped up some of the RAS API's so I could use them with P/Invoke. They are:
RasEnumConnections
RasGetConnectionStatistics
RasHangUp
RasEnumEntries
InternetDial
I also had to create some structures that these API's could use:
RASCONN
RasEntryName
RasStats
I created a simple class called RASDisplay
which has the following methods and properties:
Methods
int Connect(string Connection)
void Disconnect()
Properties
bool IsConnected
string ConnectionName
double BytesReceived
double BytesTransmitted
string[] Connections
string Duration
The RASDisplay
class takes care of all the complexity of using the RAS API. If you not familiar with using the RAS API, you have to pass in the structure sizes, so the API knows which version you are working with. The constructor for this class uses the following code to set the above properties:
private string m_duration;
private string m_ConnectionName;
private string[] m_ConnectionNames;
private double m_TX;
private double m_RX;
private bool m_connected;
private IntPtr m_ConnectedRasHandle;
public RASDisplay()
{
m_connected = true;
RAS lpras = new RAS();
RASCONN lprasConn = new RASCONN();
lprasConn.dwSize = Marshal.SizeOf(typeof(RASCONN));
lprasConn.hrasconn = IntPtr.Zero;
int lpcb = 0;
int lpcConnections = 0;
int nRet = 0;
lpcb = Marshal.SizeOf(typeof(RASCONN));
nRet = RAS.RasEnumConnections(ref lprasConn, ref lpcb,
ref lpcConnections);
if(nRet != 0)
{
m_connected = false;
return;
}
if(lpcConnections > 0)
{
RasStats stats = new RasStats();
m_ConnectedRasHandle = lprasConn.hrasconn;
RAS.RasGetConnectionStatistics(lprasConn.hrasconn, stats);
m_ConnectionName = lprasConn.szEntryName;
int Hours = 0;
int Minutes = 0;
int Seconds = 0;
Hours = ((stats.dwConnectDuration /1000) /3600);
Minutes = ((stats.dwConnectDuration /1000) /60) -
(Hours * 60);
Seconds = ((stats.dwConnectDuration /1000)) -
(Minutes * 60) - (Hours * 3600);
m_duration = Hours + " hours " + Minutes +
" minutes " + Seconds + " secs";
m_TX = stats.dwBytesXmited;
m_RX = stats.dwBytesRcved;
}
else
{
m_connected = false;
}
int lpNames = 1;
int entryNameSize=Marshal.SizeOf(typeof(RasEntryName));
int lpSize=lpNames*entryNameSize;
RasEntryName[] names=new RasEntryName[lpNames];
for(int i=0;i<names.Length;i++)
{
names[i].dwSize=entryNameSize;
}
uint retval = RAS.RasEnumEntries(null,null,names,
ref lpSize,out lpNames);
m_ConnectionNames = new string[lpNames];
if(lpNames>0)
{
for(int i=0;i<lpNames;i++)
{
m_ConnectionNames[i] = names[i].szEntryName;
}
}
}
The code to connect to the Internet uses the InternetDial WinInet API:
public int Connect(string sConnection)
{
int intConnection = 0;
uint INTERNET_AUTO_DIAL_UNATTENDED = 2;
int retVal = RAS.InternetDial(IntPtr.Zero, sConnection,
INTERNET_AUTO_DIAL_UNATTENDED,ref intConnection,0);
return retVal;
}
The code to disconnect is very simple as well:
public void Disconnect()
{
RAS.RasHangUp(m_ConnectedRasHandle);
}
The web application that uses the RASDisplay
class looks like:
protected System.Web.UI.WebControls.Label lblName;
protected System.Web.UI.WebControls.Label lblDuration;
protected System.Web.UI.WebControls.Label lblTransmitted;
protected System.Web.UI.WebControls.DataGrid dgAllConnections;
protected System.Web.UI.HtmlControls.HtmlTable tblCurrentConnection;
protected System.Web.UI.WebControls.Button btnDisconnect;
protected System.Web.UI.WebControls.Label lblRecieved;
private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
BindData();
}
}
private void BindData()
{
try
{
RASDisplay display = new RASDisplay();
lblName.Text = display.ConnectionName;
lblDuration.Text = display.Duration;
lblRecieved.Text = display.BytesReceived.ToString();
lblTransmitted.Text = display.BytesTransmitted.ToString();
if(!display.IsConnected)
{
tblCurrentConnection.Visible = false;
}
else
{
dgAllConnections.Visible = false;
}
dgAllConnections.DataSource = display.Connections;
dgAllConnections.DataBind();
}
catch(Exception e){
Response.Write(e.Message);
}
}
protected void btnConnect_Click(object sender, EventArgs e)
{
RASDisplay rasDisplay = new RASDisplay();
Button btnSender = (Button) sender;
DataGridItem ob = (DataGridItem) btnSender.Parent.Parent;
int ErrorVal = rasDisplay.Connect(ob.Cells[1].Text);
if(ErrorVal != 0)
{
Response.Write(ErrorVal);
}
else
{
Response.Redirect("Default.aspx");
}
}
protected void btnDisConnect_Click(object sender, EventArgs e)
{
RASDisplay rasDisplay = new RASDisplay();
rasDisplay.Disconnect();
Response.Redirect("Default.aspx");
}
The web application has a DataGrid
that displays all the connections on the machine, this DataGrid
only gets displayed if the computer isn't connected to the Internet. Otherwise a table with the connection's statistics gets displayed. The web page has a meta refresh tag, to keep the statistics up to date.
Know issues
Currently the Connect
method calls InternetDial, this method takes the name of the connection. If this connection doesn't have a password saved, the call will fail. In a future version I'd like to change the way the application connects to the Internet, maybe using RASDial
and have the application store the username and password for the user (or require them to enter it ? )
History
- Article Created: 30/06/2003
- Updated 7/7/2003 - Just a bug fix, if the user had more than one dialup internet phonebook, they might have some problems.