1. Trang chủ
  2. » Công Nghệ Thông Tin

professional VB 2005 - 2006 phần 10 pdf

110 248 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 110
Dung lượng 5,72 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Private Sub SendPageByVal response As HttpListenerResponse, _ ByVal message As String Dim sb As New StringBuilder ‘build string With sb .Append“” .AppendFormat“{0}”, message .Append“” Tr

Trang 1

context.Response.StatusCode = 404 End Select

End While End Sub

Our background task performs its work in a loop as long as the HttpListener is actively tening Every developer knows that performing a set of tasks in a (relatively) tight loop is dan- gerous, possibly leading to computer or application lockup However, the BackgroundWorker performs this on another thread, leaving our application responsive.

lis-For this application, we first get access to the context for the listener The context groups together one client’s set of communication with our listener Similar to the HttpContext in ASP.NET, the HttpListenerContext provides access to the HttpListenerRequest and HttpListenerResponse objects, so the first step in handling a request should always be to get this context Next, the code uses a very simple means of determining the request URL In a more full-featured implementation, this could be more complex, separating any query values from the path requested, etc For this sample, the listener only responds to three main paths, “/time”,

“/date”, and “/random” to receive the current (server) time or date, or a random Integer value.

If the user requests anything else, we return a 404.

7. The SendPage subroutine simply writes out a basic HTML page, and the value determined Private Sub SendPage(ByVal response As HttpListenerResponse, _

ByVal message As String) Dim sb As New StringBuilder

‘build string With sb Append(“<html><body>”) AppendFormat(“<h3>{0}</h3>”, message) Append(“</body></html>”)

Try Using writer As New StreamWriter(.OutputStream) With writer

.Write(sb.ToString) Flush()

End With End Using

Catch ex As Exception Me.EventLog.WriteEntry(ex.Message, EventLogEntryType.Error)

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 2

‘close the response to end Close()

End Try End With End Sub

Hopefully there isn’t much surprising in this code Using a StringBuilder , a response is built Then the content is written back to the browser (see Figure 26-8) using a StreamWriter that is created on top of the Response.OutputStream Remember to Close the Response , or the request will never close until it times out.

Figure 26-8

8. Before you can install and test your Windows Service, however, it must be installed On theProperties window for the actual service, click Add Installer (see Figure 26-9) This adds a new file to the project called ProjectInstaller.vb , and adds two components to the file, ServiceInstaller1 and ServiceProcessInstaller1 You can either keep these names

or change them as you desire In addition, set the properties as in the following table.

Figure 26-9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 3

Component Property Value

ServiceInstaller1 Description Sample Service from Wrox Professional Visual

Basic 2005 DisplayName Sample Service ServiceName SampleService ServiceProcessInstaller1 Account LocalSystem

Most of these properties only affect the display values for the Windows Service However, the Account property of the ServiceProcessInstaller deserves special mention Windows Services run on behalf of the user Therefore, they can actually run under another user account.

By setting the Account property to LocalSystem , you are setting the resulting Windows Service to run under the local system account This account has a lot of access to the system, so you may want to instead use an account with more limited rights to the system; however you would have to create this account separately.

9. Build the Windows service Unfortunately, if you attempt to run the service directly from Visual Basic, you will get an error message (see Figure 26-10).

Figure 26-10

A Windows Service can only run if it has been installed into the system, and this task is formed using a command-line utility InstallUtil.exe Open the Visual Studio Command Prompt and navigate to the directory where you have built MiniServer.exe Run installutil miniserver.exe , and hopefully you’ll be greeted with a success message (see Figure 26-11).

per-Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 4

Figure 26-11

10. Finally, you can start your new service Open the Services application from Start ➪ All Programs ➪ Administrative Tools Find the Sample Service in the list (see Figure 26-12), and click Start You should now be able to request one of the items the service is listening to, such as http://local host:9090/time (see Figure 26-13).

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 6

Figure 26-14

The HttpListener adds yet another powerful way for your applications to communicate It gives you the ability to extend the reach of your applications out to Web browser clients, without requiring the additional administrative and management overhead of IIS to your deployment.

This chapter looked at many of the classes that expose network programming You’ve seen how to make Web requests without a browser, so you could use the data on the Internet in your applications; you’ve seen how you can leverage the bare sockets layer to write your own communication protocols, and finally, you’ve seen some of the new classes in Visual Basic 2005 for creating FTP clients and Web servers.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 8

V isual Basic and

the Inter net

In today’s network-centric world, it’s very likely that applications will need to work with other computers over a private network, the Internet, or both.

This chapter details how to:

❑ Download resources from the Web

❑ Design your own communication protocols

❑ Reuse Internet Explorer in your applications

A good place to start working with network resources is with a look at how to download content from the Web.

Downloading Internet Resources

Downloading content from the Web is very easy, so you’ll throw together a basic application before getting onto some more meaty topics This application will download HTML from a Web page and display it in a text box Later on, you’ll look at how you can display HTML properly by hosting Internet Explorer (IE) directly using the new WebBrowser control in Windows Forms applications, but for now you’ll just use plain text.

In order to download a Web page, you need to be able to identity the remote page that you wish

to download, make a request of the Web server that can provide that page, listen for the response, and download the data for the resource.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 9

The relevant classes for this example are System.Uri, System.Net.WebRequest, System.Net HttpWebRequest , and System.Net.HttpWebResponse :

❑ System.Uriis a useful general-purpose class for expressing a Uniform Resource Identifier (URI).

A Uniform Resource Locator (URL) is a type of URI (although in reality the terms are so confused

that they are often used interchangeably) A URI, however is “more than” a URL, which is why this NET class is Uri and not Url System.Uri has many properties for decoding a URI For example, if you had a string like www.pretendcompany.com:8080/ myservices/ myservice.asmx?WSDL , you could use the Port property to extract the port number, the Query property to extract the query string, and so on.

❑ A WebRequest expresses some kind of Internet resource whether it is located on the LAN or WAN (so in my opinion a better name for this class would be NetRequest , as the classes aren’t specifically related to the Web protocol).

