<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services - Revision history</title>
		<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services&amp;action=history</link>
		<description>Revision history for this page on the wiki</description>
		<language>en</language>
		<generator>MediaWiki 1.11.0</generator>
		<lastBuildDate>Wed, 22 May 2013 13:43:17 GMT</lastBuildDate>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services&amp;diff=9297&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:57, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:57:33 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services&amp;diff=9183&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:31, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:31:16 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services&amp;diff=9118&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:24, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:24:43 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services&amp;diff=8999&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Visual Basic 2005: A Developer's Notebook/TOC}}&lt;br /&gt;
In earlier chapters, you learned about the most profound changes in .NET 2.0, including new features in Windows Forms, ASP.NET web applications, and ADO.NET data access. These changes are impressive, but they're only part of the story. In fact, Microsoft developers have been hard at work tweaking and fine-tuning the ''entire'' .NET class library. If you look around, you'll find new members, types, and namespaces cropping up everywhere.&lt;br /&gt;
&lt;br /&gt;
== Easily Log Events ==&lt;br /&gt;
&lt;br /&gt;
When something goes wrong in your application, the user is rarely in a position to fix the problem. Instead of showing a detailed message box, it's much more important to make sure all the details are recorded somewhere permanent, so you can examine them later to try to diagnose the problem. In previous versions of .NET, logging was straightforward but tedious. In VB 2005, life becomes much easier thanks to the &amp;lt;tt&amp;gt;My.Application.Log&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
You can use the new &amp;lt;tt&amp;gt;My.Application.Log&amp;lt;/tt&amp;gt; object to quickly write to an XML file, an ordinary text file, or the Windows event log.&lt;br /&gt;
&lt;br /&gt;
To write a log message with &amp;lt;tt&amp;gt;My.Application.Log&amp;lt;/tt&amp;gt;, you simply need to use the &amp;lt;tt&amp;gt;WriteEntry( )&amp;lt;/tt&amp;gt; method. You supply a string message as the first parameter, and (optionally) two more parameters. The second parameter is the event type, which indicates whether the message represents information, a warning, an error, and so on. The third parameter is an exception object, the details of which will also be copied into the log entry.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''When something bad happens in your application, you want an easy way to log it to a file or event log. Look no further than the My.Application.Log object''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To try this out, create and run the console application in [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-1|Example 6-1]], which writes a short string of text to the log.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-1. Simple logging'''&lt;br /&gt;
&lt;br /&gt;
 Module LogTest&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )&lt;br /&gt;
       My.Application.Log.WriteEntry(&amp;quot;This is a test!&amp;quot;, _&lt;br /&gt;
         TraceEventType.Information)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Clearly, the logging code is extremely simple—but where are the log entries recorded? It all depends on the configuration of your application. .NET uses ''trace listeners'', which are dedicated classes that listen to log messages and then copy them to another location (such as a file, event log, and so on). When you call the &amp;lt;tt&amp;gt;WriteEntry( )&amp;lt;/tt&amp;gt; method, the entry is written to the current set of trace listeners (which are exposed through the &amp;lt;tt&amp;gt;My.Application.TraceSource&amp;lt;/tt&amp;gt; collection). By default, these listeners include the &amp;lt;tt&amp;gt;FileLogTraceListener&amp;lt;/tt&amp;gt;, which writes to a user logfile. This file is stored under a user-specific directory (which is defined by the user's APPDATA environment variable) in a subdirectory of the form ''[CompanyName]\[ProductName]\[FileVersion]'', where ''CompanyName'', ''ProductName'', and ''FileVersion'' refer to the information defined in the application assembly. For example, if the Windows user ''JoeM'' runs the application LogTestApp, the logfile will be created in a directory such as ''c:\Documents and Settings\JoeM\Application Data\MyCompany\LogTestApp\1.0.0.0\LogTestApp.log''.&lt;br /&gt;
&lt;br /&gt;
Once you've found the right directory, you can open the logfile in Notepad to examine the text contents. You'll see the following information:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''To configure assembly information, double-click the My Project item in the Solution Explorer, select the Application tab, and then click the Assembly Information button''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Microsoft.VisualBasic.MyServices.Log.WindowsFormsSource    Information&lt;br /&gt;
     0    This is a test!&lt;br /&gt;
&lt;br /&gt;
The number &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; represents the information log type. Subsequent entries append data to this logfile. Data is never removed (unless you delete the file by hand).&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...logging to other locations? .NET includes a number of pre-built trace listeners that you can use. They include:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;DefaultTraceListener&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This listener writes information into the debug portion of the window in Visual Studio. It's primarily useful while testing.&lt;br /&gt;
;&amp;lt;tt&amp;gt;FileLogTraceListener&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This listener writes information to the application logfile named ''[AssemblyName].log''. The default location of the logfile depends on the user's environment settings and the application information.&lt;br /&gt;
;&amp;lt;tt&amp;gt;EventLogTraceListener&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This listener writes information to the Windows event log.&lt;br /&gt;
;&amp;lt;tt&amp;gt;XmlWriterTraceListener&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This listener writes information to a file in XML format. You specify the location where the file should be stored. If needed, the directory will be created automatically.&lt;br /&gt;
&lt;br /&gt;
By default, every new Visual Basic application you create starts its life with two trace listeners: a &amp;lt;tt&amp;gt;DefaultTraceListener&amp;lt;/tt&amp;gt; and a &amp;lt;tt&amp;gt;FileLogTraceListener&amp;lt;/tt&amp;gt;. To add new trace listeners, you need to modify the application configuration file. In Visual Studio, you can double-click the ''app.config'' item in the Solution Explorer. Trace-listener information is specified in two subsections of the &amp;lt;tt&amp;gt;&amp;lt;system.diagnostics&amp;gt;&amp;lt;/tt&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
The logging configuration settings have changed with newer builds. For a version of the code that's updated to work with the latest build, download the samples from this book's web site.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;tt&amp;gt;&amp;lt;sharedListeners&amp;gt;&amp;lt;/tt&amp;gt; subsection, you define the trace listeners you want to have the option of using, specify any related configuration properties, and assign a descriptive name. Here's an example that defines a new listener for writing XML data to a logfile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Remember, after the application is built, the app.config file is renamed to have the name of the application, plus the extension .config''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;sharedListeners&amp;gt;&lt;br /&gt;
   &amp;lt;add name=&amp;quot;MyXmlLog&amp;quot; type=&amp;quot;System.Diagnostics.XmlWriterTraceListener&amp;quot;&lt;br /&gt;
     initializeData=&amp;quot;c:\MyLog.xml&amp;quot; /&amp;gt;&lt;br /&gt;
 &amp;lt;/sharedListeners&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;tt&amp;gt;&amp;lt;sources&amp;gt;&amp;lt;/tt&amp;gt; subsection, you name the trace listeners you want to use, choosing from the &amp;lt;tt&amp;gt;&amp;lt;sharedListeners&amp;gt;&amp;lt;/tt&amp;gt; list:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;source name=&amp;quot;Microsoft.VisualBasic.MyServices.Log.WindowsFormsSource&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;listeners&amp;gt;&lt;br /&gt;
     &amp;lt;add name=&amp;quot;Xml&amp;quot;/&amp;gt;&lt;br /&gt;
   &amp;lt;/listeners&amp;gt;&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This separation between the &amp;lt;tt&amp;gt;&amp;lt;sharedListeners&amp;gt;&amp;lt;/tt&amp;gt; section and the &amp;lt;tt&amp;gt;&amp;lt;sources&amp;gt;&amp;lt;/tt&amp;gt; section allows you to quickly switch trace listeners on and off, without disturbing their configuration settings.&lt;br /&gt;
&lt;br /&gt;
You can now re-run the application shown in [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-1|Example 6-1]]. Now it will write the message to an XML file named ''MyLog.xml'' in the root ''C:'' directory. Here's what the contents look like (with the schema information removed for better readability):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;E2ETraceEvent&amp;gt;&lt;br /&gt;
   &amp;lt;System&amp;gt;&lt;br /&gt;
     &amp;lt;EventID&amp;gt;0&amp;lt;/EventID&amp;gt;&lt;br /&gt;
     &amp;lt;Type&amp;gt;0&amp;lt;/Type&amp;gt;&lt;br /&gt;
     &amp;lt;TimeCreated SystemTime=&amp;quot;2004-07-26T16:14:04.7533392Z&amp;quot; /&amp;gt;&lt;br /&gt;
     &amp;lt;Source Name=&amp;quot;Microsoft.VisualBasic.MyServices.Log.WindowsFormsSource&amp;quot; /&amp;gt;&lt;br /&gt;
     &amp;lt;Execution ProcessName=&amp;quot;LogSample.vshost&amp;quot; ProcessID=&amp;quot;3896&amp;quot; ThreadID=&amp;quot;8&amp;quot; /&amp;gt;&lt;br /&gt;
     &amp;lt;Computer&amp;gt;FARIAMAT&amp;lt;/Computer&amp;gt;&lt;br /&gt;
   &amp;lt;/System&amp;gt;&lt;br /&gt;
   &amp;lt;ApplicationData&amp;gt;&lt;br /&gt;
     &amp;lt;System.Diagnostics&amp;gt;&lt;br /&gt;
       '''&amp;lt;Message&amp;gt;This is a test!&amp;lt;/Message&amp;gt;'''&lt;br /&gt;
                '''&amp;lt;Severity&amp;gt;Information&amp;lt;/Severity&amp;gt;'''&lt;br /&gt;
     &amp;lt;/System.Diagnostics&amp;gt;&lt;br /&gt;
   &amp;lt;/ApplicationData&amp;gt;&lt;br /&gt;
 &amp;lt;/E2ETraceEvent&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-2|Example 6-2]] shows a complete configuration file example. It enables file tracing, event log tracing, and XML log tracing. Notice that the &amp;lt;tt&amp;gt;EventLogTraceListener&amp;lt;/tt&amp;gt; is fine-tuned with a filter that ensures only error messages are logged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-2. Logging data to three different trace listeners'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;configuration&amp;gt;&lt;br /&gt;
   &amp;lt;system.diagnostics&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
     &amp;amp;lt;!-- Enable all three trace listeners&lt;br /&gt;
         (from the &amp;lt;sharedListeners&amp;gt; section). --&amp;gt;&lt;br /&gt;
     &amp;lt;sources&amp;gt;&lt;br /&gt;
       &amp;lt;source name=&amp;quot;Microsoft.VisualBasic.MyServices.Log.WindowsFormsSource&amp;quot;&lt;br /&gt;
        switchName=&amp;quot;DefaultSwitch&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;listeners&amp;gt;&lt;br /&gt;
           &amp;lt;add name=&amp;quot;FileLog&amp;quot;/&amp;gt;&lt;br /&gt;
           &amp;lt;add name=&amp;quot;EventLog&amp;quot;/&amp;gt;&lt;br /&gt;
           &amp;lt;add name=&amp;quot;Xml&amp;quot;/&amp;gt;&lt;br /&gt;
         &amp;lt;/listeners&amp;gt;&lt;br /&gt;
       &amp;lt;/source&amp;gt;&lt;br /&gt;
     &amp;lt;/sources&amp;gt;&lt;br /&gt;
     &amp;lt;switches&amp;gt;&lt;br /&gt;
       &amp;lt;add name=&amp;quot;DefaultSwitch&amp;quot; value=&amp;quot;Information&amp;quot; /&amp;gt;&lt;br /&gt;
     &amp;lt;/switches&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
     &amp;amp;lt;!-- Define three trace listeners that you might want to use. --&amp;gt;&lt;br /&gt;
     &amp;lt;sharedListeners&amp;gt;&lt;br /&gt;
       &amp;lt;add name=&amp;quot;FileLog&amp;quot; type=&amp;quot;System.Diagnostics.FileLogTraceListener&amp;quot;&lt;br /&gt;
        initializeData=&amp;quot;FileLogWriter&amp;quot; delimiter=&amp;quot;;&amp;quot; /&amp;gt;&lt;br /&gt;
       &amp;lt;add name=&amp;quot;EventLog&amp;quot; type=&amp;quot;System.Diagnostics.EventLogTraceListener&amp;quot;&lt;br /&gt;
        initializeData=&amp;quot;MyApplicationLog&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;filter type=&amp;quot;System.Diagnostics.SeverityFilter&amp;quot; initializeData=&amp;quot;Error&amp;quot; /&amp;gt;&lt;br /&gt;
       &amp;lt;/add&amp;gt;&lt;br /&gt;
       &amp;lt;add name=&amp;quot;Xml&amp;quot; type=&amp;quot;System.Diagnostics.XmlWriterTraceListener&amp;quot;&lt;br /&gt;
        initializeData=&amp;quot;c:\SampleLog.xml&amp;quot; delimiter=&amp;quot;;&amp;quot;/&amp;gt; &lt;br /&gt;
     &amp;lt;/sharedListeners&amp;gt;&lt;br /&gt;
   &amp;lt;/system.diagnostics&amp;gt;&lt;br /&gt;
 &amp;lt;/configuration&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can now use the same simple application to simultaneously write the ordinary logfile, an XML logfile, and an entry in the Windows event log named Application.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, there isn't any high-level .NET API for retrieving information from a log. If the log information is stored in a file, you can use the &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;StreamReader&amp;lt;/tt&amp;gt; classes from the &amp;lt;tt&amp;gt;System.IO&amp;lt;/tt&amp;gt; namespace to read the file one line at a time. If you've entered information in the Windows event log, you'll need to rely on the &amp;lt;tt&amp;gt;EventLog&amp;lt;/tt&amp;gt; class, which you can find in the &amp;lt;tt&amp;gt;System.Diagnostics&amp;lt;/tt&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The event log is a list of messages stored by the operating system for a specific period of time. To view the event log, choose Event Viewer from the Administrative Tools section of the Control Panel''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information, look up the following classes in the MSDN help: &amp;lt;tt&amp;gt;DefaultTraceListener&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;FileLogTraceListener&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;EventLogTraceListener&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;XmlWriterTraceListener&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ping Another Computer ==&lt;br /&gt;
&lt;br /&gt;
The Internet is a dynamic network where computers appear and drop out of sight without warning. One simple test an application can always perform to check if a computer is reachable is to send a ''ping'' message. Technically, a ping is the equivalent of asking another computer, &amp;quot;Are you there?&amp;quot; To get its answer, ping sends a special type of message over a low-level Internet protocol called ICMP (Internet Control Message Protocol).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to find out if a computer is reachable over the Internet? With the new Ping class, you can make this simple request without a tangle of low-level socket code''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sending a ping message using the classes found in the &amp;lt;tt&amp;gt;System.Net&amp;lt;/tt&amp;gt; namespaces is challenging and requires dozens of low-level code statements that deal with raw sockets. In .NET 2.0, there's a much simpler solution with the new &amp;lt;tt&amp;gt;Ping&amp;lt;/tt&amp;gt; class in the &amp;lt;tt&amp;gt;System.Net.NetworkInformation&amp;lt;/tt&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
To ping a computer, you use the &amp;lt;tt&amp;gt;Ping( )&amp;lt;/tt&amp;gt; method of the &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object. This approach gives you convenient access to the bare minimum ping functionality. The &amp;lt;tt&amp;gt;Ping( )&amp;lt;/tt&amp;gt; method returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt; depending on whether it received a response from the computer you're trying to contact.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Windows includes a utility called ping.exe that you can use to ping other computers at the command line''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-3|Example 6-3]] uses this method in order to contact the web server at ''www.yahoo.com''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-3. Pinging a remote computer'''&lt;br /&gt;
&lt;br /&gt;
 Module PingTest&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )&lt;br /&gt;
         Dim Success As Boolean&lt;br /&gt;
     &lt;br /&gt;
         ' Try to contact www.yahoo.com (wait 1000 milliseconds at most,&lt;br /&gt;
         ' which is the default if you don't specify a timeout).&lt;br /&gt;
         Success = My.Computer.Network.Ping(&amp;quot;www.yahoo.com&amp;quot;, 1000)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Did the computer respond? &amp;quot; &amp;amp; Success)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you call &amp;lt;tt&amp;gt;Ping( )&amp;lt;/tt&amp;gt;, you specify two parameters: the URL or IP address for the computer you're trying to reach (e.g., ''www.microsoft.com'' or 123.4.123.4) and, optionally, a maximum wait time in milliseconds. Once this limit is reached, the request times out, and the &amp;lt;tt&amp;gt;Ping( )&amp;lt;/tt&amp;gt; method returns &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt; to indicate the failure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
