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

Programming C# 2nd Edition phần 10 pptx

55 314 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 55
Dung lượng 917,23 KB

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

Nội dung

// set the file callback for reading // begin reading the string from the client public void StartRead // when called back by the read, display the string // and echo it back to

Trang 1

myFileCallBack, // call back delegate

null); // local state object

}

The cycle begins again with another read of the file, and the cycle continues until the file has been completely read and transmitted to the client The client code simply writes a filename to the network stream to kick off the file read:

string message = @"C:\test\source\AskTim.txt";

bool fQuit = false;

while (!fQuit)

{

char[] buffer = new char[BufferSize];

You are now ready to create a new StreamReader from the NetworkStream member variable streamToServer:

System.IO.StreamReader reader =

Trang 2

The call to Read( ) takes three parameters: the buffer, the offset at which to begin reading, and the size of the buffer:

int bytesRead = reader.Read(buffer,0, BufferSize);

Check to see if the Read( ) returned any bytes; if not you are done and you can set the Boolean value fQuit to true, causing the loop to terminate:

// get a file name from the client

// open the file and send the

// contents from the server to the client

public class AsynchNetworkFileServer

buffer = new byte[256];

// create the network stream

Trang 3

// set the file callback for reading

// begin reading the string from the client

public void StartRead( )

// when called back by the read, display the string

// and echo it back to the client

private void OnReadComplete( IAsyncResult ar )

{

int bytesRead = networkStream.EndRead(ar);

// if you got a string

"Opening file {0}", fileName);

// open the file input stream

myFileCallBack, // call back delegate

null); // local state object

}

Trang 4

// when you have a buffer-full of the file

void OnFileCompletedRead(IAsyncResult asyncResult)

// after writing the string, get more of the file

private void OnWriteComplete( IAsyncResult ar )

{

networkStream.EndWrite(ar);

Console.WriteLine( "Write complete");

// begin reading more of the file

inputStream.BeginRead(

buffer, // holds the results

0, // offset

buffer.Length, // (BufferSize)

myFileCallBack, // call back delegate

null); // local state object

}

private const int BufferSize = 256;

private byte[] buffer;

private Socket socket;

private NetworkStream networkStream;

private Stream inputStream;

private AsyncCallback callbackRead;

private AsyncCallback callbackWrite;

private AsyncCallback myFileCallBack;

}

Trang 5

public static void Main( )

// if a client connects, accept the connection

// and return a new socket named socketForClient

// while tcpListener keeps listening

Trang 6

private int Run( )