❑ Protocol-specific descendants of WebRequest carry out the actual request: HttpWebRequest expresses an HTTP download and FileWebRequest expresses a file download, for example file://c:/MyFile.txt

❑ An HttpWebResponse is returned once a connection to the Web server has been made and the resource is available to download.

There are another two major classes related to working with the Internet in the NET Framework One is System.Net.WebClient and the other is System.Net.WebProxy WebClient is basically a helper class that wraps the request and response classes previously mentioned.

As this is a professional-level book, I’m going to show you what to do behind the scenes, in effect, reengineer what WebClient can do I’ll talk about WebProxy later, which allows you to explicitly define

a proxy server to use for Internet communications.

Let’s use these classes to build an application Create a new Windows application, create a new form, and add controls to it as shown in Figure 27-1.

Figure 27-1

The control names are: textUrl , buttonGo , and textData The Anchor properties of the controls are set so that the form resizes properly The control textUrl should be set to Top , Left , Right ; buttonGo

to Top , Right ; and textData to Top , Left , Bottom , Right

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 10

Add these namespace import declarations to the form’s code:

Imports System.IO Imports System.Net Imports System.Text

To keep the code simple, you’ll include all the functionality into the Click handler of buttonGo In an ideal world, you want to break the code in the handler out to a separate method This enriches the inter- face of the object and promotes good reuse.

The first thing you do here is create a new System.Uri based on the URL that the user enters into the text box:

Private Sub buttonGo_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonGo.Click

Dim uri As New Uri(textUrl.Text)

Then, you’ll illustrate some of the useful properties of System.Uri : Dim builder As New StringBuilder

builder.Append(“AbsolutePath: “ & uri.AbsolutePath & VbCrLf) builder.Append(“AbsoluteUri: “ & uri.AbsoluteUri & VbCrLf) builder.Append(“Host: “ & uri.Host & VbCrLf)

builder.Append(“HostNameType: “ & uri.HostNameType.ToString() & _

VbCrLf) builder.Append(“LocalPath: “ & uri.LocalPath & VbCrLf) builder.Append(“PathAndQuery: “ & uri.PathAndQuery & VbCrLf) builder.Append(“Port: “ & uri.Port & VbCrLf)

builder.Append(“Query: “ & uri.Query & VbCrLf) builder.Append(“Scheme: “ & uri.Scheme) MsgBox(builder.ToString())

The shared Create method of System.Net.WebRequest is used to create the actual object that you can use to download the Web resource Notice how you don’t create an instance of HttpWebRequest ; you’re working with a return object of type WebRequest However, you’ll actually be given an HttpWebRequest object, and WebRequest chooses the most appropriate class to return based on the URI This allows you to build your own handlers for different network resources that can be used by consumers who simply sup- ply an appropriate URL.

To make the request and get the response back from the server (so ultimately you can access the data), you call the GetResponse method of WebRequest In your case, you’ll get an HttpWebResponse object — once more it’s up to the implementation of the WebRequest -derived object, in this case HttpWebRequest , to return an object of the most suitable type.

If the request is not okay, you’ll get an exception (which for the sake of simplicity you won’t bother cessing) If the request is okay, you can get the length and the type of the response using properties of the WebResponse object:

pro-Dim request As WebRequest = WebRequest.Create(uri) Dim response As WebResponse = request.GetResponse() builder = New StringBuilder

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 11

builder.Append(“Request type: “ & request.GetType().ToString() & VbCrLf)

builder.Append(“Response type: “ & response.GetType().ToString() & VbCrLf)

builder.Append(“Content length: “ & response.ContentLength & _

“ bytes” & VbCrLf) builder.Append(“Content type: “ & response.ContentType & VbCrLf)

MsgBox(builder.ToString())

It just remains for you to download the information You can do this through a stream ( WebResponse objects return a stream by overriding GetResponseStream ), and what’s more, you can use a System IO.StreamReader to download the whole lot in a single call by calling the ReadToEnd method This method will only download text, so if you want to download binary data you’ll have to use the methods

on the Stream object directly, or use a System.IO.BinaryReader

Dim stream As Stream = response.GetResponseStream()

Dim reader As New StreamReader(stream)

Dim data As String = reader.ReadToEnd()

This is a simple URL The application tells you that the scheme is http , and the host name type is Dns

If, for example, you enter an IP into the URL to be requested rather than a host name, this type will come back as IPv4 This tells you where the host name came from; in this case, it’s a general Internet host name.

Next, the application as shown in Figure 27-3 provides information about the response.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 12

There may be times when you need to transfer data across a network (either a private network or the Internet) when the existing techniques and protocols don’t exactly suit your needs For example, you wouldn’t be able to download resources using the techniques discussed at the start of this chapter, and you can’t use Web Services (as described in Chapter 23) or remoting (as described in Chapter 24) When

this happens, the best course of action is to roll your own protocol using sockets.

TCP/IP, and therefore, the Internet itself, is based on sockets The principle is simple, establish a port at one end and allow clients to “plug in” to that port from the other end Once the connection is made, applications can send and receive data through a stream For example, HTTP nearly always operates on port 80 So, a Web server opens a socket on port 80 and waits for incoming connections (Web browsers, unless told otherwise, attempt to connect to port 80 in order to make a request of that Web server).

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 13

In NET, sockets are implemented in the System.Net.Sockets namespace and use classes from System.Net and System.IO to get the stream classes Although working with sockets can be a little tricky outside of NET, the Framework includes some superb classes that enable you to open a socket for inbound connections ( System.Net.TcpListener ) and for communication between two open sockets ( System.Net.TcpClient ) These two classes, in combination with some threading shenanigans, allow you build your own protocol, through which you can send any data you like With your own protocol, you have ultimate control over the communication.

To demonstrate these techniques, you’re going to build Wrox Messenger, a very basic instant messenger application similar to MSN Messenger.

Building the Application

You’ll wrap all the functionality of your application into a single Windows application This application will act as both a server that waits for inbound connections and a client that has established outbound connections.