A ping message is a low-level test that doesn't necessarily correspond to the availability of services on a particular computer. For example, even if you can ping ''www.yahoo.com'', that doesn't mean that its search engine web pages are available and working properly. Similarly, web servers or firewalls often reject ping messages to restrict the possibility of someone launching a ''denial of service'' attack by flooding the computer with millions of spurious requests. For that reason, if you ping ''www.microsoft.com'', you won't receive a response, even though you can still surf to their web site using that address.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...getting more information from the remote computer? The &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object doesn't return any additional information about the results of the ping test. For example, you won't find out how long it took to receive a response, which is a key statistic used by some applications, such as peer-to-peer software, to rank the connection speed of different computers.&lt;br /&gt;
&lt;br /&gt;
To get more information, you need to head directly to the &amp;lt;tt&amp;gt;Ping&amp;lt;/tt&amp;gt; class in the &amp;lt;tt&amp;gt;System.Net.NetworkInformation&amp;lt;/tt&amp;gt; namespace. It returns a &amp;lt;tt&amp;gt;PingResult&amp;lt;/tt&amp;gt; object with several pieces of information, including the time taken for a response. The following code snippet puts this approach to the test. It assumes that you've imported the &amp;lt;tt&amp;gt;System.Net.NetworkInformation&amp;lt;/tt&amp;gt; namespace:&lt;br /&gt;
&lt;br /&gt;
 Dim Pinger As New Ping&lt;br /&gt;
 Dim Reply As PingReply = Pinger.Send(&amp;quot;www.yahoo.com&amp;quot;)&lt;br /&gt;
 Console.WriteLine(&amp;quot;Time (milliseconds): &amp;quot; &amp;amp; Reply.RoundTripTime)&lt;br /&gt;
 Console.WriteLine(&amp;quot;Exact status: &amp;quot; &amp;amp; Reply.Status.ToString( ))&lt;br /&gt;
 Console.WriteLine(&amp;quot;Adress contacted: &amp;quot; &amp;amp; Reply.Address.ToString( ))&lt;br /&gt;
&lt;br /&gt;
Here's some sample output:&lt;br /&gt;
&lt;br /&gt;
 Time (milliseconds): 61&lt;br /&gt;
 Exact status: Success&lt;br /&gt;
 Adress contacted: 216.109.118.78&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Ping&amp;lt;/tt&amp;gt; class also provides a &amp;lt;tt&amp;gt;SendAsync( )&amp;lt;/tt&amp;gt; method you can use to ping a computer without stalling your code (you can handle the response in another thread when a callback fires), and other overloaded versions of the &amp;lt;tt&amp;gt;Send( )&amp;lt;/tt&amp;gt; method that allow you to set low-level options (like the number of hops the ping message will travel before expiring).&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