{

string message = @"C:\test\source\AskTim.txt";

Console.Write(

"Sending {0} to server.", message);

// create a streamWriter and use it to

// write a string to the server

System.IO.StreamWriter writer =

new System.IO.StreamWriter(streamToServer);

writer.Write(message);

writer.Flush( );

bool fQuit = false;

// while there is data coming

// from the server, keep reading

while (!fQuit)

{

// buffer to hold the response

char[] buffer = new char[BufferSize];

// Read response

System.IO.StreamReader reader =

new System.IO.StreamReader(streamToServer);

// see how many bytes are

// retrieved to the buffer

private const int BufferSize = 256;

private NetworkStream streamToServer;

Trang 7

will encapsulate the object pointed to by the URI That is, you can call GetResponse( ) on your WebRequest object to get the actual object (e.g., a web page) pointed to by the URI What is returned is encapsulated in a WebResponse object You can then ask that WebResponse object for a Stream object by calling GetResponseStream( ) GetResponseStream( ) returns a stream that encapsulates the contents of the web object (e.g., a stream with the web page)

The next example retrieves the contents of a web page as a stream To get a web page, you'll want to use HttpWebRequest HttpWebRequest derives from WebRequest and provides additional support for interacting with the HTTP protocol

To create the HttpWebRequest, cast the WebRequest returned from the static Create( ) method of the WebRequestFactory:

Creating the HTTPWebRequest establishes a connection to a page on my web site What you get back from the host is encapsulated in an HttpWebResponse object, which is an HTTP protocol-specific subclass of the more general WebResponse class:

Trang 8

Example 21-14 Reading a web page as an HTML stream

// get the streamReader from the response

StreamReader streamReader = new StreamReader(

<body bgcolor="#ffffff" vlink="#808080"

alink="#800000" topmargin="0" leftmargin="0">

<table border="0" cellpadding="0" cellspacing="0" width="454"

Trang 9

like to work on a programming project.&quot;

</font></b><font face="times new roman, times,

serif" size="3"><b>

</b> Barnes &amp; Noble</font></i><font size="3"><br>

The output shows that what is sent through the stream is the HTML of the page you

requested You might use this capability for screen scraping; reading a page from a site into a

buffer and then extracting the information you need

All examples of screen scraping in this book assume that you are reading a site for which you have copyright permission

21.6 Serialization

When an object is streamed to disk, its various member data must be serialized that is,

written out to the stream as a series of bytes The object will also be serialized when stored in

a database or when marshaled across a context, app domain, process, or machine boundary

The CLR provides support for serializing an object-graph an object and all the member data

of that object As noted in Chapter 19, by default, types are not serialized To serialize an object, you must explicitly mark it with the [Serializable] attribute

In either case, the CLR will do the work of serializing your object for you Because the CLR knows how to serialize all the primitive types, if your object consists of nothing but primitive types (all your member data consists of integers, longs, strings, etc.), you're all set If your object consists of other user-defined types (classes), you must ensure that these types are also serializable The CLR will try to serialize each object contained by your object (and all their contained objects as well), but these objects themselves must either be primitive types or they must be serializable

This was also evident in Chapter 19 when you marshaled a Shape object that contained a Point object as member data The Point object in turn consisted of primitive data In order to serialize (and thus marshal) the Shape object, its constituent member, the Point object, also had to be marked as serializable

When an object is marshaled, either by value or by reference, it must be serialized The difference is only whether a copy is made or a proxy is provided to the client Objects marked with the [Serializable] attribute are marshaled by value; those that derive from MarshalByRefObject are marshaled by reference, but both are serialized See Chapter 19 for more information

21.6.1 Using a Formatter

When data is serialized, it will eventually be read; either by the same program or by a different program running on another machine In any case, the code reading the data will expect that data to be in a particular format Most of the time in a NET application, the

Trang 10

expected format will either be native binary format or Simple Object Access Protocol (SOAP)

SOAP is a simple, lightweight XML-based protocol for exchanging information across the Web SOAP is highly modular and very extensible It also leverages existing Internet technologies, such as HTTP and SMTP

When data is serialized, the format of the serialization is determined by the formatter you

apply In Chapter 19, you used formatters with channels when communicating with a remote object Formatter classes implement the interface IFormatter; you are also free to create your own formatter, though very few programmers will ever need or want to! The CLR provides both a SoapFormatter for Internet serialization and a BinaryFormatter that is useful for fast local storage

You can instantiate these objects with their default constructors:

BinaryFormatter binaryFormatter =

New BinaryFormatter( );

Once you have an instance of a formatter, you can invoke its Serialize( ) method, passing

in a stream and an object to serialize You'll see how this is done in the next example

21.6.2 Working with Serialization

To see serialization at work, you need a sample class that you can serialize and then deserialize You can start by creating a class named SumOf SumOf has three member variables:

private int startNumber = 1;

private int endNumber;

private int[] theSums;

The member array theSums represents the value of the sums of all the numbers from startNumber through endNumber Thus, if startNumber is 1 and endNumber is 10, the array will have the values:

1,3,6,10,15,21,28,36,45,55

Each value is the sum of the previous value plus the next in the series Thus if the series is 1,2,3,4, the first value in theSums will be 1 The second value is the previous value (1) plus the next in the series (2); thus, theSums[1] will hold the value 3 Likewise, the third value is the previous value (3) plus the next in the series theSums[2] is 6 Finally, the fourth value

in theSums is the previous value (6) plus the next in the series (4), for a value of 10

The constructor for the SumOf object takes two integers: the starting number and the ending number It assigns these to the local values and then calls a helper function to compute the contents of the array:

Trang 11

public SumOf(int start, int end)

int count = endNumber - startNumber + 1;

theSums = new int[count];

You can display the contents of the array at any time by using a foreach loop:

private void DisplaySums( )

21.6.2.1 Serializing the object

Now, mark the class as eligible for serialization with the [Serializable] attribute:

binaryFormatter.Serialize(fileStream,this);

This will serialize the SumOf object to disk

21.6.2.2 Deserializing the object

To reconstitute the object, open the file and ask a binary formatter to DeSerialize it:

Trang 12

public static SumOf DeSerialize( )

to display its values:

public static void Main( )

{

Console.WriteLine("Creating first one with new ");

SumOf app = new SumOf(1,10);

Console.WriteLine(

"Creating second one with deserialize ");

SumOf newInstance = SumOf.DeSerialize( );

Console.WriteLine("Creating first one with new ");

SumOf app = new SumOf(1,10);

Console.WriteLine("Creating second one with deserialize ");

SumOf newInstance = SumOf.DeSerialize( );

Trang 13

private void ComputeSums( )

{

int count = endNumber - startNumber + 1;

theSums = new int[count];

private int startNumber = 1;

private int endNumber;

private int[] theSums;

Trang 14

21.6.3 Handling Transient Data

In some ways, the approach to serialization demonstrated in Example 21-15 is very wasteful Because you can compute the contents of the array given its starting and ending numbers, there really is no reason to store its elements to disk Although the operation might be inexpensive with a small array, it could become costly with a very large one

You can tell the serializer not to serialize some data by marking it with the [NonSerialized] attribute:

[NonSerialized] private int[] theSums;

If you don't serialize the array, however, the object you create will not be correct when you deserialize it The array will be empty Remember, when you deserialize the object, you simply read it up from its serialized form; no methods are run

To fix the object before you return it to the caller, implement the IDeserializationCallback interface:

[Serializable]

class SumOf : IDeserializationCallback

Also implement the one method of this interface: OnDeserialization( ) The CLR promises that if you implement this interface, your class's OnDeserialization( ) method will be called when the entire object graph has been deserialized This is just what you want; the CLR will reconstitute what you've serialized, and then you have the opportunity to fix up the parts that were not serialized

This implementation can be very simple; just ask the object to recompute the series:

public virtual void OnDeserialization (Object sender)

{

ComputeSums( );

}

Trang 15

This is a classic space/time trade-off; by not serializing the array you make deserialization somewhat slower (because you must take the time to recompute the array), and you make the file somewhat smaller To see if not serializing the array had any effect, I ran the program with the digits 1 to 5,000 Before setting [NonSerialized] on the array, the serialized file was 20K After setting [NonSerialized], the file was 1K Not bad Example 21-16 shows the source code using the digits 1 to 5 as input (to simplify the output)

Example 21-16 Working with a nonserialized object

Console.WriteLine("Creating first one with new ");

SumOf app = new SumOf(1,5);

Console.WriteLine("Creating second one with deserialize "); SumOf newInstance = SumOf.DeSerialize( );

int count = endNumber - startNumber + 1;

theSums = new int[count];

Trang 16

private void Serialize( )

// fix up the nonserialized data

public virtual void OnDeserialization

(Object sender)

{

ComputeSums( );

}

private int startNumber = 1;

private int endNumber;

[NonSerialized] private int[] theSums;

Trang 17

So far you've streamed your data to disk for storage and across the network for easy communication with distant programs There is one other time you might create a stream: to store permanent configuration and status data on a per-user basis For this purpose, the NET

Frameworks offer isolated storage

21.7 Isolated Storage

The NET CLR provides isolated storage to allow the application developer to store data on a

per-user basis Isolated storage provides much of the functionality of traditional Windows ini

files or the more recent HKEY_CURRENT_USER key in the Windows Registry

Applications save data to a unique data compartment associated with the application The CLR implements the data compartment with a data store, which is typically a directory on the

filesystem

Administrators are free to limit how much isolated storage individual applications can use They can also use security so that less trusted code cannot call more highly trusted code to write to isolated storage

What is important about isolated storage is that the CLR provides a standard place to store your application's data, but it does not impose (or support) any particular layout or syntax for that data In short, you can store anything you like in isolated storage

Typically, you will store text, often in the form of name-value pairs Isolated storage is a good mechanism for saving user configuration information such as login name, the position of various windows and widgets, and other application-specific, user-specific information The data is stored in a separate file for each user, but the files can be isolated even further by distinguishing among different aspects of the identity of the code (by assembly or by originating application domain)

Using isolated storage is fairly straightforward To write to isolated storage, create an instance

of an IsolatedStorageFileStream, which you initialize with a filename and a file mode (create, append, etc.):

Trang 18

Example 21-17 Writing to isolated storage

System.DateTime currentTime = System.DateTime.Now;

output = "Last access: " + currentTime.ToString( );

Trang 19

Example 21-18 Reading from isolated storage

private void Run( )

Trang 20

Chapter 22 Programming NET and COM

Programmers love a clean slate Although it would be nice if we could throw away all the code we've ever written and start over, this typically isn't a viable option for most companies Over the past decade, many development organizations have made a substantial investment in developing and purchasing COM components and ActiveX controls If NET is

to be a viable platform, these legacy components must be usable from within NET applications, and to a lesser degree, NET components must be callable from COM

This chapter describes the support NET provides for importing ActiveX controls and COM components into your application, for exposing NET classes to COM-based applications, and for making direct calls to Win32 APIs You will also learn about C# pointers and keywords for accessing memory directly, a technique that may be crucial in some applications

22.1 Importing ActiveX Controls

ActiveX controls are COM components typically dropped into a form, which might or might not have a user interface When Microsoft developed the OCX standard, which allowed developers to build ActiveX controls in Visual Basic and use them with C++ (and vice versa), the ActiveX control revolution began Over the past few years, thousands of such controls have been developed, sold, and used They are small, easy to work with, and an effective example of binary reuse

Importing ActiveX controls into NET is surprisingly easy, considering that the COM binary standard and the NET binary standard are not compatible Visual Studio NET is able to import ActiveX controls Microsoft has also developed a command-line utility, AxImp, which will create the assemblies necessary for the control to be used in a NET application

22.1.1 Creating an ActiveX Control

To demonstrate the ability to use classic ActiveX controls in a NET application, first develop

a simple four-function calculator as an ActiveX control and then invoke that ActiveX control from within a C# application Build the control in VB6, and test it in a VB6 application If you don't have VB6 or don't want to bother creating the control, you can download the control from my web site (http://www.libertyassociates.com/)

Once the control is working in the standard Windows environment, you'll copy it to your NET development environment, register it, and import it into a Windows Forms application

To create the control, open VB6 and choose ActiveX Control as the new project type Make the project form as small as possible, because this control will not have a user interface Right-click UserControl1 and choose Properties Rename it Calculator in the Properties window Click the Project in the project explorer, and in the Properties window, rename it to CalcControl Immediately save the project and name both the file and the project CalcControl,

as shown in Figure 22-1

Trang 21

Figure 22-1 Creating a VB ActiveX control

Now you can add the four calculator functions by right-clicking the CalcControl form, selecting View Code from the pop-up menu, and typing in the VB code shown in Example 22-1

Example 22-1 Implementing the CalcControl ActiveX control

This is the entire code for the control Compile this to the CalcControl.ocx file by choosing

File Make CalcControl.ocx on the Visual Basic 6 menu bar

Trang 22

Next open a second project in VB as a standard executable (EXE) Name the form TestForm and name the project CalcTest Save the file and project as CalcTest

Add the ActiveX control as a component by pressing Control-T and choosing CalcControl from the Controls tab, as shown in Figure 22-2

Figure 22-2 Adding the CalcControl to the VB6 toolbox

This action puts a new control on the toolbox, as shown circled in Figure 22-3

Figure 22-3 Locating CalcControl in the Visual Basic 6 toolbox

Drag the new control on to the form TestForm and name it CalcControl Note that the new control will not be visible; this control has no user interface Add two text boxes, four buttons, and one label, as shown in Figure 22-4

Trang 23

Figure 22-4 Building the TestForm user interface

Name the buttons btnAdd, btnSubtract, btnMultiply, and btnDivide All that is left is for you to implement methods for handling the button click events of the calculator buttons Each time a button is clicked, you want to get the values in the two text boxes, cast them to double (as required by CalcControl) using the VB6 CDbl function, invoke a CalcControl function, and print the result in the label control Example 22-2 provides the complete source code

Example 22-2 Using the CalcControl ActiveX control in a VB program (TestForm)

Private Sub btnAdd_Click( )

Trang 24

Figure 22-5 shows the result of running the CalcTest program, typing in two numbers and clicking the Multiply button

Figure 22-5 Running the CalcTest program

22.1.2 Importing a Control in NET

Now that you've shown that the CalcControl ActiveX control is working, you can copy the

CalcControl.ocx file to your NET development environment Once you have copied it,

register the CalcControl.ocx file using Regsvr32 You're now ready to build a test program in

.NET to use the calculator:

Regsvr32 CalcControl.ocx

To get started, create a Visual C# Windows Form project in Visual Studio NET (see Chapter 13), name the project InteropTest, and design a form (such as the TestForm form you created in VB in the preceding section) by dragging and dropping controls onto it Name the form TestForm A complete sample form is shown in Figure 22-6

Figure 22-6 Building a Windows Form to test the CalcControl ActiveX control

Trang 25

22.1.2.1 Importing a control

There are two ways to import an ActiveX control into the Visual Studio NET development environment You can use the Visual Studio NET tools themselves, or import the control

manually using the aximp utility that ships with the NET SDK Framework To use Visual

Studio NET, choose Tools Customize Toolbox from the menu On the COM Components tab find the CalcControl.Calculator object you just registered, as shown in Figure 22-7

Figure 22-7 Importing the CalcControl ActiveX control

Because CalcControl is registered on your NET machine, the Visual Studio NET Customize Toolbox is able to find it When you select the control from this dialog box, it is imported into your application; Visual Studio takes care of the details Alternatively, you can

open a command box and import the control manually using the aximp.exe utility, as shown in

Figure 22-8

Figure 22-8 Running aximp

aximp.exe takes one argument, the ActiveX control you want to import (CalcControl.dll) It

produces three files:

AxCalcControl.dll

A NET Windows control

Trang 26

CalcControl.dll

A proxy NET class library

AxCalcControl.pdb

A debug file

22.1.2.2 Adding a control to the Visual Studio toolbox

Once this is done, you can return to the Customize Toolbox window, but this time select NET Framework Components You can now browse to the location at which the NET Windows

control AxCalcControl.dll was generated and import that file into the toolbox, as shown in

Figure 22-9

Figure 22-9 Browsing for the imported control

Once imported, the control appears on the toolbox menu, as shown in Figure 22-10 Note that the control may appear at the bottom of the toolbox

Figure 22-10 Viewing the AxCalcControl calculator after importing it into the toolbox

Trang 27

Now you can drag this control onto your Windows Form and make use of its functions, just as you did in the VB6 example

Add event handlers for each of the four buttons The event handlers will delegate their work

to the ActiveX control you wrote in VB6 and imported into NET

The source for the event handlers is shown in Example 22-3

Example 22-3 Implementing event handlers for the test Windows Form

private void btnAdd_Click(object sender, System.EventArgs e)

{

double left = double.Parse(textBox1.Text);

double right = double.Parse(textBox2.Text);

label1.Text = axCalculator1.Add( ref left, ref right).ToString( );

}

private void btnDivide_Click(object sender, System.EventArgs e)

{

double left = double.Parse(textBox1.Text);

double right = double.Parse(textBox2.Text);

label1.Text = axCalculator1.Divide(ref left, ref right).ToString( ); }

private void btnMultiply_Click(object sender, System.EventArgs e)

{

double left = double.Parse(textBox1.Text);

double right = double.Parse(textBox2.Text);

label1.Text = axCalculator1.Multiply(ref left, ref right).ToString( ); }

private void btnSubtract_Click(object sender, System.EventArgs e)

{

double left = double.Parse(textBox1.Text);

double right = double.Parse(textBox2.Text);

label1.Text = axCalculator1.Subtract(ref left, ref right).ToString( ); }

Each implementing method obtains the values in the text fields, converts them to double using the static method double.Parse( ), and passes those values to the calculator's methods The results are cast back to a string and inserted in the label, as shown in Figure 22-11

Figure 22-11 Running the imported ActiveX Control in a Windows Form

22.2 Importing COM Components

Importing ActiveX controls turns out to be fairly straightforward Many of the COM components that companies develop are not ActiveX controls, however; they are standard COM dynamic link library (DLL) files To see how to use these with NET, return to VB6 and

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

TỪ KHÓA LIÊN QUAN