Create a new project called WroxMessenger Change the title of Form1 to Wrox Messenger and add a TextBox control called textConnectTo and a Button control called buttonConnect The form should appear as shown in Figure 27-5.

Figure 27-5

We’ll talk about this in more detail in a little while, but for now it’s very important that all of your UI code runs in the same thread, and that the thread is actually the main application that creates and runs Form1

To keep track of what’s happening, you’ll add a field to Form1 that allows you to store the ID of the startup thread and also report that ID on the caption This will help you gain an understanding of the thread/UI issues that are discussed later You’ll also need some namespace imports and a constant specifying the ID

of the default port Add this code to Form1 :

Trang 14

<System.Diagnostics.DebuggerNonUserCode()> _ Public Sub New()

To listen for incoming connections, you’ll create a separate class called Listener This class will use an instance of System.Net.Sockets.TcpListener to wait for incoming connections Specifically, this will

open a TCP port that any client can connect to — sockets are absolutely not platform-specific Although

connections are always made on a specific, known port, the actual communication takes place on a port

of the TCP/IP subsystem’s choosing, which means you can support many inbound connections at once, despite the fact that each of them connects to the same port Sockets are an open standard available on pretty much any platform you care to mention For example, if you publish the specification for your protocol, developers working on Linux would be able to connect to your Wrox Messenger service When you detect an inbound connection, you’ll be given a System.Net.Sockets.TcpClient object This is your gateway to the remote client To send and receive data, you need to get hold of a System Net.NetworkStream object (returned through a call to GetStream on TcpClient ), which returns a stream that you can use.

Create a new class called Listener This thread needs members to hold an instance of a System Threading.Thread object and also a reference back to the Form1 class that is the main form in the application We won’t go into a discussion of how to spin up and spin down threads, nor are we going to talk about synchronization (You should refer back to Chapter 22 if you need more information on this.) Here’s the basic code for the Listener class:

Imports System.Net.Sockets Imports System.Threading

Public Class Listener

Private _main As Form1 Private _listener As TcpListener Private _thread As Thread

Public Sub New(ByVal main As Form1) _main = main

End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 15

Public Sub SpinUp()

‘ create and start the new thread

_thread = New Thread(AddressOf ThreadEntryPoint) _thread.Start()

End Sub

End Class

The obvious missing method here is ThreadEntryPoint This is where you need to create the socket and wait for inbound connections When you get them, you’ll be given a TcpClient object, which you need to pass back to Form1 , where the conversation window can be created.

To create the socket, create an instance of TcpListener and give it a port In your application, the port you’re going to use is 10101 This port should be free on your computer, but if the debugger breaks on

an exception when you instantiate TcpListener or call Start , try another port Once you’ve done that and called Start to configure the object to listen for connections, you drop into an infinite loop and call AcceptTcpClient This method will block until the socket is closed, or a connection becomes available.

If you get Nothing back, either the socket is closed or there’s a problem, so you drop out of the thread If you get something back, then you pass the TcpClient over to Form1 through a call to the (not yet built) ReceiveInboundConnection method:

Dim client As TcpClient = _listener.AcceptTcpClient()

If client Is Nothing Then Exit Do

End If

‘ Process it

_main.ReceiveInboundConnection(client) Loop

End Sub

It’s in the ReceiveInboundConnection method that you’ll create the Conversation form that the user can use to send messages.

Creating Conversation Windows

When building Windows Forms applications that support threading, there’s always the possibility of running into a problem with the Windows messaging subsystem This is a very old part of Windows (the idea has been around since version 1.0 of the platform, although the implementation on modern Windows versions is far removed from the original) that powers the Windows user interface.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 16

Even those who are not familiar with old-school Windows programming, such as MFC, Win32, or even Win16 development, should be familiar with events When you move a mouse over a form, you get MouseMove events When you close a form, you get a Closed event There’s a mapping between these events and the messages that Windows passes around to support the actual display of the windows For example, whenever you receive a MouseMove event, a message called WM_MOUSEMOVE is sent to the window, by Windows, in response to the mouse driver In NET, and in other Rapid Application Development (RAD) environments like VB and Delphi, this message is converted into an event that you can write code against.

Although this is getting way off the topic — you know how to build Windows Forms applications by now and don’t need the details of messages like WM_NCHITTEST or WM_PAINT — it has an important implication In effect, Windows creates a message queue for each thread into which it posts the messages that the thread’s windows have to work with This queue is looped on a virtually constant basis, and the messages are distributed to the appropriate window (remember, small controls like buttons and text boxes are also windows) In NET, these messages are turned into events, but unless the message queue still gets looped the messages don’t get through.

Imagine that Windows needs to paint a window It will post a WM_PAINT message to the queue A sage loop implemented on the main thread of the process containing the window detects the message and dispatches it on to the appropriate window where it is processed Now, imagine that the queue isn’t looped The message never gets picked up, and the window will never get painted.

mes-In a Windows application, a single thread is usually responsible for message dispatch This thread is ally (although it doesn’t have to be) the main application thread, the one that’s created when the process is first created If you create windows in a different thread, then that new thread has to support the message dispatch loop so that messages destined for the windows get through However, with Listener , you have no code for processing the message loop and there’s little point in writing any, because the next time you call AcceptTcpClient you’re going to block and everything will stop working.

usu-The trick then is to create the windows only in the main application thread, which is the thread that ated Form1 and that is processing the messages for all the windows created in this thread You can pass calls from one thread to the other by calling the Invoke method of Form1

cre-This is where things start to get complicated There is an awful lot of code to write to get to a point where you can see that the socket connection has been established and get conversation windows to appear Here’s what you need to do:

❑ Create a new Conversation form This form will need controls for displaying the total content of the conversation, plus a TextBox control for adding new messages.

❑ The Conversation window will need to be able to send and receive messages through its own thread.

❑ Form1 needs to be able to initiate new connections This will be done in a separate thread that is managed by the thread pool When the connection has been established, a new Conversation window needs to be created and configured.

❑ Form1 also needs to receive inbound connections When it gets one of these, a new Conversation needs to be created and configured.

Let’s look at these problems one at a time.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 17