To use this added networking muscle, read up on the &amp;lt;tt&amp;gt;Ping&amp;lt;/tt&amp;gt; class in the MSDN Help.&lt;br /&gt;
&lt;br /&gt;
== Get Information About a Network Connection ==&lt;br /&gt;
&lt;br /&gt;
Some applications need to adjust how they work based on whether a network connection is present. For example, imagine a sales reporting tool that runs on the laptop of a traveling sales manager. When the laptop is plugged into the network, the application needs to run in a ''connected mode'' in order to retrieve the information it needs, such as a list of products, directly from a database or web service. When the laptop is disconnected from the network, the application needs to gracefully degrade to a ''disconnected mode'' that disables certain features or falls back on slightly older data that's stored in a local file. To make the decision about which mode to use, an application needs a quick way to determine the network status of the current computer. Thanks to the new &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object, this task is easy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to find out if your computer's currently online? With the My class, this test is just a simple property away''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object provides a single &amp;lt;tt&amp;gt;IsAvailable&amp;lt;/tt&amp;gt; property that allows you to determine if the current computer has a network connection. The &amp;lt;tt&amp;gt;IsAvailable&amp;lt;/tt&amp;gt; property returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; as long as at least one of the configured network interfaces is connected, and it serves as a quick-and-dirty test to see if the computer is online. To try it out, enter the following code in a console application:&lt;br /&gt;
&lt;br /&gt;
 If My.Computer.Network.IsAvailable Then&lt;br /&gt;
     Console.WriteLine(&amp;quot;You have a network interface.&amp;quot;)&lt;br /&gt;
 End If&lt;br /&gt;
&lt;br /&gt;
If you want more information, you need to turn to the &amp;lt;tt&amp;gt;System.Net&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;System.Net.NetworkInformation&amp;lt;/tt&amp;gt; namespaces, which provide much more fine-grained detail. For example, to retrieve and display the IP address for the current computer, you can use the &amp;lt;tt&amp;gt;System.Net.Dns&amp;lt;/tt&amp;gt; class by entering this code:&lt;br /&gt;
&lt;br /&gt;
 ' Retrieve the computer name.&lt;br /&gt;
 Dim HostName As String = System.Net.Dns.GetHostName( )&lt;br /&gt;
 Console.WriteLine(&amp;quot;Host name: &amp;quot; &amp;amp; HostName)&lt;br /&gt;
     &lt;br /&gt;
 ' Get the IP address for this computer.&lt;br /&gt;
 ' Note that this code actually retrieves the first&lt;br /&gt;
 ' IP address in the list, because it assumes the&lt;br /&gt;
 ' computer only has one assigned IP address&lt;br /&gt;
 ' (which is the norm).&lt;br /&gt;
 Console.WriteLine(&amp;quot;IP: &amp;quot; &amp;amp; _&lt;br /&gt;
   System.Net.Dns.GetHostByName(HostName).AddressList(0).ToString( ))&lt;br /&gt;
&lt;br /&gt;
Here's the output you might see:&lt;br /&gt;
&lt;br /&gt;
 Host name: FARIAMAT&lt;br /&gt;
 IP: 192.168.0.197&lt;br /&gt;
&lt;br /&gt;
In addition, you can now retrieve even more detailed information about your network connection that wasn't available in previous versions of .NET. To do so, you need to use the new &amp;lt;tt&amp;gt;System.Net.NetworkInformation.IPGlobalProperties&amp;lt;/tt&amp;gt; class, which represents network activity on a standard IP network.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;IPGlobalProperties&amp;lt;/tt&amp;gt; class provides several methods that allow you to retrieve different objects, each of which provides statistics for a specific type of network activity. For example, if you're interested in all the traffic that flows over your network connection using TCP, you can call &amp;lt;tt&amp;gt;IPGlobalProperties.GetTcpIPv4Statistics()&amp;lt;/tt&amp;gt;. For most people, this is the most useful measurement of the network. On the other hand, if you're using a next-generation IPv6 network, you need to use &amp;lt;tt&amp;gt;IPGlobalProperties.GetTcpIPv6Statistics()&amp;lt;/tt&amp;gt; . Other methods exist for monitoring traffic that uses the UPD or ICMP protocols. Obviously, you'll need to know a little bit about networking to get the best out of these methods.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
IP (Internet Protocol) is the core building block of most networks and the Internet. It uniquely identifies computers with a four-part IP address, and allows you to send a basic packet from one machine to another (without any frills like error correction, flow control, or connection management). Many other networking protocols, such as TCP (Transmission Connection Protocol) are built on top of the IP infrastructure, and still other protocols are built on top of TCP (e.g., HTTP, the language of the Web). For more information about networking, refer to a solid introduction such as ''Internet Core Protocols'' (O'Reilly).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following code retrieves detailed statistics about the network traffic. It assumes that you've imported the &amp;lt;tt&amp;gt;System.Net.NetworkInformation&amp;lt;/tt&amp;gt; namespace:&lt;br /&gt;
&lt;br /&gt;
 Dim Properties As IPGlobalProperties = IPGlobalProperties.GetIPGlobalProperties( )&lt;br /&gt;
 Dim TcpStat As TcpStatistics&lt;br /&gt;
 TcpStat = Properties.GetTcpIPv4Statistics( )&lt;br /&gt;
     &lt;br /&gt;
 Console.WriteLine(&amp;quot;TCP/IPv4 Statistics:&amp;quot;)&lt;br /&gt;
 Console.WriteLine(&amp;quot;Minimum Transmission Timeout... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.MinimumTransmissionTimeOut)&lt;br /&gt;
 Console.WriteLine(&amp;quot;Maximum Transmission Timeout... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.MaximumTransmissionTimeOut)&lt;br /&gt;
     &lt;br /&gt;
 Console.WriteLine(&amp;quot;Connection Data:&amp;quot;)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Current  .................... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.CurrentConnections)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Cumulative .................. : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.CumulativeConnections)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Initiated ................... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.ConnectionsInitiated)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Accepted .................... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.ConnectionsAccepted)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Failed Attempts ............. : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.FailedConnectionAttempts)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Reset ....................... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.ResetConnections)&lt;br /&gt;
     &lt;br /&gt;
 Console.WriteLine( )&lt;br /&gt;
 Console.WriteLine(&amp;quot;Segment Data:&amp;quot;)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Received  ................... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.SegmentsReceived)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Sent ........................ : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.SegmentsSent)&lt;br /&gt;
 Console.WriteLine(&amp;quot;  Retransmitted ............... : &amp;quot; &amp;amp; _&lt;br /&gt;
   TcpStat.SegmentsResent)&lt;br /&gt;
&lt;br /&gt;
Here's the output you might see:&lt;br /&gt;
&lt;br /&gt;
 TCP/IPv4 Statistics:&lt;br /&gt;
 Minimum Transmission Timeout... : 300&lt;br /&gt;
 Maximum Transmission Timeout... : 120000&lt;br /&gt;
 Connection Data:&lt;br /&gt;
   Current  .................... : 6&lt;br /&gt;
   Cumulative .................. : 29&lt;br /&gt;
   Initiated ................... : 10822&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Statistics are kept from the time the connection is established. That means every time you disconnect or reboot your computer, you reset the networking statistics''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   Accepted .................... : 41&lt;br /&gt;
   Failed Attempts ............. : 187&lt;br /&gt;
   Reset ....................... : 2271&lt;br /&gt;
     &lt;br /&gt;
 Segment Data:&lt;br /&gt;
   Received  ................... : 334791&lt;br /&gt;
   Sent ........................ : 263171&lt;br /&gt;
   Retransmitted ............... : 617&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...other connection problems, like a disconnected router, erratic network, or a firewall that's blocking access to the location you need? The network connection statistics won't give you any information about the rest of the network (although you can try to ping a machine elsewhere on the network, as described in the previous lab, &amp;quot;Ping Another Computer&amp;quot;). In other words, even when a network connection is available there's no way to make sure it's working. For that reason, whenever you need to access a resource over the network—whether it's a web service, database, or application running on another computer—you need to wrap your call in proper exception-handling code.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information on advanced network statistics, look up the &amp;quot;IPGlobalProperties&amp;quot; index entry in the MSDN help, or look for the &amp;quot;network information sample&amp;quot; for a more sophisticated Windows Forms application that monitors network activity.&lt;br /&gt;