Creating the Conversation Form

The simplest place to start is to build the new Conversation form This needs three TextBox controls ( textUsername , textMessages , and textMessage ) and a Button control ( buttonSend ) The form is shown in Figure 27-6.

Figure 27-6

This class requires a number of fields and an enumeration It needs fields to hold the username of the user (which you’ll default to Evjen ), the underlying TcpClient , and the NetworkStream returned by that client The enumeration indicates the direction of the connection (which will help you when debugging): Imports System.Net

Private _username As String = “Evjen”

Private _client As TcpClient

Private _stream As NetworkStream

Private _direction As ConversationDirection

Public Enum ConversationDirection As Integer

Inbound = 0 Outbound = 1 End Enum

We won’t look into the issues of establishing a thread for exchanging messages at this stage, but we will look at implementing the ConfigureClient method This method will eventually do more work than this, but, for now, it sets a couple of fields and calls UpdateCaption :

Public Sub ConfigureClient(ByVal client As TcpClient, _

ByVal direction As ConversationDirection)

‘ Set it up

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 18

_client = client _direction = direction

‘ Update the window

UpdateCaption() End Sub

Protected Sub UpdateCaption()

‘ Set the text.

Dim builder As New StringBuilder(_username) builder.Append(“ - “)

builder.Append(_direction.ToString()) builder.Append(“ - “)

builder.Append(Thread.CurrentThread.GetHashCode()) builder.Append(“ - “)

If Not _client Is Nothing Then builder.Append(“Connected”) Else

builder.Append(“Not connected”) End If

Text = builder.ToString() End Sub

One debugging issue that you have is that if you’re connecting to a conversation on the same machine, you need a way of changing the name of the user sending each message; otherwise, things will get confusing That’s what the topmost TextBox control is for In the constructor, set the text for the textUsername.Text property:

Public Sub New() MyBase.New()

‘This call is required by the Windows Form Designer.

UpdateCaption() End Sub

Initiating Connections

Form1 needs to be able to both initiate connections and receive inbound connections — the application is both a client and a server You’ve already created some of the server portion by creating Listener , and now you’ll look at the client side.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 19

The general rule when working with sockets is that any time you send anything over the wire, you must perform the actual communication in a separate thread Virtually all calls to send and receive do so in a blocking manner; that is, they block until data is received, block until all data is sent, and so on.

If threads are used well, the UI will keep running as normal, irrespective of the problems that may occur during transmitting and receiving This is why in the InitiateConnection method on Form1 you defer processing to another method called InitiateConnectionThreadEntryPoint , which is called from a new thread:

Public Sub InitiateConnection()

InitiateConnection(textConnectTo.Text)

End Sub

Public Sub InitiateConnection(ByVal hostName As String)

‘ Give it to the threadpool to do

a new TcpClient from this IPEndPoint and try to connect.

If at any time an exception is thrown (which can happen because the name couldn’t be resolved or the connection could not be established), you’ll pass the exception over to

HandleInitiateConnectionException If it succeeds, you’ll pass it to

ProcessOutboundConnection Both of these methods will be implemented shortly:

Private Sub InitiateConnectionThreadEntryPoint(ByVal state As Object)

Try

‘ Get the host name

Dim hostName As String = CStr(state)

‘ Resolve

Dim hostEntry As IPHostEntry = Dns.Resolve(hostName)

If Not hostEntry Is Nothing Then

‘ Create an end point for the first address.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 20

Dim endPoint As New IPEndPoint(hostEntry.AddressList(0), ServicePort)

Throw New ApplicationException(“Host ‘“ & hostName & _

“‘ could not be resolved.”) End If

Catch ex As Exception HandleInitiateConnectionException(ex) End Try

End Sub

When it comes to HandleInitiateConnectionException , you start to see the inter-thread UI lems that were mentioned earlier When there is a problem with the exception, you need to tell the user, which means that you need to move the exception from the thread-pool-managed thread into the main application thread The principle for this is the same, you need to create a delegate and call that delegate through the Invoke method of the form This method does all the hard work in marshaling the call across to the other thread.

prob-Here’s what the delegates look like They have the same parameters of the calls themselves As a naming convention, it’s a good idea to use the same name as the method and tack the word Delegate on the end: Public Class Form1

Inherits System.Windows.Forms.Form Private Shared _mainThreadId As Integer

Public Shared Function IsMainThread() As Boolean

If Thread.CurrentThread.GetHashCode() = _mainThreadId Then Return True

Else Return False End If End Function

The first thing you do at the top of HandleInitiateConnectionException is to check the thread ID If

it doesn’t match, you create the delegate and call it Notice how you set the delegate to call back into the same method, because the second time it’s called you would have moved to the main thread; therefore, IsMainThread will return True , and you can process the exception properly:

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 21

Protected Sub HandleInitiateConnectionException(ByVal ex As Exception)

‘ main thread?

If IsMainThread() = False Then

‘ Create and call

Dim args(0) As Object args(0) = ex

Invoke(New HandleInitiateConnectionExceptionDelegate(AddressOf _ HandleInitiateConnectionException), args) ‘ return

Return End If

When it comes to ProcessOutboundConnection , you have to again jump into the main UI thread However, the magic behind this method is implemented in a separate method called ProcessConnection , which can handle either inbound or outbound connections Here’s the delegate:

Public Class Form1

Inherits System.Windows.Forms.Form

Private Shared _mainThreadId As Integer

Private _listener As Listener

Protected Delegate Sub ProcessConnectionDelegate(ByVal client As _

TcpClient, ByVal direction As Conversation.ConversationDirection)

Protected Delegate Sub HandleInitiateConnectionExceptionDelegate(ByVal _

ex As Exception)

Here’s the method itself, which creates the new Conversation form and calls the ConfigureClient method:

Protected Sub ProcessConnection(ByVal client As TcpClient, _

ByVal direction As Conversation.ConversationDirection)

‘ Do you have to move to another thread?

If IsMainThread() = False Then

‘ Create and call

Dim args(1) As Object args(0) = client args(1) = direction Invoke(New ProcessConnectionDelegate(AddressOf ProcessConnection), _ args)

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 22