&lt;br /&gt;
== Upload and Download Files with FTP ==&lt;br /&gt;
&lt;br /&gt;
Earlier versions of .NET didn't include any tools for FTP (File Transfer Protocol), a common protocol used to transfer files to and from a web server. As a result, you either had to purchase a third-party component or write your own (which was easy in principle but difficult to get right in practice).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to upload files to an FTP site or download existing content? New support is available in VB 2005''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In .NET 2.0, a new &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; class neatly fills the gap. However, the &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; class has its own complexities, so Microsoft programmers simplified life for VB developers even further by extending the &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object to provide two quick access methods for completing basic FTP operations. These are &amp;lt;tt&amp;gt;UploadFile()&amp;lt;/tt&amp;gt;, which sends a file to a remote server, and &amp;lt;tt&amp;gt;DownloadFile( )&amp;lt;/tt&amp;gt;, which retrieves a file and stores it locally.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Whether you use the &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; class or the &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object, all FTP interaction in .NET is ''stateless''. That means that you connect to the FTP site, perform a single operation (like transferring a file or retrieving a directory listing), and then disconnect. If you need to perform another operation, you need to reconnect. Fortunately, this process of connecting and logging in is handled automatically by the .NET Framework.&lt;br /&gt;
&lt;br /&gt;
The easiest way to use FTP in a VB application is to do so through the &amp;lt;tt&amp;gt;My.Computer.Network&amp;lt;/tt&amp;gt; object. If you use its FTP methods, you never need to worry about the tedious details of opening, closing, and reading streams. To download a file, the bare minimum information you need is the URL that points to the FTP site and the path that points to the local file. Here's an example:&lt;br /&gt;
&lt;br /&gt;
 My.Computer.Network.DownloadFile( _&lt;br /&gt;
   &amp;quot;ftp://ftp.funet.fi/pub/gnu/prep/gtk.README&amp;quot;, &amp;quot;c:\readme.txt&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
This command retrieves the file that is on the FTP site ''ftp.funet.fi'' in the path ''/pub/gnu/prep/gtk.README'' and copies it to the local file ''c:\readme.txt''.&lt;br /&gt;
&lt;br /&gt;
Uploading uses similar parameters, but in reverse:&lt;br /&gt;
&lt;br /&gt;
 My.Computer.Network.UploadFile(&amp;quot;c:\newfile.txt&amp;quot;, _&lt;br /&gt;
   &amp;quot;ftp://ftp.funet.fi/pub/newfile.txt&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
This command copies the local file ''newfile.txt'' from the directory ''c:\'' to the FTP site ''ftp.funet.fi'', in the remote directory ''/pub''.&lt;br /&gt;
&lt;br /&gt;
Both &amp;lt;tt&amp;gt;DownloadFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;UploadFile( )&amp;lt;/tt&amp;gt; support several overloads that take additional parameters, including credentials (the username and password information you might need to log on to a server) and a timeout parameter to set the maximum amount of time you'll wait for a response before giving up (the default is 1,000 milliseconds).&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the &amp;lt;tt&amp;gt;DownloadFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;UploadFile( )&amp;lt;/tt&amp;gt; methods haven't been too robust in beta builds of Visual Basic 2005, and the methods may fail to work. An option that works better is the more sophisticated &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; class. Not only does it perform more reliably, but it also fills a few glaring gaps in the FTP support provided by the &amp;lt;tt&amp;gt;My.Network.Computer&amp;lt;/tt&amp;gt;. Because &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; allows you to execute any FTP command, you can use it to retrieve directory listings, get file information, and more.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Internet Explorer has its own built-in FTP browser. Just type a URL that points to an FTP site (like ftp://ftp.microsoft.com) into the IE address bar to browse what's there. You can use this tool to verify that your code is working correctly''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To use the &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; class, you need to follow several steps. First, pass the URL that points to the FTP site to the shared &amp;lt;tt&amp;gt;WebRequest.Create( )&amp;lt;/tt&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
 Dim Request As FtpWebRequest &lt;br /&gt;
 Request = CType(WebRequest.Create(&amp;quot;ftp://ftp.microsoft.com/MISC&amp;quot;), _&lt;br /&gt;
   FtpWebRequest)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;WebRequest.Create()&amp;lt;/tt&amp;gt; method examines the URL and returns the appropriate type of &amp;lt;tt&amp;gt;WebRequest&amp;lt;/tt&amp;gt; object. Because FTP URLs always start with the scheme ''ftp://'', the &amp;lt;tt&amp;gt;Create( )&amp;lt;/tt&amp;gt; method will return a new &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
Once you have the &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt;, you need to choose what FTP operation you want to perform by setting the &amp;lt;tt&amp;gt;FtpWebRequest.Method&amp;lt;/tt&amp;gt; property with the text of the FTP command. Here's an example for retrieving directory information with the &amp;lt;tt&amp;gt;LIST&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
 Request.Method = &amp;quot;LIST&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Once you've chosen the FTP operation you want to perform, the last step is to execute the command and read the response. The tricky part is the fact that the response is returned to you as a stream of text. It's up to you to move through this block of text line by line with a &amp;lt;tt&amp;gt;StreamReader&amp;lt;/tt&amp;gt; and parse the information.&lt;br /&gt;
&lt;br /&gt;
For example, the following code reads through a returned directory listing and displays each line in a Console window:&lt;br /&gt;
&lt;br /&gt;
 Dim Response As FtpWebResponse = CType(Request.GetResponse( ), FtpWebResponse)&lt;br /&gt;
 Dim ResponseStream As Stream = Response.GetResponseStream( )&lt;br /&gt;
 Dim Reader As New StreamReader(ResponseStream, System.Text.Encoding.UTF8)&lt;br /&gt;
     &lt;br /&gt;
 Dim Line As String&lt;br /&gt;
 Do&lt;br /&gt;
     Line = Reader.ReadLine( )&lt;br /&gt;
     Console.WriteLine(Line)&lt;br /&gt;
 Loop Until Line = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The output looks like this:&lt;br /&gt;
&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Jul  3  2002 beckyk&lt;br /&gt;
 -r-xr-xr-x   1 owner    group           15749 Apr  8  1994 CBCP.TXT&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Jul  3  2002 csformat&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Aug  1  2002 DAILYKB&lt;br /&gt;
 -r-xr-xr-x   1 owner    group             710 Apr 12  1993 DISCLAIM.TXT&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Jul  3  2002 FDC&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Jul  3  2002 friKB&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Jul  3  2002 FULLKB&lt;br /&gt;
 dr-xr-xr-x   1 owner    group               0 Jul  3  2002 Homenet&lt;br /&gt;
 -r-xr-xr-x   1 owner    group              97 Sep 28  1993 INDEX.TXT&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
Clearly, if you want to manipulate individual pieces of information (like the file size) or distinguish files from directories, you'll need to do extra work to parse the text returned by the &amp;lt;tt&amp;gt;StreamReader&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, when you're finished with the FTP request and response, you need to close the streams:&lt;br /&gt;
&lt;br /&gt;
 Reader.Close( )&lt;br /&gt;
 Response.Close( )&lt;br /&gt;
&lt;br /&gt;
To put it all in context, it helps to consider a simple FTP browsing application. [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-FIG-1|Figure 6-1]] shows a sample application that's included with the downloadable samples for this chapter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-1. A simple FTP Browser application'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_6_tt216.png|A simple FTP Browser application]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This Windows application includes the following controls:&lt;br /&gt;
&lt;br /&gt;
* A &amp;lt;tt&amp;gt;TextBox&amp;lt;/tt&amp;gt; where you can enter a URL that points to a file or directory in an FTP site.&lt;br /&gt;
* A &amp;lt;tt&amp;gt;Button&amp;lt;/tt&amp;gt; named Query Directory that retrieves the folders and files at a given URL. This task requires the &amp;lt;tt&amp;gt;FtpWebRequest&amp;lt;/tt&amp;gt; class.&lt;br /&gt;
* A &amp;lt;tt&amp;gt;Button&amp;lt;/tt&amp;gt; named Download File that downloads the file at a given URL. This task uses the &amp;lt;tt&amp;gt;My.Computer.Network.DownloadFile( )&amp;lt;/tt&amp;gt; method.&lt;br /&gt;
* A &amp;lt;tt&amp;gt;FolderBrowserDialog&amp;lt;/tt&amp;gt; that allows you to choose a folder where the downloaded file will be saved.&lt;br /&gt;
* A &amp;lt;tt&amp;gt;ListView&amp;lt;/tt&amp;gt; that shows the directory and file listing for the URL. This list is refreshed every time you click the Query Directory button. In addition, every time you click to select an item in the &amp;lt;tt&amp;gt;ListView&amp;lt;/tt&amp;gt;, that information is automatically added to the URL in the text box. This allows you to quickly browse through an FTP site, drilling down several layers into the directory structure and selecting the file that interests you.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-4|Example 6-4]] shows code for the FTP browser form&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-4. The FTP browser form'''&lt;br /&gt;
&lt;br /&gt;
 Public Class FtpForm&lt;br /&gt;
     Inherits System.Windows.Forms.Form&lt;br /&gt;
 &lt;br /&gt;
     ' Stores the path currently shown in the ListView.&lt;br /&gt;
     Private CurrentPath As String&lt;br /&gt;
 &lt;br /&gt;
     Private Sub cmdQuery_Click(ByVal sender As System.Object, _&lt;br /&gt;
       ByVal e As System.EventArgs) Handles cmdQuery.Click&lt;br /&gt;
         ' Check the URI is valid.&lt;br /&gt;
         Dim RequestUri As Uri = ValidateUri(txtFtpSite.Text)&lt;br /&gt;
         If RequestUri Is Nothing Then Return&lt;br /&gt;
 &lt;br /&gt;
         ' Clear the ListView.&lt;br /&gt;
         listDir.Items.Clear( )&lt;br /&gt;
 &lt;br /&gt;
         ' Create a new FTP request using the URI.&lt;br /&gt;
         Dim Request As FtpWebRequest&lt;br /&gt;
         Request = CType(WebRequest.Create(RequestUri), FtpWebRequest)&lt;br /&gt;
 &lt;br /&gt;
         ' Use this request for getting full directory details.&lt;br /&gt;
         Request.Method = &amp;quot;LIST&amp;quot; &lt;br /&gt;
         Request.UsePassive = False&lt;br /&gt;
 &lt;br /&gt;
         Dim Response As FtpWebResponse&lt;br /&gt;
         Dim ResponseStream As Stream&lt;br /&gt;
         Dim Reader As StreamReader&lt;br /&gt;
         Try&lt;br /&gt;
             ' Execute the command and get the response.&lt;br /&gt;
             Response = CType(Request.GetResponse( ), FtpWebResponse)&lt;br /&gt;
             Debug.WriteLine(&amp;quot;Status: &amp;quot; &amp;amp; Response.StatusDescription)&lt;br /&gt;
 &lt;br /&gt;
             ' Read the response one line at a time.&lt;br /&gt;
             ResponseStream = Response.GetResponseStream( )&lt;br /&gt;
             Reader = New StreamReader(ResponseStream, System.Text.Encoding.UTF8)&lt;br /&gt;
             Dim Line As String&lt;br /&gt;
             Do&lt;br /&gt;
                 Line = Reader.ReadLine( )&lt;br /&gt;
                 If Line &amp;lt;&amp;gt; &amp;quot;&amp;quot; Then&lt;br /&gt;
                     Debug.WriteLine(Line)&lt;br /&gt;
 &lt;br /&gt;
                     ' Extract just the file or directory name from the line.&lt;br /&gt;
                     Dim ListItem As New ListViewItem(Line.Substring(59).Trim( ))&lt;br /&gt;
                     If Line.Substring(0, 1) = &amp;quot;d&amp;quot; Then&lt;br /&gt;
                         ListItem.ImageKey = &amp;quot;Folder&amp;quot;&lt;br /&gt;
                     Else&lt;br /&gt;
                         ListItem.ImageKey = &amp;quot;File&amp;quot;&lt;br /&gt;
                     End If&lt;br /&gt;
                     listDir.Items.Add(ListItem)&lt;br /&gt;
                 End If&lt;br /&gt;
             Loop Until Line = &amp;quot;&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
             ' Operation completed successfully. Store the current FTP path.&lt;br /&gt;
             CurrentPath = RequestUri.ToString( )&lt;br /&gt;
 &lt;br /&gt;
         Catch Ex As Exception&lt;br /&gt;
             MessageBox.Show(Ex.Message)&lt;br /&gt;
 &lt;br /&gt;
         Finally&lt;br /&gt;
             ' Clean up.&lt;br /&gt;
             Reader.Close( )&lt;br /&gt;
             Response.Close( )&lt;br /&gt;
 &lt;br /&gt;
         End Try&lt;br /&gt;
     End Sub&lt;br /&gt;
 &lt;br /&gt;
     Private Sub cmdDownload_Click(ByVal sender As System.Object, _&lt;br /&gt;
       ByVal e As System.EventArgs) Handles cmdDownload.Click&lt;br /&gt;
 &lt;br /&gt;
         ' Check the URI is valid.&lt;br /&gt;
         Dim RequestUri As Uri = ValidateUri(txtFtpSite.Text)&lt;br /&gt;
         If RequestUri Is Nothing Then Return&lt;br /&gt;
 &lt;br /&gt;
         ' Prompt the user to choose a destination folder.&lt;br /&gt;
         ' Default the file name to the same file name used on the FTP server.&lt;br /&gt;
         dlgSave.FileName = Path.GetFileName(txtFtpSite.Text)&lt;br /&gt;
         If dlgSave.ShowDialog( ) &amp;lt;&amp;gt; Windows.Forms.DialogResult.OK Then&lt;br /&gt;
             Return&lt;br /&gt;
         End If&lt;br /&gt;
 &lt;br /&gt;
         ' Create a new FTP request using the URI.&lt;br /&gt;
         Dim Request As FtpWebRequest&lt;br /&gt;
         Request = CType(WebRequest.Create(RequestUri), FtpWebRequest)&lt;br /&gt;
 &lt;br /&gt;
         ' Use this request for downloading the file.&lt;br /&gt;
         Request.UsePassive = False&lt;br /&gt;
         Request.Method = &amp;quot;RETR&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
         Dim Response As FtpWebResponse&lt;br /&gt;
         Dim ResponseStream, TargetStream As Stream&lt;br /&gt;
         Dim Reader As StreamReader&lt;br /&gt;
         Dim Writer As StreamWriter&lt;br /&gt;
         Try&lt;br /&gt;
             ' Execute the command and get the response.&lt;br /&gt;
             Response = CType(Request.GetResponse( ), FtpWebResponse)&lt;br /&gt;
             Debug.WriteLine(&amp;quot;Status: &amp;quot; &amp;amp; Response.StatusDescription)&lt;br /&gt;
             Debug.WriteLine(&amp;quot;File Size: &amp;quot; &amp;amp; Response.ContentLength)&lt;br /&gt;
 &lt;br /&gt;
             ' Create the destination file.&lt;br /&gt;
             TargetStream = New FileStream(dlgSave.FileName, FileMode.Create)&lt;br /&gt;
             Writer = New StreamWriter(TargetStream)&lt;br /&gt;
 &lt;br /&gt;
             ' Write the response to the file.&lt;br /&gt;
             ResponseStream = Response.GetResponseStream( )&lt;br /&gt;
             Reader = New StreamReader(ResponseStream, System.Text.Encoding.UTF8)&lt;br /&gt;
             Writer.Write(Reader.ReadToEnd( ))&lt;br /&gt;
 &lt;br /&gt;
         Catch Err As Exception&lt;br /&gt;
             MessageBox.Show(Err.Message)&lt;br /&gt;
 &lt;br /&gt;
         Finally&lt;br /&gt;
             ' Clean up.&lt;br /&gt;
             Reader.Close( )&lt;br /&gt;
             Response.Close( )&lt;br /&gt;
             Writer.Close( )&lt;br /&gt;
         End Try&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     End Sub&lt;br /&gt;
 &lt;br /&gt;
     Private Function ValidateUri(ByVal uriText As String) As Uri&lt;br /&gt;
         Dim RequestUri As Uri&lt;br /&gt;
         Try&lt;br /&gt;
             ' Check that the string is interpretable as a URI.&lt;br /&gt;
             RequestUri = New Uri(uriText)&lt;br /&gt;
 &lt;br /&gt;
             ' Check that the URI starts with &amp;quot;ftp://&amp;quot;&lt;br /&gt;
             If RequestUri.Scheme &amp;lt;&amp;gt; Uri.UriSchemeFtp Then&lt;br /&gt;
                 RequestUri = Nothing&lt;br /&gt;
             End If&lt;br /&gt;
         Catch&lt;br /&gt;
             RequestUri = Nothing&lt;br /&gt;
         End Try&lt;br /&gt;
 &lt;br /&gt;
         If RequestUri Is Nothing Then&lt;br /&gt;
             MessageBox.Show(&amp;quot;Invalid Uri.&amp;quot;)&lt;br /&gt;
         Else&lt;br /&gt;
 &lt;br /&gt;
         End If&lt;br /&gt;
         Return RequestUri&lt;br /&gt;
     End Function&lt;br /&gt;
 &lt;br /&gt;
     Private Sub listDir_SelectedIndexChanged(ByVal sender As System.Object, _&lt;br /&gt;
       ByVal e As System.EventArgs) Handles listDir.SelectedIndexChanged&lt;br /&gt;
         ' When a new item is selected in the list, add this &lt;br /&gt;
         ' to the URI in the text box.&lt;br /&gt;
         If listDir.SelectedItems.Count &amp;lt;&amp;gt; 0 Then&lt;br /&gt;
             CurrentPath = CurrentPath.TrimEnd(&amp;quot;/&amp;quot;)&lt;br /&gt;
             txtFtpSite.Text = CurrentPath &amp;amp; &amp;quot;/&amp;quot; &amp;amp; listDir.SelectedItems(0).Text&lt;br /&gt;
         End If&lt;br /&gt;
     End Sub&lt;br /&gt;
 &lt;br /&gt;
 End Class&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The most complex code found in this example occurs in the event handler for the &amp;lt;tt&amp;gt;cmdQuery&amp;lt;/tt&amp;gt; button, which retrieves a directory listing, parses out the important information, and updates the &amp;lt;tt&amp;gt;ListView&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
In previous builds, the MSDN help included much more information on FTP access and different FTP operations under the index entry &amp;quot;FtpMethods,&amp;quot; complete with useful demonstrations of the different methods. This entry has disappeared in recent builds (along with the FtpMethods class), but check for it to return. In the meantime, you can read up on the FTP protocol and supported commands at ''www.vbip.com/winsock/winsock_ftp_ref_01.asp''.&lt;br /&gt;
&lt;br /&gt;
== Test Group Membership of the Current User ==&lt;br /&gt;
&lt;br /&gt;
The .NET Framework has always provided security classes that let you retrieve basic information about the account of the current user. The new &amp;lt;tt&amp;gt;My.User&amp;lt;/tt&amp;gt; object provided by Visual Basic 2005 makes it easier than ever to access this information.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Find out who's using your application, and the groups a mystery user belongs to''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Applications often need to test who is running the application. For example, you might want to restrict some features to certain groups, such as Windows administrators. You can accomplish this with the &amp;lt;tt&amp;gt;My.User&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;My.User&amp;lt;/tt&amp;gt; object provides two key properties that return information about the current user. These are:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;IsAuthenticated&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; if the current user account information is available in the &amp;lt;tt&amp;gt;My.User&amp;lt;/tt&amp;gt; object. The only reason this information wouldn't be present is if you've created a web application that allows anonymous access, or if the current Windows account isn't associated with the application domain.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Username&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns the current username. Assuming you're using a Windows security policy, this is the Windows account name for the user, in the form &amp;lt;tt&amp;gt;''ComputerName\UserName''&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;''DomainName\UserName''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;My.User&amp;lt;/tt&amp;gt; object also provides a single method, &amp;lt;tt&amp;gt;IsInRole( )&amp;lt;/tt&amp;gt;. This method accepts the name of a group (as a string) and then returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; if the user belongs to that group. For example, you could use this technique to verify that the current user is a Windows administrator before performing a certain task.&lt;br /&gt;
&lt;br /&gt;
To try this out, use the following console application in [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-5|Example 6-5]], which displays some basic information about the current user and tests if the user is an Administrator.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''To check the user and group list for the current computer (or make changes), select Computer management from the Administrative Tools section of the Control Panel. Then, expand the System Tools → Local Users and Groups node''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-5. Testing the current user identity'''&lt;br /&gt;
&lt;br /&gt;
 Module SecurityTest&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )&lt;br /&gt;
         ' Use Windows security. As a result, the User object will&lt;br /&gt;
         ' provide the information for the currently logged in user&lt;br /&gt;
         ' who is running the application.&lt;br /&gt;
         My.User.InitializeWithWindowsUser( )&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Authenticated: &amp;quot; &amp;amp; My.User.Identity.IsAuthenticated)&lt;br /&gt;
         Console.WriteLine(&amp;quot;User: &amp;quot; &amp;amp; My.User.Identity.Username)&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Administrator: &amp;quot; &amp;amp; My.User.IsInRole(&amp;quot;Administrators&amp;quot;))&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's the sort of output you'll see when you run this test:&lt;br /&gt;