Return End If

‘ Create the conversation window

Dim conversation As New Conversation conversation.Show()

conversation.ConfigureClient(client, direction) End Sub

Of course, ProcessOutboundConnection needs to defer to ProcessConnection : Public Sub ProcessOutboundConnection(ByVal client As TcpClient) ProcessConnection(client, Conversation.ConversationDirection.Outbound) End Sub

Now that you can connect to something on the client side, let’s look at how to receive connections (on the server side).

Receiving Inbound Connections

You’ve already built Listener , but you haven’t created an instance of it, nor have you spun up its thread to wait for incoming connections To do this, you need a field in Form1 to hold an instance of the object, and you also need to tweak the constructor Here’s the field:

Public Class Form1 Inherits System.Windows.Forms.Form

Private _mainThreadId As Integer

Private _listener As Listener

Here is the new code that needs to be added to the constructor:

Public Sub New() MyBase.New()

‘This call is required by the Windows Form Designer.

InitializeComponent()

_ mainThreadId = Thread.CurrentThread.GetHashCode() Text &= “ - “ & _mainThreadId.ToString()

Public Sub ReceiveInboundConnection(ByVal client As TcpClient) ProcessConnection(client, Conversation.ConversationDirection.Inbound) End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 23

If you run the project now, you should be able to click the Connect button and see two windows — one inbound and one outbound, as shown in Figure 27-7.

Figure 27-7

If you close all three windows, the application will keep running because you haven’t written code to close down the listener thread, and having an open thread like this will keep the application open Use the Debug ➪ Stop Debugging menu option in Visual Studio to close the application down by killing all running threads.

By clicking the Connect button, you’re calling InitiateConnection This spins up a new thread in the pool that resolves the given host name ( localhost ) into an IP address This IP address, in combination with a port number, is then used in the creation of a TcpClient object If the connection can be made, ProcessOutboundConnection is called, which results in the first of the conversation windows being created and marked as “outbound.”

Your example is somewhat artificial, as the two instances of Wrox Messenger should be running on rate computers On the remote computer (if you’re connecting to localhost , this will be the same com- puter), a connection is received through the AcceptTcpClient method of TcpListener This results in

sepa-a csepa-all to ReceiveInboundConnection , which, in turn, results in the creation of the second conversation window, this time marked as “inbound.”

Sending Messages

The next step is to work out how to exchange messages between the two Conversation windows You already have a TcpClient in each case so all you have to do is squirt binary data down the wire on one side and pick it up at the other end As the two Conversation windows act as both client and server, both need to be able to send and receive.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 24

There are three problems to solve:

❑ You need to establish one thread to send and another thread to receive data.

❑ Data sent and received needs to be reported back to the user so that he or she can follow the conversation.

❑ The data that you want to send has to be converted into a wire-ready format, which in NET terms usually means serialization.

The power of sockets means that you can define whatever protocol you like for data transmission If you wanted to build your own SMTP server, you could implement the (publicly available) specifications, set

up a listener to wait for connections on port 25 (the standard port for SMTP), wait for data to come in, process it, and return responses as appropriate.

It’s best to work in this way when building protocols Unless there are very strong reasons for not doing

so, make your server as open as possible: Do not tie it to a specific platform This is the way that things are done on the Internet To an extent, things like Web Services should negate the need to build your own pro- tocols; as you go forward, you will rely instead on the “remote object available to local client” paradigm Now it’s time to think ahead to the idea of using the serialization features of NET to transmit data across the network After all, you’ve already seen this in action with Web Services and remoting You can take an object in NET, use serialization to convert it to a string of bytes, and expose that string down to a Web Service consumer, or remoting client, or even to a file.

Chapter 24 discussed the BinaryFormatter and SoapFormatter classes You could use either of those classes, or create your own custom formatter, to convert data for transmission and reception In this case, you’re going to create a new class called Message and use BinaryFormatter to crunch it down into a wire-ready format and convert it back again for processing.

This approach isn’t ideal from the perspective of interoperability, because the actual protocol used is lost

in the implementation of the NET Framework, rather than being under your absolute control.

If you want to build an open protocol, this is not the best way to do it Unfortunately, the best way to do

it is beyond the scope of this book, but a good place to start is to look at existing protocols and standards and model any protocol on their approach BinaryFormatter is quick and dirty, which is why you’re going to use it.

The Message Class

The Message class contains two fields, _username and _message , which form the entirety of the data that you want to transmit The code for this class follows; notice how the Serializable attribute is applied to it so that BinaryFormatter can change it into a wire-ready form Also notice how you’re providing a new implementation of ToString :

Imports System.Text

<Serializable()> Public Class Message

Private _username As String Private _message As String

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 25

Public Sub New(ByVal name As String)

_username = name End Sub

Public Sub New(ByVal name As String, ByVal message As String)

_username = name _message = message End Sub

Public Overrides Function ToString() As String

Dim builder As New StringBuilder(_username) builder.Append(“ says:”)

builder.Append(ControlChars.CrLf) builder.Append(_message)

builder.Append(ControlChars.CrLf) Return builder.ToString()

End Function

End Class

Now, all you have to do is spin up two threads, one for transmission and one for reception, updating the

display You need two threads per conversation, so if you have 10 conversations open, you’ll need 20

threads plus the main UI thread, plus the thread running TcpListener

Receiving messages is pretty easy When calling Deserialize on BinaryFormatter , you give it the stream returned to you from TcpClient If there’s no data, this blocks If there is data, it’s decoded into a Message object that you can display If you have multiple messages coming down the pipe, BinaryFormatter will keep processing them until the pipe is empty Here’s the method for doing this, and this should be added to Conversation Remember, you haven’t implemented ShowMessage yet:

Protected Sub ReceiveThreadEntryPoint()

Dim message1 As Message = formatter.Deserialize(_stream)

If message1 Is Nothing Then Exit Do

End If

‘ Show it

ShowMessage(message1) Loop

End Sub