&lt;br /&gt;
 Authenticated: True&lt;br /&gt;
 User: FARIAMAT\Matthew&lt;br /&gt;
 Administrator: True&lt;br /&gt;
&lt;br /&gt;
== Encrypt Secrets for the Current User ==&lt;br /&gt;
&lt;br /&gt;
Applications often need a way to store private data in a file or in memory. The obvious solution is ''symmetric encryption'', which scrambles your data using a random series of bytes called a ''secret key''. The problem is that when you want to decrypt your scrambled data, you need to use the same secret key you used to encrypt. This introduces serious complications. Either you need to find a secure place to safeguard your secret key (which is tricky at best), or you need to derive the secret key from some other information, like a user-supplied password (which is much more insecure, and can break down entirely when users forget their passwords).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need a quick way to encrypt secret information, without needing to worry about key management? The long awaited solution appears in . NET 2.0 with the ProtectedData class''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ideal solution is to have the Windows operating system encrypt the data for you. To accomplish this, you need the DPAPI (Data Protection API), which encrypts data using a symmetric key that's based on a piece of user-specific or machine-specific information. This way, you don't need to worry about key storage or authentication. Instead, the operating system authenticates the user when he logs in. Data stored by one user is automatically inaccessible to other users.&lt;br /&gt;
&lt;br /&gt;
In previous versions of .NET, there were no managed classes for using the DPAPI. This oversight is corrected in .NET 2.0 with the new &amp;lt;tt&amp;gt;ProtectedData&amp;lt;/tt&amp;gt; class in the &amp;lt;tt&amp;gt;System.Security.Cryptography&amp;lt;/tt&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;ProtectedData&amp;lt;/tt&amp;gt; class provides two shared methods. &amp;lt;tt&amp;gt;ProtectData( )&amp;lt;/tt&amp;gt; takes a byte array with source data and returns a byte array with encrypted data. &amp;lt;tt&amp;gt;UnprotectData( )&amp;lt;/tt&amp;gt; performs the reverse operation, taking an encrypted byte array and returning a byte array with the decrypted data.&lt;br /&gt;
&lt;br /&gt;
The only trick to using the &amp;lt;tt&amp;gt;ProtectData( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;UnprotectData( )&amp;lt;/tt&amp;gt; methods is that you can only encrypt or decrypt data in a byte array. That means that if you want to encrypt strings, numbers, or something else, you need to write it to a byte array before you perform the encryption.&lt;br /&gt;
&lt;br /&gt;
To see this in action, you can run the console application code in [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-6|Example 6-6]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-6. Storing an encrypted string of text in a file'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Security.Cryptography&lt;br /&gt;
 Imports System.IO&lt;br /&gt;
     &lt;br /&gt;
 Module ProctedData&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )     &lt;br /&gt;
         ' Get the data.&lt;br /&gt;
         Console.WriteLine(&amp;quot;Enter a secret message and press enter.&amp;quot;)&lt;br /&gt;
         Console.Write(&amp;quot;&amp;gt;&amp;quot;)&lt;br /&gt;
         Dim Input As String = Console.ReadLine( )&lt;br /&gt;
     &lt;br /&gt;
         Dim DataStream As MemoryStream&lt;br /&gt;
         If Input &amp;lt;&amp;gt; &amp;quot;&amp;quot; Then&lt;br /&gt;
             Dim Data( ), EncodedData( ) As Byte&lt;br /&gt;
     &lt;br /&gt;
             ' Write the data to a new MemoryStream.&lt;br /&gt;
             DataStream = New MemoryStream( )&lt;br /&gt;
             Dim Writer As New StreamWriter(DataStream)&lt;br /&gt;
             Writer.Write(Input)&lt;br /&gt;
             Writer.Close( )&lt;br /&gt;
     &lt;br /&gt;
             ' Convert the MemoryStream into a byte array,&lt;br /&gt;
             ' which is what you need to use the ProtectData( ) method.&lt;br /&gt;
             Data = DataStream.ToArray( )&lt;br /&gt;
     &lt;br /&gt;
             ' Encrypt the byte array.&lt;br /&gt;
             EncodedData = ProtectedData.Protect(Data, Nothing, _&lt;br /&gt;
               DataProtectionScope.CurrentUser)&lt;br /&gt;
     &lt;br /&gt;
             ' Store the encrypted data in a file.&lt;br /&gt;
             My.Computer.FileSystem.WriteAllBytes(&amp;quot;c:\secret.bin&amp;quot;, &lt;br /&gt;
              EncodedData, False)&lt;br /&gt;
         End If&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you run this application, you'll be prompted to type in some text, which will be encrypted using your current user account information and stored in the file ''secret.bin''. The data won't be accessible to any other user.&lt;br /&gt;
&lt;br /&gt;
To verify that the data is encrypted, you have two choices. You can open the file and take a look for yourself, or you can modify the code so that it reads the data directly from the encrypted memory stream. This code tries the latter, and displays a string of meaningless gibberish as a result:&lt;br /&gt;
&lt;br /&gt;
 ' Verify the data is encrypted by reading and displaying it&lt;br /&gt;
 ' without performing any decryption.&lt;br /&gt;
 DataStream = New MemoryStream(EncodedData)&lt;br /&gt;
 Dim Reader As New StreamReader(DataStream)&lt;br /&gt;
 Console.WriteLine(&amp;quot;Encrypted data: &amp;quot; &amp;amp; Reader.ReadToEnd( ))&lt;br /&gt;
 Reader.Close( )&lt;br /&gt;
&lt;br /&gt;
To decrypt the data, you need to place it into a byte array and then use the &amp;lt;tt&amp;gt;UnprotectData( )&amp;lt;/tt&amp;gt; method. To extract your data out of the unencrypted byte array, you can use a &amp;lt;tt&amp;gt;StreamReader&amp;lt;/tt&amp;gt;. To add decryption support to the previous example, insert the following code, which opens the file and displays the secret message that you entered earlier:&lt;br /&gt;
&lt;br /&gt;
 If My.Computer.FileSystem.FileExists(&amp;quot;c:\secret.bin&amp;quot;) Then&lt;br /&gt;
     Dim Data( ), EncodedData( ) As Byte&lt;br /&gt;
     &lt;br /&gt;
     EncodedData = My.Computer.FileSystem.ReadAllBytes(&amp;quot;c:\secret.bin&amp;quot;)&lt;br /&gt;
     Data = ProtectedData.Unprotect(EncodedData, Nothing, _&lt;br /&gt;
       DataProtectionScope.CurrentUser)&lt;br /&gt;
     &lt;br /&gt;
     Dim DataStream As New MemoryStream(Data)&lt;br /&gt;
     Dim Reader As New StreamReader(DataStream)&lt;br /&gt;
     &lt;br /&gt;
     Console.WriteLine(&amp;quot;Decoded data from file: &amp;quot; &amp;amp; Reader.ReadToEnd( ))&lt;br /&gt;
     Reader.Close( )&lt;br /&gt;
 End If&lt;br /&gt;