Transmitting messages is a touch more complex What you want is a queue (managed by a System Collections.Queue ) of outgoing messages Every second, you’ll examine the state of the queue If you find any messages, you’ll use BinaryFormatter to transmit them Because you’ll be accessing this queue from multiple threads, you’ll use a System.Threading.ReaderWriterLock to control access.

To minimize the amount of time you spend inside locked code, you’ll quickly transfer the contents of the

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 26

shared queue into a private queue that you can process at your leisure This allows the client to continue

to add messages to the queue through the UI, even though existing messages are being sent by the mit thread.

trans-First, add these members to Conversation : Public Class Conversation

Inherits System.Windows.Forms.Form

Private _username As String = “Evjen”

Private _client As TcpClient Private _stream As NetworkStream Private _direction As ConversationDirection

Private _receiveThread As Thread Private _transmitThread As Thread Private _transmitQueue As New Queue() Private _transmitLock As New ReaderWriterLock()

Now, add this method again to Conversation : Protected Sub TransmitThreadEntryPoint()

workQueue.Clear() For Each message In _transmitQueue workQueue.Enqueue(message) Next

_transmitQueue.Clear() _transmitLock.ReleaseWriterLock()

‘ Loop the outbound messages

For Each message In workQueue

Trang 27

When you want to send a message, you call one version of the SendMessage method Here are all of the implementations, and the Click handler for buttonSend :

Private Sub buttonSend_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles buttonSend.Click

Public Sub SendMessage(ByVal username As String, ByVal message As String)

SendMessage(New Message(username, message))

application thread for processing Although, the call in response to the button click comes off the main application thread, the one from inside ReceiveThreadEntryPoint does not Here’s what the delegate looks like:

Public Class Conversation

Inherits System.Windows.Forms.Form

‘ members

Private _username As String = “Evjen”

Private _client As TcpClient

Private _stream As NetworkStream

Private _direction As ConversationDirection

Private _receiveThread As Thread

Private _transmitThread As Thread

Private _transmitQueue As New Queue()

Private _transmitLock As New ReaderWriterLock()

Public Delegate Sub ShowMessageDelegate(ByVal message As Message)

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 28

Here’s the method implementation:

Public Sub ShowMessage(ByVal message As Message)

‘ Return

Return End If

Public Sub ConfigureClient(ByVal client As TcpClient, _ ByVal direction As ConversationDirection)

‘ Set it up

_client = client _direction = direction

‘ Update the window

UpdateCaption()

‘ Get the stream

_stream = _client.GetStream()

‘ Spin up the threads

_transmitThread = New Thread(AddressOf TransmitThreadEntryPoint) _transmitThread.Start()

_receiveThread = New Thread(AddressOf ReceiveThreadEntryPoint) _receiveThread.Start()

End Sub

At this point, you should be able to connect and exchange messages, as shown in Figure 27-8.

Note that the screen shots show the username of the inbound connection as Tuija This was done with the textUsername text box so that you can follow which half of the conversation comes from where.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 29

Figure 27-8

Shutting Down the Application

You’ve yet to solve the problem of neatly closing the application, or, in fact, dealing with one person in the conversation closing down his or her window, indicating a wish to end the conversation When the process ends (whether “neatly” or forcefully), Windows automatically mops up any open connections and frees up the port for other processes.

Imagine, if you will, that you have two computers, one window per computer as you would in a duction environment If you close your window, you’re indicating that you want to end the conversa- tion You need to close the socket and spin down the transmission and reception threads At the other end, you should be able to detect that the socket has been closed, spin down the threads, and tell the user that the other user has terminated the conversation.

pro-This all hinges on being able to detect when the socket has been closed For some reason, Microsoft has actually made this very hard, thanks to the design of the TcpClient class TcpClient effectively encap- sulates a System.Net.Sockets Socket class, providing methods for helping to manage the connection lifetime and communication streams However, TcpClient does not have a method or property that answers the question, “Am I still connected?” What you need to do is get hold of the Socket object that TcpClient is wrapping and then you can use its Connected property to find out if the connection has been closed.

TcpClient does support a property called Client that returns a Socket However, this property is tected, meaning that you can only access it by inheriting a new class from TcpClient But, there is another way — you could use reflection to get at the property and call it without having to inherit a new class Microsoft claims that this is a legitimate technique, even though it appears to violate every rule in the book about encapsulation Reflection is designed not only for finding out which types are available, and

pro-Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 30

learning which methods and properties each type supports, but also for invoking those methods and properties whether they’re protected or public.

So, in Conversation , you need to store the socket:

Public Class Conversation Inherits System.Windows.Forms.Form

Private _username As String = “Evjen”

Private _client As TcpClient Private _socket As Socket

In ConfigureClient , you need to use Reflection to peek in to the Type object for TcpClient and dig out the Client property Once you have a System.Reflection.PropertyInfo for this property, you can retrieve its value by using the GetValue method Here’s the code Don’t forget to import the System.Reflection namespace:

Public Sub ConfigureClient(ByVal client As TcpClient, _

ByVal direction As ConversationDirection)

‘ Set it up

_client = client _direction = direction

‘ Update the window

UpdateCaption()

‘ Get the stream

_stream = _client.GetStream()

‘ Get the socket through reflection

Dim propertyInfo As PropertyInfo = _ _client.GetType().GetProperty(“Client”, _ BindingFlags.Instance Or BindingFlags.NonPublic)

If Not propertyInfo Is Nothing Then _socket = propertyInfo.GetValue(_client, Nothing) Else

Throw New Exception(“Couldn’t retrieve Client property from TcpClient”) End If

‘ Spin up the threads

_transmitThread = New Thread(AddressOf TransmitThreadEntryPoint) _transmitThread.Start()

_receiveThread = New Thread(AddressOf ReceiveThreadEntryPoint) _receiveThread.Start()

End Sub

Applications are able to check the state of the socket either by detecting when an error occurs because you’ve tried to send data over a closed socket or by actually asking if the socket is connected If you either don’t have a Socket available in _socket (that is, it is Nothing ), or if you have one and it tells you you’re disconnected, you give the user some feedback and exit the loop By exiting the loop, you effectively exit the thread, which is a neat way of quitting the thread Notice as well that you might not

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 31

have a window at this point (you might be the one that closed the conversation by closing the window),

so you wrap the UI call in a Try Catch (the other side will see a <disconnect> message):

Protected Sub TransmitThreadEntryPoint()

‘ Create a formatter

Dim formatter As New BinaryFormatter

Dim workQueue As New Queuevs

End Try Exit Do End If

‘ Go through the queue

ReceiveThreadEntryPoint also needs some massaging When the socket is closed, the stream will no longer be valid and so BinaryFormatter.Deserialize will throw an exception Likewise, you quit the loop and, therefore, neatly quit the thread:

Protected Sub ReceiveThreadEntryPoint()

End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 32

So, how do you deal with actually closing the socket? Well, you tweak the Dispose method of the form itself (you will find this method in the Windows-generated code section of the file), and if you have a _socket object you close it:

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then components.Dispose()

End If End If

‘ Close the socket

If Not _socket Is Nothing Then _socket.Close()

_socket = Nothing End If

MyBase.Dispose(disposing) End Sub

Now, you’ll be able to start a conversation, and if one of the windows is closed, <disconnect> will appear in the other This is illustrated in Figure 27-9 In the background, the four threads (one transmit, one receive per window) will spin down properly.

Figure 27-9

However, the application itself will still not close properly, even if you close all the windows That’s because you need to stop the Listener when Form1 closes To do this, you’ll make Listener imple- ment Idisposable :

Public Class Listener Implements IDisposable Public Sub Dispose() Implements System.IDisposable.Dispose

‘ Stop it

Finalize() GC.SuppressFinalize(Me)

End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 33

Protected Overrides Sub Finalize()

‘ Stop the listener

If Not _listener Is Nothing Then _listener.Stop()

_listener = Nothing End If

‘ Stop the thread

If Not _thread Is Nothing Then _thread.Join()

_thread = Nothing End If

Protected Overrides Sub OnClosed(ByVal e As System.EventArgs)

If Not _listener Is Nothing Then

_listener.Dispose() _listener = Nothing End If

End Sub

After the code is compiled again, the application can be closed.

Using Internet Explorer in Your Applications

A common requirement of modern applications is to display HTML files and other files commonly used with Internet applications Although the NET Framework has considerable support for common image formats (such as GIF, JPEG, and PNG), working with HTML used to be a touch trickier in versions 1.0 and 1.1 of the NET Framework Today, life has been made considerably easier with the inclusion of the new WebBrowser control in the NET Framework 2.0.

You don’t want to have to write your own HTML parser, so using this new control to display HTML pages is, in most cases, one of your only options Microsoft’s Internet Explorer was implemented as a stand-alone component comprising a parser and a renderer, all packaged up in a neat COM object The new WebBrowser control that you all use “simply” utilizes this COM object There’s nothing to stop you from using this COM object directly in your own applications, but you will find it considerably easier to use this new control for hosting Web pages in your applications.

For information on how to accomplish this task using the NET Framework 1.0 or 1.1, please review the second and third editions of this book.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 34

Yes, a COM object There is no managed version of Internet Explorer for use with NET Considering that writing an HTML parser is extremely hard, and writing a renderer is extremely hard, the natural conclu- sion is that it’s much easier to use interop to get to Internet Explorer in the NET applications than have Microsoft try and rewrite a managed version of it just for NET Maybe you will see “Internet Explorer NET” within the next year or two, but for now you do have to use interop.

Windows Forms and HTML — No Problem!

These sections demonstrate how to build a mini-browser application In some cases, you might want to display HTML pages without giving the user the UI widgets like a toolbar or the ability to enter his or her own URLs You might also want to use the control in a nonvisual manner For example, using the WebBrowser control, you can retrieve Web pages and then print the results that are retrieved without ever needing to display the contents Let’s start though by first creating a simple form that contains only

a TextBox and a WebBrowser control.

Allowing Simple Web Browsing in Your Windows Application

The first step is to create a new Windows Forms application called MiniBrowser On the default form, place a single TextBox control and the new WebBrowser control so that your form looks as shown in Figure 27-10.

Figure 27-10

The idea is that when the end user presses the Enter key (Return key), the URL that is entered into the text box will be the HTML page that is retrieved and displayed in the WebBrowser control To accom- plish this task, use the following code for your form:

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 35

Public Class Form1