&lt;br /&gt;
Remember, because the data is encrypted using the current user profile, you can decrypt the data at any time. The only restriction is that you need to be logged on under the same user account.&lt;br /&gt;
&lt;br /&gt;
Note that when you protect data, you must choose one of the values from the &amp;lt;tt&amp;gt;DataProtectionScope&amp;lt;/tt&amp;gt; enumeration. There are two choices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''No matter which DataProtectionScope you choose, the encrypted information will be stored in a specially protected area of the Windows registry''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;LocalMachine&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Windows will encrypt data with a machine-specific key, guaranteeing that no one can read the data unless they log in to the same computer. This works well for server-side applications that run without user intervention, such as Windows services and web services.&lt;br /&gt;
;&amp;lt;tt&amp;gt;CurrentUser&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Windows will encrypt data with a user-specific key, so that it's inaccessible to any other user.&lt;br /&gt;
&lt;br /&gt;
In the current example, user-specific data is stored. However, you could modify the &amp;lt;tt&amp;gt;DataProtectionScope&amp;lt;/tt&amp;gt; to store data that's accessible to any user on the current computer.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...protecting data before you put it in a database? Once you use the &amp;lt;tt&amp;gt;ProtectedData&amp;lt;/tt&amp;gt; class to encrypt your data, you can put it anywhere you want. The previous example wrote encrypted data to a file, but you can also write the binary data to a database record. To do so, you simply need a binary field in your table with enough room to accommodate the encrypted byte array. In SQL Server, you use the &amp;lt;tt&amp;gt;varbinary&amp;lt;/tt&amp;gt; data type.&lt;br /&gt;
&lt;br /&gt;
== Unleash the Console ==&lt;br /&gt;
&lt;br /&gt;
.NET 1.0 introduced the &amp;lt;tt&amp;gt;Console&amp;lt;/tt&amp;gt; class to give programmers a convenient way to build simple command-line applications. The first version of the &amp;lt;tt&amp;gt;Console&amp;lt;/tt&amp;gt; was fairly rudimentary, with little more than basic methods like &amp;lt;tt&amp;gt;Write( )&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;WriteLine( )&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;Read( )&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;ReadLine( )&amp;lt;/tt&amp;gt;. In .NET 2.0, new features have been added, allowing you to clear the window, change foreground and background colors, alter the size of the window, and handle special keys.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''At last, a Console class with keyboard-handling and screen-writing features''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The best way to learn the new features is to see them in action. [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-7|Example 6-7]] shows a simple application, ''ConsoleTest'', which lets the user move a happy face character around a console window, leaving a trail in its wake. The application intercepts each key press, checks if an arrow key was pressed, and ensures that the user doesn't move outside of the bounds of the window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
In order for the advanced console features to work, you must disable the Quick Console window. The Quick Console is a console window that appears in the design environment, and it's too lightweight to support features like reading keys, setting colors, and copying characters. To disable it, select Tools → Options, make sure the &amp;quot;Show all settings checkbox&amp;quot; is checked, and select the Debugging → General tab. Then, turn off the &amp;quot;Redirect all console output to the Quick Console window.&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-7&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-7. Advanced keyboard handling with the console'''&lt;br /&gt;
&lt;br /&gt;
 Module ConsoleTest&lt;br /&gt;
     &lt;br /&gt;
     Private NewX, NewY, X, Y As Integer&lt;br /&gt;
     Private BadGuyX, BadGuyY As Integer&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Create a 50 column x 20 line window.&lt;br /&gt;
         Console.SetWindowSize(50, 20)&lt;br /&gt;
         Console.SetBufferSize(50, 20)&lt;br /&gt;
     &lt;br /&gt;
         ' Set up the window.&lt;br /&gt;
         Console.Title = &amp;quot;Move The Happy Face&amp;quot;&lt;br /&gt;
         Console.CursorVisible = False&lt;br /&gt;
         Console.BackgroundColor = ConsoleColor.DarkBlue&lt;br /&gt;
         Console.Clear( )&lt;br /&gt;
     &lt;br /&gt;
         ' Display the happy face icon.&lt;br /&gt;
         Console.ForegroundColor = ConsoleColor.Yellow&lt;br /&gt;
         Console.SetCursorPosition(X, Y)&lt;br /&gt;
         Console.Write(&amp;quot;˘&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Read key presses.&lt;br /&gt;
         Dim KeyPress As ConsoleKey&lt;br /&gt;
         Do&lt;br /&gt;
             KeyPress = Console.ReadKey( ).Key&lt;br /&gt;
     &lt;br /&gt;
             ' If it's an arrow key, set the requested position.&lt;br /&gt;
             Select Case KeyPress&lt;br /&gt;
                 Case ConsoleKey.LeftArrow&lt;br /&gt;
                     NewX -= 1&lt;br /&gt;
                 Case ConsoleKey.RightArrow&lt;br /&gt;
                     NewX += 1&lt;br /&gt;
                 Case ConsoleKey.UpArrow&lt;br /&gt;
                     NewY -= 1&lt;br /&gt;
                 Case ConsoleKey.DownArrow&lt;br /&gt;
                     NewY += 1&lt;br /&gt;
             End Select&lt;br /&gt;
     &lt;br /&gt;
             MoveToPosition( )&lt;br /&gt;
         Loop While KeyPress &amp;lt;&amp;gt; ConsoleKey.Escape&lt;br /&gt;
     &lt;br /&gt;
         ' Return to normal.&lt;br /&gt;
         Console.ResetColor( )&lt;br /&gt;
         Console.Clear( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub MoveToPosition( )&lt;br /&gt;
         ' Check for an attempt to move off the screen.&lt;br /&gt;
         If NewX = Console.WindowWidth Or NewX &amp;lt; 0 Or _&lt;br /&gt;
           NewY = Console.WindowHeight Or NewY &amp;lt; 0 Then&lt;br /&gt;
             ' Reset the position.&lt;br /&gt;
             NewY = Y&lt;br /&gt;
             NewX = X&lt;br /&gt;
             Console.Beep( )&lt;br /&gt;
         Else&lt;br /&gt;
             ' Repaint the happy face in the new position.&lt;br /&gt;
             Console.MoveBufferArea(X, Y, 1, 1, NewX, NewY)&lt;br /&gt;
     &lt;br /&gt;
             ' Draw the trail.&lt;br /&gt;
             Console.SetCursorPosition(X, Y)&lt;br /&gt;
             Console.Write(&amp;quot;*&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
             ' Update the position.&lt;br /&gt;
             X = NewX&lt;br /&gt;
             Y = NewY&lt;br /&gt;
             Console.SetCursorPosition(0, 0)&lt;br /&gt;
         End If&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To try this out, run the application and use the arrow keys to move about. [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-FIG-2|Figure 6-2]] shows the output of a typical ''ConsoleTest'' session.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-FIG-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-2. A fancy console application'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_6_tt220.png|A fancy console application]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some of the new &amp;lt;tt&amp;gt;Console&amp;lt;/tt&amp;gt; methods used in ''ConsoleTest'' include the following:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;Clear( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Erases everything in the console window and positions the cursor in the top-left corner.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SetCursorPosition( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Moves the cursor to the designated x- and y-coordinates (measured from the top-left corner). Once you've moved to a new position, you can use &amp;lt;tt&amp;gt;Console.Write( )&amp;lt;/tt&amp;gt; to display some characters there.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SetWindowSize( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;SetBufferSize( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Allow you to change the size of the window (the visible area of the console) and the buffer (the scrollable area of the console, which is equal to or greater than the window size).&lt;br /&gt;
;&amp;lt;tt&amp;gt;ResetColor( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Resets the foreground and background colors to their defaults.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Beep( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Plays a simple beep, which is often used to indicate invalid input.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ReadKey( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Reads just a single key press and returns it as a &amp;lt;tt&amp;gt;ConsoleKeyInfo&amp;lt;/tt&amp;gt; object. You can use this object to easily tell what key was pressed (including extended key presses like the arrow keys) and what other keys were held down at the time (like Alt, Ctrl, or Shift).&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveBufferArea( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Copies a portion of the console window to a new position, and erases the original data. This method offers a high-performance way to move content around the console.&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;tt&amp;gt;Console&amp;lt;/tt&amp;gt; properties include:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;Title&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Sets the window caption.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ForegroundColor&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Sets the text color that will be used the next time you use &amp;lt;tt&amp;gt;Console.Write( )&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;Console.WriteLine( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;BackgroundColor&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Sets the background color that will be used the next time you use &amp;lt;tt&amp;gt;Console.Write( )&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;Console.WriteLine( )&amp;lt;/tt&amp;gt;. To apply this background color to the whole window at once, call &amp;lt;tt&amp;gt;Console.Clear()&amp;lt;/tt&amp;gt; after you set the background color.&lt;br /&gt;
;&amp;lt;tt&amp;gt;CursorVisible&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Hides the blinking cursor when set to &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;WindowHeight&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;WindowWidth&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns or sets the dimensions of the console window.&lt;br /&gt;
;&amp;lt;tt&amp;gt;CursorLeft&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;CursorTop&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns or moves the current cursor position.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...reading a character from a specified position of the window? Sadly, the new Console class provides no way to do this. That means that if you wanted to extend the happy face example so that the user must navigate through a maze of other characters, you would need to store the position of every character in memory (which could get tedious) in order to check the requested position after each key press, and prevent the user from moving into a space occupied by another character.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
To learn more about the new &amp;lt;tt&amp;gt;Console&amp;lt;/tt&amp;gt; class and its new properties and methods, look for the &amp;lt;tt&amp;gt;Console&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ConsoleKeyInfo&amp;lt;/tt&amp;gt; classes in the MSDN help library reference.&lt;br /&gt;
&lt;br /&gt;
== Time Your Code ==&lt;br /&gt;
&lt;br /&gt;
Timing code isn't difficult. You can use the &amp;lt;tt&amp;gt;DateTime.Now&amp;lt;/tt&amp;gt; property to capture the current date and time down to the millisecond. However, this approach isn't perfect. Constructing the &amp;lt;tt&amp;gt;DateTime&amp;lt;/tt&amp;gt; object takes a short time, and that little bit of latency can skew the time you record for short operations. Serious profilers need a better approach, one that uses low-level systems calls and has no latency.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The new Stopwatch class allows you to track how fast your code executes with unparalleled precision''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In .NET 2.0, the best way to time your code is to use the new &amp;lt;tt&amp;gt;Stopwatch&amp;lt;/tt&amp;gt; class in the &amp;lt;tt&amp;gt;System.Diagnostics&amp;lt;/tt&amp;gt; namespace. The &amp;lt;tt&amp;gt;Stopwatch&amp;lt;/tt&amp;gt; class is refreshingly simple to use. All you need to do is create an instance and call the &amp;lt;tt&amp;gt;Start( )&amp;lt;/tt&amp;gt; method. When you're finished, call &amp;lt;tt&amp;gt;Stop( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-EX-8|Example 6-8]] shows a simple test that times how long a loop takes to finish. The elapsed time is then displayed in several different ways, with different degrees of precision.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-EX-8&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-8. Timing a loop'''&lt;br /&gt;
&lt;br /&gt;
 Module TimeCode&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )&lt;br /&gt;
         Dim Watch As New Stopwatch( )&lt;br /&gt;
     &lt;br /&gt;
         Watch.Start( )&lt;br /&gt;
     &lt;br /&gt;
         ' Delay for a while.&lt;br /&gt;
         For i As Integer = 1 To 1000000000&lt;br /&gt;
         Next&lt;br /&gt;
     &lt;br /&gt;
         Watch.Stop( )&lt;br /&gt;
     &lt;br /&gt;
         ' Report the elasped time.&lt;br /&gt;
         Console.WriteLine(&amp;quot;Milliseconds &amp;quot; &amp;amp; Watch.ElapsedMilliseconds)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Ticks: &amp;quot; &amp;amp; Watch.ElapsedTicks)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Frequency: &amp;quot; &amp;amp; Stopwatch.Frequency)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Whole Seconds: &amp;quot; &amp;amp; Watch.Elapsed.Seconds)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Seconds (from TimeSpan): &amp;quot; &amp;amp; Watch.Elapsed.TotalSeconds)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Seconds (most precise): &amp;quot; &amp;amp; _&lt;br /&gt;
           Watch.ElapsedTicks / Stopwatch.Frequency)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's the output you'll see:&lt;br /&gt;
&lt;br /&gt;
 Milliseconds 10078&lt;br /&gt;
 Ticks: 36075265&lt;br /&gt;
 Frequency: 3579545&lt;br /&gt;
 Whole Seconds: 10&lt;br /&gt;
 Seconds (from TimeSpan): 10.0781705&lt;br /&gt;
 Seconds (most precise): 10.078170549609&lt;br /&gt;
&lt;br /&gt;
You can retrieve the elapsed time in milliseconds from the &amp;lt;tt&amp;gt;Stopwatch.ElapsedMilliseconds&amp;lt;/tt&amp;gt; property. (One second is 1,000 milliseconds.) The &amp;lt;tt&amp;gt;ElapsedMilliseconds&amp;lt;/tt&amp;gt; property returns a 64-bit integer (a &amp;lt;tt&amp;gt;Long&amp;lt;/tt&amp;gt;), making it extremely precise. If it's more useful to retrieve the time as a number of seconds or minutes, use the &amp;lt;tt&amp;gt;Stopwatch.Elapsed&amp;lt;/tt&amp;gt; property instead, which returns a &amp;lt;tt&amp;gt;TimeSpan&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
On the other hand, if you want the greatest possible precision, retrieve the number of ticks that have elapsed from the &amp;lt;tt&amp;gt;Stopwatch.ElapsedTicks&amp;lt;/tt&amp;gt; property. Stopwatch ''ticks'' have a special meaning. When you use the &amp;lt;tt&amp;gt;TimeSpan&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;DateTime&amp;lt;/tt&amp;gt; object, a tick represents 0.0001 of a millisecond. In the case of a &amp;lt;tt&amp;gt;Stopwatch&amp;lt;/tt&amp;gt;, however, ticks represent the smallest measurable increment of time, and depend on the speed of the CPU. To convert &amp;lt;tt&amp;gt;Stopwatch&amp;lt;/tt&amp;gt; ticks to seconds, divide &amp;lt;tt&amp;gt;ElapsedTicks&amp;lt;/tt&amp;gt; by &amp;lt;tt&amp;gt;Frequency&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...pausing a timer? If you want to record the total time taken to complete multiple operations, you can use &amp;lt;tt&amp;gt;Stop( )&amp;lt;/tt&amp;gt; to pause a timer and &amp;lt;tt&amp;gt;Start( )&amp;lt;/tt&amp;gt; to resume it later. You can then read the total time taken for all the operations you timed from the &amp;lt;tt&amp;gt;Elasped&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ElaspedMilliseconds&amp;lt;/tt&amp;gt; properties.&lt;br /&gt;
&lt;br /&gt;
You can also run multiple timers at once. All you need to do is create one &amp;lt;tt&amp;gt;Stopwatch&amp;lt;/tt&amp;gt; object for each distinct timer you want to use.&lt;br /&gt;
&lt;br /&gt;
== Deploy Your Application with ClickOnce ==&lt;br /&gt;
&lt;br /&gt;
One of the driving forces behind the adoption of browser-based applications is the fact that organizations don't need to deploy their applications to the client. Most companies are willing to accept the limitations of HTML in order to avoid the considerable headaches of distributing application updates to hundreds or thousands of users.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Want the functionality of a rich client application with the easy deployment of a web application? ClickOnce offers a new solution for deploying your software''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploying a .NET client application will never be as straightforward as updating a web site. However, .NET 2.0 includes a new technology called ClickOnce that simplifies deployment dramatically.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
ClickOnce includes a few remarkable features:&lt;br /&gt;
&lt;br /&gt;
* ClickOnce can automatically create a setup program that you can distribute on a CD or launch over a network or through a web page. This setup program can install prerequisites and create the appropriate Start menu icons.&lt;br /&gt;
* ClickOnce can configure your application to check for updates automatically every time it starts (or periodically in the background). Depending on your preference, you can give the user the option of downloading and running the new updated version, or you can just install it by force.&lt;br /&gt;
* ClickOnce can configure your application to use an online-only mode. In this case, the user always runs the latest version of your application from a web page URL. However, the application itself is cached locally to improve performance.&lt;br /&gt;
&lt;br /&gt;
ClickOnce is tightly integrated with Visual Studio 2005, which allows you to deploy a ClickOnce application to a web site using the Project → Publish menu command.&lt;br /&gt;
&lt;br /&gt;
The following steps take you through the process of preparing your project for publication:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;Using Visual Studio 2005, create a new project. A good choice is a Windows Forms application. Before continuing, save the project.&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;Choose Build → Publish &amp;lt;tt&amp;gt;''[''&amp;lt;/tt&amp;gt; ''ProjectName'' &amp;lt;tt&amp;gt;'']''&amp;lt;/tt&amp;gt; (or right-click your project in the Solution Explorer and choose Publish). This launches the Publish wizard, which gives you a chance to specify or change various settings.&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;The first dialog page of the Publish wizard (the &amp;quot;Where do you want to publish&amp;quot; dialog) prompts you to choose the location where you will publish the files to be deployed (see [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-FIG-3|Figure 6-3]]). This location is the file path or the virtual directory on your web server where you want to deploy the application. For a simple test, use a URL that starts with ''http://localhost/'' (which refers to the current computer). Click Next to continue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
When Visual Studio publishes the application, it will automatically create a subdirectory named ''publish'' in the current application directory, and it will map this to the virtual directory path you've selected.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-FIG-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-3. Choosing a deployment directory'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_6_tt222.png|Choosing a deployment directory]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;Next, choose the install mode (see [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-FIG-4|Figure 6-4]]) by clicking one of the buttons on the &amp;quot;Will the application be available offline&amp;quot; dialog page. Select &amp;quot;Yes, this application is available online or offline.&amp;quot; This way, the setup will add application icons to the Start menu. If you choose &amp;quot;No, this application is only available online,&amp;quot; the user will only be able to run it by surfing to the virtual directory to which it's been published. Click Next to continue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-FIG-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-4. Choosing the install mode'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_6_tt223.png|Choosing the install mode]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;The Publish wizard now displays a summary of your settings. Click Finish to publish it. (You can publish an updated version at any time by selecting Build → Publish &amp;lt;tt&amp;gt;''[''&amp;lt;/tt&amp;gt; ''ProjectName'' &amp;lt;tt&amp;gt;'']''&amp;lt;/tt&amp;gt; from the menu.)&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the wizard completes, the automatically generated ClickOnce web page is launched, as shown in [[Visual Basic 2005: A Developer's Notebook/.NET 2.0 Platform Services#vbadn-CHP-6-FIG-5|Figure 6-5]]. Using this page, a user can click to download and install your application. Try it out by clicking the Install &amp;lt;tt&amp;gt;''[''&amp;lt;/tt&amp;gt; ''AppName'' &amp;lt;tt&amp;gt;'']''&amp;lt;/tt&amp;gt; link.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-6-FIG-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-5. The ClickOnce installation page'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_6_tt224.png|The ClickOnce installation page]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The installation runs without any messages, unless it needs to ask for user consent. For example, before the installation can add an icon to the Start menu, it needs to prompt the user.&lt;br /&gt;
&lt;br /&gt;
Best of all, now that the application is in place, you can make use of its automatic update ability. To test this out, return to the application in Visual Studio .NET and change the main form (perhaps by adding a new button). Then, increment the version number of the application. (To do this, double-click the My Project item in the Solution Explorer, select the Application tab, and click the AssemblyInfo button. A dialog box will appear that lets you set assembly metadata, including the version number.) Finally, republish the application.&lt;br /&gt;
&lt;br /&gt;
When a new version is available on the server, client applications will update themselves automatically, based on their update settings. If you run the installed sample application, it checks for updates when it starts. In this case, it will detect the new version and prompt you to install the update.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
The ClickOnce plumbing has been tweaked and refined continuously during the beta cycle. In some builds of Visual Studio, you may encounter an error when you try to publish a project using ClickOnce. Unfortunately, there aren't any workarounds.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...computers that don't have the .NET Framework 2.0 installed? These machines can't download and install a ClickOnce application automatically. However, when they surf to the ClickOnce installation page, they will see a link that will install the required prerequisites. There are a number of other approaches you can pursue to get .NET 2.0 installed on the client ahead of time. One easy choice is to use the Windows Update feature (surf to ''http://windowsupdate.microsoft.com'' from the client computer).&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
There are a number of articles that discuss the ClickOnce technology in much greater detail. For more information, you may want to refer to the book ''Essential ClickOnce'' (Addison Wesley, forthcoming), or the introduction from MSDN magazine at ''http://msdn.microsoft.com/msdnmag/issues/04/05/ClickOnce''. You can also find a great deal of information in the MSDN help library, and online at ''http://msdn.microsoft.com/clickonce''.&lt;/div&gt;</description>
			<pubDate>Tue, 11 Mar 2008 21:52:04 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Visual_Basic_2005:_A_Developer%27s_Notebook/.NET_2.0_Platform_Services</comments>		</item>
	</channel>
</rss>