Private Sub TextBox1_KeyPress(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress

If e.KeyChar = Chr(13) Then WebBrowser1.Navigate(TextBox1.Text) End If

End Sub

End Class

For this simple example, you check the key presses that are made in the TextBox1 control, and if the key press is a specific one — the Enter key — then you use the WebBrowser control’s Navigate method to navigate to the requested page The Navigate method can take a single String value, which represents the location of the Web page to retrieve The example shown in Figure 27-11 shows the Wrox Web site.

Figure 27-11

Launching Internet Explorer from Your Windows Application

Sometimes, the goal is not to host a browser inside of the application but instead to allow the user to find the Web site in a typical Web browser For an example of this task, create a Windows Form that has

a LinkLabel control on it For instance, you can have a form that has a LinkLabel control on it that simply states, “Visit your company Web site!”

Once this control is in place, use the following code to launch the company’s Web site in an independent browser as opposed to directly in the form of your application:

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 36

Public Class Form1

Private Sub LinkLabel1_LinkClicked(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles _ LinkLabel1.LinkClicked

Dim wb As New WebBrowser wb.Navigate(“http://www.wrox.com”, True)

End Sub

End Class

In this example, when the LinkLabel control is clicked by the user, a new instance of the WebBrowser class is created Then, using the WebBrowser ’s Navigate method, the code specifies the location of the Web page as well as a Boolean value that specifics whether this end point should be opened within the Windows Form application (a False value) or from within an independent browser (a True value) By default, this is set to False With the preceding construct, when the end user clicks on the link found in the Windows application, a browser instance will be instantiated and the Wrox Web site will be immedi- ately launched.

Updating URLs and Page Titles

Notice that when working with the MiniBrowser example in which the WebBrowser control is directly

in the form, when you click the links, the text in the TextBox1 control is not updated You can fix this by listening for events coming off the WebBrowser control and adding handlers to the control.

Updating the form’s title with the HTML page’s title is easy You just have to create a DocumentTitle Changed event and update the Text property of the form:

Private Sub WebBrowser1_DocumentTitleChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles WebBrowser1.DocumentTitleChanged

Me.Text = WebBrowser1.DocumentTitle.ToString()

End Sub

In this case, when the WebBrowser control notices that the page title has changed (due to changing the page viewed), the DocumentTitleChanged event will fire In this case, you change the Form’s Text property (its title) to the title of the page being viewed using the DocumentTitle property of the WebBrowser control.

The next thing you want to do is to update the text string that appears in the Form’s text box, based on the complete URL of the page being viewed To do this, you can use the WebBrowser control’s Navigated event.

Private Sub WebBrowser1_Navigated(ByVal sender As Object, _ ByVal e As System.Windows.Forms.WebBrowserNavigatedEventArgs) Handles _ WebBrowser1.Navigated

TextBox1.Text = WebBrowser1.Url.ToString()

End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 37

In this case, when the requested page is finished being downloaded in the WebBrowser control, the Navigated event is fired You simply update the Text value of the TextBox1 control to be the URL of the page This means that once a page is loaded in the WebBrowser control’s HTML container and if the URL changes in this process, then the new URL will be shown in the text box For instance, if you employ these steps and navigate to the Wrox Web site ( www.wrox.com ), you will notice that the page’s URL will immediately change to http://www.wrox.com/WileyCDA/ This process also means that if the end user clicks on one of the links contained within the HTML view, then the URL of the newly requested page will also be shown in the text box.

Now, if you run the application with the preceding changes put into place, the form title and address bar will work as they do in Microsoft’s Internet Explorer, as demonstrated in Figure 27-12.

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 38

Figure 27-13

On startup, buttonBack , buttonForward , and buttonStop should be disabled because there is no point to the buttons if there is no initial page loaded You will later tell the WebBrowser control when to enable and disable the Back and Forward buttons, depending on where the user is in the page stack Also, when a page is being loaded, you will need to enable the Stop button — but you will also need to disable the Stop button once the page has finished being loaded.

First off though, you’ll add the functionality behind the buttons The WebBrowser class itself has all of the methods that you need, so this is all very straightforward:

Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load buttonBack.Enabled = False

buttonForward.Enabled = False buttonStop.Enabled = False End Sub

Private Sub buttonBack_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonBack.Click WebBrowser1.GoBack()

TextBox1.Text = WebBrowser1.Url.ToString() End Sub

Private Sub buttonForward_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonForward.Click

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 39

WebBrowser1.GoForward() TextBox1.Text = WebBrowser1.Url.ToString() End Sub

Private Sub buttonStop_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonStop.Click WebBrowser1.Stop()

End Sub

Private Sub buttonRefresh_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonRefresh.Click WebBrowser1.Refresh()

End Sub

Private Sub buttonHome_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonHome.Click WebBrowser1.GoHome()

TextBox1.Text = WebBrowser1.Url.ToString() End Sub

Private Sub buttonSubmit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles buttonSubmit.Click WebBrowser1.Navigate(TextBox1.Text)

buttonBack.Enabled = False End If

buttonForward.Enabled = False End If

End Sub

Private Sub WebBrowser1_Navigated(ByVal sender As Object, _ ByVal e As System.Windows.Forms.WebBrowserNavigatedEventArgs) Handles _ WebBrowser1.Navigated

TextBox1.Text = WebBrowser1.Url.ToString() Me.Text = WebBrowser1.DocumentTitle.ToString() End Sub

Private Sub WebBrowser1_Navigating(ByVal sender As Object, _ ByVal e As System.Windows.Forms.WebBrowserNavigatingEventArgs) Handles _ WebBrowser1.Navigating

buttonStop.Enabled = True End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 40

Private Sub WebBrowser1_DocumentCompleted(ByVal sender As Object, _ ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) _ Handles WebBrowser1.DocumentCompleted

buttonStop.Enabled = False End Sub

End Class

There are a lot of different activities going on in this example, since there are so many options for the end user when using this MiniBrowser application First, for each of the button Click events, there is a spe- cific WebBrowser class method assigned as the action to initiate For instance, for the Back button on the form, you simply use the Web Browser control’s GoBack() method And for the other buttons, it is the same — for the Forward button you have the GoForward() method, and for the other buttons you have methods such as Stop() , Refresh() , and GoHome() This makes it fairly simple and straightforward to create a toolbar that will give you actions similar to those of Microsoft’s Internet Explorer.

When the form is first loaded, the Form1_Load event disables the appropriate buttons From there, the end user can enter a URL into the text box and click the Submit button to have the application retrieve the desired page.

To manage the enabling and disabling of the buttons, you have to key in a couple of events As tioned before, whenever downloading begins you need to enable Stop For this, you simply add an event handler for the Navigating event to enable the Stop button:

men-Private Sub WebBrowser1_Navigating(ByVal sender As Object, _ ByVal e As System.Windows.Forms.WebBrowserNavigatingEventArgs) Handles _ WebBrowser1.Navigating

buttonStop.Enabled = True End Sub

Then the Stop button is again disabled when the document has finished loading:

Private Sub WebBrowser1_DocumentCompleted(ByVal sender As Object, _ ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) _ Handles WebBrowser1.DocumentCompleted

buttonStop.Enabled = False End Sub

Enabling and disabling the appropriate Back and Forward buttons really depend on the ability to go backward or forward in the page stack This is achieved by using both the CanGoForwardChanged and the CanGoBackChanged events.

Private Sub WebBrowser1_CanGoBackChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles WebBrowser1.CanGoBackChanged

If WebBrowser1.CanGoBack = True Then buttonBack.Enabled = True Else

buttonBack.Enabled = False End If

End Sub

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Ngày đăng: 12/08/2014, 22:22

TỪ KHÓA LIÊN QUAN