The simplest configuration of the first Magento Module

The simplest configuration of the first Magento Module – Cấu hình đơn giản nhất của 1 Module Magento

Cụ thể các bước để tạo Module:
+ Khai báo Module trong (Module declaration in): app\etc\modules\tenModule.xml
+ Hoàn tất code Module trong đường dẫn (Complete code module in the path): /app/code/local/<tên Namespace>/<tên Module>/

Trong đó phải có các thư mục sau (Which must have the following folders):
– Block: Nơi chứa block (Container block).
– Controllers: Rất quan trọng, dùng để điều khiển, xử lý các sự kiện, Load và Render Layout: cái này không có ==> không hiện Layout. (Very important, used to control and handle events, Load and Render Layout: no-controllers ==> no- display Layout.)
Etc: Nơi config Module. Cực quan trọng. Bao gồm: cấu hình cấu trúc dữ liệu xml cho Module, cấu hình cấu trúc dữ liệu xml cho file giao diện, cấu hình cấu trúc dữ liệu xml cho đường dẫn của các thư mục Block, Controllers, Model, Msql4, các cấu hình nâng cao khác… (Extremely important. Includes: Module configuration, interface configuration file, configured path of the directory block, Controllers Model, Msql4, the advanced configuration …)
– Model, bên trong là Msql4: chứa các Model để xử lý truy xuất dữ liệu.
+ Cấu hình cho giao diện: ở đường dẫn: (Configuration for the interface: in the path:): /app/design/<adminhtml hoặc (or) front end >/<interface>/<theme>/layout/<module>.xml
+ Giao diện (interface):  /app/design/<adminhtml hoặc (or) front end >/<interface>/<theme>/template/<module>/tênTemplate.phtml

 

Understanding more about magento block types

The available attributes for <block> are:

• type – This is the identifier of the module class that defines the functionality of the block. This attribute must not be modified.
• name – This is the name by which other blocks can make reference to the block in which this attribute is assigned.
• before (and) after – These are two ways to position a content block within a structural block. before=“-” and after=“-” are commands used to position the block accordingly at the very top or very bottom of a structural block.
• template – This attribute determines the template that will represent the functionality of the block in which this attribute is assigned. For instance, if this attributes is assigned ‘catalog/category/view.phtml’, the application will load the ‘app/design/frontend/template/catalog/category/view.phtml template file.

• action – <action> is used to control store-front functionalities such as loading or unloading of a JavaScript. A full list of action methods will soon become available, but in the mean time the best way to learn about the different action methods is by playing around with them in the currently available layout updates.
• as – This is the name by which a template calls the block in which this attribute is assigned. When you see the getChildHtml(‘block_name’) PHP method called from a template, it is referring to the block whose attribute ”as” is assigned the name ‘block_name’. (i.e. The method <?=$this->getChildHtml(‘header’)?> in the a skeleton template correlates to <block as=“header”> ).

•<reference> is used to make reference to another block. By making a reference to another block, the updates inside <reference> will apply to the <block> to which it correlates .
To make the reference, you must target the reference to a block by using the ”name” attribute. This attribute targets the <block> tag’s ”name” attribute. Therefore, if you were to make a reference by <reference name=“right”>, you are targeting the block named <block name=“right“>.

Example:

+ understand the meaning of <block type="page/html" name="root" output="toHtml" template="example/view.phtml"> in Magento.  ==> type="A/B".

+The A is a module’s alias. In this case page is short for Mage_Page_Block (it is defined in app/code/core/Mage/Page/etc/config.xml if you want to see).

+The B is the class name relative to the alias, initial letters of each word are capitalised. In this case html becomes Html and is appended to the resolved alias, so it is Mage_Page_Block_Html. This is probably found in the file app/code/core/Mage/Page/Block/Html.php because class names translate directly to locations in Magento.

Were you using a model alias instead of a block alias then page would be Mage_Page_Model instead. The same thing happens for resource models and helpers too. Your own module will need to define these in it’s config if it is to have blocks, models and helpers.

understanding more about magento block types following are some built-in block types which are widely used in layout.

  1. core/template: This block renders a template defined by its template attribute. The majority of blocks defined in the layout are of type or subtype of core/template.
  2. page/html: This is a subtype of core/template and defines the root block. All other blocks are child blocks of this block.
  3. page/html_head: Defines the HTML head section of the page which contains elements for including JavaScript, CSS etc.
  4. page/html_header: Defines the header part of the page which contains the site logo, top links, etc.
  5. page/template_links: This block is used to create a list of links. Links visible in the footer and header area use this block type.
  6. core/text_list: Some blocks like contentleftright etc. are of type core/text_list. When these blocks are rendered, all their child blocks are rendered automatically without the need to call thegetChildHtml() method.
  7. page/html_wrapper: This block is used to create a wrapper block which renders its child blocks inside an HTML tag set by the action setHtmlTagName. The default tag is <div> if no element is set.
  8. page/html_breadcrumbs: This block defines breadcrumbs on the page.
  9. page/html_footer: Defines footer area of page which contains footer links, copyright message etc.
  10. core/messages: This block renders error/success/notice messages.
  11. page/switch: This block can be used for the language or store switcher.

This is a list of only commonly used block types. There are many other block types which are used in advanced theme implementations.

e commerce magento tutorial beginner

Designer in Magento:

  1. Structural blocks
  2. Content Blocks
  3. Layout

Structural blocks: Đây là những khối cấu trúc tạo ra cho mục đích duy nhất chỉ định cấu trúc trực quan đến 1 trang lưu trữ như là header, left column, main column and footer (These are the building blocks created for the sole purpose to specify the visual structure => as header, left column, main column and footer).

Content blocks: Đây là những khối sản xuất nội dung thức tế trong mỗi khối cấu trúc. Chúng là đại diện của các chức năng của mỗi tính năng trong 1 trang và sử dụng các tập tin mẫu, để tạo ra XHTML được chèn vào khối cấu trúc mẹ của nó. (A content block displays the generated XHTML provided by Magento for any given feature. Content blocks are used within Magento structural blocks).

Layout (Bố trí): Xác định cấu trúc của các khối cho các trang khác nhau trong Magento Store của bạn trong các tập tin XML (.xml). Chúng được đặt tại vị trí: app/design/frontend/
interface-name/theme-name/layout/ directory. (Layouts define the structure of blocks for different pages in your Magento store in XML (.xml) files. A theme’s layout files are located in the app/design/frontend/interface-name/theme-name/layout/ directory).

THEMES

Skin:

=> css

=> images

=> js

app/design/frontend/your-name-package/your-name-themes:

=> layout

=> template

=> locale

Triển khai MVC 3 trên IIS 6.0 và Windows Server 2003

LungTung-Blog

Thứ năm, ngày 11 tháng tám năm 2011

Triển khai MVC 3 trên IIS 6.0 và Windows Server 2003

Cài đặt IIS, .Net Framework và MVC 3
Để triển khai một ứng dụng Web MVC 3 trên IIS 6.0 thì phải tiến hành cài đặt các ứng dụng như sau.
IIS 6.0
IIS 6.0 được tích hợp sẳn trên bộ cài Windows Server 2003 và chúng ta không thể cập nhật II 6.0 lên IIS 7.0 khi đang sử dụng hệ điều hành Windows Server 2003.

Để cài đặt IIS 6.0 thì vào Control Panel-> Add or Remove Programs, tiếp tục chọn Add/Remove Windows Components

Hình 1: chọn Application Server
Khi hộp thoại Windows Components Wizard hiện ra thì chọn Application Server và chọn Detais như hình 1.
Hình 2: chọn Internet Information Services (IIS)
Sau đó chọn Internet Information Services (IIS) và chọn OK.
Trong quá trình cài đặt thì yêu cầu bỏ đĩa cài Windows Server 2003 vào.
.Net Framework và MVC 3
Bước tiếp theo là tiếp tục cài đặt .Net Framework và MVC 3.
Sau khi cài đặt xong thì tiến hành đăng ký .Net Framework 4 với IIS 6.0 bằng cách thực thi aspnet_regiis.exe như hình 3.
Hình 3: đăng ký .Net Framework 4 với IIS 6.0
 
Cấu hình IIS 6.0 để thực thi ứng dụng web MVC 3
Thực hiện tùy chọn “Allow” cho ASP.NET v4 trong IIS.
Vào Administrative Tools -> Internet Information Services (IIS) Manager, sau đó chọn tiếp Web Service Extensions, thực hiện tùy chọn “Allow” theo hình 4.
Hình 4: Tùy chọn Allow cho ASP.NET v4
Cấu hình để IIS thực thi được kiểu URL mặc định của MVC 3
Thông thường sau khi tạo ứng dụng Web Site trên IIS cho MVC 3 và cho thực thi thì chúng ta sẽ gặp lỗi 404, là do IIS chưa được cấu hình định tuyến giữa các lớp M,V,C từ URL truyền vào, do đó IIS sẽ không thể tải lên được trang web mà chúng ta cần thực thi.
Chúng ta tiến hành bước cấu hình sau cho IIS.
Chọn Properties của ứng dụng Web trên IIS, sau khi hiện ra hộp thoại Properties thì chọn tiếp thẻ “Home Directory -> Configuration…” như hình 5.
Hình 5: chọn Configuration… cho mục web

Tiếp tục chọn thẻ Mappings, nếu tập tin aspnet_isapi.dll chưa được chọn thì chọn tiếp Insert… như hình 6.

Hình 6: Thêm vào file cấu hình mapping
Nhập đường dẫn tập tin aspnet_isapi.dll của phiên bản .Net Framework vào, trong ví dụ này sử dụng .Net Framwork 4.0.30319 trên Windows 64 bits thì đường dẫn là C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll. Nhớ không chọnVerify that file exists”, sau đó chọn OK như hình 7, và chọn OK như 8 để hoàn thành.
Hình 7: chọn tập tin aspnet_isapi.dll
Hình 8: hoàn thành việc thêm vào tập tin aspnet_isapi.dll
Việc cấu hình đã hoàn tất. Từ máy trạm bạn gõ vào địa chỉ của máy chủ vừa cấu hình để kiểm tra việc thực thi trang web MVC 3, kết quả sẽ như hình 9.

Hình 9: giao diện mặc định ban đầu của ứng dụng web MVC 3
Thành phố Hồ Chí Minh, ngày 03/03/2011
Lê Đức Tâm – http://begin.vn

 (Nguồn http://leductam.blogspot.com/2011/03/trien-khai-mvc-3-tren-iis-60-va-windows.html)

Basic Facts About C++Builder

Basic Facts About C++Builder

This chapter and the next five cover the basic facts that everyone needs to know about BCB. Important subjects included in this chapter are as follows:

  • The IDE
  • Projects
  • The VCL
  • Core VCL Technologies: Components, Properties, and Events
  • Stepping into the Pascal source for the VCL
  • The syntax of the VCL
  • An overview of the component architecture
  • A brief look at Run Time Type Information (RTTI)

This introduction to BCB continues in the next chapter, where I cover the new Borland additions to the C++ language and introduce several key BCB classes such as AnsiStrings and Sets. These special classes emulate features of Object Pascal. Then, in Chapter 4 you will have a look at events. Chapter 5 focuses on exceptions, Chapter 6 on using Delphi code in BCB, and Chapter 7 on graphics programming with the VCL. The latter chapter will complete my introduction to the VCL and to the syntax that is common to almost all BCB programs.

When reading this chapter, you need to remember the basic philosophy of this book. My goal here is not to plumb the depths of C++, the VCL, the Windows API, or any other hardcore technical syntax. Instead, I want to show you how to quickly build real-world applications without losing touch with the underlying Windows architecture. In these chapters there are many times when I make a pass over very complicated subjects such as C++ constructors, templates, rules of precedence, the GDI, and so on. I am, of course, aware that these are sticky subjects that take many pages to cover appropriately. However, you will not find in-depth discussions of these subjects in this book, for there are many other volumes dedicated to those topics. Furthermore, the goal of this book is to show how to use RAD tools and high-level objects to perform complicated tasks quickly, easily, and safely.

C++ is already at least 10 times faster than interpreted languages such as Visual Basic, Java, or PowerBuilder. If slowing down performance by five percent yields a 10- or 20-fold increase in reliability or ease of use, I think it is worth it to play a somewhat more cautious game. In my opinion, it is better to be nine times faster and nearly as safe as an interpreted tool than it is to be 10 times faster and 10 times more dangerous than an interpreted tool. That last 5 or 10 percent that you can eke out of the language by using every imaginable trick just isn’t worth it, except in a few unusual circumstances such as compilers, operating systems, and game engines. Even in those cases, it is still probably best to use the relatively small and fast OOP-based techniques outlined in this book.

This particular chapter is unique in that it covers a number of technical subjects that are not very complex. Everything in this chapter is here because I either

  • Think that you have to know it in order to complete BCB programs.
  • Think that you have to know it in order to feel at all comfortable inside the BCB programming environment.

Don’t worry if you find most of the material in this chapter a bit too simplistic. There is some basic material that almost has to be covered in a book of this type, and once I get it out of the way, I will move on to more interesting subject matter in the next chapters.

It’s time now to get started with an overview of the BCB environment, project management, the VCL, and the basic syntax used by BCB programmers. When you have completed this and the next five chapters, you should have all the knowledge you need to begin a robust, broad exploration of the all the exciting features found in BCB.

Creating C++Builder Projects

C++Builder has a project manager that you can access from the View menu. You can use this tool to add files to a project or to remove files from a project.

You can add files with the following extensions to your project, and C++Builder will compile and/or link them automatically, as shown in Table 2.1:

Table 2.1. Files you can add to a BCB project.

Type of File Description
CPP C++ Source module. OWL and MFC are treated in the appendices.
C C Source module.
PAS Any Pascal module that will compile in Delphi 2.01.
RC Resource script.
RES Resource file.
OBJ Compiled C++, C, or PAS file.
LIB C or C++ Library file.
DEF Module Definition file.

You can use the IDE to produce Windows executables, DLLs, or console applications:

  • To create a 32-bit Windows executable, you need do nothing. The default behavior of the IDE is to generate this type of executable.
  • To create a DLL, go to File | New and select DLL from the Object Repository. Click OK. Everything else is automatic. I discuss exporting functions from a DLL in several later chapters of the book, including Chapter 26, “Extending an Internet Server with ISAPI.” In a word, the key to exporting a method from a DLL is to use __declspec(dllexport), as described in Chapter 26 and in the comments at the top of the file ISAPI1.CPPfrom Chapter 26.
  • To create a console application, go to the Options | Project | Linker option from the menu and select Console Application. If you want to create a console application as a quick way to produce text output, you should consider using the BCB TMemo, TListBox, and TEdit controls instead. I can output text to these controls at least as quickly as I can use printf or cout. I find these components are more useful than outputting text to the command line, because they support concepts like scrolling and saving their contents to a file or the clipboard.

NOTE: Delphi programmers need to remember that it is not enough merely to #include a C, CPP, or PAS file in a module of an existing project. You also have to add the file itself to the project using the Project Manager or the Add to Project menu choices or speed buttons. Delphi’s linker assumed that you would not reference a file from your uses clause unless you wanted it to be part of your project. C++Builder, for better or worse, makes no such assumption. You must incorporate the new file into your makefile listing, or it will not be linked into your project.


BCB projects are managed in a standard C++ makefile. The easiest way to get something into your makefile is through the project manager. Editing the makefile itself is not recommended, but C++ experts will find there are some changes to your project that can only be made by editing the makefile.

Most of the important changes which can be made to a makefile are configurable through the Options | Project or Options | Environment menu choices. The developers of BCB do not expect you to find many occasions when you will need to edit the makefile. I believe the primary reason the makefile exists is that the team grew tired of trying to manage a binary project file.

The Microsoft C++ team, on the other hand, recently grew tired of trying to manage a text-based project file! This is probably one of those cases where developers have a choice between two evils.

If you are trying to manage projects that consist of multiple executables and DLLs, you will almost certainly find the current BCB project manager inadequate. Borland C++ 5.02 will support compiling C++Builder projects. You will therefore want to consider using the advanced tools in BC 5.02 for managing huge projects.

BC5 also supports a powerful scripting language not available in BCB. As a result, I think some programmers will find a combination of BC5 and BCB to produce the ultimate C++ programming environment.

Having made my pitch to that special group of programmers who are managing massive projects, I want to end this section by stating that I find BCB includes everything I need and considerably more. The goal of this book is to talk about completing high quality projects as quickly and efficiently as possible. If that is your goal, stick with BCB and with third-party tools tailored for this environment. BCB is the ideal tool for creating C++ applications. It is state of the art and leagues ahead of any other C++ environment that is planned or available at the time of this writing.

BCB File Extensions

In the last section, in Table 2.1, I list the types of files you can include in C++Builder. Most of these files will be generated automatically for you by the compiler, and I list them here just so you will know why they exist and what they do. In this section, I will talk about all the important files that become part of your project. Table 2.2 lists the key extensions in BCB projects.

Table 2.2. File types used in a BCB project.

File extension Description File type
RC Source for resource file. Text
RES Resource file. There will usually be
one RES file with the same name
as your project that contains only an icon.
It’s best to leave this file alone.
Binary
CPP, C C++ source file. Text
PAS Delphi 2.01 source file. Text
H or HPP C++ header file. Text
DSK The location of files on the desktop. Text
DFM Binary file containing form definition. Use
CONVERT.EXE to translate into text.
Binary
MAK The project makefile in text format. Text
TDS Turbo debugger symbols. Binary
ILX Incremental linker symbols. Binary

When browsing this table, take special notice of the incremental linker files. These four files, all of which begin with ILX, are huge. They are the key files needed to produce BCB’s incredibly fast link times. If you turn incremental linking off, as explained later in this chapter, these files will disappear. Turning incremental linking off means that your compilation and link times will be from 5 to 10 times slower than they are with incremental linking turned on.

The include and lib Directory Paths Issue

There are, confusingly enough, two places where you can designate the paths to your include and lib files. One is located in the Options | Project | Directories/Conditionals menu choice, and the second is located in the Options | Environment | Library section. These pages are shown below in Figures 2.1, 2.2, and 2.3. There is also a Path for Source option in the Options | Environment | Preferences page.

FIGURE 2.1.The Options menu leads to the Project dialog where you can set paths for your project.

FIGURE 2.2.The Options menu is also the gateway to the Environment dialog where you can find the Library page.

FIGURE 2.3.The Options | Environment | Preferences page gives you a place to add the path to modules you include in your projects.

The BCB macro shown in the Path statements from Figures 2.1 through 2.3 resolves into the path that points to your current installation of BCB. This kind of information is stored in the Registry under HKEY_CURRENT_USER/Software and HKEY_LOCAL_MACHINE/Software. For instance, see the RootDir entry in HKEY_LOCAL_MACHINE/Software/C++Builder/1.0. To view the Registry, select Run from the Windows Start menu, type in the word RegEdit, and press the Enter key.

As a rule, you make changes specific to one project in the Options | Project dialog, and make global changes that you want reflected in all programs in the Options | Environment dialog. Use the Path for Source to include any directories that contain utility code that you use frequently.

As a rule, additions to these various path statements are made automatically when you add modules to your project through the Project Manager. However, there are times when I need to go in and explicitly edit one of these options.

Remember that if you are adding a component to the Component Palette, you have to add the path to that component in the Options Environment dialog or the Component Palette will not load. This addition to the Path statement will be made automatically if you add a component from inside the IDE. If you add the component from the command line by recompiling CMPLIB32.CCL, you need to update the Library path statement yourself. If you are using DLLs from inside a component, make sure the DLL is in a directory that is on your global DOS/Windows path. For instance, you might consider putting it in the Windows or Windows/System directory.


NOTE: The question of whether to call a C module used in a BCB program a unit or a module is something of an open matter in BCB. My inclination is to call it a module, because that is traditional C usage, but BCB seems to refer to them as units. To be utterly frank, this is the kind of issue that doesn’t really grip me all that deeply. In particular, I’m sure you will have no trouble understanding me regardless of which term I use. As a result, you will hear me referring to C modules as either modules or units, depending more on whim than on any clearly defined practice.


Working in the IDE

Here are some tips for working inside the IDE. I’ll make this section brief, because I don’t want to waste time on issues like this in a book that is clearly aimed at experienced programmers. However, this is a new environment, so it might help to share a few tips.

Whatever you do, be sure that you understand that this tool is meant to be used from inside its IDE. This is not a command-line environment!


NOTE: I’m sure that most programmers who investigate the matter will see that the command-line approach makes no sense with BCB. If that sentence strikes a sour note with you, all I ask is that you don’t develop contempt prior to investigation. In my opinion, this IDE has something so powerful to offer that it finally makes command-line programming obsolete. With BC5, and even with MSVC, I usually worked from the command line. I was one of the last of the hardcore C++ command-line junkies. With BCB, however, I have changed my ways. I’m totally sold on this environment, and would never consider going back to the command line except for a few rare situations.


Tips on Manipulating Controls

Here are a few tips for using the visual tools. If you are new to the environment, you should boot up BCB and follow along when reading these time-saving tips.

When dropping controls on a form, do the following:

  • Use the Control key plus the arrow keys to move the location of a component one pixel at a time. In particular, drop a component such as a button on a form. Select it with the mouse. Hold down the Control key and press the left arrow key.
  • Repeat the previous steps, only this time use Shift plus the arrow keys to resize a component one pixel at a time.
  • Hold down the Shift key when selecting a component from the Component Palette if you want to drop multiple copies of it on a form without having to go back to the palette. Click the “arrow” icon at the far left of the Component Palette to break out of this process.
  • Become familiar with the Align and Size options from the Edit menu. Quickly drop down five buttons on a form, each one below the last, without taking the time to carefully align their right and left sides. Hold down the Shift key, then select the five buttons with the mouse by clicking on each one. Now use the Align dialog from the Edit menu to align the tops or sides of all the selected controls.
  • A second technique for selecting a large group of components is to click on the main form and hold down the left mouse button while dragging over several components. When you let up the mouse, you will find the components are selected. If the components you want to select are resting on top of another control such as a panel, then you will need to hold the control button down before and during the process of dragging the mouse.
  • When designing a form, make it as large as you want. Then, when you have everything in place, use the Scale option from the Edit menu to make your form as small as you want, while still keeping each item on the form in proportion.
  • Right-click a component, as shown in Figure 2.4, to bring up a menu that lets you change the size, alignment, tab order, scaling, or creation order.

FIGURE 2.4.Right-clicking a TTable object to bring up a list of custom options.

Making the Most of the IDE

Here are some tips on using the IDE:

  • Right-click almost anything in the IDE to bring up a menu to configure the component or tool you are currently using. For instance, right-click the colorful toolbar at the top left of the environment. Select Properties from the popup menu to bring up the Toolbar editor. Now drag a colorful button from the toolbar onto the Toolbar editor and grab another button from the ToolBar editor and drag it back onto the toolbar. I sometimes delete the Trace, Trace Into, and Pause buttons from the extreme right of the toolbar and replace them with Copy, Cut, and Paste buttons or with compiler buttons such as Make, Build All, and Compile Unit.
  • Notice that you can view the Project Source and Project Makefile from the View menu. The project source contains the WinMain() or main()block for your code. You rarely will need to edit this code. Its contents are usually configurable from the Options | Project menu. For instance, you can change the code in the Project Source from Forms page of the Options | Project menu.
  • Check out the Project Manager, which is also available from the View menu.
  • You can lock the controls on a form by selecting the Lock Controls option from the Edit menu. If you have spent some time working on a form and want to be sure that it does not accidentally get changed, select Lock Controls, and it will not be possible to move the controls on that form, or any other, until you deselect this option.
  • If you want to set the text on the editor page to read only, right-click the editor page and select Read Only.
  • Watch out for the AutoScroll property of a form. This is set to true by default, but it can bite back if you leave it set to true and then move your form to a different resolution. For instance, if you move from 640×480 to 800×600, you generally should not have AutoScroll set to true, especially if your form is populated with a large number of controls.
  • Use the Positionproperty of a form to decide whether the form should first appear in the screen center, at the place where you positioned it at design time, or at a default location defined by Windows.
  • Use the WindowStateproperty of a form to decide if the form should first be shown minimized, maximized, or at normal size.
  • Before releasing a project to the public, check out its appearance in at least three different resolutions and make sure at least one of them toggles the font from Small Fonts to Big Fonts, or vice versa. (You can change Windows’ resolution by right-clicking the desktop, selecting Properties, and going to the settings page. Make sure you change between Big Fonts and Small Fonts during your testing!)

Project Options

Other than the path-related issues covered in the last section, there are only a few options that you need to know about when programming BCB. The rest of the setup-related issues are handled for you automatically by the environment.

All the options you choose in the Project and Environment dialogs are written to the Registry. If you want to write custom programs that change these settings, you can learn how to proceed by reading the sections on the Registry in Chapter 13, “Flat-File, Real-World Databases.”

The Options | Project menu has six pages in it:

Forms: This page is discussed in depth later in the chapter when I discuss the Project Source file for the ShapeDem program in the section called “Creating Forms.” The core functionality on this page addresses the question of which unit will be the main module for your application–that is, which will come up first when you start the program. A secondary issue addressed in this page is whether a form will have memory allocated for it automatically at startup, or whether you want to create it explicitly at some point during your application’s runtime. Forms listed in the left-hand list box shown on this form are created automatically; those on the right-hand list box must be created explicitly by the developer. The following code will create a form, show it to the user, and delete it:

Form2 = new TForm2(this); Form2->ShowModal(); delete Form2; 

This code would not work unless the header for Form2 was included in the module that wanted to call the code quoted here:

#include "unit2.h" 

Application: This is where you can set up the icon or help file for your project. This is an intuitive process; click Help in the dialog if you have questions.

C++: This is where you can set the Debug and Release compiler options. You can also do a small amount of fine-tuning here, but this book hardly ever steps beyond recommending that you use the Debug option in development and the Release version when you ship. I almost never have occasion to do more than choose the simple binary Debug or Release option, except for occasionally toggling the precompiled headers option.

Pascal: Here is where Pascal aficionados can fine-tune their code. I would recommend leaving all these options untouched unless you have a specific reason to change them. If you want to get involved in this page, the first level of advice is to turn Range and Stack checking on only during debug cycles, and to turn Optimizations on only when you ship.

Linker: This is where you can decide to produce a Windows or console application, an EXE, or a DLL. This is also the place where you can toggle the incremental linker on and off. In development, you probably want the incremental linker on to speed compilation; when you ship, you should test the size of your executables when it is off and when it is on, and ship the smallest version.

Directories / Conditionals:
The key features of this page were covered earlier in this chapter. Note that this is also where you can define conditionals. The whole subject of unit aliases is an Object Pascal-specific issue that enables you to create an alias for the name of a unit. For instance, the 16-bit version of Delphi kept all the Windows API calls in a unit called WinProcs.pas, and all the Windows types in a unit called WinTypes.pas. When the big segment sizes of 32-bit Windows became available, the two units were consolidated into one called Windows.pas. To remove the burden of having to change the uses (#include) statements in a Pascal file, the developers enabled you to create aliases. The most common alias told the compiler to use Windows.pas whenever it saw a request to include the WinTypes or WinProcs units.
As you can see, I don’t put a lot of weight on fine-tuning the settings for your project. If you flip through these pages and see the small number of options available, you can see that the developers were not very concerned about this issue either. One of the major goals of BCB is to make C++ once again a mainstream language. The programming world used to be much simpler than it is today. Now we are all expected to know about OLE, MAPI, the Internet, multimedia, or other cutting-edge technologies. I invest my time in learning these valuable new technologies, and ask little more of my compiler than that it link quickly and easily and automatically produce small, tight code.

Environment Options

There are six pages in the Options | Environment menu choice. I play with many of these options all the time because they do nothing more than tweak the appearance or feel of the IDE. You aren’t going to accidentally mess up the link process in your program or add 500KB to the size of an executable by tweaking one of these options. Feel free to set them as you please. Following is a list of the options I often play with during development.

There are some choices that are listed in both the Environment Options pages and the Project Options pages. If you make a change in the Project pages, you are changing an option for just that one project, while if you make the change in the Environment page, you are changing things globally for the entire environment. Local options always override global options.

Preferences: In the preferences page I always set Show Compiler Progress to true so that I can tell how far along I am in the compile cycle. I set AutoSave to true for Desktop files so that the environment will remember which project I was working on and which files I had open in the IDE. I also frequently tweak Break On Exception, depending on whether or not I want to catch problems in my code (turn it on) or just test to see if exceptions are popping up as expected at runtime (turn it off). This is also where you can turn integrated debugging on and off and change the path, as described above in the section on setting the project path.

Library: This is where you can set the path for include and lib files, as described previously. You can also globally decide for all projects whether or not to use the incremental linker. If you are adding components to the Component Palette, you should set Save Library source code to true so that you can build the Component Palette from the command line to save time or to repair a damaged Component library.

Editor: I discuss this page in a later section called “Feeling at Home in the IDE.” It is here you can customize the behavior of the editor. All the major third-party editors (CodeWright, SlickEdit, MultiEdit) have some customizations for BCB, but none of them can get into the environment to the degree to which you, I, and they would like. Hopefully, improvements will come in this area in later releases.

Display: I discuss this page in a later section called “Feeling at Home in the IDE.” It is here you can choose the keystroke emulation and font for the editor.

Colors: I discuss this page in a later section called “Feeling at Home in the IDE.” It is here you can customize the colors of the editor. It particular, it enables you to switch between different color schemes or customize the colors for each element in the language, such as string, identifiers, integers, and so on. Like all the settings mentioned in these pages, the results of your decisions are written to the Registry. The Address2 program from Chapter 13 shows how you could write custom programs that tweak the Registry. For instance, you could write a program that automatically switched between four or five additional color schemes.

Palette
: If you want to change the order in which components appear in the Component Palette, this is the place to make your changes. You can also reach this page by right-clicking the Component Palette and choosing Properties. It is pretty hard to do any serious damage to the environment using this page, but if you feel you need help, just press the Help button on the dialog itself.
As you can see, most of the options on these pages address only aesthetic or habit-based preferences regarding how the IDE works. From a development point of view, the key issues involve incremental linking, paths, and saving the source for the Component Palette. Make sure you understand those important issues before moving on to the next topic of discussion.

Feeling at Home in the IDE

To help make the IDE comfortable, you might go to the Options | Environment | Editor page, shown in Figure 2.5.

FIGURE 2.5.The Options menu gives you access to the Environments dialog where you find the Editor page.

From the Editor page you can make the following changes:

  • Turn the Use tab character option on or off, depending on your liking. (I prefer to turn it off, so I always know exactly what my code will look like regardless of the editor or tab settings I happen to use.)
  • Decide what tab stops you want to use. I set mine to 3 and 5.
  • Choose the Editor speed setting you want. You can choose between the Default keymapping, IDE Classic, Brief, or Epsilon emulations.
  • Go to the Colors page and set the colors you want to use in the editor.
  • Consider setting Undo after save to true so that you can keep a buffer of changes you make, even after you save.
  • There are third-party tools such as SlickEdit, MultiEdit, and CodeWright that have some degree of integration with C++Builder. However, none of these products is able to work as closely with the IDE as one would like, due to limitations in the current Tools API for BCB.

Converting Forms to Text and Back

Everything you can do in BCB through the visual tools you can also do in code. The visual tools are just a means of expediting the programming process. They do not supplant code, they complement it. This is what the Borland marketing department means when they talk about “two way tools.” The are two different ways to approach some parts of a BCB project: in code or by using the RAD tools.

If you right-click a form, you can select the View as Text menu item to convert a form to text. To convert back, just right-click the text version of the form.

BCB also ships with a command-line utility called Convert that will convert DFM files to text, or text to DFM files. At the command line type either

convert MyForm.dfm 

or

convert MyForm.txt 

The first example converts a binary form to a text form with the extension TXT, and the second example reverses the process.

If you have 4DOS on your system, you can use the following command to convert all the DFM files in a branch of subdirectories from DFM to text files:

global /I convert *.dfm 

This command will iterate through all the subdirectories below your current position and convert all the files in those directories to text. If you are concerned about archiving files, this is a good way to proceed. In particular, a text file is a much more robust storage medium than a binary file. If you lose one byte of a binary file, it may become worthless. Losing one byte from a text file rarely causes any serious mischief.

If you have one form and want to paste all or part of it into a second form, you can select multiple objects from the first form, choose Edit | Copy, focus the second form, and then paste the selections from the first form into it. If you want, you can have an intermediate step where you paste the items from the first form into a text editor such as Notepad, edit them, and then paste them onto a form.


NOTE: If you are a Delphi programmer and want to port a form from Delphi to BCB, you might consider using the technique outlined in the last paragraph as a way to proceed. Of course, you can compile your Delphi forms directly in BCB, but if you want to port them, just cutting and pasting to the clipboard is a good way to proceed.


Here is a what a BCB button looks like in text form:

object Button1: TButton Left = 96 Top = 16 Width = 75 Height = 25 Caption = `Button1' TabOrder = 0 end 

To get this code, I Alt+Tabbed out of my word processor over to BCB, selected a button on a form, and chose Edit | Copy from the menu. I then Alt+Tabbed back to my word processor, and chose Edit | Paste. During the process the Windows button was automatically converted to text.

Here is a second version of the button code that has been slightly modified:

object MyButton: TButton Left = 1 Top = 16 Width = 75 Height = 25 Caption = `My Button' TabOrder = 0 end 

As you can see, I have changed the name of the button from Button1 to MyButton, and I have changed the Caption and Left properties. Now I can select this text in my word processor, Alt+Tab over to BCB, select and form, and choose Edit | Paste to paste it back into the form. However, this time it has a new name, a new location, and new caption.

This is what is meant by a two-way tool. I can edit the form using the visual tools, or I can edit it in a word processor. It works in two different ways, depending on my current needs.


NOTE: When working with forms, remember that the currently selected component will be the target for a Paste operation. For instance, if I have selected a TButton object, and I chose Paste, BCB will attempt to make the control currently in the clipboard into a child of the button. In most cases, this is not what I want. Instead, I should first select a form or a panel, and then paste the controls onto that object. You also want to make sure the object you are pasting into is big enough to receive the control or controls you are about to dump from the Clipboard.


You have now made it through the first section of this chapter. In the next section I am going to switch my approach from a “hot tips” format to a more discursive style. If you want more information of the type you have seen so far in this chapter, you should look in the online help or pick up a book aimed at introductory BCB programming issues. Everyone has to know the kind of information I have been ladling out in the last few pages, and indeed it is vital information, but it is not the subject matter of this book. I have included this much only because I feel many of the issues addressed here are not immediately obvious when you first open BCB, and yet you absolutely have to know these facts in order to get any serious work done in the environment.

Core Technology: Components, Properties, Delegation

Many people are confused about Borland C++Builder. They are not used to the idea of having a RAD tool that works with C++, and they don’t know quite what to make of it when they see it.

Some people think they are seeing a code generator; others think this is a visual tool meant for programmers who don’t want to write code. Some people think they have found a great tool for building databases, and others a tool for prototyping applications.

There is some truth to all of these ideas, yet they all miss the mark if your aim is to find the essence of Borland C++. The core pieces of the technology are threefold:

  • Right up front you have components and properties.
  • Buried a little deeper you have the delegation model, which involves events and method pointers.
  • Tying the whole picture together are exceptions and a very sophisticated RTTI system located in a file called TYPINFO.HPP. Most programmers rarely have to deal with this side of the product, but it is part and parcel of what makes the tool possible in its current form.

These are things that lie at the core of C++Builder. Don’t let anyone else lead you astray with tales about prototyping or about BCB being a replacement for PowerBuilder. This tool may in fact perform those roles at times, but that’s not what it is all about.


NOTE: It may be that from a commercial perspective the majority of programmers will find the database support to be the most valuable aspect of this tool. Indeed, I spend a large portion of this book covering that subject. However, the emphasis on databases is market-driven, while the technological core of the product lies elsewhere.


To get at the heart of BCB, you have to understand components, you have to understand the delegation model, and you have to understand RTTI. In particular, the first two points are essential to an understanding of how this product works, while the third helps you understand why it works.

You have, no doubt, either already noticed or have heard talk about the fact that BCB has some proprietary extensions to C++. These extensions are there to support the creation and use of components as well as the associated concepts of properties and events that make components so powerful.

There was no way to create a product like BCB without extending C++ to support components, properties, and the delegation model. This is a better way to program, and there is no force in the world that can suppress it. I have no problem at all asserting that in five years time, all compilers will support these features and most programmers will use them by two years from now (1999).

Let me say it one more time, because this is so crucially important: What’s key is the component model, and its reliance on properties and events. Components, properties, the delegation model. Those are the things that stand at the heart of this technology. The three tools make it easy to build databases or multimedia applications. To say that the tool is primarily about building games or databases or Web sites is putting the cart before the horse. The tool is primarily about components, properties, and the delegation model. The other strengths of the tool fall out more or less automatically once the ground work has been laid.

Why the VCL?

Now that you know something about the environment in which BCB exists, it’s time to dig a little deeper and start examining the VCL object model used by BCB. The VCL (Visual Component Library) is an object-oriented library designed to ease the process of creating visual components.


NOTE: When I say that BCB uses the VCL, I mean for the phrase to be taken in at least two distinct ways. BCB uses the VCL in the sense that the physical IDE is literally built into VCL, and also in the sense that we, as BCB programmers, use the VCL as the object model of choice when creating applications. Borland is not asking you to do anything they wouldn’t do. Delphi is built into the VCL. BCB is built into the VCL, and much of Latte, the new Java product, is built into the VCL. The VCL is the tool of choice for people who have a choice.


Many C++ programmers who come to C++Builder find themselves wondering why the VCL exists in the first place. What was wrong with OWL or with MFC? Why should there be yet another object framework?

The simple answer is that visual programming, RAD, needed a whole new framework with new features. RAD relied on new concepts such as event handlers, properties, property editors, components, component editors, experts, forms, and a slew of other features. The language that implemented these new syntactical elements also desperately needed improvements in the areas of streaming, string handling, object initialization, and referencing.

These features simply were not available in either the C++ or Object Pascal versions of OWL. As a result, a new framework was created that supported these features; it is called the VCL, or Visual Component Library. The name goes a long way toward explaining why OWL could never do this job correctly. This is an object-oriented library built around visual components, and visual components do not have even the most oblique reference anywhere in OWL or MFC. They are a completely new entity and required their own object-oriented framework.

Having said that, I should add that VCL is closely related to OWL, just as the current version of OWL is closely related to the 16-bit version of OWL from which it grew. If you know OWL, you will find much in VCL that is familiar. Indeed, even MFC is a good background for understanding VCL. However, this is a fundamentally different kind of beast, one that is built around a new concept called a visual component.


NOTE: I am aware, of course, that ActiveX is another specification for building visual components. The difference between ActiveX and the VCL is that the VCL is specifically designed to be used with advanced programming languages such as C++ or Object Pascal. ActiveX was designed to be used in a broader context featuring a wide variety of languages and operating systems. ActiveX is more powerful than VCL, but it is also much bigger, much more complex, and slower.


Come on Charlie, Tell Us What You Really Think!

To conclude this brief introduction to the long discussion of the VCL found in this chapter, I feel it is important to explicitly state that I am aware that many hardcore C++ programmers will not automatically greet the VCL and its occasional bits of nonstandard C++ syntax with open arms. When writing this chapter, I am conscious of the need both to explain the VCL and also explain exactly why the VCL exists.

I want to make it absolutely clear that I am one hundred and ten percent committed to the VCL, and have absolutely no doubt that it represents the correct model for programming at this point in the ongoing development of programming tools and languages. I will occasionally make statements that explicitly justify some part of the VCL in the face of possible criticisms. These statements do not in any sense represent doubts in my own mind about the VCL. I prefer the VCL to OWL or MFC, and I am absolutely certain that all of the extensions to the C++ language that it introduces are necessary and represent significant advances for the language.

I am aware that some readers have large quantities of legacy OWL and MFC code that they want to preserve. I do not take those needs lightly and feel it is my obligation to state explicitly why changes have been made to the C++ object model.

BCB enables you to use MFC and OWL code, but the only reason this feature exists is to support legacy code. I personally do not think either MFC or OWL is the best choice any longer for large programming projects. The introduction of the component, property, delegation model offers such vast improvements in our ability to write code, that I don’t think it’s advisable to use OWL or MFC simply because of the poignant weight of all that legacy code. This is not a criticism of these two excellent object frameworks. The point is simply that they do not support components, properties, or events, and without that support they don’t meet my current needs. I simply cannot imagine that anyone else who works with BCB for any significant length of time could possibly come to any other conclusion.

I am aware, however, that this is a controversial subject. Over the last four years I have been using the VCL almost every day. Most of that experience is on the Delphi side, but I have also been using BCB every day for about five months at the time of this writing. During the last four years, I have had many occasions to also use MFC and OWL. After my first six months with the VCL, there was never one single time that I went back to MFC or OWL without the feeling that I was using a much loved but outdated system that lacked fundamental features that have become a necessary part of what I believe to be the best contemporary programming model. This doesn’t mean that I won’t use OWL or MFC if it has an existing object in it that I find appealing, but only that I prefer to use the VCL if that is an option. Indeed, in most cases, the VCL provides for all my needs.

If a language or programming model does not support properties, components, and delegation, I personally find it lacking, no matter how many other fine features it may present to the user. VCL is the right way to go, and it would be absurd to read my occasional arguments on its behalf as representative of doubts in my own mind about this programming model.

In a sense, my job would be easier if the VCL were harder to use. It was very frustrating for me to realize that I had to start leaving behind all that detailed knowledge about OWL in order to use a system that was so much simpler to use. Wasn’t there something that the VCL was missing? Didn’t there have to be a catch? How could the solution to so many long-term problems turn out to be so simple?

Well, I believe that there isn’t a catch, and that the VCL does everything that OWL or MFC did, but does it better. Of course, OWL and VCL have tremendous depth in terms of the number of objects in the existing hierarchy, which is one reason why BCB supports them. You will find, however, the VCL has great depth itself, and that it supports all the features, if not all the objects, found in MFC and OWL. In other words, if you find that a particular object you used in OWL is not part of VCL, you could either use the existing object or create a new one that does the same thing in the VCL. The VCL supports all the features of OWL, but may not at this time have duplicates of all its objects. Furthermore, there is tremendous existing third-party support for the VCL that helps fill in some of the gaps.

For good measure, the VCL adds some new tricks regarding components, properties, and events that can’t be found in either OWL or MFC. The kicker, of course, is that the VCL produces code that is at least as fast and small as OWL or MFC, but it is literally about ten times easier to use.

Let me give it to you straight. Back in the old days I loved OWL and thought it was the greatest thing I had ever seen. I used it all the time and was a fanatical adherent of that programming model. I now use VCL for everything, and go back to OWL or MFC only when I absolutely must. To my eyes, the support for components, properties, and events makes VCL clearly better than OWL or MFC in the same sense that OWL was clearly better than structured programming. If you love OWL, it is sad to hear this kind of thing, but components, properties, and events simply bring something new to the table that OWL and MFC simply can’t emulate.

It is hard to accept the rate at which programming languages are evolving at this time. The burden this places on programmers is immense. The size of this burden is one of the key reasons I advocate, again and again, doing things the simplest, easiest, safest way. You have enough to worry about without trying to figure out the last picayune details of constantly changing standards!

The learning curve associated with the onrush of progress, our attachment to legacy code, a sentimental attachment to the ANSI committee (of all things!)–none of these represent a reason to stick with an outdated technology when a clear advancement in programming tools appears. I obviously have a involvement with Object Pascal. But I am also a long-term (10 years) C++ programmer, and I know about the deep attachment we all feel to the rules of this language, and I know what a huge improvement OWL and MFC represent over structured programming. However, things have changed again, and personally, I love it! VCL is wonderful. I never waste two seconds thinking about going back to the old way of doing things.


NOTE: One final note on this subject ought to go out to readers of my Teach Yourself Windows and Teach Yourself Windows 95 Programming books. Those books talk about straight C Windows API programming (a la Petzold) without the aid of any object framework. I do not believe there is anything in this book that makes the material in those books obsolete.

To be a good Windows programmer you still have to know the Windows API, and you still have to know about message loops and window procedures. Indeed, in Delphi 2 Unleashed, I went on for several hundred pages about material of that sort. I do not include any of that kind of material in this book because there are many C++ programming books that cover that material. (There were not any, as far as I knew, Pascal books that covered that material, which is why I included it in Delphi 2 Unleashed.)

Needless to say, I think you ought to use the VCL to write contemporary Windows programs. However, if you also know the Windows API, you can become a great VCL programmer. If you don’t know the Windows API, you will always be at a loss when using some features of the VCL, at least until such time as Windows becomes a true object-oriented operating system. Do you have to know OWL or MFC to become a great VCL programmer? No.

Do you have to know the Windows API in order become a great VCL programmer? Absolutely! The Windows API material found in my Teach Yourself… books has a very long life from a technical, if not a commercial, point of view. Assuming that Java does not take over the world, a knowledge of the Windows API is always invaluable to a contemporary programmer.

Remember, however, that even the glorious VCL has a shadow over it, in the form of Java. I never get too bogged down in the details of the VCL or any other framework, because I never know when this rapidly changing programming world is going to enter another period of mind-numbing change in which the slow ones get left behind!

The Windows API is the only really complex programming paradigm that is worth learning in depth, because all the others are likely to change over time. The fact that the VCL is easy to use is not just a nice feature, it’s a necessity in this contemporary programming world where the only constant is change. I should perhaps add that the reason you use the VCL instead of the raw Windows API is because you get your work done in about one-tenth the time. If you use OWL or MFC, your code will be much bigger than if you use raw Windows API code, but you will also have a better chance of getting your work done on time. If you want another four- or five-fold increase in productivity, use the VCL.


Using the VCL

Now that you have heard an advertisement for the VCL, it might help to provide a few more basic examples illustrating some of the virtues of this programming system. These are very simple examples that are a bit atypical of the type of code you will see in this book, or even in the latter portions of this chapter. I feel, however, that these basic examples are useful when illustrating some of the key features of RAD programming with the VCL. Their presence ensures that everyone understands the benefits of visual programming with the VCL.

I will, however, use these basic examples to explore some fairly complex aspects of the VCL, and especially of the code that executes just before and after the main form of your application is made visible. In particular, I will look at the code in the project source file for a typical BCB application that uses the VCL.

The first program I want to discuss uses a standard Delphi component called TShape. If you open up BCB and drop the TShape component on a form, you will see that it draws a simple white rectangle on the screen.

Of course, the TShape object can perform more than this one simple trick. For instance, if you pull down the list associated with the Shape property in the Object Inspector, you see that you can easily work with ellipses, circles, squares, and other assorted shapes. Furthermore, if you expand the Brush property, you can change the shape’s color. The pen property enables you to change the width and color of the outline of a TShape object.


NOTE: Don’t forget that you can expand properties that have a plus sign (+) next to them by double-clicking the property’s name. A Color property always has a dialog associated with it. To bring up the dialog, double-click the area to the right of the Color property. This area is called the property editor. (Later in the book I will show how to create your own property editors and how to use existing property editors.) Select a color from the dialog, click the OK button, and the color you chose automatically takes effect.


As just described, it’s trivial to change the major characteristics of a TShape object at design time. However, I spoke earlier about BCB supporting two-way tools. Anything that you do with the visual tools you can also do in code. It is, of course, a little more work to make the same changes at runtime that you made at design time, but the basic principles are still simple. The SHAPEDEM and SHAPEDEM2 programs on the CD that accompanies this book show you how to proceed.


NOTE: I try to avoid it, but you might find a few places in the sample programs where I hard-code in the path to a file. You will probably have to edit these paths to get the program to run on your system.

I go to a great deal of effort to ensure that the code that accompanies this book works correctly. If you are having trouble running any particular program, check the readme files found on the CD that accompanies this book. If you still can’t get the program running, go to my Web site and see if there is an update, hint, or bug report available. My Web site is users.aol.com/charliecal.


At its core, the SHAPEDEM program consists of nothing more than a TShape object placed on a form, along with two scroll bars and a menu. What’s interesting about the program is the ease with which you can change the size, color, and shape of the TShape object at runtime.

You can find the code for the program in Listings 2.1 through 2.3. Remember that if you want to view the source for the project file in your application, you can select the View | Project Source menu item.

Listing 2.1. The code for SHAPEDEM.CPP.

#include <vcl.h> #pragma hdrstop USEFORM("Main.cpp", Form1); USERES("ShapeDem.res"); WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); return 0; } 

Listing 2.2. The header for the main unit in SHAPEDEM.

#ifndef MainH #define MainH #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <ExtCtrls.hpp> #include <Dialogs.hpp> #include <Menus.hpp> class TForm1 : public TForm { __published: TShape *Shape1; TScrollBar *ScrollBar1; TScrollBar *ScrollBar2; TColorDialog *ColorDialog1; TMainMenu *MainMenu1; TMenuItem *Shapes1; TMenuItem *ShapeColor1; TMenuItem *FormColor1; TMenuItem *Shapes2; TMenuItem *Rectangle1; TMenuItem *Square1; TMenuItem *RoundRect1; TMenuItem *RoundSquare1; TMenuItem *Ellipes1; TMenuItem *Circle1; void __fastcall ShapeColor1Click(TObject *Sender); void __fastcall FormColor1Click(TObject *Sender); void __fastcall Rectangle1Click(TObject *Sender); void __fastcall ScrollBar1Change(TObject *Sender); void __fastcall ScrollBar2Change(TObject *Sender); void __fastcall FormResize(TObject *Sender); private: public: virtual __fastcall TForm1(TComponent* Owner); }; extern TForm1 *Form1; #endif 

Listing 2.3. The code for the main unit in SHAPEDEM.

#include <vcl.h> #pragma hdrstop #include "Main.h" #pragma resource "*.dfm" TForm1 *Form1; __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Shape1->Left = 0; Shape1->Top = 0; } void __fastcall TForm1::ShapeColor1Click(TObject *Sender) { if (ColorDialog1->Execute()) Shape1->Brush->Color = ColorDialog1->Color; } void __fastcall TForm1::FormColor1Click(TObject *Sender) { if (ColorDialog1->Execute()) Form1->Color = ColorDialog1->Color; } void __fastcall TForm1::Rectangle1Click(TObject *Sender) { Shape1->Shape = TShapeType(dynamic_cast<TMenuItem*>(Sender)->Tag); } void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Shape1->Width = ScrollBar1->Position; } void __fastcall TForm1::ScrollBar2Change(TObject *Sender) { Shape1->Height = ScrollBar2->Position; } void __fastcall TForm1::FormResize(TObject *Sender) { ScrollBar1->Max = ClientWidth - (ScrollBar2->Width + 1); ScrollBar2->Max = ClientHeight - (ScrollBar1->Height + 1); ScrollBar1->Left = 0; ScrollBar2->Top = 0; ScrollBar2->Left = ClientWidth - ScrollBar2->Width; ScrollBar2->Height = ClientHeight; ScrollBar1->Top = ClientHeight - ScrollBar1->Height; ScrollBar1->Width = ClientWidth - ScrollBar2->Width; } 

In the next few paragraphs, you’ll hear a discussion of how to change the color of the form, the shape shown on the form, and the size and shape of the object itself.

When you run the SHAPEDEM program, it looks like the screen shot shown in Figure 2.6. Use the program’s scrollbars to change the size of the figure in the middle of the screen. Use the menu to select a new shape for the object and to bring up a dialog that enables you to change the color of either the form or the shape.

FIGURE 2.6.You can use the scrollbars and buttons to change the appearance of the SHAPEDEM program’s form.

The Project Source: Where Pascal Meets the C

Before getting into any details about how the ShapeDem program works, it might be helpful to take one moment to look at the project source. You can access the project source from the View menu.

This is the place in the book I have chosen to give you a close look at how Borland blended its Pascal and C++ technology into one product. I will not show much Object Pascal code in this book, but you will see quite a bit in the next few pages.

At the top of the project source you see code that brings in the VCL:

#include <vcl/vcl.h> #pragma hdrstop 

In many programs, you will not have to add any other include statements to your program other than this one, except when you want to include other units from your current project, and even that can be automated through the File Include Unit Hdr menu option. That’s not a hard and fast statement; I only mean to imply that you don’t tend to spend a lot of time adding include statements unless you want to access obscure features of the Windows API. Many other include statements will be added to your header files, however, when you drop components down on a form.

The pragma statement shown here tells the compiler that you only want to have the VCL in your precompiled headers. Any additional files should be recompiled each time you do a build or a make. This is done for the sake of efficiency, because your own header files are likely to change on a regular basis.

The next few lines tell the project manager to bring in forms or resources:

USEFORM("Main.cpp", Form1); USERES("ShapeDem.res"); 

You would not normally add this kind of code yourself, but would ask the visual tools to insert it for you. In this case, the tool you would use is the Project Manager from the View menu. However, you don’t have to use the Project Manager; you can make this changes manually if you wish, and the Project Manager will pick up on your work.

Here is the WinMain block for your program:

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); return 0; } 

As you can see, BCB assumes you won’t be using any of the parameters passed to it. In particular, the VCL provides a global variable called HInstance, which provides the HInstance for your application. The HPrevInstance variable is never used in Win32 programming. Parameters passed to your program are available in the form of a VCL function called ParamStr, and a variable called ParamCount. To get at the name and path of your executable, access ParamStr(0), to get at the first parameter passed to it, access ParamStr(1), and so on. ParamCount contains the number of parameters passed to your executable. For instance, the following code pops up a message box showing the name and path of your executable:

void __fastcall TForm1::Open1Click(TObject *Sender) { ShowMessage(ParamStr(0)); } 

NOTE: Wary C++ programmers may be concerned about the fact that the VCL has gotten at the HInstance and program parameters before they reached WinMain. Don’t worry, there is no huge subsystem underlying your entire program! However, a few things do happen before WinMain is called, just as a few things happen in all C++ compilers before WinMain is called.

This is a case where it might help to talk about how Object Pascal handles program startup. Object Pascal simply did not support WinMain on the grounds that it was faster and more convenient to perform processing of HInstance and the program parameters without setting up a stack frame for a function call. The Object Pascal system is faster and more efficient because you don’t have to push things on the stack before calling a function named WinMain. On the other hand, the overhead of calling WinMain with four parameters is hardly significant in the face of the time it takes to load a contemporary Windows program into memory. Of course, BCB provides a WinMain for you so you will be able to compile standard C++ programs without modification.

The key point to notice here is that the VCL provides extra support for you without affecting the performance of your program and without changing the way that C++, or standard Windows programs, operate. This is the right way to handle the interface between C++ and Object Pascal. No changes to C++, no impact on performance, no changes to the standard Windows programming model, and yet additional features are provided for the programmer in terms of ParamStr and the global HInstance.


After getting to WinMain, the program uses the pre-initialized Application object to get your program up and running:

 Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); 

I will discuss each of these calls in their own sections of this chapter. Brace yourself, because I am going to step you through the Object Pascal code that underlies each of these calls.

Initializing the Application

The first call in WinMain performs some initialization:

Application->Initialize(); 

The two most important tasks performed by this code involve OLE and database code. If there is database programming in your application, the database code starts an object called TSession. If there is OLE code in your program, the program may call a method that updates the Registry automatically so that your program is a properly registered automation server. If the code uses neither technology, nothing happens in the call to initialize.

Here is the code for the Initialize method of TApplication as it appears in Forms.pas:

procedure TApplication.Initialize; begin if InitProc <> nil then TProcedure(InitProc); end; 

If you want to step through this code, simply copy Forms.pas, Controls.pas, Classes.pas, and VCL.INC from the BCB\SOURCE\VCL subdirectory into your project directory and add Forms.pas, Controls.pas, and Classes.pas to your C++ project. To add the files, bring up the Project Manager from the View menu, click the plus button, and use the drop-down list from the Files of Type section to browse for files with a .pas extension. Next, select Forms.pas, Classes.pas, and Controls.pas, and close the Project Manager. This technique is probably preferable to bringing in the source to the whole VCL by adding the CBuilder\SOURCE\VCL to the include or library path for your project.

When you step through this Object Pascal code in the BCB integrated debugger, you will find that InitProc is never called, because it is set to NULL unless you bring in database code or OLE automation code. Needless, to say, it takes one line of assembly code to test for NULL, so there is no significant overhead involved here other than the call to the Initialize method itself.

Here is the code at the bottom of the OLEAuto unit that would initialize InitProc if you used OLEAutomation in your program:

initialization begin OleInitialize(nil); VarDispProc := @VarDispInvoke; Automation := TAutomation.Create; SaveInitProc := InitProc; InitProc := @InitAutomation; end; finalization begin Automation.Free; OleUninitialize; end; end. 

This call starts by initializing OLE and then setting up a dispatch point for use when making calls into IDispatch. Next, it allocates memory for the Automation object, calls its constructor, and finally points InitProc at the proper method after saving the previous value of the function pointer. Of course, none of this code will be called unless you are using OLE Automation in your program through the routines found in the OleAuto unit.

Creating Forms

The second call in WinMain creates the MainForm for your application:

Application->CreateForm(__classid(TForm1), &Form1); 

You already have the Forms unit linked into the project, so you can step into this code without any further work. However, if you want to get into more detail here, you can also add Classes.pas and Controls.pas to your project. However, there are very few users of the VCL who really need to know what happens in either controls or classes.

Here is the call to CreateForm:

procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference); var Instance: TComponent; begin Instance := TComponent(InstanceClass.NewInstance); TComponent(Reference) := Instance; try Instance.Create(Self); except TComponent(Reference) := nil; raise; end; if (FMainForm = nil) and (Instance is TForm) then begin TForm(Instance).HandleNeeded; FMainForm := TForm(Instance); end; end; 

The vast majority of this code does nothing but initialize variables or check for errors.

The base VCL object is called TObject. All VCL objects descend from TObject by definition. It is impossible to create a VCL object that does not descend from TObject, because any object that does not descend from TObject is not part of the VCL. The call to TObject.NewInstance does nothing more than allocate memory for an object and return a pointer to it, as you can see from viewing this call in System.pas:

class function TObject.NewInstance:TObject; asm PUSH EDI PUSH EAX MOV EAX,[EAX].vtInstanceSize CALL _GetMem MOV EDI,EAX MOV EDX,EAX POP EAX STOSD { Set VMT pointer } MOV ECX,[EAX].vtInstanceSize{ Clear object } XOR EAX,EAX PUSH ECX SHR ECX,2 DEC ECX REP STOSD POP ECX AND ECX,3 REP STOSB MOV EAX,EDX POP EDI end; 

Needless to say, this is probably the only place you will ever see anyone allocate memory for an object by calling NewInstance. As a rule, the constructor for an object will call NewInstance automatically. Here is the standard code for creating an object:

TMyObject *MyObject = new TMyObject(); 

This will call NewInstance for you automatically, and it would be madness to proceed in any other fashion.


NOTE: TObject.NewInstance is what is called in Object Pascal a class method and what C++ implements as a static method. Class methods and static methods can be called without an object instance; that is, they can be called before you allocate memory for an object or call its constructor. Class methods could have been implemented as functions that are associated with a class, and indeed, there is no significant difference between a function associated with a class and a class method. However, it is syntactically useful from the user’s point of view to include them in a class declaration, because it makes the association between the class and the method obvious. In other words, class methods are aesthetically pleasing, and they help you create logical, easy-to-read code. Besides NewInstance, the following methods of TObject are all class methods: ClassName, ClassNameIs, ClassParent, ClassInfo, InstanceSize, InheritsFrom, MethodAddress, and MethodName.

These methods are segregated out of the BCB declaration for TObject and declared as a class with the somewhat intriguing name TMetaClass. This metaclass is a trick that allows C++ to get along with the VCL and its associated RTTI. TMetaClass plays no significant role in standard BCB programming and is designed primarily for the use of the compiler team itself. You may have occasion to use this metaclass when working with RTTI, but as a rule, it lies outside the realm of standard BCB programming. If you have the time and inclination to pursue this matter, the whole class is declared and implemented in SYSDEFS.H.

For instance, the ClassType method of TObject returns a variable of type TClass, and TClass is defined as a pointer to TMetaClass. You can use variables of this type to perform the same kinds of comparisons you would perform on standard C++ classes with typeid.


The most important call in TApplication CreateForm is to Instance.Create(Self). It is the call that ends up calling the constructor for the main form, as well as calling the Windows API CreateWindowEx function. If you step into this code with the debugger, you will find that it ends up stepping right into the constructor for your main form:

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } 

The last few lines of code in the CreateForm method set the variable FMainForm to the current form if it has not already been assigned.

The main form for your application will be the one that appears first when someone launches your executable. As you can see from examining the code in CreateForm, the first object passed to CreateForm will be the main form for that application:

 if (FMainForm = nil) and (Instance is TForm) then begin TForm(Instance).HandleNeeded; FMainForm := TForm(Instance); end; 

Each project that you create can have zero, one, or more forms. You can add forms to a project from the File menu or from the File | New menu choice. If you add three forms to a project, this is what the project source looks like:

Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->CreateForm(__classid(TForm2), &Form2); Application->CreateForm(__classid(TForm3), &Form3); Application->Run(); 

In this case, Form1 will be the main form for the application. If you change the order of these statements, another form could become the main form:

Application->Initialize(); Application->CreateForm(__classid(TForm3), &Form3); Application->CreateForm(__classid(TForm1), &Form1); Application->CreateForm(__classid(TForm2), &Form2); Application->Run(); 

In the preceding code, Form3 is now the main form for the application.

Normally, I do not edit the project source for my application directly. Instead, I go to the Options | Project menu item and use the Forms page from the Project Options dialog to make these kinds of changes. Once again, this is a two-way tool, and you can do things visually via the Project Options dialog or you can do things manually in code. It’s up to you to make the decision.

Calling Run: Finding the Message Loop

The last call in WinMain is to the Run method:

Application->Run(); 

TApplication.Run does a few initialization chores, and then calls a method called HandleMessage that in turn calls ProcessMessage:

function TApplication.ProcessMessage: Boolean; var Handled: Boolean; Msg: TMsg; begin Result := False; if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin Result := True; if Msg.Message <> WM_QUIT then begin Handled := False; if Assigned(FOnMessage) then FOnMessage(Msg, Handled); if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then begin TranslateMessage(Msg); DispatchMessage(Msg); end; end else FTerminate := True; end; end; 

This is the standard message loop that lies at the bottom of all Windows applications. As you can see, it calls TranslateMessage and DispatchMessage just like every other message loop in every other Windows application.

The HandleMessage routine that calls ProcessMessage also ends up calling a method named Idle. If you hook the OnIdle handler to TApplication, you can get a chance to perform background tasks while your application is running. It is generally not a good idea to respond to OnIdle events unless you are absolutely positive you know what you are doing. On the other hand, I would not suggest starting up a second PeekMessage loop in your application, so if you have to do background processing, I would indeed do it in response to OnIdle.

You now know what happens when a VCL application is first launched. I’ve shown it to you in such depth for two reasons:

  • You need to know this information if you are going to use BCB for serious work. You have to understand how your application is launched, where the message loop is, and how forms are allocated.
  • There has to be some time when you actually see the place where C++ and Object Pascal meet. So now you’ve seen it. The point I hope you take home from this is that there just plain isn’t any real difference between C++ and Object Pascal. There may be a difference between ANSI C and ANSI Pascal, but C++ and Object Pascal are the same. Certainly there is no significant difference in the way the two languages perform or in the capabilities of the two languages. It should also now be obvious that the compiler just doesn’t care which language you use. I mix and match C++ and Object Pascal all the time. I know both languages well enough so half the time I am not even conscious of which language I’m using. You can find a language bigot on every street corner. What’s rare is to find someone who can look beneath the surface and see that at bottom the two languages are virtually identical. The differences are almost all superficial, and they tend to cancel each other out. C++ does some things better than Object Pascal, and Object Pascal does some things better than C++. Who cares? The point is they are both great languages. Now you can use them both together without penalty, so you can take the best from both worlds.

Creating the ShapeDem Program

It’s time now to come back to the original purpose of this section of the chapter, which is to examine the ShapeDem program. To create the program yourself, start by dropping down a TMainMenu and a TColorDialog. The TColorDialog is found on the Dialogs page of the Component Palette, while the TMenu is on the Standards page. Now create a menu heading called Options and beneath it two menu items with captions that read Shape Color and Form Color.


NOTE: I briefly introduced using the Menu Designer in Chapter 1. Remember that you can get help on most (in a perfect world it would be all) BCB components, code elements, or tools by selecting the item in question and pressing F1.


After closing the menu designer, double-click the menu item you made called Form Color to create a method in the editor that looks like this:

void __fastcall TForm1::FormColor1Click(TObject *Sender) { if (ColorDialog1->Execute()) Form1->Color = ColorDialog1->Color; } 

When you run the program, the code shown here pops up the ColorDialog, as shown in Figure 2.7.

FIGURE 2.7.The Color Dialog gives the user an easy way to select a valid color at runtime.
If the user clicks the OK button in the form, the following line of code is executed:

Form1->Color = ColorDialog1->Color; 

This line of code sets the Color property for Form1 to the color that was selected by the user inside of ColorDialog1.

The technique just shown can be used to change the color of the TShape object. All you need to do is drop down a TShape object from the additional page, and then associate some code with the Shape Color menu item:

void __fastcall TForm1::ShapeColor1Click(TObject *Sender) { if (ColorDialog1->Execute()) Shape1->Brush->Color = ColorDialog1->Color; } 

What could be simpler?

You should now run the SHAPEDEM program so that you can see how easy it is to change the color of the elements on the form. Of course, you don’t have to give the user the exclusive right to control all the elements of your program. Sometimes you can take the initiative. For instance, you could change the color of your form or of an element on your form in order to focus the user’s attention on a particular part of the screen.

Notice that the code written here is all but self-documenting. Anyone with even the slightest acquaintance with programming can just glance at this procedure and determine what it does. Assuming you have more than a passing acquaintance with programming, here is how to translate the code into English: “If the user clicks on a visual element on Form1 that is called ShapeColor1, that visual element will delegate an activity to Form1. That activity consists of popping up a color dialog and asking the user to make a selection. If the user chooses the OK button, the color of Shape1 is set to the color selected in the TColorDialog object.”

What do I mean when I say that the ShapeColor menu item “delegates” an activity to Form1? Well, this means that the ShapeColor TMenuItem object does not itself handle being clicked, but instead allows the form to decide what happens when someone clicks it. It delegates the job to the main form.

The delegation model works through events, which are listed in the Object Inspector on the page sitting next to the properties for an object. Some people call events closures, though I regard that as a very technical term with certain platform-specific ramifications that I have never fully explored. As a result, I will usually play it safe and call events nothing more than events and leave it at that. The delegation model is implemented through events, and events are explored in depth in Chapter 3, “Events,” and Chapter 4, “Exceptions.”

In the beginning, it is probably simplest to think of events as being similar to standard Windows messages, such as WM_CREATE, WM_COMMAND, or WM_VSCROLL. Indeed, handling standard Windows messages is one of the functions of events. However, you will see that events can also delegate to another control tasks that you usually could not handle unless you subclassed a component such as an edit control, or else descended from an object that wrapped a control, as you would in OWL or MFC.

Perhaps an acceptable first crack at defining events would be to say that: “Events allow you to delegate tasks from one component to another component so that you do not have to subclass the first component, nor respond inside a window procedure to messages that the first control generates. In particular, events are usually delegated by the controls on a form to the form itself.”

The advantages of the delegation model are threefold:

  • They do not force you to subclass a control, nor inherit from a class in order to override one of its properties. For instance, you can change an edit control’s behavior without subclassing the control or descending from an OWL or MFC type object that wraps the control.
  • They enable you to forgo the ugly, lengthy, and confusing switchstatement found in standard window procedures.
  • It provides for contract-free programming. You can do whatever you want in an event handler. There are no rules binding what you can and cannot do inside an event handler. Any code you can write legally elsewhere in a program you can also write in an event handler.

It makes sense that it should not be very difficult to change the color of an object found on one of the forms you create. But using scrollbars to change its shape at least appears to be a more difficult task. In fact, experienced Windows programmers know that using scrollbars in a Windows program can be a fairly difficult task, one that requires you to trap and parse a number of messages in a complex switch statement. BCB, however, reduces the entire task of responding to a scrollbar to a single line of code.

To get started, first drop two scrollbars on the screen and set the Kind property of one of them to sbHorizontal, and the Kind property of the other to sbVertical. Now, turn to the events page of the Object Inspector and create a method for the OnChange event of each scrollbar. Fill in the methods with two lines of code so that they look like this:

void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Shape1->Width = ScrollBar1->Position; } void __fastcall TForm1::ScrollBar2Change(TObject *Sender) { Shape1->Height = ScrollBar2->Position; } 

The code shown here sets the Width and Height of the TShape object to the current position of the thumb on the scrollbar. Clearly it is extremely easy to write BCB code that performs a task, which would be relatively complex to execute if you had to work directly with the Windows API. The VCL always makes you feel as though you are working directly with an object, and tries to hide the complexities that are introduced by the Windows API or by standard object frameworks.


NOTE: Note in particular that you don’t have to first set a property of a control and then ask it to redraw itself! This magic is the result of the set method associated with the Height property of Shape1. I will explore the Set and Get methods of properties later in this chapter.


You use the program’s menu to change the shape of the TShape component. In particular, a portion of the main menu for the program should have the following items on it:

Rectangle1: TMenuItem Tag = 0 Caption = `Rectangle' Square1: TMenuItem Tag = 1 Caption = `Square' RoundRect1: TMenuItem Tag = 2 Caption = `RoundRect' RoundSquare1: TMenuItem Tag = 3 Caption = `RoundSquare' Ellipes1: TMenuItem Tag = 4 Caption = `Ellipse' Circle1: TMenuItem Tag = 5 Caption = `Circle' 

The Tag field is a special field associated with all components that can be set to an integer value. The VCL has no use for this field; it is designed explicitly for you to use as you wish, for whatever purpose you want.

All six menu items should be associated with the same event handler through their OnClick method. You can create the original handler by open up the Menu Designer, turning to the Events page in the Object Inspector, and double-clicking the OnChange event. If you then select the OnChange event for the other menu items, you will see that it opens up into a combo box from which you can select all the compatible events. For each of the other five menu items, select the event you created for the first menu item. The event itself should look like this:

void __fastcall TForm1::Rectangle1Click(TObject *Sender) { Shape1->Shape = TShapeType(dynamic_cast<TMenuItem*>(Sender)->Tag); } 

I will explain what this code means over the course of the next few paragraphs.


NOTE: Just now I said that the events that appear in the drop-down combo for the Events page of the Object Inspector must be compatible with a particular event. This means that they must have the same signature, which is yet another term that needs explanation! The signature of an event is found in the header for the method handler associated with a particular event, and in particular it is represented by the parameters passed to the handler. For instance, the signature for the Rectangle1Click method is represented by the fact that it is a method that returns nothing and which accepts a single parameter of type TObject *. Methods that look like this are of type TNotifyEvent. Here is the declaration for TNotifyEvent as it appears in CLASSES.HPP:

typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);

When you see this signature for an event, you can assume that Sender contains an instance of the object that sent the event. For instance, in the case currently under consideration, it is a TMenuItem that sent the event, and the specific TMenuItem that sent the message is passed in the Sender parameter.


The items in the menu designate each of the possible shapes the TShape component can assume. You can find these words listed in the online help under the listing for TShapeType. Or, if you want to go back to the original source code, you find the following enumerated type:

enum TShapeType { stRectangle, stSquare, stRoundRect, stRoundSquare, stEllipse, stCircle } 

NOTE: You can also access the names of the members of enumerated type by using the GetEnumName and GetEnumValue functions from the TypInfo unit, which is pronounced “tip-info.”


In this particular case, you need to write only one line of code in response to the event that occurs when a user clicks a TMenuItem:

Shape1->Shape = TShapeType(dynamic_cast<TMenuItem*>(Sender)->Tag); 

This line of code sets the Shape1->Shape property to the shape that the user has selected in the combo box. The code works because of the correspondence between the ordinal members of an enumerated type and the numerical value you assigned to the tag property of a menu. In other words, the first element in an enumerated type has the value zero, as does the first tag property.

You know, and I know, that the Sender variable contains an instance of the TMenuItem item class. However, the wonders of polymorphism allow BCB to declare this variable to be of type TObject. That way, a TNotifyEvent can accept objects of any type. (Polymorphism will be addressed later in the book in Chapter 21, “Polymorphism.”)

The programmer’s knowledge of the Sender parameter needs to be shared with the compiler. In other words, you and I need to have some way of telling the compiler that the object is not really of type TObject, but that it is a just a polymorphic disguise for a TMenuItem. This is an important step because the TObject class does not have a Tag field, and the TMenuItem class does have a Tag field. In this case, it is absolutely necessary to get at the Tag field because it contains the code information about the shape the user wants to select.

To cast a TObject as a TMenuItem you can use the dynamic_cast syntax. This syntax can be used as part of RTTI to test whether a component is of a particular type, or it can be used to actually make the cast. In this case I am daring and make the cast without bother to test it first. This is safe in this case because I know the only object that can be passed here is a TMenuItem. However, if I wanted to be extra careful, I could write the following:

if (dynamic_cast<TMenuItem*>(Sender)) Shape1->Shape = TShapeType(dynamic_cast<TMenuItem*>(Sender)->Tag); 

This would ensure that no exceptions were raised if the cast should happen to fail, which it won’t in this particular case.

The final part of the ShapeDem program that is worthy of discussion involves making sure that the scrollbars cling to the bottom and right edges of the control. To do this, you need to respond to WM_SIZE messages, which come to BCB programmers under the friendly guise of an OnResize event:

void __fastcall TForm1::FormResize(TObject *Sender) { ScrollBar1->Max = ClientWidth - (ScrollBar2->Width + 1); ScrollBar2->Max = ClientHeight - (ScrollBar1->Height + 1); ScrollBar1->Left = 0; ScrollBar2->Top = 0; ScrollBar2->Left = ClientWidth - ScrollBar2->Width; ScrollBar2->Height = ClientHeight; ScrollBar1->Top = ClientHeight - ScrollBar1->Height; ScrollBar1->Width = ClientWidth - ScrollBar2->Width; } 

This code uses third grade math (the only kind I ever understood!) to ensure that the scrollbars are allowed as tall or as wide as the form, no matter how it is stretched or pulled by the user. Furthermore, it ensures that the Min and Max property for the control ranges from zero to the exact size of the current client window. This ensures that you can use the OnChange event for the scrollbar to make the TShape object as large as the client window, but no larger.

Notice also that the TForm object has properties called ClientWidth and ClientHeight. These properties calculated the client size of the current window; that is, the size of the window minus the menu, caption, and frame. If this was done for you, you would have to write code that looked something like this:

int Menu, Caption, Frame; Caption = GetSystemMetrics(SM_CYCAPTION); Frame = GetSystemMetrics(SM_CXFRAME) * 2; Menu = GetSystemMetrics(SM_CYMENU); ScrollBar1->Left = 0; ScrollBar2->Top = 0; ScrollBar1->Max = Width; ScrollBar2->Max = Height; ScrollBar2->Left = Width - Frame - ScrollBar2->Width; ScrollBar2->Height = Height - Frame - Caption- Menu; ScrollBar1->Top = Height - ScrollBar2->Width - Frame - Caption - Menu; ScrollBar1->Width = Width - ScrollBar2->Width - Frame; 

The point here is that without having to know about GetSystemMetrics and all its associated constants, you can still write code that calculates the size of the TShape object down to the last pixel.

A First Look at RTTI and TypInfo

If you look on the disk, you will find a second copy of the ShapeDem program, called ShapeDem2, that uses a combo box as well as a menu to let the user select the current shape of the object. For instance, you can drop down the combo box and pick a shape such as stCircle or stSquare. These shapes have funny-looking Hungarian squiggles prefixed to them because they are taken directly from the source code of the program at runtime rather than typed in by the programmer at design time. In the next few paragraphs I explain how to use that control, primarily because it also contains an interesting description of how to use Run Time Type Information.

You would think that you need to explicitly type in the names of the shapes that appear in the combo box. For instance, you might assume you need to manually type in the words stCircle and stSquare, and so on. These are the same names you see listed in the Object Inspector for the Shape1 object under the property called Shape, and they are the same words that appear in the combo box at runtime. In other words, if you highlight Shape1 on the form and look at the Shape property in the Object Inspector, you find a list of the possible shapes that can be associated with this object. These are the same shapes you should list in the combo box.

As I said earlier, it’s not possible to get access to the Object Inspector at runtime. As a result, if you wanted to explicitly type in the names, you would need to first pop open the Property editor for the Items property, and then manually type these names. To get started, first highlight the combo box on the form by clicking it. Then, double-click the right side of the Items property in the Object Inspector, or you can click the […] button on the Strings property once. This pops up a String list editor, as shown in Figure 2.8.\

FIGURE 2.8.The String list editor enables you to type in a set of default names that appear in a combo box.


NOTE: In the bottom-left corner of the String list editor is a button that says “Code Editor.” If you press this button you can edit your list inside the regular editor for the IDE. This can be especially useful with SQL query statements, which actually provide syntax highlighting. You access the SQL list editor from inside a TQuery component as explained in Chapter 10, “SQL and the TQuery Object.”


The actual items that you would type into the String list editor are shown next. Be sure to type them in exactly as shown, and in the identical order.

stRectangle stSquare stRoundRect stRoundSquare stEllipse stCircle 

Now when you run the program, the user can drop down this list and select a shape. Here is the code associated with the OnClick event for the combo box:

void __fastcall TForm1::ComboBox1Change(TObject *Sender) { Shape1->Shape = TShapeType(ComboBox1->ItemIndex); } 

As you can see, there is no significant difference between this code and the code written in response to a click on the shapes listed in the menu. Of course, in this case you don’t have to perform a dynamic cast, but that is a mere bagatelle.

The interesting thing about this second version of the ShapeDem program is the code that appears in the constructor for the form:

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { int i; PPropInfo PropInfo = GetPropInfo(PTypeInfo(ClassInfo(__classid(TShape))),"Shape"); for (i = 0; i < 6; i++) ComboBox1->Items->Add(GetEnumName(PropInfo->PropType, i)); ... // Code irrelevant to this example omitted here. } 

This code uses RTTI to retrieve the names of the enumerated type that underlies the Shape property for the TShape object.

All VCL classes are illuminated with type information. You can find out virtually anything you want about these published properties of a class at runtime merely by asking through one or more of the functions in the TypInfo unit.

RTTI is an essential part of RAD programming because there must be a way for the Object Inspector to query an object about its published properties. For instance, the Object Inspector has to list the names of the TShapeType so that you can select them from the Property editor for the Shape property. The code shown in the constructor for the main form of this application uses the same technique to retrieve this information that the Object Inspector uses. It would not do to have BCB developers type in this information manually before releasing the project because the Object Inspector must be able to retrieve this information for any component, including the ones that you create or find after purchasing the product.

Are classes illuminated with RTTI larger than non-illuminated classes? You bet! RTTI is bought at a price. However, it is a price that I am willing to pay for two reasons:

  • It makes RAD possible, as described previously in the text that explains how the property types are listed in the Property editor.
  • It enables me to query objects at runtime. Querying objects at runtime gives me the ability to create generic code that will work automatically, regardless of the type of object I pass to it. For instance, the RTTI example using dynamic_cast shown previously explains how you can safely test the type of an object before performing an action on it. You will see many examples of that type of code in this book, including cases where you can pass an entire form to a routine, and ask that routine to perform some function on all the controls of a certain type on that form. Generic code of that type can be tested once and then used over and over again with multiple objects. The ability to talk to an object and ask it its type gives us the ability to manage objects automatically. This can be especially useful in Ole Automation, and particularly in DCOM. For instance, you can start wandering about on a network, looking for objects. If you find one, you can use RTTI to ask it questions about itself, and to query its capabilities!

Using the RGB Function

In this section I take a very quick look at a second program that uses many of the same controls from the first program. This is just an example, but it is interesting in that it involves using at least one simple Windows API function, or rather, macro. It’s important to understand that you can, of course, call the Windows API whenever you want.

Whenever a TShape component is painted, its interior and border are drawn in particular, predefined colors. By default, these colors are white and black, respectively. More specifically, the interior of the ellipse is filled with the color of the currently selected brush. You can change this color by making an assignment of the following type:

Shape1->Brush->Color = MyNewColor; 

The RGBSHAPE program on your disk shows how you can get very specific control over the colors of an object that you paint to the screen. The letters RGB stand for red, green, and blue; each of the these colors makes up one of the colors passed to the Windows API RGB macro itself:

COLORREF RGB( BYTE bRed, // red component of color BYTE bGreen, // green component of color BYTE bBlue // blue component of color ); 

The parameters passed to this function describe an intensity to be assigned to one of these three colors. These numbers always exist within a range between 0 and 255.

If you pass the RGB function the following parameters, it will return a long integer representing the color red:

void __fastcall TForm1::Button1Click(TObject *Sender) { int Red; Red = RGB(255, 0, 0); Shape1->Brush->Color = TColor(Red); } 

The Color property of a TBrush object is of type TColor, but TColor is nothing but an enumerated type ranging over all the possible values that can be returned by the RGB macro.


NOTE: You can call the entire Windows API from inside BCB. However, most of the time your calls to the Windows API will be mapped through WINDOWS.HPP, which is a wrapper around the Pascal version of these calls. The Object Pascal team did not translate all the headers for the Windows API. As a result, when you call some of the more obscure or some of the most recent Windows API functions, you may have to explicitly include the header files for these calls. For instance, there is no Pascal translation of the headers for DirectX. As a result, you must include DDRAW.H, and so on in your project if you want to make calls to DirectDraw or other DirectX functions. The vast majority of Windows API calls are in WINDOWS.HPP, however, and that unit is included in all your projects automatically, so you don’t have to #include anything in your projects. Needless to say, all Windows API calls are just mappings into a DLL, and it doesn’t matter one wit whether you are mapped into the call through Pascal or C++. The compiler does the exact same thing in both cases, and there is no difference in performance, size, and so on.


Here’s how you get the colors green and blue:

Green = RGB(0, 255, 0); Blue = RGB(0, 0, 255); 

If you combine these three colors in various ways, you can produce particular shades. For instance, if you drop a button into a form and respond to a click on the button with the following code, you draw a bright yellow ellipse on the screen:

Shape1->Brush->Color = RGB(255, 255, 0); Shape1->Shape = stEllipse; 

To achieve the color gray, pass in the following parameters:

Gray = RGB(127, 127, 127); 

To get a fleshlike color, enter

Skin = RGB(255, 127, 127); 

You see how it works. Remember that the first parameter controls the amount of red in the final color, the second the amount of green, and the third the amount of blue. RGB: red, green, blue!

The RGBSHAPE program has a TShape component, three labels, three scrollbars, and three edit controls.

The RGBSHAPE program has only one method:

void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Shape1->Brush->Color = RGB(ScrollBar1->Position, ScrollBar2->Position, ScrollBar3->Position); Edit1->Text = IntToStr(ScrollBar1->Position); Edit2->Text = IntToStr(ScrollBar2->Position); Edit3->Text = IntToStr(ScrollBar3->Position); } 

This method first uses the current positions of the scrollbars to assign a color the TShape objects brush. To make this work correctly, I set the Max property for each scrollbar to 255. When the color has been drawn on the screen, I show the actual numbers passed to the scrollbar in the edit components.

The point of the RGB program is to give you a graphical representation of the way the RGB function works. You might also find that this program helps you choose colors that you want to use in your own programs.


NOTE: When working with the RGBSHAPE program, some users may find that Windows cannot create pure tones for some colors, but instead creates a kind of patchwork that approximates the shade described by the parameters passed to the RGB function. However, you can generally get pure tones if you set each of the parameters to 0, 128, or 255. Numbers halfway between 0 and 128 also usually produce pure tones. Of course, the actual results you see depend on whether you are using a 16-color card, 256-color card, or some video card that offers many thousands of colors. You also need to have the correct video drivers in place. For instance, you don’t want to be using a powerful video card and get only 16 colors from it simply for want of the right driver! If you suspect your card is not performing correctly, get on the Web, visit the home page of the company that makes your video card or computer, and download the latest drivers. In Windows 95, you can usually simply unzip the drivers into a temporary directory, right click the desktop, and select Properties | Settings | Change Display Type. When Windows asks for your new drivers, simply point it at the directory where you unzipped the files and it will add them to the system automatically. Be sure to test your work before you restart Windows!


Listing 2.4. The code for the main unit in the RGBSHAPE program.

#include <vcl.h> #pragma hdrstop #include "Main.h" #pragma resource "*.dfm" TForm1 *Form1; __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Shape1->Brush->Color = RGB(ScrollBar1->Position, ScrollBar2->Position, ScrollBar3->Position); Edit1->Text = IntToStr(ScrollBar1->Position); Edit2->Text = IntToStr(ScrollBar2->Position); Edit3->Text = IntToStr(ScrollBar3->Position); } 

Summary

In this chapter you have been introduced to the basic facts about Borland C++Builder. In particular, you learned about the environment, manipulating components, and some of the basic tricks of RAD programming. You also had a brief introduction to some advanced topics such as RTTI.

By now the boards are clear to head into some more technical subject matter, such as the examination of extensions to the language featured in the next chapter. After that you will look at events, exceptions, graphics, and sharing code with Delphi. By the time you have finished all these sections, you will have a solid base from which you can launch an exploration of how to use BCB to create cutting-edge Windows applications.

There is so much information to give out in a book like this that I sometimes forget to mention how much I love this programming environment. All the information I am presenting here adds up to a tool that is more powerful, more flexible, and easier to use than any other C++ compiler on the market. Furthermore, this is just the 1.0 version of a product that should become the environment of choice for serious programmers during the next few years.

Introduction to C++Builder

Introduction to C++Builder

Overview

In this chapter I introduce Borland C++Builder (BCB) and explain what it is about. I also devote considerable time to explaining the purpose of this book and the philosophy behind my approach to technical writing.

Technical subjects covered in this chapter include

  • Creating a simple Multimedia RAD program that plays movies, WAV files, and MIDI files.
  • Shutting down the BCB RAD programming tools and writing raw Windows API code instead.
  • Creating components dynamically on the heap at runtime.
  • Setting up event handlers (closures) dynamically at runtime.
  • A brief introduction to using exceptions. This topic is covered in more depth in Chapter 5, “Exceptions.”
  • A brief introduction to ANSI strings. This subject is covered in more depth in Chapter 3, “C++Builder and the VCL.”
  • Using the online help.
  • Greping through the include and source files that come with the product and with this book.

This chapter includes sample programs or code snippets illustrating all of these concepts. The sample programs for this chapter are found on the CD that accompanies this book in the directory called Chap01. The same pattern is followed for all other chapters. For instance, the code for Chapter 2, “Basic Facts About C++Builder,” is in a subdirectory on the CD called Chap02.

Getting in the Mood

Programming is part of an esoteric world where logic is sacred. Even if you understand exactly why a program works, there is still a magical element involved. Things appear and disappear. Objects materialize, and then dematerialize. They do so according to strictly defined logical rules; but still, there is the fact that things appear and disappear right before our eyes.

To be a good programmer, you have to be a wizard. You have to study arcane material, sit up over it until your eyes are bleary, and ponder its meaning, seeking to understand its mysteries. Many people never understand the subtleties of programming. They don’t ever penetrate to the inner mysteries of this challenging field.

But think of the joy you feel when you finally figure it out! The profound satisfaction of actually cracking the code, mastering the spells, and seeing through to the inner mystery! The arcane minutiae of programming is part of a subtle, intricate world that can be mastered only by a few dedicated souls who are willing to work hard to get at the inner truth of an algorithm, of an object hierarchy, of a coding technique.

Some products seem to be effective at capturing the essence of the beautiful, mysterious logic that underlies the world of programming. C++ has always had free entry into this realm. C++Builder, however, raises the ante in the C++ world by allowing you to create programs with a powerful set of tools that gracefully augment your programming skills.

BCB is one of the first serious compilers that allows you to pick up objects called components with the mouse and move them around so that you can change the logic of your program visually, rather than solely with code. The core of this technology is component programming–not large, bloated, difficult to create components but small, sleek, easy-to-build components that run at lightning speed, components that appear and disappear before your eyes at the click of a mouse.

Programming is intellectually exciting. At times, it’s even–dreaded word–fun! C++Builder puts the excitement back in C++ programming. If you like to write fast programs that are easy and fun to use, this is the right tool for you. Best of all, C++Builder gives you full access to all the advanced features of C++, including templates, name spaces, operator overloading, and the entire Windows API, including cutting-edge APIs such as DirectX, OLE Automation, and ActiveX.

Most of the time, BCB programming is surprisingly easy. On occasion, it’s very challenging. It is, however, always interesting and exciting. Let other programmers plod along with boring compilers made by some huge soulless conglomerate full of middle managers who middle-manage their products into one giant, boring access violation. There is something different about BCB. Like its cousin Delphi, it has something of the true spark of the real programmer’s art in its sleek lines, in its fast compilation, and in its subtle and artful use of the C++ language.

The Audience for This Book

Throughout this book, I walk a subtle line between extremes. Sometimes the text has pulled me in the direction of system programming, and at other times I have relied on the RAD tools that make BCB so easy to use. At times, I have wanted to find the fastest way to perform a particular task and at others I have wanted to find the clearest, simplest way to perform a task. Almost a third of the book concentrates on database tasks, but I also dig deeply into OOP, component creation, and esoteric Windows APIs such as DirectX.

C++ is the language of choice for programmers obsessed with speed, who long to optimize their programs down to the last clock cycle, and who love to plumb the most intricate depths of the computer. Some C++ programmers feel physical pain when they have to give up clock cycles that could be optimized out given sufficient time. In short, C++ is a language designed for creating operating systems and compilers.

RAD tools, on the other hand, are designed for programmers who have a job to do and want to get it done quickly. These people want a safety net so they don’t crash and burn! They are willing to give up clock cycles in return for usable code.

In short, RAD programmers are intent on getting a job done quickly and safely, whereas C++ programmers are traditionally intent on creating the smallest, fastest programs possible.

This book, and BCB as a whole, is about the meeting of these two diverse camps. I am very much aware that many C++ programmers won’t like the “smell” of RAD, and that many RAD programmers will be appalled by the ornate subtleties of C++. However, I believe that there is a place where these two groups can meet, and furthermore, I think C++ can provide the high productivity tools that RAD programmers expect, along with the high performance, system-loving, optimized intricacies that true aficionados of C++ demand.

In short, this book is for contemporary programmers who practice their art on the cutting edge of modern programming techniques. That does not mean that this book is about the most technical aspects of C++ and Windows, nor does it mean that this book is about a dangerous, new form of programming that wastes clock cycles indiscriminately. Instead, this book is about techniques that allow systems programmers to get their work done quickly, while allowing RAD programmers to speed up and enhance their programs.

I should perhaps add that a large portion of this book is dedicated to client/server database programmers. Nearly 80 percent of the applications made today involve databases, and this tool will undoubtedly be used very heavily by client/server developers. I go into considerable lengths to talk about the advanced database features found in BCB; I cover SQL, stored procedures, triggers, filters, lookups, and numerous other database techniques.

BCB Versus VB

There is one thing that ought to be made clear right at the start. The programs you write with BCB are comparable in terms of size and performance with the programs you create with OWL or MFC. It would be a mistake to assume that BCB has any of the limitations you find in VB or PowerBuilder, or even in Optima. Anything you can do in MSVC or in BC5 you can also do in BCB, and you can do it with the same, or an increased, degree of subtlety and artfulness.

Both BCB and VB are RAD tools. But that is where the comparison between the two products must end. VB is a nice product, but it is not a serious programming tool. BCB is a very serious programming tool. It is a real C++ compiler that comes with all the bells and whistles.

The presence of RAD tools can lead you to believe that BCB is somehow crippled in terms of performance or capability. However, that is an erroneous conclusion. If you take the time to explore the product in depth, you will find that it lacks nothing in terms of power or capability.

The RAD tools in this package add no real overhead to your programs that you would not find in either OWL or MFC. The VCL is comparable to OWL and MFC in every way, except for the fact that it is much easier to use and much more elegantly designed.

The word component can also conjure up images of slow, buggy, hard-to-understand ActiveX controls. BCB components are much faster, much smaller, and much easier to make than ActiveX controls. OLE is a powerful technology–and one that I use quite frequently–but it lacks the subtlety, speed, and elegance of the VCL code that underlies BCB.

A Cautious Approach to Programming

Having gone to some lengths to emphasize the technical depth of BCB, I want to turn around and discuss the relatively conservative approach I take to the art of writing programs.

I have been writing code long enough to have grown suspicious of techniques that are too fancy, too subtle, and too hard to parse, execute, and maintain. As a result, I have adopted the style of programming championed by people who want to write safe, easy-to-maintain programs.

I tend to promote a conservative programming style–and indeed, almost all the good programmers I know use these same techniques, even when writing code that is designed for high performance applications.

A certain degree of caution is necessary if you want to write robust code. When in doubt, I always err on the side of caution.

Does this mean I want you to write slow, bloated code? No, of course not! My goal is to walk that fine line between writing code that is such a high wire act that it can’t be maintained, and writing code that is so high-level, so abstracted, that its performance becomes an abomination.

BCB is about the place you can get the maximum in terms of safety, without giving up significant power in terms of speed and flexibility. It’s about walking the line between two extremes.

On Using C++

When creating the sample applications for this book, I tried to choose code that walks the middle line between being too cautious and too daring. I tried to take the best ideas from the C++ language and combine them with the benefits of RAD.

I want to get far enough into C++ to leverage its power, without going so far that I spend whole chapters parsing the subtleties of some obscure syntactical corner of the language. I also want to use many high-level, RAD-based tools, but I don’t want to rely on them so completely that they overshadow the power of the C++ language.

The goal is to find the middle ground, the artful line that yields the best programs. If I am in doubt, I will err on the side of the RAD programmers who have a job to do. The primary reason for this decision is simply that there are already many great books out there on the intricacies of C++ and on the subtleties of the Windows API. There is no need for another book on those subjects. Instead, I want to show what C++Builder brings to the table.

When exploring BCB, however, I will always keep at least one eye on the system programmer. I know what you want, I believe in your cause, and I want to show you how BCB can help you complete even the subtlest jobs more quickly than traditional environments such as BC5 or MSVC. My promise is that the executables you produce with BCB will be at least as small, and at least as fast as the executables you produce with MFC or OWL. And, if you want, you can cut out BCB’s object-oriented tools and produce tiny executables that match anything that you can do with BC5 or MSVC.

I am not trying to create a companion volume to a classic hard-core tome such as the Zen of Assembly Language, More Effective C++, Undocumented Windows, the ARM, or Inside Windows. Books like that have their place, of course, but that is not the kind of programming I want to write about.

Clearly, I am trying to set practical, reasonable goals for this book. However, I don’t mean to imply that this is a plodding, methodical book that will never take flight into any interesting subjects. On the contrary, I want to show how you can do fancy, flashy, exciting things with a computer, without having to parse the lowest-level bits in the operating system. If you want to plumb to the lowest depths of the operating system, I will take you right up to the edge, show you how to get started, and then wish you Godspeed. You can use BCB to do some great system programming, but I will leave the specifics of how to proceed to other authors, or to a second book of my own on the subject.

This book contains lots of exciting code on subjects such as multimedia, games, and Internet programming. I concentrate on very high-performance tools such as DirectX and on cutting-edge technologies such as OLE. Unlike other books on these subjects, however, my goal is to show how you can integrate these things into your projects even if you are on a tight schedule and even if you would not normally be inclined to do the kind of spelunking that those names imply.

In my opinion, the kind of programming described in this book is the essence of cutting-edge computer technology (at the time of this writing). The best programmers today use whatever tools they can find to allow them to quickly produce high-performance programs. Plumbing the depths is fun, but it loses some of its appeal when the Internet calls, or when you need to produce an inventory program quickly, or when you want to spice up an application so that your users actually enjoy sitting down to work with one of your creations.

My point is quite simply that today many of the best programmers are specializing, not in plumbing the depths of the operating system, but in producing real-world applications quickly. This is an advanced programming book that assumes a good deal of experience on the part of the reader. However, I want your experience to be not deep and narrow, but broad and expansive.

Humility, Crooked Timber, and the Practical Programmer

In the book Code Complete (published by Microsoft Press), Steve McConnell quotes an award-winning paper by Edsger Dijkstra called the “The Humble Programmer.” I regard this work as one of the guiding lights of this book.

I would much rather write a humble program that works well than be involved in a flashy, ego-ridden project that is never finished, or that ships two years late. The key to getting things done is to show a little humility.

In particular, if you work under the assumption that any one programmer is perfect, you are doomed to failure. Computers are reliable; programmers make mistakes.

Computers, on the other hand, look remarkably dense when compared to the creativity a good programmer can wield. Machines get a zero on the creativity scale, whereas programmers can be very creative. The key is not to try to make people like computers, but to find the best way to leverage the virtues of both programmers and computers.

If you write code that assumes the programmer is perfect, sooner or later that code will fail. Don’t mix up the programmer and the computer. The computer is the one that doesn’t make mistakes; the programmer is the one that comes up with ideas.

I write code that assumes I not only can make mistakes, but that I will make mistakes. I write code that shies away from the extremely low-level code that crimps my creative side, and which invites bugs.

The code I like to write assumes that I tend to make errors, and that I should be free to exercise a degree of creativity. Code that is too technical, too cutting-edge, or too clever is code that is prone to bugs and late, sleepless nights.

The right kind of code gets the job done quickly enough to leave programmers still fresh and alert, so that they can exercise creativity in coming up with the best solutions.

Quite often in this book, I will recommend techniques that fly in the face of the advice you undoubtedly get from that hotshot programmer who works down the hall. My problem is not that I fail to appreciate the importance of performance or producing small, fast programs. Rather, I worship at a different shrine, the one that finds the middle ground between code that is too subtle and code that is too abstract, too high-level.

This book is dressed in jeans or cords, good practical shoes, and a tough, but attractive, plaid work shirt. Programmers who like to dress in patent leather shoes and $2,000 suits might make fun of some of my techniques. What I like about the clothes that this book wears is that they are tough, well-suited to a wide variety of conditions, and they look great on a wide range of people.

I don’t write for someone who wants to be the best programmer in a group. Instead, I am interested in people who want to make things. I want to get from conception to a finished product, and I don’t care if all of the techniques I use aren’t the fanciest available. I don’t, quite frankly, care all that much about the schedule my manager wants to live by; rather, my goal is to get the job done before I become utterly sick of it. I like to make things. I want to finish the project.

Immanuel Kant is one writer who aptly captured the spirit by which most programmers should live: “Out of timber so crooked as that from which man is made nothing entirely straight can be carved.” In other words, don’t expect your programs to be perfect, and don’t waste time trying to achieve perfection. Aim a little lower, instead. Rather than perfect, shoot for: “It works.” Or, at best: “This program is remarkably bug-free!”

Even better, aim for programs that are creative, fun to use, and useful. The strongest suit a programmer can take to a task is creativity. Even the best programmers look like bunglers when compared to the reliability of a computer.

The best programmers also make mistakes with a frightening regularity. I try to accept that fact, accept my limitations, and then find ways to program that are safe! If I have the humility to admit I am not perfect, I can start making programs that work and that get turned in on time!

Once again, I don’t really care about my manager’s schedule; I care about my schedule. I want to start a project, bring it to fruition, and then move on to the next thing that interests me! I don’t want to be stuck working on the same task for years on end!

API Code Versus RAD Code

At this stage, it might be helpful to give a few specific examples of the difference between RAD programming with BCB and traditional Windows programming. My primary point here is to show that BCB can do it all. If you want to write API code, BCB will let you write it. If you want to write OWL code, BCB will let you do that, too. If–heaven forbid–you should even be foolish enough to want to write MFC code (perish the thought!), you can also do that with BCB.

In this section, you will see two sample programs. The first is a traditional RAD application written in BCB, and the second is a standard Windows API program. I will spend a few moments talking about each program and then will use these examples to illustrate just what it is about RAD that I find appealing, as well as explain something about the parts of RAD development that I will focus on in this book.

A Very Short Introduction to the VCL

Reading this text over, I find that I am throwing a number of acronyms around. One that really begs for a short explanation is VCL.

The VCL is the Visual Component Library. It is to BCB what OWL is to BC5, and what MFC is to MSVC. (This is called acronym immersion therapy.) In other words, it is the object- oriented library, or framework, that underlies BCB. The difference between VCL and OWL is that the VCL is based on components, properties, and events, while OWL and MFC have none of these features. In particular, events support something called the delegation model, which is an alternative to simple inheritance.

The VCL fully supports all standard OOP concepts such as inheritance, polymorphism, and encapsulation. What it brings to the party are components, properties, and events. (Events are also known as closures.) One of the goals of this book is to explain components, properties, and events in the clearest possible terms, and to state why I feel this is the proper programming model for this stage of computer programming development.

Perhaps most importantly, the VCL is written in Object Pascal. In fact, it is literally the same code base used by Delphi. Later in this chapter, I will explain a bit more about BCB’s relationship to Delphi, and I will explore the subject in detail in the next chapter. For now, you need to know only that the VCL would not be any faster or smaller were it written in C++. Object Pascal has some stark differences from C++, but speed and size are not among them. As explained previously, Object Pascal is a real, compiled, object-oriented language that fully supports true inheritance, polymorphism, and encapsulation. All Object Pascal code that works in Delphi works automatically in C++Builder.

That’s all I will say on this subject at this point, though I will spend considerable time defining the VCL and its related programming models more carefully in later chapters. In particular, Chapter 2 and Chapter 3 go into considerable depth on the subject of VCL, how it works, and why it exists.

On Using the Visual Tools

Before I get started with a specific programming example, I want to take a moment to discuss the technique I use when writing about BCB programs. Except for a few places in the first chapters, I will generally skip over detailed descriptions of the visual programming tools.

In this text, I will usually not explain the process of setting up a standard component at all. For instance, if text on a TButton component says OK, Exit, or Close, I will not say Set the Caption field of the TButton component to Exit, or Close. Instead, I will assume that you can figure that much out just by looking at the figure that accompanies my description of the program.

As you gain more experience with C++Builder, you will quickly learn how to work with most of the properties associated with components. As a result, I usually do not bother to write detailed explanations about setting up a component, such as that you need to set its Align property to alClient, or its Stretch property to True. I assume that you can see that much just from glancing at the figure. Of course, I assume there will be times when you will want to run the programs on disk to see exactly how I have achieved a particular affect.

My point here is that you should be able to glean information of this kind from the manuals that ship with the product or from the online help. You could also turn to a beginning level C++Builder book, such as Teach Yourself C++Builder in 21 Days (published by Sams). In the current text, however, I will try to skip over that kind of beginning material, in order to best give you what you expected when you bought an intermediate- to advanced-level book.

I am aware, however, that BCB is a new product and that some introductory material is needed. I will try to keep it to a minimum. In particular, almost all the introductory material occurs in this chapter and the next. After that, I’ll assume you know how to use the environment.

A Simple RAD Multimedia Program

The code for the Multimedia RAD program is shown in Listing 1.1. The entire program is found on the CD that accompanies this book. An explanation of the program follows these listings.

Listing 1.1. The header file for the Multimedia RAD program.

//-------------------------------------------------------------------------- #ifndef MainH #define MainH //-------------------------------------------------------------------------- #include <vcl\Classes.hpp> #include <vcl\Controls.hpp> #include <vcl\StdCtrls.hpp> #include <vcl\Forms.hpp> #include <vcl\ExtCtrls.hpp> #include <vcl\MPlayer.hpp> #include <vcl\Menus.hpp> #include <vcl\Dialogs.hpp> //-------------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TPanel *Panel1; TImage *Image1; TMediaPlayer *MediaPlayer1; TMainMenu *MainMenu1; TMenuItem *File1; TMenuItem *Load1; TMenuItem *Play1; TMenuItem *N1; TMenuItem *Exit1; TOpenDialog *OpenDialog1; TMenuItem *Options1; TMenuItem *ChangeBackground1; void __fastcall Load1Click(TObject *Sender); void __fastcall Play1Click(TObject *Sender); void __fastcall Exit1Click(TObject *Sender); void __fastcall ChangeBackground1Click(TObject *Sender); private: // User declarations public: // User declarations virtual __fastcall TForm1(TComponent* Owner); }; //-------------------------------------------------------------------------- extern TForm1 *Form1; //-------------------------------------------------------------------------- #endif 

Listing 1.2. The main module for the Multimedia RAD program.

/////////////////////////////////////// // File: Main.cpp // Project: Muli-media RAD // Copyright (c) 1997 by Charlie Calvert #include <vcl\vcl.h> #pragma hdrstop #include "Main.h" #pragma resource "*.dfm" TForm1 *Form1; __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } void __fastcall TForm1::Load1Click(TObject *Sender) { if (OpenDialog1->Execute()) { MediaPlayer1->FileName = OpenDialog1->FileName; MediaPlayer1->Open(); } } void __fastcall TForm1::Play1Click(TObject *Sender) { try { MediaPlayer1->Play(); } catch(EMCIDeviceError &E) { AnsiString S("\rUse the File | Open menu item to select an AVI file."); ShowMessage("Bummer: " + E.Message + ". " + S); } } void __fastcall TForm1::Exit1Click(TObject *Sender) { Close(); } void __fastcall TForm1::ChangeBackground1Click(TObject *Sender) { AnsiString RootDir(ParamStr(0)); AnsiString SaveDir = OpenDialog1->InitialDir; AnsiString SaveFilter = OpenDialog1->Filter; OpenDialog1->InitialDir = ExtractFilePath(RootDir); OpenDialog1->Filter = "Picture | *.bmp"; if (OpenDialog1->Execute()) { Image1->Picture->LoadFromFile(OpenDialog1->FileName); Image1->Stretch = True; } OpenDialog1->InitialDir = SaveDir; OpenDialog1->Filter = SaveFilter; } 

This program pops up in a window that has a picture of a Mayan temple as a background, as shown in Figure 1.1. From the menu, you can pop up an open file common dialog that will let you select and play either a movie file, WAV file, or MIDI file. You can also browse to select new backgrounds for the main form.

FIGURE 1.1.The main screen for the Multimedia Adventure program.

The RAD Tasks for Creating the Multimedia Program

To create the program, bring up BCB and select New Application from the File menu. Drop down the following components on the main form:

TPanel *Panel1; TImage *Image1; TMediaPlayer *MediaPlayer1; TOpenDialog *OpenDialog1; 

NOTE: : In some RAD programming books, code is presented that shows the exact location of the objects placed on a form. For instance, here are some selections from the text representation of the form for the Multimedia program:

object Form1: TForm1 Left = 244 Top = 147 Width = 493 Height = 388 Caption = `Form1' Menu = MainMenu1 object Image1: TImage Left = 0 Top = 41 Width = 485 Height = 301 Align = alClient Picture.Data = { Lots of numbers omitted here } end object Panel1: TPanel Left = 0 Top = 0 Width = 485 Height = 41 Align = alTop TabOrder = 0 object MediaPlayer1: TMediaPlayer Left = 192 Top = 5 Width = 253 Height = 30 TabOrder = 0 end end // Menu would appear here object OpenDialog1: TOpenDialog FileEditStyle = fsEdit Filter = `Movies, Sound, Midi|*.avi;*.wav;*.mid' InitialDir = `c:\' Left = 48 Top = 48 end end

You can convert your forms into this kind of text by right-clicking them and selecting View as Text from a popup menu. Conversely, you can translate text into visual forms by right-clicking them and selecting View as Form from a popup menu. You can also convert programs back and forth from text to binary files by running a command-line utility called Convert.exe. Finally, you can type the preceding code into a text editor, copy it to the Clipboard, and then paste it onto a BCB form or panel. After the paste operation, you will not see text, but live visual components.

At any rate, I have opted not to use the kind of textual description of a form shown above, primarily because the form itself is available on disk, and secondarily, because a picture of a form, like that shown in Figure 1.2, provides more information than a textual description. I do, however, provide both binary and textual copies of the forms on the CD that accompanies this book.


A Brief Note on Creating Menus

You can add a menu to this program by dropping down a TMenu component, double- clicking it, and filing it out as follows:

TMenuItem *File1; // Popup menu TMenuItem *Load1; // menu item TMenuItem *Play1; // menu item TMenuItem *N1; // (separator made by entering a dash in the Caption field) TMenuItem *Exit1; // menu item TMenuItem *Options1; // Popup menu TMenuItem *ChangeBackground1; // menu item 

The text for each of these menu items is the same as the name of the menu item itself, except that the 1 is removed from the end of the name.

FIGURE 1.2. The Menu Designer as it looks during the construction of the menu for the Multimedia RAD program.


NOTE: I would not normally cover this kind of material in this book, but it might be helpful to hear one description of how to use the C++Builder visual tools in hyper-mode. If you grasp the subtleties of this technique, you will find that you do not need to manually switch back and forth between a form and the Object Inspector. Instead, the environment will move you back and forth automatically between the two tools.

To get started working in hyper-mode, make sure the Menu Designer is closed, because this process works best if you start from scratch. Now bring up the Menu Designer by double-clicking the TMenu object you dropped on the form. Immediately after double-clicking the item, start typing into the Menu Designer dialog. Don’t bring up the Object Inspector. Instead, type directly on the Menu Designer, and watch as the Object Inspector comes up of its own accord. When you want to switch back to the Menu Designer, just press Enter.

For instance, focus the Menu Designer on the first blank menu item and type the word File. Press Enter. Type Open. Press Enter. Type Close. Press Enter. To move over to the next column, press the right arrow key. Type Edit, press Enter, and so on.

You can also select items on the Menu Designer and edit them inside the Object Inspector. However, it is easier to use the hyper-mode method. For more details, see the online help or an introductory book on BCB programming.


You should also go to the Object Inspector for the TImage component and set its Picture property to the bitmap you want to have as the background for the main form. The TImage component should have its Align property set to alClient, and its Stretch property set to True.

You should set the Filter property for the TOpenDialog component so it looks like the screen shot shown in Figure 1.3. In particular, the text for the property would look like this, if you were filling it out in code rather than via the Object Inspector:

OpenDialog1->Filter = "Movies, Sound, Midi | *.avi;*.wav;*.mid"; 


FIGURE 1.3.The Property editor for the Filter property of the TOpenDialog component.

Loading a Multimedia File

The following function is called when the user selects the Load menu item from the File menu:

void __fastcall TForm1::Load1Click(TObject *Sender) { if (OpenDialog1->Execute()) { MediaPlayer1->FileName = OpenDialog1->FileName; MediaPlayer1->Open(); } } 

This code first opens a common dialog to allow the user to select a filename. If the user clicks the OK button in this dialog, the selected filename is assigned to the multimedia component and the component is opened. The act of opening the component “turns on the lights” on the multimedia control itself. In other words, after you open the component, it moves from a grayed-out, inactive state to a colorful, active state.

The VCL and Memory Allocation

Notice that the objects you are working with here are all created on the heap. There is no such thing as a static instance of a VCL component, or indeed, of a VCL object. All VCL objects are created on the heap–that is, you always create a pointer to a VCL object and you don’t ever create a static instance of a VCL component. In fact, you can’t create a static instance of a VCL object. The compiler won’t let you. VCL objects exist on the heap by definition, as will be explained further in the next chapter.


NOTE: My mixing of the words component and object in the previous paragraph is intentional. All VCL components are nothing more than VCL objects with a small amount of overhead added to them so they can appear on the Component Palette. All VCL components are also VCL objects, but not all VCL objects are components. This subject will be explained in depth in the chapters on creating your own components.

You will also find that I use the words class and object interchangeably. Contrary to popular belief, this is not an error. Needless to say, I understand that quite often people use the word class to refer to a declaration, and they use the word object to refer to an instance of a class. However, this rather fine distinction becomes a bit precious in real life, so I tend to use the words interchangeably unless I have a specific need to make a distinction between the two concepts. When that is the case, I will make it abundantly clear that you need to make a distinction between a class declaration and an object instance.


Notice also that you are not actually responsible for allocating or deallocating memory for VCL components. If you want to, you can explicitly create a component in code. For instance, here is how to dynamically create a TButton control:

MyButton = new TButton(this); MyButton->Parent = this; MyButton->Caption = "Dynamic Button"; MyButton->Width = 250; MyButton->Show(); 

This type of code is explored in depth later in this chapter, but if you want to see it in action right away, you can run the DynamicButton program found on the CD-ROM that accompanies this book, in the Chap01 directory. A screen shot of that program appears in Figure 1.4.

FIGURE 1.4.The DynamicButton program dynamically allocates a button at runtime.

It is also not usually your concern to deallocate the memory associated with a component. BCB has no garbage collection facility. The constructor for TButton shown above assigns this as the owner of the control. That means that the main form for the program is responsible for disposing MyButton, because it has been designated as the owner of the button. (The this pointer is a bit of syntactical hand-waving that allows an object to refer to itself. In this case, Form1 owns TButton, and the identifier this is a way of referring to Form1.) The code in TForm that deallocates MyButton is built into the VCL. You never have to think about it. I will, however, discuss how it works in several parts of this book.

The scheme described here sounds a lot like garbage collection. In fact, if you drop a component on a form from the Component Palette and then run the program and terminate it normally, you will never have to worry about either allocating or deallocating memory for components. This is not a true garbage-collection scheme because you have the option of not passing in a parent to an object when you create it and you can decide to deallocate an object at any time if you want. In other words, many of the memory management chores are taken care of for you, but they do not have to be handled by the system if you would rather do them yourself. This freedom is what I love about BCB, but it also brings responsibility with it.

A true garbage collection scheme would never allow you to make mistakes allocating or deallocating memory. The VCL does not go that far. You can definitely make mistakes with memory management if you are not careful. On the other hand, it should be clear to you that BCB programmers are relieved of many onerous memory management chores. In fact, if you do things right, you rarely have to think about memory management. Part of the job of this book will be to explain how to ensure that you never have to worry about memory management. I will, however, also show you how to take matters into your own hands, if you wish.

A First Glance at Exception Handling

The simple act of opening the TMediaPlay component is not the same as causing the component to play a movie or song. If the users want to play a file that has been loaded into the component, they can push the green button on the TMediaPlayer control. Alternatively, they can select the Play menu item from the File menu:

void __fastcall TForm1::Play1Click(TObject *Sender) { try { MediaPlayer1->Play(); } catch(EMCIDeviceError &E) { AnsiString S("\rUse the File | Open menu item to select an AVI file."); ShowMessage("Bummer: " + E.Message + ". " + S); } } 

As you can see, this code includes a try..catch block that shoulders some exception-handling chores. If you wanted, you could safely leave the explicit try..catch block out of this code and the VCL would still automatically raise an exception if the user has picked the Play option before loading a file. In that case, the VCL will automatically create an exception that tells the user No MCI Device Open.

For an error built into a programmer’s library, the simple strings popped up by the VCL are usually very complete and comprehensible error messages. However, you will probably want to improve on it before showing it to the users of one of your own programs. The code above accomplishes this task by catching the exception, modifying its output string, and then using custom code to display it for the user.

Exceptions in BCB are exactly like exceptions in normal C++ programs, only now your entire program is automatically wrapped in a try..catch block and you also have a very rich set of exception classes at your disposal courtesy of the VCL. In particular, you can see that the Play1Click method catches an exception class called EMCIDeviceError. All VCL exception classes begin with the letter E, so this class might be more readily comprehended as the MCIDeviceError exception class, used to raise exceptions that occur when using the TMediaPlayer control.


NOTE: The TMediaPlayer control is a wrapper around the now somewhat old-fashioned Windows Media Control Interface, or MCI–hence the name MCIDeviceError. As you will see, BCB can also use DirectX, DirectMovie, and other advanced multimedia APIs made by companies such as Apple or Intel. In fact, BCB can use any API that works in Windows, but this particular example happens to use the MCI, which is more than adequate for the task at hand.


As will be explained in depth in Chapter 4, “Events,” the following code gives you access to a variable named E, of type EMCIDeviceError:

catch(EMCIDeviceError &E) 

As you will see in Chapter 3, you can use E to call a number of methods of the EMCIDeviceError class. One of these methods is called Message, and it returns a human-readable string that can be displayed to the user. I include this string in the text I show to the user, but I add other information including a potential response to the error.

A Brief Introduction to AnsiStrings

One pair of lines that have surely caught your eye by this time are the ones that use a new, BCB-specific class called AnsiString:

AnsiString S("\rUse the File | Open menu item to select an AVI file."); ShowMessage("Bummer: " + E.Message + ". " + S); 

The AnsiString class is explained in depth in Chapter 3. For now, all I will say is that it provides a type of string that is fully compatible with the Object Pascal strings used by the VCL. In particular, the AnsiString class overrides the + operator to support concatenating strings. Underneath it is a simple string class that most likely calls strcat in one of its methods. The use of operator overloading and several other techniques makes it look and act like a Pascal string.

Though it is tempting to use either plain NULL-terminated strings, one of the string classes from the STL, or one from some other library, you will find that I use AnsiStrings almost exclusively in the code that accompanies this book. The primary reason for this is their compatibility with the VCL. However, I am also drawn to their safety and ease of use.

Two-Way Tools: Changing Properties at Runtime

The code in the ChangeBackgroundClick method shows how you can manipulate properties either in code, or via the Object Inspector:

void __fastcall TForm1::ChangeBackground1Click(TObject *Sender) { AnsiString RootDir(ParamStr(0)); AnsiString SaveDir = OpenDialog1->InitialDir; AsiString SaveFilter = OpenDialog1->Filter; OpenDialog1->InitialDir = ExtractFilePath(RootDir); OpenDialog1->Filter = "Picture | *.bmp"; if (OpenDialog1->Execute()) { Image1->Picture->LoadFromFile(OpenDialog1->FileName); Image1->Stretch = True; } OpenDialog1->InitialDir = SaveDir; OpenDialog1->Filter = SaveFilter; } 

This code changes the InitialDir and Filter properties of the TOpenDialog object at runtime. At first, you might suspect that the only way to manipulate a BCB component is through the Object Inspector. However, everything that you can do visually with BCB also can be done in code.

Of course, I could have saved time writing some code here by placing two TOpenDialog controls on the form. However, I do things this way so that

  • You can see how BCB’s two-way tools work. You can do things two ways: You can write code, or you can do things visually.
  • It is also a potentially more efficient use of memory not to have two copies of the object on the form. I would, of course, have to run more tests to be sure that this technique is really saving memory, but the point is that BCB gives you the flexibility to do things as you think best.

The ChangeBackground1Click method first saves the current state of the Filter and InitialDir properties. Then it changes them to values that support the loading of a new bitmap for the background of the form. In particular, the VCL functions called ParamStr and ExtractFilePath are used to get the initial name of the program as it was passed to the executable by Windows. (There is no need to parse argv; that task is already done for you by ParamStr.) The ExtractFilePath function strips off the executable name, leaving only the path to the directory where your program was launched. The code then assumes that some bitmaps suitable for a background are available in that directory, which is the case with the code that ships with this book.

The VCL and the Windows API

Notice the LoadFromFile method used to initialize the Picture property of the TImage component:

 if (OpenDialog1->Execute()) { Image1->Picture->LoadFromFile(OpenDialog1->FileName); Image1->Stretch = True; 
 } 

LoadFromFile entirely hides the act of loading a bitmap into memory, getting a handle to it, and passing it to the TImage component that can display it to the user.

This capability of components to hide the intricacies of the Windows API from the user is one of the VCL’s key strengths. It would be a mistake, however, to view components as black boxes. They are no more black boxes than in any other object you would find in OWL, MFC, or any other OOP library. The whole source to the VCL ships with most versions of BCB and is always available from Borland.


NOTE: The fact that the VCL is in Pascal should not be a great hindrance to most BCB programmers. If you examine the actual source files, you will find that they consist primarily of calls to the Windows API and of calls to other portions of the VCL. Because BCB programmers work with the VCL all day, they should have little trouble reading calls to the VCL that happen to be wrapped in begin..end blocks rather than curly braces. Furthermore, a Windows API call in Object Pascal looks almost exactly like a Windows API call in C++. There is no significant difference in their appearance or performance, as long as you are not confused by minor differences such as the appearance of a := operator where you expect to see a simple = operator. In Chapter 2, “Basic Facts About C++Builder,” I will show how you can step into the Pascal code of the VCL with the debugger.


Even more important than the presence of the source to the VCL is the fact that you can use BCB to create your own components. If you see yourself as primarily as a system hacker who wants to be close to the Windows API, you can get as close as you want while creating your own components. In fact, BCB allows you to use the Windows API at any time, and in any way you want.

Needless to say, good VCL programmers use components whenever possible, because they are so robust. If you need to drop to the Windows API, it is often a good idea to wrap up the resulting code in a component and then share it with or other programmers–or, if you prefer, sell it to other programmers.

Tools like Visual Basic or PowerBuilder gave people the mistaken impression that RAD was innately slow and perhaps designed for programmers who didn’t really know how to write “real” code. Delphi put the lie to that misconception, but it had to struggle uphill against ignorant prejudices concerning Object Pascal. BCB is not as fully encumbered by that weight as Delphi, and it will show everyone who cares to listen that the fastest way to build small, tight, robust OOP-based programs is through RAD.

Writing RAW Windows API Code with BCB

Having ended the last section on a rather provocative note, it’s perhaps time to show the more technical side of BCB programming. The WinAPICode program shown below is written entirely using raw Windows API calls. There are no objects in this program; instead, everything is done in a manner similar to the one Petzold used back when Windows programming was just getting started.

The code shown here does not follow Petzold exactly, in that I use Windowsx and STRICT to help make the code more readable, more maintainable, and more portable. The basic approach, however, is tied very closely to the Windows API. For instance, I have a real WndProc and message loop, and I make calls to old standbys such as CreateWindow, ShowWindow, UpdateWindow, and RegisterClass. The code itself is shown in Listing 1.3.

Listing 1.3. Standard Windows API program that compiles unchanged in BCB.

/////////////////////////////////////// // File: WinAPICode.cpp // Project: WinAPICode.cpp // Copyright (c) 1997 by Charlie Calvert #define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> #pragma warning (disable: 4068) #pragma warning (disable: 4100) static char szAppName[] = "Window1"; static HWND MainWindow; LRESULT CALLBACK WndProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam); BOOL Register(HINSTANCE hInst); HWND Create(HINSTANCE hInst, int nCmdShow); // =================================== // INITIALIZATION // =================================== ////////////////////////////////////// // The WinMain function is the program entry point. // Register the Window, Create it, enter the Message Loop. // If either step fails, exit without creating the window ////////////////////////////////////// #pragma argsused int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { MSG Msg; if (!hPrevInstance) if (!Register(hInst)) return FALSE; MainWindow = Create(hInst, nCmdShow); if (!MainWindow) return FALSE; while (GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } ////////////////////////////////////// // Register the window ////////////////////////////////////// BOOL Register(HINSTANCE hInst) { /* You can use WNDCLASSEX and RegisterClassEx with WIN32 */ WNDCLASS WndClass; WndClass.style = CS_HREDRAW | CS_VREDRAW; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = hInst; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = szAppName; return (RegisterClass(&WndClass) != 0); } ////////////////////////////////////// // Create the window ////////////////////////////////////// #include <wtypes.h> __RPC_FAR Sam() { return 0; } HWND Create(HINSTANCE hInstance, int nCmdShow) { HWND hWindow = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); if (hWindow == NULL) return hWindow; ShowWindow(hWindow, nCmdShow); UpdateWindow(hWindow); return hWindow; } // ===================================== // IMPLEMENTATION // ===================================== #define Window1_DefProc DefWindowProc void Window1_OnDestroy(HWND hwnd); ////////////////////////////////////// // The window proc is where messages get processed ////////////////////////////////////// LRESULT CALLBACK WndProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { HANDLE_MSG(hWindow, WM_DESTROY, Window1_OnDestroy); default: return Window1_DefProc(hWindow, Message, wParam, lParam); } } ////////////////////////////////////// // Handle WM_DESTROY message ////////////////////////////////////// #pragma argsused void Window1_OnDestroy(HWND hwnd) { PostQuitMessage(0); } 

This program looks like, and indeed is, an old-fashioned Windows program from back before the days of the object frameworks such as OWL or the VCL. It will, however, compile unchanged in BCB. In fact, I did not have to change any of the code or any of BCB’s compiler settings in order to create this program.

To get started producing this code from scratch, open up BCB and start a new project. Go to View | Project Manager and remove Unit1 from the project. Now save the file to a directory created specifically for this program.


TIP: I believe you should always create unique directories for each program you create. If you do not take this step, you will never be able to keep the files from this program sorted from files used by other programs you create. To not put a BCB project in its own directory is to go face to face with the forces of chaos.


Go to the View menu again and choose the Project Source menu item. Strip out all the code created for you by BCB and replace it with the code shown above. You are taking control of this project and don’t need any help from the BCB or its rudimentary code generation processes.

In the next chapter I will explain in some depth why BCB should not be considered a code generator, a CASE tool, or anything of the kind. I do, however, feel that BCB is primarily an IDE-oriented tool. In this one case, it would not matter much whether you wrote the code inside the IDE or from the command line. However, BCB code is meant to be written, and your programs are meant to be designed, inside the IDE.

This is not the place to get into a lengthy explanation of how the WinAPICode program works. If you are truly curious, it is taken nearly verbatim from one of the early chapters of my book called Teach Yourself Windows 95 Programming, published by Sams Publishing. That book covers raw Windows API programming, which is an invaluable skill, even for BCB RAD programmers. However, unraveling the secrets of the Windows API is not the goal of this current book, so I will merely point out a few key passages from the program.

The following code shows the message loop for the WinAPICode program:

while (GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } 

This is the engine that drives the program, but it is unlike any of the code you see in a standard BCB program. It is not, however, any different from the message loop that appears inside the VCL. I will show you how to step into the code that contains that loop in Chapter 2.

At first, the following code also looks completely foreign to the BCB paradigm:

LRESULT CALLBACK WndProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { HANDLE_MSG(hWindow, WM_DESTROY, Window1_OnDestroy); default: return Window1_DefProc(hWindow, Message, wParam, lParam); } } 

It would, however, be a mistake to assume the VCL knows nothing about Windows procedures. In fact, you can add code to your BCB programs that gets called every time the WndProc for your program or any of its forms gets called. Once again, I am in danger of wandering too far afield, but if you open up Forms.hpp from the include/VCL directory, you will find the following declaration:

virtual void __fastcall WndProc(Messages::TMessage &Message); 

This call is one of the methods of TForm. As you can see, it is declared virtual, so you can override it in any of your own programs if you want. By doing so, you place yourself directly inside the window procedure for your form. This is an extremely powerful technique, but is one that most programmers will never need to utilize. However, it is good to know that it is available if you need it. I give an example of how to override this method in Chapter 4.


NOTE: The TMessage structure that is passed to the WndProc method of TFormcontains all the fields that are passed to a standard Windows procedure:

struct TMessage { unsigned int Msg; union { struct { unsigned short WParamLo; unsigned short WParamHi; unsigned short LParamLo; unsigned short LParamHi; unsigned short ResultLo; unsigned short ResultHi; }; struct { long WParam; long LParam; long Result; }; }; };

All the information is there, if you need it. However, my point is that there are few cases in BCB programming in which it is necessary to get down to this level. Once again, this subject will be taken up later in greater depth. In particular, I discuss TMessage and related structures in Chapter 4.


In this section of the chapter, I have shown that BCB allows you to get down to the raw Windows API level if you want. There is, of course, nothing involving the Windows API you cannot do in BCB, just as there was nothing involving the Windows API that you could not do in either BC5 or Delphi. It would be silly and fruitless to try to hunt for exceptions. Callbacks, pointers to pointers, COM, whatever it is you want to do; BCB is up to the challenge. My point in this section is simply to show the great technical depth of this product.

The DynamicButton Program

The DynamicButton program, shown in Listing 1.4, demonstrates how to use code to do all the same tasks you would normally do with the visual tools. Needless to say, I am not showing you this program as an example of how to program VCL projects. Instead, I want to broaden the common base of understanding regarding the way the VCL works. The goal is to demystify the visual tools so that you can see that they do nothing magical or special but only execute code in the background automatically, so that you are spared the laborious task of writing the same lines of code over and over again.

Listing 1.4. The header for the DynamicButton program.

/////////////////////////////////////// // File: Main.cpp // Project: DynamicButton // Copyright 1997 by Charlie Calvert #ifndef MainH #define MainH #include <vcl\Classes.hpp> #include <vcl\Controls.hpp> #include <vcl\StdCtrls.hpp> #include <vcl\Forms.hpp> class TForm1 : public TForm { __published: // IDE-managed Components TButton *CreateDynamicButtonBtn; void __fastcall CreateDynamicButtonBtnClick(TObject *Sender); void __fastcall DynamicClick(TObject *Sender); private: // User declarations TButton *MyButton; public: // User declarations virtual __fastcall TForm1(TComponent* Owner); }; //-------------------------------------------------------------------------- extern TForm1 *Form1; //-------------------------------------------------------------------------- #endif 

Listing 1.5. The Main source file for the DynamicButton program.

/////////////////////////////////////// // File: Main.cpp // Project: DynamicButton // Copyright 1997 by Charlie Calvert #include <vcl\vcl.h> #pragma hdrstop #include "Main.h" #pragma resource "*.dfm" TForm1 *Form1; __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } void __fastcall TForm1::DynamicClick(TObject *Sender) { AnsiString S("As you can see, I have an event associated with me. " "Also, you can see the other button on the form has been " "grayed out. Charlie explains all this in Chapter 1 of " "the book. "); ShowMessage(S); } void __fastcall TForm1::CreateDynamicButtonBtnClick(TObject *Sender) { MyButton = new TButton(this); MyButton->Parent = this; MyButton->Caption = "Push me: I'm a Dynamic Button"; MyButton->Width = 250; MyButton->Show(); MyButton->OnClick = DynamicClick; CreateDynamicButtonBtn->Enabled = False; } 

Here is code that creates a button dynamically:

MyButton = new TButton(this); MyButton->Parent = this; MyButton->Caption = "Push me: I'm a Dynamic Button"; MyButton->Width = 250; MyButton->Show(); MyButton->OnClick = DynamicClick; CreateDynamicButtonBtn->Enabled = False; 

The first two lines of code duplicate what happens when you drop a component on a form. First, the button is assigned an owner that will take care of memory allocations. Next, the button is assigned a parent, so that Windows will know what surface to draw the button on.

The last sentence contains a very important point. The VCL never does anything more than call standard Windows API functions. It’s not as if these objects completely take over Windows and do things mysteriously in some dense black box that cannot be understood. Instead, they wrap simple Windows API calls or wrap entire Windows API classes, such as the button class. Underneath, you just have the same old button that you see in any standard Windows program. The job of the VCL is simply to make it easy for you to use these standard buttons.

The code then sets the Caption and Width for the component. If you wanted, you could also have set the Left, Top, and Height properties of the component.

To make the component visible, simply call its Show method. This will ensure that the object is made visible to the user.

The next-to-last line of code in the method sets up an event handler:

MyButton->OnClick = DynamicClick; 

This line of code is comparable to double-clicking a button at design time to set up an OnClick event handler. As you will see in Chapter 4 the whole delegation model is based on the simple task of assigning a method pointer to a method.

Right now it is not terribly important that you understand how events work. Instead, I just want to show you that you do the same things in code that you do with visual tools. In particular, you can create components dynamically, assign values to their properties, and set up events. This is what RAD is all about; the VCL environment makes it easy for you to do these things with visual tools, and the code shown here demonstrates how to do the same task in code.

The only key piece of the RAD puzzle not demonstrated by this program is the art of making components. But that truly is a more complicated subject, and one that I will broach only at the appropriate time.

In the last few sections of this chapter, you have seen a number of programs that demonstrate key features of BCB. I have shown you these programs not so that you can understand them in their entirety but as an introduction to the VCL and to the material covered in this book.

During the course of Chapter 2, the core technical content of this book will be introduced bit by bit. By the time you reach Chapter 3, the story will be in full swing, and it will not let up until the last page. The remainder of this chapter, however, provides overviews of important matters of general interest.

On the Structure and Contents of This Book

I will now go on to give an overview of the contents of this book, as well as highlight certain ideas and programming techniques that permeate most of the rest of the text. Few of the ideas presented in the rest of this chapter are of earth-shattering importance, and you are free to skip them if you are in a hurry. I include them here only to help clarify my purposes in this book and to create a common foundation for the ideas on which the rest of this text will build.

Program Builders Versus Component Builders

An important distinction can be made between people who do low-level work and people who build entire programs. For the moment, let me call the people who do low-level work component builders or tool vendors, and those who do high-level work program builders or program designers.

Suppose you are a program builder and you develop a need to convert files of type PCX to type GIF. There are two things you can do:

1. You could find a book on file formats and start writing the code for converting PCX files to GIF files.2. You could search the Net, bookstores, and vendors, such as Programmer’s Paradise (http://www.pparadise.com), for components, objects, or libraries that will do this for you.

Here is the moment of truth. What do you want to be: a component builder or a program builder? Most programmers do a little of both, but there comes a time when you have to decide which camp you will call home. You might think that you live in both worlds, but the odds are against it!

Stop and think for a moment about the PCX-to-GIF creation tool. Here are some of the factors you need to take into account:

1. You can’t just assume there is only one version of the PCX and GIF file format. Both standards have been around for a long time, and there are undoubtedly several versions of each format.2. You can’t just work with 16-bit color files; you need to be prepared for 8-bit, 16-bit, and 24-bit color–at a minimum! And don’t forget that new formats might come out, so you might have to update your code!

3. You have to create code that is reusable and maintainable. You can’t just throw this code together. What happens if you need to add a new format to your program? What happens if you need to move from 32-bit Windows to 16-bit Windows? What if you wanted to start supporting OLE automation? You have to structure your code so it can be maintained.

4. Think long and hard about bugs. How clean is your code going to be? What about corrupt PCX files? How about testing on a wide range of systems? What about all the different size bitmaps people might want to make. What if you find that your code works fine under most circumstances, but all of a sudden a user from Germany complains that he wants 1152×864 size bitmaps and that your code is messing up the last 20 rows of pixels. You tested 1024×768, 800×600, and 640×480 bitmaps, but you never thought about 1152×864 bitmaps. In fact, you didn’t even know that this was a standard screen size supported by Windows 95 and used by lots of people all over the world! Where are you going to find a computer that can even show screens at that resolution?

Take all the points listed above into consideration. How long is it going to take you to create code that is at least reasonably bug-free and safe? One week? Well, that’s probably optimistic. Maybe two weeks? Well, possibly, if you are very good, and if you add in maybe one week more for bug fixes. But let’s be realistic. For many programmers, a job of that size might take a month, or even two. That’s just to release the beta. Then you enter into another month or two of testing, bug fixing, and updating.

Now, how long is it going to take you to find a component that does the same thing? Maybe you could get one in an hour on the Internet. It might even be free, and it might even come with source. Maybe you will have bad luck–and it might take five hours to find the code, you have to pay some shareware low-level stud $50 for it, and all you get are the binaries. (Bad sign. You want that source code!) Or maybe, worst-case scenario, you can’t find the code out there anywhere on the Net, and you have to go to Programmer’s Paradise (1-800-445-7899, http://www.pparadise.com) and actually buy the code for some big-time $150 to $200 expenditure!

Think: $150 now, plus three hours to learn the product, means I could have this portion of the code written by tomorrow at noon. Or, maybe I should do it myself and spend four weeks on the project. Four weeks of work is 4×40 hours a week; that’s 160 hours. Now suppose the following:

1. I’m a hotshot consultant being paid $100 an hour for my work. 160 hours times $100 is $16,000.2. I’m a struggling corporate type being paid $25 an hour. 160 hours times $25 is $4,000.

3. I’m a hotshot housewife hacker who has three shareware products on the market. My husband is complaining about being a computer widower, but he kind of likes the extra $15,000 I brought in last year with my products. A month or two of weekends and weeknights hacking the PCX format might finally force a real domestic crisis! After all, there would be no time for dinners out and no time for movies or concerts. Just that darn GIF format! Or, maybe I should just bite the bullet, buy the software tools for $150, and maybe skip having dinner and a movie out this one week. Which is worse, another shoot-out at the domestic corral or maybe that one painful $150 expenditure?

My point here is that being a low-level programmer is not necessarily the intelligent thing to do any longer. It used to be that all the smart people learned to hack the low-level code, and their effort made them very successful. Now, however, the smart people find ways to avoid writing the low-level code!

Of course, if you work day in and day out with components, you need to know how to build them as well as use them. This book will teach you enough about components to show how to create them when you need them. I will cover all the key areas, including the major sticky points that trip up beginners. However, I will not focus on the subject to the exclusion of other, perhaps more important, themes.

The Case for the Low-Level Programmer

The majority of readers of this book will fit into the program builder, component user category. Of course, there are others who will want to take the opposite tack. These are the programmers who want to make programming tools for a living. In particular, if you think about the facts I’ve outlined above, it must be obvious that there is a market for people who build programming tools.

It happens that I spend a good deal of my professional life talking to programmers who build tools for a living. I’ve been working at Developer Relations at Borland for several years now. The primary job of Developer Relations is to work with independent software vendors (ISVs) who build tools that can be used with Borland’s programming tools.

During my years at Developer Relations, I have met many people who build programming tools for a living. Some of them work alone, some of them work for small companies, and some of them work at large companies and build tools on the side as a moonlighter. Many of them work part-time as writers, part-time as consultants, and part-time as tool vendors.

I understand that there is a thriving market for tool vendors. In fact, I want to say as loudly as possible that if you want to build tools for a living, that is certainly a reasonable goal. It is not necessarily an easy way to make a living and not necessarily a very romantic way to make a living, but it can be done.

The trick, however, is not to get caught in some never-never land between being a program developer and a component or tool developer. Don’t start to build a program and then end up spending months building the tools you want to work with! In that way lies madness!

This book shows quite a bit about how to build components, and how to add component editors and property editors to your tools. However, if you want to work full time on components, you should use this book as a starting point and then go on to read Delphi Component Design, (Addison Wesley, by Danny Thorpe) and Delphi Components, (Coriolis Group, by Ray Kanopka). These guys work in Object Pascal, but they take the VCL down to the bare metal. Of course, I’ve read those books, so many of the hot techniques these authors cover will be included here. But as a rule, this book is about program building, not about tool building!

Though I cover components in considerable depth, this book is aimed primarily at program builders. Whether you create shareware, work for a corporation, or work as a consultant, the goal of this book is to show you how to use components to create programs.

I will definitely take time to talk about the Web, about what’s on the Web, and about how to find things on the Web. I will talk about companies like Programmer’s Paradise. I will show you what is available, and I will supply many tools on the CD that comes with this book.

Types of Technical Books

There are three types of technical writing commonly used today:

Reference: These texts contain lists of information. For instance, a common reference book might contain a list of functions in one or more APIs. The reference would state the name of the function, where it can be found, its purpose, and its parameters.

Tutorial: These texts step you through a process. For instance, a tutorial might explain how to create a form by walking you through the process one step at a time. Here is some sample text from an imaginary tutorial: “First create a new form. Now drop a button on the form and use the align dialog to center it at the bottom of the window.”

Discursive (explanatory): A discursive, or explanatory, text attempts to explain a concept in plain English. These texts work with concepts and theories and frequently attempt to take complex subjects and explain them in clear, easy-to-understand language.

This book uses all three of the techniques described above, but my primary emphasis is on the third. The decision to focus on this third technique is in part a natural consequence of my own talents and inclinations. However, I have also spent considerable time thinking about how to write technical books, and I have concluded that the discursive technique is the best one to use when explaining complex material such as a compiler.

The problems I have with reference books include the following:

  • They are intensely, even painfully, boring to read.
  • They present material on paper, even though reference materials are perhaps best kept in online help files. In other words, I think reference texts are slowly being moved from paper to binary format, because binary formats allow the users to search quickly for the specific information they need.
  • There is no question that some reference books are invaluable; but as a rule, this technique is not useful when trying to teach someone a new language or product. Reference books don’t teach people anything; they are simply a useful tool that can aid a programmer.

For some reason, tutorials are a favorite technique among managers. They appear to be a shortcut to a particular end, and like downsizing, they seem to promise easy profits with little work. Indeed, like layoffs, one can gain a lot from a tutorial in a short period of time. However, tutorials are plagued by the following problem:

  • They are brutally boring, though not quite as mind deadening as a reference. You usually know where a tutorial is headed, so there is little sense of surprise or anticipation.

Tutorials force you to constantly move back and forth between a text and your computer. This is a very uncomfortable thing to do. You read a sentence, try something on your computer, and then try to find your place in the text again, only to be immediately torn away and forced to find your train of thought onscreen. I find this an extremely unpleasant and distracting process that I have never managed to pursue for more than an hour at a time. Usually, I find some excuse to escape from a tutorial after about 10 or 15 minutes.

Discursive, or explanatory text, has the following advantages:

  • It teaches you the theory behind an idea. After you understand the theory or the concepts behind an idea, you can perform the entire action on your own, unaided. A tutorial might step you through 30 moves to show how a single concept works. Conversely, if you grasp a single concept correctly, you can usually intuit the entire 30-step process needed to bring it to fruition.
  • You can read a discursive text straight through without interruption. You need not read a few sentences, go to your computer, and then come back to your text. You can concentrate first on the text and then on the computer, which is more pleasant and palatable.
  • Discursive text has some intellectual tension inherent in it, which can, at least on occasion, make it enjoyable to read. For instance, a discursive text can pose a problem and then take you on a small, intellectual excursion while you learn the solution to it. When a matter is posed in this way, the reader has his curiosity peaked and then gets a chance to, as it were, solve the problem with the writer of the book. This creates intellectual tension, excites curiosity, and gets the reader’s mind working. In other words, a discursive text can be useful because it is interesting to read, whereas references and tutorials tend to gather dust simply because they are so dreadfully boring.

Once again, I want to emphasize that I use all three techniques in this book. There are many miniature tutorials in this text, and on occasion I will allow my prose to degenerate into a reference if I feel it is helpful. As a rule, however, I try to make this text as interesting as possible and provide you with a book that is at least somewhat compelling to read.

In particular, I am much taken by the power of computer languages and by the intellectual excitement inherent in the act of programming. It’s fun to program, and writing code is one of the most intellectually engaging tasks I know. If a subject is this interesting, the text written about it should also capture some of that excitement. The Internet, GDI, DirectX, OLE, and other topics are innately interesting, and I write about them with as much energy and enthusiasm as they engender in the best programmers.

Programming Theory

In keeping with the discursive theory of writing, this book tries to encourage the development of general solutions to technical problems. In short, when solving problems, I often find solutions that work in one instance, but that aren’t applicable to general instances. My goal in this book is to look beyond single instances to find the common threads that run through a particular type of programming problem.

To grossly oversimplify the matter, consider the following function:

int AddTen(int Value) { return Value + 10; } 

This function adds 10 to whatever number you pass into it.

Now consider the following function:

int Add(int Value1, int Value2) { Result = Value1 + Value2; } 

This function adds two numbers together.

The first function presented above solves one specific problem. The second function solves a general class of problem.

When reduced to an example as simple as the one shown above, it would seem like programmers would always opt for general solutions rather than specific ones. The second function requires two parameters rather than one, but it obviously has considerably more power. As a result, most programmers would choose it over a function like AddTen, which has less flexibility and less power.

Of course, in the real world, there is a much greater temptation to select specific, rather than general, solutions. Some problems are hard to find general solutions for, and as a result, programmers tend to find simple solutions that solve the immediate problem, rather than looking for a more general solution. However, the key point here is that general solutions can be reused, whereas specific solutions are generally good for only one try. It is therefore usually worthwhile spending extra time trying to find general solutions whenever possible. It is also often worth using up one or two clock cycles on a general solution rather than constantly writing custom solutions tailored to speed up individual tasks. In other words, I often prefer a general solution that takes up 50 clock cycles to a hand-crafted solution that takes up 40 clock cycles.

Throughout this book, I often take the time to find general solutions to problems. Sometimes this will make it appear that I am going the long way around when doing something. For instance, the first example above is briefer, more concise. It takes only one parameter rather than two. Isn’t it therefore better, more highly optimized, than the second example? Well, in a word, no. Sometimes it’s better to write a little more code in order to save room in the long run. One method with two parameters is better than 50 methods with one parameter, even if at first it appears to be the long way around.

About Comments

I tend to avoid using comments in my programs. However, I will occasionally jot notes at the top of a module or right before a potentially complicated or confusing procedure.

I am adamant about not including comments inside a block of code or inside a class declaration, except under the most extreme and unusual circumstances. Code is hard enough to read as it is, but adding comments to the middle of a block of code leads to the worst kind of cognitive dissonance.

As a rule, I limit comments to single block before the implementation of each method, or a longer section at the top of a module. This way, you can view the code without having to simultaneously wrestle with reading comments. When you need the comments, you can scroll up to find them.

RAD Versus the Command Line

Some long-term C programmers find themselves shying away from BCB’s components, experts, and other visual programming tools. Indeed, there was a time when it made sense for some programmers to eschew fancy environments and to stick to a “command-line ethic” that involved working mostly with simple text editors and the Make utility. After all, there was a time when the IDE merely helped to simplify the effort involved in learning certain tasks. During that phase, the IDE did not necessarily speed up or improve development; it just made things easier. BCB, however, actually brings tools to the table that outperform anything you can do from the command line.

If you have decided to work in BCB, you have, in a sense, crossed the Rubicon. There is no point in going back to the old command-line way of thinking. BCB is a rapid application development tool, and the correct attitude is to find ways to increase your ability to utilize the IDE, and even to come up with suggestions for how the Borland team can improve the IDE. In fact, you can even create your own experts that will improve the IDE itself.

One of the burdens of this book is to convince even the most recalcitrant command-line programmer that RAD programming, when done right, is every bit as technical and every bit as macho as the old “all I need is Brief and a makefile” way of viewing programming.


NOTE: Brief is an old text-based editor that has always been very popular in the C programming world. Makefiles, of course, are still used in Builder. However, there is rarely a reason why BCB programmers should ever need to write or edit a makefile themselves. BCB uses the make syntax primarily because it is a convenient, text-based way to manage projects. Some members of the BCB team prefer text-based project management to the kind of binary file currently used by Microsoft, and previously used by the Borland C++ products.


The point I’m making in this section is not that programmers should give up the old, hard-core systems view of programming. All of that is very much a part of good RAD environments such as BCB. Everything that you know about the Windows API, assembly language, and the art of memory management applies just as much to BCB as it did to BC5 or MSVC. Good systems programmers are as badly needed in BCB as they were in BC5.

What, then, is the difference between programming BCB and programming BC5 or VC++? One thing that has changed is the number of people who can play the game. In BC5, only a relatively small set of programmers could make the grade. With BCB, more people can manage to acquire true competency in the product.

BCB programmers are divided into two groups. There are the expert system programmers who spend most of their time designing objects and creating components. The second group of programmers are the consumers of these components. It often takes a great deal of skill to create the components, and considerably less skill to use them.

Of course, in many cases, the same programmer will be both a creator and a consumer of components. This means some of the time you will have your systems programmer hat on, and some of the time you will just be a RAD programmer who produces programs in an incredibly short period of time.

The beauty of BCB is that it allows you to wear both hats. When you need to be productive, you can use BCB to put programs together faster and with more ease than any Visual Basic programmer. At other times, you can get back into that command-line ethic and dig into the Windows API, into the system’s code.

My personal belief, however, is that 95 percent of the time, the command-line ethic should just be a metaphorical view of the world. You shouldn’t literally program at the command line with Brief and a series of makefiles. BCB is bringing that world to a close. But you want to hang onto that tough “I’m a real programmer” attitude. That’s something that’s very much a part of the BCB programming world.

One final word, just to avoid any misunderstandings: You will find that at times I do program from the command line, and I will discuss techniques for doing so with BCB. In other words, I am an advocate of hard-core Windows API system programming. I have spent much of my C/C++ programming career working from the command line with Brief and a series of makefiles. I believe in that heritage, but I think it is important to recognize the sea of change that BCB is introducing into the programming world. BCB is a RAD tool based inside an IDE, not at the command line.

Of course, part of the fun of the programming world is that it allows people to maintain a sense of identity. I’m sure there will be some successful programmers who never come over to the RAD way of thinking. I’m not saying “Beware, your career is toast unless you get this new visual programming paradigm!” That’s not my point at all. However, there is something pretty important going on here, and like Dylan’s Mr. Jones, you’ll probably be better off if you figure out what it is.

On Builder’s Object Pascal Origins

BCB is an unusual product in that it is a C++ tool built on top of an Object Pascal object framework called the Visual Component Library, or VCL. In fact, all of the VCL, and almost all of the IDE, is written not in C++, but in Object Pascal.

The reason for this unusual structure is simply that BCB is built on top of the architecture for the successful Object Pascal-based Delphi product. Delphi solved many of the difficult architectural problems inherent in creating a high-performance, compiled, object-oriented language that could be elegantly hosted in a RAD development environment. It was natural to build BCB on top of this framework, even though it is C++-based and Delphi is Object Pascal-based.

I am well aware of the fact that, for many people, BCB’s Object Pascal heritage will be considered a rather serious drawback. For many programmers, a reliance on Object Pascal immediately implies that the product must be both slow and somehow crippled. This is a natural position for a C++ programmer to take, and for those who have never been exposed to Object Pascal, it is perhaps the only possible conclusion to reach.

It happens, however, that I have two great loves in the programming world: C++ and Object Pascal. This puts me in a somewhat unusual position, which is often rather awkward. However, it does give me good credentials for writing this book, and hopefully it qualifies me to at least attempt an explanation of the true nature of the relationship between BCB and Delphi, and between C++ and Object Pascal.

As a big fan of both Object Pascal and C++, I want to take a few moments to clear up some commonly held opinions that I regard as erroneous. Some of the most important of these misconceptions I can simply list in a few short sentences:

  • There are many people who think that C++ is better than Object Pascal because it is faster. This is simply untrue. There is no significant difference in speed between the two languages. For instance, the 32-bit version of Delphi uses the same compiler as Borland C++ 5.0 and as BCB itself. Both languages produce the same code; they just express the ideas behind the code somewhat differently. The equality of speed between the two products was equally true in Borland’s Windows and DOS implementations of C++ and Object Pascal. This does not mean that you won’t find particular cases in which one product is faster than another, but the overall trend is toward two products that have the same performance level.
  • It is also untrue that C++ produces smaller, tighter code than Object Pascal. In fact, the advantage in this particular issue probably resides with Object Pascal.
  • There are many people who think there are limitations inherent in Object Pascal that make it incapable of performing certain functions. This is also completely untrue. For instance, many people simply assume that you can’t express the idea of a pointer to a pointer in Object Pascal. To those who know Object Pascal, the very idea of this objection is ridiculous. Of course, you can express that concept or any other fundamental programming concept in Object Pascal. It’s a general purpose programming language just like C++. It’s not an innately limited language like Visual Basic.
  • Some people think that the Pascal language stopped growing in 1970. Object Pascal is not your father’s Pascal any more than C++ is the same language that Kernighan and Ritchie invented those many long years ago, back when the earth’s crust was still cooling. In fact, Object Pascal is a much more dynamic language than C++, and it has changed to adopt to the latest programming developments in a way that C++ cannot hope to change, due to the nature of its committee.

Okay, so some of the objections to BCB’s reliance on Object Pascal can be eliminated. It does not have an effect on the speed or size of the executables you produce. In fact, if the whole product were written in C++, the code produced by BCB would not be any smaller or faster. Nor does the reliance on Object Pascal mean that BCB is innately incapable of performing certain functions. You can do anything in Object Pascal that you can do in C++, so the VCL is every bit as flexible and powerful as a C++ framework.

Does this then mean that there are no drawbacks to BCB’s Object Pascal heritage? Well, I would have to stop short of making quite so bold a claim. There are some problems, some of which are more than minor annoyances, that result from BCB’s heritage.

The first and most significant problem is that the VCL is indeed written in Object Pascal, so you have no C++ source that you can peruse to find out how your object framework is put together. This does not mean, however, that there is no source for you to look at. The Object Pascal source ships with the product, and if you have any understanding of Object Pascal, you should be able to read it to see what BCB is doing in the particular cases when you really need to know. You can also link this code into your project and step through it if you desire, as shown in Chapter 2.

You will also find that that VCL sometimes expresses concepts in a manner somewhat foreign to C++ programmers. This can be a bit like listening to a very intelligent foreigner speak English. What they are saying makes sense and might even be rather eloquent; but at times their choice of words, or their word order, or just something about their accent, betrays the fact that they are not native-born English speakers. Ninety percent of the time, this fact might not even be noticeable, but occasionally it will surface.

Xenophobes always tend to assume that the awkwardness they perceive in a foreigner’s speech is due to some inherent limitation in that person’s culture. From my experience, as someone who knows both C++ and Object Pascal, I find that this type of analysis of the two languages is flawed. For instance, there are some things Object Pascal does better than C++, just as there are some things C++ does better than Object Pascal. And it is only natural that some people will prefer one language to the other, just as someone might prefer French to Spanish, or vice versa. But the truth is that there might be some things that sound wonderful in French that sound rather prosaic in Spanish, but that doesn’t mean French is better than Spanish. The issue, of course, is that you can almost always reverse the tables by finding something that sounds wonderful in Spanish, but doesn’t sound so good in French.

In short, it’s best not to judge these matters too quickly. C++ and Object Pascal are both extremely sophisticated languages, and the marriage between them found in BCB may prove to be a much better match than some suppose. In fact, the ability to pull the best from both languages may prove to be an advantage for the product, rather than a drawback!

To close this section, I should perhaps add that I am not an advocate of either language over the other language. I like both languages equally. C++ has a richness and syntactical fluidity that can make Object Pascal appear quite naked, and, conversely, Object Pascal has a simplicity and elegance that can make C++ appear rather florid and overdone.

Ultimately, I believe the only crime is to be a language bigot who assumes his language is better than the other guy’s language due to a classic case of contempt prior to investigation. Almost all the programmers I know who are truly fluent in the latest incarnations of both languages tend to agree with the conclusions I am reaching here. The hardcore Object Pascal and C++ bigots that I have met are usually only truly conversant in one language or the other. And strangely enough, it’s always the language that these people know well that they consider best!

Creating Programs Quickly

In the last days of DOS, back in the early ’90s, I remember a stage where many of the people I knew were creating great utilities that we could all use. In fact, many of the programs that I ran day to day were utilities that I or my friends had created in C or in Object Pascal.

The introduction of Windows soon put an end to that outpouring of great utilities. Windows was more fun that DOS, and more interesting, but it usually took a good deal of work to create a program that was the least bit useful.

Now, after five or six years of work, Windows is finally at the stage where programmers can again feel as though they are productive when using common programming tools. Using C++Builder, you can put together all kinds of useful applications in a short period of time.

Part of the fun of programming is the ability to create your own programs for your own needs. It’s much more fun to use one of your own programs than it is to use a program written by someone else.

C++Builder is one of the set of exciting new tools that puts the fun back in programming. Using this tool, you can create your own applications in days or hours, rather than in months or years. This makes our managers in our assorted corporations happy, but more importantly, it helps us put the fun back in our own jobs.

Most programmers are in the business because they got bitten by the excitement of this profession at some point in their careers. Most of us have been able to stay with the fun for a long time. Some programming experiences, however, can be too much like drudgery and too little like a good time. C++Builder helps make our careers enjoyable. That’s a very strong statement, when you come to think about it, but I believe it to be true. The great thing about RAD is not what it does for corporations, but what it does for you, the programmer!

Use the Source, Luke!

Some versions of BCB ship with not only the invaluable header files found in the include directory, but also the original Pascal source to the VCL. You should become familiar with both “sources” of information.

The key include files for BCB are in the ../BCB/include/VCL directory, where the first part of the path references the place where you installed BCB. You should become familiar with all of these files, but as you will see in the next two chapters, two of the most important are the ones called sysdefs.h and dstrings.h.

My Favorite Program: Grep

BCB ships with a command-line utility program called Grep.exe. This humble 73,000-byte program is one of the most useful tools in my programming arsenal. I use it to scan over tens of thousands of lines of source code for examples that I can use.

For instance, suppose I have just finished reading an article on exceptions and I want to add them to my program. Perhaps one of the first lines of code I write looks like this:

catch(runtime_error) 

Unfortunately, when the compiler reaches this line of code, it pops up a message about runtime_error being an unknown identifier. Having been down this road a number of times before, I know immediately what is wrong: I need to include a header file in my project that declares runtime_error. The question, of course, is which file is it that I need to include?

If I’m lucky, I can put my cursor over the word, press F1, and be taken to an online example that will show me how to use the code and which files to include with my project. However, it’s quite possible that

  • The online help is broken.
  • The online help is incomplete.
  • The online help is working and complete in this instance, but this particular reference was apparently written in under 30 seconds by a harried individual who at least shows signs of being under the influence of drugs that might increase a person’s production, but at the expense of his or her fundamental reasoning powers.
  • The code I’m looking for is from a proprietary library that has no help file.
  • I wrote the function or identifier I’m searching on myself, and therefore there is no online help on it.
  • This is a beta version of a product, and as a result the documentation is not yet complete.

Of course, the first three options are out of the question for a Borland product–say what?–but the last three occur from time to time. When you are stuck in this kind of situation, Grep is one of the best ways out.

What I normally do is go down to the root include directory for BCB, and type

grep -id "runtime_error" *.h* 

This command causes Grep to search over the include directory, and all subdirectories beneath it, for the string "runtime_error", without regard to case, as it appears in files that have an extension that begins with an H. In particular, the -I switch says search without concern for case, and the -d switch says search in subdirectories. I sometimes add the -l switch, which tells Grep to just return the names of files that include the string and not to quote any occurrences of the string:

grep -lid "runtime_error" *.h* 

Here is the result of running the first command:

File STDEXCEP.H: class RWSTDExport runtime_error : public exception runtime_error (const string& what_arg): exception(what_arg) {;} class RWSTDExport range_error : public runtime_error range_error (const string& what_arg): runtime_error(what_arg) {;} class RWSTDExport overflow_error : public runtime_error overflow_error (const string& what_arg): runtime_error(what_arg) {;} File STDMUTEX.H: runtime_error, runtime_error, runtime_error, runtime_error,

Here is the result from running the second command:

File STDEXCEP.H: File STDMUTEX.H: 

Needless to say, there are many times when I find it useful to Grep across the directories for the sample programs that ship with a product, across the directories for the Microsoft SDK, or across the sample directories in the CD for a book. For instance, if I wanted to see not the declaration for runtime_error but the way it is used in a program, I would Grep across sample directories, looking for *.c* rather than *.h*.

When I am writing a book like this or when I am programming a new API with which I am not familiar, I might use Grep as often as 10 times a day. There are, of course, many Windows-based versions of Grep, and I’m sure there are even some available that will integrate directly into the BCB IDE. However, I rarely use any of these tools for long. For one reason or another, I find it simplest just to use the plain old, humble, command-line version of grep.

If you grep across directories and get many lines in return, use the more command to browse through one page of information at a time:

grep -id "WinINet" *.cpp | more 

If you want, you can also send the output to a text file:

grep -id "Windows" *.htm > WebResults.txt 

Hardware Requirements for Using BCB

BCB needs at least 20MB of memory, and really comes into its own if you have 32MB or more. Trying to use it on Windows NT with less that 32MB of memory is probably too frustrating.

You should have a Pentium 120MHz or higher computer, with at least 100MB of disk space free on the drives where you store BCB and your projects. That is 100MB free after the installation, not before!

If at all possible, you should run at a screen resolution of 1024×768 pixels. 800×600 is probably acceptable in most circumstances, though it is far from ideal. You can also run at 640×480, but you will spend a lot of time shuffling Windows around. (I should know, because I use this resolution a lot when I am on the road. Its appeal wears thin over time.)

In this day and age, you really should try to get not just a good machine, but an ideal machine. At the time of this writing, the ideal machine is a 200MHz Pentium Pro with 64MB of memory and a 4GB SCSI drive. The machines I wrote this book on were 120 and 133MHz Pentiums with 32MB of memory. One was a laptop with 1.2GB of hard drive space, and my home machine has about 3GB of hard drive space. Throughout the whole process, I was shameless enough to long for yet more power, more memory, and more hard drive space. If Ecclesiastes were writing today, I’m sure he would have added: “Of the longing for more MIPS, there is no end. This also is vanity and a striving after wind.”

Getting Updates for C++Builder Unleashed

If you want to find updates for the code, you can visit my Web site at users.aol.com/charliecal. If AOL stays afloat, and if its rates remain reasonable, it will remain my online headquarters indefinitely. A backup site is http://www.supersonic.com/calvert.

I am afraid that I reserve the right not to answer questions sent directly to my e-mail address. However, if you cannot find my Web site or if you have a bug to report that is not addressed on my Web site, you can send me e-mail and I will attempt to get some form of answer back to you. If you just want to say hello, to pass the time of day, or to rag on me regarding some portion of this book, of course you should feel free to write, and I will try to respond if at all possible.

Every effort is made to assure that the code in this book works correctly. However, if you see something in the book that does not look exactly right, check the CD to see if it is corrected in the electronic version. If that still doesn’t look right, check my Web site. If all else fails, send me mail.

Be sure to find the readme file on the CD and to examine it carefully. You should find both a text-based and an HTML-based version of the readme file.

You can write me at ccalvert@wpo.borland.com, 76711.533@compuserve.com, charliecal@aol.com, or 71601.1224@compuserve.com. If you want to send a binary file to me via the Internet, write to ccalvert@corp.borland.com. I tend not to answer technical questions sent to me by mail, but I can give you updates on the location of my Web site and about where to search on the site for answers to your question.

My Web site contains links to places of interest on the Web, including other major BCB or Delphi Web sites and to vendors of BCB components. If you have never visited a really hot, privately maintained, product-specific Web site, you are missing out on one of the more valuable and interesting aspects of contemporary programming. Programmers who can somehow carve some free time out of their schedules often create fantastic Web sites with lots of free components, tech tips, and news of general interest. I personally don’t have time for that kind of endeavor, but I try to list links to the most important of these sites on my Web pages.

Adapting to the Tides of Change

To sum up this introductory chapter, it might be helpful to simply focus on the subject of change. Clearly this book is very different from most C++ programming books. Instead of concentrating on the C++ language or on the Windows API, I am focusing on components, databases, games, and on the Web. Furthermore, I am saying that I am doing this because I believe this is the best, even the mainstream, way to build programs in these waning years of the twentieth century. If I am right about all this, that means that the programming industry is changing.

There will probably be a time when the waves of rapid change that have been washing over the computer industry will at last subside. But for now, and for the foreseeable future, we can look forward to change and then even more change.

C++Builder is part of one of the big waves of change that have come washing up on our shores of late. This is a RAD-based tool with powerful component and database capabilities. This is a more powerful tool than what we had before, but it is also a new tool for most users, and that means we have lots to learn.

During these times of change, the only way to survive is simply to adapt to the idea of living in a constantly shifting landscape. In fact, it’s not a landscape at all, but a shifting sea of technology that looks different depending on the flow of the tide, the force of the wind, and the hour of the day.

Those who don’t take the risk inherent in changing tools will find that others get the jump on them. With C++Builder, most developers can turn out programs at least twice as fast, and often three or four times as fast, as they could with Borland C++ or Microsoft C++. Furthermore, there will be no price to pay in terms of the size or performance of your final program. This product represents a huge change in the way we think about programming.

The fundamental idea we need to grasp is that components are now the core around which our programs are built. At one point it was procedures and functions; then it was objects, and now it is components. Undoubtedly, the future will bring yet more changes, but this is what we need to learn now.

If there is any doubt, I should perhaps add that I believe C++Builder, and its sister tool, Delphi, are the best programming tools on the market today. They aren’t just better than Borland C++, Microsoft Visual C++, or Visual Basic, they are three or four times better. Four or five years from now, I expect that tools like Borland C++ and Visual C++ will play the same role in the market that Assembler plays today. Visual Basic will probably still be widely used, but it is unlikely that it will ever catch up with the flexibility and performance of C++Builder or Delphi.

In short, I am an extremely dedicated adherent of C++Builder. I am completely serious in saying that I think this is the best way to program C++ in today’s world. Furthermore, I have made every effort to ensure that this book captures what is best in this product and shows you how it can be used to create the best, cutting-edge development tools that simultaneously impress users and solve a wide range of problems.

Summary

In this chapter you learned a few facts about BCB, and quite a bit about my approach to writing this book. There will be a few more general-interest comments in the next chapter, but most of the material in the remaining portions of this book focuses on hardcore programming issues. This chapter is the place where I set the mood and the tone for the book. The rest of the material focuses in on the core task of writing code!

Before closing, I should perhaps point out that over the last few years, I have spent many long hours using C++Builder’s sister product, Delphi. My long experience with Delphi means that I have, literally, more than three and one half years of experience using a product that is very similar to C++Builder. Just how similar the two products really are will become clear over the course of the next three chapters.

I believe that my Delphi-based experience helps me focus on what is best in C++Builder. The opinions I have about this remarkable product have not been reached hastily. They have been forged through years of experience.

I have now spent about six months with BCB, and I can say that it is a product that lives up to the Delphi name, and that surpasses that product in many ways. This is a great tool, which has really changed the way I think about writing code.

There is a lot of material to study in this book and lots of hard work ahead. But don’t be so rushed that you never take the time to see what’s exciting and artful about this product. Programming is a lot of work, but it is also a lot of fun. Right now C++Builder exists on the cusp of what’s best and most exciting in the programming world. Learn how to use the product, but also take a little time to admire the sheer elegance of its execution. I have found that it always pays off to take the time to see beneath the surface of a product, to see not only how it works, but why it works!

Web Developer’s Guide

1 YEAR UPGRADE
B U Y E R P R O T E C T I O N P L A N
Develop and Deliver Enterprise-Critical Desktop and Web
Applications with C# .NET
• Complete Case Studies with Ready-to-Run Source Code and Full Explanations
• Hundreds of Developing & Deploying, Migrating, and Debugging Sidebars,
Security Alerts, and C# .NET FAQs
• Complete Coverage of Web Services and the Integrated Development
Environment (IDE)
Adrian Turtschi
DotThatCom.com
Jason Werry
Greg Hack
Joseph Albahari
Saurabh Nandu Technical Editor
Wei Meng Lee Series Editor
C# . N ET
Web Developer’s Guide
solutions@syngress.com
With more than 1,500,000 copies of our MCSE, MCSD, CompTIA, and Cisco
study guides in print, we continue to look for ways we can better serve the
information needs of our readers. One way we do that is by listening.
Readers like yourself have been telling us they want an Internet-based service
that would extend and enhance the value of our books. Based on
reader feedback and our own strategic plan, we have created a Web site
that we hope will exceed your expectations.
Solutions@syngress.com is an interactive treasure trove of useful information
focusing on our book topics and related technologies. The site
offers the following features:
 One-year warranty against content obsolescence due to vendor
product upgrades. You can access online updates for any affected
chapters.
 “Ask the Author” customer query forms that enable you to post
questions to our authors and editors.
 Exclusive monthly mailings in which our experts provide answers to
reader queries and clear explanations of complex material.
 Regularly updated links to sites specially selected by our editors for
readers desiring additional reliable information on key topics.
Best of all, the book you’re now holding is your key to this amazing site.
Just go to http://www.syngress.com/solutions, and keep this book handy when
you register to verify your purchase.
Thank you for giving us the opportunity to serve your needs. And be sure
to let us know if there’s anything else we can do to help you get the
maximum value from your investment. We’re listening.
http://www.syngress.com/solutions

1 YEAR UPGRADE
B U Y E R P R O T E C T I O N P L A N
Adrian Turtschi
DotThatCom.com
Jason Werry
Greg Hack
Joseph Albahari
Saurabh Nandu Technical Editor
Wei Meng Lee Series Editor
C# . N ET
Web Developer’s Guide
Syngress Publishing, Inc., the author(s), and any person or firm involved in the writing, editing, or
production (collectively “Makers”) of this book (“the Work”) do not guarantee or warrant the results to be
obtained from the Work.
There is no guarantee of any kind, expressed or implied, regarding the Work or its contents.The Work is
sold AS IS and WITHOUT WARRANTY. You may have other legal rights, which vary from state to state.
In no event will Makers be liable to you for damages, including any loss of profits, lost savings, or other
incidental or consequential damages arising out from the Work or its contents. Because some states do not
allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation
may not apply to you.
You should always use reasonable care, including backup and other appropriate precautions, when working
with computers, networks, data, and files.
Syngress Media®, Syngress®,“Career Advancement Through Skill Enhancement®,” and “Ask the Author
UPDATE®,” are registered trademarks of Syngress Publishing, Inc. “Mission Critical™,”“Hack Proofing™,”
and “The Only Way to Stop a Hacker is to Think Like One™” are trademarks of Syngress Publishing, Inc.
Brands and product names mentioned in this book are trademarks or service marks of their respective
companies.
KEY SERIAL NUMBER
001 CDFE48952P
002 NHBN9436KH
003 BAEN24P7BV
004 HY9W84UJTA
005 RTW9B39RE4
006 JSE4FAHT82
007 VTS8TYCGF2
008 AUTGFLDCWR
009 833K74SLAF
010 VFR4MHY3XW
PUBLISHED BY
Syngress Publishing, Inc.
800 Hingham Street
Rockland, MA 02370
C# .NET Web Developer’s Guide
Copyright © 2002 by Syngress Publishing, Inc. All rights reserved. Printed in the United States of America.
Except as permitted under the Copyright Act of 1976, no part of this publication may be reproduced or
distributed in any form or by any means, or stored in a database or retrieval system, without the prior
written permission of the publisher, with the exception that the program listings may be entered, stored,
and executed in a computer system, but they may not be reproduced for publication.
Printed in the United States of America
1 2 3 4 5 6 7 8 9 0
ISBN: 1-928994-50-4
Technical Editor: Saurabh Nandu Freelance Editorial Manager: Maribeth Corona-Evans
Co-Publisher: Richard Kristof Cover Designer: Michael Kavish
Acquisitions Editor: Catherine B. Nolan Page Layout and Art by: Shannon Tozier
Developmental Editor: Kate Glennon Copy Editor: Darren Meiss
CD Production: Michael Donovan Indexer: Rich Carlson
Distributed by Publishers Group West in the United States and Jaguar Book Group in Canada.
v
We would like to acknowledge the following people for their kindness and support in
making this book possible:
Richard Kristof and Duncan Anderson of Global Knowledge, for their generous access
to the IT industry’s best courses, instructors, and training facilities.
Ralph Troupe, Rhonda St. John, and the team at Callisma for their invaluable insight into
the challenges of designing, deploying and supporting world-class enterprise networks.
Karen Cross, Lance Tilford,Meaghan Cunningham, Kim Wylie, Harry Kirchner, Kevin
Votel, Kent Anderson, and Frida Yara of Publishers Group West for sharing their
incredible marketing experience and expertise.
Mary Ging, Caroline Hird, Simon Beale, Caroline Wheeler,Victoria Fuller, Jonathan
Bunkell, and Klaus Beran of Harcourt International for making certain that our vision
remains worldwide in scope.
Annabel Dent of Harcourt Australia for all her help.
David Buckland,Wendi Wong, Marie Chieng, Lucy Chong, Leslie Lim,Audrey Gan, and
Joseph Chan of Transquest Publishers for the enthusiasm with which they receive our
books.
Kwon Sung June at Acorn Publishing for his support.
Ethan Atkin at Cranbury International for his help in expanding the Syngress program.
Jackie Gross, Gayle Vocey, Alexia Penny, Anik Robitaille, Craig Siddall, Darlene Morrow,
Iolanda Miller, Jane Mackay, and Marie Skelly at Jackie Gross & Associates for all their
help and enthusiasm representing our product in Canada.
Lois Fraser, Connie McMenemy, and the rest of the great folks at Jaguar Book Group for
their help with distribution of Syngress books in Canada.
Acknowledgments
Contributors
Todd Carrico (MCDBA, MCSE) is a Senior Database Engineer for
Match.com. Match.com is a singles portal for the digital age. In addition to its
primary Web site, Match.com provides back-end services to AOL, MSN, and
many other Web sites in its affiliate program.Todd specializes in design and
development of high-performance, high-availability data architectures primarily
on the Microsoft technology. His background includes designing, developing,
consulting, and project management for companies such as Fujitsu, Accenture,
International Paper, and GroceryWorks.com. In addition to his contribution to
C# .NET Web Developer’s Guide,Todd has also contributed chapters to other
books in the Syngress .NET Series including the ASP .NET Web Developer’s
Guide, and the VB .NET Developer’s Guide.Todd resides in Sachse,TX, with his
wife and two children.
Mark Tutt is a Senior Software Engineer with MICROS Systems. MICROS
provides complete information management solutions for the hospitality
industry, including software, hardware, enterprise systems integration, consulting,
and support. Mark is the principle designer of a number of software packages,
including Guest Service Solution, a customer relationship management system
for the MICROS Restaurant Enterprise Series platform. In addition to his
product development duties, Mark is a key contributor to the design and development
of system integration software and customer-specific product extensions
that allow MICROS clients to fully integrate MICROS products into
their overall technology plans. Mark currently resides in Baltimore, Maryland
with his wife Malinda and their twin sons, Fred and Jackson.
Jason Werry (MCSD) runs a consulting firm, Synergy Data Solutions, in
Australia. He currently provides strategic and technical consulting to his clients
and specializes in Windows-based enterprise systems development. Jason has an
extensive background using Microsoft technologies and is currently developing
state-of-the-art,Web-based applications on the .NET platform. His clients have
ranged from a Taiwanese multimedia company to various government departments
and local startups. A natural born programmer, Jason started coding
Z80 processors in Assembly at age 13. Since then he has used most popular
vi
vii
programming languages and presently enjoys working with SQL Server, MTS,
IIS,Visual Basic, and C#. Jason holds a bachelor’s degree in Mathematics/
Computer Science from The University of Queensland. He dedicates his
writing to his loving wife, LiHsing.
Patrick Coelho (MCP) is an Instructor at The University of Washington
Extension, North Seattle Community College, Puget Sound Center, and Seattle
Vocational Institute, where he teaches courses in Web Development (DHTML,
ASP, XML, XSLT, C#, and ASP .NET). Patrick is a Co-Founder of
DotThatCom.com, a company that provides consulting, online development
resources, and internships for students. He is currently working on a .NET
solution with contributing author David Jorgensen and nLogix. Patrick holds a
Bachelor’s of Science degree from the University of Washington, Bothell.
Patrick lives in Puyallup,WA with his wife Angela.
David Jorgensen (MCP) is an Instructor at North Seattle Community
College, University of Washington extension campus, and Puget Sound Centers.
He is also developing courses for Seattle Vocational Institute, which teaches
.NET and Web development to the underprivileged in the Seattle area. David
also provides internship opportunities through his company DotThatCom.com,
which does online sample classes and chapters of books. David holds a bachelor’s
degree in Computer Science from St. Martin’s College and resides in
Puyallup,WA with his wife Lisa and their two sons Scott and Jacob.
Greg Hack is a Senior Software Engineer with Allscripts Healthcare Solutions.
Greg has over 15 years experience developing software on platforms ranging
from the mainframe to the desktop using a wide variety of languages and technologies.
Recent work includes a Web-based application that allows patients to
view their medical records and a Pocket PC application that delivers clinical
information to physicians at the point of care.
Axel Goldbach is a Senior Consultant with modulo3 GmbH, a consulting
company based in Germany and specializing in project management consulting
throughout Europe. modulo3 is a process implementation specialist for the
major networking frameworks, including eXtreme Programming, MSF and
V Modell.Axel currently provides senior-level strategic and technical consulting
to all modulo3 clients in Germany and Central Europe. His duties include analysis
and development of multi-tiered applications in heterogeneous environments.
viii
Axel also works as a technical scout and trainer for modulo3. His training specialties
include programming languages, networking, and academic fields such as
development methodology, parser- and interpreter-technology, theory of complexity,
and provable correct software.
Joseph Albahari is a freelance consultant and developer with over 10 years
experience in designing networked systems. He has led a string of successful
projects, from custom application frameworks for start-up companies, to highperformance
OLAP and data warehousing systems for telecommunications
giants. His knowledge in object-oriented user interface design has been called
upon in the planning or production of many large and complex systems, where
well-balanced abstractions are of key importance. Joseph is also experienced in
SQL Server database administration, and has developed high-performance solutions
for clients with specialized requirements—such as a replication system
providing field level synchronization, or a high-throughput bulk-copying agent.
Joseph holds a Bachelor’s degree in computer science and physics.
Adrian Turtschi (MCSE, MCSD) is Lead Architect Solution Development
with Avanade (Germany), where he is responsible for the solution offering in
the mobile computing space. He has been working on the Microsoft .NET
platform since fall 2000, specializing in developing enterprise systems using Web
Services. He is particularly interested in using Web Services to bridge platform
and system boundaries. Prior to joining Avanade,Adrian worked for KPMG’s
Global Knowledge Exchange in Boston, where he helped design and develop
KPMG’s global knowledge management and collaboration solution, used by its
100,000 professionals world-wide.Adrian has work experience in Switzerland,
the Netherlands, and the US. He has degrees in Mathematics and Computer
Science. He currently lives in Berlin, Germany.
ix
Technical Editor and Reviewer
Saurabh Nandu is the Founder of http://www.MasterCSharp.com which concentrates
on teaching C# and .NET. He worked with HTML, JavaScript, Flash 5.0
before he started programming in Java. Saurabh has been impressed by the
power and flexibility of .NET. He is currently employed by YesSoftware Inc.
as Technical Evangelist.
Technical Editor’s Acknowledgements
I would like to thank my friend Nanu Jogi without whose direction I would
have never got into working on the .NET Platform. I would also like to thank
my family, especially my brother Pritesh, for their support.
Wei Meng Lee is Series Editor for Syngress Publishing’s .NET Developer
Series. He is currently lecturing at The Center for Computer Studies, Ngee
Ann Polytechnic, Singapore.Wei Meng is actively involved in Web development
work and conducts training for Web developers and Visual Basic programmers.
He has co-authored two books on WAP. He holds a Bachelor’s degree in
Information Systems and Computer Science from the National University of
Singapore.The first and second books of the .NET series, VB .NET Developer’s
Guide (ISBN: 1-928994-48-2), and ASP .NET Developer’s Guide (ISBN:
1-928994-51-2) are currently available from Syngress Publishing.
Series Editor
x
This CD-ROM contains the code files that are used in each chapter of this book.The
code files for each chapter are located in a chXX directory (for example, the files for
Chapter 8 are in the ch08 directory).Any further directory structure depends on the projects
that are presented within the chapter.
To work with the examples provided, you will need at least the Windows 2000 or
Windows XP Professional operating system with the latest service packs, IIS 5.x, and IE
6.0, since ASP.NET and Web Services (a part of ASP.NET) are not supported on earlier
operating systems such as Windows 9x/WindowsME/WindowsNT. Also needed is the
.NET SDK Beta2 (the latest public release available while writing this book) and the
Visual Studio.NET Beta2 IDE.
The C# .NET Web Developer’s Guide provides you with extensive examples that will
help solve the problems you might face while developing applications for the .NET
Platform rather than concentrating on the theory of C# and .NET programming.
Therefore code is the main feature of this book.
The chapters contain both code snippets and sample programs that illustrate the
principles discussed. Chapter 2 presents a series of sample programs that introduce concepts
in C# that are different from other object-oriented languages. Chapter 4 helps you
understand the basics of building Graphical User Interface (GUI)-rich Windows Forms
applications; the examples presented in this chapter are the launch pad for Windows
Forms applications used in other chapters. Similarly, code presented in Chapter 8 helps
you to interact with various databases using ADO.NET; again, this chapter acts as a foundation
for further chapters’ database coverage. Chapter 9 will acquaint you with using
.NET Class Libraries to interact with XML and its related technologies.
Chapters 5, 6, and 11 discuss technologies and Application Program Interfaces (APIs)
that help two applications to communicate and interact with each other. Chapter 5
focuses on enabling applications to communicate over the TCP and UDP protocols and
provides an overview of the techniques used to interact with Web pages programmatically.
Code examples in Chapter 6 and Chapter 11 concentrate on using Simple Object Access
Protocol (SOAP) and object serialization and deserialization.
About the CD
xi
Chapter 7 examples examine message delivery in distributed applications using
Microsoft Message Queuing (MSMQ). Chapter 10 takes a comprehensive look at
ASP.NET and helps you build various applications of increasing complexity and functionality,
starting with an XML Poll, progressing to a SQL-powered Message Board, and
ending with a Shopping Cart.
Lastly, to end on a lighter note, Chapter 12 takes you through building a Jokes Web
Service.The code in this chapter helps you build both the Jokes Web Service as well as
the Windows Forms Client for the service.
Look for this CD icon to obtain files used
in the book demonstrations.
xii
From the Series Editor
For many years, C and C++ programmers have been searching for alternative programming
languages that offer the same kind of flexibility and power of C and C++,
but without the complexities and steep learning curve required for mastery of the
language.What many programmers desired was a language that would allow applications
to be built rapidly, but at the same time giving them the ability to code at low
level.The search has finally ended with Microsoft’s new language—C#, a member of
the .NET Framework.
C# is the revolutionary new language from Microsoft, designed solely to run on
the .NET framework. Drawing experiences from C, C++, and Visual Basic, C# was
designed to be a simple and modern object oriented programming language.
But why learn C#? With the integration of C# and the Visual Studio.NET
(known as Visual C#), developing Windows and Web applications has been radically
simplified.With full access to the .NET Class Libraries, C# includes built-in support
for developing robust Web services and ASP.NET applications. (It was reportedly said
that Visual Studio.NET was built entirely using C# and that most of the examples in
MSDN were coded in C#.That in and of itself is a very good reason to learn C#!)
Besides this, C# enhances the productivity of programmers by eliminating common
errors often associated with C and C++.
While many of the earlier C# books have primarily focused on the language
syntax, The C# .NET Web Developer’s Guide illustrates the uses of C# for Web developers
looking to harness the new functionality and ease of this powerful programming
language.The best way to learn a new language is by trying out the examples
while you are reading this book.Within many chapters, you will find numerous code
examples used in various practical situations; this hands-on, code-intensive approach
allows you to have a deeper understanding of issues involved in C# Web development,
and at the same time allows you to cut and paste portions of applicable code
into your current projects, thereby shortening development time.
We are constantly working hard to produce the best technical books needed by
professional programmers like you. I sincerely hope you will enjoy reading this book
as much as the authors did writing it!
Wei Meng Lee, Series Editor
Syngress .NET Developer Series
Seldom in the history of computer software has any technology received such a positive
response from developers and the industry, even while the technology is still in
its nascent beta stage.The .NET Beta2 SDK from Microsoft has already been downloaded
by millions of developers all over the world.There have been dozens of published
books,Web sites and newsgroups devoted to the .NET platform, its related
technologies and languages.
Microsoft has invested billions of dollars and years of research in the creation of
.NET. .NET is a comprehensive strategy ,consisting of operating systems, database
servers, application servers, and the .NET Runtime, as well as managed languages
that operate over the .NET platform.
Many people see the .NET platform as the practical implementation of the previously
formulated Windows DNA. Others see it as a response to developer woes
from working with previous technologies and languages. However, the common
opinion simply offers that .NET is a significant improvement over previous Microsoft
technologies.The .NET platform has been built from the ground up with numerous
goals in mind, including security, scalability, reliability, flexibility, and interoperability—
these goals have all been dealt with from the start to help to make the .NET
platform enterprise ready and developer-friendly.
The .NET platform displays a significant shift in Microsoft’s thinking.While
building the .NET platform, Microsoft has shown strong support for open standards
like XML, SOAP, and UDDI, rather than building its own proprietary standards and
technologies. Even the core part of the .NET platform—the Common Language
Infrastructure (CLI)—and the C# specifications have been placed before ECMA for
standardization.
C# is defined as a simple, modern, object-oriented, and type-safe programming
language derived from C and C++. Developed by Anders Hejlsberg of Microsoft
especially for the .NET platform, C# derives its features from a number of languages
xxi
Foreword
xxii Preface
like C, C++, and Java. Specifically written to offer the simplicity of Visual Basic and
power of C++ as an object-oriented language, C# makes it easier for developers to
create, debug, and deploy enterprise applications. It has also been predicted that C#
will become the favored language for developing applications on the .NET platform.
Visual Studio.NET, the next version of Visual Studio IDE, is also a key component
of the .NET strategy.The Visual Studio.NET IDE has also been given a facelift
and packed with a wide variety of new functionalities. A bitmap editor, debugger,
Web Forms designer,Windows Forms designer,Web Services designer, XML editor,
HTML editor,Web browser, Server Resources Explorer, and multi-language support
have all been packed into one single IDE.
The focus of The C#.NET Web Developer’s Guide is not on teaching you the core
C# language, but rather providing you with code examples that will help you
leverage the functionalities of the .NET Framework Class Libraries.The .NET
Framework collection of base classes cover many of the multiple APIs. Although
impossible for one book to cover all the features, in this book we have covered the
key concepts, libraries, and APIs of the .NET Framework that we feel will help you
easily create new applications using C#.
You have a whole host of features to learn and master, so why wait? Let’s get
started!!
—Saurabh Nandu,Technical Editor
Founder, http://www.MasterCSharp.com
http://www.syngress.com
xiii
Contents
Foreword xxi
Chapter 1 Introducing the Microsoft .NET Platform 1
Introduction 2
Introducing the.NET Platform 2
Microsoft .NET and Windows DNA 3
Microsoft .NET Architecture Hierarchy 4
Features of the .NET Platform 5
Multilanguage Development 5
Platform and Processor Independence 7
Automatic Memory Management 7
Versioning Support 8
Support for Open Standards 9
Easy Deployment 9
Distributed Architecture 10
Interoperability with Unmanaged Code 11
Security 12
Performance and Scalability 14
Components of the .NET Architecture 14
.NET Runtime 14
Managed/Unmanaged Code 14
Intermediate Language 15
Common Type System 15
.NET Base Class Library (BCL) 15
Assemblies 16
Metadata 16
Assemblies and Modules 17
Assembly Cache 18
Reflection 19
Just In Time Compilation 19
Garbage Collection 20
Exploring the Code Cycle 21
The Pursuit of Standardization 24
Summary/Solutions Fast Track/Frequently Asked Questions 26
Chapter 2 Introducing C# Programming 33
Introduction 34
Getting Started 35
Creating Your First C# Program 37
Compiling and Executing 38
Defining a Class 40
Declaring the MainMethod 43
Organizing Libraries with Namespaces 43
xiv Contents
Using the using Keyword 44
Adding Comments 45
Introducing Data Types 47
Value Types 47
Primitive Data Types 47
Reference Types 48
Explaining Control Structures 49
Using the ifStatement 49
Using the if-elseStatement 50
Using the switch caseStatement 50
Using the forStatement 51
Using the whileStatement 52
Using the do whileStatement 52
Using the breakStatement 52
Using the continueStatement 53
Using the returnStatement 54
Using the gotoStatement 55
Understanding Properties and Indexers 56
Using Properties 56
Get Accessor 59
Set Accessor 59
Accessing Lists with Indexers 60
Using Delegates and Events 69
Delegates 70
Single Cast 74
Multicast 75
Events 79
Using Exception Handling 85
Using the tryBlock 89
Using the catchBlock 89
Using the finallyBlock 89
Using the throwStatement 89
Understanding Inheritance 90
Summary/Solutions Fast Track/Frequently Asked Questions 104
Chapter 3 Visual Studio.NET IDE 109
Introduction 110
Introducing Visual Studio.NET 110
Components of VS.NET 112
Design Window 112
Code Window 113
Server Explorer 114
Toolbox 116
Docking Windows 117
Properties Explorer 117
Solution Explorer 118
Object Browser 119
Dynamic Help 120
Task List Explorer 121
Contents xv
Features of VS.NET 122
IntelliSense 122
XML Editor 124
Documentation Generation (XML Embedded Commenting) 127
Adding XML Document Comments to C# Pages 127
Customizing the IDE 129
Creating a Project 130
Projects 130
Creating a Project 130
Add Reference 131
Build the Project 131
Debugging a Project 132
Summary/Solutions Fast Track/Frequently Asked Questions 133
Chapter 4 Windows Forms 137
Introduction 138
Introducing Windows Forms 138
Writing a Simple Windows Forms Application 141
Adding Controls 142
Adding an Event Handler 145
Adding Controls at Runtime 147
Attaching an Event Handler at Runtime 152
Writing a Simple Text Editor 154
Starting the Project 154
Creating a Menu 155
Adding a New Form 157
Creating a Multiple Document Interface 159
Creating a Dialog Form 160
Using Form Inheritance 162
Adding a TabControl 164
Anchoring Controls 166
Changing the Startup Form 167
Connecting the Dialog 167
Using the ListView and TreeView Controls 170
Building an ImageList 170
Adding a ListView 172
Using the Details View 173
Attaching a Context Menu 174
Adding a TreeView 175
Adding a Splitter 177
Implementing Drag and Drop 178
Creating Controls 181
Creating a User Control 181
Adding a Property 182
Adding Functionality 182
Writing a Custom Control 183
Testing the Control 187
Enhancing the Control 189
Subclassing Controls 191
xvi Contents
Custom Controls in Internet Explorer 193
Setting Up IIS 193
Creating a Virtual Directory 193
Writing a Test Page 194
Summary/Solutions Fast Track/Frequently Asked Questions 196
Chapter 5 Network Programming:
Using TCP and UDP Protocols 203
Introduction 204
Introducing Networking and Sockets 204
Introduction to TCP 206
Introduction to UDP 208
Introduction to Ports 211
System.NetNamespace 212
System.Net.SocketsNamespace 213
Example TCP Command Transmission and Processing 214
General Usage of Needed .NET Classes 216
The Server 217
The Client 220
Compiling and Running the Example 226
Example UDP Command Transmission and Processing 227
General Usage of Needed .NET Classes 228
The Server 229
The Client 231
Compiling and Running the Example 234
Creating a News Ticker Using UDP Multicasting 235
General Usage of Needed .NET Classes 236
The Server 240
The Client 243
Compiling and Running the Example 250
Creating a UDP Client Server Chat Application 250
The TCPServerSessionClass 253
The TCPServerClass 256
The Chat Protocol 260
The ChatServerClass 260
The ChatClientClass 265
Compiling and Running the Example 268
Creating a TCP P2P File Sharing Application 269
The Remote File Stream Protocol 271
The RemoteFileStreamServerClass 272
The RemoteFileStreamProxyClass 276
The FileSharingPeerClass 279
Compiling and Running the Example 283
Access to Web Resources 283
General Usage of Needed .NET Classes 284
A Web Access Client 285
Compiling and Running the Example 289
Request Method 290
Redirection 290
Contents xvii
Authentication 291
Cookies 291
Summary/Solutions Fast Track/Frequently Asked Questions 292
Chapter 6 Remoting 299
Introduction 300
Introducing Remoting 301
Remoting Architecture 302
Creating a Simple Remoting Client Server 303
Creating the Remote Server Object 303
Creating the Hosting Application 305
Creating the Client Application 306
Understanding the Remoting Code 308
Improving the Sample Application 310
Adding Event Logging and Error Handling 310
Using the soapsuds Tool 312
Using Configuration Files 313
Updating Configuration Files Using the .NET
Framework Configuration Tool 318
Changing the Hosting Application to a Service 319
Using the TCP Channel with the Binary Formatter 321
Summary of the Improved Sample Application 321
Creating an Intranet Application 321
Object Lifetime and Leasing 321
Creating the CountServer Project 322
Creating the CountHost Project 325
Creating the CountClient Project 326
Understanding the Leasing and Sponsorship Code 329
Client Activated Objects 331
Sending and Receiving Objects by Value 332
Sending and Receiving Objects by Reference 333
Creating Service-Based Applications 334
Building a Versioned Remoting Application 334
Creating the VersionHost Project 336
Creating the VersionClient Project 337
Testing Side-By-Side Execution of Remote Objects 339
Summary/Solution Fast Track/Frequently Asked Questions 340
Chapter 7 Message Queuing Using MSMQ 345
Introduction 346
Introducing MSMQ 346
MSMQ Architecture 348
Installing MSMQ 349
Using Visual Studio to Manage Queues 349
Creating a Simple Application 349
Understanding the Messaging Code 353
Sending Messages 353
Message Formats 355
Sending and Receiving Messages with Complex Objects 356
Storing Files within Messages 360
xviii Contents
Setting Queue Options 364
Creating a Complex Application 365
Creating the MSMQGraphics Drawing Library 366
Creating the DrawingSender Project 369
Creating the DrawingReceiver Project 373
Creating an Asynchronous Application 376
Using Public Queues While Disconnected from the Network 378
Summary/Solutions Fast Track/Frequently Asked Questions 379
Chapter 8 ADO.NET 383
Introduction 384
Introducing ADO.NET 384
ADO.NET Architecture 386
Understanding the ConnectionObject 388
Building the Connection String 389
Understanding the CommandObject 391
Understanding DataReaders 396
Understanding DataSets and DataAdapters 396
DataTable 398
DataColumn 398
DataRow 402
Differences between DataReader Model and DataSet Model 405
Understanding the DataViewObject 406
Working with System.Data.OleDb 408
Using DataReaders 408
Using DataSets 414
Working with SQL.NET 418
Using Stored Procedures 419
Working with Odbc.NET 422
Using DSN Connection 423
Summary/Solutions Fast Track/Frequently Asked Questions 426
Chapter 9 Working with XML 431
Introduction 432
Introduction to XML 432
Explaining XML DOM 434
Explaining XPath 435
Explaining XSL 436
Explaining XML Schemas 437
XML Classes in the .NET Framework 437
Working with XML DOM 439
Creating an Empty XML DOM Document 442
Adding an Element to the XML Document 443
Updating an Element in the XML Document 446
Deleting an Element in the XML Document 450
Loading and Saving the XML Document 451
Working with XML and Relational Data 452
XML and the DataSetClass 456
XML Schemas and the DataSetClass 461
Traversing Relations in the DataSetClass 464
Contents xix
Working with XPath and XSL Transformations 469
Working with XPath 469
Working with XSL 479
Summary/Solutions Fast Track/Frequently Asked Questions 490
Chapter 10 ASP.NET 495
Introduction 496
Introducing the ASP.NET Architecture 496
ASP.NET Server Controls 497
Working with User Controls 501
Custom Controls 510
Understanding the Web.config File 511
Using the Global.asax Page 513
Working with Web Forms 513
Creating a Simple Web Form 513
Building an XML Poll 517
Creating the updateXPoll Method 523
Creating the updateFile Method 525
Displaying the Current Poll Statistics 526
Working with ADO.NET 529
Building a Message Board with SQL 529
Using VS.NET to Validate Form Input with a Regular Expression 538
XML and XSLT 543
Using the String Builder Class 547
Building a Shopping Cart with SQL 549
Creating dataaccess.cs 551
Creating XmlShoppingCart.cs 553
Creating catalog.cs 556
Summary/Solutions Fast Track/Frequently Asked Questions 571
Chapter 11 Web Services 575
Introduction 576
The Case for Web Services 576
The Role of SOAP 577
Why SOAP? 578
Why Web Services? 579
The World of Web Services 579
Web Service Standards 581
Wiring Up Distributed Objects—The SOAP Protocol 581
Creating Your Very First Web Service 581
Running Your Very First Web Service 589
Describing Web Services—WSDL 602
Discovering Web Services—DISCO 608
Publishing Web Services—UDDI 610
Working with Web Services 611
Passing Complex Data Types 611
Error Handling 614
Malformed SOAP Request 614
Wrong Argument Types 617
Exceptions in Server Code 617
xx Contents
Writing a SOAP Client Application 619
Passing Objects 626
Passing Relational Data 631
Passing XML Documents 635
Working with UDDI 639
SOAP Headers 646
Advanced Web Services 646
Maintaining State 647
State Information in the URL (URL Mangling) 648
State Information in the Http Header (Cookies) 651
State Information in the Http Body (SOAP Header) 653
Security 662
Summary/Solutions Fast Track/Frequently Asked Questions 664
Chapter 12 Building a Jokes Web Service 669
Introduction 670
Motivation and Requirements for the Jokes Web Service 670
Functional Application Design 672
Defining Public Methods 672
Defining the Database Schema 673
Defining the Web Service Architecture 674
Security Considerations 676
State Management 677
Error Handling 677
Implementing the Jokes Data Repository 677
Installing the Database 678
Creating the Stored Procedures 680
Implementing the Jokes Middle Tier 694
Setting Up the Visual Studio Project 694
Developing the Error Handler 698
Developing the Database Access Component 702
Developing the User Administration Service 704
Adding New Users 704
Checking Existing User Information 709
Adding Moderators 713
Creating the Public Web Methods—Users 715
Error Handling for the Public Web Methods 718
Creating the Public Web Methods—Administrators 720
Testing the Public Web Methods 722
Developing the Jokes Service 724
Best Practices for Returning Highly Structured Data 724
Setting Up Internal Methods to Wrap the Stored Procedure Calls 727
Setting Up Internal Methods to Manage Jokes and Ratings 734
Setting Up Internal Methods to Return Jokes 742
Creating the Public Web Methods 748
Creating a Client Application 758
Some Ideas to Improve the Jokes Web Service 775
Summary/Solutions Fast Track/Frequently Asked Questions 776
Index 781
Introducing the
Microsoft .NET
Platform
Solutions in this chapter:
 Introducing the .NET Platform
 Features of the .NET Platform
 Components of the .NET Architecture
 Exploring the Code Cycle
 The Pursuit of Standardization
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 1
1
2 Chapter 1 • Introducing the Microsoft .NET Platform
Introduction
The .NET platform is the foundation upon which the next generation of software
will be built. Microsoft has invested a lot of capital in its development, and
is putting its considerable weight behind its adoption as a new standard. A long
list of Microsoft partners have also announced support for .NET tools and components—
you can check http://msdn.microsoft.com/vstudio/partners for a current
list of vendors who have .NET offerings.
The .NET platform is much more than a new language, software development
kit (SDK), or even an operating system. It offers powerful new services, a
new processor-independent binary format, new managed languages, managed language
extensions to existing languages, and the list goes on. Effectively using
these new tools is not possible without a firm background of the platform that
will empower your applications.
In this chapter, we take a look at the various components of the .NET platform.
We introduce not only the concepts and their technology, but explain the
terminology used to describe them.This will enable you to have a strong understanding
of the internal workings of the .NET platform, and get the full benefit
of the information in the following chapters.
Introducing the .NET Platform
The precept behind the .NET platform is that the world of computing is
changing from one of PCs connected to servers through networks such as the
Internet, to one where all manner of smart devices, computers, and services work
together to provide a richer user experience.The .NET platform is Microsoft’s
answer to the challenges this change will provide for software developers.
The .NET platform has several components—however, who you ask will
probably affect the answer you receive. Servers such as BizTalk and SQL Server,
as well as services such as .NET My Services and its first visible component,
.NET Passport, are being described by some as integral parts of the .NET platform.
However, for many of us, the .NET Framework is what we think of when
.NET is mentioned. It includes Visual Studio.NET (VS.NET), the .NET
Common Language Runtime (CLR), and the .NET Base Class Libraries (BCL).
The other components may be required by specific applications, but they are not
a necessary part of all .NET applications.
Looking at the overall architecture, .NET consists of three primary
components:
http://www.syngress.com
http://www.syngress.com
 The .NET Framework A completely new application development
platform.
 Several .NET products Various applications from Microsoft based on
the .NET Framework, including new versions of Exchange and SQL
Server, which are Extensible Markup Language (XML)–enabled and
integrated into the .NET platform.
 Several .NET services Provided by Microsoft for use in developing
applications running under the .NET Framework. Microsoft’s Hailstorm
project is actually an attempt to package some of the most crucial Web
Services under the Microsoft brand name.
The .NET Framework itself can be divided into three parts:
 The CLR A managed execution environment that handles memory
allocation, error trapping, and interacting with the operating-system
services.
 The Base Class Library An extensive collection of programming
components and application program interfaces (APIs).
 Two top-level development targets One for Web applications
(ASP.NET) and another for regular Windows applications (Windows
Forms).
The advantages offered by the .NET Framework include shorter development
cycles (code reuse, fewer programming surprises, support for multiple programming
languages), easier deployment, fewer data type–related bugs due to
integral type safety, reduced memory leaks thanks to the garbage collector, and, in
general more scalable, reliable applications.
Microsoft .NET and Windows DNA
If some of the marketing speak surrounding .NET sounds familiar, there’s a good
reason:The .NET platform is the next generation of what was called Windows
DNA. However, although Windows DNA did offer some of the building blocks
for creating robust, scalable, distributed systems, it generally had little substance in
and of itself.
Windows DNA was a technical specification that focused on building software
based on Microsoft server products, utilizing numerous technologies and
languages (ASP, HTML, JavaScript, MTS, COM, and so on), many of which are
quite unrelated from a developer’s point of view.The servers and languages
Introducing the Microsoft .NET Platform • Chapter 1 3
4 Chapter 1 • Introducing the Microsoft .NET Platform
involved all have varying APIs and type systems, making interoperability a challenge
at best. Herein lies the big difference: .NET is much more than a specification.
A product in its own right, it includes the tools and languages required to
make developing these types of n-tiered applications easier, neatly packaged as a
single coherent and comprehensive API.
Microsoft .NET Architecture Hierarchy
The diagram in Figure 1.1 shows the .NET platform architecture. Essentially, the
.NET families of languages are each compiled into Microsoft Intermediate
Language (MSIL, or just IL) output according to the Common Language
Specification.The primary types of application development are Web Forms,Web
Services, and Windows Forms applications.These applications communicate using
XML and Simple Object Access Protocol (SOAP), getting their functionality
from the Base Class Library and run within the Common Language Runtime
environment.Visual Studio.NET is not required in order to develop .NET
Framework applications, however it does offer an extensible architecture that
makes it an ideal choice for developing .NET software.
http://www.syngress.com
Figure 1.1 The .NET Platform Architecture
VB.NET Managed C#
C++
Perl Other .NET
Languages
Common Language Specification (CLS)
Web Services
Web Forms
Windows Forms
Data and XML
Base Class Library
Common Language Runtime (CLR)
Visual Studio .NET
Introducing the Microsoft .NET Platform • Chapter 1 5
Features of the .NET Platform
The core of the .NET platform is found in the Common Language Runtime,
Base Class Library, and the Common Language Specification.The .NET Base
Class Library exposes the features of the Common Language Runtime in much
the same way that the Windows API allows you to utilize the features of the
Windows operating system; however, it also provides many higher-level features
that facilitate code reuse.
This architecture gives a great number of benefits, not the least of which is a
consistent API. By writing to the Common Language Runtime and using the
.NET Base Class library, all application services are available via a common
object-oriented programming model.Today some OS functions are accessed via
DLL calls using the C-based API and other facilities are accessed via COM
objects, making the developer do the necessary legwork to make everything work
together smoothly. Some features are available only to developers working in
low-level languages, forcing design decisions.
This new programming model greatly simplifies the efforts that were required
when writing Windows DNA applications, or for that matter, almost any Win32
and COM project. Developers no longer need to be a Windows or COM architecture
guru with an in-depth understanding of GUIDs, IUnknown,AddRef,
Release, HRESULTS, and so on. .NET doesn’t just hide these from the developer;
in the new .NET platform, these concepts simply do not exist at all.
Another great benefit for .NET developers is its model for error handling via
exceptions. Developing software for the Windows platform has always meant you
were pulled into its own inconsistencies; particularly in the ways errors were
returned. Some functions would return Win32 error codes, some return HRESULTS,
and some raise exceptions, all requiring the programmer to write different
types of error-handling code. In .NET, all errors are reported via
exceptions, which greatly simplifies writing, reading, and maintaining code.
Thanks to the Common Language Specification and Common Type System,
.NET exceptions work across module and language boundaries as well.
Multilanguage Development
Because many languages target the .NET Common Language Runtime, it is now
much easier to implement portions of your application using the language that’s
best suited for it. Older methods of allowing different programming languages to
interoperate, such as COM or CORBA did so through the use of an Interface
Definition Language (IDL).The .NET platform allows languages to be integrated
http://www.syngress.com
6 Chapter 1 • Introducing the Microsoft .NET Platform
with one another through the use of the MSIL. Although it contains instructions
that appear similar to assembly code, such as pushing and popping values and
moving variables in and out of registers, it also contains instructions for managing
objects and invoking their methods, manipulating arrays, and raising and catching
exceptions.
The Microsoft Common Language Specification describes what other development
tool authors must do in order for their compilers to output IL code that
will allow them to integrate well with other .NET languages. Microsoft currently
provides several compilers that produce IL code targeting the .NET Common
Language Runtime: C++ with managed extensions, C#, Jscript, and Visual Basic.
In addition, several companies other than Microsoft are producing compilers for
languages that also target the .NET Common Language Runtime. Currently
support for COBOL, Eiffle, Fortran, Perl, Python, Scheme, and many more have
been announced by various vendors. For a current list check http://msdn
.microsoft.com/vstudio/partners/language/default.asp.
Why should you care about the details of IL? Because this is how .NET
manages many of its cross-language features. No Interface Definition Language is
required to enable cross-language functionality because IL metadata handles the
entire translation overhead. For instance, with an exception object defined by IL,
the same object can be caught regardless of the .NET language used.Your component
written in C# can raise an exception that can be caught by the Fortran
application using it. No more worries about different calling conventions or data
types, just seamless interoperability.
Cross-language inheritance is another feature made possible by the use of IL.
You can now create new classes based on components written in other languages,
without needing the source code to the base component. For example, you can
create a class in C++ that derives from a class implemented in Visual Basic. .NET
can enable this because it defines and provides a type system common to all
.NET languages.
One of the great challenges of developing applications under the Windows
DNA specification was in debugging applications developed in a variety of languages.
Thanks to the unified development environment of Visual Studio.NET
and the use of IL as the output of all .NET languages, cross-language debugging
is possible without resorting to assembly language.The .NET Common
Language Runtime fully supports debugging applications that cross language
boundaries.The runtime also provides built-in stack-walking facilities, making it
much easier to locate bugs and errors.
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 7
Platform and Processor Independence
The intermediate language is CPU-independent, and it’s much higher level than
most machine languages. Once written and built, a managed .NET application
can execute on any platform that supports the .NET Common Language
Runtime. Because the .NET Common Type System defines the size of the base
data types that are available to .NET applications, and applications run within the
Common Language Runtime environment, the application developer is insulated
from the specifics of any hardware or operating system that supports the .NET
platform.
Although at the time of this writing .NET applications run only on Windows
platforms, on June 27th, 2001 Microsoft announced that it had reached an agreement
with Corel to develop a shared-source implementation of a C# compiler
and the .NET Framework infrastructure components for the FreeBSD version of
Unix.This is currently expected to be available in a beta version sometime in the
first half of 2002.
A few weeks later, on July 10, 2001 Microsoft gave the go-ahead to an opensource
version of .NET being planned by Ximian, the developer the popular
GNOME user interface for Linux.You can find the project, called Mono, at
http://www.go-mono.net.The group is developing a C# language compiler, along with
the .NET Common Language Runtime.Work has also begun on the Base Class
Library.The release of the first usable Project Mono code is planned for the end
of 2001.
Automatic Memory Management
The mere mention of a memory leak problem brings forth images of endless
hours of debugging for developers who’ve come from a development environment
that did not offer automatic memory management. Even for those fortunate
enough to work with this in some form have likely spent some time trying
to hunt down obscure bugs caused by tricky code that circumvented the resource
management methodology.
Developers coming from Visual Basic or COM backgrounds are familiar with
the reference counting technique.This technique recovers the memory used by
an object when no other object has a reference to it, essentially when it’s no
longer needed. Although this sounds perfect in theory, in practice it has a few
problems. One of the most common is a circular reference problem where one
object contains a reference to another object which itself contains a reference
back to the first object.When the memory manager looks for objects that are not
http://www.syngress.com
8 Chapter 1 • Introducing the Microsoft .NET Platform
in use, these objects will always have a reference count greater than zero, so unless
they are implicitly deconstructed, their memory may never be recovered.
For a C or C++ programmer—accustomed to ensuring that objects are
properly destroyed, essentially managing memory on their own—this sounds perfectly
normal, and a good reason for not trusting anyone else to take care of
managing resources. However, in the .NET environment,Microsoft is striving to
make developing software easier. Later in this chapter, we cover a how .NET
garbage collection works, and the improvements that have been made over strict
reference counting or manual memory management approaches.
Versioning Support
Anyone who doesn’t understand the phrase “DLL Hell” hasn’t been developing
(or at least supporting) software for Windows very long. For the uninitiated,
you’ll find yourself in DLL Hell someday when a customer installs a software
package that uses one of the same DLLs as your application. However, your application
used version 1.0 of this DLL, and the new software replaces it with version
1.1.We developers all always make sure everything is 100% backwards-compatible,
right? The new DLL makes your application exhibit some strange problem
or perhaps just stop working altogether. After a lot of investigation, you figure out
what the offending DLL is and have the customer replace the new one with the
version that works with your software. Now their new software doesn’t work…
welcome to DLL Hell. Many developers resort to simply installing every DLL
their application requires in the application directory so that it will be found first
when the application loads the libraries.This defeats the purpose of shared
libraries, but it is one way around the problem.
COM was going to change this; one of its primary tenants was that you never
changed a methods interface you simply add new methods. Unfortunately, software
developers are frequently perfectionists, and leaving a “broken” function alone just
chafes some people. Problem is, changing a components interface once it’s in use
can have adverse affects on the client software that expected the old behavior.
Because COM objects are loaded using information in the Registry, simply placing
the DLL or control in the application directory doesn’t work for this problem.
The .NET architecture now separates application components so that an
application always loads the components with which it was built and tested. If the
application runs after installation, the application should always run.This is done
with assemblies, which are .NET-packaged components. Although current DLLs
and COM objects do contain version information, the OS does not use this
information for any real purpose.Assemblies contain version information that the
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 9
.NET Common Language Runtime uses to ensure that an application will load
the components it was built with.We cover more of the specifics of how assemblies
and versioning works later in the chapter.
Support for Open Standards
In today’s world, not every device you may want to work with is going to be
running a Microsoft OS or using an Intel CPU. Realizing this, the architects of
.NET are relying on XML and its most visible descendant, SOAP, an emerging
standard for sending messages across the Internet that activates programs or applications
regardless of their underlying infrastructure. SOAP will provide the means
for disparate systems to exchange information easily, but even more, SOAP allows
you to invoke methods on remote systems and return the results. Because SOAP
is a simple text-based protocol similar to HTTP, it can easily pass through firewalls,
unlike DCOM or CORBA objects.
Other standards employed by the .NET platform include Universal
Description, Discovery, and Integration (UDDI), a directory of companies and
their XML interfaces and the Web Services Description Language (WSDL),
which describes what a piece of application code can do. By basing much of
.NET on open standards and by submitting the proposed draft standards for C#
and the .NET Common Language Infrastructure to ECMA, an international
standards organization, Microsoft hopes to see its version of the future of software
adopted beyond its own domain.
Easy Deployment
Today, developing installations for Windows-based applications can be incredibly
difficult, to the point that most companies use third party tools for developing
their installation programs, and even then it’s not pleasant.There are usually a
large number of files to be installed in several directories, various Registry settings,
installation of required COM components, and shortcuts that need to be
created, and so on. Completely uninstalling an application is nearly impossible,
most leave bits and pieces of themselves around even if they provide an uninstall
feature.With the release of Windows 2000, Microsoft introduced a new installation
engine that helps with some of these issues, but it is still possible that the
author of a Microsoft Installer Package may fail to do everything correctly. Even
with those third party tools specifically designed to make developing installation
programs easier, it is still frequently a monumental task to correctly install a
retrievial application.
http://www.syngress.com
10 Chapter 1 • Introducing the Microsoft .NET Platform
The .NET design team must have felt the same way about this problem,
because .NET plans to do away with these issues for good. .NET components
are not referenced in the Registry, thanks to the use of metadata and reflection,
components are self describing. In fact, installing many .NET applications will
require no more than copying their files to a directory, and uninstalling an application
will be as easy as deleting those files.
Distributed Architecture
Today’s distributed applications are much different than those we will see in the
future. Microsoft certainly believes this; they say they are betting the company on
the concept of distributed Web services.
http://www.syngress.com
Using the Visual Studio.NET Setup Tools
Realizing that deploying applications and authoring installation packages
is frequently a monumental task, the Visual Studio.NET team integrated
a number of setup tools into the Visual Studio.NET environment.
After you have completed your Visual Studio.NET project development,
start a new project from the File menu. Choose Setup and
Deployment Projects from the selection list. You’ll see a number of
setup project options listed:
 Cab Project
 Deploy Wizard
 Merge Module Project
 Setup Project
 Setup Wizard
 Web Setup Project
Using the wizards, you can select the Visual Studio project you want
to use and have a setup or deployment project created automatically. If
the defaults are not sufficient for your needs, you can use the new setup
project as a basis for creating your custom setup or deployment.
Developing & Deploying…
Introducing the Microsoft .NET Platform • Chapter 1 11
For example, today when a user is interacting with a portal site, it appears to
them that they are working with one remote server. Most of us know that is normally
not the case, at least for a site of any significant size.There are various
servers and applications behind the scenes are accessing information on several
remote sites, combining it with information from their user database and merging
it all into an integrated product that is delivered to the user via their browser.
As useful as these types of applications are, they are all very complex to
develop and maintain. Each provider of information has developed different
interfaces to access data and processes on their servers.This redundant development
is grossly inefficient and for the most part fairly boring, so there has been a
great deal of activity around three standards to streamline the process: XML,
SOAP, and UDDI.As we discussed earlier, these are used in .NET and also in
competing, less well known initiatives from IBM and Sun.
Interoperability with Unmanaged Code
As you can probably guess, unmanaged code is code that isn’t managed by the
.NET Common Language Runtime. However, this code is still run by the CLR,
it just doesn’t get the advantages that it offers, such as the Common Type System
and Automatic Memory Management.You will probably end up using unmanaged
code in a couple of different situations:
 Calling DLL functions There is a lot of functionality locked inside
DLLs today. Not every company is going to rush to deliver a .NET
component version of their products, so if you need to interface with
them, you’ll be calling unmanaged code.
 Using COM components This is likely to be for pretty much the
same reasons you might be required to call DLL functions.
 Calling .NET services from COM components Although this
sounds a little odd, it is possible.A COM client can be made to call a
.NET component as though it was a COM server.
Here’s a little more information on the COM interoperability issue.Microsoft
didn’t want to force companies to abandon their existing COM components;
especially because many of Microsoft’s own products are COM-based today.
COM components interoperate with the .NET runtime through an interop layer
that handles all the work required when translating messages that pass back and
forth between the managed runtime and the COM components operating as
unmanaged code.
http://www.syngress.com
12 Chapter 1 • Introducing the Microsoft .NET Platform
On the other side of the coin, companies with a vested interest in COM
technology might want to use a few bits and pieces from the .NET platform,
sticking a toe in before taking the plunge. COM clients can easily interface with
.NET components through the COM interop layer.
Security
Distributed component-based applications require security, and thus far Microsoft
hasn’t had a lot of positive feedback about its products’ security features.
Fortunately, the .NET designers decided to take a new approach, different than
traditional OS security, which provides isolation and access control based on user
accounts, and also unlike the model used by Java, where code that is not trusted is
run in a “sandbox,” with no access to critical resources.The .NET Framework
provides a fine-grained control of application security.
Security for .NET applications starts as soon as a class is loaded by the CLR.
Before the class loader instantiates a class, security information—such as accessibility
rules and self-consistency requirements—are checked. Calls to class methods
are checked for type safety. If you’ve ever heard of a security vulnerability caused
by a “buffer overrun,” you can understand why this is important.With verified
code, a method that is declared as taking a 4-byte integer parameter will reject an
attempt to call it with an 8-byte integer parameter.Verification also prevents
applications from executing code at a random location in memory, a common
tactic in buffer overflow exploits.
Additionally, as code requests access to certain resources, the class credentials are
verified. .NET security crosses process boundaries and even machine boundaries to
prevent access to sensitive data or resources in a distributed application environment.
The following are some of the basic elements of the .NET security system:
 Evidence-based security is a new concept introduced by the
.NET Framework. An assembly contains several important pieces of
information that can be used to decide what level of access to grant the
component. Some of the information used includes what site the component
was downloaded from, what zone that site was in, (Internet,
intranet, local machine, and so on) and the strong name of the assembly.
The strong name refers to an encrypted identifier that uniquely defines
the assembly and ensures that it has not been tampered with.
 The .NET Common Language Runtime further provides security
using a Policy-Driven Trust Model Using Code Evidence.
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 13
It sounds worse than it really is. Essentially this is a system of security
policies that can be set by an administrator to allow certain levels of
access based on the component’s assembly information.The policies are
set at three levels: the enterprise, the individual machine, and the user.
 Calling .NET Framework methods from the Base Class Library
get the benefits of built in security. That is, the developer doesn’t
have to make explicit security calls to access system resources. However,
if your components expose interfaces to protected resources, you will be
expected to take the appropriate security measures.
 Role-based security plays a part in the .NET security scheme.
Many applications need to restrict access to certain functions or
resources based on the user, and .NET introduces the concepts of identities
and principals to incorporate these functions.
 Authentication and authorization functions are accessed
through a single API. It can easily be extended to incorporate application-
specific logic as required. Authentication methods include basic
operating system user identification, basic HTTP,ASP.NET forms,
Digest and Kerberos, as well as the new .NET service,Microsoft .NET
Passport.
 Isolated storage is a special area on disk assigned to a specific
assembly by the security system. No access to other files or data is
allowed, and each assembly using isolated storage is separated from each
other. Isolated storage can be used for a saving a components state, or
saving settings, and can be used by components that do not have access
to read and write files on the system.
 A robust set of cryptographic functions that support encryption,
digital signatures, hashing, and random-number generation
are included in the .NET Framework. These are implemented
using well-known algorithms, such as RSA, DSA, Rijndael/AES,Triple
DES, DES, and RC2, as well as the MD5, SHA1, SHA-256, SHA-384,
and SHA-512 hash algorithms. Additionally, the XML Digital Signature
specification, under development by the Internet Engineering Task Force
(IETF) and the World Wide Web Consortium (W3C), is also available.
The .NET Framework uses these cryptographic functions to support
various internal services.The cryptographic objects are also available in
the Base Class Library for developers who require this functionality.
http://www.syngress.com
14 Chapter 1 • Introducing the Microsoft .NET Platform
Performance and Scalability
Let’s face it—there is no magic bullet that will allow a poorly designed application
to scale well.What the .NET Framework is giving you are tools to make it
easier to design better performing software. One big gain for Web development
will come from ASP.NET’s improved support for keeping code, data, and presentation
separate. .NET offers features for transaction handling and component
pooling, but makes them easier to use than they were in previous incarnations, so
more development will be likely to take advantage of them.The .NET Base Class
Library has an enormous set of functionality, which means that you will have to
write less basic code and spend more time refining the features and performance
of your applications.
New versions of Microsoft software christened with the .NET emblem offer
improved performance over earlier versions. SQL Server.NET offers quite an
enhancement over earlier versions of the database engine, and other server products
offer enhanced scalability as well.When you redesign an application around
the .NET Framework, take advantage of the latest advances all around and see
what the results are.
Components of the .NET Architecture
As we mentioned earlier, there is a lot to the .NET Framework. In this section,
we identify the individual components and describe their features and how they
fit into the overall picture.
.NET Runtime
The heart of the .NET Framework is the CLR. Similar in concept to the Java
Virtual Machine, it is a runtime environment that executes MSIL code. Unlike
the Java environment, which is the concept of one language for all purposes, the
.NET platform supports multiple programming languages through the use of the
Common Language Specification, which defines the output required of compilers
that want to target the CLR.
Managed/Unmanaged Code
Because all code targeted at the .NET platform runs with the CLR environment,
it is referred to as managed code.This simply means that the execution of the
code and its behavior is managed by the CLR.The metadata available with managed
code contains the information required to allow the CLR to manage its safe
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 15
execution. By safe execution we mean memory and security management, type
safety, and interlanguage interoperability. Unmanaged code can write to areas of
memory it does not own, execute instructions at arbitrary locations in memory,
and exhibit any number of other bad behaviors that cannot be managed or prevented
by the CLR. Most of the applications running on Windows today are
unmanaged.
Intermediate Language
The .NET intermediate language, MSIL, is defined in the Common Language
Specification. It is an amalgam of a low-level language similar in many ways to a
machine language and a higher object language.You can write applications directly
in MSIL,much as you can write directly in assembly language.Thankfully, this is
not necessary for most purposes.
Common Type System
.NET applications, regardless of their source languages all share a common type
system.What this means is that you no longer have to worry when doing development
in multiple languages about how a data type declared in one language
needs to be declared in another.Any .NET type has the same attributes regardless
of the language it is used in. Furthermore, all .NET data types are objects,
derived from System.Object.
Because all data types derive from a common base class, they all share some
basic functionality, for example the ability to be converted to a string, serialized,
or stored in a collection.
.NET Base Class Library (BCL)
If I could have bought a library that offered everything the .NET Base Class
Library offers when I started programming, a year’s salary would have seemed
reasonable—there really is that much to it. Almost everything in the .NET environment
is contained within the BCL. Let’s look at a “Hello World” example:
using System;
class Hello
{
public static void Main()
{
http://www.syngress.com
16 Chapter 1 • Introducing the Microsoft .NET Platform
Console.WriteLine(“Hello World”);
}
}
The only function contained in this simple program is a call to the WriteLine
method of the Console class.What is really unique about the .NET environment
is that .NET languages don’t have to implement even the most basic functions;
they are available in the BCL. Because all .NET languages share the same
common set of libraries, the code being executed by your C# program is the
same code being executed by a program written in another language.This means
that all languages that target the .NET environment essentially share the same
capabilities, except they have different syntax.
Some people will wonder why we even have different languages if they all
have the same capabilities. A few reasons immediately spring to mind:
 Programmers don’t like change.
 Programmers usually have a favorite language.
 Programmers don’t like change…
Imagine if Microsoft had come out with all the good things in .NET, but said
that in order to use it, we all had to learn a new language. Lots of people might
have never even given it an honest look unless forced by their employers.Making
it available for all languages makes it seem less like the chore of learning a new
language and more like the excitement of receiving a new library with tens of
thousands of functions that will make your life as a developer easier.
Assemblies
Assemblies are the means of packaging and deploying applications and components
in .NET. Just like a compiled application or component today, assemblies
can be made up of either single or multiple files. An assembly contains metadata
information (covered in the next section), which is used by the CLR for everything
from type checking and security to actually invoking the components
methods. All of this means that you don’t need to register .NET components,
unlike COM objects.
Metadata
Metadata is the feature that lets the CLR know the details about a particular
component.The metadata for an object is persisted at compile time and then
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 17
queried at runtime so that the CLR knows how to instantiate objects, call their
methods, and access their properties.Through a process called reflection, an application
can interrogate this metadata and learn what an object exposes.This is
similar to the way IDispatch and type libraries work in COM.
Unlike COM, where the information about a component can be found in
type libraries and the Registry, where it is only associated with the actual component,
.NET metadata is stored within the component itself in a binary format
packaged inside the assembly.The metadata contains a declaration for every type
and a declaration, including names and types, for all of its members (methods,
fields, properties, and events). For every method implemented by the component,
the metadata contains information that the loader uses to locate the method
body. It is also possible (but not required) for the creator of a class type to associate
help text and comments with a method or parameter in the metadata, similar
to the way that information can be associated with a component using
information within the IDL in the COM world.
Besides the low-level information described in this section, a component also
includes information regarding its version and any culture information specific to
the component.The culture information can be queried at runtime and used in
developing localized applications. Look at the System.Reflection.AssemblyName class
as a place to get started, and check out the CultureInfo class to see how extensive
the culture support of .NET components can be.You can also use reflection to
determine a components version, which might be useful if your application is
dynamically loading components and needs to make adjustments for different
versions.
Assemblies and Modules
.NET applications are deployed as assemblies, which can be a single executable or
a collection of components.When you create a .NET application, you are actually
creating an assembly, which contains a manifest that describes the assembly.
This manifest data contains the assembly name, its versioning information, any
assemblies referenced by this assembly and their versions, a listing of types in the
assembly, security permissions, its product information (company, trademark, and
so on), and any custom attribute.
An assembly that is shared between multiple applications also has a shared
name (also known as a strong name).This is a key pair containing a globally unique
name (think GUID from COM) as well as an encrypted digital signature to prevent
tampering.This information is optional and may not be in a component’s
manifest if it was not intended as a shared component.
http://www.syngress.com
18 Chapter 1 • Introducing the Microsoft .NET Platform
Creating .NET modules that do not contain assembly manifest data is also
possible.These modules can then be added to an assembly, by including it in the
Visual Studio project. An example of why you might want to do this would be if
you had a component that was logically divided into several subcomponents that
would be best distributed and versioned as a single unit.
Assembly Cache
The assembly cache is a directory normally found in the \WinNT\Assembly directory.
When an assembly is installed on the machine, it can be merged into the
assembly cache, depending upon the installation author or the source of the
assembly.The assembly cache has two separate caches: a global assembly cache and
a transient assembly cache.When assemblies are downloaded to the local machine
using Internet Explorer, the assembly is automatically installed in the transient
assembly cache. Keeping these assemblies separated prevents a downloaded component
from impacting the operation of an installed application.
Now for what may be a great feature that you won’t think of until your project
is finished.The assembly cache will hold multiple versions of an assembly,
and if your installation programs are written correctly, they cannot overwrite a
http://www.syngress.com
Finally, a Complete Debugging Solution
Some old-school programmers eschew today’s fancy Integrated
Development Environments (IDEs) as a mere toy for the weak. (Giving
away my age, it’s mostly us crusty old Unix programmers) However, the
debugging capabilities offered by the new Visual Studio.NET IDE may
finally change their minds. The new IDE provides end-to-end debugging
of applications across languages, projects, processes, and stored procedures.
This is a monumental achievement on the part of the Visual
Studio development team.
Using the integrated debugger, developers can step between HTML,
script, and code written in any of the .NET supported languages complete
with integrated call stacks offering a total solution for end-to-end
development.
Debugging…
Introducing the Microsoft .NET Platform • Chapter 1 19
previous version of an assembly that may be needed by another application.You
read that right, the .NET Framework is making a solid effort to banish DLL Hell.
Just to clarify what this means, the assembly cache can contain multiple versions
of a component, as an example, we’ll say we’ve installed versions 1.0 and 1.1
of MyComponent.dll on a system. If an application was built and tested using
Version 1.0 of MyComponent.dll, the CLR will see this when it reads the application’s
metadata and will load Version 1.0 of MyComponent.dll, even though a
later version of the assembly exists in the cache.The application will continue to
function normally because the code that it is executing is the same code that it
was built and tested with.Thanks to this feature, you also don’t have to maintain
compatibility with earlier versions of your components.This feature alone is
enough to make the .NET architecture great.
Reflection
Reflection is the means by which .NET applications can access an assembly’s metadata
information and discover its methods and data types at runtime.You can also
dynamically invoke methods and use type information through late binding
through the Reflection API.
The System.Type class is the core of the reflection system. System.Type is an
abstract class that is used to represent a Common Type System type. It includes
methods that allow you to determine the type’s name, what module it is contained
in, and its namespace, as well as if it is a value or reference type.
For example, using the System.Reflection.Assembly class you can retrieve all of
the types in an assembly, and all of the modules contained in the assembly.To
invoke a method of a class loaded at runtime, you would use a combination of
the Activator class to create an instance of the type you had obtained through the
Assembly class.Then you can use the type’s GetMethod method to create a
MethodInfo object by specifying the method name that you wish to invoke. At
this point, you can use the MethodInfo object’s Invoke method, passing it the
instance of the type you created with the Activator class.
It sounds a lot like some of the nasty bits of COM programming, but the
Reflection API genuinely makes it a lot easier.
Just In Time Compilation
The .NET CLR utilizes Just In Time (JIT) compilation technology to convert
the IL code back to a platform/device–specific code. In .NET, you currently have
three types of JIT compilers:
http://www.syngress.com
20 Chapter 1 • Introducing the Microsoft .NET Platform
 Pre-JIT This JIT compiles an assembly’s entire code into native code at
one stretch.You would normally use this at installation time.
 Econo-JIT You would use this JIT on devices with limited resources. It
compiles the IL code bit-by-bit, freeing resources used by the cached
native code when required.
 Normal JIT The default JIT compiles code only as it is called and
places the resulting native code in the cache.
In essence, the purpose of a JIT compiler is to bring higher performance to
interpreted code by placing the compiled native code in a cache, so that when
the next call is made to the same method/procedure, the cached code is executed,
resulting in an increase in application speed.
Garbage Collection
Memory management is one of those housekeeping duties that takes a lot of programming
time away from developing new code while you track down memory
leaks. A day spent hunting for an elusive memory problem usually isn’t a productive
day.
.NET hopes to do away with all of that within the managed environment
with the garbage collection system. Garbage collection runs when your application
is apparently out of free memory, or when it is implicitly called but its exact
time of execution cannot be determined. Let’s examine how the system works.
When your application requests more memory, and the memory allocator
reports that there is no more memory on the managed heap, garbage collection is
called.The garbage collector starts by assuming everything in memory is trash
that can be freed. It then walks though your application’s memory, building a
graph of all memory that is currently referenced by the application. Once it has a
complete graph, it compacts the heap by moving all the memory that is genuinely
in use together at the start of the free memory heap. After this is complete,
it moves the pointer that the memory allocator uses to determine where to start
allocating memory from the top of this new heap. It also updates all of your
application’s references to point to their new locations in memory.This approach
is commonly called a mark and sweep implementation.
The exception to this is with individual objects over 20,000 bytes.Very large
objects are allocated from a different heap, and when this heap is garbage collected,
they are not moved, because moving memory in this size chunks would
have an adverse effect on application performance.
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 21
As you can see, garbage collection involves a lot of work, and it does take
some time.A number of performance optimizations involved in the .NET
garbage collection mechanism make it much more than the simple description
given here.
Normally you will just let the CLR take care of running garbage collection
when it is required. However, at times you may want to force the garbage collector
to run, perhaps before starting an operation that is going to require a large
amount of memory.To do this, just call GC.Collect(). And if you want to report
on your memory use at various points during your application’s execution to
help you determine when might be a good time to force collection, you can use
GC.GetTotalMemory(bool forceFullCollection).
As you can probably guess, the parameter forceFullCollection determines if
garbage collection is run before returning the amount of memory in use.
NOTE
For those of you who may want to know more about how the .NET
garbage collector actually works its magic, Jeffery Richter wrote two articles
for MSDN magazine in the November and December 2000 issues
that describe the system architecture in some depth. You can find them
online at msdn.microsoft.com/msdnmag/issues/1100/GCI/GCI.asp and
msdn.microsoft.com/msdnmag/issues/1200/GCI2/GCI2.asp.
Exploring the Code Cycle
Let’s take a look at what’s really going on with a .NET application from code to
execution.We’ve already covered that the compiler is going to transform your
source code into IL, but what else is happening from code to running application?
Here’s an example:
1. You write your “Hello World” application in Visual Studio .NET using
the C# Console Application project.
2. The compiler outputs the MSIL code and a manifest into an exe file
that has a standard Win32 executable header.
Let’s stop here and take a look at the output using ildasm.exe, a MSIL disassembly
tool provided with the .NET SDK. Here is the Hello.exe manifest:
http://www.syngress.com
22 Chapter 1 • Introducing the Microsoft .NET Platform
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 1:0:2411:0
}
.assembly Hello
{
.custom instance void [mscorlib]System.Reflection.
AssemblyKeyNameAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyKeyFileAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyDelaySignAttribute::.ctor(bool) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyProductAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 )
.custom instance void [mscorlib]System.Reflection.
AssemblyTitleAttribute::.ctor(string) = ( 01 00 00 00 00 )
//–The following custom attribute is added automatically, do not
// uncomment–
// .custom instance void
// [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(bool,
// bool) = ( 01 00 01 01 00 00 )
.hash algorithm 0x00008004
.ver 1:0:628:38203
}
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 23
.module Hello.exe
// MVID: {D840F359-1315-4B70-8238-0D77358D57D0}
.imagebase 0x00400000
.subsystem 0x00000003
.file alignment 512
.corflags 0x00000001
// Image base: 0x032c0000
You can see that the manifest references the assembly mscorlib that contains
the System.Console.WriteLine method. It also contains the version of this assembly
that the application was built with, so that the correct one can be loaded when
the application is executed. And here is the MSIL output for the Main() function:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr “Hello World”
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Hello::Main
Pretty basic stuff—the CLR is doing all the work.What you can’t see is that
behind the scenes the compiler added a call to a function in the .NET Runtime
named _CorExeMain. If you examine the EXE using the depends.exe utility that
installs with VS .NET, you will see that it is bound to mscoree.dll for this function,
but you won’t see any of the DLLs containing the .NET Base Class Library
Functions.This is because those functions are invoked through the CLR, not
through the normal Windows operating system functions. Figure 1.2 illustrates
the process by which your application’s source code is eventually executed as
native code.
1. When the application is executed, it first behaves just like a normal
Win32 application, loading any required libraries, including mscoree.dll,
which exports the _CorExeMain function.
2. The loader then jumps to the EXE’s entry point. Because the Windows
operating system itself cannot execute the MSIL code, the C# compiler
placed the _CorExeMain function at the entry point.
http://www.syngress.com
24 Chapter 1 • Introducing the Microsoft .NET Platform
3. When the _CorExeMain function is invoked, it begins the execution of
the MSIL code.
4. The CLR compiles the MSIL code into the native machine format as it
processes the MSIL code by using a JIT compiler.The JIT compiles
code as it is executed, it does not process the entire application before
beginning execution. Once a given function is compiled, the resulting
machine code is cached so that it does not have to be recompiled at a
later point.
5. The native code is then executed by the system.
The Pursuit of Standardization
Microsoft is actively pursuing a process whereby the Common Language
Infrastructure and C# Programming Language can be standardized so that any
http://www.syngress.com
Figure 1.2 Code Cycle Diagram
Application source code is written.
A .NET compiler generates IL code and builds assembly.
Application execution begins normally, with the loader reading the Windows executable header.
The _CorExeMain function inserted at the application’s entry point is executed by the OS.
_CorExeMain uses a Just In Time (JIT) compiler to compile the IL code to native code.
Native code is executed.
Required DLLs are loaded.
Introducing the Microsoft .NET Platform • Chapter 1 25
company or individual can create applications based on a recognized standard. On
October 31, 2000, Hewlett-Packard, Intel, and Microsoft jointly submitted proposed
draft standards to ECMA for use in defining the C# Programming
Language (ECMA TC39/TG2) and the Common Language Infrastructure
(ECMA TC39/TG3) standards.The official submissions are available from the
ECMA Web site at http://www.ecma.ch.
Since submission, the original documents have been reviewed and edited by
the participants of ECMA TC39/TG2 and TC39/TG3. However, little information
is available about the group’s progress, other than the availability of updated
documentation once or twice a month. Given that the standards process for platforms
and languages that have already been released and are in widespread use is
generally measured in years, it is probably premature at this point to say much
about the progress that is being made.
Fujitsu Software, Hewlett-Packard, Intel Corporation, International Business
Machines, ISE, Microsoft Corporation, Monash University, Netscape, Openwave,
Plum Hall, and Sun Microsystems are all participants in the standards process.
http://www.syngress.com
26 Chapter 1 • Introducing the Microsoft .NET Platform
Summary
The .NET platform is a great leap forward in the evolution of computing from
PCs connected to servers through networks such as the Internet, to one where all
manner of smart devices, computers, and services work together to provide a
richer user experience.The .NET platform is Microsoft’s vision of how the
developers of this new breed of software will approach the challenges this change
will provide.
If some of the .NET concepts sound familiar, there’s a good reason:The
.NET platform is the next generation of what was called Windows DNA.
Although Windows DNA did offer some of the building blocks for creating
robust, scalable, distributed systems, it generally had little substance in and of
itself, where .NET actually has an integrated, comprehensive design and well
conceived, usable tools.
The components at the heart of the .NET platform are the Common
Language Runtime, the Base Class Library, and the Common Language
Specification.The .NET Base Class Library exposes the features of the Common
Language Runtime in much the same way that the Windows API allows you to
utilize the features of the Windows operating system. However, it also provides
many higher-level features that facilitate code reuse.The Common Language
Specification gives language vendors and compiler developers the base requirements
for creating code that targets the .NET Common Language Runtime, making it
much easier to implement portions of your application using the language that’s
best suited for it.The .NET platform allows languages to be integrated with one
another by specifying the use of the Microsoft Intermediate Language (MSIL, or
just IL) as the output for all programming languages targeting the platform.This
intermediate language is CPU-independent, and much higher level than most
machine languages.
Automatic resource management is one of the most discussed features of the
.NET platform, and for good reason. Countless man-hours have been spent
chasing problems introduced by poor memory management.Thanks to the managed
heap memory allocator and automatic garbage collection, the developer is
now relieved of this tedious task and can concentrate on the problem to be
solved, rather than on housekeeping.When an allocated object is no longer
needed by the program, it will be automatically be cleaned up and the memory
placed back in the managed heap as available for use.
Once written and built, a managed .NET application can execute on any
platform that supports the .NET Common Language Runtime. Because the
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 27
.NET Common Type System defines the size of the base data types that are available
to .NET applications, and applications run within the Common Language
Runtime environment, the application developer is insulated from the specifics of
any hardware or operating system that supports the .NET platform.Although
currently this means only Microsoft Windows family of operating systems, work is
underway to make the .NET core components available on FreeBSD and Linux.
The .NET architecture now separates application components so that an application
always loads the components with which it was built and tested. If the application
runs after installation, the application should always run.This is done with
assemblies, which are .NET-packaged components.Assemblies contain version
information that the .NET Common Language Runtime uses to ensure that an
application will load the components it was built with. Installing a new version of
an assembly does not overwrite the previous version, thanks to the assembly cache,
a specialized container (directory) that store system-installed .NET components.
Given the massive amount of legacy code in use, it was necessary to allow
.NET applications to interact with unmanaged code. As you can probably guess,
unmanaged code is code that isn’t managed by the .NET Common Language
Runtime.However, this code is still run by the CLR, it just doesn’t get the advantages
that it offers, such as the Common Type System and Automatic Memory
Management.There are a couple of times when you will probably end up using
unmanaged code, making API or other DLL calls, interfacing with COM components
or allowing COM components to utilize .NET components. However,
realize that by calling unmanaged code, you may be giving up portability.
Developing software using .NET technology is a big change; the technology
has a lot of pieces to the puzzle and more than a few new ideas. Hopefully, we have
given you a solid introduction into the basics, and you now have a foundation
upon which to build your skills using the information found in the rest of the
book. If you want more detail on a particular feature of the platform, the MSDN
Web site contains a vast amount of reference material that covers the features of the
.NET platform at a much more technical level than we attempted here.
Solutions Fast Track
Introducing the .NET Platform
􀀻 Software is changing from a closed to a connected world, much like
personal computers themselves are.The .NET Framework is designed to
http://www.syngress.com
28 Chapter 1 • Introducing the Microsoft .NET Platform
make it easier to create distributed applications that leverage this new
paradigm.
􀀻 There are multiple pieces to the .NET Framework, starting from a
shared Common Language Infrastructure and extended to various
Microsoft servers and services.
􀀻 The .NET Framework is designed as a single consistent development
environment offering shorter development cycles, improved scalability,
and better behaved programs.
Features of the .NET Platform
􀀻 The .NET platform hides the gory details of interfacing with the
underlying operating system functions and lets you concentrate on the
solution at hand.
􀀻 Multilanguage development is greatly simplified thanks to the use of the
intermediate language and Common Language Runtime.
􀀻 Automatic memory management reduces the level of effort required to
manage resources; you can simply let the garbage collector take care of
cleaning up and preventing memory leaks.
􀀻 It includes a new versioning system designed to end DLL Hell.
􀀻 Much of the platform is built on open standards, such as XML and SOAP.
􀀻 You are not forced to rewrite everything to use .NET—interoperability
with existing code and components is maintained.
􀀻 It includes an improved security model, which allows a fine-grained
control as well as integrated safety from security flaws caused by
problems related to buffer overruns.
Components of the .NET Architecture
􀀻 The Common Language Runtime is a managed execution environment
offering many advantages over the traditional native code development
methods.
􀀻 All languages compile to the same intermediate language.The IL is
platform- and processor-independent, potentially allowing .NET
applications someday to run on non-Windows operating systems.
http://www.syngress.com
Introducing the Microsoft .NET Platform • Chapter 1 29
􀀻 The Common Type System allows all languages to share data types
without requiring that the developer deal with interpreting different
languages conventions.
􀀻 It includes a large Base Class Library shared by all .NET languages,
offering a wide range of functionality intended to improve developer
functionality.
􀀻 Assemblies and metadata are designed to improve on some of the
weaknesses of the COM model, by including information about the
versions of required components a given component was built with.
􀀻 The assembly cache is a new facility designed to contain shared .NET
components.The assembly cache can contain multiple versions of a
given assembly, helping to put an end to DLL Hell.
􀀻 Through a process called reflection, an application can interrogate this
metadata and learn what an object exposes.
Exploring the Code Cycle
􀀻 Compiling your source code, regardless of the language used, results in
IL code output.
􀀻 Behind the scenes, the compiler inserts a stub function to load the CLR,
which then runs the Just In Time Compiler to transform the IL code
into native code.
The Pursuit of Standardization
􀀻 Microsoft is making an active effort to see that the technologies on
which the .NET platform is based are accepted as standards by a
recognized standards organization.
􀀻 The draft standards for the CLI and C# language have been submitted
to ECMA.
􀀻 The current versions of the standards are available online.They are
updated once or twice a month at the current time.
http://www.syngress.com
30 Chapter 1 • Introducing the Microsoft .NET Platform
Q: If any .NET language has access to everything in the Base Class Library, why
is there so much talk about C#?
A: Although in theory all .NET languages have equal access to the BCL, in
reality it was left up to the language teams to determine what level of support
they wanted to offer, at least beyond the minimums needed for basic compliance.
In our opinion, C#, because it was developed as a new language specifically
for the .NET platform, has the best support for .NET applications.
Q: I’m a C/C++ developer.Why on earth would I give up all the power and
control I have now? And what about speed—native code is always better.
A: The .NET platform is all about a new way of developing applications. Many
of the enhancements are there for increased productivity. For example, today a
C++ application for the desktop and PocketPC are vastly different pieces of
code. In the .NET world, they can be the same. Additionally, there are a lot of
prebuilt classes available in the BCL that have a lot to offer any developer.As
to the native code issue, that is debatable. In a perfect model, you might be
right, but for the majority of applications developed today, it’s just not a significant
factor.The improvements in versioning support and automatic memory
management alone make a good argument for the managed environment.
Q: Is everything in the Win32 API exposed through the BCL?
A: Not through the BCL, but you can make API calls directly through most
languages.
Q: Why not just switch to Java?
A: I’m going to preface this answer by saying that I like Java, I’ve written several
successful commercial projects in Java, and it met the requirements of those
projects well. However, Java as a platform requires the developer to buy into
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.
Introducing the Microsoft .NET Platform • Chapter 1 31
the idea of a single language for all things, which goes against my philosophy
of “use the right tool for the job.”The .NET design allows and encourages
cross-language development, letting programmers make use of language skills
already developed as well as leverage the various strengths of each .NET language.
As to the cross-platform features, my experience and that of many
others is summarized by the often-heard phrase “write once, test everywhere,”
rather than the advertised “Write once, run everywhere.” In my
opinion, Java also suffers from some earlier design oversights that .NET
appears to have learned from. Look at the difference in the Streams implementation
on both platforms for an example; the initial Java design did not
accommodate Unicode character streams.When this was corrected in JDK
1.1, Java ended up with four base stream classes.
Q: Isn’t the fact that .NET applications aren’t native code going to increase PC
requirements?
A: This depends on what type of application you’re developing, but it’s a pretty
safe bet.The managed environment introduces additional memory requirements,
but they will be negligible in practice. Every new development in software
engineering has required more horsepower, and we’re really not taxing
today’s processors with most software. Buying more memory, if it is required,
should be a simple sale; developer man-hours are generally a lot more expensive
than more memory.
http://www.syngress.com

Introducing C#
Programming
Solutions in this chapter:
 Getting Started
 Creating Your First C# Program
 Introducing Data Types
 Explaining Control Structures
 Understanding Properties and Indexers
 Using Delegates and Events
 Using Exception Handling
 Understanding Inheritance
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 2
33
34 Chapter 2 • Introducing C# Programming
Introduction
Let’s start with your first look into the C# language.This chapter teaches you to
write, build, and execute console applications.This provides the groundwork for
developing applications that use more advanced features of .NET in later chapters.
The C# language is a modern object-oriented language. In some ways, it can
be seen as an extension of other languages that came before it. C# is most often
compared with Java and C++. If you are not familiar with C# programming, this
chapter gets you started. If you are familiar with Java or C++, you may want to
just skim this chapter because the concepts presented will look very familiar to
you. If you already know C#, feel free to skip this chapter entirely.We assume
you have a basic understanding of at least one object-oriented language.
We’ve mentioned that C# is a modern object-oriented language. Let’s take a
little time to explain what we mean by that. C# is a modern language. It supports
the notion of data types, flow of control statements, operators, arrays, properties,
and exceptions. Depending on the language(s) you are accustomed to programming
in, most of these concepts should be familiar to you.Throughout the
chapter, you will see examples and/or discussions of most of these features of C#.
C# is an object-oriented language. It supports the notion of classes and the
object-oriented nature of classes including encapsulation, inheritance, and polymorphism.
C# also supports interfaces in conjunction with the .NET Common
Language Runtime (CLR) garbage collection, which some feel is necessary in an
object-oriented language. It also supports the notion of indexers, which in simplified
terms lets you manipulate objects as arrays and delegates, which you can
think of as method callbacks on steroids.
The .NET Framework supports console applications, graphical user interface
(GUI) applications (Windows Forms), browser-based applications (Web Forms
and ASP.NET), and Web Services.This chapter will focus on command line
applications, which are known as console applications. Console applications have a
text-only user interface. In later chapters, you will learn how to create other types
of applications.The focus of this chapter is to explain the concepts that are new
and/or different in C# from other object-oriented languages. Concepts that are
familiar to object-oriented programmers are covered in brief.
Throughout the chapter, a series of sample programs are presented that illustrate
the concepts introduced in each section.The sample programs are available on the
CD included with this book. Although there are separate sample programs for each
section, each sample builds on concepts covered in earlier sections of this chapter.
http://www.syngress.com
http://www.syngress.com
Getting Started
Microsoft supplies a full-blown development environment—Visual Studio
.NET—for building .NET applications. But, you don’t need to buy anything to
get started writing C# programs.The Microsoft.NET Framework software development
kit (SDK) is available for download from Microsoft’s Web site for free
(http://msdn.microsoft.com/net). It contains a command line C# compiler that
we use to compile the examples in this chapter.This chapter assumes you have
already installed the Microsoft.NET Framework SDK.The only other thing you
need to get started is a text editor. Because you are writing C# programs for the
Microsoft.NET Framework on Microsoft Windows platforms, you have several
choices freely available in the Windows operating system.We will stick with the
old reliable Notepad as our source code editor of choice.
For users that may be new to the Windows operating system, we run through
some explicit instructions on using the command line and Notepad. If you are
familiar with Windows, or if you aren’t interested in typing the programs in yourself,
you can skip ahead to the next section.
The first things you need to do are start a command line session and create
some directories to store your sample programs in.To start a new command line
session, click Start on the lower-left corner of the screen. Select the Run menu
option from the pop-up menu.The Run dialog box will appear.Type cmd in the
edit box and click OK.You should now see a command line window similar to
Figure 2.1.
Now you will create directories to save your C# programs in.You can set up
any directory structure you like, but for purposes of this example, we use a structure
that uses an abbreviated book title as the root directory, the chapter as a subdirectory,
and the program name as the lowest level directory:
Introducing C# Programming • Chapter 2 35
Figure 2.1 A Command Line Window
36 Chapter 2 • Introducing C# Programming
1. Type md C#.NET at the command prompt and press Enter.
2. Type cd C#.NET and press Enter to navigate to the C# .NET
directory.
3. Type md chap1 and press Enter to create a subdirectory called chap1.
4. Type cd chap1 and press Enter to navigate to the chap1 directory.
5. Type md FirstCSharpProgram and press Enter.
6. Type cd FirstCSharpProgram.
You have now created the directory to store your first C# program, which
will be called FirstCSharpProgram. Leave the command-line window open.
You will use it to compile your first program a little later.
As previously mentioned, Notepad is our preferred source code editor.To
start Notepad, click Start | Programs | Accessories | Notepad.You should
now see the Notepad application.You will now create an empty source code file
in the directory you previously created for your first C# program:
1. Click File | Save.
2. In the Save dialog box, use the Save In drop-down list to select the
FirstCSharpProgram folder you just created.
3. C# programs typically use the file extension .cs, which is the convention
we will follow.Type FirstCSharpProgram.cs in the File name edit
area.The dialog box should look like Figure 2.2.
4. Click Save.
http://www.syngress.com
Figure 2.2 Saving a File in Notepad
Introducing C# Programming • Chapter 2 37
You now have an empty source file available for your first C# program. As
you type C# source code into Notepad, you can save your source at any time by
clicking File | Save.You are finally done with the preliminaries and are ready to
start writing code.
Creating Your First C# Program
The first C# program we look at is a very simple program that writes a couple
of lines of text to the console.The program source code is shown in Figure 2.3.
If you are following along, type in the program using Notepad and save it.We
examine the code in the following sections to get a general feel for the structure
of a C# console application.The source code is included on the CD that accompanies
this book in a file named FirstCSharpProgram.cs.
Figure 2.3 The FirstCSharpProgram.cs Listing
using System;
namespace FirstCSharpProgram
{
/// <summary>
/// My first C# class. Contains the program entry point.
/// </summary>
class FirstCSharpClass
{
static void Main( string[] args )
{
try
{
/*
* Show when we wrote our first program on screen.
*/
DateTime today = DateTime.Now;
Console.WriteLine( “I wrote my first C# program at: ” +
today.ToString() );
http://www.syngress.com
Continued
38 Chapter 2 • Introducing C# Programming
if ( args.Length > 0 )
{
// Show an optional message on screen.
string msg = “You wanted to say: ” + args[0];
Console.WriteLine( msg );
}
}
catch ( Exception exception )
{
// Display any errors on screen
Console.WriteLine( exception.Message );
}
}
}
}
Compiling and Executing
The command line compiler included in the Microsoft.NET Framework
SDK is named csc.exe.To compile the application, type csc.exe /out:
FirstCSharpProgram.exe FirstCSharpProgram.cs on the command line
and press Enter. If you typed the program correctly, no errors should display.
Taking a look at the command line, the first part of the statement, csc.exe,
invokes the C# compiler.The compiler takes two arguments in this case.The first
is /out:FirstCSharpProgram.exe.The /out compiler switch indicates that the following
text will be the name of the compiled file that will be created, in our case
FirstCSharpProgram.exe.The final argument is the name of the source code file to
compile, FirstCSharpProgram.cs.The compiler takes many other optional arguments.
But for simple programs, you should be able to use the same commandline
text and just replace the name of the output file and the name of the source
code file.
FirstCSharpProgram takes one optional command-line argument when it executes,
that is, the message to display.To execute the program with an optional
http://www.syngress.com
Figure 2.3 Continued
Introducing C# Programming • Chapter 2 39
message, type the following at the command line prompt: FirstCSharpProgram
“C#, I like it!” Be sure to include the quotes.You should see output very similar
to Figure 2.4 after compiling and running the program.
Now that you’ve written, compiled, and executed your first C# program, let’s
take a look in the next sections at some of the features of C#.
http://www.syngress.com
Figure 2.4 Compiling and Executing the FirstCSharpProgram
Compiling C# Programs/Environment Variables
Your environment variables may not be properly set up if you get the following
error message when compiling your program:
‘csc.exe’ is not recognized as an internal or external command,
operable program or batch file.
When you installed the Framework SDK, your environment variables
should have been set up correctly for you to compile programs. If you
get the error just shown, your environment variables have not been set
correctly. To fix this problem, execute the batch file corvars.bat located
in the bin directory of the SDK. Change directories to the Framework
SDK root directory, normally installed in the directory \Program Files\
Microsoft.NET\FrameworkSDK\bin. Execute the batch file by typing
corvars on the command line. Now change directories back to where
your program is saved and compilation should be successful.
Debugging…
40 Chapter 2 • Introducing C# Programming
Defining a Class
Classes are the basic ingredients of object-oriented languages. Classes are declared
in C# by using the class keyword followed by the class name and brackets surrounding
the body of the class.The FirstCSharpProgram sample has one class,
named FirstCSharpClass.
C#, like most object-oriented classes, supports member variables and methods.
Here is another class that contains some methods and member variables:
class Employee
{
// Member variables
private string m_FirstName;
private string m_LastName;
// Constructor
public Employee( string FirstName, string LastName )
{
m_FirstName = FirstName;
m_LastName = LastName;
}
// Public methods
public string getFirstName() { return m_FirstName; }
public string getLastName() { return m_LastName; }
}
This class has three methods, the constructor Employee and two other
methods, getFirstName and getLastName. It has two member variables, m_FirstName
and m_LastName. Classes and class members (methods and variables) can have
access modifiers associated with them that define their level of visibility.Table 2.1
lists the class and class member visibility access modifiers. Some restrictions apply
to use of the access modifiers—consult the .NET SDK documentation for
details.
http://www.syngress.com
Introducing C# Programming • Chapter 2 41
Table 2.1 Class and Class Member Visibility Access Modifiers
Access Modifier Visibility
public Accessible from anywhere
protected Accessible from this class or any class derived from
this class
internal Accessible within current program (assembly) only
protected internal Accessible within current program (assembly) or any
class derived from this class
private (default) Accessible only within current class
You can see some of these access modifiers applied to the Employee class and
its members. Classes can also support interfaces.You can think of interfaces as
contracts with a class to supply methods defined in the interface. Interfaces supply
class methods and signatures but no implementations. Classes that support a given
interface must supply the implementation of the methods defined by the interface.
Here is the previous Employee class extended to support an interface:
// IXmlRepresentation interface signature
interface IXmlRepresentation
{
string getXML();
}
// Employee class implements IXmlRepresentation
class Employee : IXmlRepresentation
{
private string m_FirstName;
private string m_LastName;
public Employee( string FirstName, string LastName )
{
m_FirstName = FirstName;
m_LastName = LastName;
}
public string getFirstName() { return m_FirstName; }
http://www.syngress.com
42 Chapter 2 • Introducing C# Programming
public string getLastName() { return m_LastName; }
// getXML method implements a method in IXmlRepresentation interface
public string getXML()
{
string xmlEmployee = “<Employee>”;
xmlEmployee += “<FirstName>” + m_FirstName + “</FirstName>”;
xmlEmployee += “<LastName>” + m_LastName + “</LastName>”;
xmlEmployee += “</Employee>”;
return xmlEmployee;
}
}
An interface named IXmlRepresentation, which has one method getXML,
returns a string.The definition of the interface supplies no implementation.The
declaration of the Employee now looks like this:
class Employee : IXmlRepresentation
You can see the interface IXmlRepresentation after the class name and a colon.
This signifies that the Employee class must supply an implementation for all the
methods declared in an interface. As you can see, the Employee class does supply
an implementation for the getXML method.The compiler would generate an
error if the getXML method were missing from the Employee class. Interfaces are
often used to supply functionality to a class that really is not part of the class’s
core functionality. In the case of the Employee class, getting an XML representation
of the employee really is not related to being an employee at all. But, it may
be useful for another class that outputs XML to call the getXML method on
Employee.We show other examples of interfaces later in this chapter.
WARNING
C# does not have deterministic destructors like C++ does. The .NET
Common Language Runtime (CLR) uses garbage collection to clean up
memory and other resources. Long time C++ programmers have a hard
time getting used to this idea. This is a topic that is hotly debated on
newsgroups and bulletin boards devoted to C# programming.
http://www.syngress.com
Introducing C# Programming • Chapter 2 43
Declaring the Main Method
Every C# program must have a Main method. Here is the declaration of the
Main method of the FirstCSharpProgram:
static void Main( string[] args )
Execution of the program starts at the Main method.The Main method is
always declared static, which indicates that it is a method of the class and not of a
particular class instance. Also note that the Main method is declared as a method
of the class FirstCSharpClass. In other languages, such as C/C++, the entry point
is often a global function. Global functions are not supported in C#. Also note
that the letter M is capitalized in the keyword Main.
The Main method can take command-line arguments in the form of a string
array. In FirstCSharpProgram, we check to see if at least one command-line argument
exists. If yes, we print a message to the screen. Here is the relevant code
from FirstCSharpProgram to accomplish this:
if ( args.Length > 0 )
{
string msg = “You wanted to say: ” + args[0];
Console.WriteLine( msg );
}
Program flow of control starts at the beginning of the Main method and continues
executing all statements within the Main method, or until a return statement
is encountered.When all statements have been executed, the program
terminates.
Organizing Libraries with Namespaces
Namespaces are used in.NET to organize class libraries into a hierarchical structure.
One reason to do this is to help organize classes in a meaningful way that is
understood by consumers of the class library. For instance, the .NET Framework
SDK has many namespaces, such as System, System.Windows.Forms, System.IO, and
System.XML.You get a good idea of the types of classes that are contained within
the namespace from just the namespace name itself.The fully qualified name of a
class is the class name prefixed with the namespace name.The period character is
used to separate namespaces nested within other namespaces. It is also used to
separate the class name from the innermost namespace. For example, within the
http://www.syngress.com
44 Chapter 2 • Introducing C# Programming
System.IO namespace is a class named File. Here is some C# code to create an
instance of the class—observe how we wrote the fully qualified name of the class
File by prefixing the namespace name System.IO. Also note that System is the top
namespace and IO is the nested namespace within which the File class resides:
System.IO.File file = new System.IO.File();
Another reason to use namespaces is to reduce naming conflicts. For example,
if your company name is Synergistic Corporation, you could have all of your
namespaces contained with a root namespace named Synergistic.One namespace
might be Synergistic.Tools, and a typical class within the namespace might be
Logger.The full name of the class would be Synergistic.Tools.Logger. It is unlikely
that you will find another class with the same name anywhere, thus eliminating
naming conflicts.
Here is a snippet of the FirstCSharpProgram source code:
namespace FirstCSharpProgram
{
/// <summary>
/// My first C# class.
/// </summary>
class FirstCSharpClass
{

}
}
The namespace keyword indicates that the class FirstCSharpClass is contained
within the namespace FirstCSharpProgram.Therefore, to create an instance of
FirstCSharpClass, use the following code:
FirstCSharpProgram.FirstCSharpClass myInstance =
new FirstCSharpProgram.FirstCSharpClass();
Using the using Keyword
You might be thinking this namespace thing is all right, but you sure do have to
type a lot code to create a new instance of a class. Fortunately, a shortcut exists:
the using keyword.
http://www.syngress.com
Introducing C# Programming • Chapter 2 45
In FirstCSharpProgram, we call the static method WriteLine of the Console class
to write text to the screen.The Console class is actually part of the System namespace
in the .NET Framework class library, so you would expect to see
System.Console.WriteLine() instead of Console.WriteLine().Take a look at the following
line code at the top of the program:
using System;
The using keyword allows you to reference classes in the System namespace
without having to include System prior to the class name.This works equally well
with nested namespaces as in our example of the File class.You can now create a
new instance of a file object by using the following statements:
using System.IO;
File file = new File();
Adding Comments
C# supports three different types of source code comments, single-line comments,
multiline comments, and source code documentation comments. Singleline
comments begin with //.Multiline comments begin with /* and end with
*/ and can span multiple lines.Text between them constitutes the comment.
Source code documentation comments begin with ///. Examples of all three
types of comments from FirstCSharpProgram are shown here:
// Show an optional message on screen.
/*
* Show when we wrote our first program on screen.
*/
/// <summary>
/// My first C# class.
/// </summary>
Source code documentation comments deserve further explanation.You can
supply an additional argument to the C# compiler to parse the source code documentation
comments and emit XML as documentation.The additional argument
takes the form /doc:filename. Here is the command line to build FirstCSharpProgram
modified to create the documentation file:
http://www.syngress.com
46 Chapter 2 • Introducing C# Programming
csc.exe /out:FirstCSharpProgram.exe FirstCSharpProgram.cs /
doc:FirstCSharpProgram.xml
Here is the XML that is generated by the compiler.
<?xml version=”1.0″?>
<doc>
<assembly>
<name>FirstCSharpProgram</name>
</assembly>
<members>
<member name=”T:FirstCSharpProgram.FirstCSharpClass”>
<summary>
My first C# class.
</summary>
</member>
</members>
</doc>
FirstCSharpProgram uses the <summary> tag, which is recognized by the
compiler as a source code documentation tag.You can use many other tags to
document other parts of your code, including parameters, return codes, and so
http://www.syngress.com
Debugging Console Applications: Cordbg.exe
The .NET Framework SDK includes a command-line debugger that you can
use at runtime to debug your applications. A simple example follows:
cordbg FirstCSharpProgram.exe !b FirstCSharpProgram.cs:100
The example starts execution of FirstCSharpProgram.exe and sets a
breakpoint at line 100 of the file FirstCSharpProgram.cs. The debugger
allows you to set and display the value of variables in your program to
aid in debugging your application. You can find more information on
cordbg in the .NET Framework SDK documentation.
Debugging…
Introducing C# Programming • Chapter 2 47
on. In effect, you can self-document your classes and methods for other programmers
using source code documentation comments.The XML emitted can be
converted into other formats, such as HTML, and then be published so that other
programmers can learn the classes and methods available in your program.You
can learn more about XML in Chapter 9.
Introducing Data Types
A programming language wouldn’t be able to do much if it didn’t have data to
work with. C# supports two data types: value types and reference types. Value
types are the typical primitive types available in most programming languages and
are allocated on the stack. Reference types are typically class instances and are allocated
on the heap. Both are discussed in further detail in the following sections.
Value Types
Value types encompass the data types you would traditionally encounter in
nonobject-oriented programming languages.This includes numeric, strings, bytes,
and Booleans.Value types in C# are implemented in the form of Structures and
Enums.Value types are allocated on the stack and therefore have little overhead
associated with them.
Primitive Data Types
Primitive data types include all value types except structures.The primitive data
types are shown in Table 2.2.
Table 2.2 Primitive Data Types, Sizes, and Descriptions
Data Type Size in Bytes Description
sbyte 1 Signed byte
byte 1 Unsigned byte
short 2 Signed short
ushort 2 Unsigned short
int 4 Signed integer
uint 4 Unsigned integer
long 8 Signed long integer
ulong 8 Unsigned long integer
float 4 Floating point
http://www.syngress.com
Continued
48 Chapter 2 • Introducing C# Programming
double 8 Double-precision floating point
decimal 8 96-bit signed number
string n/a Unicode string
char 2 Unicode character
bool n/a True or false
Reference Types
Instances of classes are reference types. Reference types are allocated on the heap.
In C#, all classes are derived from the .NET Framework class Object within the
System namespace. C# does not support pointers, but classes, being reference data
types, act like pointers. If you copy a pointer to another pointer, they both still
reference the same object.You can modify the contents of the original object
from either pointer. In C#, if you instantiate a class object and then make a copy
of it, changes made to either instance of the class change the original object. If
you pass an instance of a class to a class method, changes made to the object
passed in will persist upon returning from the method call.
As we mentioned previously, reference types are allocated on the heap.The
new keyword is used to allocate a new instance of a reference type (class).You
don’t need to free an instance of a class in C#, however.The CLR does garbage
collection on object instances that are no longer referenced. Here is a simple
example of instantiating an object of a class:
using System;
class TestSomeClass
{
static void Main(string[] args)
{
// Class is instantiated here using the new keyword. A new object
// of type SomeClass will be allocated on the heap.
SomeClass instance = new SomeClass();
instance.showMessage( “Here is the message” );
}
http://www.syngress.com
Table 2.2 Continued
Data Type Size in Bytes Description
Introducing C# Programming • Chapter 2 49
}
class SomeClass
{
public void showMessage( string message )
{
Console.WriteLine( message );
}
}
Sometimes class methods require class instances derived from the .NET
Framework class object.The odd thing is that you can pass a primitive data type,
such as an int, to the method. How can this be? C# has a feature called boxing,
which will automatically convert a value type to a reference type when a reference
type is required. Upon return from the method, the reverse process, called
unboxing, will convert back to a value type. As a programmer, you don’t need to
do anything special to take advantage of boxing.You should note, however, that
some overhead is involved in the boxing/unboxing process.
Explaining Control Structures
The C# language supports all of the flow-of-control statements you would normally
expect.This section gives you a very brief look at them.We point out a
few of the problem areas (especially for C/C++ programmers).
Using the if Statement
The if statement executes a series of statements if a test Boolean expression evaluates
to true.The test expression to evaluate must be Boolean.You cannot use a
test numeric expression as in C/C++:
int i = 3;
int j = 0;
if ( i > 2 )
{
j = 3;
}
http://www.syngress.com
50 Chapter 2 • Introducing C# Programming
Using the if-else Statement
The if-else statement adds a path for the false evaluation of the Boolean expression.
int i = 3;
int j = 0;
int k = 0;
if ( i > 2 )
{
j = 3;
}
else
{
j = 4;
k = 5;
}
Using the switch case Statement
The switch statement chooses flow of control based on the evaluation of a
numeric or string comparison.The switch statement does not allow control to fall
through to the next case as in C/C++ unless the case statement is followed
immediately by another case statement. In other words, you must use a break statement
with every case statement.You can also use a goto statement, although most
programmers frown on using them. Here are two examples:
int j = 0;
int i = 1;
switch ( i )
{
case 1:
j = 7;
break;
case 2:
case 3:
http://www.syngress.com
Introducing C# Programming • Chapter 2 51
j = 22;
break;
default:
j = 33;
break;
}
string lastName = “”;
string text = “fred”;
switch ( text )
{
case “fred”:
lastName = “Flinstone”;
break;
case “barney”:
lastName = “Rubble”;
break;
default:
lastName = “Slate”;
break;
}
Using the for Statement
The for statement is used to loop through a series of statements until a test
Boolean expression evaluated at the beginning of the loop is false. In the following
example, the WriteLine method will execute five times:
for ( int i = 0; i < 5; i++ )
{
Console.WriteLine( “I will not talk in class” );
}
http://www.syngress.com
52 Chapter 2 • Introducing C# Programming
Using the while Statement
The while statement is also used to loop through a series of statements until a test
Boolean expression evaluated at the beginning of the loop is false.The following
code has the same result as the previous for statement example:
int i = 0;
while ( i < 5 )
{
Console.WriteLine( “I will not talk in class” );
i++;
}
Using the do while Statement
The do while statement is also used to loop through a series of until a test
Boolean expression evaluated at the end of the loop is false.Therefore, the series
of statements contained within the do while loop will always execute at least once:
int i = 6;
do
{
Console.WriteLine( “I will not talk in class” );
i++;
}
while ( i < 5 );
Using the break Statement
The break statement exits the loop of a for, while, or do while statement regardless
of value of the test Boolean expression. In each of the following examples, the
WriteLine method will execute two times:
int j = 0;
for ( int i = 0; i < 5; i++ )
{
Console.WriteLine( “I will not talk in class” );
j++;
if ( j == 2 )
http://www.syngress.com
Introducing C# Programming • Chapter 2 53
break;
}
int i = 0;
int j = 0;
while ( i < 5 )
{
Console.WriteLine( “I will not talk in class” );
i++;
j++;
if ( j == 2 )
break;
}
int i = 0;
int j = 0;
do
{
Console.WriteLine( “I will not talk in class” );
i++;
j++;
if ( j == 2 )
break;
}
while ( i < 5 );
Using the continue Statement
The continue statement will pass flow of control immediately to the start of a loop
when encountered. In the following example, “I will not talk in class” will display
twice and “At least I’ll try not to talk in class” will display three times:
int j = 0;
for ( int i = 0; i < 5; i++ )
{
j++;
http://www.syngress.com
54 Chapter 2 • Introducing C# Programming
if ( j > 2 )
{
Console.WriteLine( “At least I’ll try not to talk in class” );
continue;
}
Console.WriteLine( “I will not talk in class” );
}
Using the return Statement
The return statement returns flow of control from a method to the caller, optionally
passing back a return value. Here is a complete example:
using System;
class TestDivision
{
static void Main(string[] args)
{
int dividend = 2;
int divisor = 0;
Divider divider = new Divider();
bool ret = divider.divide( dividend, divisor );
if ( ret == true )
Console.WriteLine( “I divided!” );
else
Console.WriteLine( “Something went horribly wrong!” );
}
}
class Divider
{
public bool divide( int dividend, int divisor )
{
http://www.syngress.com
Introducing C# Programming • Chapter 2 55
if ( divisor == 0 )
return false;
int result = dividend / divisor;
return true;
}
}
NOTE
A better way to handle this case would have been to throw an exception
when the divisor is zero. We cover exceptions in a later section.
Using the goto Statement
The goto statement has been the bain of structured programming for many years.
C# supports the goto statement, although as previously stated, we wouldn’t recommend
using it.The goto statement immediately transfers flow of control to the
statement following a label. If you must use goto, here is an example:
int i = 0;
int j = 0;
while ( i < 5 )
{
Console.WriteLine( “I will not talk in class” );
i++;
j++;
if ( j == 2 )
goto jumpeddoutofloop;
}
jumpeddoutofloop:
Console.WriteLine( “I jumped out” );
http://www.syngress.com
56 Chapter 2 • Introducing C# Programming
Understanding Properties and Indexers
Two of the more interesting features of C# are properties and indexers. Properties
allow you to call methods on a class using syntax that indicates you are accessing
member variables. Indexers allow you to access collections within a class using
array syntax.You will see examples of each in the following sections.
Using Properties
If you come from a C++ background, you have probably written many get and
set methods for classes you have created. A typical pattern used by C++ programmers
is to make member variables of a class private and provide public accessor
methods to assign and retrieve the values of the member variables. A public set
method is written to assign a value to a member variable, and a get method is
written to retrieve the value assigned to a member variable. An alternate solution
is to make the member variables themselves public.The advantage of using get
and set methods is that if the underlying data type ever changes, the consumer of
the class does not have to change his code. Only the get and set methods need to
be rewritten.This is often referred to as data hiding.
Using get and set methods has a couple of disadvantages. First, it seems a little
more intuitive to just assign a value to a data member or retrieve its value rather
than having to use accessor methods. Also, slightly less typing is involved in
accessing the data member directly.
C# provides the best of both methods. It supports the idea of properties.
Properties are method calls that look like direct access to member data. Figure 2.5
is a complete listing that shows properties in action.The program is included on
the CD in the file Properties.cs.
NOTE
Throughout the rest of the chapter, we expand on this example of an
employee list.
Figure 2.5 The Properties.cs Program Listing
using System;
/// <summary>
http://www.syngress.com
Continued
Introducing C# Programming • Chapter 2 57
/// Contains the program entry point for the Properties Sample.
/// </summary>
class PropertySample
{
static void Main( string[] args )
{
try
{
// Create a new employee
Employee employee = new Employee();
// Set some properties
employee.FirstName = “Timothy”;
employee.MiddleName = “Arthur”;
employee.LastName = “Tucker”;
employee.SSN = “555-55-5555″;
// Show the results on screen
string name = employee.FirstName + ” ” + employee.MiddleName +
” ” + employee.LastName;
string ssn = employee.SSN;
Console.WriteLine( “Name: {0}, SSN: {1}”, name, ssn );
}
catch ( Exception exception )
{
// Display any errors on screen
Console.WriteLine( exception.Message );
}
}
}
http://www.syngress.com
Figure 2.5 Continued
Continued
58 Chapter 2 • Introducing C# Programming
/// <summary>
/// Represents a single employee
/// </summary>
class Employee
{
private string m_firstName;
private string m_middleName;
private string m_lastName;
private string m_SSN;
// FirstName property
public string FirstName
{
get { return m_firstName; }
set { m_firstName = value; }
}
// MiddleName property
public string MiddleName
{
get { return m_middleName; }
set { m_middleName = value; }
}
// LastName property
public string LastName
{
get { return m_lastName; }
set { m_lastName = value; }
}
// SSN property
http://www.syngress.com
Figure 2.5 Continued
Continued
Introducing C# Programming • Chapter 2 59
public string SSN
{
get { return m_SSN; }
set { m_SSN = value; }
}
}
Get Accessor
Let’s take a look at the source code for the get accessor of the SSN property
(SSN being the employee’s social security number). Consider the following
source code for the SSN property:
public string SSN
{
get { return m_SSN; }
set { m_SSN = value; }
}
First, let’s take a look at the property declaration.The public keyword of the
property indicates its visibility. Normally you will make a property public because
the purpose is to allow consumers to have access to the data associated with the
property. Next, this property works with string data as indicated by the string keyword.
Finally, the name of the property is SSN.
The get accessor method is relatively simple. It just returns the value of the
private data member m_SSN. In the program, you can see the SSN property is
accessed using syntax usually reserved for accessing member data:
string ssn = employee.SSN;
Set Accessor
Here are code snippets that show invocation of the set property of SSN and the
implementation of the set property itself:
employee.SSN = “555-55-5555”;
set { m_SSN = value; }
http://www.syngress.com
Figure 2.5 Continued
60 Chapter 2 • Introducing C# Programming
The set accessor assigns a value to the member variable m_SSN.The value
keyword contains the value of the right side of the equal sign when invoking the
set property.The data type of value will be the type in the declaration of the
property. In this case, it is a string.
One thing to note about the set accessor is that it can do more than just set
the value of a data member. For instance, you could add code to validate the
value and not do the assignment if validation fails.
NOTE
Throughout the samples in this chapter, you will see a lot of string operations
that use the overloaded concatenation operators such as “+” and
“+=” as in the following code:
string string1 = “a” + “b” + “c”;
string1 += “e” + “f”;
In C#, strings are immutable, which means they cannot be changed
once they have a value assigned to them. In the previous example, each
time the string is modified, a new copy of the string is created. This can
lead to performance problems in code that does a large amount of
string operations. The .NET Framework supplies the
System.Text.StringBuilder class, which allows you to create and manipulate
a string using a single buffer in memory for cases where you do a
lot of string processing.
Accessing Lists with Indexers
The need to create and manipulate lists is a common programming task. Let’s
extend our employee example from the last section. Let’s say you need to display
a list of employees.The most logical thing to do would be to create a new
Employees class, which contains all of the individual Employee instances.You would
then iterate through all of the employees displaying each one until there are no
further employees. One way to solve this would be to create a property that
returns the number of employees and a method that returns a given employee
given its position in the list, such as the following:
for ( i = 0; i < employees.Length; i++ )
{
http://www.syngress.com
Introducing C# Programming • Chapter 2 61
Employee employee = employees.getEmployee( i );
Console.WriteLine( employee.LastName );
}
However, it would be more intuitive if we could just treat the list of
employees as an array contained with the Employee object. Here is what that
might look like:
for ( i = 0; i < employees.Length; i++ )
{
Console.WriteLine( employees.[i].LastName );
}
This is precisely what indexers do.They let you use array syntax to access a
list of objects contained inside another class. Indexers do not imply a specific
implementation for the list, however.The list within the containing class could be
a static array, a file on disk, one of the collection classes supplied by the .NET
Framework, or some other implementation. If the underlying implementation is
changed from a static array to a collection class, for example, a programmer using
the Employees class would not need to change her code.This is highly desirable
and analogous to the same situation described in the section discussing properties
in this chapter. Figure 2.6 extends the code listing in Figure 2.5 to make use of
an indexer to iterate through a list of employees.The program is included on the
CD in the file Indexers.cs.
Figure 2.6 The Indexers.cs Program Listing
using System;
using System.Collections;
/// <summary>
/// Contains the program entry point for the Indexers Sample.
/// </summary>
class IndexersSample
{
static void Main( string[] args )
{
try
{
http://www.syngress.com
Continued
62 Chapter 2 • Introducing C# Programming
// Create a container to hold employees
Employees employees = new Employees(4);
// Add some employees
employees[0] = new Employee ( “Timothy”, “Arthur”,
“Tucker”, “555-55-5555” );
employees[1] = new Employee ( “Sally”, “Bess”,
“Jones”, “666-66-6666” );
employees[2] = new Employee ( “Jeff”, “Michael”,
“Simms”, “777-77-7777” );
employees[3] = new Employee ( “Janice”, “Anne”,
“Best”, “888-88-8888″ );
// Display the employee list on screen
for ( int i = 0; i < employees.Length; i++ )
{
string name = employees[i].FirstName + ” ” +
employees[i].MiddleName + ” ” +
employees[i].LastName;
string ssn = employees[i].SSN;
Console.WriteLine( “Name: {0}, SSN: {1}”, name, ssn );
}
Employee employee = employees[“777-77-7777″];
if ( employee != null )
{
string name = employee.FirstName + ” ” +
employee.MiddleName + ” ” + employee.LastName;
http://www.syngress.com
Figure 2.6 Continued
Continued
Introducing C# Programming • Chapter 2 63
string ssn = employee.SSN;
Console.WriteLine( “Found by SSN, Name: {0}, SSN: {1}”,
name, ssn );
}
else
{
Console.WriteLine(
“Could not find employee with SSN: 777-77-7777” );
}
}
catch ( Exception exception )
{
// Display any errors on screen
Console.WriteLine( exception.Message );
}
}
}
/// <summary>
/// Container class for employees. This class implements two
/// indexers
/// </summary>
class Employees
{
private ArrayList m_Employees;
private int m_MaxEmployees;
public Employees( int MaxEmployees )
{
m_MaxEmployees = MaxEmployees;
m_Employees = new ArrayList( MaxEmployees );
http://www.syngress.com
Figure 2.6 Continued
Continued
64 Chapter 2 • Introducing C# Programming
}
// Here is the implementation of the indexer by array index
public Employee this[int index]
{
get
{
// Check for out of bounds condition
if ( index < 0 || index > m_Employees.Count – 1 )
return null;
// Return employee based on index passed in
return (Employee) m_Employees[index];
}
set
{
// Check for out of bounds condition
if ( index < 0 || index > m_MaxEmployees-1 )
return;
// Add new employee
m_Employees.Insert( index, value );
}
}
// Here is the implementation of the indexer by SSN
public Employee this[string SSN]
{
get
{
Employee empReturned = null;
http://www.syngress.com
Figure 2.6 Continued
Continued
Introducing C# Programming • Chapter 2 65
foreach ( Employee employee in m_Employees )
{
// Return employee based on index passed in
if ( employee.SSN == SSN )
{
empReturned = employee;
break;
}
}
return empReturned;
}
}
// Return the total number of employees.
public int Length
{
get
{
return m_Employees.Count;
}
}
}
/// <summary>
/// Represents a single employee
/// </summary>
class Employee
{
private string m_firstName;
private string m_middleName;
private string m_lastName;
private string m_SSN;
http://www.syngress.com
Figure 2.6 Continued
Continued
66 Chapter 2 • Introducing C# Programming
// Constructor
public Employee( string FirstName, string LastName, string
MiddleName, string SSN )
{
m_firstName = FirstName;
m_middleName = MiddleName;
m_lastName = LastName;
m_SSN = SSN;
}
// FirstName property
public string FirstName
{
get { return m_firstName; }
set { m_firstName = value; }
}
// MiddleName property
public string MiddleName
{
get { return m_middleName; }
set { m_middleName = value; }
}
// LastName property
public string LastName
{
get { return m_lastName; }
set { m_lastName = value; }
}
// SSN property
http://www.syngress.com
Figure 2.6 Continued
Continued
Introducing C# Programming • Chapter 2 67
public string SSN
{
get { return m_SSN; }
set { m_SSN = value; }
}
}
You can see how this sets the value of an item in the list and get the value of
an item in the list using arraylike syntax such as this:
employees[0] = new Employee ( “Timothy”, “Arthur”,
“Tucker”, “555-55-5555” );
string ssn = employees[i].SSN;
The portion of the code that implements an Indexer follows:
public Employee this[int index]
{
get
{
if ( index < 0 || index > 4 )
return null;
return m_Employees[index];
}
set
{
if ( index < 0 || index > 4 )
return;
m_Employees[index] = value;
updateCount();
}
}
http://www.syngress.com
Figure 2.6 Continued
68 Chapter 2 • Introducing C# Programming
This sample code implements two indexers, one based on an index entry in
the list and the second based on the SSN of an employee.The code to implement
an indexer is just a property on the containing class.The only real difference
is that now the property takes the index within the list as a parameter.This
example uses an ArrayList, which is part of the System.Collections namespace of
the .NET Framework. So, the code to get an item in the list via an index entry
just returns the item in the ArrayList based on the index entry requested.
Similarly, the code to set an item in the list just sets the item in the ArrayList.A
check is also done to validate that the index entry passed in is within bounds
based on the maximum size of the list passed to the constructor of the Employees
class. Our implementation is relatively simple in that it returns if the index is out
of bounds. A better implementation would be to throw an exception.We cover
exceptions later in this chapter.
The code also implements a second read-only indexer based on SSN.This
illustrates that an indexer can be implemented using more than just the index of
an entry in the list. In the Main method of the program, you can see the following
statement:
Employee employee = employees[“777-77-7777″];
This code calls our SSN indexer implementation.The SSN indexer loops
through the Employee instances contained in the m_Employees ArrayList. If it finds
an Employee instance that has the SSN requested, it returns that Employee instance.
If it doesn’t find it, it returns null.
In C#, the foreach keyword is used to iterate through a list of objects contained
within another object. Here is what our sample program would look like
using foreach:
foreach ( Employee employee in employees )
{
string name = employee.FirstName + ” ” +
employee.MiddleName + ” ” + employee.LastName;
string ssn = employee.SSN;
Console.WriteLine( “Name: {0}, SSN: {1}”, name, ssn );
}
To use the foreach keyword, the class that contains the list must implement the
IEnumerable interface contained within the System.Collections namespace.The
http://www.syngress.com
Introducing C# Programming • Chapter 2 69
IEnumerable interface has one responsibility: return an instance of an object that
implements the IEnumerator interface also from the System.Collections namespace.
The class that implements the IEnumerator interface is responsible for maintaining
the current position in the list and knowing when the end of the list has
been reached. Although this seems overly complex, it allows the flexibility of
having the implementation of IEnumerator be in the class containing the list or in
a separate class.
The complete sample that implements the IEnumerable interface is on the CD
in the Enumerable.cs file. Because the ArrayList class already implements the
IEnumerable interface, all that is necessary in the Employees class is to declare the
class as implementing the IEnumerable interface and then provide the implementation
of the GetEnumerator method of the IEnumerable interface.The GetEnumerator
method simply returns the ArrayList implementation.The relevant code from the
sample on the CD that accomplishes this is shown here:
/// <summary>
/// Container class for employees. This class implements
/// IEnumerable allowing use of foreach sytax
/// </summary>
class Employees : IEnumerator
{
// IEnumerable implementation, delegates IEnumerator to
// the ArrayList
public IEnumerator GetEnumerator()
{
return m_Employees.GetEnumerator();
}
}
At first glance, indexers seem somewhat complex, and talking about them in
the abstract can be a bit confusing. However, when you see the code, it is relatively
simple and provides a clean and simple syntax to iterate though a list of
objects.
Using Delegates and Events
If you are familiar with Windows programming, you’ve most likely dealt with
callbacks. Callbacks are method calls that are executed when some event happens
http://www.syngress.com
70 Chapter 2 • Introducing C# Programming
during processing. For instance, a callback can be established to handle the processing
of an incoming message on a communications port. Another part of the
communications program can wait for messages on a communications port and
invoke the callback whenever a new message arrives. Function pointers perform
the same sort of tasks in straight C/C++ programs.
Delegates in C# improve on method callbacks in two areas. Delegates are type
safe, unlike callbacks in Windows programming. In addition, delegates can call
more than one callback when an event occurs.This is termed multicasting.
Delegates
Let’s extend our employees sample to use delegates.This sample simulates a background
process that receives messages to add new employees to the employee list.
Our queue will be a static array, but in the real world it could be a message
queue (Microsoft Message Queue [MSMQ]), a socket, or some other type of
queue.The source code in Figure 2.7 shows the relevant portions of the sample
pertaining to delegates.The full source code for this sample is on the CD in the
file Delegates.cs.
Figure 2.7 Relevant Portions of the Delegates.cs Program Listing
using System;
using System.Collections;
/// <summary>
/// Contains the program entry point for the Delegates Sample.
/// </summary>
class DelegatesSample
{
static void Main( string[] args )
{
try
{
// Create a container to hold employees
Employees employees = new Employees(4);
// Create and drain our simulated message queue
EmployeeQueueMonitor monitor =
http://www.syngress.com
Continued
Introducing C# Programming • Chapter 2 71
new EmployeeQueueMonitor( employees );
monitor.start();
monitor.stop();
// Display the employee list on screen
Console.WriteLine(
“List of employees added via delegate:” );
foreach ( Employee employee in employees )
{
string name = employee.FirstName + ” ” +
employee.MiddleName + ” ” + employee.LastName;
string ssn = employee.SSN;
Console.WriteLine( “Name: {0}, SSN: {1}”, name, ssn );
}
}
catch ( Exception exception )
{
// Display any errors on screen
Console.WriteLine( exception.Message );
}
}
}
/// <summary>
/// Simulates our message queue.
/// </summary>
class EmployeeQueueMonitor
{
// Delegate signature
http://www.syngress.com
Figure 2.7 Continued
Continued
72 Chapter 2 • Introducing C# Programming
public delegate void AddEventCallback( string FirstName,
string LastName, string MiddleName, string SSN );
// Instance of the delegate
private AddEventCallback m_addEventCallback;
private Employees m_employees;
private int m_lengthQueue;
private string[, ] m_msgQueue =
{
{“Timothy”, “Arthur”, “Tucker”, “555-55-5555”},
{“Sally”, “Bess”, “Jones”, “666-66-6666” },
{“Jeff”, “Michael”, “Simms”, “777-77-7777”},
{“Janice”, “Anne”, “Best”, “888-88-8888” }
};
public EmployeeQueueMonitor( Employees employees )
{
m_employees = employees;
m_lengthQueue = 4;
// Create an instace of the delegate and register the
// addEmployee method of this class as a callback.
m_addEventCallback = new AddEventCallback(
this.addEmployee );
}
// Drain the queue.
public void start()
{
if ( m_employees == null )
return;
http://www.syngress.com
Figure 2.7 Continued
Continued
Introducing C# Programming • Chapter 2 73
for ( int i = 0; i < m_lengthQueue; i++ )
{
string FirstName = m_msgQueue[i,0];
string MiddleName = m_msgQueue[i,1];
string LastName = m_msgQueue[i,2];
string SSN = m_msgQueue[i,3];
// Invoke the callback registered with the delegate
Console.WriteLine( “Invoking delegate” );
m_addEventCallback( FirstName, LastName, MiddleName,
SSN );
}
}
public void stop()
{
// In a real communications program you would shut down
// gracefully.
}
// Called by the delegate when a message to add an employee
// is read from the message queue.
public void addEmployee( string FirstName, string MiddleName,
string LastName, string SSN )
{
Console.WriteLine( “In delegate, adding employee\r\n” );
int index = m_employees.Length;
m_employees[index] = new Employee ( FirstName, MiddleName,
LastName, SSN );
}
}
http://www.syngress.com
Figure 2.7 Continued
74 Chapter 2 • Introducing C# Programming
Single Cast
The source code in the previous section is an example of a single cast delegate.A
single cast delegate invokes only one callback method. Let’s examine our previous
sample to see this.
The EmployeeQueueMonitor class simulates a message queue. It contains a static
array that holds the current messages. At the top of EmployeeQueueMonitor are the
following lines:
public delegate void AddEventCallback( string FirstName,
string LastName, string MiddleName, string SSN );
private AddEventCallback m_addEventCallback;
The first statement defines a delegate and the parameters an object instance
of the delegate takes. In this case, we callback to a method that takes first name,
last name, middle name, and SSN.We do this whenever a request to add a new
employee appears in the message queue.
The second statement declares a member variable to hold our delegate. It is
initially set to null. A new object instance must be created prior to making
method calls through the delegate. An object instance is instantiated in the constructor
of EmployeeQueueMonitor.
m_addEventCallback = new AddEventCallback( this.addEmployee );
This statement creates a new object instance of the delegate.The delegate
takes as an argument the method to call when the delegate is invoked. In this
case, whenever the delegate is invoked, the method that will execute is
EmployeeQueueMonitor.addEmployee.
In the start method of EmployeeQueueMonitor is the following code:
for ( int i = 0; i < m_lengthQueue; i++ )
{
string FirstName = m_msgQueue[i,0];
string MiddleName = m_msgQueue[i,1];
string LastName = m_msgQueue[i,2];
string SSN = m_msgQueue[i,3];
// Invoke the callback registered with the delegate
Console.WriteLine( “Invoking delegate” );
http://www.syngress.com
Introducing C# Programming • Chapter 2 75
m_addEventCallback( FirstName, LastName, MiddleName, SSN );
}
This code simulates draining the message queue of any waiting messages.The
callback function is invoked by treating the m_addEventCallback member variable
as if it were a method call passing it our four parameters. Note that you do not
specify the callback itself when making the call.The delegate maintains the
address of the callback internally and therefore knows the method to call.The
following example shows what not to do:
// Incorrect
m_addEventCallback.addEmployee( FirstName, LastName, MiddleName, SSN );
Multicast
The true power of delegates becomes apparent when discussing multicast delegates.
Let’s extend our previous example a bit further. Because background processes
do not usually have a user interface for human interaction, they typically
log incoming events for later review. Let’s add a second callback to our sample to
log incoming add employee requests.The relevant snippets of code are shown in
Figure 2.8.The full source code is for this sample is on the CD in the file
Multicasting.cs.
Figure 2.8 Relevant Portions of the Multicasting.cs Program Listing
class EmployeeQueueMonitor
{
// Delegate signature for add employee event callback
public delegate void AddEventCallback( string FirstName,
string LastName, string MiddleName, string SSN );
// Instance of the delegate
private AddEventCallback m_addEventCallback;
private EmployeeQueueLogger m_logger;
public EmployeeQueueMonitor( Employees employees )
{
http://www.syngress.com
Continued
76 Chapter 2 • Introducing C# Programming
m_employees = employees;
m_lengthQueue = 4;
m_logger = new EmployeeQueueLogger( “log.txt” );
// Register the methods that the delegate will invoke when an
// add employee message is read from the message queue
m_addEventCallback =
new AddEventCallback( this.addEmployee );
m_addEventCallback +=
new AddEventCallback( m_logger.logAddRequest );
}
// Drain the queue.
public void start()
{
if ( m_employees == null )
return;
for ( int i = 0; i < m_lengthQueue; i++ )
{
string FirstName = m_msgQueue[i,0];
string MiddleName = m_msgQueue[i,1];
string LastName = m_msgQueue[i,2];
string SSN = m_msgQueue[i,3];
Console.WriteLine( “Invoking delegate” );
// Invoke the delegate passing the data associated with
// adding a new employee resulting in the subscribed
// callbacks methods being executed, namely
// Employees.this.addEmployee()
http://www.syngress.com
Figure 2.8 Continued
Continued
Introducing C# Programming • Chapter 2 77
// and EmployeeQueueLogger.logAddRequest()
m_addEventCallback( FirstName, LastName, MiddleName,
SSN );
}
}
// Called by delegate whenever a new add employee message
// appears in the message queue. Notice the signature matches
// that requried by AddEventCallback
public void addEmployee( string FirstName, string MiddleName,
string LastName, string SSN )
{
Console.WriteLine( “In delegate, adding employee\r\n” );
int index = m_employees.Length;
m_employees[index] = new Employee ( FirstName, MiddleName,
LastName, SSN );
}
}
/// <summary>
/// Writes add employee events to a log file.
/// </summary>
class EmployeeQueueLogger
{
string m_fileName;
public EmployeeQueueLogger( string fileName )
{
m_fileName = fileName;
}
// Called by delegate whenever a new add employee message
http://www.syngress.com
Figure 2.8 Continued
Continued
78 Chapter 2 • Introducing C# Programming
// appears in the message queue. Notice the signature matches
// that requried by AddEventCallback
public void logAddRequest( string FirstName, string LastName,
string MiddleName, string SSN )
{
string name = FirstName + ” ” + MiddleName + ” ” + LastName;
FileStream stream = new FileStream( m_fileName,
FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter writer = new StreamWriter( stream );
writer.BaseStream.Seek( 0, SeekOrigin.End );
writer.Write(“{0} {1} \n”, DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
writer.Write( “Adding employee – Name: {0}, SSN: {1}”,
name, SSN );
writer.Write(“\n————————————\n\n”);
writer.Flush();
writer.Close();
}
}
A new class, EmployeeQueueLogger, has been added. It has a method
logAddRequest, which logs requests to add employees to a log file.The important
thing to note is that the logAddRequest method has a signature that matches the
AddEventCallback delegate signature. An instance of the logger is created in the
constructor of EmployeeQueueMonitor.The code that wires up the delegates is also
in the constructor and is shown here:
m_logger = new EmployeeQueueLogger( “log.txt” );
m_addEventCallback = new AddEventCallback( this.addEmployee );
m_addEventCallback += new AddEventCallback(
m_logger.logAddRequest );
http://www.syngress.com
Figure 2.8 Continued
Introducing C# Programming • Chapter 2 79
First, a new logger instance is created. Next, the delegate is initialized with a
first callback function to the addEmployee method of EmployeeQueueMonitor.
Finally, a second callback is added to the delegate, which will invoke the
logAddRequest of the EmployeeQueueLogger class. Notice that the plus sign is used
to add the second callback to the delegate.The plus sign (addition operator) has
been overloaded in the System.Delegate class of the .NET Framework to call the
Combine method of that class.The Combine method adds the callback to the list of
methods the delegate maintains.The minus sign (subtraction operator) is also
overloaded to call the Remove method, which removes a callback from the list of
methods the delegate maintains.The rest of the source code remains unchanged.
When the delegate is invoked in the start method of EmployeeQueueMonitor, both
EmployeeQueueMonitor.addEmployee and EmployeeQueueLogger.logAddRequest are
executed.
Events
The event model is often referred to as the publish/subscribe model or the listener
pattern.The idea behind the event model is that a class publishes the events that it
can raise. Consumers of the class object subscribe to the events they are interested
in.When the event occurs, the object that monitors the event notifies all subscribers
that the event has been raised.The subscribers then take some action.
The event model is often used in GUI programs. Handlers are set up for
common events, such as pressing a button.When the button press event occurs,
all subscribers registered for the button press event are invoked.The .NET
Framework uses the event model and in particular the System.Event delegate for
Windows Forms–based applications.
The .NET Framework supplies a built in delegate of type System.Event.The
idea of events in the .NET Framework is to supply a single signature for the delegate
regardless of the data that is passed to the subscribed callback. One of the
arguments for the Event delegate is an object derived from the .NET Framework
class System.EventArgs, which contains the data the callback needs.You declare a
class derived from System.EventArgs with the data your callback needs.When the
event takes place, you instantiate your derived EventArgs object and invoke the
event. Callback functions subscribed to the event are called passing the object
derived from EventArgs. Changes to the multicast delegate code sample that
implement events are shown in Figure 2.9.The full source code for this sample is
on the CD in the file Events.cs.
http://www.syngress.com
80 Chapter 2 • Introducing C# Programming
Figure 2.9 Relevant Portions of the Events.cs Program Listing
/// <summary>
/// Defines the data that will be passed from the event delegate to
/// the callback method when the event is raised
/// </summary>
class AddEmployeEventArgs : EventArgs
{
string m_FirstName;
string m_LastName;
string m_MiddleName;
string m_SSN;
public AddEmployeEventArgs( string FirstName,
string LastName, string MiddleName, string SSN )
{
m_FirstName = FirstName;
m_LastName = LastName;
m_MiddleName = MiddleName;
m_SSN = SSN;
}
// Event argument properties contain the data to pass to the
// callback methods subscribed to the event.
public string FirstName { get { return m_FirstName; } }
public string LastName { get { return m_LastName; } }
public string MiddleName {get { return m_MiddleName; } }
public string SSN { get { return m_SSN; } }
}
/// <summary>
/// Simulates monitoring a message queue. When a message appears
/// the event is raised and methods subscribed to the event
// are invoked.
/// </summary>
http://www.syngress.com
Continued
Introducing C# Programming • Chapter 2 81
class EmployeeQueueMonitor
{
// Event signature for AddEmployeeEvent
public delegate void AddEmployeeEvent( object sender,
AddEmployeEventArgs e );
// Instance of the AddEmployeeEvent
public event AddEmployeeEvent OnAddEmployee;
private EmployeeQueueLogger m_logger;
private Employees m_employees;
private int m_lengthQueue;
private string[, ] m_msgQueue =
{
{“Timothy”, “Arthur”, “Tucker”, “555-55-5555”},
{“Sally”, “Bess”, “Jones”, “666-66-6666” },
{“Jeff”, “Michael”, “Simms”, “777-77-7777”},
{“Janice”, “Anne”, “Best”, “888-88-8888” }
};
public EmployeeQueueMonitor( Employees employees )
{
m_employees = employees;
m_lengthQueue = 4;
m_logger = new EmployeeQueueLogger( “log.txt” );
// Register the methods that the Event will invoke when an add
// employee message is read from the message queue
OnAddEmployee +=
new AddEmployeeEvent( this.addEmployee );
http://www.syngress.com
Figure 2.9 Continued
Continued
82 Chapter 2 • Introducing C# Programming
OnAddEmployee +=
new AddEmployeeEvent( m_logger.logAddRequest );
}
// Drain the queue.
public void start()
{
if ( m_employees == null )
return;
for ( int i = 0; i < m_lengthQueue; i++ )
{
// Pop an add employee request off the queue
string FirstName = m_msgQueue[i,0];
string MiddleName = m_msgQueue[i,1];
string LastName = m_msgQueue[i,2];
string SSN = m_msgQueue[i,3];
Console.WriteLine( “Invoking delegate” );
// Create the event arguments to pass to the methods
// subscribed to the event and then invoke event resulting
// in the callbacks methods being executed, namely
// Employees.this.addEmployee() and
// EmployeeQueueLogger.logAddRequest()
AddEmployeEventArgs args = new AddEmployeEventArgs( FirstName,
LastName, MiddleName, SSN );
OnAddEmployee( this, args );
}
}
public void stop()
{
http://www.syngress.com
Figure 2.9 Continued
Continued
Introducing C# Programming • Chapter 2 83
// In a real communications program you would shut down
// gracefully.
}
// Called by event whenever a new add employee message appears
// in the message queue. Notice the signature matches that required
// by System.Event
public void addEmployee( object sender, AddEmployeEventArgs e )
{
Console.WriteLine( “In delegate, adding employee\r\n” );
int index = m_employees.Length;
m_employees[index] = new Employee ( e.FirstName, e.MiddleName,
e.LastName, e.SSN );
}
}
/// <summary>
/// Writes add employee events to a log file.
/// </summary>
class EmployeeQueueLogger
{
string m_fileName;
public EmployeeQueueLogger( string fileName )
{
m_fileName = fileName;
}
// Called by event whenever a new add employee message appears
// in the message queue. Notice the signature matches that required
// by System.Event
public void logAddRequest( object sender, AddEmployeEventArgs e )
http://www.syngress.com
Figure 2.9 Continued
Continued
84 Chapter 2 • Introducing C# Programming
{
string name = e.FirstName + ” ” + e.MiddleName + ” ” +
e.LastName;
FileStream stream = new FileStream( m_fileName,
FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter writer = new StreamWriter( stream );
writer.BaseStream.Seek( 0, SeekOrigin.End );
writer.Write(“{0} {1} \n”, DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
writer.Write( “Adding employee – Name: {0}, SSN: {1}”,
name, e.SSN );
writer.Write(“\n————————————\n\n”);
writer.Flush();
writer.Close();
}
}
A new class, AddEmployeEventArgs, has been added. It contains the information
that will be passed to callback methods subscribed to the event. Notice the
data members of the AddEmployeEventArgs class are the same as the signature for
the AddEventCallback delegate in our previous sample. Instead of invoking the
callback with individual arguments, when using events, you pass a class object,
which contains the arguments instead.
Just as with the delegates samples, we declare the signature and create a
member variable for the delegate in EmployeeQueueMonitor class.The only difference
is that the signature matches the signature necessary for events.The first
parameter is the object that raised the event, and the second is the object instance
that contains the arguments passed to subscribed callback methods.This is shown
here:
public delegate void AddEmployeeEvent( object sender,
http://www.syngress.com
Figure 2.9 Continued
Introducing C# Programming • Chapter 2 85
AddEmployeEventArgs e );
public event AddEmployeeEvent OnAddEmployee;
In the constructor of the class, we subscribe the callback methods to the
event as shown here:
OnAddEmployee +=
new AddEmployeeEvent( this.addEmployee );
OnAddEmployee +=
new AddEmployeeEvent( m_logger.logAddRequest );
The callback methods have the correct signature for event callbacks. Here are
the callback method’s signatures:
public void addEmployee( object sender, AddEmployeEventArgs e )
public void logAddRequest( object sender, AddEmployeEventArgs e )
When an add employee message is popped off the queue in the start method
of EmployeeQueueMonitor, an instance of the AddEmployeeEventArgs is created and
the event is invoked. Here is the code that accomplishes this:
AddEmployeEventArgs args = new AddEmployeEventArgs( FirstName,
LastName, MiddleName, SSN );
OnAddEmployee( this, args );
As you can see, using events instead of delegates is really just a syntactic difference.
The code is nearly identical.The main benefit is that you don’t have a
different delegate signature for every delegate you create based on the data that is
passed to subscribed callbacks. Instead, the standard event delegate signature will
suffice.
Using Exception Handling
If you look through the .NET Framework SDK documentation, you won’t find
an error code returned from any method calls in the library. Instead, the
Framework uses exceptions to indicate errors that occur.To illustrate exceptions,
consider the code snippet in Figure 2.10 that builds upon the Enumerable sample
from the Indexers section of this chapter.The complete sample is included on the
CD in the file Exceptions.cs.
http://www.syngress.com
86 Chapter 2 • Introducing C# Programming
Figure 2.10 Relevant Portions of the Exceptions.cs Program Listing
using System;
using System.Collections;
/// <summary>
/// Contains the program entry point for the Exceptions Sample.
/// </summary>
class ExceptionsSample
{
static void Main( string[] args )
{
try
{
// Create a container to hold employees
Employees employees = new Employees(4);
// Add some employees
addOneEmployee ( employees, “Timothy”, “Arthur”,
“Tucker”, “555-55-5555” );
addOneEmployee ( employees, “Sally”, “Bess”,
“Jones”, null );
addOneEmployee ( employees, “Jeff”, “Michael”,
“Simms”, “777-77-7777” );
addOneEmployee ( employees, “Janice”, “Anne”,
“Best”, “9888-88-88889″ );
// Display the employee list on screen
foreach ( Employee employee in employees )
{
string name = employee.FirstName + ” ” +
employee.MiddleName + ” ” + employee.LastName;
http://www.syngress.com
Continued
Introducing C# Programming • Chapter 2 87
string ssn = employee.SSN;
Console.WriteLine( “Name: {0}, SSN: {1}”, name, ssn );
}
}
catch ( Exception exception )
{
// Display any errors on screen
Console.WriteLine( exception.Message );
}
}
// Helper method to add an employee to the list
static void addOneEmployee( Employees employees,
string FirstName, string MiddleName, string LastName,
string SSN )
{
bool addedEmployee = false;
try
{
Console.WriteLine( “Adding an employee” );
// SSN cannot be NULL, throw exception
if ( SSN == null )
throw new ArgumentNullException( “SSN is null!” );
// SSN length must be 11, throw exception
if ( SSN.Length != 11 )
throw new ArgumentOutOfRangeException(
“SSN length invalid!” );
http://www.syngress.com
Figure 2.10 Continued
Continued
88 Chapter 2 • Introducing C# Programming
// Add the employee
employees[employees.Length] = new Employee ( FirstName,
MiddleName, LastName, SSN );
addedEmployee = true;
}
catch ( ArgumentOutOfRangeException exception )
{
Console.WriteLine( “We caught ArgumentOutOfRangeException” );
Console.WriteLine( exception.Message );
}
catch ( ArgumentNullException exception )
{
Console.WriteLine( “We caught ArgumentNullException” );
Console.WriteLine( exception.Message );
}
catch ( Exception exception )
{
Console.WriteLine( “We caught a base exception” );
Console.WriteLine( exception.Message );
}
catch
{
Console.WriteLine( “We caught an unknown exception” );
Console.WriteLine( “Unknown exception caught!” );
}
finally
{
if ( addedEmployee == true )
Console.WriteLine( “Add was successful\r\n” );
else
Console.WriteLine( “Add failed\r\n” );
}
http://www.syngress.com
Figure 2.10 Continued
Continued
Introducing C# Programming • Chapter 2 89
}
}
Using the try Block
Code that may throw an exception is placed inside a try block. In this example,
the addOneEmployee method has a try block surrounding the code that will add a
new employee to the list of employees. If an exception is thrown in a try block,
control is passed to the catch block.
Using the catch Block
The catch block is where you handle exceptions that are thrown.The first exception
type that matches the exception thrown has control passed to its block of source
code. In our example, if SSN length is not 11, an ArgumentOutOfRangeException
exception is thrown.This results in execution of the catch block of
ArgumentOutOfRangeException.
You should order your catch blocks so that the most general exceptions come
last. If you put the general exceptions at the top of your catch blocks, they will
always catch the exception.This can cause problems if you need to do special
processing based on the exception type. Because all exceptions in the .NET
Framework derive from System.Exception, the last two catch blocks in our sample
are equivalent.They will catch any exceptions that are not caught by a more specific
exception above.They are both shown in Figure 2.10 for completeness.
Using the finally Block
The finally block is the last part of a try-catch-finally block for handling exceptions.
The finally block is always executed regardless of whether an exception was
thrown.Typically, finally blocks include cleanup code, such as closing files or
databases.You do not have to include a finally block if you have no need to do
special processing. In our example, the finally block prints a different message
based on whether an exception was thrown.
Using the throw Statement
You can throw exceptions to indicate errors that occur in your programs by using
the throw keyword.To throw an exception, you create a new instance of a
http://www.syngress.com
Figure 2.10 Continued
90 Chapter 2 • Introducing C# Programming
System.Exception class that indicates the type of exception encountered. Exceptions
derived from the System.Exception class take a message, which you can set as one
of the parameters.The code that catches the exception can retrieve the message
for display or logging purposes. In the previous sample code, an exception is
thrown when SSN is null or is not eleven characters in length. Here is the relevant
code:
// SSN cannot be NULL, throw exception
if ( SSN == null )
throw new ArgumentNullException( “SSN is null!” );
// SSN length must be 11, throw exception
if ( SSN.Length != 11 )
throw new ArgumentOutOfRangeException( “SSN length invalid!” );
The CLR will also throw exceptions if it encounters errors. For instance, it
will throw an error if a divide-by-zero operation is attempted. If an exception is
thrown, and the method it is thrown in doesn’t contain a catch block, the CLR
will look for a catch block in the calling method, if one exists. It will keep looking
for a catch block up the call chain until it finds one that matches or until it has
reached the top-level method call. If it still doesn’t find a match, the system will
handle the exception.This typically results in an error message being displayed
and the program being aborted.You need to understand that even though you
may not throw any exceptions, the runtime may. So, if you have a program that
needs to keep running indefinitely, you should catch exceptions somewhere in
the call chain and then continue executing your application.
All of the exceptions in the sample are ones defined by the .NET
Framework.You can define your own exceptions as well.Typically, you just need
to derive your own exception from the System.Exception class provided by the
Framework and implement any behavior specific to your custom exception.
Before you can do that however, you need to understand inheritance in C#—we
cover that next.
Understanding Inheritance
Inheritance and polymorphism are the two characteristics that make object-oriented
programming languages so powerful. Many books, articles, and Web sites
have been written explaining the subjects with flowing prose.We distill it down
to a couple of short sentences. Inheritance means you can create a new type of
http://www.syngress.com
Introducing C# Programming • Chapter 2 91
object B that inherits all of the characteristics of an existing object A.
Polymorphism means that this new object B can choose to inherit some characteristics
and supply its own implementation for others.
Just in case it needs a bit more explanation, here is an example.Throughout
this chapter, you have seen examples that use the Employee class. An employee in
our case has a first name, middle name, last name, and SSN.What happens when
we add in wage information? Now we have two different types of employees:
salaried and hourly.They both still have the original characteristics of an
employee but one now has an hourly wage and the other a yearly salary.When
you need to run payroll for the employees, each type of employee’s pay is calculated
differently.
One way to solve this would be to put a flag in the Employee class indicating
hourly or salaried.Then whenever you need to do something that requires
knowledge of the type of employee, you have to check the flag and do the
appropriate thing.This works fine for our simple example, but what if there are
20 kinds of things? Suddenly, a lot of code is spent just checking what type of
thing it is before doing further processing.
Fortunately we have inheritance to help us solve this problem. Inheritance
lets you create two new types of employees—hourly and salaried—that inherit all
of the characteristics of the Employee class. Here are the declarations of the two
new classes.We get to the implementations in a moment.
class SalariedEmployee : Employee
{
}
class HourlyEmployee : Employee
{
}
The text to the right of the colon indicates the base class of the new class.
Therefore, both SalariedEmployee and HourlyEmployee each have Employee as their
base class, or you can say they are derived from Employee.This means that they
inherit all of the characteristics of the Employee class. For instance, you can instantiate
a new SalariedEmployee object and write code like this:
string LastName = salariedEmployee.LastName;
http://www.syngress.com
92 Chapter 2 • Introducing C# Programming
That solves our first problem.You now have two types of employees to
manipulate. But you still don’t have a way to calculate payroll. Derived classes can
override methods defined in the base class. So one way to solve this is to create a
new base class method named getPayroll and have both classes write their own
implementation of the method. Portions of the class implementations are shown
here to demonstrate this:
class Employee
{
virtual public double getPayroll()
{
return 0.0;
}
}
class SalariedEmployee : Employee
{
private double m_Salary;
public SalariedEmployee( double Salary )
{
m_Salary = Salary;
}
override public double getPayroll()
{
return m_Salary / 12;
}
}
class HourlyEmployee : Employee
{
private double m_HourlyRate;
private double m_HoursWorked;
public HourlyEmployee ( double HourlyRate )
http://www.syngress.com
Introducing C# Programming • Chapter 2 93
{
m_HourlyRate = HourlyRate;
}
public double HoursWorked
{
get { return m_HoursWorked; }
set { m_HoursWorked = value; }
}
override public double getPayroll()
{
return m_HoursWorked * m_HourlyRate;
}
}
Notice that all three classes have a getPayroll method.The SalariedEmployee
class calculates monthly payroll by dividing yearly salary by 12.The HourlyEmployee
class calculates payroll by multiplying pay rate by the number of hours worked.
This is exactly what we want. Each type of employee calculates payroll the appropriate
way. Notice the getPayroll method of the Employee class is prefaced with the
keyword virtual. Also notice that the SalariedEmployee and HourlyEmployee classes
are prefaced with the keyword override.The virtual keyword indicates that if a
derived class provides the same method with the same signature and is prefaced
with the override keyword, call the derived classes implementation instead of the
base classes.The best way to explain is with a simple example:
Employee employee = new Employee();
SalariedEmployee salariedEmployee = new SalariedEmployee( 600000 );
HourlyEmployee hourlyEmployee = new HourlyEmployee( 10.00 );
hourlyEmployee.HoursWorked = 10;
Console.WriteLine( employee.getPayroll() );
Console.WriteLine( salariedEmployee.getPayroll() );
Console.WriteLine( hourlyEmployee.getPayroll() );
The resulting output would be as follows:
http://www.syngress.com
94 Chapter 2 • Introducing C# Programming
0
5000
100
This is just what you would expect. Each type of employee returns the correctly
calculated payroll amount.This is polymorphism at work.We can choose to
inherit those things that are common and implement those things that aren’t in
derived classes.
Let’s take a further look at polymorphism.The true power of polymorphism
allows you to use a derived class when an object of the base class is specified.The
following code demonstrates this:
Employee employee = new Employee();
SalariedEmployee salariedEmployee = new SalariedEmployee( 600000 );
HourlyEmployee hourlyEmployee = new HourlyEmployee( 10.00 );
hourlyEmployee.HoursWorked = 10;
displayPayrollAmount( employee );
displayPayrollAmount( salariedEmployee );
displayPayrollAmount( hourlyEmployee );
public void displayPayrollAmount( Employee employee )
{
Console.WriteLine( employee.getPayroll() );
}
The resulting output would once again be as follows:
0
5000
100
Notice that the displayPayrollAmount method takes an Employee object as a
parameter. But it is passed an instance of both SalariedEmployee and
HourlyEmployee.The displayPayrollAmount method also displays the payroll amount
appropriate to the class type passed in.This is polymorphism at work.A
SalariedEmployee is an Employee, and an HourlyEmployee is an Employee as far as the
http://www.syngress.com
Introducing C# Programming • Chapter 2 95
CLR is concerned. So any method that expects an object of class type Employee
will also take an object of class types SalariedEmployee or HourlyEmployee.
There is still one odd thing about the code.The class Employee returns zero if
displayPayrollAmount is called. In truth, it doesn’t make any sense to create an
object of type Employee.All employees must be salaried employees or hourly
employees. But with the current code, nothing is stopping a programmer from
instantiating a class object of type Employee.
Fortunately, in C# you can make the Employee class an abstract class, and the
compiler will generate an error if an object of type Employee is created. Here are
the changes necessary to the enable this:
abstract class Employee
{
abstract public double getPayroll();
}
If you now try to create an instance of Employee, such as
Employee employee = new Employee();
the compiler will generate an error saying it cannot create an abstract class
Employee.
Notice that the Employee class declaration uses the keyword abstract.This indicates
to the compiler that an object of this class type can never be created.
Another change is that the getPayroll() method is also prefixed by the keyword
abstract. Notice that we supply only the signature for the method and no implementation.
The abstract keyword indicates that a derived class must implement the
method. Note the distinction between the virtual and abstract keywords applied to
a base class method.The virtual keyword says the derived class is free to implement
its own version of a method. If the derived class does not implement the
method, the base classes method will execute when called.The abstract keyword
says that the derived class must implement the method.
You can apply one other keyword to classes.The sealed keyword indicates that
the class cannot be used as a base class. Use the sealed keyword if you never want
other classes to derive from a class.
The getPayroll method shown in the examples in this section could also be
written as a property. Let’s take a look at how the code would change to support
this.The full source code for the three classes is shown here (the code is also
included on the CD in a sample program in the file Payroll.cs):
http://www.syngress.com
96 Chapter 2 • Introducing C# Programming
/// <summary>
/// Base class for an employee. Note that this is an abstract class
/// and therefore cannot be instantiated.
/// </summary>
abstract class Employee
{
private int m_ID;
private string m_firstName;
private string m_middleName;
private string m_lastName;
private string m_SSN;
public Employee( int ID, string FirstName, string LastName,
string MiddleName, string SSN )
{
m_ID = ID;
m_firstName = FirstName;
m_middleName = MiddleName;
m_lastName = LastName;
m_SSN = SSN;
}
abstract public double Payroll
{
get;
}
public int ID
{
get { return m_ID; }
}
public string FirstName
{
http://www.syngress.com
Introducing C# Programming • Chapter 2 97
get { return m_firstName; }
set { m_firstName = value; }
}
public string MiddleName
{
get { return m_middleName; }
set { m_middleName = value; }
}
public string LastName
{
get { return m_lastName; }
set { m_lastName = value; }
}
public string SSN
{
get { return m_SSN; }
set { m_SSN = value; }
}
}
/// <summary>
/// Salaried employee class. Implements the abstract method Payroll
/// defined in the base class.
/// </summary>
class SalariedEmployee : Employee
{
private double m_Salary;
public SalariedEmployee( int ID, string FirstName,
string LastName, string MiddleName,string SSN,
double Salary ) :
http://www.syngress.com
98 Chapter 2 • Introducing C# Programming
base( ID, FirstName, LastName, MiddleName, SSN )
{
m_Salary = Salary;
}
override public double Payroll
{
get { return m_Salary / 12; }
}
}
/// <summary>
/// Hourly employee class. Implements the abstract method Payroll
/// defined in the base class. Also implements some class
/// specific methods
/// </summary>
class HourlyEmployee : Employee
{
private double m_HourlyRate;
private double m_HoursWorked;
public HourlyEmployee( int ID, string FirstName,
string LastName, string MiddleName, string SSN,
double HourlyRate ):
base( ID, FirstName, LastName, MiddleName, SSN )
{
m_HourlyRate = HourlyRate;
m_HoursWorked = 0;
}
public double HoursWorked
{
get { return m_HoursWorked; }
set { m_HoursWorked = value; }
http://www.syngress.com
Introducing C# Programming • Chapter 2 99
}
override public double Payroll
{
get { return m_HoursWorked * m_HourlyRate; }
}
}
The Employee class now has a Payroll property that is declared as abstract:
abstract public double Payroll
{
get;
}
Notice that the get method has no implementation.The SalariedEmployee and
HourlyEmployee classes supply the following implementations of the property:
// SalariedEmployee implementation
override public double Payroll
{
get { return m_Salary / 12; }
}
// HourlyEmployee implementation
override public double Payroll
{
get { return m_HoursWorked * m_HourlyRate; }
}
The payroll sample program included on the CD in the file payroll.cs incorporates
most of the concepts we have covered in this chapter. It extends the
employee message queue we have seen throughout this chapter. In particular, it
highlights the power and practical use of inheritance and polymorphism in C#.
The sample extends the messages received in the message queue to include messages
that indicate hours worked for hourly employees as well as supporting the
add new employee message. After processing all of the messages in the queue, the
program lists each employee and the amount of their paycheck for the month.
http://www.syngress.com
100 Chapter 2 • Introducing C# Programming
Along with the Employee, SalariedEmployee, and HourlyEmployee classes just
shown, it illustrates inheritance and polymorphism in a few other classes. As mentioned
earlier in the chapter when discussing exceptions, you can derive custom
exceptions.We have derived a new custom exception from System.Exception that
is thrown when an attempt is made to read past the end of the message queue.
Here is the declaration of the class:
/// <summary>
/// Custom exception which is thrown when an attempt is made to
/// read past the end of the queue.
/// </summary>
class EndOfMessageQueueException : Exception
{
public EndOfMessageQueueException( string Message ) :
base( Message )
{
}
}
The sample also derives the Employees message queue class directly from
ArrayList instead of including an ArrayList as a member of the class. Because
ArrayList already supports IEnumerable, there is little we need to implement ourselves.
Here is the Employees class code:
/// <summary>
/// Container class for employees derived from ArrayList
/// </summary>
class Employees : ArrayList
{
public int Length
{
get { return this.Count; }
}
}
We’ve also created a new Logger base class and have derived the EmployeeLogger
class and a new ErrorLogger class from it.You can see from the following code that
http://www.syngress.com
Introducing C# Programming • Chapter 2 101
the Logger class performs the actual writing of text to a disk file while the other
two classes implement methods specific to the type of logging they perform:
/// <summary>
/// General logging class to a file. Base class for other more
/// specific loggers.
/// </summary>
class Logger
{
string m_fileName;
public Logger( string fileName )
{
m_fileName = fileName;
}
protected void log( string text )
{
FileStream stream = new FileStream( m_fileName,
FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter writer = new StreamWriter( stream );
writer.BaseStream.Seek( 0, SeekOrigin.End );
writer.Write(“{0} {1} \n”, DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
writer.Write( text );
writer.Write(“\n————————————\n\n”);
writer.Flush();
writer.Close();
}
}
/// <summary>
http://www.syngress.com
102 Chapter 2 • Introducing C# Programming
/// Writes add employee events to a log file.
/// </summary>
class EmployeeQueueLogger : Logger
{
public EmployeeQueueLogger( string filename ) :
base( filename )
{
}
public void logAddRequest( object sender,
AddEmployeEventArgs e )
{
string name = e.FirstName + ” ” + e.MiddleName + ” ” +
e.LastName;
string text = “Adding Employee\n”;
text += “EmployeeID: ” + e.ID.ToString();
text += “, Name: ” + name;
log( text );
}
public void logHoursWorked( object sender,
HoursWorkedEventArgs e )
{
string text = “Adding Hours Worked\n”;
text += “EmployeeID: ” + e.ID.ToString();
text += “, Hours Worked: ” + e.Hours.ToString();
log( text );
}
}
/// <summary>
/// Logs error meessage to a log file.
/// </summary>
http://www.syngress.com
Introducing C# Programming • Chapter 2 103
class ErrorLogger : Logger
{
public ErrorLogger( string filename ) :
base( filename )
{
}
public void logError( Exception exception )
{
log( exception.Message );
log( exception.StackTrace );
}
}
The payroll sample should provide you with several good examples of inheritance
and polymorphism.With the other samples you have seen and the concepts
discussed in this chapter, you should have a solid foundation to start creating your
own C# programs.
http://www.syngress.com
104 Chapter 2 • Introducing C# Programming
Summary
C# is a modern object-oriented language.The Microsoft.NET Framework software
development kit (SDK) and a text editor are all you need to get started programming
in C#. In conjunction with the Common Language Runtime (CLR),
you can develop console applications, graphical user interface (GUI) applications,
and Web-based applications using C# and the .NET Framework.
C# includes all of the features you would expect in a modern object-oriented
language. It supports the notion of classes and the object-oriented nature of
classes, including inheritance and polymorphism. Classes are one of two data
types in C#, reference types, which are allocated on the heap.Value types, which
are allocated on the stack, are also supported including the usual primitive
numeric and string data types.The looping and conditional statements available
in most modern languages are part of C#, including if-else statements, switch statements,
for loops, while loops, and do-while loops.
C# also includes advanced features such a properties and indexers which provide
intuitive syntax for accessing data members while promoting data hiding.
Delegates and events allow you to define events in your programs and set up callbacks
to subscribed class methods when the event is raised. Exception handling is
supported, which moves the clutter of error-checking outside of your main processing,
resulting in clearly defined business logic.
Solutions Fast Track
Getting Started
􀀻 C# is a modern object-oriented language.
􀀻 The Microsoft .NET Framework software development kit (SDK) and a
text editor are all you need to begin programming in C#.The Windows
family of operating systems supplies several adequate editors including
Notepad.
􀀻 Microsoft sells a feature rich development environment for developing
.NET applications:Visual Studio .NET.
􀀻 You can use C# in development of console applications, graphical user
interface (GUI) applications, and Web-based applications.
http://www.syngress.com
Introducing C# Programming • Chapter 2 105
Creating Your First C# Program
􀀻 A command-line compiler, csc.exe, is supplied with the .NET
Framework SDK.You use it to compile console applications.
􀀻 Every C# program must have a static Main method, which is the entry
point of the program. C# does not support global functions, so Main
must be a class method.
􀀻 You can use namespaces to group related classes together.The using
keyword allows you to reference a class object without prefixing it with
the full namespace.
􀀻 You can also use source code control comments to document your
program classes and methods for other programmers that may call
your code.
Introducing Data Types
􀀻 C# supports two data types: value types and reference types.
􀀻 Value types are allocated on the stack and include primitive types such as
numerics, Booleans, characters, and strings. Structures and Enums are
also value types.
􀀻 Reference types are allocated on the stack and are typically instances of
class objects.
􀀻 C# does not support pointers.
Explaining Control Structures
􀀻 C# supports the control structures you normally find in a modern
language: if-else conditional, for loop, do while loop, while loop, and the
switch statement.
􀀻 The test expression in an if-else statement must evaluate to a Boolean
value. Numeric test expressions are not supported as they are in C/C++.
􀀻 The switch statement does not support falling through to the next case
statement as it does in C/C++.
http://www.syngress.com
106 Chapter 2 • Introducing C# Programming
Understanding Properties and Indexers
􀀻 Properties are method calls that appear to be member variables.
Properties hide the underlying data type allowing you to change the
implementation without the need to change code that uses the property.
􀀻 Indexers allow you to use array syntax to access a list of objects
contained inside another class. Like properties, indexers hide the
underlying implementation allowing you to change it without the need
to change code that uses the indexer.
􀀻 Implementing indexers that support the IEnumerator interface allows you
to use the for-each looping syntax to access the list objects of the indexer.
Using Delegates and Events
􀀻 You can use delegates to call subscribed method calls when a triggering
event happens. Delegates are similar to callback functions in Microsoft
Windows programs or function pointers in C++.
􀀻 A single cast delegate invokes a single subscribed method.A multicast
delegate invokes more than one subscribed method.
􀀻 Events are a type of a delegate that is provided for you in the .NET
Framework. Methods subscribing to an event always provide the same
set of arguments.This differs from delegates in that each delegate
provides a unique signature for its subscribed methods.
Using Exception Handling
􀀻 You use exception handling return and trap errors in C# programs.
􀀻 Exception handling uses the try-catch-finally syntax. Try blocks define the
code that may throw exceptions. One or more catch blocks trap and
handle exceptions of various types.The finally block is always executed
regardless of whether an exception was thrown and is typically used to
free resources.
􀀻 Because the .NET Common Language Runtime (CLR) may throw
exceptions even if you don’t, you should catch exceptions somewhere
near the top of your call chain to be sure you program will continue
running.
http://www.syngress.com
Introducing C# Programming • Chapter 2 107
Understanding Inheritance
􀀻 C# is an object-oriented language and as such supports inheritance and
polymorphism. Inheritance means you can create a new type of object B
that inherits all of the characteristics of an existing object A.
Polymorphism means that this new object B can choose to inherit some
characteristics and supply its own implementation for others.
􀀻 The virtual keyword is used to define methods in a base class that a
derived class supplies its own implementation of.The override keyword is
used by a method with the same signature in the derived class to provide
a different implementation than the base class.
􀀻 The abstract keyword applied to a class definition indicates that the class
is abstract and cannot be instantiated.
􀀻 The abstract keyword applied to a method call of an abstract class means
the method has no implementation in the base class and must be
implemented in the derived class.
Q: Does C# support friend classes like in C++?
A: No. About the closest you can come is using the internal keyword, which
allows access within the current assembly. But, there is no way to specify specific
a class that knows about another class’s private implementation.
Q: I thought destructors didn’t exist in C#.Why can I declare one?
A: You can declare a method that looks like a C++ destructor such as ~Employee().
But it really becomes a shorthand way to define System.Object.Finalize.The
Finalize method is not the same as a C++ destructor.A short explanation is
that C++ destructors are called when the call stack unwinds.The Finalize
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.
108 Chapter 2 • Introducing C# Programming
method will be called when the garbage collector of the CLR is ready to
destroy the object.
Q: How do I convert a numeric type to a string?
A: Call Sytem.Object.ToString(). For example, if the variable count is an integer,
write the following:
string text = count.ToString();
Q: How do I call a member method and pass a primitive type (Value Type) by
reference?
A: Use the ref keyword when declaring the parameter of the method, for
example:
public bool GetValue( ref int returnValue );
This will pass the numeric by reference.You can modify the value of
returnValue within the body of GetValue and it will persist when the method
call returns.
Q: Can I call a Win32 API function from a C# program?
A: Yes, you can.The .NET Framework provides a set of services called Platform
Invoke, also known as PInvoke. Refer to the .NET Framework documentation
for examples of using PInvoke services.
http://www.syngress.com
Visual Studio.NET
IDE
Solutions in this chapter:
 Introducing Visual Studio.NET
 Components of VS.NET
 Features of VS.NET
 Customizing the IDE
 Creating a Project
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 3
109
110 Chapter 3 • Visual Studio.NET IDE
Introduction
In addition to the powerful .NET platform, Microsoft has introduced a new version
of its Visual Studio Suite, called Visual Studio.NET (VS.NET). Even in its
Beta stages,VS.NET provides the developer with powerful visual tools for developing
all kinds of applications on the .NET platform.
VS.NET helps in the speedy creation and deployment of applications coded
in any of the managed languages, including C#.This chapter gets you familiar
with the new features of VS.NET and teaches you to customize it according to
your needs.We cover the many new features of VS.NET, including the .NET
Framework,Web Services, XML support, and the Integrated Development
Environment (IDE).
Also, we cover the XML editor, which has tag completion for Extensible
Stylesheet Language Transformations (XSLTs).We go over the IntelliSense feature
and how it is used in the different windows. Finally, we cover how to customize
your settings within the IDE.
VS.NET is a complete development environment.The components stay the
same regardless of language, making it very easy to switch projects and languages
and have the same features in the same place. Also, with the expanded IntelliSense
with tag completion, routine code writing is faster.
Introducing Visual Studio.NET
The Start pages deliver a great many resources for the development environment.
The Start page is the default home page for the browser inside of the IDE.You
can tap all aspects of the IDE from these pages.We go over the three most useful
Start pages, starting out with the “What’s New” page and the “My Profile” page,
and ending with the “Get Started” page.We show you what is new with VS.NET,
set up your profile, and get started using the tool.
Let’s open up VS.NET and take a look at the first of the Start pages (see
Figure 3.1).
You can filter the “What’s New” Start page to whatever topic you are interested
in—we have chosen to filter by .NET Framework. All content in the
“What’s New” Start page will be rendered based on the filter, so you can save
some time by not looking up new features for VB, for example.You can also
select Check availability of Visual Studio.NET service packs from this
Start page and check to see if you need the latest download for VS.NET. Let’s
look at the “My Profile” page next, shown in Figure 3.2.
http://www.syngress.com
http://www.syngress.com
The “My Profile” section of the Start page lets you create your own (custom)
profile or select from any of the options listed. If you happen to come from a VB
background, using the VB profile would be beneficial so that you could be
familiar with the tools from VS 6. Likewise, a C++ or Interdev user from VS 6
will benefit from the same environment.This will help you to learn the tool by
showing a familiar layout.You can also select to have only external help, which
will open the Help documentation in a new window outside of the IDE.You can
filter the Help topics; in our case, we’ve selected .NET Framework SDK in
Visual Studio.NET IDE • Chapter 3 111
Figure 3.1 VS.NET Start Page: What’s New
Figure 3.2 VS.NET Start Page: My Profile
112 Chapter 3 • Visual Studio.NET IDE
the What’s New section Start page.You can also select the window layout that
you want to use.You then can select the Get Started Start page, shown in
Figure 3.3.
Here you can select projects you worked on previously, and you can also see
where they are located on the machine by dragging the mouse over the name of
the file.This is a nice feature that you can use where you have two projects
named the same but at different locations.
The Start page is the default page for the Web browser window in VS.NET,
so if you close it and want to get it back, simply hit the home icon on the Web
toolbar and the page will load in the design window.
Components of VS.NET
The Visual Studio.NET IDE is made up of many components that interact with
one another.You can use each of them separately or at the same time.This feature
lets the user decide which set of components he wishes to use. All of the components
together create an intuitive graphical user interface (GUI).
Design Window
The design window is used when you are graphically creating an application using
the Toolbox to drag and drop objects onto the window. Much like the code
http://www.syngress.com
Figure 3.3 VS.NET Start Page: Get Started
Visual Studio.NET IDE • Chapter 3 113
window and browser, the design window cannot be docked or set to Auto Hide.
You can split the design view or have tab groups added to it. Splitting the window
helps when you need to compare code from two separate files (see Figure 3.4).
Here you can see windows for both design and code.This is a C# Web application,
but the functionality is the same for any project.
Code Window
As we mentioned, the code window is much like the design window.There is no
toolbox functionality within the code view, however—you cannot drag and drop
objects from the toolbox into the code view. If you create objects in the code
view and then switch back to the design view, the objects that you added would
persist in design view. Again you cannot dock this window nor allow it to float.
You can, however, split it and add new tab groups to the display. Figure 3.5 shows
the code window split and a tab vertical tab order added.
If you look at Figure 3.5 a little more closely, you can see a collapsible menu
tree on the left-hand side.This is created every time you create a class or function,
enabling you to collapse each section independently to save space for viewing
http://www.syngress.com
Figure 3.4 Split Window View
114 Chapter 3 • Visual Studio.NET IDE
other code present within the window. Note that you must have the default
option Outlining Mode checked for this to work. If you want to have line numbers
show for your code, you will have to choose Tools | Options. In the
Options dialog box, select Text/Editors. Select C# and then choose the option
to have line numbers added.
You may also define your own regions of code that may be collapsed.To do
this simply add the following code to your class or function you want to make
into a region:
#region
///Comments and code
#endregion
Server Explorer
The Server Explorer is by far one of the best features in VS.NET. From this
window you can connect to a server on the network and have full access to that
server or servers.You can also link to any database servers on the network. Let’s
see how to do that. Click the Connect to Database icon in the title bar of the
window (see Figure 3.6).You will be prompted to give all information required
for a Universal Data Link (UDL).
Fill out the UDL Wizard and test the connection. After this is done, you can
access everything within that database that the user has rights to. Let’s take a look
at that in Figure 3.7.
http://www.syngress.com
Figure 3.5 Code View
Visual Studio.NET IDE • Chapter 3 115
http://www.syngress.com
Figure 3.6 Add Database to Server Explorer
Figure 3.7 Expanded Database View
116 Chapter 3 • Visual Studio.NET IDE
You can now click on any object within the Server Explorer and edit it
within VS.NET.This is a timesaver from having to have both the Query Analyzer
and VS.NET open at the same time and going back and forth between the two
just to switch a data type of one stored procedure input parameter.
Toolbox
The Toolbox, shown in Figure 3.8, includes Data, Components,Web Forms, and
Window Forms tabs. As stated earlier in the chapter, you can use the Toolbox
with the Design View window.You can drag and drop each component or control
onto the design window. Also, you may customize the Toolbox by adding in
your own code fragments and renaming them to something meaningful.
To do this, simply right-click on the Toolbox and select Add Tab. Give it a
name that is different than the existing tabs, and you are ready to add your own
tools.To add a new tool, highlight a block of code that you want to make into a
tool and drag it onto the Toolbox label you just created.
The Clipboard Ring stores all the items that you have copied in code view
automatically.You can then double-click these and add them to the source code.
http://www.syngress.com
Figure 3.8 The Toolbox Window
Visual Studio.NET IDE • Chapter 3 117
Docking Windows
One of the new features for VS.NET is that you can dock or expand or collapse
all the windows within the IDE.To add windows to your IDE, navigate to the
standard toolbar and select View; here you can select all the windows that you
want to have immediately available in your environment. One drawback to this is
that you will not have much room left to work in if you select a lot of windows
to show, but the Auto Hide feature of each window makes them slide off the
screen and embed in the side when not needed.This enables you to have maximum
code view but still have all windows present.To see a window that has
Auto Hide enabled, simply position your mouse over the window icon on either
side of the IDE.You can dock each window into place by clicking on the pin or
by navigating to the standard toolbar and choosing the Window menu option.
Once a window is docked, it is there permanently; you can, however, make the
window float by selecting Window | Floating (see Figure 3.9).
Properties Explorer
The Properties Explorer is much as it was in VS 6 and the Visual Basic IDE and
Visual Interdev IDE. Select an object from the design window, and in the
Properties Explorer, you will see available attributes for that object listed, as
shown in Figure 3.10.The right-hand column lists the property names, and the
left-hand column stores the attribute’s value.The Properties window enables
Rapid Application Development (RAD) by allowing you to quickly create a
graphical representation of the application you are building without doing any
http://www.syngress.com
Figure 3.9 Floating Window
118 Chapter 3 • Visual Studio.NET IDE
coding whatsoever. Some options are available in the Properties Explorer.You can
select from the drop-down list the actual object you want to view.You can also
select the Events option and have the event available to that object displayed.You
can organize the Properties Explorer either by categories or alphabetically.
Any changes made in this window will be propagated to the design view and
code view windows, respectively.
Solution Explorer
The Solution Explorer is the same as it was in VS 6.The Solution Explorer is a
look at all the files in your solution. In the title menu bar, you have four options:
Refresh, Copy Web, Show All Files, and Properties.The Properties option lets you
set all of your solutions’ properties, including debug parameters options.The
.NET IDE has two different types of containers available for holding items: solutions
and projects.The main difference between the two is that you can have multiple
projects within a solution, whereas the project container keeps only files and
items within files.To view a project’s properties, right-click the project and select
Properties. Let’s look at project properties in more detail in Figure 3.11.
Here, you need to make two changes. Set the target schema to Internet
Explorer 3.2 & Navigator 3.0. Also, change the page layout from Grid to
http://www.syngress.com
Figure 3.10 Properties Explorer
Visual Studio.NET IDE • Chapter 3 119
Flow.These two changes will make all the JavaScript comply with the selected
browsers.This will enable you to code without having to check to make sure if
your scripts will work in older browsers. By making the change to “flow layout,”
you prevent your code from using absolute positioning within span tags so that it
will be safe for Netscape users.These two changes are useful for any ASP.NET
development you may do inside of the VS.NET IDE.
Object Browser
The Object browser will give you a complete list of all classes’ methods and
properties in your solution. Everything is listed, and it is quite in depth. If you
want to, you can look up parents of classes that you are using and list out the
methods and properties you might need. By double-clicking on an external class
in your solution, the Object browser will load and have all parent and child nodes
of the class listed with each of their methods and properties included.This comes
in handy when you are in need of finding a suitable substitute class to handle
some part of your application. Like in Java, .NET has an incredible quantity of
built-in classes that can accomplish just about everything you may need—the
trouble is finding their location and how to access their methods and properties.
Using the Object Browser enables you to achieve this in a timely fashion (see
Figure 3.12).
http://www.syngress.com
Figure 3.11 Project Properties
120 Chapter 3 • Visual Studio.NET IDE
From this window, you can quickly drill through a class that is not your own
and see what methods and properties it has; you also will get a summary of what
it does and how it is instantiated.
Dynamic Help
Dynamic Help is a dockable window just like the previous windows we have discussed.
To get Dynamic Help to appear, simply choose Help | Dynamic Help.
You can then make the window float or Auto Hide.One thing to note is that
each part of Help (Index, Contents, Search, Index Results, and Search Results),
are all separate windows, so if you undock them and make them all float you will
have quite a few windows appearing on the screen. One thing you may do is
load all the Help windows into themselves and a bottom tab order will appear
inside the main Help window; you can then access all parts of Help from the
same window (see Figure 3.13).
To customize the Dynamic Help window, choose Tools | Options. In
the Options dialog box, select Environment and then select Dynamic Help.
http://www.syngress.com
Figure 3.12 Object Browser
Visual Studio.NET IDE • Chapter 3 121
Here you can specify what topics you want to have available and in what order.
You may also specify how many links are displayed per topic.You may also create
a custom Help file on your own for your project, by following the XML schema
named vsdh.xsd. Create your XML file based off of that schema list and place the
file where you want your Help topics to be displayed.
Tabbing through the many different Help options and getting to the information
you need is now easy. If you have the hard drive space, loading all the
MSDN Help files from the disks that come with VS.NET would be beneficial.To
do this, simply check the option on the installation sequence that will run from
the computer and not the CD.This will prevent you from constantly having to
load another disk every time you want to look up a particular topic.This gets
quite annoying when you need one disk to open the tree view and another to
access the topic within.
Task List Explorer
The Task List (see Figure 3.14) enables you to add tasks that need to be done and
organize them in a number of different ways and with priority. It is very simple
to use. If you are using Source Safe, a group of developers can quickly see what
needs to be done and what has been done by viewing the Task List for each file
in the project.
http://www.syngress.com
Figure 3.13 Docked Help Windows
122 Chapter 3 • Visual Studio.NET IDE
Another feature of the Task List is that it will create tasks on the fly as you
debug your application by marking down any errors.You can then go back and
fix each task and have it removed.You can organize the task list on Build errors.
Also you can create your own custom token, which is a unique key that tells the
Task List that a comment needs to be added to the list, to appear in your Task
List from your code.You can map out your function or method or whatever you
are coding with your own custom tokens and have them appear in the Task List.
To create your own custom token to add to the default tokens available
(HACK,TODO, UNDONE), choose Tools | Options | Task List. Give the
token name and priority.To use the token, simply add something like the following
in your code window (use the comment tag “//” and then the token
name followed by the instruction for the task):
// FUBAR what I want in the task list to appear.
Features of VS.NET
VS.NET has a combination of new and old features built into the IDE.We discuss
the additions to IntelliSense, the new features of XML support, and the many different
ways you can now customize the IDE. Let’s begin with IntelliSense.
IntelliSense
IntelliSense is a form of code completion that has been part of most Microsoft
developer tools for many years now. Code completion technology assists when you
start to type a tag, attribute, or property by providing the resulting ending so that
you will not have to write out the whole item.You will notice this right away.
http://www.syngress.com
Figure 3.14 Task List
Visual Studio.NET IDE • Chapter 3 123
VS.NET has IntelliSense support for all of the primary programming languages:
VB.NET, C#, and C++. IntelliSense even exists for Cascading Style
Sheets and HTML. Unfortunately,VS.NET doesn’t include IntelliSense for XSLT
in the Beta2 version—we may have to wait for the release version. Currently
ActiveState does make an XSLT plug-in for VS.NET that provides this functionality;
you can obtain a free trial version at http://aspn.activestate.com/ASPN/
Downloads/VisualXSLT.
While developing, you will notice that IntelliSense provides information
about active classes only, meaning those that you have created in your project or
those referenced in your page with the using Directive (for code-behind pages:
pagename.aspx.cs). If you are trying to use an object or method, and no
IntelliSense appears for it, you may have forgotten to include the reference.
For example, if you attempt to do data operations using the SqlCommand
object, no IntelliSense will appear until you reference the appropriate data class
(see Figure 3.15):
using System.Data.SqlClient;
http://www.syngress.com
Figure 3.15 Using IntelliSense
124 Chapter 3 • Visual Studio.NET IDE
For C#, IntelliSense is available only in the code-behind page and not in the
ASPX page itself.This may change in the release version.To disable IntelliSense,
choose Tools | Options | Text/Editor and select the editor you are using,
which should be C#. In the Statement Completion section, uncheck all the
options, which will disable IntelliSense for the editor.
XML Editor
When working with XML,VS.NET has some interesting features. If you create a
well-formed XML document of your own, you can easily generate a corresponding
XSD schema that conforms to the 2001 W3C XML schema. Once this
is done, your XML document will have code completion based on this new
schema.To test creating a schema, let’s open poll.xml and generate a schema for it:
 Choose File | Open. Navigate to your CD-ROM drive and locate the
file poll.xml.
 Click Open.This should load the page into the IDE.
 If the XML is one continuous line, simply click the Format the Whole
Document icon (see Figure 3.16).
http://www.syngress.com
Figure 3.16 Formatting an XML Document
Visual Studio.NET IDE • Chapter 3 125
Now, let’s create a schema for this file. Right-click anywhere in the text editor
and select Create Schema.You can see these resulting changes in Figure 3.17:
 A new file called poll.xsd was auto-generated by VS.NET.
 In the Properties window, the new schema is set as the file’s target
schema.
 An XML namespace attribute is added.
 IntelliSense based on the schema is now available for this document.
You can also select a different schema to base the XML file on by selecting a
new schema from the targetSchema drop-down (see Figure 3.18).This would then
provide IntelliSense based on the schema selected.
You can also view XML documents from the Data mode.This presents the
document in a hierarchical structure. From this view, you can also add new nodes
and data to the document (see Figure 3.19).
http://www.syngress.com
Figure 3.17 Generating a Schema for a Well-Formed XML Document
126 Chapter 3 • Visual Studio.NET IDE
http://www.syngress.com
Figure 3.18 Selecting a Target Schema
Figure 3.19 Viewing an XML Document in Data Mode
Visual Studio.NET IDE • Chapter 3 127
Documentation Generation
(XML Embedded Commenting)
This feature enables you to comment your code with an embedded XML tagging
structure.When XML documentation is enabled, an XML documentation
file will be created during the build process. In the Solutions Explorer, right-click
on the project name, then select Properties.The Project Properties dialog
appears. Click the Configuration Properties folder and select Build.
Find the item called XML Documentation File in the textbox next to this,
provide a relative path to the file location you would like the Documentation
written to, and click Apply (see Figure 3.20).
Now let’s look at how to add XML comments to the code.
Adding XML Document Comments to C# Pages
The file used in this example is from a sample application built in Chapter 10
and is on the CD (See catalog.cs in the components folder.) To add XML documentation
comments to your code, simply type three slashes above any class,
method, or variable.
http://www.syngress.com
Figure 3.20 Setting the XML Documentation File Source in the Project
Properties Dialog
128 Chapter 3 • Visual Studio.NET IDE
public DataSet catalogItemDetails( string book_isbn )
{
return catalogRangeByCategory( -1, -1, book_isbn);
}
An XML representation of its inputs and outputs will be generated:
/// <summary>
///
/// </summary>
/// <param name=”book_isbn”></param>
/// <returns></returns>
public DataSet catalogItemDetails( string book_isbn )
{
return catalogRangeByCategory( -1, -1, book_isbn);
}
Simply add appropriate notes and build the project:
/// <summary>
/// Specialized interface to catalogRangeByCategory.
/// This Method returns all the data for only the given book
/// </summary>
/// <param name=”book_isbn”>string</param>
/// <returns>DataSet</returns>
public DataSet catalogItemDetails( string book_isbn )
{
return catalogRangeByCategory( -1, -1, book_isbn);
}
When you build the project, you will receive a list of warnings corresponding
to every Public variable, property, method, and class that is not commented. Figure
3.21 shows what happens when you tell it to create comments; this is how it tells
you what variable isn’t commented.This will not prevent program execution, nor
the writing of the documentation file. Figure 3.22 contains the XML generated
on build.
http://www.syngress.com
Visual Studio.NET IDE • Chapter 3 129
Customizing the IDE
The VS.NET IDE is fully customizable. All windows can be set to dockable, hide,
auto hide, and floating.You can display different toolbars for each different type of
file, and you can create customizable toolbars.You can set font, tab, and text
layout properties for each type of file.You can set the default Start page to open
the last project, or even set it to a user-created page. If you mess up the layout,
you can easily set it back to several predefined layouts.
http://www.syngress.com
Figure 3.21 Warning for Uncommented Public Variables, Properties,
Methods, and Classes
Figure 3.22 Generated XML Documentation
130 Chapter 3 • Visual Studio.NET IDE
Creating a Project
Now that we have covered all the different aspects of the IDE, let’s create a test
project.We cover the different type of projects available, show how to add a Web
reference to the project, and briefly go over some of the debugging tools available
to the IDE.This should give a well-rounded tour of the complete IDE. Now
let’s go over the projects available.
Projects
We cover the projects available to C# development:
 Windows application
 Class Library
 Windows Control Library
 ASP.NET Web application
 ASP.NET Web service
 Web Control Library
 Console application
 Windows Service
 Empty project
 Empty Web project
 New project in existing folder
Most of these are self-explanatory. Users new to .NET will see that three
Web projects are added into the project listing for all languages.These are the
ASP. NET, Application,Web Service, and Control Library.The other projects will
be familiar to all VS 6 users (see Figure 3.23).
Creating a Project
For this example, we will build an ASP.NET Web application (see Figure 3.23).
You may keep the name as the default or select a new name.The location should
be localhost if you are developing on the same box as the IIS server; if not, you
will have to place the location of the server in that text box, either through IP or
the name of server.The next option is to either close any open solutions and
open this new, or add it to the existing solution.We recommend that you choose
http://www.syngress.com
Visual Studio.NET IDE • Chapter 3 131
to have it close all open solutions and open new, so as not to task your machine
with having multiple solutions in the same IDE. Click OK, and VS.NET will
create the project for you.
Add Reference
One of the great benefits of working within the IDE of VS.NET is that you can
add references to your project with ease.Try it out: In this project, select the project
name in the Solutions Explorer. Right-click and select Add Web Reference.
Now you will have to have a location to a WSDL file from which to locate and
add in the Web Service to the project.This is covered later in the book.
You may also add a reference to a DLL to your project.This will be done
much the same way as the Web Reference. Instead of selecting Add Web
Reference as we just did, select Add Reference, then choose from all the available
references on your machine.
Build the Project
To build a project, simply press F5 or click the Start icon on the main window
menu bar.The project will be compiled.You must also set a Start page before this
takes place.To do that, right-click on the file you want to have be the Start page or
window and set it to Start page.This will launch this page first after the project
has been compiled and run (see Figure 3.24).
http://www.syngress.com
Figure 3.23 Project Listing in the IDE
132 Chapter 3 • Visual Studio.NET IDE
Debugging a Project
While building the project, any errors will bring up a dialog box, which will ask
you to continue with the errors in place, or to strop debugging and correct any
errors displayed.These errors will show in the Task window.You may doubleclick
on any error in the Task window, and the IDE will take you to that location
in the code. As you fix the bugs present in the task list, they will be removed.You
can also set breakpoints and step over and step into options.
http://www.syngress.com
Figure 3.24 Compiling a Project
Visual Studio.NET IDE • Chapter 3 133
Summary
In this chapter, we’ve taken a tour of the VS.NET IDE.We’ve seen an overview
of the interface, some of its component windows and some of its built-in features.
The design window and the code window are graphical tools used in creating
an application.You can split the windows or have tab groups added to
them; you can use the Toolbox (which includes Data, Components,Web Forms,
and Window Forms) to drag and drop objects onto the design window.The
Server Explorer window allows you to connect to a server on the network and
have full access to that server, and to link to any database servers on the network.
One of the new features for VS.NET is that you can dock all the windows, or
expand and collapse them within the view of the IDE.The Auto Hide feature of
each window makes them slide off the screen and embed in the side when not
needed; this enables you to have maximum code view but still have all windows
present.
The Properties Explorer (similar to the one in VS 6 and the Visual Basic IDE
and Visual Interdev IDE) allows you to select an object from the design window
to see available attributes for that object listed.Any changes made in this window
will be propagated to the design view and code view windows respectively.
The Solution Explorer (the same as in VS 6) is a look at all the files in your
solution via the four options: Refresh, Copy Web, Show All Files, and Properties.
The VS.NET IDE has two different types of containers available for holding
items, solutions and projects (you can have multiple projects within a solution,
whereas the project container keeps only files and items within files).The Object
browser will give you a complete list of all classes’ methods and properties in your
solution.
Other windows include Dynamic Help and the Task List. Dynamic Help is a
dockable window that you can fully customize to make it easy to tab to whatever
information you are interested in.You can use the Task List for collaborative projects
and in debugging; it lets you add and prioritize tasks.
IntelliSense, the code-completion technology Microsoft uses, is supported in
VS.NET for VB.NET, C#, and C++, but not yet for XSLT. IntelliSense provides
information about active classes. For C#, IntelliSense is available only in the
code-behind page and not in the ASPX page itself.
Another important feature is XML Documentation.This feature enables you
to comment your code with an embedded XML tagging structure.When XML
documentation is enabled, an XML documentation file will be created during
the build process.
http://www.syngress.com
134 Chapter 3 • Visual Studio.NET IDE
We’ve looked at some issues like the customizable, dockable, hide, auto hide,
and float settings for many of the component windows along with the profile
setting on the Start page.VS.NET is a collection of integrated developer tools
that you should definitely familiarize yourself with.
Solutions Fast Track
Introducing Visual Studio.NET
􀀻 Visual Studio.NET (VS.NET) provides a consistent interface across the
primary development languages.
􀀻 VS.NET provides easy to use tools for Windows and WebForms rapid
prototyping across languages (including C# and Managed C++).
Components of VS.NET
􀀻 Enhanced window manipulation for user preferences within the
Integrated Development Environment (IDE) gives the developer the
ability to dock, auto hide, hide, or float all component windows.
􀀻 Task List has the ability to create custom tokens to map out and
prioritize your code via the Task List.
􀀻 Server Explorer allows the developer to quickly connect and access any
database server on the network, enabling direct access to all database
objects, including stored procedures, functions, and user settings.
Features of VS.NET
􀀻 IntelliSense is one of the best tools at your disposal when learning a new
language or technology.VS.NET has built IntelliSense into almost every
aspect of the development process.
􀀻 Dynamically generated XML Documentation provides a fast and easy
way to comment your code and generate a separate XML formatted
documentation file.This tool makes code more self-documenting, and it
should save developers time and ensure that some documentation is
provided.
http://www.syngress.com
Visual Studio.NET IDE • Chapter 3 135
􀀻 Generating XML schemas from well-formed XML is now a breeze with
.NET.You can also create new XML documents that conform to
popular standards by selecting a targetSchema and using the IntelliSense
feature to create valid XML documents.
Customizing the IDE
􀀻 The VS.NET IDE is fully customizable. All windows can be set to
dockable, hide, auto hide, and floating.You can display different toolbars
for each different type of file and create customizable toolbars.You can
set font, tabbing, and text layout properties for each type of file.
􀀻 You can set the default Start page to open the last project, or even set it
to a user-created page.
􀀻 The IDE also includes several common default settings in case you mess
up while customizing your interface, settings like the default VB 6
interface or Visual InterDev.
Creating a Project
􀀻 One of the great benefits of working within the IDE of VS.NET is that
you can add references to your project with ease.
􀀻 To build a project, simply press F5 or click the Start icon on the main
window menu bar.
􀀻 While building the project, any errors will bring up a dialog box, which
will ask you to continue with the errors in place, or to strop debugging
and correct any errors displayed.
http://www.syngress.com
136 Chapter 3 • Visual Studio.NET IDE
Q: How can I look up a parent class method or property of any System-level
object?
A: Use the Class View window, accessed from the standard toolbar by clicking
View | Class View.
Q: Does VS.NET support line numbering in its text editor?
A: Yes, from the standard toolbar select Tools | Options.This will open the
Options dialog; select the Text Editor folder, pick the language, and click on
the check box for line numbering under the display section.
Q: Is there a way to set the tab size in the text editor?
A: Yes, from the standard tool bar select Tools | Options.This will open the
Options dialog; select the Text Editor folder, choose a language folder, select
Tabs, and set them to your desired setting.
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.
Windows Forms
Solutions in this chapter:
 Introducing Windows Forms
 Writing a Simple Windows
Forms Application
 Writing a Simple Text Editor
 Using the ListView and TreeView Controls
 Creating Controls
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 4
137
138 Chapter 4 • Windows Forms
Introduction
With so much focus on Web-based user interfaces, it’s easy to dismiss the traditional
Windows architecture when developing for the Internet.The recent popularity,
however, of peer-to-peer file sharing and online chat programs
demonstrates that the “rich client” can work extremely well over the Internet,
and provide features unavailable in thin client model.The .NET platform provides
a modern solution for developing Windows applications, with the following
key features:
 A revamped object-oriented model, with a focus on consistency and
extensibility
 A rapid application development environment in Visual Studio
 Easy access to the Internet through .NET networking libraries and
Web Services
 Managed execution environment that allows custom controls to be
hosted in a Web page
 Compilation to a small executable
And, of course, you no longer have any installation worries—you just need
to copy a small executable to the target machine and run it. Rich client has
become thin.
The components provided in the .NET library for writing Windows applications
can broadly be divided into two groups:Windows Forms (the components
that manage windows and controls) and the graphics device interface known as
GDI+ (the classes that encapsulate the lower-level graphics functions).This chapter
covers Windows Forms in some detail, also touching upon GDI+, and it takes you
step by step through the process of creating typical rich client applications.
Introducing Windows Forms
In essence,Windows Forms is a collection of classes and types that encapsulate
and extend the Win32 API in a tidy object model. In other words, the components
used to create Windows GUI applications are provided as .NET classes and
types that form part of an orderly hierarchy.
This hierarchy is defined by inheritance: Simple reusable classes such as
Component are provided, and then used as a base from which more sophisticated
classes are derived.We can draw a useful overview by representing the inheritance
http://www.syngress.com
http://www.syngress.com
hierarchy in a treelike diagram. Figure 4.1 summarizes at a high level the classes
that comprise Windows Forms and GDI+.
The arrows represent inheritance: Control assumes all the functionality of
Component, which assumes all the functionality of Object.Table 4.1 provides a
quick and pragmatic summary of the four essential classes on which the Windows
Forms types are based.
Windows Forms • Chapter 4 139
Figure 4.1 A Summary of Window Forms and GDI+ Classes
Object
Component
Control
Windows Forms
Components
Timer
MainMenu
ImageList

Containers Hosting
Child Controls
Form
Panel
TabPage
GroupBox
UserControl

Windows Forms
Controls
Label
Button
TextBox
CheckBox
ListBox

Subclassed Forms
and UserControls
Custom Controls
drawn with GDI+
System.Windows.Forms
GDI+ Classes
Graphics
Pen
Brush
Bitmap

System.Drawing
140 Chapter 4 • Windows Forms
Creating a Windows Forms application is largely just a matter of instantiating
and extending the Windows Forms and GDI+ classes. In a nutshell, you typically
complete the following steps:
1. Create a new project defining the structure of a Windows Forms
application.
2. Define one or more Forms (classes derived from the Form class) for the
windows in your application.
3. Use the Designer to add controls to your forms (such as textboxes and
checkboxes), and then configure the controls by setting their properties
and attaching event handlers.
http://www.syngress.com
Table 4.1 Core Classes
Class What It Does Why We Need It
Object
Component
Control
Form
For a tidy unified type system,
and to provide core functionality
available to all types (such
as ToString).
So Visual Studio’s Designer can
host a wide variety of controls
and components in a generic
way, to provide a base from
which you can write nonvisual
components, and to allow the
cleanup of Windows handles
and file handles in a timely and
reliable manner.
As a common superclass for all
controls, such as textboxes,
labels, and buttons, allowing
them to be treated in a consistent
manner, as well as providing
a base from which you
can derive your own custom
controls.
To provide a base class with
standard windowing and containership
functionality that
you can subclass to create
forms in your application.
Acts as a base class for all
types in the .NET Framework.
Provides the basics of containership,
facilitates hosting in a
visual designer, and defines a
protocol for resource disposal.
Provides the core functionality
for a visual control that
responds to mouse and keyboard
messages, accepts focus,
and can participate in dragand-
drop operations.
Defines a class representing a
window to which you can add
controls.
Windows Forms • Chapter 4 141
4. Add other Designer-managed components, such as menus or image lists.
5. Add code to your form classes to provide functionality.
6. Write custom controls to meet special requirements, using GDI+ classes
to handle low-level graphics.
In this chapter, we cover each of these steps through a series of walkthroughs.
Starting with a new Windows Forms project, we visually add controls to a simple
form, add an event handler, and then demonstrate how controls can be added at
runtime. In the next walkthrough, we write a simple text editor, illustrating
menus, single and multiple-document interfaces, dialog forms, and visual inheritance.
In the following example, we introduce the ListView and TreeView controls,
going step-by-step through the process of setting up a splitter, adding a context
menu, and enabling drag and drop between the controls. In the final walkthrough,
we write our own controls—starting with a simple address container
and finishing with a scrolling text banner.We then show how custom controls
can be hosted on an HTML page—demonstrating how C# and Windows Forms
can be used to write Java-like Internet applets.
Writing a Simple Windows
Forms Application
The first step to building a Windows Forms application is creating a project.A
Windows Forms project is just like any other type of project in that it consists of
a grouping of source code files, a list of references to required .NET code
libraries, and an appropriate configuration of compilation and debugging options.
When you use Visual Studio to create a project from a template, it sets all of this
up for you, providing a “skeleton” appropriate to the template you’ve selected. In
the case of Windows Forms, this consists of the following:
 A project of Output Type Windows Application.You can view or change
this in the Project | Properties dialog box.
 References to the .NET assemblies required for typical Windows Forms
applications (covering most of the types in the Windows Forms namespace).
You can see a list of the project references in the Solution Explorer.
 A blank form, called Form1 (a C# class with the structure required for a
visually editable form).
 A Main method in Form1 that instantiates and displays the form.
http://www.syngress.com
142 Chapter 4 • Windows Forms
Let’s start the walkthrough by creating a new Windows Forms project. From
the main menu, choose File | New | Project, click Visual C# Projects, and
choose the Windows Application template (see Figure 4.2). Change the project
name to SimpleApp and click OK.
Adding Controls
Once we’ve created the project,Visual Studio opens the main form (Form1) in
the Designer—the visual editor for our C# form class. Basically, a form created in
Visual Studio is just a C# file, defining a class based on
System.Windows.Forms.Form, containing code to add and configure the controls
created visually.Visual Studio is a “two-way tool” meaning that we can work with
the same code either visually (using the Designer) or programmatically (in the
Code Editor).
Let’s use the Designer to add a few controls to Form1.We can add controls
and components from the toolbox window and then configure them using the
Properties window.
1. From the toolbox, add a Label control to the form. By default,Visual
Studio will name the control Label1.
2. From the Properties Window (F4) change label1’s Text property to
Favorite CD, and change its AutoSize property to True (see Figure 4.3).
This tells the control to size itself according to the metrics of the font
and width of the text.
http://www.syngress.com
Figure 4.2 Creating a New Windows Forms Project
Windows Forms • Chapter 4 143
3. Now add a TextBox from the toolbox onto the form, and position it
below the label. Enlarge it horizontally and clear its Text property.
4. Add another label to the form, setting its Text property to Favorite
Style, and AutoSize property to True.
5. Add a ComboBox and position it below the Favorite Style label. Clear
its Text property.
6. Select the combo’s Items property, and then click the ellipses on the right
to open the String Collection Editor.Type in a few styles of music—
each on a separate line, as shown in Figure 4.4.
7. Click OK, and then press F5 to save, compile, and run the application.
http://www.syngress.com
Figure 4.3 Adding and Configuring a Label Control
144 Chapter 4 • Windows Forms
http://www.syngress.com
Figure 4.4 Populating a ComboBox Items Collection
Working with Controls: Using TextBoxes
To create and work with textboxes having more than one line:
1. Set MultiLine to True and AutoSize to False.
2. Set AcceptsTab and AcceptsReturn to True to allow tabs and
new lines to be entered via the keyboard.
3. Set the ScrollBars property to Vertical (or Both if WordWrap is
false).
4. Use the Lines property to access the control’s text one line at
a time.
5. Use \r\n for a new line, for example, Flat 18\r\nQueen St.
To use the control for entering a password, set the PasswordChar
property to *. To read or update selected text, use the SelectionStart,
SelectionLength, and SelectedText properties.
Developing & Deploying…
Windows Forms • Chapter 4 145
Adding an Event Handler
Let’s add some functionality to the form.
1. Add a Button and ListBox to the form.
2. Select the button, and change its Text property to Update.Then click
the lightning icon in the Properties window to switch to the Events
View (see Figure 4.5).
Think of these events as “hooks” into which we can attach our own
methods.You can either double-click on an event to create a new eventhandling
method, or use the drop-down list to connect into an existing
compatible method.
3. Double-click on the Click event.Visual Studio will write a skeleton
event-handling method, wiring it to the event. It will then place you in
the Code Editor, inside the empty method definition:
private void button1_Click(object sender, System.EventArgs e)
{
}
The .NET convention for event handling requires two parameters: a
sender parameter of type object, and an event arguments parameter of
http://www.syngress.com
Figure 4.5 Properties Window Events View
146 Chapter 4 • Windows Forms
type EventArgs—or a descendant of EventArgs.The sender parameter tells
us which control fired the event (this is useful when many controls have
been wired to the same event-handling method).The second parameter
is designed to supply special data about the event. In the case of Click,
we have a standard EventArgs object, and this contains no useful information—
it’s just there to meet the protocol required to support more
sophisticated events (such as KeyPress or MouseDown).
The actual name for this method (button1_Click) is just a convenient
identifier generated by Visual Studio;Windows Forms doesn’t impose
any particular naming convention.
4. Add the following code to the event handler:
private void button1_Click(object sender, System.EventArgs e)
{
listBox1.Items.Clear();
listBox1.Items.Add (“Fav CD: ” + textBox1.Text);
listBox1.Items.Add (“Fav Style: ” + comboBox1.Text);
}
Here we’re manipulating our list box through its Items property. Items
returns a collection object, having methods to add and remove items from
its list. Note how we access each control through its name—this is possible
because the Designer creates class fields matching the names of each control.
You can see these declarations at the top of the class definition.
5. Press F5 to compile and run the program (see Figure 4.6).
http://www.syngress.com
Figure 4.6 Running a Simple Windows Forms Application
Windows Forms • Chapter 4 147
Adding Controls at Runtime
Sometimes it’s necessary to add controls without the help of the Designer. For
instance, you might want some controls to appear on a form only when a particular
button is clicked.
In learning how to programmatically add controls, it’s very helpful to
examine a visually created form in the Code Editor. If you expand the Designer
Generated Code region, you’ll see a method called InitializeComponent containing
all the code that creates and configures each of the form’s visual components.
http://www.syngress.com
Working with Controls: Using the
ComboBox and ListBox Controls
To add items to the controls’ selection lists programmatically:
1. Call the Item property’s Add method to append to the end of
the list, for example:
myControl.Items.Add (“My New Item”);
2. Use the Item property’s Insert method to insert within the list.
3. Because these methods expect an Object type, the item you
add can be of any class, including your own (this is polymorphism
in action—one of the benefits of a working in an
object-oriented language). The control simply calls the item’s
ToString method to determine what to display.
To get the currently selected item:
1. Use the Text property to return a string.
2. Use SelectedIndex to get a numeric position within the list.
3. Use SelectedItem to get an object reference. If the item is of
your own custom class, you’ll need to explicitly cast the
returned value back to your type.
To allow the user to select only from items in a ComboBox list, set
the DropDownStyle property to DropDownList.
Developing & Deploying…
148 Chapter 4 • Windows Forms
WARNING
Although reading Designer-generated code is useful in understanding
how components are instantiated and configured, you shouldn’t make
manual changes to this code without exercising some caution. In particular,
you should check that the control renders as expected in the
Designer before saving the form. You should also check your code after
making some visual change—Visual Studio completely rewrites the
Designer-generated code section, so your modifications may not appear
as originally entered.
Here are the four steps to programmatically adding a control or component:
1. Add a class field declaration for the new control.
2. Instantiate the control.
3. Configure the control by setting its properties and adding event handlers,
if required.
4. Add the control to the form’s Controls collection (or alternatively, to the
Controls collection of a container control, such as a GroupBox).
Let’s work through an example: we’ll create a new form, add a button, and
then have a textbox appear when the user clicks the button:
1. Create a new Windows Forms project called SimpleApp2 and add a
Button control from the toolbox onto the new form.
2. Press F7 to open the Code Editor, and locate button1’s declaration.
Below this, add a similar declaration for our new textbox, as follows (you
can exclude the System.Windows.Forms prefix if your form has the appropriate
using statement):
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox myTextBox;
You need to understand that this declaration doesn’t actually create a
textbox. All it does is instruct the compiler, once our form is instantiated,
to create a field that can reference (point to) a textbox object—one
that does not yet exist.This declaration exists so as to provide a convenient
way to refer to the control throughout the lifetime of the form. In
the cases where we don’t need to explicitly reference the control after its
been created, we can do away with this declaration.
http://www.syngress.com
Windows Forms • Chapter 4 149
3. Return to the Designer, and double-click on the button.This is a quick
way to attach an event handler to the button’s default event (Click).
4. Add the following code to the button’s event handler:
private void button1_Click(object sender, System.EventArgs e)
{
// Create the actual textbox and assign its reference to
myTextBox
this.myTextBox = new TextBox();
// Position the control
myTextBox.Location = new Point (30, 20);
// Put the control on the form.
this.Controls.Add (myTextBox);
}
5. Press F5 to test the application (illustrated in Figure 4.7).
You might have noticed that we created a Point object to position the control.
Point, Size, and Rectangle are three “helper types” defined in the
System.Drawing namespace, and are used extensively in Windows Forms—as well
as other parts of the .NET Framework.Table 4.2 illustrates how these types are
most commonly applied in Windows Forms.
http://www.syngress.com
Figure 4.7 Adding Controls at Runtime
150 Chapter 4 • Windows Forms
http://www.syngress.com
Table 4.2 Helper Types for Positioning and Sizing
Type Example Notes
Point
struct
Size
struct
Rectangle
struct
Sets button1’s
position 100 pixels
across and 80 pixels
down.
Equivalent to the
above.
Equivalent to outputting
button1.Left.
Not permitted because
of the way structs are
marshaled in C#.
Resizes button1 to 75
by 25 pixels.
Equivalent to the
above.
Attempts to resize the
form so it just fits
button1. However, the
form’s Size property
includes the title bar
and borders—its
usable space is less,
and button1 won’t
quite fit.
ClientSize excludes
title bars and borders
so this works
correctly.
Rectangle combines
Point and Size.
Moves and sizes
button1 to fill the
whole client area of
our form (later we’ll
see that docking provides
a better solution
to achieving this).
button1.Location = new Point (100, 80);
button1.Left = 100;
button1.Top = 80;
Console.WriteLine (button1.Location.X);
button1.Location.X = 100;
button1.Size = new Size (75, 25);
button1.Width = 75;
button1.Height = 25;
// Assuming “this” is our form
this.Size = new Size (button1.Right,
button1.Bottom);
this.ClientSize = new Size
(button1.Right, button1.Bottom);
button1.Bounds = new Rectangle
(100, 80, 50, 20);
button1.Bounds = new Rectangle
(0, 0, this.ClientSize.Width,
this.ClientSize.Height);
Windows Forms • Chapter 4 151
http://www.syngress.com
Working with Controls: Using Controls Collections
The form class is an example of a control that hosts other controls.
Windows Forms manages this containership by providing a Controls
property, returning a ControlCollection object that has methods to add,
remove, and access the child controls. Like other .NET collections, it
implements standard interfaces such as ICollection and IList—and this
means we can work with them all in a similar way.
To access an individual control by its position in the collection, use
its Indexer—for example:
Controls[0].Hide() // hide the first control in the collection
To iterate through every control, use the foreach structure—for
example:
// Write the Text property of each control on the form
foreach (Control c in Controls)
Console.WriteLine (c.Text);
To remove a control from the collection, use the Remove method—
for example:
Controls.Remove (txtMiddleName);
To reparent a control to another collection:
 Change the control’s Parent property.
 A control’s position in the collection determines its z-order
(front-to-back order), where position 0 is at the front. When
you use Bring To Front and Send To Back in the Designer,
you’re actually changing the control’s position in its parent’s
Controls collection. You can also achieve the same thing at
runtime by calling the object’s BringToFront and SendToBack
methods, or by using the parent collection’s SetChildIndex
method.
Here are some other commonly used container-style controls that
offer the same property:
Developing & Deploying…
Continued
152 Chapter 4 • Windows Forms
Attaching an Event Handler at Runtime
Let’s suppose we want to set up our newly created textbox so that when it’s
right-clicked, a message box appears.We need to add an event handler to the
textbox at runtime, and there are two steps to this:
 Writing the event-handling method.
 Attaching the method to the control’s event.
In our case, we’ll need to attach to the textbox’s MouseDown event (because
there’s no specific right-click event). First, we need to write the event-handling
method, with parameters of the correct type for a MouseDown event.You can
determine an event’s signature in two ways:
 Look for the event in the Microsoft documentation, and then click on
its delegate (in our case, MouseEventHandler).
 Using the Designer, add a dummy control of the type we’re attaching to,
create an appropriate event handler, and then delete the dummy control.
The event-handling method will still be there—with the correct signature.
All we need to do is rename it.
Here’s how we do it:
1. Using either approach, add a method to our form, as follows:
void myTextBox_MouseDown (object sender, MouseEventArgs e)
{
if (e.Buttons == MouseButtons.Right)
// Show is a static method of System.Windows.Forms.MessageBox
MessageBox.Show (“Right Click!”);
}
http://www.syngress.com
 Panel A simple container for other controls.
 GroupBox A container with a border and caption text, used
for visually grouping controls on a form. It’s also often used
to host RadioButton controls (only one radio button can be
checked at a time inside each group box).
 TabPage A TabControl contains a collection of TabPage controls—
each of which acts as a container for child controls,
with its own Controls property.
Windows Forms • Chapter 4 153
2. Next, we attach this method to myTextBox’s MouseDown event. Return
to the button1_Click method and add the following line of code:
myTextBox.MouseDown += new MouseEventHandler (myTextBox_MouseDown)
On the left-hand side,myTextBox.MouseDown is the event to
which we’re attaching, using the += operator. On the right-hand side,
we’re creating a new MouseEventHandler delegate instance: in other words,
an object containing a pointer to a method (myTextBox_MouseDown) conforming
to MouseEventHandler’s signature.
3. Test the application.
http://www.syngress.com
Why We Need Delegates
It’s often asked, “why can’t we simply assign a target method (for
example, myTextBox_MouseDown) directly to an event?” C# doesn’t
allow this because the language is strongly typed, and the event needs
to pass parameters to the target method. If we could assign a method
directly to an event, there would be no place to formalize the number
and types of these parameters (the method signature). We need a way
of describing an agreed method signature, and for this we have delegates.
The easiest way to think of a delegate is in two parts:
 The delegate definition This simply describes a method
signature.
 A delegate instance This is an object containing a pointer
to a method conforming to the signature.
Most of the delegate definitions you’ll come across are part of the
.NET Framework—although sometimes you define your own—usually
when writing custom controls. Delegate instances, however, are created
whenever you hook up to an event.
Here’s an example of a complete delegate definition:
public delegate void EventHandler (object sender, EventArgs e)
As you can see, all this does is set out a signature: two parameters,
one of type object, and the other of type EventArgs, and a void return
type. EventHandler is the “plain vanilla” delegate used extensively in the
Developing & Deploying…
Continued
154 Chapter 4 • Windows Forms
Writing a Simple Text Editor
This walkthrough will take you through developing a simple Notepad-style text
editor, demonstrating the following:
 Adding a menu
 Creating and activating a new form
 Creating a Multiple Document Interface
 Creating a dialog form
 Using form inheritance
 Adding a tab control
 Anchoring controls
 Connecting the dialog form
The code for this walkthrough is on the accompanying CD-ROM, in folder
the TextEditor directory.
Starting the Project
First, we’ll create a new project.We’ll then rename the main form Visual Studio
creates for us to something more meaningful:
1. Create a new Windows Forms Project, naming the project TextEditor.
2. From the Solution Explorer, rename Form1.cs to MainForm.cs (press F2
or right-click and choose Rename). Also, from within the Properties
http://www.syngress.com
.NET Framework. Events are declared of this type if they don’t require
any special information sent to the target.
Here’s an example of a delegate instance:
EventHandler eh = new EventHandler (textBox1_Click);
This simply contains a reference (pointer) to textBox1_Click. The
compiler will check that the target method’s signature agrees with the
delegate definition (EventHandler). The following line of code attaches
eh to myTextBox’s click event:
myTextBox.Click += eh;
Review Chapter 2 for more information on delegates and events.
Windows Forms • Chapter 4 155
window, change the form’s name to MainForm (this changes its class
name), and change its Text property to Simple Editor.
3. In the Code Editor, check that the class’s Main method references
MainForm rather than Form1, changing it if necessary.
Creating a Menu
Next, we’ll create the main menu:
1. From the toolbox, drag a MainMenu component onto the form.The
Designer provides a WYSIWYG interface for populating the menu. In
other words, it’s just a question of typing directly into the menu.
2. Type in menu items for File, New, and Exit, as in Figure 4.8.
To enter the underlined accelerator keys, put an ampersand (&) before
the desired character (the same principle works with label controls).To
enter the separator between New and Exit, type a single hyphen (-).
3. Click on the New menu item, and from the Properties window, set its
shortcut to Ctrl+N.
4. Right-click on one of the menu items, select Edit Names, and enter
meaningful menu item names such as miFile, miNew, and miExit.
This will help later on with coding and debugging. Right-click again
and uncheck Edit Names.
5. Double-click on the Exit menu item.This will create and attach an
event handler (to Click, the default event for the MenuItem class) and
place you in the code window. Add the following line:
private void miExit_Click(object sender, System.EventArgs e)
{
http://www.syngress.com
Figure 4.8 Creating a Main Menu
156 Chapter 4 • Windows Forms
Close();
}
Because we’re in the application’s startup form, closing the form is
sufficient to close the application (and any other forms that are open). If
we wanted to exit the application from another form, we could instead
call Application.Exit().
6. Run the application.There’s our menu!
http://www.syngress.com
Working with Controls: Using Menus
Menus are not strictly controls—in fact, they’re based on Component—
because menus and menu items don’t exhibit the normal behavior of a
control. Each menu is encapsulated by a MainMenu component, comprised
of a collection of MenuItem components. Although you can have
any number of main menus on a single form, only one can be active at
a time (this is determined by the form’s Menu property). A context menu
(right-click pop-up menu) is encapsulated by the ContextMenu component,
and this also comprises a collection of MenuItems.
To add a menu item at runtime:
1. Define an appropriate event-handling method for the menu
item’s Click event, such as the following:
void miNew_Click (object sender, EventArgs e)
{
MessageBox.Show (“New Item Clicked!”);
}
2. Create and configure a MenuItem object, and then add it to
the main menu’s MenuItem collection. For example:
MenuItem mi = new MenuItem
(“New”, new EventHandler (miNew_Click));
mi.Shortcut = Shortcut.CtrlN;
mainMenu1.MenuItems.Add (mi);
Developing & Deploying…
Continued
Windows Forms • Chapter 4 157
Adding a New Form
Let’s create a new form for editing text documents:
1. Go to Project | Add Windows Form, name the class EditForm.cs,
and then change the form’s Text property to Untitled.
2. Drag a TextBox control from the toolbox to the form, and from the
Properties windows, change its name to txtEdit.
3. Clear the textbox’s Text property and change its font’s point size to 10.
4. Set AutoSize to False and MultiLine to True.This allows us to vertically
enlarge the textbox.
5. Change the Dock property to Fill (from the drop-down, click the box in
the center).This expands the textbox so that it fills the entire client area
(inside area) of the form. If you subsequently resize the form, the
textbox will still fill the entire area.
6. Set AcceptsReturn and AcceptsTab to True.
7. Drag a MainMenu control onto the form, and create a View |
Options menu structure, as in Figure 4.9.
Let’s now hook this up to our main form.
8. Return to the main form, and double-click on the menu item for New.
Add the following code to its event handler:
private void miNew_Click(object sender, System.EventArgs e)
{
EditForm ef = new EditForm(); // Create new instance of form
http://www.syngress.com
To add subitems at runtime:
1. Define an event-handling method, then create and configure
a MenuItem object as in the previous bullet item.
2. Add the new object to the parent menu item’s MenuItem
collection, as follows:
miFile.MenuItems.Add (mi);
To enable and disable menu items, set the menu item’s Enabled
property to True or False (the parent menu item’s Popup event is a convenient
place in which to do this). To check and uncheck menu items, set
the menu item’s Checked property to True or False.
158 Chapter 4 • Windows Forms
ef.Show(); // Display form modelessly
}
Now run the application, and click New a few times to open up several text
editor windows. Notice how each of the forms is modeless (you can click randomly
on any form) and top-level (each window floats independently on the
desktop). If we moved the File menu to the child form itself, and did away with
the main form entirely, we’d have a Single Document Interface (SDI) application.
Internet Explorer is an example of an SDI (see Figure 4.10).
http://www.syngress.com
Figure 4.9 EditForm Menu structure
Figure 4.10 Single Document Interface
Windows Forms • Chapter 4 159
Creating a Multiple Document Interface
In the example in the preceding section, we would prefer the editor forms to be
physically constrained to the main parent window, and to have only one menu,
with the View menu items merged into the main menu.This describes a Multiple
Document Interface (MDI) style. Let’s turn our interface into an MDI:
1. Enlarge the main form, and change its IsMdiContainer property to True.
2. Click on our main menu component and add a new menu item for a
Window menu. Set its MdiList property to True (this instructs
Windows to add items for child forms automatically) and set its
MergeOrder to a large value such as 20 (so that the Window menu item
appears at the right-hand side, when merged with child menus).
3. Press F7 to return to the Code Editor, and enhance the event handler
for miNew as follows:
private void miNew_Click(object sender, System.EventArgs e)
{
EditForm ef = new EditForm();
ef.MdiParent = this; // this makes ef an MDI
// child form
ef.Show();
}
4. Run the application.We now have an MDI (see Figure 4.11).
http://www.syngress.com
Figure 4.11 Multiple Document Interface
160 Chapter 4 • Windows Forms
Let’s now enhance this by adding “Tile” and “Cascade” menu items:
1. Add menu items to the Window menu, titled Tile Vertical, Tile
Horizontal, and Cascade.
2. Double-click each of the menu items to create event handlers. In each
method, call the form’s LayoutMdi method with an appropriate member
of the MdiLayout enumeration, such as in the example below:
private void miTileVertical_Click(object sender,
System.EventArgs e)
{
LayoutMdi (MdiLayout.TileVertical);
}
Creating a Dialog Form
A form with OK and Cancel buttons is usually described as a dialog. In most
cases, dialog forms are modal rather than modeless, meaning the user must accept
or cancel before clicking on another form. Making and displaying a dialog form
involves three parts:
 Creating a form that has the “look and feel” of a dialog
 Displaying the form modally—using ShowDialog() instead of Show()
 Disposing of the form when we’re finished
Let’s first create a basic dialog form. Later we’ll use this as a base for creating
an Options Form within our text editor:
1. Add a new form to the project called DialogForm.
2. Put two buttons onto the form. Name one btnOK and the other
btnCancel. Change their Text properties to OK and Cancel.
3. Set the DialogResult property of the OK button to OK and the Cancel
button to Cancel.This instructs the form to automatically close when
the button is pressed (and to return an appropriate DialogResult to the
calling program).
4. Click directly on the form, and change its FormBorderStyle property to
FixedDialog.This will prevent the user from resizing the form. Of
course, you can still resize it from within the Designer.
http://www.syngress.com
Windows Forms • Chapter 4 161
5. Set MaximizeBox and MinimizeBox properties to False and the
StartPosition to CenterScreen.
6. Set the AcceptButton property to btnOK and the CancelButton property
to btnCancel.This will hook up the Enter and Escape keys to the OK
and Cancel buttons (see Figure 4.12).
7. Finally, we need to remove the form’s icon and associated menu.This is
not possible with the Designer, however, it can be done programmatically.
In the form’s constructor, after the call to InitializeComponent, add
the following:
this.Icon = null;
8. Next, we need to activate and test the dialog.We’ll do this from the
Options menu item in EditForm. Return to EditForm and double-click
on the Options menu item. Add the following code:
private void miOptions_Click(object sender, System.EventArgs e)
{
DialogForm df = new DialogForm();
if (df.ShowDialog() == DialogResult.OK)
MessageBox.Show (“OK Pressed!”);
df.Dispose(); // modal forms don’t dispose automatically!
}
The ShowDialog method returns a DialogResult enumeration, and this tells us
how the form was closed.You can also query the form’s DialogResult property to
http://www.syngress.com
Figure 4.12 Basic Dialog Form
162 Chapter 4 • Windows Forms
the same effect.The call to Dispose is required because a form activated with
ShowDialog does automatically clean up when it’s closed.This is a useful feature
because it allows us to query the state of its controls after the form’s been closed.
But once we’re done, we must remember to call Dispose—otherwise the form
will continue to consume operating system resources—even after the garbage
collector has released its memory.This completes the skeleton dialog form.You
can run the application as it is, to ensure that the form works as expected.
Using Form Inheritance
The dialog form we’ve just designed is an example of a template that could be
utilized in many places within an application.We could keep this form as it is
http://www.syngress.com
Remembering to Call Dispose
As a rule, if a .NET object has a Dispose or Close method, it must be
called once the object is no longer required. But in practice, we rarely
dispose Windows Forms components explicitly because most components
are parented to a container collection that handles disposal automatically.
For instance, a control object is normally parented to a form’s
Controls collection (or some other Controls collection), and this is programmed
to dispose all child controls automatically with the parent.
In a couple of situations, however, you do need to explicitly dispose—
when you’ve programmatically instantiated an object having a
Dispose method that’s not managed through a component collection
(such as a Bitmap), and when you’ve instantiated a form modally (by
calling ShowDialog).
Disposing is about releasing resources—such as Windows handles
and file handles. It’s not about releasing memory: The CLR’s garbage collector
does this automatically (some time) after an object is no longer
referenced. Calling Dispose does not influence garbage collection, and
conversely, the garbage collector knows nothing about Dispose.
It’s sometimes asked, “why doesn’t the class’s destructor handle
disposal?” The answer is that inherent limitations are associated with
destructors activated via automatic garbage collection, and disposal is
considered too important to be subject to these limitations.
Debugging…
Windows Forms • Chapter 4 163
(our “skeleton” dialog), and then whenever we need a real dialog, we could create
a copy to which we add controls.
But this approach is inflexible in that if we later enhance the base dialog
form, we’d have to manually update each of the forms we’ve already created. By
using inheritance, we get around this problem: Forms that have been subclassed
from the base dialog form will automatically assume its functionality—even if the
base class is later modified.
Let’s turn our DialogForm into a reusable base class.We need to make only
one small change. Select the OK button and change its Modifiers property to
Protected (sometimes called Family), and likewise with the Cancel button.
This allows subclasses to access the buttons—and change their properties.
Subclassed dialogs will need to modify the buttons’ Location properties, otherwise
they’ll be stuck in one position on the form.
WARNING
Once you’ve created a reusable form, such as a dialog, it’s quite
tempting to subclass it again to create another reusable form—such as a
tabbed dialog, which in turn is subclassed into a sizable tabbed dialog,
then a dialog with an Apply button, and so on. This leads to a messy and
inflexible hierarchy, causing many more problems than the designer set
out to solve. It’s usually best to keep (implementation) inheritance as
simple as possible—the best object-oriented designs often employ component
reuse and interface inheritance as alternatives to keep complexity
and coupling to a minimum. It’s worth reading a book or two on objectoriented
design before diving into a big project—if these concepts are
unfamiliar.
Now we can subclass and create the options form. First, rebuild the project
(Shift+Ctrl+B).Then select Project | Add Inherited Form, name the class
OptionsForm, and select DialogForm from the Inheritance Picker (see
Figure 4.13).
To test this, modify the miOptions_Click method in EditForm so that it
instantiates OptionsForm instead of DialogForm and run the application.
http://www.syngress.com
164 Chapter 4 • Windows Forms
Adding a TabControl
When designing a form, it’s a good idea to start with a TabControl if you plan to
have a lot of controls—or if you anticipate a lot of controls in the future. It discourages
future developers from cluttering the form, as well as giving dialog
forms a tidy presentation.
Let’s add a tab control to OptionsForm:
1. Drag a TabControl onto the options form, and align it with the OK
and Cancel buttons, as shown in Figure 4.14. (The easiest way to align
the Cancel button is to select it together with the tab control by using
the Ctrl key, and then choosing Align Rights from the Layout toolbar
or Format menu.)
2. Select the tab control and then click Add Tab at the bottom of the
Properties window.
http://www.syngress.com
Figure 4.13 Inheritance Picker
Figure 4.14 Options Form with TabControl
Windows Forms • Chapter 4 165
3. Click inside the dashed rectangle on the tab control to select a TabPage,
and then set its Text property to Editor.
Note that you can also add and configure tab pages by clicking the
ellipses on the tab control’s TabPages property. Now we’ll add controls to
the tab page.
4. Put a couple of checkboxes, a NumericUpDown control, and a label onto
the tab page, as in Figure 4.15. Name the controls chkWordWrap,
chkApplyAll, and nudFontSize.
5. Choose View | Tab Order and click each control in sequence, from
top to bottom.This sets the order of focus when the Tab and Shift+Tab
keys are used.
http://www.syngress.com
Figure 4.15 Adding Controls to the TabPage
Working with Controls: Using TabControls
A TabControl consists of a collection of TabPages, each of which hosts a
collection of controls.
To determine the active tab page:
1. Use the SelectedTab property to get a TabPage object.
2. Use the SelectedIndex property to get its position.
To add a page at runtime:
Developing & Deploying…
Continued
166 Chapter 4 • Windows Forms
Anchoring Controls
Next, we’ll make the form sizable.This is a useful feature in forms that have controls
with a lot of information to display—such as the TabPage Collection Editor
in Visual Studio. Of course in our case, we have only two checkboxes and an updown
control, but we’ll gloss over that for now:
1. Change the tab control’s Anchor property to all four sides (from the
drop-down, click on the bottom and right rectangles so that all four
rectangles are selected). Selecting two opposite sides instructs a control
to expand or shrink in that direction. Our tab control will expand or
shrink both vertically and horizontally.
2. Change the OK and Cancel button’s Anchor properties to Bottom and
Right (from the drop-down, uncheck the rectangles at the top and left,
and check those at the bottom and right).This instructs the buttons to
maintain their alignment to the bottom and right of their parent container
(in this case the form).
3. Change the Form’s FormBorderStyle to Sizable.
Now try resizing the form.You can test this better by adding a dummy list
box to the tab page (placing it the area at the right), and anchoring it to all four
sides. Anchoring works in the same way at runtime.
http://www.syngress.com
1. Create a new TabPage control:
TabPage tp = new TabPage (“Advanced Properties”);
2. Add the new TabPage control to the tab control’s TabPages
collection:
tabControl1.TabPages.Add (tp);
To programmatically add controls to a tab page:
1. Declare, create, and configure the control as if it were to go
directly on the form.
2. Add the control to the tab page’s Controls collection instead
of the form’s Controls collection:
tabPage4.Controls.Add (myTextBox);
/*or*/ tabControl1.TabPages[3].Controls.Add (myTextBox);
Windows Forms • Chapter 4 167
Changing the Startup Form
Once you have several forms in your application, you might want to change the
form used for startup.This is simply a matter of moving the Main method:
1. Cut and paste the Main method from the old startup form to the new
startup form.
2. Update this method so that it instantiates the new form class instead.
As long as you have only one Main method in your project, the compiler will
find it, and make that class the startup object. If you have more than one method
in your project with this name, you need to specify which should be the startup
object in the Project | Properties dialog.
Connecting the Dialog
Let’s now write the code to make the Options form function.We’ll need to pass
data to and from the dialog form—in our case, the editing form’s textbox.To do
this, the first thing we’ll need is a field in the Options form to hold a reference
to textbox it’s controlling:
http://www.syngress.com
Navigating in the Designer and Code Editor
 To select the parent of the control you’re on, press Escape.
For example, if you have a TabPage selected, pressing Escape
will select its TabControl, and pressing Escape again will
select the form.
 In the Code Editor, press Ctrl+spacebar to redisplay an
object’s list of members. Press Shift+Ctrl+spacebar to
redisplay its parameters.
 Use the F12 shortcut to jump to a class or member’s
definition.
 Enable Auto Hide on the Output and Task List windows to
see more form and code.
Developing & Deploying…
168 Chapter 4 • Windows Forms
1. Add the following declaration to the OptionsForm class:
public class OptionsForm : TextEditor.DialogForm
{
private TextBox hostControl;
Next, we’ll need some way to get the textbox in, so we can save it to
the class field.The easiest way is through its constructor. Once we have
the textbox, we can also set the initial values for the word wrap and font
size controls.
2. Modify the form’s constructor, as follows:
public OptionsForm (TextBox hostControl)
{
InitializeComponent();
// Save hostControl parameter to class field
this.hostControl = hostControl;
chkWordWrap.Checked = hostControl.WordWrap;
nudFontSize.Value = (decimal) hostControl.Font.Size;
}
When the user clicks OK, we need to update the textbox’s word
wrap and font properties.
3. Double-click on the OK button to attach a Click event handler, and
enter the following:
private void btnOK_Click(object sender, System.EventArgs e)
{
hostControl.WordWrap = chkWordWrap.Checked ;
hostControl.Font = new Font
(hostControl.Font.Name, (float) nudFontSize.Value);
}
The method that displays this form is going to be responsible for
propagating the settings to all other open windows, if the Apply All
checkbox is checked.This means we need to provide a way in which
this checkbox can be queried from outside the class.
http://www.syngress.com
Windows Forms • Chapter 4 169
4. Add a property definition inside the OptionsForm class as follows:
public bool ShouldApplyAll
{
get {return chkApplyAll.Checked;}
}
Finally, we need to make a couple of modifications to EditForm.We
require a property to expose the textbox, and miOptions_Click needs to
be updated so that it passes in the form’s textbox to OptionsForm, and
then checks and handles the “Apply All” scenario.The following below
illustrates how to iterate through MDI child forms. Note that because
the MdiChildren collection consists of plain Form objects, we need to
cast each child into the expected class, so we access its specific properties
(in this case, EditControl ).
5. Make the following changes to EditForm.cs:
public TextBox EditControl
{
get {return txtEdit;}
}
private void miOptions_Click(object sender, System.EventArgs e)
{
OptionsForm of = new OptionsForm (txtEdit);
if (of.ShowDialog() == DialogResult.OK && of.ShouldApplyAll)
foreach (Form child in MdiParent.MdiChildren)
{
TextBox childEdit = ((EditForm) child).EditControl;
childEdit.WordWrap = txtEdit.WordWrap;
childEdit.Font = txtEdit.Font;
}
of.Dispose();
}
This completes the simple text editor.
http://www.syngress.com
170 Chapter 4 • Windows Forms
Using the ListView
and TreeView Controls
Most people are very familiar with Windows Explorer: On the left is a tree view
displaying folders hierarchically; on the right is a list view offering four modes of
display (Large Icons, Small Icons, List, and Detail). In this walkthrough, we’ll
create a ListView and TreeView control, add images and items, and then attach a
context menu to allow the user to switch between each of the four views.Then
we’ll insert an Explorer-style splitter and enable a simple drag-and-drop facility
between the controls.The code for this walkthrough is on the accompanying
CD-ROM, in the WeatherView folder.
Building an ImageList
Before we can set up a list or tree view capable of displaying icons, we need to
create an ImageList component. An image list is just a convenient repository, into
which we can load a collection of same-sized images, and then use in any
number of controls on the form.
In this example, we’ll create two image lists: one suitable for a TreeView and a
ListView’s Small Icons view and another suitable for a ListView’s Large Icons view:
1. Create a new Windows Forms project called WeatherView, and drag an
ImageList from the toolbox to the form. Because it’s a component
rather than a control, its icon appears in the bottom section of the
Designer.
http://www.syngress.com
Using the Console Class
You’ll remember from the second chapter that the Console class provides
Write and WriteLine methods that send output to the screen in
command-line applications. You can call the same methods from a
Windows Forms application, and the text will be diverted to Visual
Studio’s Output window—providing a quick and easy mechanism for
generating debugging output.
Debugging…
Windows Forms • Chapter 4 171
2. Change its Name property to ilSmall, and its ImageSize to 16×16
pixels—this is the size of the small icons we’ll be loading.
3. Next we need to find some images to load in. Search your hard drive
for the Elements folder (this is usually in Program Files\Microsoft Visual
Studio.NET\Common7\Graphics\Icons).
4. Expand the component’s Images collection property, and add four icons
appropriate for Sun, Snow, Clouds, and Rain (see Figure 4.16).
Note that while we’ve loaded images from ICO files, the image list
control stores the data in ordinary bitmap format.
5. Add a new ImageList called ilLarge, change its ImageSize to 32×32
pixels, and repeat the previous steps (using the same icons).
6. Check that the images in the two lists appear in the same order. If not,
use the up and down arrow buttons in the Collection Editor to rearrange
the images.
NOTE
When designing custom graphics for use in an ImageList control, saving
into the GIF format is a good idea, because it provides transparency in
an easy and reliable manner. If you’re using Microsoft Paint in Windows
2000, you can select the transparency color from Image | Attributes
(this option is only enabled once the file’s been saved as a GIF).
http://www.syngress.com
Figure 4.16 Populating an ImageList
172 Chapter 4 • Windows Forms
Adding a ListView
Now that we have the image lists set up, creating a list view is easy:
1. Add a ListView control to the form, setting its LargeImageList property
to ilLarge and its SmallImageList property to ilSmall.
2. Expand its Items property and add four items with text properties: Sun,
Snow, Clouds, and Rain. Set the ImageIndex on each to the corresponding
icon (see Figure 4.17).
The control defaults to the Large Icons view.You can see the Small
Icons view by changing the control’s View property in the Designer.
3. Attach a handler to the control’s ItemActivate event, and add the following
code:
MessageBox.Show (listView1.SelectedItems[0].Text);
Because list views allow multiple items to be selected, the control has
a collection property for this purpose. In this case, we’re interested only
in the first selected item.
4. Run the application and double-click on a list item to test the event
handler.
http://www.syngress.com
Figure 4.17 ListViewItem Collection Editor
Windows Forms • Chapter 4 173
Using the Details View
The Details view allows us to add columns.This is often used in Windows Forms
to provide simple grid control, without with the need for a dataset. In this
example, we’re going to enhance our list view by defining two columns:
1. Change the list view’s View property to Details, and then expand its
Columns collection property. Add two columns, and set their Text properties
to Outlook and Probability.
Once you close the dialog, you can visually resize the columns by
dragging their headers in the Designer.
2. Return to the Items Collection Editor, and for each member, open its
SubItems collection. Add a subitem, and set its Text property to some
random value, such as in Figure 4.18.
We’ll also add an item programmatically.
3. In the form’s constructor, after the call to InitializeComponent, add the
following:
ListViewItem lvi = new ListViewItem
(new string[] { “Hail”, “Possible” } );
listView1.Items.Add (lvi);
4. Run the form (see Figure 4.19).
http://www.syngress.com
Figure 4.18 Adding SubItems to a ListViewItem
174 Chapter 4 • Windows Forms
Attaching a Context Menu
It would be nice if the user could right-click on the list view control, and then
from a menu, select one of the four available views:
1. Add a ContextMenu component to the form, naming it cmView, and
type in four menu items: Large Icons, Small Icons, List, and Details,
as shown in Figure 4.20. Right-click and select Edit Names, and
rename them miLargeIcon, miSmallIcon, miList, and miDetails.
2. Double-click each of the menu items, to create handlers for their Click
events. Code each method as follows (where XXXX is LargeIcon,
SmallIcon, List, or Details):
private void miXXXX_Click(object sender, System.EventArgs e)
{
http://www.syngress.com
Figure 4.19 Details View at Runtime
Figure 4.20 Designing a Context Menu
Windows Forms • Chapter 4 175
listView1.View = View.XXXX;
}
3. Select the cmView component, and in the Properties window, switch to
the Events view and then double-click its Popup event. Here’s where
we’ll tick the selected view:
private void contextMenu1_Popup(object sender,
System.EventArgs e)
{
miLargeIcon.Checked = (listView1.View == View.LargeIcon);
miSmallIcon.Checked = (listView1.View == View.SmallIcon);
miList.Checked = (listView1.View == View.List);
miDetails.Checked = (listView1.View == View.Details);
}
4. Finally, select the list view control, set its ContextMenu property to
cmView, and then test the form.
Adding a TreeView
Setting up a tree view control is rather similar to setting up a list view. First you
create and attach an image list (if icons are required), and then add items to the
tree—either visually or programmatically. In this example, we’ll use one of the
image lists we created earlier:
1. Put a TreeView control on the form, set its ImageList property to
ilSmall.With this control, there’s only one image list, equivalent to the
list view’s Small Icons view.
2. Expand the tree view’s Nodes collection, and add three root nodes for
Sun, Snow, and Clouds.Then add a child node for Rain, below Clouds.
Set their Label, Image, and Selected Image properties as in Figure 4.21.
Now we’ll add an item programmatically.The tree view’s items are
managed through Nodes—a property returning a collection—rather like
with the list view control’s Items property, except that in this case it’s
hierarchical. Nodes itself has itself a Nodes property, returning another
tree node collection. Adding nodes is largely just a question of finding
the right place in the containership tree.
http://www.syngress.com
176 Chapter 4 • Windows Forms
Let’s insert a node as a child to Snow. First, we need to know its
numeric position. Because it’s second in the list, and the list is zeroindexed,
its position is 1.We’ll also give the new node an ImageIndex—
in this case, we’ll use Snow’s image (also position 1).
3. Add the following to the form’s constructor:
// Use snow’s ImageIndex (1) for image & selected image
TreeNode tn = new TreeNode (“Sleet”, 1, 1);
// treeView1.Nodes[1] is the Snow Node.
// We want to add to *its* node collection.
treeView1.Nodes[1].Nodes.Add (tn);
4. Test the form.
NOTE
Sometimes you need to add custom information to list view items or tree
nodes. The easiest solution is to use the Tag property. This property is of
type Object (allowing data of any class to be stored)—and this works in
the same way as the Tag property in the Control class. As an alternative
you can subclass ListViewItem or TreeNode, adding your own fields and
methods, and then instantiating the subclassed versions instead to create
items or nodes. Note that with the latter approach, you cannot then add
your subclassed items or nodes through the Designer.
http://www.syngress.com
Figure 4.21 TreeNode Editor
Windows Forms • Chapter 4 177
Adding a Splitter
Let’s now add an Explorer-style splitter bar between the tree view and list view
controls. Getting a splitter to work is largely about getting all the controls in the
correct front-to-back order (z-order). In a nutshell, we need the following:
 A side-docked control, at the back of the z-order
 A splitter control, docked to the same side, in the middle of the z-order
 A fill-docked control, at the front of the z-order
We already have the two controls we want to split—all that’s required is the
splitter control, and of course, everything in the right z-order.
1. Set the tree view’s Dock property to Left (click the leftmost rectangle
in the drop-down).This pushes it up hard against the left-hand side of
the form.
2. Add a Splitter control from the toolbox, and change its Dock property
to Left (if not already docked left). Because we’ve just put it on the
form, it’ll be in front of the tree view, and will appear to its right.
3. Set the list view’s Dock property to Fill (click the center rectangle in the
drop-down) and then right-click the control and select Bring to
Front. Now it’ll be at the front, with the splitter in the middle, and the
side-docked tree view at the back.
4. Test the application.The controls will automatically resize as you drag the
splitter (and also when you resize the form), as shown in Figure 4.22.
http://www.syngress.com
Figure 4.22 Splitter Control at Runtime
178 Chapter 4 • Windows Forms
Implementing Drag and Drop
When demonstrating the list view and tree view controls, it’s hard to put them
side-by-side without someone asking about drag and drop.The good news is that
dragging between these controls is reasonably easy to implement in Windows
Forms.
WARNING
When you create a Windows Forms project, Visual Studio adds the
[STAThread] attribute to the startup form’s Main method. This tells the
compiler to apply the Single Threaded Apartment threading model,
allowing your application to interoperate with other Windows and COM
services. If you remove this attribute, features such as drag and drop will
not work—even between controls within your own application.
Let’s take a look at drag and drop in general.As you might guess, it consists of
two parts. In the first part, you need to identify when the user starts dragging the
mouse, and then ask Windows to start the operation, supplying data necessary for
the recipient when processing the drop. In Windows Forms, this is done as follows:
1. Decide from which event to start the operation. If you’re dragging from
a list view or tree view control, it’ll be the special event called ItemDrag.
With other controls, it will usually be the MouseDown or MouseMove
event.
2. Package information to be sent to the target in a DataObject. If you want
to interoperate with another Windows application, you must use one or
more of the standardized formats listed in the DataFormats class, such as
Text or HTML.
3. Decide on what actions (such as Move or Copy) are permitted.You can’t
always be sure at this point on what will end up happening, because it
could depend on where the item is dropped.
4. Call DoDragDrop to start the operation.
The second part is about enabling a target control to accept a drop. In
Windows Forms, this is done as follows:
1. Set the target’s AllowDrop property to True.
http://www.syngress.com
Windows Forms • Chapter 4 179
2. Handle the DragEnter or DragMove event. DragEnter fires just once when
the cursor enters the control; DragMove fires continually as the cursor
moves through the control. In the event handler, you need to decide if
the drop is allowable—and this is done by checking that the packaged
data is of an expected type. If so, you set the DragEventArg parameter’s
Effect property to one of the permitted actions, and this enables the drop
(changing the cursor accordingly).
3. Handle the DragDrop event.To get at the packaged data, you first need
to extract it and cast it back into its original type.
NOTE
The advantage of passing a DataObject to DoDragDrop is that you can
include data in multiple formats, allowing external applications, such as
Microsoft Word, to function as drop targets. Standard formats are
defined (as static public field) in the DataFormats class.
In our example, we’re going to allow dragging from the tree view to the
list view:
1. Double-click the tree view’s ItemDrag event, and type the following:
treeView1.DoDragDrop (e.Item, DragDropEffects.Move);
The first parameter is our package of information. Because we’ve not
wrapped it in a DataObject,Windows Forms does this for us automatically,
as if we did the following:
treeView1.DoDragDrop (new DataObject (e.Item),
DragDropEffects.Move);
e.Item is the actual data we want to send to the target: in this case the
TreeNode we’re dragging.The second parameter describes the allowed
actions: In this example, we’re going to allow only moving.
2. Set the list view’s AllowDrop property to True.
3. Double-click the list view’s DragEnter method, and type the following:
private void listView1_DragEnter(object sender,
System.Windows.Forms.DragEventArgs e)
{
http://www.syngress.com
180 Chapter 4 • Windows Forms
if (e.Data.GetDataPresent (typeof (TreeNode)))
e.Effect = DragDropEffects.Move;
}
e.Data returns the packaged information, as a DataObject. Regardless
of how the data went in when we called DoDragDrop, we always get
back a DataObject.This class is designed to hold information in multiple
formats, and we call its GetDataPresent method to find out if a particular
type of data is supported.
4. Double-click the list view’s DragDrop event, and type the following:
private void listView1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent (typeof (TreeNode)))
{
TreeNode tn = (TreeNode) e.Data.GetData
(typeof (TreeNode));
listView1.Items.Add (tn.Text, tn.ImageIndex);
treeView1.Nodes.Remove (tn);
}
}
We use the data object’s GetData method to retrieve our original
data, and then cast it back to the original type. Once this is done, we can
treat it again as a normal TreeNode.
5. Test the application.You’ll now be able to drag items from the tree view
to the list view.
http://www.syngress.com
Dragging Into a Tree View
If setting up to drag into a tree view, you might want the dropped item
to be inserted into the tree at the position under the mouse pointer. For
this, you first need to determine which tree node is positioned under the
mouse, as follows:
Developing & Deploying…
Continued
Windows Forms • Chapter 4 181
Creating Controls
Sometimes your requirements demand extending or replacing standard Windows
Forms controls. It could be that your requirements are specific to a particular
application—or they could warrant developing a general-purpose component for
use in thousands of applications.Writing and deploying custom components is
easy, because .NET components are self-describing, they don’t require registration,
and are not accidentally overwritten by subsequent software installations.
Let’s look at the three most common scenarios:
 You have a recurring group of controls that you would like to make into
a reusable component (a UserControl ).
 You need a control that cannot be assembled or adapted from existing
components (a custom control).
 You want to extend a standard control—in order to modify or enhance
its appearance or behavior (an inherited control).
In the following sections, we’ll walk through solutions to each of the scenarios.
Creating a User Control
Suppose your application contains several forms with a group of controls for
entering an address. Assembling these controls into a reusable class would be
nice—both for visual consistency, and so that common functionality can be
added, such as postcode lookup. In this walkthrough, we’ll create a user control to
show how to do this:
1. Create a new Windows Forms project, and then choose Project | Add
User Control, naming the file Address.cs.
2. Add a group of controls suitable for entering an address, such as in the
example in Figure 4.23.
http://www.syngress.com
void treeView1_DragDrop(object sender, DragEventArgs e)
{
Point pos = treeView1.PointToClient (new Point (e.X, e.Y));
TreeNode tn = treeView1.GetNodeAt (pos);

182 Chapter 4 • Windows Forms
3. Build the project (Shift+Ctrl+B) and return to Form1.At the bottom
of the toolbox, in the Windows Forms tab, will be a new control called
Address. Add this to Form1 and then run the application.
Adding a Property
Our address control is not much use because there’s no way for the form to
determine what the user typed in. For this, we need to add properties to our
control. Here’s how we add a property to allow access the contents of the Street
textbox:
1. Add the following declaration to the Address class:
[Category (“Data”), Description (“Contents of Street Control”)]
public string Street
{
get {return txtStreet.Text;}
set {txtStreet.Text = value;}
}
The first line is optional—it specifies Category and Description
attributes, to enhance the control’s presentation in the Designer.Without
the Category attribute, the property would appear in the “Misc” section
in the Properties window.
2. Rebuild the project, and return to Form1.The address control now has a
Street property into which you can type. Of course, it can also be
accessed programmatically as with any other control property.
Adding Functionality
Once the control has been set up, it’s fairly easy to modify its class so as to add
reusable functionality, such as postcode lookup. It’s just a matter of capturing
events such as TextChanged or Validating and then updating the properties of other
http://www.syngress.com
Figure 4.23 UserControl in Designer
Windows Forms • Chapter 4 183
controls accordingly.We don’t provide an example, because it doesn’t introduce
aspects of Windows Forms we haven’t already covered. However, it’s worth mentioning
that in a real situation you would consider good object-oriented design,
and abstract the postcode-lookup functionality into a class separate from the user
interface.You could also consider basing this class on a (C#) interface—to which
the user control would be programmed.This would allow the control to plug in
to different implementations (to facilitate internationalization, for instance).
Writing a Custom Control
If your needs are more specialized, you can paint a control from scratch with
GDI+. In principle, this is fairly simple:You subclass Control, and then override its
OnPaint method, where you render the graphics.You can also capture mouse and
keyboard events by overriding methods such as OnMouseDown and OnKeyPress.
NOTE
Every event had a corresponding protected method, prefixed with the
word On. Some people have asked about the difference between handling
the event (such as Paint) and overriding the protected OnXXXX
method (such as OnPaint). There are a number of differences:
 Overriding the protected method is faster because the CLR
doesn’t have to traverse an event chain.
 Because the protected method fires the event, you can effectively
snuff the event simply by failing to call base.OnXXXX.
 Events can be attached and detached at runtime; code in overridden
OnXXXX methods always runs.
 When subclassing a control, you generally override protected
methods rather than handling events.
GDI+ is accessed through a Graphics object—a representation of a drawing
surface, with methods to draw lines, shapes, and text. GDI+ is stateless, meaning
that a graphics object doesn’t hold properties to determine how the next object
will be drawn (the “current” color, pen, or brush)—these details are supplied with
each call to a GDI+ drawing method.Tables 4.3 and 4.4 summarize the most
common GDI+ helper types.
http://www.syngress.com
184 Chapter 4 • Windows Forms
Table 4.3 Commonly Used GDI+ Helper Types
Type Description
Color struct Represents an RGB or ARGB color (where A represents alpha,
or transparency). Also used in Windows Forms.
Font class Represents a font consisting of a name (referred to as the
font “family”), a size, and a combination of styles (such as
bold or italic). Also used in Windows Forms.
Brush class Describes a fill color and style for areas and shapes. A brush
can consist of solid color, graded color, a bitmap, or hatching.
Pen class Describes a line color and style. A pen has a color, thickness,
dash-style, and can itself contain a brush describing how the
line should be rendered.
http://www.syngress.com
Table 4.4 Instantiating GDI+ Helper Types
Type Example Notes
Color
Font
Creates a color from its
red, blue, and green
intensities (0 to 255).
The alpha component
is optional and specifies
opacity: 0 is
totally transparent;
255 is totally opaque.
gray is defined above.
Green is a static property
of the Color
struct.
Use this class if the
color you need is part
of the Windows color
scheme.
When specifying font
styles, use the bitwise
OR operator (|) to
combine members of
the enumeration.
There are 13 ways to
call Font’s constructor.
Color gray = Color.FromArgb
(192, 192, 192);
Color blueWash = Color.FromArgb
(80, 0, 0, 128);
Color grayWash = Color.FromArgb
(80, gray);
Color green = Color.Green;
Color background = SystemColors.Control;
Color foreground =
SystemColors.ControlText;
Font f1 = new Font (“Verdana”, 10);
Font f2 = new Font (“Arial”, 12,
FontStyle.Bold | FontStyle.Italic);
Font f3 = new Font (f2,
FontStyle.Regular);
Continued
Windows Forms • Chapter 4 185
In this walkthrough, rather than defining our control as part of a Windows
Forms project, we’ll make a class library—so our control can be used in a
number of different applications:
1. From File | New Project, choose the Windows Control Library
template, calling the library FunStuff.Visual Studio assumes we’ll start
with a user control. However in this case we want a custom control.
2. From the Solution Explorer, delete UserControl1.The project should now
be empty.
http://www.syngress.com
Table 4.4 Continued
Type Example Notes
Brush
Pen
Returns a solid blue
brush.
The preferred way to
obtain brushes consistent
with the
Windows color
scheme.
The Brush class itself is
abstract; however, you
can instantiate its subclasses
such as
SolidBrush or
HatchBrush.
Creates a violet pen
with thickness of one
pixel.
The preferred way to
obtain pens consistent
with the Windows
color scheme.
A beige pen 30 pixels
wide.
A pen 20 pixels wide
drawn with the crisscross
brush (defined
earlier in this table).
Brush blueBrush = Brushes.Blue;
Brush border =
SystemBrushes.ActiveBorder;
Brush grayBrush = new SolidBrush
(this.BackColor);
Brush crisscross = new HatchBrush
(HatchStyle.Cross, Color.Red);
Pen p = Pens.Violet;
Pen ht = SystemPens.HighlightText;
Pen thick = new Pen (Color.Beige, 30);
Pen ccPen = new Pen (crisscross, 20);
186 Chapter 4 • Windows Forms
3. From Project | Add New Item, select Custom Control. Name the
file ScrollingText.cs.
4. Switch to the Code View. Notice that Visual Studio has based our class
on Control, and that it has added code to overwrite the OnPaint method.
This is where we use GDI+ to draw the control—for now, we’ll just fill
the control’s area with red, and then draw a green ellipse in the middle.
5. Enter the following code into the overridden OnPaint method:
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.Clear (Color.Red);
Brush b = Brushes.Green;
pe.Graphics.FillEllipse (b, ClientRectangle);
b.Dispose();
base.OnPaint (pe);
}
The PaintEventArgs parameter contains a Graphics object used to
access GDI+ methods—such as Clear and FillEllipse.We use static properties
of the Color and Brushes types as shortcuts to creating Color and
SolidBrush objects. ClientRectangle describes the inside bounds of the
control based on a (0, 0) origin. (In this case, the inside and outside areas
are equivalent because there are no Windows-imposed borders or scrollbars).
We call base.OnPaint so that the Paint event still fires—in case the
end user of our control wants to attach to this event for any reason.
6. Build the project.We now have a custom control (ScrollingText) as part of
a reusable library (FunStuff ).
http://www.syngress.com
Using GDI+ to Draw Custom Controls
To obtain a Graphics object:
 From within a subclassed OnPaint method, use
PaintEventArgs parameter’s Graphics property.
Developing & Deploying…
Continued
Windows Forms • Chapter 4 187
Testing the Control
Now that we’ve built the custom control, we can use it two different ways:
 From a new Windows Forms project, we can add the compiled custom
control to the toolbox.We do this by right-clicking the toolbox,
selecting Customize Toolbox, and from the .NET Framework
Components tab, clicking Browse and locating the Control Library
DLL (in our case, FunStuff\bin\debug\FunStuff.dll).The component
(ScrollingText) will then appear in the list, and if checked, will be added
to the toolbox.
 We can create a solution containing two projects: both the Control
Library and a new Windows Forms project.
Normally, you opt for the second approach if you are still developing the
control (and have access to its project file and source code), because it means you
can more easily make any necessary changes.This is what we’ll do in our
example:
1. From the Solution Explorer, right-click on the solution and select Add
| New Project.Then choose the Windows Application template,
naming the project TestFunStuff.
http://www.syngress.com
 From outside an OnPaint method, use Control.CreateGraphics
or Graphics.FromImage. It’s not often that you should need
to access GDI from outside OnPaint—an example is the use
of MeasureString to calculate how many pixels are required
to display a string in a given font. Remember to call Dispose
when you’re finished.
To draw a bitmap, create an image using the Bitmap class’s constructor,
and call DrawImage, for example:
Image im = new Bitmap (@”c:\docs\pics\mypic.bmp”);
pe.Graphics.DrawImage (im, ClientRectangle);
im.Dispose();
To repaint the control, call Invalidate.
To draw 3D borders, sizing handles, selection frames, disabled text,
and images, use static methods provided in the ControlPaint class.
188 Chapter 4 • Windows Forms
2. Locate the ScrollingText control in the toolbox, and drag it to Form1. If
the control is not in the toolbox, rebuild the project and look again. If it
still doesn’t appear, right-click the toolbox, select Customize ToolBox,
and from the .NET Framework Components tab, click Browse and
locate the Control Library DLL (try FunStuff\bin\debug\FunStuff.dll),
and then check the ScrollingText component in the list.
You’ll notice that as you resize the control in the Designer, it won’t
render properly because the control doesn’t assume it needs to be
redrawn when resized.We can resolve this in two ways:We can override
its OnResize method, calling Invalidate (marking the control “dirty” so
that it gets redrawn), or in the control’s constructor we can set a special
flag to have this happen automatically. Let’s take the latter approach:
3. Modify the control’s constructor as follows:
public ScrollingText()
{
SetStyle (ControlStyles.ResizeRedraw, true);
}
4. Rebuild the project and return to Form1. It will now render properly in
the Designer when resized (see Figure 4.24).
5. Finally, we should test the form at runtime. Because we started out creating
a control library, the startup project will be a DLL—which can
only compile and not run.We can change this from the Solution
Explorer: Right-click the TestFunStuff project, select Set as Startup
Project, and then run the application.
http://www.syngress.com
Figure 4.24 Custom Control in Designer
Windows Forms • Chapter 4 189
Enhancing the Control
Let’s turn this custom control into a real-world example: a scrolling text banner.
This is easier than it sounds: it’s simply a matter of maintaining a text string, to
which with a Timer, we periodically remove a character from the left—and add
to the right.The text is rendered using DrawString in the Graphics class, using a
graded brush for effect.We can also allow the user to start and stop the animation
by overriding the control’s OnClick method.The code for the ScrollingText control
is on the accompanying CD-ROM, in the FunStuff folder. Here’s the complete
code listing:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace FunStuff
{
public class ScrollingText : System.Windows.Forms.Control
{
Timer timer; // this will animate the text
string scroll = null; // the text we’re going to animate
public ScrollingText()
{
timer = new Timer();
timer.Interval = 200;
timer.Enabled = true;
timer.Tick += new EventHandler (Animate);
}
void Animate (object sender, EventArgs e)
{
// Create scroll string field from Text property
http://www.syngress.com
190 Chapter 4 • Windows Forms
if (scroll == null) scroll = Text + ” “;
// Trim one character from the left, and add it to the right.
scroll = scroll.Substring (1, scroll.Length-1)
+ scroll.Substring (0, 1);
// This tells Windows Forms our control needs repainting.
Invalidate();
}
void StartStop (object sender, EventArgs e)
{ timer.Enabled = !timer.Enabled; }
// When Text is changed, we must update the scroll string.
protected override void OnTextChanged (EventArgs e)
{
scroll = null;
base.OnTextChanged (e);
}
protected override void OnClick (EventArgs e)
{
timer.Enabled = !timer.Enabled;
base.OnClick (e);
}
public override void Dispose()
{
// Since the timer hasn’t been added to a collection (because
// we don’t have one!) we have to dispose it manually.
timer.Dispose();
base.Dispose();
}
http://www.syngress.com
Windows Forms • Chapter 4 191
protected override void OnPaint(PaintEventArgs pe)
{
// This is a fancy brush that does graded colors.
Brush b = new System.Drawing.Drawing2D.LinearGradientBrush
(ClientRectangle, Color.Blue, Color.Crimson, 10);
// Use the control’s font, resized to the height of the
// control (actually slightly less to avoid truncation)
Font f = new Font
(Font.Name, Height*3/4, Font.Style, GraphicsUnit.Pixel);
pe.Graphics.DrawString (scroll, f, b, 0, 0);
base.OnPaint (pe);
b.Dispose(); f.Dispose();
}
}
}
Figure 4.25 illustrates the control in the test form, at design time, with its Text
and Font properties set. A nice touch in Visual Studio is that the control animates
in the Designer.
Subclassing Controls
Once we’ve designed a user control or custom control we can use inheritance to
subclass it in the same way we did with our reusable dialog earlier in the chapter.
You can also inherit from a standard control such as a TextBox or Button—in
http://www.syngress.com
Figure 4.25 Completed Scrolling Text Control in Designer
192 Chapter 4 • Windows Forms
order to modify its appearance or behavior without going to the trouble of
designing a new control from scratch.
Visual Studio distinguishes between inheriting user controls and custom controls.
You can create an inherited user control directly—from Project | Add
Inherited Control—whereas to create an inherited custom control (or to subclass
a standard control) you need to write the class manually.The easiest way to
go about this is to ask Visual Studio to create a custom control, and then in the
Code Editor, to edit the control definition’s base class.
To take an example, suppose your marketing department demands customizable
“skins” in your Windows application. One approach (other than skinning the
marketing department!) is to subclass some of the standard controls, such as
Button and Label.The challenge would then be to decorate the controls without
upsetting their existing graphics. Let’s walk through this briefly:
1. From a new or existing Windows Forms project, go to Project | Add
New Item and select Custom Control.
2. Switch to the Code Editor, and change the class definition so that we’re
subclassing Button instead:
public class WashedButton : System.Windows.Forms.Button
Now we need to override OnPaint. First, we’ll have to invoke the
base class’s code so that it renders the button.Then we’ll “wash” the
control with a linear gradient brush—the same brush used in the
scrolling text example, except that we’ll use translucent colors so as not
to erase the existing graphics.This is called alpha blending and activated
simply by using a color with an alpha-value.
3. Update the OnPaint method as follows:
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint (pe);
// Create two semi-transparent colors
Color c1 = Color.FromArgb (64, Color.Blue);
Color c2 = Color.FromArgb (64, Color.Yellow);
Brush b = new System.Drawing.Drawing2D.LinearGradientBrush
(ClientRectangle, c1, c2, 10);
pe.Graphics.FillRectangle (b, ClientRectangle);
b.Dispose();
http://www.syngress.com
Windows Forms • Chapter 4 193
}
4. Build the project, and put the custom control onto a form as we did in
the previous walkthrough (see Figure 4.26).
Custom Controls in Internet Explorer
The scrolling textbox we wrote in the “Writing a Custom Control” section is begging
to be hosted on a Web page. Its compiled assembly is small enough to be
downloaded over the Internet in a couple of seconds, and is secure because .NET
code runs in a managed environment, rather like Java’s virtual machine. In fact,
both C# and Windows Forms have their roots in Java—their technical predecessors
being J++ and its supplied Windows Foundation Classes. Of course,Windows
Forms applets require that the client has a Windows operating system—with the
.NET runtime installed. It also requires support at the server-end.
Setting Up IIS
Before starting this walkthrough, you need to check that Internet Information
Services is installed and running. IIS is shipped with Windows 2000 Professional—
you can check that it’s present and install it from the Control Panel (go to
Add/Remove Programs | Windows Components and locate Internet
Information Services).To check that it’s running, choose Administrative
Tools | Services from the Control Panel and locate the World Wide Web
Publishing Service.
Creating a Virtual Directory
We’ll need a Virtual Directory to host the Web page and applet.This is done
using the Internet Services Manager—in Windows 2000, we can access this
through the Control Panel, under Administrative Tools.
http://www.syngress.com
Figure 4.26 Subclassed Buttons in Designer
194 Chapter 4 • Windows Forms
1. From Internet Services Manager, expand your computer’s icon and
right-click Default Web Site choosing New | Virtual Directory.
The Virtual Directory Wizard will then appear.
2. When prompted for an alias, type FunStuff.
3. It will then ask for a directory:When testing, it’s easiest to specify the
folder where Visual Studio compiles the component library’s DLL (for
example, FunStuff\bin\debug).
4. The wizard will then prompt for Access Permissions. Check Read and
Run Scripts and uncheck everything else.
Writing a Test Page
We’ll need to create an HTML page to test the component.We can do this
either in Visual Studio or with an independent HTML or text editor program
such as Notepad. In this example, we’ll use Visual Studio’s editor. Note that the
file will have to be in the folder specified when creating the virtual directory:
1. From Visual Studio, reopen the FunStuff solution. Click on the
FunStuff project in the Solution Explorer, then click the Show All
Files icon. Expand the folder containing the compiled DLL (for
example, bin\debug), right-click the folder, and select Add | Add
New Item and use the HTML Page template, naming the file
test.htm.
2. Switch to the HTML view and add the following, inside the <BODY>
section of the page:
<p> Testing our control! </p>
<object id=”test”
classid=”http:funstuff.dll#FunStuff.ScrollingText”
height=”50″ width=”500″>
<param name=”Text” value=”The quick brown fox…”>
</object>
This is rather like inserting a Java applet or ActiveX control. For the
Class ID, we specify the DLL containing the custom control, followed by
the class’s full name, including its namespace (note that this is case-sensitive).
The object size is specified in pixels—it’s for this reason that when
http://www.syngress.com
Windows Forms • Chapter 4 195
writing the control, we created a font matching the height of the control,
rather than the other way round.
3. The final step is viewing the test page. Start Internet Explorer, and open
http://localhost/FunStuff/test.htm (see Figure 4.27).
http://www.syngress.com
Figure 4.27 Custom Control in Internet Explorer
196 Chapter 4 • Windows Forms
Summary
Tools for writing Windows applications have come a long way since the early
days of Visual Basic and C++; in this chapter we examined Windows Forms,
Microsoft’s modern object-oriented solution.
One of the benefits of an object-oriented framework is consistency:The same
core classes, interfaces, and protocols are used repeatedly throughout the framework.
And at the heart of consistency is inheritance:A combo box supports all
the functionality of a control, which supports all the functionality of a component.
This means that most of the objects we worked with here, were in effect,
components, and it was by virtue of this we could manipulate them in the
Designer, and know that they would be disposed automatically with the host
control.
We dealt in this chapter with many components that hosted child objects:
Forms that hosted controls, tab controls that hosted tab pages, menus that contained
menu items, tree view controls that contained nodes. In all cases, the child objects
were managed through a property returning a collection object—implementing
consistent interfaces for adding, removing, and enumerating the members.
A Windows application is a consumer of events, and we saw in this chapter
how this is modeled in Windows Forms through C# events and delegates. In
adding an event handler programmatically, we saw how to instantiate a delegate
object—a pointer to a method of an agreed signature, and then how it’s attached
to an event, using the += operator.
In writing a text editor, we discovered the default behavior of newly activated
forms—modeless and top-level. But by changing a few properties, we created a
multiple document interface (MDI) application, and later on we saw how we
could use the MdiChildren collection property to enumerate the child forms.We
also created a modal dialog, by building a form with the “look and feel” of a
dialog, and then activating it with ShowDialog.
The anchoring and docking features make it easy to design forms that can be
usefully resized.We found that anchoring was useful in creating a sizable dialog
form, and docking was required when setting up a list view/tree view/splitter
combination. Because docking space is allocated on a “first-come, first-served”
basis—where controls at the back of the z-order are first—we needed to ensure
the z-order of the participating controls was correct.
Windows Forms also provides access to operating system features such as drag
and drop, and we looked briefly at a common scenario—calling DoDragDrop
from a list view’s ItemDrag event; seeing how a DataObject is marshaled to the
http://www.syngress.com
Windows Forms • Chapter 4 197
recipient; and discussing how the DragEnter and DragDrop events on the target are
handled to enable the operation.
The .NET Framework’s object-oriented model is extensible, and by subclassing
the Windows Forms components and controls—as well as our own, we
can start creating reusable classes. In the text editor example, we built a reusable
dialog; later on we subclassed various Windows Forms classes to build custom
controls.We derived from UserControl to create a composite of existing components,
while we derived from Control to create a custom control on par with a
label or textbox.
In our custom control, we used the stateless graphics device interface (GDI+)
to render the graphics, through a Graphics object exposed in the PaintEventArgs
parameter within the control’s OnPaint method. Many of the GDI+ methods,
such as DrawLine or FillRectangle, accept as parameters helper objects, such as
pens, brushes, or images.These objects we created and disposed explicitly.
In the last walkthrough, we hosted a custom control in Internet Explorer.
This raised the issue of C# and Windows Forms as an alternative to Java for
Internet applets—this is currently limited by its requirement for the (less
portable) .NET Common Language Runtime (CLR) on the client machine.
However, it does illustrate how Windows Forms programs can compile to small
executables that can run securely, requiring no special setup or deployment.These
features—combined with other benefits provided by C# and the .NET CLR—
make the platform a good choice for developing modern Internet-connected
Windows applications.
Solutions Fast Track
Writing a Simple Windows Forms Application
􀀻 Use Visual Studio’s Windows Forms project template to create the
structure of a Windows application.
􀀻 Add controls and components visually using the Designer, and then use
the Properties window to configure the objects and add event handlers.
􀀻 To add controls programmatically, first declare, instantiate, and configure
the controls, then add them to the form or parent container’s Controls
collection.
http://www.syngress.com
198 Chapter 4 • Windows Forms
􀀻 To attach an event handler at runtime, define a method matching the
event delegate’s signature, then attach a delegate instance wrapping the
method to the event using the += operator.
Writing a Simple Text Editor
􀀻 To add a main menu, use the MainMenu component—and then enter its
items visually in the Designer.
􀀻 A new form is displayed by instantiating its class and calling Show.This
results in a top-level modeless form.
􀀻 To implement a multiple document interface, set the parent window’s
IsMdiContainer property to True, and then assign each child’s MdiParent
property to the parent form.
􀀻 Use form inheritance to encapsulate common functionality. But keep
the abstractions simple to minimize coupling and complexity.
􀀻 Use a TabControl to simplify forms with many controls.
􀀻 Use the anchoring and docking features to create resizable forms.
􀀻 Define public properties in a form’s class to expose its controls to other
forms or classes.
􀀻 Use the MdiChildren collection of an MDI parent to traverse its child
forms.
Using the ListView and TreeView Controls
􀀻 First set up one or more ImageList components if icons are required in
the ListView or TreeView.
􀀻 Add items to a ListView control through its Items collection property.
􀀻 Use the ListView’s details view for a multicolumn grid-like control.
􀀻 Add items to a TreeView through its Nodes collection property. Subnodes
can also be added to each node in the same way.
􀀻 To configure a splitter control, first set the docking properties of the
participating controls, then arrange their z-order.
􀀻 Start a drag-and-drop operation by calling DoDragDrop from the Itemdrag
event on the source control, passing any data required by the recipient.
http://www.syngress.com
Windows Forms • Chapter 4 199
􀀻 Enable a drop target by setting its AllowDrop property to True, and then
handling its DragEnter and DragDrop methods.
Creating Controls
􀀻 To encapsulate a reusable group of controls, build a UserControl and then
add properties to enable access to its data.
􀀻 When you need to start from scratch, define a custom control—overriding
the Control class’s OnPaint method to render its graphics, using GDI+.
􀀻 When using GDI+, remember to dispose any Pens and Brushes that
you create.
􀀻 Utilize inheritance to enhance existing controls, overriding their
methods to add or change functionality.
􀀻 Use the object tag to insert controls from component libraries into
HTML pages, specifying the assembly’s DLL and the control’s fully
qualified name.
Q: Can I easily rename a solution or project?
A: Yes—right-click from the Solution Explorer and choose Rename.You’ll
also have to edit the namespace declarations in any source code files you’ve
created.
Q: How do I detach an event handler?
A: In the same way you attach an event handler, except using the -= operator.
For example:
button1.Click -= new EventHandler (button1_Click);
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.
200 Chapter 4 • Windows Forms
Q: Where is image data loaded into ImageList controls actually stored in the
project?
A: Each form has an associated resource file (with a .resx extension) where
image data and localized strings are stored.You can see this by clicking the
Show All Files icon in the Solution Explorer.
Q: Can a textbox control contain text in more than one color or font?
A: No. For this you need to use the RichTextBox control.
Q: How can I add icons to menu items?
A: Unfortunately there is no built-in feature for this.You need configure the menu
items to be owner-drawn (set OwnerDraw to True) and then use GDI+ to
draw both the text and graphics (handle the MeasureItem and DrawItem events).
Microsoft’s “Got Dot Net” site (www.gotdotnet.com) is a good place to start
for information about implementing owner-drawn controls.
Q: What’s the difference between tab-order and z-order?
A: Tab-order describes the order of focus as the user moves between controls
using the Tab and Shift+Tab keys.This is determined by the control’s
TabIndex property—set either in the Properties window, or by selecting View
| Tab Order and then clicking each control in order. Z-order describes the
front-to-back order of controls, and this set in the Designer using the Bring
to Front and Send to Back layout options. Z-order matters when controls
visually overlap and also when docking:Those at the back of the z-order will
be assigned docking space first.
Q: I need to determine the current mouse position and state of the Shift,
Control, and Alt keys—but I’m not inside an event handler that provides
this information. How can it be done?
A: Use the MousePosition, MouseButtons, and ModifierKeys static properties of the
Control class.
Q: How can I screen input in a textbox?
A: For this, it’s usually best to start by subclassing the TextBox control so that
your solution is reusable within your project (or outside your project).
Override OnTextChanged, or for more control, OnKeyPress and OnKeyDown.
http://www.syngress.com
Windows Forms • Chapter 4 201
OnKeyPress fires for printable characters; OnKeyDown fires for all key combinations.
Both of these offer a Handled property in the event arguments
parameter, which you can set to True to cancel the event.
Q: When designing an inherited or custom control, can I trap windows messages
such as WM_PASTE?
A: Yes—by overriding the WndProc method.
Q: Is there a Windows Forms newsgroup where I can get help?
A: Microsoft provides a newsgroup: microsoft.public.dotnet.framework
.windowsforms.
http://www.syngress.com

Network
Programming:
Using TCP and UDP
Protocols
Solutions in this chapter:
 Introducing Networking and Sockets
 Example TCP Command Transmission
and Processing
 Example UDP Command Transmission
and Processing
 Creating a News Ticker Using
UDP Multicasting
 Creating a UDP Client Server
Chat Application
 Creating a TCP P2P File
Sharing Application
 Access to Web-Resources
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 5
203
204 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Introduction
Networking can be defined, in a general sense, as inter-process communication.Two or
more processes (such as applications) communicate with each other.The processes
can run on the same or different computers or other physical devices. Connections
between the network nodes are made mostly by a wire (such as local area network
[LAN], wide area network [WAN], and Internet); by wireless via radio frequencies
(such as cell phone, wireless appliances, wireless LAN, Internet, and Bluetooth); or
by infrared (IR) light (such as a cell phone to a laptop).
In this chapter, we cover the basics of networking and how it is accomplished
with C#.We start out with some theory, covering a little bit about the history of
networking and the Internet and sockets; then we discuss commonly used protocols
such as the Transmission Control Protocol (TCP) and User Datagram
Protocol (UDP). Subsequently, we have a look at ports and their uses.The last
point of theory is to get to know the .NET classes we use.
Keeping theory in mind, we then go into practice. First, we develop some
very simple communication examples using TCP and UDP.Then we develop a
multicast news ticker.We have a look at a chat server and client, where we combine
the learned techniques. After all the client/server-applications, we develop a
P2P file sharing system, and finally, we show how you can use special .NET
classes that encapsulate the networking.
Introducing Networking and Sockets
In the sixties, researchers of the Advanced Research Projects Agency (ARPA) in
the U.S. were requested by the Department of Defense (DoD) to develop a
system for saving information military important in case of a war.The result of
their work was an electronic network—the ARPAnet. Military information was
stored on all computers that were part of the network.The computers were
installed in different places far away from each other and information was
exchanged in several different ways. New or updated data on the computers was
to be synchronized in a very short time so that in case of the destruction of one
or more computers, no data would be lost.
In the 1970s, the DoD allowed nonmilitary research institutes to access the
ARPAnet.The researchers were more interested in the connected computers
than in synchronizing data.They used it for exchanging information, and students
at these institutes used a part of the network as a blackboard for communicating
with each other—this was the beginning of Usenet.
http://www.syngress.com
http://www.syngress.com
In the 1980s, the military and civil parts of the ARPAnet were divided. In other
countries, similar activities led to national networks. At the end of the 1980s, most
of the national networks became connected to each other.The Internet was born.
It was necessary to have a standardized way to communicate over different
types of networks and with different kinds of computers. So TCP/Internet
Protocol (TCP/IP), which was developed by ARPA, became a worldwide standard.
TCP/IP is a “protocol family” that allows connected computers to communicate
and share resources across a network. (TCP and IP are only two of the protocols
in this family, but they are the most widely recognized. Other protocols in
this set include UDP.) For all protocols provided by .NET, have a look at the
.NET reference documentation (class System.Net.Sockets.Socket).
To access IP-based networks from an application, we need sockets.A socket is
a programming interface and communication endpoint that can be used for connecting
to other computers, sending and receiving data from them. Sockets were
introduced in Berkeley Unix, which is why sockets are often called Berkeley
Sockets. Figure 5.1 shows the general architecture of IP-based communication.
Generally, three types of sockets exist:
 Raw sockets This type is implemented on the network layer (see
Figure 5.2). An example for a protocol on this layer is IP.
 Datagram sockets Datagrams are packets of data.This type of sockets
is implemented on the transport layer (see Figure 5.2). However, the
assignment to a layer is not strict, because, for instance, IP is also datagram-
oriented.We go more in detail on this type of sockets later in this
section.
 Stream sockets In contrast to datagram sockets, these sockets provide
a stream of data.We go into more detail on this type of sockets later in
this section.
Network Programming: Using TCP and UDP Protocols • Chapter 5 205
Figure 5.1 General Communication Architecture
Computer
Application
Socket
Computer
Application
Socket
206 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Modern communication architectures use a stack of different protocol layers
where data is given to the top layer. Each layer is adding layer-specific protocol
information to the data and then it is given to the next layer.The lowest layer is
sending the data to another process running on another computer (or maybe the
same computer) where the data goes up in the same stack of layers. Each layer
removes the protocol-specific information until the application layer is reached.
Figure 5.2 shows such a stack.
The application layer can be divided into sublayers.You may think of an
application using the XML-based Simple Object Access Protocol (SOAP) using
the Hypertext Transfer Protocol (HTTP) for sending SOAP commands in XML.
This is called HTTP tunneling and is used especially with firewalls, so that the
firewalls do not have to be reconfigured for passing through SOAP.
Introduction to TCP
The Transmission Control Protocol is a connection- and stream-oriented, reliable
point-to-point protocol.TCP communication is analogous to a phone call.You
(the client) may want to talk with your aunt (the server).You establish a connection
by dialing the number of your aunt’s phone (point-to-point).This is shown
in Figure 5.3.
If your aunt is at home, she will pick up her phone and talk to you (see
Figure 5.4).The phone company guarantees that the words you and your aunt
are speaking are sent to the other end and in the same order (reliability). As long
as you are on the phone, you can speak continuously (stream-oriented).
http://www.syngress.com
Figure 5.2 Communication Protocol Stack
Application Layer
(HTTP, FTP, …)
Transport Layer
(TCP, UDP, …)
Network Layer
(IP, …)
Physical Layer
(Ethernet, …)
Network Programming: Using TCP and UDP Protocols • Chapter 5 207
The connection will be established until you and your aunt are finished with
your conversation (connection-oriented). See Figure 5.5 for an example of disconnecting.
TCP uses IP as its network protocol. IP is datagram-oriented and a best-effort
protocol.As mentioned before, datagrams are packets of data. Best-effort means that
datagrams are sent without the guarantee of delivery and correct order.
As we have seen,TCP is stream-oriented.TCP must simulate the streaming of
data.Therefore, it is necessary that TCP controls the order and correct occurrence
of the datagrams. If a datagram is corrupt or lost, it must be resent. If this does
not function, an error is reported.TCP also implements a number of protocol
timers to ensure synchronized communication.These timers also can be used to
produce timeouts, if needed.
The advantage of TCP is its reliability—TCP is the base of most reliable protocols
such as HTTP, File Transfer Protocol (FTP), or Telnet.Those protocols are
needed if delivery and order of packets is important. For instance, if you send an
http://www.syngress.com
Figure 5.3 Calling Your Aunt (Point-to-Point Connection)
RING
RING
Figure 5.4 Talking to Your Aunt (Reliability and Stream-Orientation)
“Hello
“Hello Aunt!”
Aunt!”
Figure 5.5 Aunt Hangs Up (Connection-Orientation)
CLICK
208 Chapter 5 • Network Programming: Using TCP and UDP Protocols
e-mail to your aunt starting with “Hello, Aunt,” first it must be delivered, and
second, it should not be delivered as “Hlnt Aeluo.”The disadvantage to TCP is
the loss of performance due to the administration overhead for handling the reliability.
Figure 5.6 shows a sample stack of communication layers with HTTP.
If reliability is not needed, you can choose the protocol UDP.We discuss this
protocol in the next section.
Introduction to UDP
The User Datagram Protocol is a connection-less and datagram-oriented besteffort
protocol.A UDP-communication is analogous to sending a letter.You (a
peer) may want to send a letter to your aunt (another peer).You don’t have to
hand-deliver the letter to your aunt—the post office delivers the letter (a datagram),
and it delivers it as a whole entity, rather than delivering page by page (see
Figure 5.7).
Sending a letter is mostly, but not always, reliable.The post office offers a
best-effort service.They don’t guarantee an order in sending letters. If you send
letter 1 today and send letter 2 tomorrow, your aunt may receive letter 2 before
letter 1 arrives (see Figure 5.8).
On the other hand, one of your letters may get lost.The post office doesn’t
guarantee that a letter will be delivered (see Figure 5.9).
http://www.syngress.com
Figure 5.6 HTTP Communication Layers Stack
HTTP
TCP
IP
Ethernet
HTTP
TCP
IP
Ethernet
Application
Layer
Transport
Layer
Network
Layer
Physical
Layer
Reliable
Best Effort
Client Server
Network Programming: Using TCP and UDP Protocols • Chapter 5 209
So why should you use such a protocol? For the following reasons:
 Performance UDP is faster than TCP because there is no administrative
overhead to bring data in order or for resending lost packets. So it
can be used for time-critical applications such as video- or audiostreaming.
 If your application doesn’t care about a lost packet. Consider a
time server: If the server sends a packet and the packet is lost, it doesn’t
make sense to resend it.The packet will be incorrect if the client
receives it on the second try.
http://www.syngress.com
Figure 5.7 Sending a Letter to Your Aunt (Whole Communication Delivery)
Figure 5.8 No Delivery Order Guaranteed
1
2
Figure 5.9 Loss of a Communication Is Possible
210 Chapter 5 • Network Programming: Using TCP and UDP Protocols
 UDP causes less network traffic. UDP needs 8 bytes for protocol
header information, whereas TCP needs 20 bytes. In times where we
speak of gigabyte hard drives, 16 bytes doesn’t seem like it should be a
problem, but think of the sum of all packets sent in global communication—
then 16 bytes becomes a very heavy weight.
 If your application needs a best-effort protocol for analyzing the
network. For instance, the ping command is used to test communication
between two computers or processes. It needs to know about lost or corrupt
packets to determine the quality of the connection. It doesn’t make
sense to use a reliable protocol for applications such as ping.
UDP is typically used for Domain Name System (DNS), Simple Network
Management Protocol (SNMP), Internet telephony, or streaming multimedia.
Another advantage of UDP is in multicasting, which means that a number of
processes can be grouped together by a special IP address (see Figure 5.10).The IP
address must be in the range 224.0.0.1 to 239.255.255.255, inclusive. Every process
contained in the group can send packets to all other processes of the group.
No process of the group knows how many other processes the group contains.
If one application wants to send data to the others, it has to send the data to
the IP address of the group. On the protocol layer, no process is a specialized
server.Your job is to define clients and servers if needed. For more details, see the
section “Creating a News Ticker Using UDP Multicasting” later in this chapter.
The next section introduces ports. Ports are important for identifying applications
running on a computer.
http://www.syngress.com
Figure 5.10 UDP Multicasting
Process2
Process4
Process3 Process1
Group
IP
Network Programming: Using TCP and UDP Protocols • Chapter 5 211
NOTE
Many firewalls are configured not to allow UDP. Firewalls are used to
permit unauthorized access from outside the firewall. Using UDP, the
firewall cannot determine if a packet comes from inside or outside
because no connection is made explicitly. Remember that TCP is connection-
oriented, as in a direction from the client to the server.
Introduction to Ports
Generally, a computer has a single connection to the network. If all data arrives
through one connection, how can it be determined which application running
on the computer receives the data? The answer is through the use of ports.
A port is a 16-bit number in the range or 0 to 65535.The port numbers 0 to
1023 are reserved for special services such as HTTP (port 80), Mail (port 25), and
Telnet (port 23).
A connected application must be bound to at least one port. Binding means that
a port is assigned to a socket used by an application.The application is registered
with the system. All incoming packets that contain the port number of the application
in the packet header are given to the application socket (see Figure 5.11).
Please note that Figure 5.11 for TCP does not mean that only one socket can
be bound to one port. If a socket is waiting on a port for an incoming connection,
normally the port is blocked for other applications (“normally” means that
this feature can be switched off—for more details, please have a look at the .NET
reference documentation System.Net.Sockets.Socket.SetSocketOption() method). Let’s
call a socket waiting on a connection to a server socket. If a connection is
accepted by a server socket, it creates a new socket representing the connection.
http://www.syngress.com
Figure 5.11 Ports
Application
Application
Application
TCP
or
UDP
Port
Port
Port
Port# Data Client
Datagram
212 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Then, the server socket can wait for a new connection request. So, multiple
clients can communicate concurrently over the same port.
An example for an application using this feature is a Web server. For instance,
while a Web page you requested is loaded by the browser, you can use a second
browser to request another page from the same server.The next sections introduce
the most important .NET classes we use for our examples.
NOTE
In this book, we focus more on real-life examples than on theory.
Because classes like the .NET Socket class are complex in use, we show
.NET classes that simplify the developing rather than the core networking
classes.
System.Net Namespace
Whereas the namespace System.Net.Sockets provide classes for more basic networking
functionality, the System.Net namespace contains classes that encapsulate
these basics for easier access.The classes of System.Net are a simple programming
interface for some protocols used for networking.
At the core of this namespace are the classes WebRequest and WebResponse.
These abstract classes are the base for protocol implementations.Two protocols
are pre-implemented: HTTP with HttpWebRequest (with corresponding
HttpWebResponse) and file system access (request-URIs starting with file://”) with
FileWebRequest (with corresponding FileWebResponse).The other classes are mostly
helper-classes, such as IP addresses, authorization and permission classes, exceptions,
and certificates.Table 5.1 shows the classes we use for our examples.
Table 5.1 System.Net Classes
Class Description
IPAddress Represents an IP address.
IPEndPoint Identifies a network endpoint. A network endpoint is an IP
address and a port.
WebRequest Makes a request to a Uniform Resource Identifier (URI). This
class is abstract and must be extended for the destination
protocol.
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 213
WebResponse Represents a response to a URI. This class is abstract and
must be extended for the destination protocol.
WebProxy Identifies an HTTP proxy. It contains all proxy settings used
by WebRequest.
System.Net.Sockets Namespace
As mentioned earlier in the chapter, the System.Net.Sockets namespace contains
classes that provide basic networking functionality.The central class is Socket.As
mentioned, a socket is the most basic programming interface for networking.We
use most of the classes of this namespace for our example.Table 5.2 shows the
class we use.
Table 5.2 System.Net.Sockets Classes
Class Description
Socket Implements the Berkeley sockets programming interface.
NetworkStream Allows easy access to data of stream sockets.
TcpClient Provides a TCP client that can connect to a server socket.
TcpListener Implements a TCP server socket listening for incoming
connection-requests.
UdpClient Provides a UDP peer with the possibility of multicasting.
Enough theory—let’s go into practice.The next section describes a simple
command transmission and processing using TCP.
NOTE
For simplifying the code, all examples presented in this chapter do
not contain any exception handling. (Refer to Chapter 2 for more information
on exception handling.) Please have a look at the .NET class
reference for each method which exceptions must be handled.
http://www.syngress.com
Table 5.1 Continued
Class Description
214 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Example TCP Command
Transmission and Processing
The example in this section has a strict separation between the presentation layer
and the functional layer.The presentation layer is the user interface (UI). In this
example, you use a console application because you should focus first on communication
and then concentrate on creating a good-looking UI.The functional
layer is the part of the application that does all the work—for example, a business
object for calculating something. Figure 5.12 shows the simplified architecture of
the first example.
For the presentation layer, where the functionality is executed is unimportant.
You can implement the functional layer within the same application, in another
process on the same computer, or on another computer anywhere in a LAN or on
the Internet.To make this architecture more flexible, you will add a command processor
between the presentation and functional layers.The command processor is a
standardized interface for the functional layer.The presentation layer is giving its
requests in the form of commands to the processor.The processor is executing
methods of the functional layer based on the commands. Finally, the command processor
will take the results and give it back to the presentation layer. Figure 5.13
shows the extended architecture.
The command processor makes it simple to access the functional layer in various
ways—either within the same application or via network communication on
another computer. Figure 5.14 shows an example with a remote functional layer.
The advantage of this model is that the presentation layer does not have to know
where the functional layer is running. It just gives commands to the command
processor and receives the result.
A typical example of such an architecture is a Web-browser-to-Web-server
communication.You are typing in an URL in the address field of your browser.
http://www.syngress.com
Figure 5.12 Example Architecture
Presentation Layer
1. Request
Functional Layer
2. Response
Network Programming: Using TCP and UDP Protocols • Chapter 5 215
The browser is converting the URL to a GET request for a Web server and is
sending the request to the Web server.The Web server analyzes the request and
returns an HTML page to the browser.
http://www.syngress.com
Figure 5.13 Example Architecture with Command Processor
Presentation Layer
1. Request
Command
Processor
4. Response
2. Request
Functional Layer
3. Response
Figure 5.14 Example Architecture with Communication
Presentation Layer
1. Request
Communication
Layer
10. Response
3. Request
Communication
Layer
8. Response
5. Request
Functional Layer
6. Response
Client
Server
Network
Command
Processor
2. Request 9. Response
4. Request 7. Response
Command
Processor
216 Chapter 5 • Network Programming: Using TCP and UDP Protocols
This example performs the same action in a very simplified form.A console
client is sending a request to a server and the server returns “Hello World !” to
the client.This example implements a simple communication protocol with two
commands: GET and EXIT.A sample communication looks like this:
c: (establish tcp connection to the server)
s: (accept connection)
c: GET<CRLF>
s: “Hello World !”<CRLF>
c: EXIT<CRLF>
s: BYE<CRLF>
c: (close connection)
s: (close connection)
c: indicates the client and s: the server. <CRLF> means a carriage return followed
by a line feed to indicate that the line is finished.This is commonly used
with communication protocols such as HTTP or SMTP.
General Usage of Needed .NET Classes
You need two main network classes for this example. On the client side, you
use System.Net.Sockets.TcpClient and on the server side it is System.Net.Sockets
.TcpListener.
Generally, on the client side a TcpClient connects to the server.Then you
work with a stream given by the client on the connection. After all the work is
done, you close the client:
// connect client to the server 127.0.0.1:8080
TcpClient client = new TcpClient ( “127.0.0.1”, 8080 );
// get the network stream for reading and writing something
// to the network
NetworkStream ns = client.GetStream ();
// read/write something from/to the stream
// disconnect from server
client.Close ();
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 217
The server side involves one more step, but generally the code looks like the
client code.You bind a TcpListener on a local port.Then, if a client connects to
the listener, you get a socket.With this socket, you create a stream. From this
point on, the code is the same as on the client side:
// create a listener for incoming TCP connections on port 8080
TcpListener listener = new TcpListener ( 8080 );
listener.Start ();
// wait for and accept an incoming connection
Socket server = listener.AcceptSocket ();
// create a network stream for easier use
NetworkStream ns = new NetworkStream ( server );
// read/write something from/to the stream
// disconnect from client
server.Close ();
After having a look at the general use of the networking classes, let’s go further
in our first example.
The Server
Let’s start with the server.The class is called TCPHelloWorldServer.The source
code of Figures 5.15 to 5.18 is included on the CD in a file named
TCPHelloWorldServer.cs. For simplification, only the client has a command processor
component. Later, we show examples where the server also has a processor.
For the server (see Figures 5.15 to 5.18), you need the following namespaces;
again, for simplification, the class has a Main() method only.
Figure 5.15 Needed Namespaces in TCPHelloWorldServer.cs
using System;
using System.IO;
using System.Net.Sockets;
http://www.syngress.com
218 Chapter 5 • Network Programming: Using TCP and UDP Protocols
The code in Figure 5.16 is a snippet of the Main() method in
TCPHelloWorldServer. It shows the server initialization. For waiting for
incoming connections on port 8080, use a TcpListener instance.
Figure 5.16 Server Initialization in TCPHelloWorldServer.cs
Console.WriteLine ( “initializing server…” );
TcpListener listener = new TcpListener ( 8080 );
listener.Start ();
Console.WriteLine ( “server initialized, waiting for ” +
“incoming connections…” );
Socket s = listener.AcceptSocket ();
// create a NetworkStream for easier access
NetworkStream ns = new NetworkStream ( s );
// use a stream reader because of ReadLine() method
StreamReader r = new StreamReader ( ns );
The listener listens on port 8080 for incoming connections.The AcceptSocket()
method returns a socket representing the connection to the client.This method
blocks the program until a client opens a connection to the listener.
After a connection is established, the socket returned by AcceptSocket() is used
for exchanging data with the connected client.The easiest way to do this is the
use of a NetworkStream.This class is located in the namespace System.Net.Sockets.
NetworkStream encapsulates the methods for reading and writing data with a
socket. So, you can use this stream with code working only on streams.
The next step is creating a StreamReader.This class is part of the System.IO
namespace.This class simplifies the access to a stream. Here, you use it because of
its ReadLine() method.This method reads a single line of characters.The .NET
reference documentation defines a line as follows:“A line is defined as a sequence
of characters followed by a carriage return (“\r”), a line feed (“\n”), or a carriage
return immediately followed by a line feed.”
After the client establishes a connection, it sends a command to the connected
server. Now the incoming commands must be parsed and executed by the
server.The code is shown in Figure 5.17.
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 219
Figure 5.17 Command Processing in TCPHelloWorldServer.cs
bool loop = true;
while ( loop )
{
// read a line until CRLF
string command = r.ReadLine ();
string result;
Console.WriteLine ( “executing remote command: ” +
command );
switch ( command )
{
case “GET”:
result = “Hello World !”;
break;
// finish communication
case “EXIT”:
result = “BYE”;
loop = false;
break;
// invalid command
default:
result = “ERROR”;
break;
}
if ( result != null )
{
Console.WriteLine ( “sending result: ” + result );
http://www.syngress.com
Continued
220 Chapter 5 • Network Programming: Using TCP and UDP Protocols
// add a CRLF to the result
result += “\r\n”;
// convert data string to byte array
Byte[] res = System.Text.Encoding.ASCII.GetBytes (
result.ToCharArray () );
// send result to the client
s.Send ( res, res.Length, 0 );
}
}
If the GET command is received, the server returns the string “Hello World
!”, and the loop continues.The loop also continues if an unknown command
arrives. In that case, the string “ERROR” is returned. On the EXIT command,
the server stops the loop. After that, the connection must be closed (see Figure
5.18).You can do this by simply calling the Close() method of the socket. Finally,
the server waits for the pressing of the Return key.
Figure 5.18 Server Shutdown in TCPHelloWorldServer.cs
Console.WriteLine ( “clearing up server…” );
s.Close ();
Console.Write ( “press return to exit” );
Console.ReadLine ();
That’s all for the server. Let’s move on to the client.
The Client
The client is a bit more complex than the server. It has two parts: the UI (a
simple console application), and the command processor, which contains the
communication components.
Let’s have a look at the command processor, named
TCPRemoteCommandProcessor.The source code for Figures 5.19 to 5.25 is
http://www.syngress.com
Figure 5.17 Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 221
included on the CD in a file named Base.cs.This file is compiled to a library
named Base.dll that is also contained on the CD. For the command processor, the
needed namespaces are as shown in Figure 5.19.
Figure 5.19 Used Namespaces in Base.cs
using System;
using System.IO;
using System.Net.Sockets;
First, you will write an interface.This interface gives you the flexibility to
implement more than one type of command processor with different underlying
network protocols, so a client gets only an object that implements the interface.
This makes the client independent from the used network protocol (see Figure
5.20).
Figure 5.20 CommandProcessor Interface in Base.cs
public interface CommandProcessor
{
// execute a command and return the result
// if the return value is false the command processing loop
// should stop
bool Execute ( string command, ref string result );
}
Now, create the TCPRemoteCommandProcessor class that implements the
CommandProcessor interface.The class has three methods: a constructor, a Close()
method, and the implementation of the Execute() method.The command processor
has two different running modes. In the Hold Connection mode, the constructor
establishes the connection to the server directly from the constructor.
Disconnecting will be done at the moment the Close() method is called. In the
Release Connection mode, every time the processor is requested to send a command
to the server, the connection is established. After retrieving the result, the
connection is closed.The first mode is for short-term or high-performance communication.
The second mode is for long-term communication and can be used
for saving money on the Internet or reducing use of network resources.
Let’s start with the class fields. Figure 5.21 shows all the information and
objects needed for running the communication process.
http://www.syngress.com
222 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.21 Class Fields of TCPRemoteCommandProcessor in Base.cs
// remote host
private string host = null;
// remote port
private int port = -1;
// connection mode
private bool releaseConnection = false;
// communication interface
private TcpClient client = null;
// outgoing data stream
private NetworkStream outStream = null;
// ingoing data stream
private StreamReader inStream = null;
Now for the constructor (see Figure 5.22). It has three parameters: the name
and port of the host to connect with and a Boolean flag determining the mode.
If the flag is true, the command processor works in Release Connection mode.
Otherwise, the Hold Connection mode is active. If the processor runs in the last
mentioned mode, the constructor connects immediately to the server specified by
the host name and port. Finally, it initializes the stream input and output stream
fields.
Figure 5.22 Constructor of TCPRemoteCommandProcessor in Base.cs
public TCPRemoteCommandProcessor ( string host, int port,
bool releaseConnection )
{
// add parameter checking here
this.host = host;
this.port = port;
this.releaseConnection = releaseConnection;
if ( !this.releaseConnection )
{
Console.WriteLine ( “connecting to ” + this.host + “:” +
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 223
this.port + “…” );
this.client = new TcpClient ( this.host, this.port );
this.outStream = this.client.GetStream ();
this.inStream = new StreamReader ( this.outStream );
Console.WriteLine ( “connected to ” + this.host + “:” +
this.port );
}
}
The Close() method is quite simple. It closes only the connection (see Figure
5.23).This will be done only in Release Connection mode. If the command processor
is in Hold Connection mode, this method does nothing because the client
field will be null.
Figure 5.23 Close() Method of TCPRemoteCommandProcessor in Base.cs
public void Close ()
{
if ( this.client != null )
{
this.client.Close ();
Console.WriteLine ( “connection closed: ” + this.host +
“:” + this.port );
}
}
NOTE
You do not have to flush the streams by using the Flush() methods of
NetworkStream because these are not buffered streams. But if you
develop classes that work only on streams without knowing which kind
of streams it uses, you should always consider flushing them.
http://www.syngress.com
Figure 5.22 Continued
224 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Execute() is more complex. If the command processor is in the Release
Connection mode, it first must connect to the server and finally close the connection
after sending the command (see Figure 5.24). For sending, the command
is concatenated with a trailing carriage return and line feed. After that, it is converted
to a byte array.This array is given to the output stream.Then the processor
reads the response from the input stream. Finally, it checks if the response in the
string is “BYE”. If so, false is returned; true otherwise.
Figure 5.24 Execute() Method of TCPRemoteCommandProcessor in Base.cs
public bool Execute ( string command, ref string result )
{
// add parameter checking here
bool ret = true;
if ( this.releaseConnection )
{
Console.WriteLine ( “connecting to ” + this.host + “:” +
this.port + “…” );
// open connection to the server
this.client = new TcpClient ( this.host, this.port );
this.outStream = this.client.GetStream ();
this.inStream = new StreamReader ( this.outStream );
Console.WriteLine ( “connected to ” + this.host + “:” +
this.port );
}
// add a CRLF to command to indicate end
command += “\r\n”;
// convert command string to byte array
Byte[] cmd = System.Text.Encoding.ASCII.GetBytes (
command.ToCharArray () );
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 225
// send request
this.outStream.Write ( cmd, 0, cmd.Length );
// get response
result = this.inStream.ReadLine ();
if ( this.releaseConnection )
{
// close connection
this.client.Close ();
Console.WriteLine ( “connection closed: ” + host + “:”
+ port );
}
ret = !result.Equals ( “BYE” );
return ret;
}
Finally, you need a client using the command processor. Call it
TCPHelloWorldClient.The source code for Figure 5.25 is included on the CD in
the file TCPHelloWorldClient.cs. It creates a TCPCommandProcessor instance for
communicating with the server.Then, it sends the GET command and displays
the result on the console. After that, it sends the EXIT command and closes the
connection.
Figure 5.25 TCPHelloWorldClient Listing in TCPHelloWorldClient.cs
using System;
using System.IO;
using System.Net.Sockets;
public class TCPHelloWorldClient
http://www.syngress.com
Figure 5.24 Continued
Continued
226 Chapter 5 • Network Programming: Using TCP and UDP Protocols
{
public static void Main ()
{
Console.WriteLine ( “initializing client…” );
TCPRemoteCommandProcessor proc = new
TCPRemoteCommandProcessor ( “127.0.0.1”, 8080, false );
string result;
Console.WriteLine ( “requesting…” );
proc.Execute ( “GET”, ref result );
Console.WriteLine ( “result: ” + result );
Console.WriteLine ( “closing connection…” );
proc.Execute ( “EXIT”, ref result );
proc.Close ();
Console.Write ( “press return to exit” );
Console.ReadLine ();
}
}
Now you can compile and run the example.
Compiling and Running the Example
Go to the directory where you can find the files TCPHelloWorldServer.cs and
TCPHelloWorldClient.cs. For compiling, batch file exists called compile.bat.
Because we are using TCP for this example, you must start the server before the
client is running.
Now you can start the client by double-clicking on TCPHelloWorldServer
.exe.A Console window like the one shown in Figure 5.26 will appear.
Now you can start the client by double-clicking on TCPHelloWorldClient
.exe. Another Console window like Figure 5.27 will appear.
http://www.syngress.com
Figure 5.25 Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 227
The server window now looks like Figure 5.28. Now you can stop both
applications by pressing any key.The next section uses the same example using
UDP as underlying transport protocol.
NOTE
Because you are using TCP, you must always start the server before the
client begins trying to connect to the server.
Example UDP Command
Transmission and Processing
In this section, you rewrite the example from the section “Example TCP
Command Transmission and Processing” for using UDP as the transport protocol.
http://www.syngress.com
Figure 5.26 Server Waiting for Client Connection
Figure 5.27 Running Client
Figure 5.28 Server after Doing Its Work
228 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Refer back to that section for the introduction to the architecture and the implemented
communication protocol.
NOTE
As mentioned earlier, UDP is normally not used for request/response protocols
like client/server command processing. This example is used for
showing the differences in using UDP and TCP.
General Usage of Needed .NET Classes
In contrast to TCP, in using UDP only one main network class is needed for this
example.This is because the handling is like peer-to-peer (P2P). On both sides
(client and server), we use System.Net.Sockets.UdpClient.
As a matter of principle, we can say a UDP client binds to a local port from
which it receives data. Data is sent directly to another UDP client without connecting
explicitly.That is what is meant by connection-less communication.
Generally, the code on both sides looks the same.A UdpClient is bound to a
local port. Now it is ready to send and receive data. Because you bind the client
to a local port only, you must use one Send() method that needs the remote host
connection information.This information is used for sending the data to another
UDP client. Because you bind the UdpClient to a local port, you receive data
from this port, and you do not have to specify a receive point for the Receive()
method.That is the reason why we use the dummy variable that is set to null.
// bind client to local port where it receives data
UdpClient client = new UdpClient ( 8081 );
// create a byte array containing the characters of
// the string “a request”
Byte[] request = System.Text.Encoding.ASCII.GetBytes (
“a request”.ToCharArray () );
// send request to the server
client.Send ( request, request.Length, “127.0.0.1”, 8080 );
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 229
// create a dummy endpoint
IPEndPoint dummy = null;
// receive something from the server
byte[] response = client.Receive ( ref dummy );
// do something with the response
// unbind the client
client.Close ();
After having a look at the general use of the networking classes, let’s move on
to the second example.
The Server
First, let’s have a look at the server.The class is called UDPHelloWorldServer and is
included on the CD in the file UDPHelloWorldServer.cs.The code does not
differ very much from the code of the TCPHelloWorldServer class (see the section
“Example TCP Command Transmission and Processing”).
For simplification, the class also has a Main() method only.The initialization of
the server is very simple.You just have to bind a UdpClient to a local port.The
code of Figure 5.29 appears at the beginning of the Main() method.
Figure 5.29 Server Initialization in UDPHelloWorldServer.cs
Console.WriteLine ( “initializing server” );
UdpClient server = new UdpClient ( 8080 );
Because UDP is a connection-less protocol, you cannot send back a response
directly without the knowledge of where a request comes from.The header of a
UDP datagram, among other things, contains the port where the sending socket is
bound to (source port). On the IP layer, you can say the UDP datagram is
embedded in an IP datagram.The header of the IP datagram contains the sender’s
IP address. But with C#, you cannot access this information with the simple API
you use (at least with the Beta 2 of the .NET Framework). So the simplest way is
to add the sender’s information to a datagram if you want a receiver returning data.
The syntax of command that will be sent to the server is as follows:
http://www.syngress.com
230 Chapter 5 • Network Programming: Using TCP and UDP Protocols
IP ADDRESS “:” PORT “:” COMMAND
where IP ADDRESS and PORT are the IP address and port of the sender.
COMMAND is the command to execute.The server code for receiving a command
is shown in Figure 5.30. After receiving the command string, it will be split
into the parts described earlier.
Figure 5.30 Receiving a Command in UDPHelloWorldServer.cs
// an endpoint is not needed the data will be sent
// to the port where the server is bound to
IPEndPoint dummy = null;
bool loop = true;
while ( loop )
{
Console.WriteLine ( “waiting for request…” );
byte[] tmp = server.Receive ( ref dummy );
// split request string into parts, part1=client IP
// address or DNS name, part2=client port, part3=command
string dg =
new System.Text.ASCIIEncoding ().GetString (
datagram );
string[] cmd = dg.Split ( new Char[] {‘:’} );
string remoteClientHost = cmd[0];
int remoteClientPort = Int32.Parse ( cmd[1] );
string command = cmd[2];
string result = null;
// command execution
The command execution code is the same as in the TCPHelloWorldServer
class. Also the result-sending code is similar to the code of the mentioned class
(see Figure 5.31).
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 231
Figure 5.31 Result Sending in UDPHelloWorldServer.cs
// convert data string to byte array
Byte[] d = System.Text.Encoding.ASCII.GetBytes (
result.ToCharArray () );
// send result to the client
server.Send ( d, d.Length, remoteClientHost,
remoteClientPort );
The shutdown code is also the same as the code you knew from
TCPHelloWorldServer. Now let’s have a look at the client.
The Client
The client is called UDPHelloWorldClient and is included on the CD in the file
UDPHelloWorldClient.cs. It is modified code from TCPHelloWorldClient with
only one difference: the command processor and its instantiation.The command
processor is called UDPCommandProcessor, and you can find it on the CD in the
file Base.cs. Figure 5.32 shows the only different line of the code.
Figure 5.32 Instantiation of the Command Processor in
UDPHelloWorldClient.cs
UDPRemoteCommandProcessor proc = new
UDPRemoteCommandProcessor ( 8081, “127.0.0.1”, 8080 );
The parameter 8081 is the local port where the command processor is
bound.The other two parameters of the constructor are the remote IP address
and port of the server to which the command processor connects.
Now comes the command processor, called UDPCommandProcessor. Just like
TCPCommandProcessor, this class has three methods: a constructor, a Close()
method, and an Execute() method. First, let’s have a look at the class fields (see
Figure 5.33).
Figure 5.33 Class Fields of UDPCommandProcessor in Base.cs
// the local port where the processor is bound to
private int localPort = -1;
http://www.syngress.com
Continued
232 Chapter 5 • Network Programming: Using TCP and UDP Protocols
// the remote host
private string remoteHost = null;
// the remote port
private int remotePort = -1;
// communication interface
private UdpClient client = null;
The next stop is the constructor. It sets all class fields and binds the UDP
client to a local port (see Figure 5.34).
Figure 5.34 Constructor of UDPRemoteCommandProcessor in Base.cs
public UDPRemoteCommandProcessor ( int localPort,
string remoteHost, int remotePort )
{
// add parameter checking here
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
this.client = new UdpClient ( localPort );
}
The Close() method is very simple. It calls the Close() method of the UDP
client (see Figure 5.35).
Figure 5.35 Close() Method of UDPRemoteCommandProcessor in Base.cs
public void Close ()
{
this.client.Close ();
}
The Execute() method is very similar to the same named method of
TCPCommandProcessor.You have a different handling in communication because
http://www.syngress.com
Figure 5.33 Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 233
of UDP.You need code for adding the local IP address and port to the command.
Also, the sending and receiving of data is different. See Figure 5.36 for the UDP
code.
Figure 5.36 Execute() Method of UDPRemoteCommandProcessor in Base.cs
public bool Execute ( string command, ref string result )
{
// add parameter checking here
bool ret = true;
Console.WriteLine ( “executing command: ” + command );
// build the request string
string request = “127.0.0.1:” + this.localPort.ToString ()
+ “:” + command;
Byte[] req = System.Text.Encoding.ASCII.GetBytes (
request.ToCharArray () );
client.Send ( req, req.Length, this.remoteHost,
this.remotePort );
// we don’t need an endpoint
IPEndPoint dummy = null;
// receive datagram from server
byte[] res = client.Receive ( ref dummy );
result = System.Text.Encoding.ASCII.GetString ( res );
ret = !result.Equals ( “BYE” );
return ret;
}
http://www.syngress.com
234 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Compiling and Running the Example
Go to the directory where you can find the files UDPHelloWorldServer.cs and
UDPHelloWorldClient.cs and start the compile.bat batch file.
Now, after successfully compiling all files, you are ready to run the example.
Start the server by double-clicking on UDPHelloWorldServer.exe in Windows
Explorer.A console window like the one shown in Figure 5.37 will appear.
Start the client by double-clicking on UDPHelloWorldClient.exe. Another
console window like Figure 5.38 will appear.
The server window now looks like Figure 5.39.
Now you can stop both applications by pressing any key.The next section
discusses how to write a UDP multicasting application.
http://www.syngress.com
Figure 5.37 Server Waiting for Client Connection
Figure 5.38 Running Client
Figure 5.39 Server after Doing its Work
Network Programming: Using TCP and UDP Protocols • Chapter 5 235
Creating a News Ticker
Using UDP Multicasting
A news ticker is an application where a news server sends messages to a number
of clients. A client subscribes to the news server. From the moment of subscription,
the client is allowed to receive new messages from the server.
You can implement implement such an architecture in several ways, but the
easiest is using UDP multicasting. As described in the section “Introduction to
UDP,” with UDP, you can group applications (peers) together. An IP address
together with a port is the alias for the group; that means a peer sends data to
that IP address and port and all peers of that group will receive the data.
In this section, you will see how to develop a simple news ticker server and
client.The server is a simple Windows Forms application with a text box and a
button.The user types in the news in the text box. By clicking on the button, the
server sends the news to the group (see Figure 5.40).The server must send news
continuously so that a client can be started at any time for receiving the news.
The client also is a simple Windows Forms application with only one text
box. If news arrives, it will be displayed in the text box by shifting the text from
the right to the left like a marquee (see Figure 5.41).
http://www.syngress.com
Figure 5.40 UDP Multicast News Server
Click to Send News New Text Box
Figure 5.41 UDP Multicast News Client
Shifting Characters
236 Chapter 5 • Network Programming: Using TCP and UDP Protocols
General Usage of Needed .NET Classes
As you have seen with UDP, you need only one class: System.Net.Sockets.UdpClient.
In addition to the methods discussed in the section “Example UDP Command
Transmission and Processing,” you can use the UdpClient.JoinMulticastGroup()
method.This method registers a UDP peer to a multicast group.
The initialization of the news server and client is done by the same code.
First, you bind a UdpClient to a local port.Then you register this client to a multicast
group by calling its method JoinMulticastGroup().This method gets the IP
address of the group. Finally, you create an IPEndPoint to receive data from.As
mentioned in the introduction, an IPEndPoint is the combination of an IP address
and a port:
// create a peer bound to a local port
UdpClient peer = new UdpClient ( LOCAL_PORT );
// create the group IP address
IPAddress groupAddress = IPAddress.Parse ( GROUP_IP );
// add the peer to the group
peer.JoinMulticastGroup ( groupAddress );
// create an end point for sending data to the group
IPEndPoint groupEP = new IPEndPoint ( groupAddress,
GROUP_PORT );
The code for sending and receiving is similar to the code in the section
“Example UDP Command Transmission and Processing”:
// send data to the group, d is a byte array
peer.Send ( d, d.Length, groupEP );
// receiving data from the group
IPEndPoint dummy = null;
byte[] d = peer.Receive ( ref dummy );
After having a look at the general use of the needed classes, let’s go further into
the news ticker example. Let’s first have a look at a class that is used by the news
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 237
client and news server.This class is called UDPPeer, and it is included on the CD
in the file Base.cs. It represents a simplified interface to the UdpClient class.
The server can be driven in unicast or multicast mode. If the class is instantiated
with the local port, only the unicast mode is active. If it is instantiated with
an additional UDP multicast group IP address and port, multicast mode is used.
The Close() method shuts down the server and the Receive() and Send() methods
are used for receiving and sending data, respectively.
Let’s now go more in detail and start with the class fields (see Figure 5.42).
Figure 5.42 Class Fields of UDPPeer in Base.cs
// udp peer
private UdpClient server = null;
// multicast group IP address
private IPAddress groupAddress = null;
// multicast group endpoint (IP address and port)
private IPEndPoint group = null;
The server field is needed as a communication interface for unicasting and
multicasting.The groupAddress and group fields are only needed in case of multicasting.
The groupAddress field is the IP address of the UDP multicast group and
group is the end point where the data is sent to.
The next is the unicast constructor (see Figure 5.43). It is very simple; it just
binds the UDP peer to a local port.
Figure 5.43 Unicast Constructor of UDPPeer in Base.cs
public UDPPeer ( int localPort )
{
// add parameter checking here
Console.WriteLine ( “initializing UDP server, port=” +
localPort + “…” );
this.server = new UdpClient ( localPort );
Console.WriteLine ( “UDP server initialized” );
}
http://www.syngress.com
238 Chapter 5 • Network Programming: Using TCP and UDP Protocols
The multicast constructor calls the unicast constructor for binding the UDP
peer to a local port and additionally registers the peer with the multicast group (see
Figure 5.44). For registering an instance of IPAddress initialized with the group IP
address is needed.This address is represented by the field groupAddress.The field
group is an instance of the IPEndPoint class and is needed later for receiving data.
Figure 5.44 Multicast Constructor of UDPPeer in Base.cs
public UDPPeer ( int localPort, string groupIP,
int groupPort ) : this ( localPort )
{
// add parameter checking here
Console.WriteLine ( “adding UDP server to multicast ” +
“group, IP=” + groupIP + “, port=” + groupPort + “…”);
this.groupAddress = IPAddress.Parse ( groupIP );
this.group = new IPEndPoint ( this.groupAddress,
groupPort );
this.server.JoinMulticastGroup ( this.groupAddress );
Console.WriteLine ( “UDP server added to group” );
}
The Close() method is very simple. In case of multicasting, it deletes the peer
from the multicast group. Finally, it calls the Close() method of UdpClient (see
Figure 5.45).
Figure 5.45 Close() Method of UDPPeer in Base.cs
public void Close ()
{
if ( this.groupAddress != null )
this.server.DropMulticastGroup ( this.groupAddress );
this.server.Close ();
}
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 239
The Receive() method is a simple method that encapsulates the byte-array
handling (see Figure 5.46).The received byte array is converted to a string, and it
is returned to the caller of this method.
Figure 5.46 Receive() Method of UDPPeer in Base.cs
public String Receive ()
{
IPEndPoint dummy = null;
// receive datagram
byte[] data = this.peer.Receive ( ref dummy );
return new System.Text.ASCIIEncoding ().GetString (
data );
}
The Send() method is also simple. After converting the given string to a byte
array, it calls the Send() method of the UDP peer (see Figure 5.47).
Figure 5.47 Send() Method of UDPPeer in Base.cs
public void Send ( string message )
{
// add parameter checking here
Console.WriteLine ( “sending ” + message + “…” );
// convert news string to a byte array
Byte[] d = System.Text.Encoding.ASCII.GetBytes (
message.ToCharArray () );
this.server.Send ( d, d.Length, this.group );
Console.WriteLine ( “message sent” );
}
http://www.syngress.com
240 Chapter 5 • Network Programming: Using TCP and UDP Protocols
The next section discusses the UI of the news server.
The Server
The UDPPeer class now makes it very easy to develop a simple user interface
class for the news server.The class is named UDPNewsServer and is included on
the CD in the file UDPNewsServer.cs.
The class has one constructor and three methods: an event handler for a
window-closed event, an event handler for a button contained in the UI, and a
method that is used by a thread for sending news continuously.
The news server class is derived from System.Windows.Forms.Form. Let’s first
have a look at the class fields, in Figure 5.48.
Figure 5.48 Class fields of UDPNewsServer in UDPNewsServer.cs
// local port where the UDP server is bound to
private const int LOCAL_PORT = 8080;
// multicast group IP address
private const string GROUP_IP = “225.0.0.1”;
// multicast group port
private const int GROUP_PORT = 8081;
// UDP server
private UDPPeer server = null;
// a thread for sending new continuously
private Thread serverThread = null;
// a data field for typing in a new message
private TextBox text = null;
// a button for setting the new message
private Button setButton = null;
// the news message
private string news = “”;
Figure 5.49 shows the constructor code whereby the initialization of the UI
components is not shown. If the Send button is clicked, the news server should
update the news to be sent to the multicast group. In order to get notified by the
button, register the OnSet() method with the button as a click event handler.The
OnClosed() method is registered with the window for the Closed event. Finally,
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 241
start a thread with the Run() method that continuously sends the news typed in
the text field.
Figure 5.49 Constructor of UDPNewsServer in UDPNewsServer.cs
public UDPNewsServer ()
{
// UI components initialization
// add an event listener for click-event
this.setButton.Click += new System.EventHandler ( OnSet );
// add an event listener for close-event
this.Closed += new System.EventHandler ( OnClosed );
// create communication components
this.server = new UDPPeer ( LOCAL_PORT, GROUP_IP,
GROUP_PORT );
// start communication thread
this.serverThread = new Thread (
new ThreadStart ( Run ) );
this.serverThread.Start ();
Console.WriteLine ( “initialization complete” );
}
The thread is needed because the server must send the news continuously.
Let’s now have a look at the thread (see Figure 5.50). Every second it sends the
content of the class field news to the multicast group and writes a message to the
console that it is sending data. After sending, this method puts the thread to sleep
for one second by calling the static method Sleep() of the Thread class.The value
1000 means one-thousand milliseconds—that is, one second.This call causes the
current thread to sleep for the specified time.
http://www.syngress.com
242 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.50 Sending Thread of UDPNewsServer in UDPNewsServer.cs
// sending thread
public void Run ()
{
while ( true )
{
if ( !this.news.Equals ( “” ) )
{
Console.WriteLine ( “sending ” + this.news );
this.server.Send ( this.news );
}
// wait one second
Thread.Sleep ( 1000 );
}
}
The news field is set by the event handler that is registered for the click event
of the Set button (see Figure 5.51).
Figure 5.51 Button Event Handler of UDPNewsHandler in
UDPNewsHandler.cs
// button click event handler
public void OnSet ( Object sender, EventArgs e )
{
this.news = this.text.Text;
}
Finally, let’s look at the shutdown code. It is placed in an event handler that is
called if the form receives the Closed event.The method requests the sending
thread to stop by calling its Abort() method and waits until it is dead.This is done
with the call to the Join() method of the thread. After that, it calls the Close()
method of the UDPPeer object.The code is shown in Figure 5.52.
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 243
Figure 5.52 OnClosed() Method of UDPNewsHandler in UDPNewsHandler.cs
public void OnClosed ( Object sender, EventArgs e )
{
Console.WriteLine ( “server shut down…” );
// stop thread
this.serverThread.Abort ();
// wait until it’s stopped
this.serverThread.Join ();
this.server.Close ();
Application.Exit ();
}
The Client
The client is also divided into two parts: a UDP multicast client class and a user
interface. First, let’s have a look at the client class. It is called UDPMulticastClient
and is included on the CD in the file Base.cs.
In this example, you develop an asynchronous communication. An example
for asynchronous communication is talking with a friend via e-mail or chat.You
send a message to a friend and then you can do something else while you wait
for the response. After a while, you receive your friend’s answer and you are notified.
Here in this example, asynchronous means that the client UI can be used
while a thread in the background is waiting for incoming data. But the UI must
be notified by the receiving thread if a message arrives.This is done by the thread
calling a delegate that is implemented by the UI form. Figure 5.53 shows the
architecture of the client.
The client is built of three main components; the UI, the ticker thread, and
the receiving thread.The UI is a simple form with a text box.The ticker thread
shifts the characters of the text box content by one position to left.
The receiving thread is implemented in UDPMulticastClient and is listening
permanently for incoming messages. If a message arrives, it calls a Notify() delegate
that is implemented as the SetNews() method in the UI.The Notify() delegate
http://www.syngress.com
244 Chapter 5 • Network Programming: Using TCP and UDP Protocols
is shown in Figure 5.54. It is included on the CD in the file Base.cs. It acts a little
bit like an event handler. If the thread receives a new message, it calls the delegate
by passing the message to it.You will see this in the UDPMulticastClient class.
Figure 5.54 The Notify() Delegate in Base.cs
public delegate void Notify ( string text );
NOTE
The System.Net.Sockets.Socket class implements an interface to the
Windows sockets DLL. That means that not only the default Berkeley
sockets are supported. You also find methods for asynchronous communication
in this DLL and in the Socket class. For all methods like Accept()
or Receive(), you’ll find asynchronous methods like BeginAccept()/
EndAccept() or BeginReceive()/EndReceive(). For instance, BeginAccept()
initiates the asynchronous wait for an incoming connection. Among
others, this function takes a so-called AsyncCallback, which is a delegate
that is called if a connection is accepted.
Now comes the UDPMulticastClient code. It has one constructor and two
methods.The constructor initializes the UDP client that receives messages from
the news server.The Run() method is used by a thread to listen for news, and the
http://www.syngress.com
Figure 5.53 Architecture of the News Client
UI
3. SetNews (Message)
Ticker Thread
(Shifting Characters)
Receiving
Thread
1. Message
2. Notify (Message)
Network Programming: Using TCP and UDP Protocols • Chapter 5 245
Close() method shuts down the news client.We need at least three class fields: the
notification delegate, the communication components, and a thread for asynchronous
receiving of data (see Figure 5.55).
Figure 5.55 Class Fields of UDPMulticastClient in Base.cs
// notification delegate
private Notify notify = null;
// communication interface
private UDPPeer peer = null;
// receiving thread
private Thread clientThread = null;
The constructor stores the notification delegate and initializes the UDP peer
with the given group IP address and port. Finally, it starts the news receiving
thread (see Figure 5.56).
Figure 5.56 Constructor of UDPMulticastClient in Base.cs
public UDPMulticastClient ( string groupIP, int groupPort,
Notify notify )
{
// add parameter validation here
Console.WriteLine ( “initializing UDP multicast ” +
“client, group=” + groupIP + “, port=” + groupPort +
“…” );
this.notify = notify;
// create communication components
this.client = new UDPPeer ( groupPort, groupIP,
groupPort );
// start listener thread
this.clientThread = new Thread (
new ThreadStart ( Run ) );
http://www.syngress.com
Continued
246 Chapter 5 • Network Programming: Using TCP and UDP Protocols
this.clientThread.Start ();
Console.WriteLine ( “UDP multicast client initialized” );
}
The receiving thread is implemented by the Run() method. It is an endless
loop that receives available data and gives it directly to the notification delegate
(see Figure 5.57).
Figure 5.57 Receiving Thread of UDPMulticastClient in Base.cs
public void Run ()
{
while ( true )
this.notify ( this.peer.Receive () );
}
The Close() method shuts down the client. It stops the receiving thread and
calls the Close() method of its UDP peer (see Figure 5.58).
Figure 5.58 Close() Method of UDPMulticastClient in Base.cs
public void Close ()
{
this.clientThread.Abort ();
this.clientThread.Join ();
this.peer.Close ();
}
That’s all there is to the UDP multicast client. Now let’s look at the news
client UI.The UI is a class derived from System.Windows.Forms.Form. It is called
UDPNewsClient and contained on the CD in the file UDPNewsClient.cs. It
simply contains a TextBox.The class also has one constructor and four methods.
The constructor initializes the client application. Furthermore, it includes an
event handler method called OnClosed() registered for the Closed event. Finally,
there are the methods RunTicker() for shifting the characters in the text field and
http://www.syngress.com
Figure 5.56 Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 247
the SetNews() method. SetNews() implements the Notify() delegate and is used by
the listener thread of UDPMulticastClient to update the news text field. First, consider
the class fields shown in Figure 5.59.
Figure 5.59 Class Fields of UDPNewsClient in UDPNewsClient.cs
// multicast group IP address
private const string GROUP_IP = “225.0.0.1”;
// multicast group port
private const int GROUP_PORT = 8081;
// communication interface
private UDPMulticastClient client = null;
// ticker thread
private Thread tickerThread = null;
// new messages
private TextBox text = null;
// default news displayed at the beginning
private string news = “Please wait…”;
The constructor initializes the TextBox, event handler, UDP peer, and ticker
thread. Figure 5.60 shows the constructor without TextBox initialization.
Figure 5.60 Constructor of UDPNewsClient in UDPNewsClient.cs
public UDPNewsClient ()
{
// initialize UI
// add an event listener for close-event
this.Closed += new System.EventHandler ( OnClosed );
// start communication thread
this.client = new UDPMulticastClient ( GROUP_IP,
GROUP_PORT, new Notify ( SetNews ) );
// start ticker thread
http://www.syngress.com
Continued
248 Chapter 5 • Network Programming: Using TCP and UDP Protocols
this.tickerThread = new Thread (
new ThreadStart ( RunTicker ) );
this.tickerThread.Start ();
Console.WriteLine ( “initialization complete” );
}
The news client shutdown method called by the Closed event is shown in
Figure 5.61. It closes the client and stops the ticker thread.
Figure 5.61 Event Handler for Closed Event in UDPNewsClient.cs
public void OnClosed ( Object sender, EventArgs e )
{
Console.WriteLine ( “client shut down” );
this.client.Close ();
this.tickerThread.Abort ();
this.tickerThread.Join ();
Application.Exit ();
}
The ticker thread shifts—every 500 milliseconds—one character of the news
string into the text box on the right and deletes one on the left.The implementation
is not very smart, but for a simulation it is enough. Figure 5.62 also shows
the notification method. It simply sets the message received by the multicast
client to the news variable.
Figure 5.62 Ticker Thread and Notification Method in UDPNewsClient.cs
public void RunTicker ()
{
// initialze the textbox with the default text
this.text.Text = ” -+-+- ” + this.news + ” -+-+- ” +
http://www.syngress.com
Figure 5.60 Continued
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 249
this.news + ” -+-+- “;
while ( true )
{
string data = this.news + ” -+-+- “;
// repeat as long as there are characters in the data string
while ( !data.Equals ( “” ) )
{
// wait 500 milliseconds
Thread.Sleep ( 500 );
// remove the first character from the text field and add the
// first character of the data string
this.text.Text = this.text.Text.Substring ( 1 ) +
data[0];
// remove the first character from the data string
data = data.Substring ( 1 );
}
}
}
// notification method, used by multicast client
public void SetNews ( string news )
{
this.news = news;
}
You now have everything you need to compile and run the example.
http://www.syngress.com
Figure 5.62 Continued
250 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Compiling and Running the Example
Go to the directory where you can find the files UDPNewsClient.cs and
UDPNewsClient.cs. Start the compile.bat batch file. After successful compiling,
double-click UDPNewsClient.exe. A form like Figure 5.63 appears.
Note that the server must not be started first.This is because UDP is connection-
less, that is, the client does not have to connect to the server. If the server
sends data to the UDP multicast group, the clients simply receive the data.
To start the server, open a new console and type in UDPNewsServer or
double-click on UDPNewsServer.exe. After typing in some news, the server
form may looks like Figure 5.64.
Now, click Set, and after a short period, your client looks like Figure 5.65.
In the next section, you will develop a client/server chat application combining
TCP and UDP technologies.
Creating a UDP Client
Server Chat Application
For users, a chat application seems to be a classic P2P application.You send a
message to the chat room, and all users that take part at the chat receive the message.
So far, you have learned something about the client/server protocol TCP,
about the P2P (unicasting), and also peer-to-group (multicasting) protocol UDP.
http://www.syngress.com
Figure 5.63 UDP News Client Form
Figure 5.64 UDP New Server Form
Figure 5.65 UDP News Client Receiving News
Network Programming: Using TCP and UDP Protocols • Chapter 5 251
So for a chat application, the UDP multicasting seems to be the best choice
(okay, it is the simplest!).You can develop a UDP multicast peer, send this to your
friends, and give them a common IP address to connect.You can send messages
to this address and all friends that are connected receive these messages—a very
simple but effective chat application.
Let’s do something a little different to show a technique of other applications
like Web servers.The result will be an application that guarantees a reliable
delivery of the messages from a chat client to a chat server.The server will be a
TCP server.You will see how the server can handle more than one client at the
same time on the same port.This is like a Web server that responds to many
requests at the same time on the standard HTTP port 80.Then, the server sends
the messages via UDP to all connected chat clients.
Figure 5.66 shows the three phases from the client’s connect request to client/
server communication:
 Connect The client connects to the server via TCP.
 Create thread The server creates a server thread for the client.
 Communication/listen for new connection The client communicates
with the server thread. At the same time, the server listens for new
connections.
More interesting than listening for new connections while communicating is
that the server can communicate with more than one client at the same time.
This can happen at the same port. So a server is not restricted to “only” 65,536
connections.Theoretically the number of concurrent connections is unlimited. In
reality, the number of concurrent connections depends on various conditions—
for simplicity, this example focuses on the technique that builds the base for handling
concurrent connections.
http://www.syngress.com
Figure 5.66 The Three Phases from Connection Request to Communication
Client 1. Connect Server
2. Create Thread
Communication
Thread
3. Communication
3. Listen for New Connection
252 Chapter 5 • Network Programming: Using TCP and UDP Protocols
You will reuse most of the classes you developed until now. On the client
side, you use TCPCommandProcessor for communicating with the chat server and
UDPMulticastClient for receiving messages from the server that were sent by other
clients. On the server side, you use UDPPeer for sending chat messages received
from the clients.
For handling multiple client connections, you will develop two new classes.
The TCPServer class will be the class that listens for incoming client connections.
If a connection is accepted, TCPServer creates an instance of TCPServerSession
that handles the communication with the client.This instance will be driven by a
thread. TCPServerSession will receive the chat protocol commands from the
TCPCommandProcessor on the client side.The commands will be given to a command
processor object that implements the interface CommandProcessor; correspondingly,
they are given to a method of this object that implements the
delegate ExecuteCommand.This method interprets the commands and sends the
containing message to the chat members. Figure 5.67 shows a UML-like
sequence diagram that describes this behavior.
All mentioned classes, including the new ones, are contained on the CD in
the file Base.cs. Let’s start with the delegate ExecuteCommand() (see Figure 5.68).
It has the same signature as the Execute() method of the interface
CommandProcessor and is used to access this method of CommandProcessor implementing
instances.
http://www.syngress.com
Figure 5.67 UML-Like Sequence Diagram of the Chat Client/Server Behavior
UMC TCP
Client Server
TS TSS CP
Connect
Create
Command
Command
Message
UMC: UDPMulticastClient
TCP: TCPCommandProcessor
TS: TCPServer
TSS: TCPServerSession
CP: CommandProcessor
Network Programming: Using TCP and UDP Protocols • Chapter 5 253
Figure 5.68 The Delegate ExecuteCommand()
public delegate bool ExecuteCommand ( string command,
ref string result );
The TCPServerSession Class
This class has a constructor for initializing the server session and two methods.
The Close() method shuts down the session and Run() listens for incoming commands.
Let’s start with the class fields of TCPServerSession (see Figure 5.69).
Figure 5.69 Class Fields of TCPServerSession in Base.cs
// command processor
private ExecuteCommand executeCommand = null;
// communication interface
private Socket socket = null;
// open flag
private bool open = false;
The constructor gets a socket for listening for and responding to requests. It
also gets an ExecuteCommand() delegate for executing incoming commands.
public TCPServerSession ( Socket socket,
ExecuteCommand executeCommand )
{
this.socket = socket;
this.executeCommand = executeCommand;
this.open = true;
}
public void Close ()
{
// session closing

}
http://www.syngress.com
254 Chapter 5 • Network Programming: Using TCP and UDP Protocols
public void Run ()
{
// command execution

}
}
The constructor gets the socket and an ExecuteCommand delegate.The socket
represents the connection to the client. Behind the delegate is a command processor
that executes the incoming commands.
Figure 5.70 shows the Run() method. It reads—in a loop—a command from
the client.The command is given to the ExecuteCommand delegate that returns a
result.The result is returned to the client.These steps are repeated as long as the
delegate returns false or the Close() method was called.
Figure 5.70 Run() Method of TCPServerSession in Base.cs
public void Run ()
{
NetworkStream ns = new NetworkStream ( this.socket );
StreamReader reader = new StreamReader ( ns );
bool loop = this.open;
while ( loop )
{
if ( ns.DataAvailable )
{
// read command from client
string cmd = reader.ReadLine ();
string result = “”;
// execute command
loop = this.executeCommand ( cmd, ref result );
Console.WriteLine ( “sending result, result=” + result );
result += “\r\n”;
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 255
Byte[] res = System.Text.Encoding.ASCII.GetBytes (
result.ToCharArray () );
// return result to client
this.socket.Send ( res );
Console.WriteLine ( “result sent” );
}
// repeat until executeCommand() returns false or
// server session is closed
loop = loop && this.open;
}
Close ();
}
The Close() method clears the open flag and closes the connection to the
client (see Figure 5.71). If the thread is still running, the cleared open flag causes
the Run() method to terminate.
Figure 5.71 Close() Method of TCPServerSession in Base.cs
public void Close ()
{
if ( this.open )
{
Console.WriteLine ( “TCP session is closing…” );
this.open = false;
this.socket.Close ();
http://www.syngress.com
Figure 5.70 Continued
Continued
256 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Console.WriteLine ( “TCP session closed” );
}
}
The TCPServer Class
The next class is TCPServer. As the name implies, it implements a simple TCP
server. It can handle multiple clients by using a session for each client.The sessions
are instances of TCPServerSession. TCPServer contains a constructor and two
methods.The constructor initializes the server.The Close() method shuts down
the server and Run() listens for incoming connection requests. Furthermore,
Run() starts a session for each connected client. First, Figure 5.72 shows the class
fields of TCPServer.
Figure 5.72 Class Fields of TCPServer in Base.cs
// sessions list
private ArrayList sessions = null;
// session threads list
private ArrayList sessionThreads = null;
// command processor
private ExecuteCommand executeCommand = null;
// connection listener
private TcpListener listener = null;
// server thread
private Thread server = null;
// open flag
private bool open = false;
Figure 5.73 shows the constructor.
Figure 5.73 Constructor of TCPServer in Base.cs
public TCPServer ( int port, ExecuteCommand executeCommand )
{
http://www.syngress.com
Figure 5.71 Continued
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 257
this.sessions = new ArrayList ();
this.sessionThreads = new ArrayList ();
this.executeCommand = executeCommand;
Console.WriteLine ( “initializing TCP server…” );
Console.WriteLine ( “creating listener…” );
this.listener = new TcpListener ( port );
Console.WriteLine ( “starting listener…” );
this.listener.Start ();
this.open = true;
this.server = new Thread ( new ThreadStart ( Run ) );
this.server.Start ();
Console.WriteLine ( “TCP server initialization complete, port=” +
port );
}
First, it creates two instances of ArrayList.The first is the class field sessions that
contains all sessions.The second one is a list of the session threads and is represented
by the class field sessionThreads.This list is needed for shutting down the
session threads.This will be done by the Close() method. Furthermore, the constructor
creates a listener that listens on the given port for incoming client connection
requests.The other parameter is a delegate that implements a command
processor.This delegate instance will be given to each started session for command
execution. Finally, the constructor starts a thread for listening on incoming
connections and starting a session for each connection.The thread runs the Run()
method (see Figure 5.74).
http://www.syngress.com
Figure 5.73 Continued
258 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.74 Run() Method of TCPServer in Base.cs
public void Run ()
{
while ( this.open )
{
Console.WriteLine ( “listening for incomming connections…” );
// wait for incoming client connection requests
Socket s = this.listener.AcceptSocket ();
if ( s.Connected )
{
Console.WriteLine ( “client connected, starting client ” +
“session…” );
// create a client session
TCPServerSession session = new TCPServerSession ( s,
this.executeCommand );
// add it to the session list
this.sessions.Add ( session );
// create a thread for the session
Thread th = new Thread ( new ThreadStart ( session.Run ) );
// start it
th.Start ();
// add it to the session thread list
this.sessionThreads.Add ( th );
}
}
}
The Run() method listens for incoming connections. If the method receives a
connection request, a session is started with the accepted socket and the command
processor delegate.This is repeated as long as the open flag is set. If the
open flag is cleared by the Close() method, the loop terminates (see Figure 5.75).
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 259
Figure 5.75 Close() Method of TCPServer in Base.cs
public void Close ()
{
Console.WriteLine ( “TCP server is closing…” );
if ( this.open )
{
this.open = false;
// stop listner
this.listener.Stop ();
// stop server thread
this.server.Abort ();
this.server.Join ();
// stop all session threads and close the sessions
while ( this.sessions.Count > 0 )
{
// stop session thread
Thread th = (Thread)this.sessionThreads[0];
th.Abort ();
th.Join ();
this.sessionThreads.Remove ( th );
// close session
TCPServerSession s = (TCPServerSession)this.sessions[0];
s.Close ();
this.sessions.Remove ( s );
}
}
Console.WriteLine ( “TCP server closed” );
}
http://www.syngress.com
260 Chapter 5 • Network Programming: Using TCP and UDP Protocols
The Close() method stops the listener and the server thread that listens for
incoming connections.Then, each session thread is stopped, and the suitable session
is closed.
The Chat Protocol
Here, you will implement three commands: HELLO, SAY, and BYE.The general
syntax of a command line is as follows:
user_name “:” command [ “:” message ] CRLF
That syntax means a line contains the username that sends the command line
followed by the actual command. An optional message may follow the command.
The message is part of the command line if the command is SAY.A carriage
return/linefeed terminates the line.The following is a sample communication
between a client c and a server s:
c: <user_name>:HELLO<CRLF>
s: HELLO<CRLF>
(sends via UDP multicast “<user_name> has joined the chat room”)
c: <user_name>:SAY:<message><CRLF>
s: OK<CRLF>
(sends via UDP multicast “<user_name>: <message>”)
c: <user_name>:BYE<CRLF>
s: BYE<CRLF>
(sends via UDP multicast “<user_name> has left the chat room”)
You now can describe the chat server class and client class. Let’s start with the
chat server.
The ChatServer Class
This class is contained on the CD in the file ChatServer.cs. For simplification, the
chat command processor is contained in the user interfaces classes. User interface
is not a correct name; it is a simple console application without any user interaction.
Because all functionality is contained in the classes described earlier, the
server is very simple. It implements the CommandProcessor interface and has only
three methods: a constructor, a Close(), and an Execute() method. Figure 5.76
shows the class fields of the ChatServer class.
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 261
Figure 5.76 Class Fields of the ChatServer Class in ChatServer.cs
// listening port for incoming connection requests
private const int TCP_PORT = 8080;
// local port for the UDP peer for sending new messages
private const int UDP_LOCAL_PORT = 8081;
// multicast group IP address
private const string UDP_GROUP_IP = “224.0.0.1”;
// multicast group port
private const int UDP_GROUP_PORT = 8082;
// TCP server for incoming connection requests
private TCPServer tcpServer = null;
// UDP peer for sending new messages
private UDPPeer udpPeer = null;
// list of currently connected users
private ArrayList users = null;
Now let’s have a look at the constructor (see Figure 5.77). First, it creates the
currently connected users list.Then the constructor starts the TCP server and the
UDP peer.
Figure 5.77 Constructor of ChatServer in ChatServer.cs
public ChatServer ()
{
this.users = new ArrayList ();
this.tcpServer = new TCPServer ( TCP_PORT,
new ExecuteCommand ( Execute ) );
this.udpPeer = new UDPPeer ( UDP_LOCAL_PORT, UDP_GROUP_IP,
UDP_GROUP_PORT );
}
The next method is the Close() method (see Figure 5.78). It simply shuts
down the UDP peer and TCP server by calling their Close() methods.
http://www.syngress.com
262 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.78 Close() method of ChatServer in ChatServer.cs
public void Close ()
{
this.udpPeer.Close ();
this.tcpServer.Close ();
}
The command execution is determined by the chat protocol. A user can send
messages to others only if the HELLO command was sent before. If the server
receives that command, the username is added to the connected users list. Now if
the server receives the SAY command from that user, it sends the message to the
UDP multicast group. If a user wants to leave the chat room, it simply sends the
BYE command.The server now removes the user form the user list and suppresses
all possible SAY commands from that user. Figure 5.79 shows the Execute()
method.
Figure 5.79 Execute() Method of ChatServer in ChatServer.cs
public bool Execute ( string command, ref string result )
{
bool ret = true;
Console.WriteLine ( “executing command: ” + command );
// split the command into parts
string[] cmd = command.Split ( new Char[] {‘:’} );
string user = cmd[0];
string operation = cmd[1];
string message = null;
// if the command string contains more than two ‘:’ concatenate the
// splitted rest, this may happen if the message contains ‘:’
if ( cmd.Length > 2 )
{
message = cmd[2];
for ( int i = 3; i < cmd.Length; i++ )
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 263
message += cmd[i];
}
// execute the command
switch ( operation )
{
// user enters the chat room
case “HELLO”:
if ( !this.users.Contains ( user ) )
{
result = “HELLO”;
// add user to currently connected users list
this.users.Add ( user );
// send message to all users
this.udpPeer.Send ( user + ” has joined the chat room” );
}
break;
// user sent message to the chat room
case “SAY”:
// execute only if user is currently connected
if ( this.users.Contains ( user ) && ( message != null ) )
{
result = “OK”;
// send message to all users
this.udpPeer.Send ( user + “: ” + message );
}
break;
// user disconnects from chat room
http://www.syngress.com
Figure 5.79 Continued
Continued
264 Chapter 5 • Network Programming: Using TCP and UDP Protocols
case “BYE”:
// execute only if user is currently connected
if ( this.users.Contains ( user ) )
{
result = “BYE”;
// remove user from currently connected users list
this.users.Remove ( user );
// send message to all users
this.udpPeer.Send ( user + ” has left the chat room” );
}
break;
// unknown command, return an error
default:
result = “ERROR”;
break;
}
return ret;
}
SECURITY ALERT
A client can track all chat room messages if it knows the group IP
address and port. It doesn’t have to be connected with the HELLO command.
The server’s user administration takes care that unconnected users
do not send messages to the chat room.
http://www.syngress.com
Figure 5.79 Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 265
The ChatClient Class
This class is contained on the CD in the file ChatClient.cs. For simplification, the
client chat functionality is contained in the user interfaces classes. Here we have a
Windows Forms application (see Figure 5.80).
Let’s go through a small chat session. Assume that the chat server and client
are still running.Type in a name to the Name data field. After clicking Connect,
the code of its click event handler is executed (see Figure 5.81).This event handler
is the OnConnect() method.
Figure 5.81 OnConnect() Event Handler of the ChatClient Class in
ChatClient.cs
public void OnConnect ( Object sender, EventArgs e )
{
this.proc = new TCPRemoteCommandProcessor ( “127.0.0.1”, TCP_PORT,
true );
string result = null;
this.proc.Execute ( this.name.Text + “:HELLO”, ref result );
this.connected = result.Equals ( “HELLO” );
// enable or disable controls on connection status
}
http://www.syngress.com
Figure 5.80 The Chat Client Form
266 Chapter 5 • Network Programming: Using TCP and UDP Protocols
The class field proc is the command processor that sends commands to the
chat server. After creating an instance of TCPCommandProcessor, the HELLO
command is sent to the server. If the result of the command is HELLO, the connected
flag is set. Now you can type a message into the Message data field. After
clicking Send, the OnSend() method is called.This method is the click event
handler of the Send button (see Figure 5.82).
Figure 5.82 OnSend() Event Handler of the ChatClient Class in ChatClient.cs
public void OnSend ( Object sender, EventArgs e )
{
string result = null;
this.proc.Execute ( this.name.Text + “:SAY:” + this.message.Text,
ref result );
}
The message is sent within a SAY command to the server. Before you get a
look at the message-receiving code, let’s discuss the disconnect code.The
OnDisconnect() method is the click event handler of the Disconnect button (see
Figure 5.83).
Figure 5.83 OnDisconnect() Event Handler of the ChatClient Class in
ChatClient.cs
public void OnDisconnect ( Object sender, EventArgs e )
{
if ( this.connected )
{
string result = null;
this.proc.Execute ( this.name.Text + “:BYE”, ref result );
this.proc.Close ();
this.connected = false;
// enable or disable controls on connection status
}
}
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 267
For disconnecting, first the BYE command is sent. After that, the command
processor will be closed and the connected flag will be cleared.
For receiving messages from the chat server, you use an instance of the
UDPMulticastClient class. As you have seen in the section “Creating a News
Ticker Using UDP Multicasting,” the constructor of UDPMulticastClient needs a
Notify delegate.This delegate will be initialized with the SetMessage() method of
ChatClient.The instantiation of the multicast client is done by the constructor of
the ChatClient class. (see Figure 5.84). A closed event handler for the form is registered
here also.
Figure 5.84 Constructor of ChatClient in ChatClient.cs
public ChatClient ()
{
// create controls
// add an event listener for close-event
this.Closed += new EventHandler ( OnClosed );
// create communication components
this.group = new UDPMulticastClient ( UDP_GROUP_IP, UDP_GROUP_PORT,
new Notify ( SetMessage ) );
}
As a result of receiving a message from the chat server, the SetMessage()
method is called by the multicast client instance.The method simply concatenates
the given string to the text in the multiline data field that shows the messages
(see Figure 5.85).
Figure 5.85 SetMessage() Method of ChatClient in ChatClient.cs
public void SetMessage ( string text )
{
if ( !this.messages.Text.Equals ( “” ) )
this.messages.Text += “\r\n”;
this.messages.Text += text;
}
http://www.syngress.com
268 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Finally, we have a look at the OnClosed() method of the form.This method is
registered to the form as a closed event handler. If the window is closed, the code
is executed (see Figure 5.86).
Figure 5.86 OnClosed() Method of ChatClient in ChatClient.cs
public void OnClosed ( Object sender, EventArgs e )
{
OnDisconnect ( null, null );
this.group.Close ();
}
First, the OnDisconnect() method is called for disconnecting the command
processor if the Disconnect button wasn’t clicked before.Then the multicast
client is closed. Now you can compile and run your example.
Compiling and Running the Example
Please go to the directory on the CD where you can find the files ChatServer.cs
and ChatClient.cs. Start the batch file compile.bat to compile the example. After
successful compiling, start the batch file run.bat. A server and a client is started.
Now you can start a chat session as described in this section. Figure 5.87 shows
the chat client after finishing a very short chat session. Figure 5.88 shows the chat
server after the mentioned session.
http://www.syngress.com
Figure 5.87 Chat Client after a Short Chat Session
Network Programming: Using TCP and UDP Protocols • Chapter 5 269
The next section shows one way you can implement a file sharing peer
with .NET.
Creating a TCP P2P File
Sharing Application
The concept of peer-to-peer (P2P) is becoming more and more important in
networks, and P2P Internet applications such as Napster and Gnutella are widely
popular. But what exactly is a peer-to-peer application?
Well, first consider client/server applications, the most common model for
communication today. A client requests or posts data to or from a server.The
client knows how to request or post data and the server knows how to respond.
For instance, a Web server listens for incoming connections until a browser
requests a page.The Web server reacts only to browsers; it cannot arbitrarily contact
a browser.Any communication is initiated by a client.
In P2P communication, on the other hand, all applications act as clients and
servers at the same time.When peer A requests data from peer B, A acts like a
client and B as a server. However, B can also request data from A, so B acts as a
client and A as a server. Every peer adds a bigger amount of value to network.
Furthermore, no centralized server is needed, which decreases the effort needed
in administrating the data. Another advantage is that if a peer is down, only a
small portion of data is unavailable.
This model does require additional functionality from the peer application.
First, a peer must be able to find other peers.This is called discovery.There are
http://www.syngress.com
Figure 5.88 Chat Server after a Short Chat Session
270 Chapter 5 • Network Programming: Using TCP and UDP Protocols
different strategies for discovery. So-called pure P2P applications can discover
other peers directly. Another approach is to have discovery servers where peers
are registering if they are online.A peer searching for another peer requests the
connection information from the discovery server.
Another key functionality of P2P networks is the so-called content lookup.A
peer must be able to find data contained in the network.A pure P2P application
can directly query the network for data.A second approach is the existence of a
lookup server. Peers send information about their data to the lookup server. For
instance, for file sharing peers, this information can be filename, length, type, and
descriptions about file content. Another possible server is a content server. Peers
upload their files to this server.Then a peer can download the files from this server.
In this section, you create a simplified P2P file sharing application with
reduced functionality.The peer can upload or download files from another peer
only. No discovery or lookup functionality is contained in this peer.
You may think that UDP is the best way to implement such a peer. Indeed
this is how some remote file access systems are implemented. As mentioned, the
advantage of UDP is the performance. However, you would have to implement a
method that guarantees the correct order of the datagrams sent between the
peers, so you’ll use another way to implement the peer.
For the remote functionality, you develop a remote file stream that works in
principle like CORBA, remoting, or RMI—because we’re discussing .NET, we
use the term remoting.
Generally speaking, all remote object architectures work in the same way.A
remote object is divided into two parts. On the client side is a proxy object.The
actual object functionality is implemented on the server side. For communication
between the proxy and the server object, SOAP is used. Figure 5.89 shows this
very simplified remoting architecture.
A proxy object acts like a normal object.The application using that object
doesn’t notice anything about the remote activity (except maybe a lower performance
while executing object methods). Every method call to the proxy object
http://www.syngress.com
Figure 5.89 Very Simplified Remoting Architecture
Client
Proxy
Server
Server
Object
SOAP
Network Programming: Using TCP and UDP Protocols • Chapter 5 271
leads to a protocol request to the server object.The parameters of the method
must be serialized for sending. At the server side, the server object executes the
called method with the deserialized data and returns a result as a response to the
client request.This response also is serialized by the server object and deserialized
at the client side. Now the client object returns the result as a normal object.
Based on this architecture, you develop a similar one.Your proxy server object
is an instance of a class called RemoteFileStreamProxy and RemoteFileStreamServer
respectively. Both classes are contained on the CD in the file Base.cs. Because a
peer is both a client and a server, your peer class FileSharingPeer uses both remote
file classes.The FileSharingPeer class is also contained on the CD in the file
Base.cs. Figure 5.90 shows the architecture of our example.
The Remote File Stream Protocol
Let’s start with the protocol between the proxy and the server.The commands of
the protocol reflect the most important methods of a stream:
 OPEN Reflects the proxy object instantiating.
 READ Sent if the Read() method of the proxy is called.
 WRITE Sent if the Write() method of the proxy is called.
 CLOSE Sent if the Close() method of the proxy is called.
Now let’s look at some example communications. Here we describe the
communication between the proxy and the server class.The proxy requests are
marked with c: and the server responses with s:.
Let’s first have a look at a download scenario.The proxy calls the server for
reading from a file:
http://www.syngress.com
Figure 5.90 Architecture of File Sharing Peer Example
Peer
Remote
File Stream
Proxy
Remote
File Stream
Server
Download()
Upload()
Peer
Remote
File Stream
Proxy
Remote
File Stream
Server
Download()
Upload()
Remote File Stream Protocol
272 Chapter 5 • Network Programming: Using TCP and UDP Protocols
c: OPEN:<file_name>:true<CRLF>
s: (opens the file <file_name> for reading and returns the file length
of <file_name>)
c: READ:<count><CRLF>
s: (returns max. <count> bytes of the file <file_name>)
c: CLOSE<CRLF>
s: (closes connection)
If <count> is bigger than the file length, only the contained bytes of the file
are sent. On the other hand, if <count> is less than the file length, the READ
command will be repeated as long as the end of the file is reached.
The next example shows an upload scenario.The proxy calls the server for
writing to a file:
c: OPEN:<file_name>:false<CRLF>
s: (opens the file <file_name> for writing)
c: WRITE:<count><CRLF>
s: (reads <count> bytes from the client and writes it to the file
<file_name>)
c: CLOSE<CRLF>
s: (closes connection)
If <count> is less than the client’s file length, the WRITE command will be
repeated as long as the end of the file is reached.
The RemoteFileStreamServer Class
This class is used by a thread and has only two methods: a constructor and the
Run() method. Additionally, it has a private class field client of the type
System.Net.Sockets.NetworkStream.The constructor initializes only the client field
(see Figure 5.91).
Figure 5.91 Constructor of RemoteFileStreamServer in Base.cs
public RemoteFileStreamServer ( Socket socket )
{
Console.WriteLine ( “initializing remote filestream server…” );
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 273
this.client = new NetworkStream ( socket );
Console.WriteLine ( “remote filestream server initialized” );
}
As you will see later, the socket comes from a connection request from a
RemoteFileStreamProxy instance.
The Run() method is used by a thread that runs as long as the connected
RemoteFileStreamProxy instance closes the connection. In a loop, all protocol
request commands are handled. Figure 5.92 shows a snippet of the Run() method.
For clarity, we first show the method frame without command processing.
First, a StreamReader is created for easier access to CRLF-terminated command
lines from the proxy class.Then the method reads command lines in an
endless loop.After reading a line, it is split into the parts described in the protocol
section above. Now the parts are processed in the command processing.
Figure 5.92 Snippet of the Run() Method of RemoteFileStreamServer in
Base.cs
public void Run ()
{
Console.WriteLine ( “starting remote filestream server…” );
StreamReader cmdIn = new StreamReader ( this.client );
FileStream f = null;
int count = -1;
byte[] buffer = null;
bool loop = true;
while ( loop )
{
// read the request line
string[] buf = cmdIn.ReadLine ().Split ( new Char[] {‘:’} );
http://www.syngress.com
Figure 5.91 Continued
Continued
274 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Console.WriteLine ( “request received, req=” + buf[0] );
// buf[0] is the command
switch ( buf[0] )
{
// command processing

}
Console.WriteLine ( “request executed, req=” + buf[0] );
}
Console.WriteLine ( “stopping remote filestream server…” );
}
Have a look at the OPEN command processing (see Figure 5.93). On the
OPEN command, the server opens a local file.The file mode, reading or writing,
depends on the read flag—true means reading and false means writing. If the file
is opened for reading, the server returns the number of bytes of the file.
Figure 5.93 OPEN Command Processing of RemoteFileStreamServer in Base.cs
case “OPEN”:
// the name of the local file to open
string file = buf[1];
// open for reading or writing
bool read = bool.Parse ( buf[2] );
// open the local file
f = new FileStream ( “.\\” +
( read ? “download” : “destination” ) + “\\” + file,
( read ? FileMode.Open : FileMode.Create ) );
http://www.syngress.com
Figure 5.92 Continued
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 275
// return the file length to client
if ( read )
{
string length = f.Length.ToString () + “\r\n”;
Byte[] l = System.Text.Encoding.ASCII.GetBytes (
length.ToCharArray () );
this.client.Write ( l, 0, l.Length );
}
break;
On the READ command, the server reads the requested number of bytes
from the local file and returns it to the client (see Figure 5.94).
Figure 5.94 READ Command Processing of RemoteFileStreamServer in Base.cs
case “READ”:
// number of bytes to read
count = int.Parse ( buf[1] );
// read/write buffer
buffer = new byte[count];
// read from the local file
count = f.Read ( buffer, 0, count );
// return the bytes to the client
this.client.Write ( buffer, 0, count );
break;
On the WRITE command, the server reads the requested number of bytes
from the client and writes it to the local file (see Figure 5.95).
http://www.syngress.com
Figure 5.93 Continued
276 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.95 WRITE Command Processing of RemoteFileStreamServer in Base.cs
case “WRITE”:
// number of bytes to write
count = int.Parse ( buf[1] );
// read/write buffer
buffer = new byte[count];
// read bytes from the client
count = this.client.Read ( buffer, 0, count );
// write bytes to the local file
f.Write ( buffer, 0, count );
break;
On the CLOSE command, the server closes the local file and the connection
to the client.The loop terminates and the so does the thread (see Figure 5.96).
Figure 5.96 CLOSE Command Processing of RemoteFileStreamServer in Base.cs
case “CLOSE”:
// close local file
f.Close ();
// close connection to the client
this.client.Close ();
// stop the loop
loop = false;
break;
The RemoteFileStreamProxy Class
This class is derived from the abstract class System.IO.Stream. An instance of this
class can be used as a normal stream. For instance, it can be given to a method
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 277
that generally works on streams. Here we focus on the constructor and the
Read(), Write(), and Close() methods. For all other methods and properties that
must override abstract method’s properties, refer to the class code in the file
Base.cs on the CD.
First, the constructor (see Figure 5.97) opens the connection to the server,
sends the OPEN command, and receives the remote file length if the open mode
is read.
Figure 5.97 Constructor of RemoteFileStreamProxy in Base.cs
public RemoteFileStreamProxy ( string host, int port, string file,
bool read )
{
this.read = read;
this.remoteFile = new TcpClient ( host, port ).GetStream ();
this.open = true;
Send ( “OPEN:” + file + “:” + read );
if ( read )
{
this.length = int.Parse (
new StreamReader ( this.remoteFile ).ReadLine () );
}
}
The next one is the Read() method (see Figure 5.98). It sends the READ
command to the server and receives the bytes sent by the server.
Figure 5.98 Read() Method of RemoteFileStreamProxy in Base.cs
public override int Read ( byte[] buffer, int offset, int count )
{
// to do: implement exceptions here as described in .NET reference
if ( !CanRead )
throw new NotSupportedException ( “stream cannot read” );
http://www.syngress.com
Continued
278 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Send ( “READ:” + count );
return this.remoteFile.Read ( buffer, offset, count );
}
Now, let’s look at the Write() method (see Figure 5.99). It sends the WRITE
command and the bytes to write to the server.
Figure 5.99 Read() Method of RemoteFileStreamProxy in Base.cs
public override void Write ( byte[] buffer, int offset, int count )
{
// to do: implement exceptions here as described in .NET reference
if ( !CanWrite )
throw new NotSupportedException ( “stream cannot write” );
Send ( “WRITE:” + count );
this.remoteFile.Write ( buffer, offset, count );
}
Finally, the Close() method (see Figure 5.100). It sends the CLOSE command
to the server and then it closes the connection.
Figure 5.100 Close() Method of RemoteFileStreamProxy in Base.cs
public override void Close ()
{
this.open = false;
Send ( “CLOSE” );
this.remoteFile.Close ();
}
http://www.syngress.com
Figure 5.98 Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 279
As you have seen, the methods of the proxy are simpler than those of the
server because the functionality is implemented in the server.
The FileSharingPeer Class
FileSharingPeer has two main parts.The first part is a thread that accepts proxy connections.
The thread creates for each connection request a RemoteFileStreamServer
instance that handles the commands coming from the proxy.The second part contains
two methods: Download() and Upload(). Both methods each create an instance
of RemoteFileStreamProxy that communicates with the server for the requested functionality.
Have a look at the class fields (see Figure 5.101).
Figure 5.101 Class Fields of FileSharingPeer in Base.cs
// listener for incoming connections
private TcpListener listener = null;
// listening server thread
private Thread server = null;
Now, let’s discuss the constructor (see Figure 5.102). It first initializes and
starts a listener for incoming connection requests.Then it creates and starts a
thread that uses the Run() method.This method is described later.
Figure 5.102 Constructor of FileSharingPeer in Base.cs
public FileSharingPeer ( int localPort )
{
Console.WriteLine ( “initializing file sharing peer, local port=” +
localPort );
// initialize proxy listener
this.listener = new TcpListener ( localPort );
this.listener.Start ();
// start listening thread for incoming connection requests
this.server = new Thread ( new ThreadStart ( Run ) );
this.server.Start ();
http://www.syngress.com
Continued
280 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Console.WriteLine ( “file sharing peer initialized” );
}
In the constructor, you see the use of the Run() method for the server
thread.This method handles the incoming connection requests (see Figure
5.103). After a proxy connects to the server, the resulting socket is given to the
RemoteFileStreamServer instance.Then a thread for this instance is created and
started.The Run() method of the RemoteFileStreamServer instance is used by this
thread for handling the proxy requests.
Figure 5.103 Run() Method of FileSharingPeer in Base.cs
public void Run ()
{
while ( true )
{
Socket s = listener.AcceptSocket ();
Console.WriteLine ( “client connected” );
RemoteFileStreamServer srv = new RemoteFileStreamServer ( s );
Thread th = new Thread ( new ThreadStart ( srv.Run ) );
th.Start ();
}
}
The Close() method stops the proxy listener and the server thread (see
Figure 5.104).
Figure 5.104 Close() Method of FileSharingPeer in Base.cs
public void Close ()
{
// stop proxy listener
this.listener.Stop ();
http://www.syngress.com
Figure 5.102 Continued
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 281
// stop server
this.server.Abort ();
this.server.Join ();
}
As mentioned before, the proxy class is derived from System.IO.Stream.
System.IO.FileStream is also derived from this class. So, downloading and
uploading file is nothing else than reading data from one stream and writing this
data to another stream. In other words, for downloading and uploading, you need
only one method for a copy functionality.And now you have found a name for
the method: Copy() (see Figure 5.105).
Figure 5.105 Copy() Method of FileSharingPeer in Base.cs
protected void Copy ( Stream sin, Stream sout )
{
byte[] buf = new byte[4096];
long l = 0;
while ( l < sin.Length )
{
int n = sin.Read ( buf, 0, 4096 );
sout.Write ( buf, 0, n );
l += n;
}
sout.Close ();
sin.Close ();
}
The Download() and Upload() methods are opening a local file and a proxy
stream. Download() reads from the proxy stream and writes to the local file.
Upload() does the inverse. Figure 5.106 shows both methods.
http://www.syngress.com
Figure 5.104 Continued
282 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.106 Download() and Upload() Methods of FileSharingPeer in
Base.cs
public void Download ( string remoteHost, int remotePort,
string file )
{
Console.WriteLine ( “downloading file, host=” + remoteHost +
“, port=” + remotePort + “, file=” + file + “…” );
Stream sin = new RemoteFileStreamProxy ( remoteHost, remotePort,
file, true );
Stream sout = new FileStream ( “.\\destination\\” + file,
FileMode.Create );
Copy ( sin, sout );
Console.WriteLine ( “file downloaded, host=” + remoteHost +
“, port=” + remotePort + “, file=” + file );
}
public void Upload ( string remoteHost, int remotePort, string file )
{
Console.WriteLine ( “uploading file, host=” + remoteHost +
“, port=” + remotePort + “, file=” + file + “…” );
Stream sin = new FileStream ( “.\\upload\\” + file, FileMode.Open );
Stream sout = new RemoteFileStreamProxy ( remoteHost, remotePort,
file, false );
Copy ( sin, sout );
Console.WriteLine ( “file uploaded, host=” + remoteHost +
“, port=” + remotePort + “, file=” + file );
}
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 283
Now you can compile and run our example.You will learn something about
the user interface in the next section.
Compiling and Running the Example
Please go to the directory on the CD where you can find the file
FileSharingPeer.cs. Start the compile.bat batch file. Start the resulting
FileSharingPeer.exe two times.You can do this by simply starting the run.bat file
in the same directory.
Two forms appear.Try the download or upload by choosing a file and
pressing the button for the functionality you want to try. Also have a look at the
two DOS consoles. Now the forms should be similar to Figure 5.107.
Note that this peer class just simulates a file sharing peer. It has a download
and upload functionality only, and it knows which files can be found on the
other peer.
Access to Web Resources
We’ve now investigated remote operating classes that encapsulate communication
protocols and work like local classes; now we’ll have a short look at some Web
access classes of the .NET Framework.Three classes are of particular interest:
System.Net.WebRequest, System.Net.WebResponse, and System.Net.WebProxy.
As mentioned in the introductory sections, the abstract classes WebRequest and
WebResponse are general APIs to underlying protocol handlers like an HTTP
handler.Your goal is to develop a small application that makes a request via an
HTTP proxy to a Web server and receives the response.You could use such an
application as a base for a Web browser or a crawler, for instance.
http://www.syngress.com
Figure 5.107 File Sharing Peers in Action
284 Chapter 5 • Network Programming: Using TCP and UDP Protocols
A crawler is an application that “walks” through the Web along the links in the
HTML documents to track the structure behind. Crawlers are used by search
engines to create a searchable database of documents. A search request to a search
engine means that a query to the database of the engine is made.A crawler can
also be useful for intranets to determine the structure, for example, for creating
index pages.
General Usage of Needed .NET Classes
Today many companies use proxies to channelize and control outgoing requests
from the company to the Web (see Figure 108).
So, you can first define the parameters for the proxy to give them to the
requesting class.This class then makes the request and receives the results page:
// create a request to the Syngress homepage
WebRequest request = WebRequest.Create (
http://www.syngress.com/&#8221; );
// set the proxy IP address an port
request.Proxy = new WebProxy ( proxyHost, proxyPort );
// set the proxy user and password
request.Proxy.Credentials = new NetworkCredential ( proxyUser,
proxyPassword );
// get the reponse page
http://www.syngress.com
Figure 5.108 Clients Access the Internet via Proxy
Client 1
Client 2 Proxy
Client n
Internet
Network Programming: Using TCP and UDP Protocols • Chapter 5 285
WebResponse response = request.GetResponse ();
// get the response stream
Stream s = response.GetResponseStream ();
// read from the stream
// close the stream
s.Close ();
WebRequest.Create() is a static method that creates a request object depending
on the protocol defined in the URL parameter.The resulting object is of the
type System.Net.HttpWebRequest because the protocol of the URL is HTTP.The
string proxyHost and the int proxyPort are the IP address and port of your proxy.
The System.Net.NetworkCredential class holds the authorization parameters for the
proxy, that is, proxyUser and proxyPassword are the username and password needed
to go through the proxy.
A Web Access Client
Now, let’s develop a small form that shows the HTML code of a Web page. It
looks a little bit like a Web browser (see Figure 5.109).
On the top of the form are fields for the proxy parameters.The URL field is
for typing in the destination URL (for example, http://www.syngress.com/).The
untitled field contains the source of the HTML page specified by the URL.
http://www.syngress.com
Figure 5.109 HTML Page Source Viewer
286 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Before going into the form, you need to use a small class that allows easier
handling of the Web access classes. It is called WebAccessClient and is included on
the CD in the file Base.cs.The class has two constructors and the Get() method.
One constructor is for initializing without using a proxy and one with a proxy.
The constructor for initializing the client without a proxy simply does nothing.
The Get() method returns a Web page based on a request URI. Figure 5.110
shows the class fields of WebAccessClient.
Figure 5.110 Class Fields of WebAccessClient in Base.cs
// proxy parameters
private WebProxy proxy = null;
The proxy field holds the proxy parameters and is initialized by the constructor.
The constructor code using a proxy looks like Figure 5.111.
Figure 5.111 Constructor Using Proxy of WebAccessClient in Base.cs
// with proxy
public WebAccessClient ( string proxyHost, int proxyPort,
string proxyUser, string proxyPassword )
{
// create a proxy
WebProxy proxy = new WebProxy ( proxyHost, proxyPort );
// set user name and password for proxy
proxy.Credentials = new NetworkCredential ( proxyUser,
proxyPassword );
// disable proxy use when the host is local
proxy.BypassProxyOnLocal = true;
// all new requests use this proxy info
GlobalProxySelection.Select = proxy;
}
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 287
First, we create a proxy object as shown in the general usage section. But now
comes something new—the property BypassProxyOnLocal is a flag that advises the
request class not to try to connect through the proxy if a local URL such as
localhost is requested (for example, a local Web server on the same computer).
The other new element is the GlobalProxySelection class of the namespace
System.Net.This class has a static property Select.This property is a proxy instance
that WebRequest instances use to connect to the outside.You can set this once, and
you don’t have to set the Proxy property of WebRequest. (Note that this doesn’t
make a lot of sense in your class because there is only one constructor, but it’s
worth mentioning.)
The Get() method requests and returns a stream containing a Web page for a
given URL (see Figure 5.112). It is a very simple method that does nothing too
different from the example code in the general usage section.
Figure 5.112 Get() Method of WebAccessClient in Base.cs
public Stream Get ( string url )
{
// create a request based on the URL
WebRequest req = WebRequest.Create ( url );
// get the response
WebResponse res = req.GetResponse ();
// return a stream containing the response
return res.GetResponseStream ();
}
Now we come to the form.The class is called WebAccessClientForm and is
contained on the CD in the file WebAccessClient.cs.This class has only two
methods: a constructor that initializes all controls, and a key event handler that is
called if the URL field receives a KeyUp event. Let’s focus here on the event handler
(see Figure 5.113).
http://www.syngress.com
288 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Figure 5.113 KeyUp Event Handler of WebAccessClientForm in
WebAccessClient.cs
public void OnKeyUp ( Object sender, KeyEventArgs a )
{
// read a page if the return key was pressed
if ( a.KeyCode == Keys.Return )
{
// clear the result field
this.text.Text = “”;
// create a Web access client
WebAccessClient client = null;
if ( this.proxyHost.Text.Equals ( “” ) )
client = new WebAccessClient ();
else
client = new WebAccessClient ( this.proxyHost.Text,
int.Parse ( this.proxyPort.Text ),
this.proxyUser.Text, this.proxyPassword.Text );
// get the response stream
StreamReader s = new StreamReader (
client.Get ( this.url.Text ) );
// read the response and write it to the text field
int BUFFER_SIZE = 4096;
Char[] buf = new Char[BUFFER_SIZE];
int n = s.Read ( buf, 0, BUFFER_SIZE );
while ( n > 0 )
{
this.text.Text += new String ( buf, 0, n );
n = s.Read ( buf, 0, BUFFER_SIZE );
}
http://www.syngress.com
Continued
Network Programming: Using TCP and UDP Protocols • Chapter 5 289
// close the stream
s.Close ();
}
}
The request should be made if the Return key was pressed in the URL
field.This a little bit browser-like. First, you can create a WebAccessClient instance
based on the proxy parameter fields.Then, you can make the request by calling
the Get() method.The StreamReader is for your convenience because its Read()
method reads into a char array that you can easily convert to a string.This string
is simply concatenated with the content of your result text field. Finally, the
stream is closed. Now you can compile and run the example.
Compiling and Running the Example
Please go to the directory on the CD where you can find the file
WebAccessClient.cs. Start the compile.bat batch file. After successful compiling,
double-click on WebAccessClient.
Now a form appears. Please type in the proxy information and an URL.
Finally press the Return key while the cursor resides in the URL field. Now the
form should be similar to Figure 5.114.
This example is only a start into Web access with .NET WebRequest classes.
These classes offer many more features. Here you’ll focus on HttpWebRequest.An
http://www.syngress.com
Figure 5.113 Continued
Figure 5.114 HTML Page Source Viewer after Doing a Request
290 Chapter 5 • Network Programming: Using TCP and UDP Protocols
instance of this class is returned by the Create() method of WebRequest if the given
URL starts with http://.
The HttpWebRequest class has a large number of properties to directly influence
the HTTP request. Among others are properties for directly manipulating
HTTP request header fields such as Accept, Content-Length, Content-Type, and so
on. All headers can be accessed by the property Headers.This property is an
instance of WebHeaderCollection and contains the headers exposed by the
HttpWebRequest properties or unchangeable headers needed by the system. Please
see the .NET reference documentation for mutable and immutable headers.
Some other functionality can be influenced directly by manipulating properties
of HttpWebRequest.The following sections describe a part of it, especially the
request method, redirection, authentication, and cookie handling.
Request Method
By default, an HttpWebRequest instance created by the Create() method requests
with the HTTP GET. If you want to use another method, such as POST, you
can do this by setting the Method property of HttpWebRequest.
Other HTTP 1.1 methods are HEAD, PUT, DELETE, TRACE, or
OPTIONS. If you want to use a version other than 1.1, you must set the
ProtocolVersion property with the needed version. Please have a look at the .NET
reference documentation for the HttpVersion class.The default HTTP version of
HttpWebRequest is HttpVersion.Version11.
Redirection
Normally, if you are implementing an HTTP client, you must react on the
HTTP status codes starting with 301.These codes define redirection methods.To
see which status code the response to your request has, have a look at the
StatusCode property of the HttpWebResponse instance returned by the
GetResponse() method of HttpWebRequest.
Mostly, redirection means that the requested page is not available anymore
under the specified URL (see the W3C Web site for HTTP specifications, at
http://www.w3.org).The response then contains the new URL of the requested page
or another redirection page, so you then have to re-request with the new URL.
With the HttpWebRequest class you do not have to do this by hand if you do
not want to. If the Boolean property AllowAutoRedirect is set to true the class does
all the work for you. If this property is set to false, you must implement redirection
by yourself.The default value of this property is true.
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 291
Authentication
Sometimes a Web site requests an authentication from you for login.This is, if the
response has the HTTP status code 401 Unauthorized. Normally, if you know what
you need for authentication (for example, username and password) you re-request
the page with these requirements contained in the WWW-Authenticate HTTP
header.With the HttpWebRequest class, you can do this easily with the
PreAuthenticate and Credentials properties.
The following procedure is the same whether you get a 401 Unauthorized
response or you know before for which Web site you need an authentication:
1. Set the PreAuthenticate property to true.
2. Create an instance of NetworkCredential. It is the same procedure as
described for proxies in this section.
3. Set the Credentials property to the credential you created in Step 2.
4. Request or re-request the page.
Cookies
Normally, cookies are used to store a state needed during long-term communication,
such as revisiting a page. For instance, a Web site stores some customer information
in a cookie on your computer. If you revisit the site it requests the
cookie, in order to know immediately who you are, so that a page may be customized
especially for you.
Because cookies are particular to the sites you request, we can only give you
direction to get more detail about cookies:
 If you are new to cookies, please have a look at general documentation
about cookies (RFC 2965, Proposed Standard for HTTP State
Management Mechanism).
 In the .NET reference documentation, you will find the System.Net
.Cookie class. As the name implies, this class represents an HTTP cookie.
 The HttpWebRequest class has a property named CookieContainer.This is
an instance of the System.Net.CookieContainer class and contains all
cookies for the request.
 The HttpWebResponse class has a property named Cookies.This is an
instance of the System.Net.CookieCollection class and contains all cookies
of the response.
http://www.syngress.com
292 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Summary
This chapter presents some examples of how to implement networking applications
with the .NET Framework.
The most widely used protocols in networking are TCP and UDP.You can use
TCP for reliable client/server applications because it is a reliable and connectionoriented
protocol. On the other hand, you can use UDP for applications such as
DNS, time servers, Internet telephony, and streaming multimedia in general because
it is faster than TCP.The better performance is caused by the relative unreliability
of the protocol. If a packet of data gets lost, no administrative overhead is needed to
resend it. UDP also supports another option:multicasting. Multicasting means that
one application sends data simultaneously to a group of applications without the
knowledge of which kinds of applications are listening, or how many.
The chapter discussed the meaning of ports for developing networking applications—
only if an application is registered with a port it can be reached by
other processes.
As introductory examples, we developed simple remote command processing
with TCP and UDP.These examples show how you can use the .NET networking
classes for networking and what the differences are in using the TCP
and UDP classes.These differences are caused by the different natures of the protocols.
TCP is a connection- and stream-oriented client/server protocol. So, the
.NET TCP classes reflect the client/server model by providing client and server
classes. Clients have methods for connecting to and disconnecting from a remote
host. Servers have methods for listening for and accepting incoming connections.
Furthermore, after successful connection,TCP classes provide stream classes for
accessing the data streams between client and server.The .NET UDP classes on
the other hand have no connection establishment and stream functionality.
Because UDP is connection-less and packet-oriented, these classes need send and
receive methods only where network addresses and data are given directly. Data is
sent and received without making a connection. UDP is peer-oriented, reflected
in the absence of explicit client and server classes.The same class is used for
sender and receiver applications.
The TCP and UDP examples are followed by a UDP multicasting example
and a news ticker application. Multicasting is an option of UDP where a sender
application sends data to an IP address.This address represents a group of applications.
All these applications are able to receive the sent data. An application can
take part in the group simply by registering with the group IP address.
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 293
The next example was a client/server chat application. It showed how you
can use TCP and UDP in combination.You can use TCP to send messages to a
server in a reliable way.You can use UDP multicasting for sending the chat messages
to all clients that take part in the chat room.The most important technique
shown with this example is how Web servers handle multiple client requests at
the same time. If a TCP client establishes a connection to a server listener class,
the server creates a socket for only this connection, then the server is ready for
listening on its port for new clients.
A simple P2P file sharing application was the next example. Here we showed
how to use the client/server-like protocol TCP for developing P2P applications.A
peer must act like a TCP server and a TCP client simultaneously. Such a peer must
be divided in two parts. First, a TCP client that connects to another peer for
sending and receiving data (upload and download of files). Second, there must be a
TCP server that accepts connection from another peer, so that this peer can download
and upload files from the TCP server.The most important technique in this
section is how to implement remote object access—such as remoting or
CORBA—in a very simplified way.We developed a remote file stream. On the
client side, we have a proxy, and on the server side a server class.The remote file
stream on the client side is used similar to a “normal” file stream. But methods such
as Open(), Close(), Read(), and Write() are working over a network using a simple
communication protocol.The remote file stream class is derived from the .NET
stream class and can be used in the same way as other stream classes. An application
that works only on streams does not recognize a difference from other streams.
Finally, we show how to use special .NET classes for accessing Web resources.
With the System.Net.WebRequest and System.Net.WebResponse classes, accessing a
Web server is simple using only a few methods.These classes encapsulate HTTP
or FTP handling.We also have shown how to request a Web page through a
proxy. A proxy is an intermediate process between a Web client and server to
channelize and control communication. Finally, we mentioned some other techniques
in accessing Web resources by using .NET classes.
We described how to change request methods with the System.Net
.HttpWebRequest class.The default method of this class is the HTTP GET,
but for instance, some applications need the HTTP POST method.
Another point mentioned was redirection of Web pages. Sometimes it is necessary
to change the URL of a Web page.Maybe this is caused by changing the host
name of the Web server or other administrative work. But the page should still be
accessible via the old URL.The Web server then returns a special redirection
status code and a new URL for the page.The client then requests the page with
http://www.syngress.com
294 Chapter 5 • Network Programming: Using TCP and UDP Protocols
the new URL.With .NET, we do not have to develop this by ourselves—the
HttpWebRequest class does this work for us.
This class also does authentication handling. If a Web server requests authentication
for accessing a page, we do not have to develop the HTTP authentication
procedure. If authentication is requested by a Web server, it returns a special status
code.The client now must re-request by adding the authentication information,
for instance, username and password.This work is done by the HttpWebRequest.
Finally, we mentioned cookie handling. Cookies are small packets of information
that bring states to the state-less HTTP. State-less means that every HTTP
request/response pair is independent from former and further communication.
State information on the client side can be stored with cookies.The Web server
requests a cookie and knows in which state, for instance, a Web shop transaction
is. Because of the special character of cookies depending on the application and
their use, we showed only the .NET cookie class and where to find cookies in
request and response classes.
Solutions Fast Track
Introducing Networking and Sockets
􀀻 Networking is inter-process communication.Two or more processes
communicate with each other.The processes can run on the same or
different computers or other technical devices.
􀀻 The most important networking API is the socket.
􀀻 Most networks today use the Internet Protocol (IP) as base protocol.The
most widely used application protocols are the Transmission Control
Protocol (TCP) and the User Datagram Protocol (UDP).TCP and UDP
run on IP.
􀀻 TCP is a reliable connection- and stream-oriented point-to-point
protocol.The communication is client/server–oriented.The delivery and
order of data is guaranteed.
􀀻 UDP is a connection-less and datagram-oriented best-effort protocol.
The delivery and order of data is not guaranteed. It can be used as a
point-to-point protocol (unicasting) or as a point-to-group protocol
(multicasting).
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 295
Example TCP Command Transmission and Processing
􀀻 For TCP communication, the easiest way is to use the System.Net
.TcpListener and System.Net.TcpClient classes.
􀀻 This section showed how a TcpClient on the client side connects to a
TcpListener on the server side.
􀀻 The client sends a command and receives a result.
􀀻 This is similar to a browser making a request to a Web server and
receiving a Web page.
Example UDP Command
Transmission and Processing
􀀻 For UDP communication, the easiest way is to use the System.Net
.UdpClient classes.
􀀻 This section showed how a UdpClient on the client side communicates
to another UdpClient on the server side.The client sends a command
and receives a result.
􀀻 This example is similar to the example in the “Example TCP Command
Transmission and Processing” section for showing the differences
between TCP and UDP.
Creating a News Ticker Using UDP Multicasting
􀀻 UDP can be used for sending data to a group of peers (multicasting).
􀀻 For multicasting, System.Net.UdpClient can also be used.
􀀻 This section showed how to develop multicasting between UDP peers.
Creating a UDP Client Server Chat Application
􀀻 This example combined our TCP and UDP knowledge.
􀀻 TCP is used for transferring messages to the chat server; UDP is used
for sending the messages to all connected chat clients.
http://www.syngress.com
296 Chapter 5 • Network Programming: Using TCP and UDP Protocols
Creating a TCP P2P File Sharing Application
􀀻 This example showed how to develop TCP P2P applications.
􀀻 A TCP peer has one TCP server and one TCP client.
􀀻 This example showed in a very simplified way how remote object access
such as remoting or CORBA is implemented.This is done by a so-called
remote file stream.
Access to Web-Resources
􀀻 You can easily create access to Web resources with the .NET classes
System.Net.WebRequest and System.Net.WebResponse.
􀀻 WebRequest makes a request to a Web resource, such as a Web server.The
result of the request is WebResponse instance that gives access to a stream,
such as representing the requested Web page.
􀀻 Communicating through proxies is made with help of the
System.Net.WebProxy class.
http://www.syngress.com
Network Programming: Using TCP and UDP Protocols • Chapter 5 297
Q: What is TCP and how does is work?
A: Today, most networks use the Internet Protocol (IP) on the network protocol
layer. IP is an unreliable data packet (datagram) delivery service where the
delivery and order of packets is not guaranteed (best-effort).The Transmission
Control Protocol (TCP) is designed to address this problem—it guarantees
reliability. If packets are lost,TCP can resend them. If the order of packets is
not correct,TCP can put them in the right order.On the other hand,TCP is
stream-oriented, that is, you can read your data byte-by-byte. Finally,TCP is
connection-oriented, that is, a client opens a connection to a server, communicates
with the server, and after finishing, it closes the connection.
Q: What is UDP and how does it work?
A: The User Datagram Protocol (UDP) provides an unreliable datagram-oriented
protocol on top of IP.The delivery and order of datagrams are not guaranteed.
It is connection-less, that is, a UDP application does not have to connect
explicitly to another. Datagrams are simply sent or received.
Q: What is multicasting?
A: Multicasting means that a set of applications can be grouped together by an
IP address. If an application sends data to that IP address, all members of the
group receive the data. UDP provides this service.
Q: When do I use TCP, and when do I use UDP?
A: You should use TCP if a reliable connection is necessary.You can use UDP
when you don’t need reliability when or you need more performance.
Q: Why does UDP multicasting sometimes not work under the German version
of Windows 2000?
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.
298 Chapter 5 • Network Programming: Using TCP and UDP Protocols
A: We encountered problems with the German version of Windows 2000
Professional and Service Pack 2 using the Beta 2 of the .NET Framework. At
the time of this writing, no solution has been found, either on the Microsoft
Web site or from other sources.This problem seems to be deeper than the
.NET Framework.Tests with Java applications also lead to negative results. At
the time of this writing, we do not know if the problems also exist with
other Windows 2000 versions.
Q: Why does the UDP unicast example sometimes not work under the German
version of Windows NT 4?
A: We encountered a problem with the Beta 2 of the .NET Framework running
under German Windows NT 4 Service Pack 6a. If the example is started
with the run.bat file, UDP unicasting doesn’t work. If the example is started
directly, it works well. At press time, we do not know if the problems also
exist with other Windows NT versions.With Windows 2000, the example
works well when started directly or via batch file.
http://www.syngress.com
Remoting
Solutions in this chapter:
 Introducing Remoting
 Creating a Simple Remoting Client Server
 Creating an Intranet Application
 Creating Service-Based Applications
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 6
299
300 Chapter 6 • Remoting
Introduction
Ever since the early days of Windows programming, there has been a gradual
improvement in operating system stability. Much of this is due to the separation
of applications into distinct processes so that each has its own area of memory.
Developers have had to use many tricks to get these applications to communicate
with each other. Some of these methods included using the system clipboard,
sending windows messages, using the Visual Basic (VB) SendKeys function or similar,
transferring “message” files, or declaring an area of shared memory. Each of
these methods had pros and cons and were generally “hacks” around the separation
of processes.When Microsoft’s Component Object Model (COM) arrived,
the situation vastly improved, and such tricks were no longer needed, but COM
did introduce a number of issues with versioning, registration, and administration
that a generation of Windows developers has had to deal with. Now with the
.NET platform, you get cross-application communication built-in, which provides
you with an amazing amount of flexibility and control as to how you want
your applications to communicate with each other.
Every application on the .NET platform exists in its own unique Application
Domain.And every Application Domain is able to expose objects to the outside
world from any type of application—from simple console applications to Windows
Forms and Internet Information Server (IIS)–hosted applications.To enable applications
running in one Application Domain to communicate with other applications
in another Application Domain, you use remoting.Or you could say remoting
allows you to call methods and pass objects across Application Domains.
The Remoting API on the .NET platform takes a different approach than the
other application programming interfaces (APIs), such as Distributed COM
(DCOM) and Remote Method Invocation (RMI) for communication and message
format. Rather than relying on a proprietary message and protocol, the
Remoting API uses well-established standards such as Simple Object Access
Protocol (SOAP) for messaging and Hypertext Transfer Protocol/Transmission
Control Protocol (HTTP/TCP) protocols for communication.This allows applications
to communicate just as easily across the Internet as they do within the
enterprise.
To understand how remoting works, imagine that you need to create your
own method of cross-application communication. Imagine that you have an
object that needs to accept calls from client applications across HTTP. First, you’d
need to define your object’s location as a URL of some kind.Then you would
need to choose a port that the object should listen to.You would also need some
http://www.syngress.com
http://www.syngress.com
way of publishing the interface of your object so that clients would know what
methods are available to call, and you would need a method of describing the
interface and handling the messaging between objects.The creators of the .NET
Framework have done just that and have exposed the remoting functionality as a
powerful way for programmers to start getting their applications to communicate.
This chapter introduces the remoting framework and provides many examples
from real-world scenarios that occur during development. First, we get an
overview of how remoting works and look at the variety of choices available to
the developer as to how remoting is configured.
In the second part of the chapter, we produce a simple remoting example that
is gradually extended to use a range of remoting services.We also take a look at
how to deal with issues that developers face during the development lifecycle in
regard to deployment, debugging, administration, documentation, and versioning
while using the remoting framework.
Introducing Remoting
Remoting provides you with a number of choices as to the method and configuration
of communication used. Configuration areas are the choice of channel, type
of hosting application, the activation model, the configuration method, and the
method of exposing server metadata to the client application.
The channel is the means of communication used by an application to call to
a remote object; the selection is between HTTP and TCP (SMTP doesn’t appear
to be ready in Beta 2).The HTTP channel is mostly used for Internet communication
where firewalls need to be negotiated.The TCP channel has a performance
gain by using direct socket connections over an arbitrary port selected by
the developer. Both channels use SOAP for communication; the TCP channel
defaults to use a faster (but proprietary) binary representation of the SOAP message,
whereas the HTTP channel defaults to use the XML standard.The TCP
channel can also use the normal XML-formatted SOAP messaging format.
The selection of the hosting application for the remote object is the next
choice.A hosting application must be configured to listen on a channel and
create the requested object in its own AppDomain when required. In Visual Basic
6, developers often used IIS or COM+ services to host remote objects—the mysterious
dllhost.exe that you may see running in your Windows 2000 Task
Manager is the hosting application used by COM+.With the .NET Framework,
you can still use these hosting services, but you can gain more control by writing
your own hosting applications.When creating your own hosting application, as
Remoting • Chapter 6 301
302 Chapter 6 • Remoting
we do in the first example, you may choose from a Console application,
Windows Service, or Windows Forms application.
Choice number three is the activation model for the remote object. SingleCall
objects are stateless in that they handle only single calls from clients and do not
hold state between calls. After the call is handled, the object is discarded. Singleton
objects can be shared between multiple clients.They are often used when the
resources needed to initialize the object are large and the object’s state needs to
be preserved between method calls.You need to remember that Singleton objects
do have a default lifetime and may be recycled—we’ll see later how developers
can control the object’s lifetime to suit their needs. Client Activated Objects
(CAOs) allows a client application to create a remote instance of the object for
exclusive use and to preserve state between remote method calls.
Choice number four is the method of configuring the remote server.The
host application can programmatically configure itself on startup or a configuration
file can be used. Of course, using an external file to hold remoting configuration
data enables changes to be made without a recompile of the source code.
The configuration information contains the channel, port, activation model, type
name, and assembly name of the object. A Uniform Resource Identifier (URI),
which clients use to identify the object, is also specified.
The final choice is how the client obtains the remote object’s metadata. Again
comparing with Visual Basic 6, a server object’s interface definition had to be on
the client, either as a type library or an exported MTS package, to enable the
client VB code to make the call over DCOM.With remoting, the situation is
similar but improved by the .NET Framework’s use of metadata.The first method
is to set a reference to the remote object’s DLL in the client project so that the
compiler can extract the metadata.The second method, but only if using the
HTTP channel, is to use the soapsuds.exe utility to generate a “proxy” class from
the remote object’s URI.This proxy class can then be included in the client project
and used as if it is a local .NET type. Internally, the proxy class will route the
call to the remote object.
Remoting Architecture
An end-to-end picture of remoting is as follows.The host application is loaded
and registers a channel and port on which to listen for incoming calls.The configuration
file, if any, is read and an object’s remoting information is loaded—the
host application can now map a URI to the physical assembly and instantiate the
object when required.The client application also registers the same channel and
then attempts to create a new instance of the remote class.The remoting system
http://www.syngress.com
Remoting • Chapter 6 303
handles the request for a new instance by providing a proxy object in place of the
actual object on the server.The actual object is either created immediately for
CAOs or on the first method call for Singleton/Singlecall objects—the remoting
framework takes care of this for you automatically.When the client calls a
method on the proxy object, the information is sent across the channel to the
remote object.The remoting system will then pass back the results of the method
across the channel in the same manner.
Creating a Simple
Remoting Client Server
We’ll now create a simple client server application to demonstrate the usage of
the remoting framework.The code for the server side is located in the ListServer
directory of the CD—double-click on the solution file ListServer.sln so that you
load both the server and the hosting application together. First, we’ll create the
remote class named CompanyLists that contains the functionality.All of the following
code is on the CD.
NOTE
The code in this chapter uses localhost as the target server—this will selfreference
your local computer so that you may use both the client and
server code on the same PC. If you wish to place the server-side code on
a remote server, you will need to replace localhost with the correct server
name.
Creating the Remote Server Object
The remote server object contains all the server-side functionality for our
application:
1. Create a new Class Library application in Visual Studio named
ListServer.
2. Right-click the default Class1.cs module in the Solution Explorer and
choose Delete.
3. Right-click the ListServer project in the Solution Explorer, select Add
| Add Class, and name your new class CompanyLists.cs.
http://www.syngress.com
304 Chapter 6 • Remoting
4. Modify the class declaration to inherit from MarshalByRefObject so that a
reference to the object can be passed remotely:
public class CompanyLists: MarshalByRefObject
{
}
5. Add a private variable to the CompanyList class that contains an array of
strings:
private String[] Countries = {“Spain”,”France”,”Italy”};
6. Add a public method to CompanyList that returns the array of strings
defined in the preceding step.The complete class should appear as:
public class CompanyLists: MarshalByRefObject
{
private String[] Countries = {“Spain”,”France”,”Italy”};
public String[] getCountryList()
{
return Countries;
}
}
The CompanyList class can now be loaded by a hosting application for
remoting. If you already have classes that you’d like to make remoting aware of,
it’s as simple as inheriting from MarshalByRefObject and then recompiling.
NOTE
If your class must receive and send objects during method calls, you
will need to use the <Serializable> custom attribute to pass these
objects by value or inherit from MarshalByRefObject to pass by reference.
An example of this is shown later. If your class already inherits from
another class, you’ll need to make the parent class inherit from
MarshalByRefObject because multiple inheritance is not allowed in C#.
http://www.syngress.com
Remoting • Chapter 6 305
Creating the Hosting Application
Now we create the hosting application.This will be a console application initially,
but in the real world, this would probably be a Windows Service application:
1. From the Visual Studio menu, choose File | Add Project | New
Project. Select Console Application and name the new project
ListHost.
2. Rename the default Class1.cs file to CompanyListHost.cs.
3. Add a reference to the System.Runtime.Remoting namespace and the
ListServer project.
4. Add the following using statements at the top of the code window to
reference the relevant namespaces:
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtim.Remoting.Channels.Http;
5. Add the following code to the Main method.This code creates
an HttpChannel object that uses port 8080.The RegisterChannel
method is then used to register the channel, after which the
RegisterWellKnownServiceType method is called to register the class with
the remoting framework.The RegisterWellKnownServiceType method contains
three parameters that specify the type of the remoting class, the
URI, and the object activation mode. After this method has been called,
your class is then ready to accept requests from client applications.
static void Main(string[] args)
{
HttpChannel myChannel = new HttpChannel (8080);
ChannelServices.RegisterChannel(myChannel);
RemotingConfiguration.RegisterWellKnownServiceType
(typeof(ListServer.CompanyLists),
“CompanyLists”, WellKnownObjectMode.Singleton);
}
http://www.syngress.com
306 Chapter 6 • Remoting
6. Build the console application to create the ListHost.exe console
application.
The CompanyList class can now accept calls from remote clients.You’ll notice
that we have chosen port 8080 to listen to for client requests.The choice of port
is rather arbitary, although port 80 should be used to be firewall friendly.You
need to remember that a port can only be registered once per machine.To see
what happens when an attempt is made to register the same port twice, perform
the following experiment:
1. In Windows Explorer, find and run the host application ListHost.exe.
2. While the console application is running, run the same host application
from within the Visual Studio IDE.You may need to right-click the
ListHost project in the Solution Explorer and select Set as StartUp
Project to enable the IDE to do this.
3. Figure 6.1 shows the exception that occurs when the same port is
reused.
Creating the Client Application
The client application will be a standard Windows Application with a main form,
but it could also be any other type of .NET application.The source for this project
is located under the ListClient directory of the CD:
1. From the Visual Studio menu choose File | New | Project. Select
Windows Application, and name the new project ListClient.
2. Rename the Form1.cs file to ListClient.cs.
3. Add a reference to the System.Runtime.Remoting namespace and also to
the ListServer.dll.
4. Add the following using statements at the top of the ListClient.cs code
window to reference the relevant namespaces:
http://www.syngress.com
Figure 6.1 The Exception Generated after an Attempt to Reuse a Port
Remoting • Chapter 6 307
using ListServer;
using System.Runtime.Remoting;
using System.Runtime.Remoting Channels;
using System.Runtime.Remoting.Channels.Http;
5. Modify the code in the Form1 constructor to appear as follows so that a
new HttpChannel object is created and registered on application startup:
public Form1()
{
InitializeComponent();
HttpChannel c = new HttpChannel();
ChannelServices.RegisterChannel(c);
}
6. Add a button and a textbox to the form. In the button’s click event, add
the following code.This code will create a reference to the remote
object by using the Activator.GetObject method.Three parameters are
used by this method to specify the type of the remote class, its URI, and
the creation mode.The list of countries is then retrieved and used to
populate the form’s ListBox control:
private void button1_Click(object sender, System.EventArgs e)
{
CompanyLists cLst = (CompanyLists)Activator.GetObject(typeof(
CompanyLists),”http://localhost:8080/CompanyLists&#8221;,
WellKnownObjectMode.Singleton);
listBox1.DataSource = cLst.getCountryList();
}
7. Run the host application ListHost.exe and leave the console window
open. Figure 6.2 shows the host application.
http://www.syngress.com
308 Chapter 6 • Remoting
8. Run the ListClient application. Click the button to retrieve the list
country list from your server object. In Figure 6.3, you can see that the
county list has been successfully obtained from the remote object.
Understanding the Remoting Code
The host application simply needs to register a channel and port
using RegisterChannel and to register the remoting object using
RegisterWellKnownServiceType.The RegisterWellKnownServiceType method
takes three parameters—the type of the object, the object’s URI as defined
by the developer, and the creation mode.The first parameter provides the link
between the hosting application and the remoting object—this is why having a
reference to your class library’s DLL is necessary. Developers that have used previous
versions of Visual Basic may notice that we cannot magically determine the
location of a DLL using CreateObject.We must explicitly tell the compiler the
DLL’s location.This is actually a major benefit of the .NET Framework because
we no longer must trust that the Registry has accurate information to instantiate
an object.
Another important point is that an object does not “own” a channel.You are
free to register as many channels and objects in the hosting application as you
like. Communication on the server side is multithreaded, so there is no need to
worry about a request blocking a channel while processing is done.You may also
want to use one channel for Internet clients and another for intranet clients and
force this policy by screening ports on your proxy server.
http://www.syngress.com
Figure 6.2 The Server Application Waiting for Clients
Figure 6.3 The Client Application
Remoting • Chapter 6 309
The client application must also register a channel, but in this case the port
does not need to be specified.This may seem strange at first—doesn’t the client
need to know which port to communicate with? The confusion lies in the
double life of the HttpChannel class. Creating a HttpChannel object actually creates
a ClientChannel and a ServerChannel object.The ClientChannel object does
not need a port number because it can communicate with any port specified in
the URL.You could replace HttpChannel with ClientChannel in the client code
and everything would still work fine.The ServerChannel object is given to us for
free by the remoting framework so that the server object can call back to the
client if needed. By specifying a port when creating a HttpChannel, we are
allowing our client app to “listen” on this port, but it has no influence on what
port our app may talk to. Also, if you are a lazy programmer, you can actually
forget about registering a channel altogether.The remoting framework will create
one for you the first time you attempt to reference a remote object.Try commenting
out the two lines of code that create and register a channel on the client
(shown in Step 5 in the previous section) and then rerun the application.
The client application also needs a reference to ListServer.dll but for a different
reason than the hosting application has a reference.The hosting application
needs the reference so that it can create the remoting object to handle incoming
requests.The client application needs the reference only so that it can access the
DLL’s metadata. As you will see soon, the SoapSuds.exe utility removes the need
to reference the DLL by extracting the metadata and providing it to the client in
the form of a proxy class.
To obtain a reference to the remote object, Activator.GetObject is used.This
method takes two parameters—the type of the object and the remote object’s
URI.The reference returned by GetObject is actually a reference to a proxy
object that routes messages to the remote server.The remote object is not created
until the client makes the first method call.This explains why the first time the
button is clicked in our example application that there is a delay—the remoting
framework is instantiating the remote object. And for those developers that
deleted the code to register the channel, there will be a slightly longer delay
while the framework sets up a default channel for you to use.
Note that if you are using the HTTP channel then the host application can
be tested by typing the remote object’s URI into a browser.Try typing in
http://localhost:8080/CompanyLists?wsdl into Internet Explorer. As long as
the host application is running and configured correctly, you’ll see the SOAP definition
of the remote class as it appears in Figure 6.4.
http://www.syngress.com
310 Chapter 6 • Remoting
Improving the Sample Application
Although the sample application is a good start and has shown how to execute
calls to a remote object, some areas need improving in order to become a more
real-world application.We introduce these improvements by adding to the sample
code one step at a time.
Adding Event Logging and Error Handling
A good coding standard would be to always have a hosting application write to
the event log information regarding startup success or failure, the application
name, server port number, and any other useful data.We now add event logging
and error handling to the sample hosting application.This updated code is in the
CompanyListHost2.cs file on the CD.The complete code for the host is shown
in Figure 6.5.
Figure 6.5 Adding Event Logging and Error Handling to the Hosting
Application
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Diagnostics;
namespace ListHost
{
public class CompanyListHost
http://www.syngress.com
Figure 6.4 The SOAP Definition of the Remoting Class
Continued
Remoting • Chapter 6 311
{
EventLog myLog = new EventLog();
myLog.Source = “ListHost”;
bool failed = false;
try
{
HttpServerChannel myChannel = new HttpServerChannel (8080);
ChannelServices.RegisterChannel(myChannel);
myLog.WriteEntry(“Registered HTTPChannel(8080)”);
}
catch (Exception e)
{
myLog.WriteEntry(“Failed to register HTTPChannel(8080) ” +
e.Message,System.Diagnostics.EventLogEntryType.Error);
failed = true;
}
try
{
RemotingConfiguration.RegisterWellKnownServiceType(typeof(
ListServer.CompanyLists), “CompanyLists”,
WellKnownObjectMode.Singleton);
myLog.WriteEntry(“Registered ListServer.CompanyLists as
Singleton”);
}
catch (Exception e)
{
myLog.WriteEntry(“Failed to register ListServer.CompanyLists
” + e.Message);
http://www.syngress.com
Figure 6.5 Continued
Continued
312 Chapter 6 • Remoting
failed = true;
}
if (failed)
{
System.Console.WriteLine(“Errors at startup –
see Event Log.”);
}
System.Console.WriteLine(“Press [Enter] to exit…”);
System.Console.ReadLine();
}
}
The code that writes messages to the event log is quite straightforward.The
WriteEntry method of the EventLog object is used to write error messages from
within the catch blocks. Error handling has been added to trap exceptions caused
while setting up the remoting configuration.
Using the soapsuds Tool
The need for every client application to have a reference to the remote assembly
may be inconvenient for some third-party services.You use the soapsuds.exe tool
to create a proxy object from the remote assembly’s metadata so that a reference
to the assembly is not needed.We now modify the sample application to use this
proxy object by following the next few steps (The updated ListClient code is
located in the ListClient2.cs file on the CD):
1. Open the ListClient project in Visual Studio.
2. From the command prompt, type soapsuds –url:http://
localhost:8080/CompanyLists?wsdl –gc.This creates a proxy
class named ListServer.cs.
3. Copy the ListServer.cs file to your source code directory.
4. Remove the project’s reference to ListServer from the Solution Explorer
window.
http://www.syngress.com
Figure 6.5 Continued
Remoting • Chapter 6 313
5. Right-click the ListClient project in the Solution Explorer window.
Select Add | Existing Item and choose the ListServer.cs file to add
it to your project.
6. Modify the button1_click method so that the code is as follows:
private void button1_Click(object sender, System.EventArgs e)
{
CompanyLists cLst = new ListServer.CompanyLists();
listBox1.DataSource = cLst.getCountryList();
}
7. Build the application.
Notice that the ListServer.cs file has taken the place of the reference to the
remote assembly. Inspection of the ListServer.cs code reveals that this class is
acting as a proxy by routing the remoting calls to the remote object’s URI.This
allows us to do away with the use of Activator.GetObject to obtain a remote reference—
we can now program against ListServer as if it was a local class.
NOTE
The soapsuds utility has a range of command line options to aid clientside
development—see the Microsoft documentation for details. When
using this utility, it helps to remember that wsdl means Web Services
Description Language and -gc means generate code. You’ll then be able
to amaze your friends and colleagues when you can type in soapsuds
commands from memory.
Using Configuration Files
Many settings to the configuration of .NET applications can be achieved not
only inside code but with configuration files as well. All of these files use XML
so that they are humanly readable and easily parsed by the .NET Framework.
With remoting, you can use configuration files to handle all of the work necessary
to expose and consume remoting objects.
http://www.syngress.com
314 Chapter 6 • Remoting
You use the Configure method of the RemotingConfiguration class to configure
the remoting framework by specifying the configuration file’s location.We now
modify the ListHost hosting application to read a configuration file at startup:
1. Open the ListHost project in Visual Studio.
2. Add a new file to the project called ListHost.exe.config (which is also
located on the CD) with the following contents:
<configuration>
<system.runtime.remoting>
<application name=”ListServer”>
<service>
<wellknown mode=”Singleton” type=
“ListServer.CompanyLists,ListServer” objectUri=”CompanyLists”/>
</service>
<channels>
<channel type=”System.Runtime.Remoting.Channels.Http.HttpChannel,
System.Runtime.Remoting” port=”8080″/>
</channels>
</application>
<debug loadTypes=”true” />
</system.runtime.remoting>
</configuration>
3. Modify the Main() method to use this configuration file on startup
(CompanyListHost3.cs on the CD):
static void Main(string[] args)
{
EventLog myLog = new EventLog();
myLog.Source = “ListHost”;
bool failed = false;
try
{
RemotingConfiguration.Configure(@”..\..\ListHost.exe.config”);
myLog.WriteEntry(“Configuration from ListHost.exe.cfg
successful”);
http://www.syngress.com
Remoting • Chapter 6 315
}
catch (Exception e)
{
myLog.WriteEntry(“Failed to configure host application: ” +
e.Message,System.Diagnostics.EventLogEntryType.Error);
failed = true;
}
if (failed)
{
System.Console.WriteLine(“Errors at startup – see Event Log.”);
}
System.Console.WriteLine(“Press [Enter] to exit…”);
System.Console.ReadLine();
}
Note that while running the host application in the Visual Studio IDE, the
bin\debug directory will contain the executable.You’ll therefore need to use the
“..\..\” syntax in the file path to reach the configuration file in your source code
directory. A further improvement would be to use a command line argument to
specify the CFG file location.This would help during deployment, and you could
test out a variety of configuration options easily without recompiling.
Configuration files may also contain multiple channels definitions and object
URI entries.
NOTE
The Microsoft standard for configuration files is that they should have
the same name as the assembly, but with a .config extension. For
example, myapp.exe will have the configuration file myapp.exe.config.
This configuration file must be placed in the same directory as the
assembly to enable utilities such as the .NET Framework Configuration
tool to locate configuration information.
http://www.syngress.com
316 Chapter 6 • Remoting
The type parameter is of the format type=“TypeName,AssemblyName”.These
parameters can be difficult to debug if they are wrong—no error message will be
displayed during the call to RemotingConfiguration.Configure.To help with debugging,
the <debug loadTypes=“true” /> attribute has been added, which causes the
types specified in the configuration file to be loaded.Any errors in the spelling of
a type name will then appear as a FileNotFoundException type exception.
On the client side a slightly different configuration file can be used:
<configuration>
<system.runtime.remoting>
<application name=”ListClient”>
<client>
<wellknown type=”ListServer.CompanyLists, ListServer”
url=”http://localhost:8080/CompanyLists”/&gt;
</client>
<channels>
<channel type=”System.Runtime.Remoting.Channels.Http.HttpChannel,
System.Runtime.Remoting”/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
The client code also uses the Configure method of the RemotingConfiguration
class to read the configuration file on startup.A client that uses a configuration
file still needs a reference to the remoting application’s DLL but can use the new
keyword to instantiate the class.The client-side configuration actually redirects
the object creation to the server and returns the remote reference. By using this
method, it can be difficult to know if you are successfully creating the remote
object. A mistake in the configuration file can cause the object to be instantiated
locally instead of remotely.To avoid such subtle bugs, you can simply close down
the remote hosting application and make sure that the object creation code
causes an exception when running the client.
http://www.syngress.com
Remoting • Chapter 6 317
http://www.syngress.com
Remoting Applications
Remoting applications on the .NET platform have a great deal of flexibility
as to how objects communicate with one another. It is even possible
to “plug-in” your own (or a third party’s) functionality to handle
custom formatting, encryption, and more. This makes it all the more
important for remoting issues to be considered up front in any design
work. The areas that need to be examined include the following:
 Should objects be sent over the network by value or by
reference?
 How large are these objects?
 How often will these objects need to be sent?
 For every remote method call, how many bytes of data
would a typical call contain?
 How many client applications will a Singleton object need to
handle?
 What are the lifetime issues with these objects? (that is, for
how long must they maintain state?)
 Can a stateful object be used to increase performance?
 Will your firewalls allow your remoting calls through?
 Do your server-side objects need to call back to the clients? If
so, will these clients have their own firewalls?
 If you need to shut down a hosting application to upgrade
the server object, how will the clients handle this?
Deployment of remoting applications seems quite easy—and
indeed it is. You could send the client-side executables with their configuration
files via e-mail to a friend and he would only need to copy
them to a directory and double-click the EXE to get started.
But wait, what happens if you want to move your server-side objects
to another server? When version 2 of the server-side functionality is
released, how do you let the client-side applications know? The solution
to these issues is largely dependent on the type of applications you
create, whether they are Internet- or intranet-based, and the number of
clients that must be administered. One idea to get you started is to have
Developing & Deploying…
Continued
318 Chapter 6 • Remoting
Updating Configuration Files Using
the .NET Framework Configuration Tool
Most developers are happy to use Notepad to update configuration files, but as the
number of files increases, locating the necessary files in the directory tree can be
troublesome.The .NET Framework provides you with a Microsoft Management
Console (MMC) snap-in that serves as a central location for .NET configuration.
Although in Beta 2 this snap-in appears to still need some improvement, it does
hold promise of being a very useful tool.To start the snap-in, open a command
prompt window and change the current directory to the installation directory of
the .NET Framework, which will be WINNT\Microsoft.Net\Framework\vx.y.z
(where WINNT is your windows directory and x.y.z is the version of the .NET
Framework).Type mscorcfg.msc to start the .NET Framework Configuration
tool.You will see a screen similar to Figure 6.6.
http://www.syngress.com
your client configuration files actually located on your Web server. This
would need to be a server that is almost guaranteed not to have a
domain name change. Instead of having thousands of client configuration
files distributed around the globe—you now have only one. When
client applications start up, they can get the configuration file via HTTP
from your server and always have the latest version.
Figure 6.6 The .NET Framework Configuration Tool
Remoting • Chapter 6 319
To add ListHost.exe to the Applications node, simply click the Add an application
to be configured hyperlink and select the ListHost.exe file from the
dialog. As long as your configuration file is named ListHost.exe.config and located
in the same directory as the executable, you’ll be able to modify the remoting
configuration settings.To update the settings, right-click the Remoting Services
node under ListHost.exe and select Properties from the context menu.
Changing the Hosting Application to a Service
Hosting all of your remoting objects from console applications does appear strange
at first sight. It’s the 21st century and we still haven’t completely got rid of those
character-based applications! The fact is that console applications do provide a
good environment for debugging applications that use remoting—you can immediately
see if your hosting application is running, and you can easily send debug
messages to the console window in real-time while you run your client-side app.
Once your server-side classes are ready for deployment, a Windows Service
provides a better hosting environment. System administrators can easily start and
stop your service, you can view your service from within Visual Studio’s new Server
Explorer, and you can guarantee that your service will be started after a reboot of
the server.The service application we will create is located under the ListService
directory on the CD.To create a new hosting service, follow these steps:
1. Load the ListHost project into Visual Studio.
2. Select and copy all the code from within the Main() method.
3. Select File | New | Project. Select the Windows Service template
and type in ListService for the project name. Make sure that the Add
to Solution option is set and then click OK.
4. While the Service1.cs file is in design view, use the Properties window
to set the service name to ListService.
5. Switch to code view and paste the code you copied in Step 2 into the
OnStart() method. Remove any code that was used to write to the console
window. Replace any text within the code that refers to ListHost
to be ListService.
6. Add the line using System.Runtime.Remoting to the start of
Service1.cs.
7. Switch back to the Service1.cs design view. At the base of the Properties
window, select the Add Installer link—see Figure 6.7.
http://www.syngress.com
320 Chapter 6 • Remoting
8. Select the serviceProcessInstaller1 component (if this component is
not visible, double-click the ProjectInstaller.cs file in the solution
explorer) and set its Account property to Local System.
9. Copy the ListHost.exe.config file to the winnt\system32 directory and
rename as ListService.exe.config.
10. Change the method call that reads the configuration file to the following:
RemotingConfiguration.Configure(“ListService.exe.config”)
11. Build the ListService project.
12. Open a command prompt window and change the current directory to
the installation directory of the .NET Framework, which will be
WINNT\Microsoft.Net\Framework\vx.y.z (where WINNT is your
windows directory and x.y.z is the version of the .NET Framework).
13. Type installutil appPath where appPath is the directory path to
ListService.exe.This will install your service.
14. The service is now installed.You can now start the service by using the
Server Explorer from within Visual Studio.
You can also view the Event Log from the Server Explorer making Visual
Studio the central hub of your development activities. Notice that the configuration
file was placed in the winnt/system32 directory because this is a Windows
Service application. If you need to keep the configuration file together with the
executable, you will have to use the absolute path. Installing the service with the
installutil tool has to be done only once.To update the executable, simply stop the
service and rebuild the project.
http://www.syngress.com
Figure 6.7 Setting the Properties of a Windows Service Application
Remoting • Chapter 6 321
Using the TCP Channel with the Binary Formatter
Within a corporate intranet, you can gain more speed by using the TCP channel.
To change the sample application to use the TCP channel all you need to do is
do a search and replace of every “Http” with “Tcp” within the configuration
files.The TCP channel uses binary formatting by default, whereas the HTTP
channel defaults to SOAP formatting.Two downsides of using the TCP channel
is that communication may be blocked by firewalls, and you cannot use your
browser to examine the SOAP description of your hosting application.
Summary of the Improved Sample Application
Your sample application now contains enough bells and whistles to provide a base
for a real-world multitier application.You have seen how to host your remoting
objects from within a Windows Service, how to write to the event log, how to
handle exceptions on startup, and how clients can easily communicate with your
remote objects.To further enhance the application you could connect to a
database to obtain various lists of data that are in common use across all corporate
applications—countries, clients, customers, languages, application settings, and
so on. On the client side, you could then subclass a ComboBox control and add a
property called ListType, which would load the corresponding list of items from
your remote object on initialization.This control would save development time
and provide a standardized user interface.ASP.NET applications could also use
your remote objects in the same way.
Creating an Intranet Application
The remoting framework provides fine control over how objects are sent to and
from remote applications and also how objects are created and destroyed.We now
look at an example of how you can use these features in a remoting application.
Object Lifetime and Leasing
In the COM world, object lifetime was controlled by reference counting. As
clients disconnected from the server object, the reference count was decremented
until it reached zero.The server object was then unloaded immediately, and any
hold on system resources was released.With the .NET Framework, no reference
counting occurs. Instead, an object is marked to be garbage collected when no
other object holds a reference to it. Because the garbage collector cannot detect
http://www.syngress.com
322 Chapter 6 • Remoting
remote references (because they are in another AppDomain), .NET uses another
method for handling object lifetime called leasing.
Objects have a default lease time—when this time has passed, the object will
be ready for garbage collection provided there are no references to the object
from its own AppDomain.An object can change its own lease period on startup
or even set it to infinity to maintain state forever (forever = until a server
reboot!). Clients are able to renew this lease if they wish to keep communicating
with the same object instance. Also, the client can register a sponsor for a lease.
When the lease expires, the sponsor is given the opportunity to renew the lease.
We now create a sample application that uses the leasing features of the
remoting framework.The source code for this project is in the CountServer
directory—opening up the solution file CountServer.sln will make sure that both
the server and the hosting application are loaded into Visual Studio.
Creating the CountServer Project
This project contains the server-side functionality.The Count class implements a
counter that can be incremented and decremented with the inc and dec methods
respectively:
1. Create a new Class Library application in Visual Studio named
CountServer.
2. Right-click the default Class1.cs module in the Solution Explorer and
choose Delete.
3. Right-click the ListServer project in the Solution Explorer, select Add
| Add Class and name your new class Count.cs.
4. Add the following code to Count.cs:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime;
namespace CountServer
{
public class Count: MarshalByRefObject
{
private int mVal;
http://www.syngress.com
Remoting • Chapter 6 323
public Count()
{
mVal = 0;
}
public override Object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.FromSeconds(5);
lease.RenewOnCallTime = TimeSpan.FromSeconds(1);
lease.SponsorshipTimeout = TimeSpan.FromSeconds(5);
}
return lease;
}
public int inc()
{
mVal++;
return mVal;
}
public int dec()
{
mVal—;
return mVal;
}
}
}
This code is quite straightforward except for the InitializeLifetimeService
method. Every remoting object has this method because InitializeLifetimeService is
http://www.syngress.com
324 Chapter 6 • Remoting
a method of the inherited MarshalByRefObject class.This method obtains the current
lease for the object, and by overriding this method, an object can control/set
its own lease properties.These lease properties can be set only before the object
has been marshaled to the client—the CurrentState property is used to check that
the lease is in its initial state and can therefore be modified.The three lease properties
used in the code are the following:
 InitialLeaseTime The time of a lease.The object will be ready for
garbage collection after this amount of time. Setting this property to null
gives an infinite lease time.
 RenewOnCallTime Every call to the object will increase the lease
time by this amount.
 SponsorshipTimeout When the lease has expired, the lease will contact
any registered sponsors.The sponsor then has the opportunity of
extending the lease.The SponsorshipTimeout value is the amount of time
that the object will wait for a response from the sponsor.The sponsor
class will be introduced shortly in the client-side code.
These default lease settings can also be placed within the configuration file as
follows:
<application name=”CountServer”>
<lifetime leaseTime=”5S” sponsorshipTimeOut=”5S” renewOnCallTime=”1S”/>
. . .
</application>
The units of time used in the configuration file are D for days, M for minutes,
S for seconds, and MS for milliseconds.
NOTE
For a lease on the server to contact a sponsor on the client, the client
must register a ServerChannel to listen on a port. If the lease attempts to
contact your client-side sponsor and you do not have a ServerChannel,
the contact will fail and the remoting object will be deactivated after the
specified SponsorshipTimeout value. You will not receive an error in this
situation.
http://www.syngress.com
Remoting • Chapter 6 325
Creating the CountHost Project
This host application will configure the Count class for remoting as a Singleton
object. Being a Singleton object, it is shared between all client applications:
1. Add a new Console Application project named CountHost to the current
solution and add a reference to the CountServer project.
2. Add the call to RemotingConfiguration in the main method and reference
the System.Runtime.Remoting namespace so that the complete console
application code appears as follows:
using System;
using System.Runtime.Remoting;
namespace CountHost
{
class Class1
{
static void Main(string[] args)
{
try
{
RemotingConfiguration.Configure(@”..\..\CountHost.exe.config”);
}
catch (Exception e)
{
System.Console.WriteLine(“Failed to configure
hostapplication:

+e.Message,System.Diagnostics.EventLogEntryType.Error);
}
System.Console.WriteLine(“Press [Enter] to exit…”);
System.Console.ReadLine();
}
}
}
http://www.syngress.com
326 Chapter 6 • Remoting
3. Create the configuration file named CountHost.exe.config and place
in the project directory:
<configuration>
<system.runtime.remoting>
<application name=”CountServer”>
<channels>
<channel displayName=”MyChannel”
type=”System.Runtime.Remoting.Channels.Http.HttpChannel,
System.Runtime.Remoting” port=”8085″ />
</channels>
<service>
<wellknown displayName=”MyService” mode=”Singleton”
type=”CountServer.Count,CountServer”
objectUri=”CountServer” />
</service>
</application>
<debug loadTypes=”true” />
</system.runtime.remoting>
</configuration>
4. Build the project to produce the hosting application—CountHost.exe.
Creating the CountClient Project
The CountClient project is a Windows Application that will remote to the serverside
Count object and update the counter value.The app will also have two buttons
that allow us to renew the lease and to also add a sponsor for the object.
Follow the next steps to create the project or alternatively access the code from
the CountClient directory on the CD:
1. Create a new Windows Application for the client side called
CountClient.
2. Add four buttons to the form—btnInc, btnDec, btnRenew, and
btnSponsor with the captions—“Inc”,“Dec”,“Renew Lease”, and
“Add Sponsor”. Also add a textbox called txtValue.
http://www.syngress.com
Remoting • Chapter 6 327
3. Add click event handlers to each button and add the following code to
the form:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime;
namespace CountClient
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnInc;
private System.Windows.Forms.Button btnDec;
private System.Windows.Forms.Button btnRenew;
private System.Windows.Forms.Button btnSponsor;
private System.Windows.Forms.TextBox txtValue;
private System.ComponentModel.IContainer components;
private CountServer.Count objCount;
private ClientSponsor mSponsor;
private ILease mLease;
public Form1()
{
InitializeComponent();
RemotingConfiguration.Configure(
@”..\..\CountClient.exe.config”);
objCount = new CountServer.Count();
}
private void btnInc_Click(object sender, System.EventArgs e)
http://www.syngress.com
328 Chapter 6 • Remoting
{
txtValue.Text = objCount.inc().ToString();
}
private void btnDec_Click(object sender, System.EventArgs e)
{
txtValue.Text = objCount.dec().ToString();
}
private void btnRenew_Click(object sender, System.EventArgs e)
{
mLease = (ILease)RemotingServices.
GetLifetimeService(objCount);
try
{
mLease.Renew(System.TimeSpan.FromSeconds(10));
MessageBox.Show(this,”Lease renewed for 10 seconds”);
}
catch
{
MessageBox.Show(this,”Lease has expired”);
}
}
private void btnSponsor_Click(object sender, System.EventArgs e)
{
mLease = (ILease)RemotingServices.
GetLifetimeService(objCount);
mSponsor = new ClientSponsor();
mSponsor.RenewalTime = TimeSpan.FromSeconds(15);
try
{
mLease.Register(mSponsor);
}
http://www.syngress.com
Remoting • Chapter 6 329
catch
{
MessageBox.Show(this,”Lease has expired”);
}
MessageBox.Show(“Sponsor registered with object”);
}
}
}
4. Create the client-side configuration file:
<configuration>
<system.runtime.remoting>
<application name=”CountClient”>
<client>
<wellknown type=”CountServer.Count, CountServer”
url=”http://localhost:8085/CountServer”/&gt;
</client>
<channels>
<channel type=”System.Runtime.Remoting.Channels.Tcp.TcpChannel,
System.Runtime.Remoting” port=”8011″/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Understanding the Leasing and Sponsorship Code
The increment and decrement buttons simply call the corresponding methods on
the server-side Count object and display the result in the textbox. By observing
the returned value, you can determine if you are still using the original class
instance.
The Renew Lease button renews the lease of the current server-side Count
object.To do this, the lease is obtained by calling GetLifetimeService on the remote
object. A remote reference to the server-side lease is then returned and the Renew
method is called on the lease. Note that the lease is also acting as a remote object
http://www.syngress.com
330 Chapter 6 • Remoting
in this scenario.The Renew method takes a TimeSpan parameter that specifies the
new lease time.
The Add Sponsor button registers a sponsor so that you can receive a notification
when the lease has expired.The code obtains a remote reference to the
lease, creates a sponsor, and then registers the sponsor with the lease. In the preceding
code, the sponsor will set the lease time to 15 seconds when notified by
the server-side lease. By running the client-side application, you will see a form
as shown in Figure 6.8.
To test the object lifetime features of .NET remoting, click on the Inc
button two times so that the value in the textbox is 2.The InitialLeaseTime set by
the server-side Count object is 5 seconds—if you wait more than 5 seconds and
then click on Inc again, you will notice that the counter has been reset.The
remoting framework has destroyed the original instance after 5 seconds and has
created a new object to handle the latest call.
The server-side lease property, RenewOnCallTime, has a value of 1 second.
This will cause 1 second to be added to the lease time on every call to the
remote object.You can test this by clicking on the Inc button quickly 20 times—
you will notice that after waiting 20 seconds, and clicking Inc again, that the
counter has not been reset.
Clicking Renew Lease will set the current lease time to 10 seconds. Again, by
clicking on Inc a couple of times, waiting about 8 seconds, and then clicking
Renew Lease, you will notice that the counter’s life is extended. Clicking Add
Sponsor has the effect of having a permanent Singleton object on the server.The
sponsor will always set the lease time of the remote object to 15 seconds whenever
it is notified by the server that the lease has expired. After the client application is
closed, the server site lease will eventually attempt to notify the client of lease expiration.
In our example, 15 seconds will pass while the server lease waits for an
answer.When that answer doesn’t arrive, the remote object will be shut down.
As a final experiment, start up two instances of the CountClient.exe and run
them side-by-side. By clicking on the Inc and Dec buttons of each application,
you will see that they both share the same Singleton instance of the server-side
http://www.syngress.com
Figure 6.8 Controlling Object Lifetime with Leases and Sponsors
Remoting • Chapter 6 331
object. Also, if all client-side applications are shut down, the server-side component
will still maintain its state if a new application calls the component before its
lease expires.
Client Activated Objects
The server-activated objects that we have been using so far in this chapter have
been of two types, Singleton and SingleCall.The third type of remoting object is
the CAO or Client Activated Object, which allows a client application to create a
remote instance of the object for exclusive use, similar to the way that clients do
in the COM world.These objects can maintain state without you having to
http://www.syngress.com
Remoting Applications
Anyone that has started learning the .NET remoting framework will
know how easy it is to accidentally stop an application from working. A
wrong port number or a misspelled type name can take some time to
track down. In a perfect world, you would get an error message like
“You tried to communicate with server xyz on port 8050. There is
nothing listening on that port but 8051 has something that may interest
you.” To help avoid these problems, a base application might help—this
application would contain the three projects needed for a remoting app
(server, host, and client) plus any boilerplate code and configuration
files. This application could then serve as a starting point for all your
remoting applications.
Another method to help (or avoid) debugging your applications is
to start with the simplest case. A simple “test” method on each remote
object could be used to test the communication channels between the
tiers of your application. Such an approach is better than calling a complex
method on an object that may contain other object references and
serialized objects as parameters—there would just be too many places
where something could go wrong. This “test” method would also help
in the construction of a monitoring application that could ping your
remote objects every few minutes and e-mail an administrator if an
object does not respond.
Debugging…
332 Chapter 6 • Remoting
worry about another client connecting to the remoting object and changing its
state without your knowledge.
Making your objects ready for client activation is as easy as modifying the
configuration file on the server. For example, the CountClient’s configuration file
has the following section:
<service>
<wellknown mode=”Singleton” type=”CountServer.Count,CountServer”
objectUri=”CountServer” />
</service>
To change this to a CAO, the activated attribute is used with only the type
parameter instead of the wellknown attribute:
<service>
<activated type=”CountServer.Count,CountServer”>
</activated>
</service>
The client-side configuration file then requires a modified <client> specification
that uses the same activated attribute parameters as the server-side configuration
file:
<client url=”http://localhost:8085″&gt;
<activated type=”CountServer.Count,CountServer”/>
</client>
When the client-side uses new to create a remote instance, the remote object
is created immediately for the exclusive use of the client. Lifetime leasing and
sponsorship need to be used in the same way as in the previous example—even
though the object “belongs” to the client, it still has a lease that may expire,
causing the object to lose state.
Sending and Receiving Objects by Value
For more complex remoting applications, you may need to pass objects as parameters
to remote method calls or receive such objects in return. For example, instead
of passing a customer name and a customer ID in separate calls, it is more efficient
to create a Customer object containing the required information and send the
whole object to the server in one call.To achieve this, the remoting framework
needs to be able to serialize your class so that it can be sent over a channel.
http://www.syngress.com
Remoting • Chapter 6 333
The [serializable] attribute is used to specify a class as being serializable and
able to be remoted by value. Using the customer example, the class definition
would appear as follows:
[Serializable]
class Customer
{
public Customer()
{}
int ID;
String Name;
}
NOTE
It is important to consider the suitability of a class for remoting. If the
class can hold large amounts of data and must be sent over a slow connection,
application performance will suffer. Also, some types of data
(for example, a file path) would have no meaning on a remote server.
Sending and Receiving Objects by Reference
For overly large objects, passing them by reference to remote servers may be
more efficient.This is roughly equivalent to simplified remoting—the remoting
framework will create a proxy for your object on the server. As the server calls
methods on your object, the proxy will route the calls to the real object on the
client side. As you are controlling the creation of the object instance and handling
the calls explicitly, you don’t need to consider ports, channels, and object lifetime
issues (although if you would like the server to call-back to your client object,
keeping a reference to it would be a good idea to prevent it from being garbage
collected).
For a class to be sent by reference, it is necessary for the class to inherit from
MarshalByRefObject.The customer class would then appear as follows:
class Customer: MarshalByRefObject
{
http://www.syngress.com
334 Chapter 6 • Remoting
public Customer()
{}
int ID;
String Name;
}
Creating Service-Based Applications
A major improvement of .NET components compared to legacy COM components
is the ability to use side-by-side deployment. Upgrading COM components
is an all-or-nothing affair, which can cause problems with client applications
relying on a specific version of a component.With the .NET Framework, you
can have different versions of the same component running at the same time.To
achieve this with your remoting applications, you need to give your server-side
assemblies what is known as a strong name.
Building a Versioned Remoting Application
A strong name is a unique identifier for an assembly, which is generated by combining
a text name, the version number, culture information (if it exists), a public
key, and a digital signature.This may sound complicated, but it is in fact quite
easy.We now create a remoting class and build the assembly with a strong name.
The following code is in the VersionServer directory on the CD:
1. Create a new Class Library application in Visual Studio named
VersionServer.
2. Right-click the default Class1.cs module in the Solution Explorer and
choose Delete.
3. Right-click the ListServer project in the Solution Explorer, select Add
| Add Class and name your new class Test.cs.
4. Add the following code to Test.cs.The getVersion method will be used to
return the current version string back to the client application:
using System;
using System.Windows.Forms;
using System.Reflection;
http://www.syngress.com
Remoting • Chapter 6 335
namespace VersionServer
{
public class Test:MarshalByRefObject
{
public Test()
{
}
public String getVersion()
{
return Assembly.GetAssembly(this.GetType()).
GetName().Version.ToString();
}
}
}
5. Now, use the strong name utility (sn.exe) to generate a new strong name
key.To do this, use the -k parameter with the output file name. From the
Visual Studio .NET Command Prompt type in sn –k mykey.snk. Copy
the new key file to the source code area of the VersionServer project.
6. Now, add the key to the assembly manifest. Open the AssemblyInfo.cs
file, which contains the assembly attributes, and find the AssemblyKeyFile
attribute.Add the path to the key file to the AssemblyKeyFile attribute as
shown here:
[assembly: AssemblyKeyFile(“..\\..\\mykey.snk”)]
7. Also, set the desired version number using the AssemblyVersion attribute
as shown here:
[assembly: AssemblyVersion(“1.0.0.99″)]
8. After building the VersionServer.dll, you need to install the assembly into
the Global Assembly Cache (GAC).The GAC is located in the Assembly
directory under the Windows system directory. For example
C:\WINNT\Assembly.To install the assembly, you can drag and drop
the DLL into the GAC or you can use the gacutil.exe utility.
http://www.syngress.com
336 Chapter 6 • Remoting
9. Now update the version to 2.0.0.0. Rebuild the project and repeat
Step 8. Update the version to 3.0.0.0 and repeat Step 8 again.You will
now have three versions of the VersionServer in the GAC, as shown in
Figure 6.9.
Creating the VersionHost Project
The VersionHost project is a simple console application that will host the versioned
components.The code for the project is located in the VersionHost directory
on the CD.This code is the same as that used for the earlier examples
except a slightly different configuration file is used (see Figure 6.10). In this configuration
file, the required version has been added to the wellknown attribute.
Even though you earlier created a version 3 of the assembly, you are able to
choose version 2 (or any other version) by modifying this configuration file.
Figure 6.10 Configuring a Versioned Remoting Application
<configuration>
<system.runtime.remoting>
<application name=”CountServer”>
<channels>
<channel type=”System.Runtime.Remoting.
Channels.Http.HttpChannel,System.Runtime.Remoting”
port=”8085″/>
</channels>
<service>
<wellknown mode=”SingleCall”
type=”VersionServer.Test,VersionServer,Version=2.0.0.0″
objectUri=”VersionServer2″ />
http://www.syngress.com
Figure 6.9 Installing Multiple Versions in the GAC
Continued
Remoting • Chapter 6 337
</service>
</application>
<debug loadTypes=”true” />
</system.runtime.remoting>
</configuration>
This version setting will be used whenever a server activated VersionServer
object is needed.This means that clients requesting a server activated object from
a URI are not able to request a specific version—versioning is determined by the
server.To enable client requests for different versions, you need to use a different
URI for each version.You can do this by adding extra wellknown attributes to the
configuration file, as shown in Figure 6.11.
Figure 6.11 Including Multiple Versions in a Configuration File
<wellknown mode=”SingleCall”
type=”VersionServer.Test,VersionServer,Version=2.0.0.0″
objectUri=”VersionServer2″ />
<wellknown mode=”SingleCall”
type=”VersionServer.Test,VersionServer,Version=3.0.0.0″
objectUri=”VersionServer3″ />
NOTE
If the version is not specified in the server-side configuration file, the
latest version available will always be loaded.
Creating the VersionClient Project
The VersionClient project will be used to connect to a specific version of
VersionServer.This will be done by specifying the corresponding URI in the
client-side configuration file. Follow the next steps to create the project (or access
the code from the VersionClient directory on the CD):
http://www.syngress.com
Figure 6.10 Continued
338 Chapter 6 • Remoting
1. Create a new Windows Application called VersionClient.
2. Add a button to the form called btnGetVersion.
3. Add a click event handler to the button and add the following code to
the form.The button will retrieve the version information from the
remote object and display it within a message box:
using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.Remoting;
namespace VersionClient
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnGetVersion;
private System.ComponentModel.Container components = null;
private VersionServer.Test objRemote;
public Form1()
{
InitializeComponent();
RemotingConfiguration.Configure(
@”..\..\VersionClient.exe.config”);
objRemote = new VersionServer.Test();
}
private void btnGetVersion_Click(object sender,
System.EventArgs e)
{
MessageBox.Show(objRemote.getVersion());
}
http://www.syngress.com
Remoting • Chapter 6 339
}
}
4. Create the client-side configuration file.The VersionServer2 URI is used
to connect to version 2 of the remote component:
<configuration>
<system.runtime.remoting>
<application name=”VersionClient”>
<client>
<wellknown type=”VersionServer.Test, VersionServer”
url=”http://localhost:8085/VersionServer2″/&gt;
</client>
</application>
</system.runtime.remoting>
</configuration>
5. Start the VersionHost.exe console application and then build and run the
VersionClient project. Clicking the button will display the version of the
remote object—see Figure 6.12.
Testing Side-By-Side Execution of Remote Objects
As a final experiment, we get two versions of the remote object running side by
side.To do this, keep the VersionClient application running.Then open up the
client-side configuration file and change the URI from VersionServer2 to
VersionServer3—this will not impact the running application because the configuration
file is read only on startup. Now find the VersionClient.exe executable in
Windows Explorer and run it. After clicking the button, you’ll see that version 3
of the remote object is now being used. Click the button of the first application
instance and version 2 is still available! Both application instances can run independently
on the client, while multiple versions of the server-side objects can
handle client requests at the same time.
http://www.syngress.com
Figure 6.12 Including Multiple Versions in a Configuration File
340 Chapter 6 • Remoting
Summary
Remoting is used to allow .NET applications to communicate with each other
across TCP or HTTP protocols.This communication takes place across a channel
which uses SOAP to format message calls.These SOAP messages can either be
XML formatted or sent as a binary stream. Although the HTTP channel is suitable
for applications distributed on the Internet, the TCP channel is faster and is
often used on corporate networks.
Server-side objects must be hosted in a hosting application to expose them to
requests from client applications. A hosting application may be a Console,
Windows Service, or Windows Forms application.When the hosting application
starts, it must register a channel to listen for client requests by calling
ChannelServices.RegisterChannel.The host will then register remoting configuration
information with the remoting framework either in code (using the
RemotingConfiguration.RegisterWellKnownServiceType method) or by using a configuration
file (using the RemotingConfiguration.Configure method).
Remoting objects have three activation models—SingleCall, Singleton, and
Client Activated Objects (CAO). SingleCall objects are stateless, whereas Singleton
objects are stateful and able to be shared between client applications. CAO
objects are created by a client application for exclusive use and they preserve state
between remote method calls.
For a client application to be compiled in Visual Studio.NET, the remote
server classes metadata is needed.The easiest method is to reference to the remote
object’s DLL in the client project.The other method is to use the soapsuds.exe
utility to generate a proxy class from the remote object’s URI.
For a client application to use remoting, a channel must be registered and the
remoting framework configured in a similar manner to that used in the hosting
application. If a configuration file is not used on the client and a proxy class is
not available (from the soapsuds utility), the Activator.GetObject method must be
used to create a reference to the remote object.
The lifetime of a remoting object is controlled by a lease. Objects have a
default lease time after which they can be garbage collected. An object may
change its default lease time on startup by overriding the InitializeLifetimeService
method. Clients may also renew a lease to keep a remote object active.When a
lease expires, the remoting framework will notify any registered sponsors so that a
sponsor may renew the lease if required.
An assembly in which remoting classes reside may be versioned by using a
strong name.A strong name allows the assembly to be placed in the Global
http://www.syngress.com
Remoting • Chapter 6 341
Assembly Cache (GAC) so that it may be located by the remoting framework.
The server-side configuration file is used to expose a specific version of a component
for remoting clients to access. It is possible for multiple versions of
remoting objects to run side-by-side.
Solution Fast Track
Introducing Remoting
􀀻 Remoting allows cross-application communication, whether they are
located on the same PC or across the Internet.
􀀻 Channels are used as the communications mechanism—HTTP and TCP
channels may be used.
􀀻 Server-side objects need a hosting application to handle incoming
requests. A hosting application may be in the form of a console
application,Windows Service, forms-based app, IIS, or COM+ service.
Creating a Simple Remoting Client Server
􀀻 All remoting objects must inherit from MarshalByRefObject.
􀀻 Hosting applications use the RegisterWellKnownServiceType method of the
RemotingConfiguration class to register objects for remoting.
􀀻 Singletons objects only have a single instance and handle multiple client
requests.
􀀻 SingleCall objects do not maintain state.They handle a single request and
are then recycled by the remoting framework.
􀀻 Remoting applications that act as servers must listen on a port as
specified by the developer.
􀀻 External XML configuration files may also be used to configure
remoting on both the server and the client.
􀀻 Hosting remote objects in a Windows Service application eases the
administration of server-side remoting objects.
http://www.syngress.com
342 Chapter 6 • Remoting
Creating an Intranet Application
􀀻 A lease controls object lifetime—the lease specifies the time-to-expire
of the object.
􀀻 The default values of the lease may be specified by the remote object
on startup.
􀀻 A client application may control the lease to keep a remote object
active.
􀀻 A sponsor can be attached to a lease.When the lease has expired, the
sponsor will be notified so that the lease may be extended if required.
Creating Service-Based Applications
􀀻 Versioned assemblies require a strong name so that they can be uniquely
identified by the .NET Framework.
􀀻 To generate a strong name, a strong name key is needed.The sn.exe
utility is used to create key files.
􀀻 Versioned assemblies should be placed in the Global Assembly Cache
(GAC)—the .NET Framework will search the GAC for strong-named
(shared) assemblies.
􀀻 For server activated objects, the server configuration file is used to map a
URI to the version of an assembly.
http://www.syngress.com
Remoting • Chapter 6 343
Q: If I have a Singleton object to handle multiple clients and it only listens on a
single port, doesn’t this create a performance bottleneck?
A: Don’t worry. Remoting objects are multithreaded so that one request does
not block another.
Q: With .NET, it seems much easier to maintain state on the server.Will this
change the way applications are developed?
A: The stateless model of development is often the most scalable and robust
architecture.The lessons learned with Windows DNA multitier development
still apply today.
Q: It is also easier now to have the server perform callbacks to the client side, in
what situations can this be used?
A: Callbacks are easier with .NET as compared to VB in the past.They are also
very interesting to program, but in a business setting, you should use them only
when you have no other choice. For example, a callback to notify a user of a
certain situation may be better handled with a generated e-mail instead.You
could develop and debug the e-mail code a lot faster, and the end-user could
then use her e-mail program to assign tasks, forward the e-mail, and so on.
Q: Where can I find out more about remoting?
A: The best newsgroup for this is the
microsoft.public.dotnet.framework.remoting group. Also, the MSDN area on
the Microsoft site often publishes articles on aspects of .NET remoting.
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.

Message Queuing
Using MSMQ
Solutions in this chapter:
 Introducing MSMQ
 Creating a Simple Application
 Creating a Complex Application
 Creating an Asynchronous Application
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 7
345
346 Chapter 7 • Message Queuing Using MSMQ
Introduction
The connectivity of local area networks (LANs) and the Internet has made the
concept of distributed applications a reality. Applications now routinely utilize
remote machines to perform tasks and provide services. Unfortunately, a distributed
application has many more points of failure than a nondistributed application—
servers may be offline, the network may be overloaded, or the scheduled
maintenance or upgrading of servers can also cause problems for distributed
applications.What is needed is for an application to be able to continue running
when distributed resources are unavailable—which is what the messaging infrastructure
of Microsoft Message Queue (MSMQ) provides.
With MSMQ handling the communication between machines in a distributed
application, it has a great deal of responsibility. All communication (messages)
must have guaranteed delivery, security needs must be met, messages must
be logged, and delivery confirmations must be sent, too.The MSMQ product
provides all of these services and more—in fact, by using MSMQ technology, you
can make your applications more robust and scalable. For a small increase in
application complexity, you gain many rewards.
The .NET Framework provides added functionality during the development
of .NET applications. Objects can be serialized to binary or XML and then sent
as a message. By using .NET serialization you send any dataset, image, or file into
a message with very little coding.
In this chapter, we look at how MSMQ uses queues to store messages that
provide a communications mechanism between applications.You’ll see the
options that are available for the usage of queues and the variety of ways in
which messages can be created, sent, and received.We develop a simple MSMQ
example application, and we also discuss debugging and deployment issues.We
then move on to the more advanced areas of MSMQ and develop some additional
example applications to show how MSMQ can be of use in your own
applications.
Introducing MSMQ
MSMQ provides solutions to a range of problem scenarios that appear during
application development.A common example is in client-server architecture
where a direct connection to the server is needed to handle information updates.
In this situation, any disruption to the server will cause all client applications to
fail, possibly causing idle staff and lost productivity. Such a disruption may be
http://www.syngress.com
http://www.syngress.com
caused by a hardware failure, power outage, or a server upgrade.The solution is to
use message queuing to “uncouple” the client and the server by providing a
storage point for communication between them. Now, if a connection to the
server cannot be made, data entered in client application can be stored in a
client-side message queue. Once a connection is reestablished, the messages are
then automatically forwarded to the destination queue on a remote server.An
application on the server will then read the messages from the message queue and
perform the necessary database updates.
A similar situation to the client/server scenario is when it becomes quite
common for client applications to be disconnected from the network. Sales staff
often use notebook computers to run their sales applications while they are on
the road. Some may type in information directly into the notebook, whereas
others may wait until they arrive back at the office. Instead of writing code to
handle the online and offline situations (and possibly an “upload to server” function),
using message queuing to handle both cases is more efficient. In online
mode, all messages will be sent directly to the server queue. In offline mode, the
messages will be temporarily stored on the client, ready for forwarding to the
server queue when the network connection is made.
MSMQ also provides the plumbing you need for true distributed processing.
A good example of this is in the area of mathematical computation. If the work
required to find a solution to a mathematical problem can be divided into pieces
then such pieces could be placed on a queue. Each piece would constitute a message
containing all the information required for an idle computer on a network
to get started on the problem.These computers could take a piece of the mathematical
puzzle, calculate their share of the solution, and send the result back to
the queue.A central controlling application could then correlate the response
messages with the request messages and combine these results into the total solution.
You may also need MSMQ when you hear the following during conversations
between IT personnel:
 “Not again!,This Web server always takes at least three minutes to
respond on Monday morning, and my app slows to a crawl.”
 “The application locks up when they back up the server every night.
What can we do?”
 “How can we trace the communication between these applications?”
 “Last time our server was offline, we had 50 data entry operators idle for
an hour. How can we avoid this in the future?”
Message Queuing Using MSMQ • Chapter 7 347
348 Chapter 7 • Message Queuing Using MSMQ
A side effect of using MSMQ in your applications is that you can now easily
trace the messages sent between your application tiers.This tracing can help with
auditing and debugging your application.You can also authorize new applications
to hook onto your messaging infrastructure to reuse the services that your message
queuing applications provide.
The next time you participate in the planning phase of a software project,
keep these scenarios in mind, and you may find areas where MSMQ can really
save development time and increase application stability.
MSMQ Architecture
In the world of MSMQ, you will be dealing with two main objects—queues and
messages. A queue is a storage area for messages on a MSMQ server. A queue
may be public or private. Private queues can only be used on a single machine,
whereas public queues are replicated around the network for all machines to
access. A message can be thought of as an envelope containing data plus information
that describes the type of data being sent, its priority, security needs,
acknowledgement, and timing information. Applications may send and receive
messages from any queue that they have access to.
Computers running client applications must have MSMQ installed to be able
to send messages.Within the MSMQ architecture are two types of clients:
 Dependent clients Dependent clients must have direct access to a
Message Queuing server. Client applications will then rely on the server
to carry out all message queuing functions.When many dependent
clients are connected to a Message Queuing server, a performance
penalty can occur. Also, you cannot use the “store and forward” features
of MSMQ with dependent clients—client apps will simply fail when
disconnected from the network. For this and other reasons, independent
clients are the recommended choice where possible.
 Independent clients Independent clients do not need continuous
access to a Message Queuing server to send messages.Any messages that
are sent while disconnected from the network are stored locally in an
outgoing queue.When a connection is reestablished, the messages in the
outgoing queue will be sent to the destination queue.
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 349
Installing MSMQ
To get started using MSMQ, you will need a computer running Windows 2000.
Simply choose Add/Remove Programs from the Control Panel, select Add/
Remove Windows Components, and select Message Queuing Services
from the list of components, as shown in Figure 7.1.You may require your
Windows 2000 installation CD to complete the install.
Using Visual Studio to Manage Queues
Visual Studio.NET provides the Server Explorer to handle common server
administration tasks, and you can use it to manage your MSMQ applications. If
the Server Explorer is not visible, just use Ctrl+Alt+S to display it—you will see
under the Servers node a list of computers that are available (see Figure 7.2).Your
own computer will be listed here, but you can connect to other servers by using
the Connect To Server toolbar button. Under a computer node is a list of
application services including Message Queues, which allows you to create/delete
queues and set various queue properties.
Creating a Simple Application
We now create a simple application that will send a string message to a queue
and receive it back.This application will be built upon later to demonstrate other
aspects of Message Queue applications.The code for this application is located on
the CD inside the MSMQapp1 project.
http://www.syngress.com
Figure 7.1 Selecting Message Queuing Services for Installation
350 Chapter 7 • Message Queuing Using MSMQ
NOTE
If you are using the example code from the CD, you will need to modify
the path to your message queue. To do this, click on the message queue
component while the form is in design view. In the Property window,
modify the path to a queue on your local Windows 2000 PC or server.
1. Create a new Windows Application type project in Visual Studio and
name the project MSMQapp1.
2. From within the Server Explorer, right-click Private Queues, select
Create Queue, and enter the name Alpha for the queue.
3. While Form1 is in design mode, click and drag the Alpha queue from
the Server Explorer to your form’s design surface.This will create a
queue component for your application to use.
4. Add two Buttons to the form called btnSend and btnReceive.
5. Add a private variable of type int to the class declaration of Form1 as
follows:
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnSend;
private System.Windows.Forms.Button btnReceive;
http://www.syngress.com
Figure 7.2 The Server Explorer Showing Available Queues
Message Queuing Using MSMQ • Chapter 7 351
private System.Messaging.MessageQueue MyMQ;
private System.ComponentModel.IContainer components;
private int iCount = 0;
. . .
6. Add the following code to the click events of the two buttons. A quick
method of adding event handlers is to view the properties of a button,
switch to event view by clicking the Events toolbar button at the top of
the property window, and then double-click the click event from the
event listing.This will create the event handler method for you and
attach the new event handler to the button. It will also place the cursor
at the correct location in the code for you to start coding the event handler.
For those of you in more of a hurry, you can simply double-click
the button on the form.
private void btnSend_Click(object sender, System.EventArgs e)
{
iCount++;
try
{
MyMQ.Send(“message contents ” + iCount.ToString());
}
catch(Exception ex)
{
MessageBox.Show(this,ex.Message);
}
}
private void btnReceive_Click(object sender, System.EventArgs e)
{
System.Messaging.Message m;
String str;
try
http://www.syngress.com
352 Chapter 7 • Message Queuing Using MSMQ
{
m = MyMQ.Receive(new TimeSpan(0, 0, 3));
str = (String)m.Body;
}
catch
{
str = “No messages were receieved”;
}
MessageBox.Show(this,str);
}
7. Build and run the MSMQapp1 application.
You can now click Send to send some messages to the Alpha message queue.
Now take a look at the Server Explorer and navigate to the Alpha node to see
the messages that you have sent. If you are running the application from within
Visual Studio, you will need to select Debug | Break All to pause execution so
that you can use the Server Explorer. Notice that as you click on a message in
Server Explorer, the Property window displays a large amount of information
regarding the specific message.
To receive messages, click Receive.A message box will display the contents
of the message received, as shown in Figure 7.3. Notice that the messages are
received in the same order that they were sent—this cannot be relied upon.
Messages that are sent with a higher priority can jump ahead of other messages
on the queue while other applications may remove messages from the queue
without your knowledge.A queue should be thought of as a “bag” of messages to
emphasize that we cannot make assumptions as to the order of messages we will
receive.
http://www.syngress.com
Figure 7.3 A Simple Messaging Application
Message Queuing Using MSMQ • Chapter 7 353
As a further experiment, try opening up two or more instances of the
MSMQapp1 application by using Explorer to find and execute MSMQapp1.exe
multiple times. Use one application instance to send messages and another
instance to receive them.You can see that each application is completely independent
and that they all share the same message queue.
Understanding the Messaging Code
The MSMQ classes are contained within the System.Messaging namespace. Of the
more than 20 classes contained within this namespace, the most important is
MessageQueue. In Step 3 in the previous section, when the Alpha queue was
dragged from Server Explorer to the form’s design surface, four lines of code were
automatically generated that will reference the System.Messaging namespace, declare
and create a MessageQueue object, and set the Path property to the location of the
Alpha queue.The format of the Path property is MachineName\\QueueName for
public queues and MachineName\\$Private\\QueueName for private queues.
Because the amount of generated code is quite small, manually adding similar code
to your applications is quite easy.
To send a message to a queue, you use the Send method of the MessageQueue
object. For the purposes of our example, a simple string was used as a message,
but we will see later how more complex messages may be sent.
To receive a message from a queue, use the Receive method of the MessageQueue
object.This method returns an object of type Message to the caller.The Message
object contains a large amount of information in regard to the message itself—if
you just want to know the content of the message, use the Body property.The Body
is of type object and therefore must be cast to the correct type of the receiving variable,
which in the example is type (String).The Receive method accepts a TimeSpan
parameter, which specifies how long we want to wait for the arrival of a message. If
a message exists on the queue, Receive will return quickly, but if no messages exist,
the application’s thread of execution will be blocked while waiting for the next
message. For this reason, keeping the wait time short is a good idea.An asynchronous
(nonblocking) method of receiving messages is described later in this
chapter in the section “Creating an Asynchronous Application.”
Sending Messages
Within the System.Messaging namespace is the Message class, which can be considered
to be the “envelope” in which messages are sent.When you call the Send
method of a queue and pass in an object as a parameter, the .NET Framework
http://www.syngress.com
354 Chapter 7 • Message Queuing Using MSMQ
creates a new message and sets the Body property of the message to the object.
For example, the following code fragment
MyMQ.Send(“content”);
is equivalent to
Message m = new Message();
m.Body = “Content”;
MyMQ.Send(m);
Be aware, though, that in creating your own Message object that you must set
the other properties of the Message object to suit your needs.When you send an
object that is not a Message, the Message properties are set to those defined in the
DefaultPropertiesToSend property of the queue.
You can test this method of sending messages by adding a button to Form1
called btnSend2 and adding the following code to the click event handler of the
button:
private void btnSend2_Click(object sender, System.EventArgs e)
{
System.Messaging.Message m = new System.Messaging.Message();
m.Body = “Custom Message”;
MyMQ.Send(m);
}
NOTE
The code for this change is in the Form2.cs file on the CD. To update
your project as you read the chapter, from within Visual Studio, rightclick
the Form1.cs file, choose Exclude From Project, then right-click the
solution, choose Add | Add Existing Item, and select the Form2.cs file.
Then add code to set the queue’s default properties for sending messages
directly after the InitializeComponent call in the form’s constructor:
public Form1()
{
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 355
InitializeComponent();
MyMQ.DefaultPropertiesToSend.Label = “Default Label”;
MyMQ.DefaultPropertiesToSend.Priority =
MessagePriority.Highest ;
}
Now build and run the application. Clicking on the first Send button will
now use the DefaultProperties for the queue because we are only passing a string
to the Send method. After clicking this button, a few times you can use the
Server Explorer to see the labels of the messages have been set to “Default
Label”. Using the second Send button will use the Message object that does not
have a label set. Notice that we are also making all default messages to be of
highest priority.This will cause the messages containing the words “message contents”
to always be received before the custom messages that contain the text
“Custom Message”.
Message Formats
All data that is sent to a remote server during Message Queue processing must be
serialized into a stream.The method of serializing an object into a stream is called
formatting.The Message class handles streaming automatically for you by applying a
formatter to whatever object you set as the Body property.The output of the formatter
appears in the BodyStream property of the message:
 XMLMessageFormatter Streams objects and value types to humanreadable
XML.
 BinaryMessageFormatter Streams objects to a binary stream.
 ActiveXMessageFormatter Persists basic data types and enables a
message queuing application to work together with previous versions of
MSMQ.
You can also write a stream directly to the BodyStream property of the
Message object, and we use this method later to send images to a queue.
To examine the contents of the BodyStream property, you can use the Server
Explorer again to display the properties of a message. Click on the BodyStream
property from within the Properties window and you will see a “. . .” button
appear within the property value cell. Clicking on the button will display the
Byte Array Property Editor, as shown in Figure 7.4.
http://www.syngress.com
356 Chapter 7 • Message Queuing Using MSMQ
To change the formatter that is used, you can use the property window of the
MessageQueue component and select the formatter from the drop-down list.
From within the code, you can do the same by setting a reference to the
System.Runtime.Serialization.Formatters namespace and attaching a new formatter
to the queue as follows:
MyMQ.Formatter = new BinaryMessageFormatter(FormatterAssemblyStyle.Full,
FormatterTypeStyle.TypesAlways);
The FormatterAssemblyStyle and FormatterTypeStyle parameters determine how
assembly and type names are sent to the stream.These choices handle all situations
by using the most verbose method of describing the types—if a more compact
stream is needed, you may want to experiment with the different options
available.
Sending and Receiving
Messages with Complex Objects
Formatters provide a means of streaming not only single objects but also complex
objects within a message.To test the sending of complex objects in a message, we
now add two new classes to the example application, have one class contain a
collection of the other class, instantiate the objects, and then pass them to a message
queue.The updated code is within the Form3.cs file on the CD.
First, add the following assembly reference to the top of the form’s code:
using System.Xml.Serialization;
Now, add two new classes to the application:
http://www.syngress.com
Figure 7.4 The BodyStream Contents—The Result of Streaming a
Message to XML
Message Queuing Using MSMQ • Chapter 7 357
[XmlInclude(typeof(Student))]
public class Teacher
{
public String name;
public System.Collections.ArrayList students;
public int salary;
public Teacher()
{
students = new System.Collections.ArrayList();
}
}
public class Student
{
public String name;
public int minutesInClass;
}
Don’t forget the [XmlInclude] attribute! This is needed when you send an
object of type Teacher to the XML formatter because the formatter will not recognize
the Student objects within the ArrayList.This attribute allows the formatter
to serialize the Student objects found nested within a Teacher object. If this
attribute is not added, you will receive a runtime exception with the following
message:
There was an error generating the XML document. The type
MSMQapp1.Student was not expected. Use the XmlInclude or SoapInclude
attribute to specify types that are not known statically.
Now add a new button to the form called btnTeacher and add the following
code to the button’s click event:
private void btnTeacher_Click(object sender, System.EventArgs e)
{
Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();
http://www.syngress.com
358 Chapter 7 • Message Queuing Using MSMQ
Teacher t = new Teacher();
s1.name = “Jason”;
s2.name = “Marlo”;
s3.name = “Jacky”;
s1.minutesInClass = 90;
s2.minutesInClass = 5;
s3.minutesInClass = 100;
t.name = “Tom”;
t.salary = 50000;
t.students.Add(s1);
t.students.Add(s2);
t.students.Add(s3);
System.Messaging.Message m = new System.Messaging.Message();
m.Body = t;
try
{
MyMQ.Send(m);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ” ” + ex.InnerException.Message);
}
}
After building and running the application, click on the new button to send a
Teacher object with its contained Students to a message queue. By using the Byte
Array Property Editor to display the BodyStream property of the new message,
you can see that the objects have been successfully streamed to human-readable
XML, as shown in Figure 7.5.
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 359
Figure 7.5 A Complex Object Streamed with the XML Formatter
<?xml version=”1.0″?>
<Teacher xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt;
<name>Tom</name>
<students>
<Object xsi:type=”Student”>
<name>Jason</name>
<minutesInClass>90</minutesInClass>
</Object>
<Object xsi:type=”Student”>
<name>Marlo</name>
<minutesInClass>5</minutesInClass>
</Object>
<Object xsi:type=”Student”>
<name>Jacky</name>
<minutesInClass>100</minutesInClass>
</Object>
</students>
<salary>50000</salary>
</Teacher>
Retrieving a complex object message from a queue is done using the familiar
Receive method of the MessageQueue class. Once the message is received, you will
need to use a formatter to obtain the original object from the BodyStream.This is
done by creating a formatter and specifying the type of object that needs to be
obtained.The Body property of the message can then be cast to the correct type.
To try this, append the following code to the end of the code in the
btnTeacher_Click event (this code is in Form4.cs on the CD):
System.Messaging.Message m2;
Teacher t2;
try
{
http://www.syngress.com
360 Chapter 7 • Message Queuing Using MSMQ
m2 = MyMQ.Receive(new TimeSpan(0, 0, 3));
m2.Formatter = new XmlMessageFormatter(new
Type[]{typeof(Teacher),typeof(Student)});
t2 = (Teacher)m2.Body;
MessageBox.Show(“Message received. ” + t2.name + ” has ” +
t2.students.Count + ” students.”);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
After building and running the application, click on the same button used
earlier to send the Teacher object to the message queue.The preceding code will
then immediately receive the message, define an XML formatter, and extract the
Teacher object from the message.A dialog saying “Message received.Tom has 3
students.” will then be displayed.
Storing Files within Messages
The BodyStream property of the Message class contains the serialized form of the
message contents and usually does not need to be directly accessed by the developer
(although it can be handy during debugging). One situation in which we
do need to use the BodyStream property is when we already have information in
a stream and wish to send it directly to a message queue.
Streams are usually created during memory and file I/O operations—we use
an image file to create a stream, pass the stream to a Message object, and then send
it to a queue.This message will then be received and a Bitmap object created
from the stream and displayed inside a PictureBox control.
The code for this example is in Form5.cs on the CD. First, add two new buttons
to the simple messaging application called btnSendImage and btnReceiveImage.
Add a picture box control named picBox1 and also an OpenFileDialog component
to the form.Then add a reference to the System.IO namespace as follows:
using System.IO;
Now add the following code to the click events of the two new buttons:
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 361
private void btnSendImage_Click(object sender, System.EventArgs e)
{
Stream imageStream;
System.Messaging.Message mImage = new System.Messaging.Message();
openFileDialog1.Filter = “image files (.bmp,.jpg,.gif)|
*.bmp;*.jpg;*.gif;*.exe” ;
openFileDialog1.FilterIndex = 1 ;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
if((imageStream = openFileDialog1.OpenFile())!= null)
{
mImage.BodyStream = imageStream;
try
{
MyMQ.Send(mImage);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
imageStream.Close();
}
}
}
}
private void btnReceiveImage_Click(object sender, System.EventArgs e)
{
Bitmap bmp;
http://www.syngress.com
362 Chapter 7 • Message Queuing Using MSMQ
Stream imageStream;
System.Messaging.Message mImage = new System.Messaging.Message();
try
{
mImage = MyMQ.Receive(new TimeSpan(0, 0, 3));
}
catch
{
MessageBox.Show(“No messages were received”);
}
try
{
imageStream = mImage.BodyStream;
bmp = new Bitmap(imageStream);
picBox1.Image = bmp;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
In the btnSendImage_Click event, the code obtains a file path from the user
and a stream is opened from the file and passed to the BodyStream property of the
message.The message is then sent in the usual manner using the Send method.
In the btnReceiveImage_Click event, a message is received, and a stream is
obtained from the BodyStream property.A new bitmap is then created from the
stream, and this bitmap is passed to a picture box object for display.The three
lines of code to achieve this can be reduced to the following one line of code:
picBox1.Image = new Bitmap(mImage.BodyStream);
After building and running the application, click on the btnSendImage button
and select a file from the dialog. Now click on the btnReceiveImage button, and
the image will be displayed as shown in Figure 7.6.
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 363
http://www.syngress.com
Figure 7.6 Sending and Receiving an Image from a Queue
MSMQ Applications
During the development of MSMQ applications, many objects must act
in unison to achieve the desired results. Problems can occur in the following
areas:
 The DefaultPropertiesToSend property collection of a message
queue can cause unexpected behavior during the
sending of a primitive data type—make sure to check the settings
of the default properties.
 The wrong formatter is used when sending a complex object.
 A complex object does not contain the required attributes to
allow the serialization of the object’s contents.
Developing the message queuing application using simple string
messages can often be useful. Once the application has been debugged,
you can use more complex messages and formatters. Remember that
while debugging, you can always pause the execution of the code and
examine the contents of the messages with the Server Explorer window.
Another useful debugging tool is the Performance Viewer located
in the Administrative Tools area of the Control Panel. This tool displays
performance information for a range of services and applications, one of
them being MSMQ. To view the MSMQ performance statistics, click on
the + icon and select MSMQ Queue from the Performance Object list.
Choose one of the counters that displays either bytes or messages and
then click Add. You will now see the activity of MSMQ in real time and
can check that your queue is behaving as expected.
Debugging…
364 Chapter 7 • Message Queuing Using MSMQ
Setting Queue Options
So far we have mostly accepted the default properties of a queue that the .NET
Framework has provided.To improve the robustness of a message queuing application,
you need to modify some of these properties.To do so, click on Message
Queue component while your form is in design mode.The Property window
will display the DefaultPropertiesToSend property with a + icon so that you can
expand it. Some important properties to modify are the following:
 AcknowledgeType Use this property to request acknowledgement on
the success or failure of messages to reach a destination queue.
Acknowledgements are sent as messages to the Administration queue
defined by the developer.
 AdministrationQueue This queue will receive acknowledgement
messages that are generated on success or failure conditions as defined in
the AcknowledgeType property.
 Recoverable Setting this property to true will guarantee the delivery
of a message even if a computer or network failure occurs.To achieve
this, the message and its state are written to disk at all times in order to
recover from such failures. Setting this option does degrade the
throughput of the application.
 TimeToBeReceived Sets the maximum amount of time for a message
to wait on the destination queue. If the UseDeadLetterQueue property is
set to true, the expired message will be sent to the dead-letter queue.
 TimeToReachQueue Specifies the maximum amount of time for a
message to arrive at the destination queue. If the UseDeadLetterQueue
property is set to true, the expired message will be sent to the deadletter
queue.
NOTE
You can also specify the properties listed in this section on a per-message
basis by setting the equivalent properties on a Message object.
We now use a couple of these properties to send expired messages to the
dead-letter queue (the code for this is in the Form6.cs file on the CD):
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 365
1. Open the form of the simple messaging application in design mode.
2. Click on the message queue component under the form and bring up
the Property window.
3. Expand the DefaultProperties property by clicking on the + icon.
4. Set the TimeToBeReceived property to 5 seconds.
5. Set the UseDeadLetterQueue property to true.
6. Build and start the project.
Now click Send (the very first button that was added to the project).You
now have five seconds to click the Receive button to obtain the message before
it expires and is sent to the dead-letter queue.Try this a few times and make sure
to let a few messages expire! You can now view the dead-letter queue by using
the Server Explorer, as shown in Figure 7.7.
Creating a Complex Application
We now create a more complex message queuing application.This application is
composed of a form that allows the user to draw pictures using line segments.A
Drawing object that contains a collection of Line objects will hold the drawing’s
information.The Drawing object will then be streamed to XML and sent to a
queue. Another application will “listen” in on the queue and receive any new
drawings that appear on the queue and then display them.
The application has three separate projects (the full source code is available on
the CD):
http://www.syngress.com
Figure 7.7 Examining the Dead-Letter Queue
366 Chapter 7 • Message Queuing Using MSMQ
 MSMQGraphics A class library application that contains the Drawing
and Line classes.This DLL is used by the other two projects.
 DrawingSender A Windows application that allows the user to draw
on the form and send the drawing as a message.
 DrawingReceiver A Windows application that listens for new drawings
on the queue.
Creating the MSMQGraphics Drawing Library
Figure 7.8 shows the code listing of the MSMQGraphics class library.This library
contains all the functionality needed to draw a collection of line segments on a
graphics surface.
Figure 7.8 The Drawing Library Project
using System;
using System.Drawing;
using System.Xml.Serialization;
using System.Collections;
namespace MSMQGraphics
{
[XmlInclude(typeof(Line))]
public class Drawing
{
public ArrayList lines;
public Drawing()
{
lines = new ArrayList();
}
public void clear()
{
lines.Clear();
}
http://www.syngress.com
Continued
Message Queuing Using MSMQ • Chapter 7 367
public void add(Line l)
{
lines.Add(l);
}
public void draw(Graphics g)
{
foreach (Line l in lines)
{
l.draw(g);
}
}
}
public class Line
{
public int x1;
public int y1;
public int x2;
public int y2;
public int Win32Color;
public Line()
{
}
public Line(int Win32Color,int x1,int y1,int x2,int y2)
{
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.Win32Color = Win32Color;
http://www.syngress.com
Figure 7.8 Continued
Continued
368 Chapter 7 • Message Queuing Using MSMQ
}
public void draw(Graphics g)
{
g.DrawLine(newPen(ColorTranslator.FromWin32(Win32Color)),
x1,y1,x2,y2);
}
}
}
This code should be straightforward for those that have spent some time with
C# class definitions and collections, but some points must be noted. First, the
XmlInclude attribute is necessary so that the XML formatter can recognize the
Line objects within the ArrayList. Also, a default constructor for the Line class has
been added because this is also required by the XML formatter. Finally, you will
notice that we have used an integer value to determine the color instead of a
Color object.This roundabout way is due to the XML formatter being unable to
handle static classes. As you can see, it is important to design classes so that they
may be easily handled by the XML formatter if you expect that they will be used
within a MSMQ application.
http://www.syngress.com
Figure 7.8 Continued
MSMQ Applications
The development of message queuing applications can be aided by creating
a class that hides many of the message queuing details from other
areas. Such a class would have an event that was called when messages
arrived allowing you to attach your own event handler. All formatting
and streaming would be handled inside this class, too, and you could
use extra methods to make development more efficient. For example,
you could have a Clear method that would clear out all messages from
your queue and a Dump method that could write the contents of all
messages to a file. With some extra effort, this class could be reused in
future message queue applications.
Developing & Deploying…
Continued
Message Queuing Using MSMQ • Chapter 7 369
Creating the DrawingSender Project
This project will use the MSMQGraphics library to allow the user to draw on a
form.This form contains the following components:
 pictureBox1 The drawing surface.
 btnColor This button is used to change the current color.
 btnSend This button will send the drawing to the drawings message
queue.
 drawingMQ The message queue component that was created by dragging
the drawings message queue from the Server Explorer window.
 colorDialog1 A color dialog window that allows the selection of a
color.
The code that handles the drawing and sending of the message is shown in
Figure 7.9.
Figure 7.9 The DrawingSender Project
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
http://www.syngress.com
Message queuing code often has to depend on a queue having been
already created by an administrator. By adding an installer for your
queue, you can make sure that such queues are created during installation.
To add an installer, click on the Message Queue component under
your form while it is in design mode. View the properties for the component
and click on the Add Installer hyperlink at the bottom of the
Properties window. Visual Studio will create a new file named
ProjectInstaller.cs and place the file in design mode so that the
messageQueueInstaller1 component is visible on the design surface. You
can then click on the messageQueueInstaller1 component and set the
properties of the queue in the Property window. The executable built
from this project will now contain a ProjectInstaller class that is detected
by the installutil.exe tool. Running installutil.exe with the path to the project
executable as a command-line parameter will then install the queue.
Continued
370 Chapter 7 • Message Queuing Using MSMQ
using System.Data;
using System.Messaging;
using System.Xml.Serialization;
using MSMQGraphics;
namespace DrawingSender
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Button btnSend;
private System.Windows.Forms.ColorDialog colorDialog1;
private System.Windows.Forms.Button btnColor;
private System.Drawing.Color currentColor;
private System.Drawing.Pen currentPen;
private int startx;
private int starty;
private int endx;
private int endy;
private bool lineInProgress = false;
private MSMQGraphics.Drawing thisDrawing =
new MSMQGraphics.Drawing();
private System.Messaging.MessageQueue drawingMQ;
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
currentColor = Color.Black;
currentPen = new Pen(currentColor);
}
private void btnSend_Click(object sender, System.EventArgs e)
http://www.syngress.com
Figure 7.9 Continued
Continued
Message Queuing Using MSMQ • Chapter 7 371
{
System.Messaging.Message m = new System.Messaging.Message();
m.Body = thisDrawing;
try
{
drawingMQ.Send(m);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ” ” +
ex.InnerException.Message);
}
}
private void btnColor_Click(object sender, System.EventArgs e)
{
colorDialog1.ShowDialog();
currentColor = colorDialog1.Color;
btnColor.BackColor = currentColor;
currentPen = new System.Drawing.Pen(currentColor);
}
private void pictureBox1_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
startx = e.X;
starty = e.Y;
lineInProgress = true;
}
private void pictureBox1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
http://www.syngress.com
Figure 7.9 Continued
Continued
372 Chapter 7 • Message Queuing Using MSMQ
if (lineInProgress)
{
endx = e.X;
endy = e.Y;
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseUp(object sender,
System.Windows.Forms.MouseEventArgs e)
{
if (lineInProgress)
{
lineInProgress = false;
Graphics g = pictureBox1.CreateGraphics();
g.DrawLine(currentPen,startx,starty,e.X,e.Y);
Line l = new Line(ColorTranslator.ToWin32(
currentColor),startx,starty,e.X,e.Y);
thisDrawing.add(l);
}
}
private void pictureBox1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
thisDrawing.draw(e.Graphics);
if (lineInProgress)
{
e.Graphics.DrawLine(currentPen,startx,starty,endx,endy);
}
}
}
}
http://www.syngress.com
Figure 7.9 Continued
Message Queuing Using MSMQ • Chapter 7 373
The form defined in this code has a private property that holds the current
drawing, thisDrawing. As mouse events are detected, Line objects are created and
then added to the line collection within the Drawing object.When the user is
happy with the drawing, clicking the Send button sends the Drawing object
together with its line objects to the message queue. Note that this project must
have a reference to the MSMQDrawing project to enable the application to be
compiled.
Creating the DrawingReceiver Project
This project also uses the MSMQGraphics library and consists of the following
components:
 pictureBox1 The drawing surface.
 timer1 A timer that attempts to receive messages every 5 seconds.
 drawingMQ The message queue component that was created by dragging
the drawings message queue from the Server Explorer window.
The code that handles the receiving of the drawing message and displays it is
shown in Figure 7.10.
Figure 7.10 The DrawingReceiver Project
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Messaging;
using System.Xml.Serialization;
using MSMQGraphics;
namespace DrawingReceiver
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.PictureBox pictureBox1;
http://www.syngress.com
Continued
374 Chapter 7 • Message Queuing Using MSMQ
private MSMQGraphics.Drawing thisDrawing;
private System.Messaging.MessageQueue drawingMQ;
private System.Windows.Forms.Timer timer1;
private System.ComponentModel.IContainer components;
public Form1()
{
InitializeComponent();
thisDrawing = new MSMQGraphics.Drawing();
timer1.Enabled = true;
}
private void checkForDrawing()
{
System.Messaging.Message m;
MSMQGraphics.Drawing d;
try
{
m = drawingMQ.Receive(new TimeSpan(0, 0, 1));
m.Formatter = new XmlMessageFormatter(new
Type[]{typeof(MSMQGraphics.Drawing ),
typeof(MSMQGraphics.Line )});
d = (MSMQGraphics.Drawing )m.Body;
thisDrawing = d;
pictureBox1.Invalidate();
}
catch
http://www.syngress.com
Figure 7.10 Continued
Continued
Message Queuing Using MSMQ • Chapter 7 375
{
// We don’t want to display a message after
every 5 second poll if no messages are available
}
}
private void pictureBox1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
thisDrawing.draw(e.Graphics);
}
private void timer1_Tick(object sender, System.EventArgs e)
{
checkForDrawing();
}
}
}
This form also has a reference to the MSMQDrawing project to give access to
the Drawing and Line classes. After the queue is initialized, the timer control is
enabled and polling of the queue at five-second intervals is started.When a message
is received, the XML formatter is applied to the message to build the
Drawing object together with the collection of Line objects.The Invalidate method
is then called on the picture box control, which forces a Paint event and the subsequent
display of the drawing.
After building the solution, two executables (DrawingSender.exe,
DrawingReceiver.exe) will be created. By running the DrawingSender application,
a form will be displayed, as shown in Figure 7.11.
By clicking and dragging on the form, you can draw line segments; you can
change the current color selection by clicking the Change Color button. Clicking
Send will send the drawing to the message queue.As you draw, you can send the
latest version of the drawing to the queue at any time. By starting the drawing
receiver application (DrawingReceiver.exe), you will see your drawing progressively
appear as each drawing is received at five second intervals—see Figure 7.12.
http://www.syngress.com
Figure 7.10 Continued
376 Chapter 7 • Message Queuing Using MSMQ
Creating an Asynchronous Application
You may have noticed while working with the examples in this chapter that the
application will stop responding while waiting to receive messages (especially if
none are in the queue). In some situations, you may want the user or the application
to perform some other task while waiting for a message.To make an asynchronous
call, you use the BeginReceive method of the MessageQueue object.
The call to BeginReceive returns immediately, and a callback is made to the
ReceiveCompleted method of the MessageQueue component when a message
arrives.We will now modify the Drawing Receiver application to make asynchronous
calls for receiving messages (this modified form is saved as Form2 in the
DrawingReceiver source code directory).This modified code is in the file Form2.cs
in the DrawingReceiver directory on the CD:
 Remove the timer1 control from the form.
 Remove the timer1_Tick method and the timer1.Enabled = true line of
code from the form.
http://www.syngress.com
Figure 7.11 The DrawingSender Application
Figure 7.12 The DrawingReceiver Application Receiving Images from a
Message Queue
Message Queuing Using MSMQ • Chapter 7 377
 While the form is in design-view, double-click the message queue component
located under the form.This will create the ReceiveCompleted
method.
 In the forms constructor, add a call to checkForDrawing().The constructor
will then have the following code:
public Form1()
{
InitializeComponent();
thisDrawing = new MSMQGraphics.Drawing();
checkForDrawing();
}
 Modify the checkForDrawing method to call the BeginReceive method of
the message queue and add code to the ReceiveCompleted method as
shown here:
private void checkForDrawing()
{
drawingMQ.BeginReceive(new TimeSpan(0, 0, 3));
}
private void drawingMQ_ReceiveCompleted(object sender,
System.Messaging.ReceiveCompletedEventArgs e)
{
System.Messaging.Message m;
MSMQGraphics.Drawing d;
try
{
if (e.Message != null)
{
m = e.Message;
m.Formatter = new XmlMessageFormatter(new Type[]{typeof(
MSMQGraphics.Drawing ),typeof(MSMQGraphics.Line )});
d = (MSMQGraphics.Drawing )m.Body;
http://www.syngress.com
378 Chapter 7 • Message Queuing Using MSMQ
thisDrawing = d;
pictureBox1.Invalidate();
}
}
catch
{ }
drawingMQ.BeginReceive(new TimeSpan(0, 0, 3));
}
The initial call to checkForDrawing simply initiates the asynchronous call.
When the message is received (or the timeout has been reached), the
ReceiveCompleted method is called, and it is here where the work is done to
extract the message. After the message has been processed, the BeginReceive
method is called again to restart the process.
Using Public Queues While
Disconnected from the Network
A useful feature of MSMQ applications is their ability to keep operating even
when disconnected from the network. Once the client application is reconnected,
the messages that have been stored on the client are then forwarded to
the designation queue.To use this feature in your own applications, you need to
modify the method of identifying the queue.
All of the examples in the chapter use the Path property to point to a queue.
For example:
this.drawingMQ.Path = “synergy\\drawings”;
If you attempt to send a message, and the queue specified in the Path can not
be found, an exception is raised and the send fails.To avoid this problem, you can
specify a path location in a second way, which uses the syntax FormatName:[
format name ].
To obtain the FormatName for a queue, you use the Solution Explorer and
click on the queue you want to use.The Property window will then show the
FormatName property, which you can cut and paste into your code.The code to
specify the message queue will then appear similar to this:
drawingMQ.Path=”FormatName:Public={81c4c70d-71e7-4ec6-a910-
9fcf16278f8b}”;
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 379
Summary
This chapter gave an introduction to the powerful services that Microsoft
Message Queue (MSMQ) provides. Message queuing helps distributed applications
in the following areas:
 Less reliance on permanent connections to servers
 Asynchronous requests capability
 Robustness in the face of hardware of network instability
 Communication encryption and authorization
 Tracing and failure notifications
A message queuing application sends and receives messages from queues.
These queues may be located on the same machine or on a remote server.
Examining the client tier of an application, there are two types of MSMQ
clients—dependent and independent.A dependent client requires a direct connection
to a Message Queuing server to send messages, whereas an independent
client can operate with or without such a connection.
MSMQ is a Windows component.Therefore, you can install it from the
Add/Remove Windows Components area of the Control Panel. During installation,
you will be asked whether you require a dependent or independent client
installation. After a successful installation, you can use Visual Studio.NET to
manage your queues from the Server Explorer window.
The message queuing area of the .NET Framework is in the System.Messaging
namespace.The most important class in this namespace, MessageQueue, allows you
to send and receive messages and to manage your queues.The Send method of
the MessageQueue class allows you to send messages; you use the Receive method
to receive them. Although you can send simple strings as messages, you can also
create and send a Message object. Using the Message object allows you to fine-tune
the message settings on a per-message basis.The Receive method will block code
execution while waiting for messages.To prevent this, you can use the asynchronous
BeginReceive method—the method call will return immediately and
the ReceiveCompleted method of the MessageQueue object will be called when a
message arrives.
Messages that are sent to a queue must be serialized into a stream.The .NET
Framework provides two serialization methods (formatters) for this purpose:
XMLMessageFormatter and BinaryMessageFormatter, which are selected by using
the Formatter property of the MessageQueue class. A third formatter type,
http://www.syngress.com
380 Chapter 7 • Message Queuing Using MSMQ
ActiveXMessageFormatter, is used when connecting to queues created with previous
versions of MSMQ. By using formatters to stream message data, the .NET
Framework allows the sending of complex nested objects as messages, too. It may
be necessary to modify such classes so that they are “formatter friendly” before
using them in a message queuing application. If an application is dealing with
streams of data from files, memory, images, and so on, you can bypass the formatting
process and send the stream directly into the message by using the
BodyStream property of the Message class.
Message queues have a number of configuration settings that modify the way
messages are handled.These settings are within the DefaultPropertiesToSend property
collection of the MessageQueue class.The TimeToBeReceived and
TimeToReachQueue properties specify the timeout settings of any messages sent to
the queue—any messages that expire are sent to the dead-letter queue if
UseDeadLetterQueue is set to true.The Recoverable property makes sure that all
messages are written to disk so that you can recover them after an unexpected
system shutdown.The AcknowledgeType property requests acknowledgement messages
reporting on the success or failure of messages in reaching the destination
queue—the AdministrationQueue specifies which queue will receive these
acknowledgement messages.You can also set all of these properties for individual
messages by setting the corresponding properties on a Message object.
When sending messages to a public queue on a remote server, you may need
to handle the situation where a network connection is unavailable. In this case,
you must use the FormatName of the queue.The FormatName is composed of a
GUID string that uniquely identifies the queue on the network. All messages sent
while disconnected from a network are stored temporarily in an outgoing queue,
ready to be forwarded to the destination queue when a network connection is
reestablished.
Solutions Fast Track
Introducing MSMQ
􀀻 Message queuing applications use queues and messages to communicate.
􀀻 Queues are storage areas for messages. Queues can be either public or
private.
􀀻 Public queues can be shared by all computers on a network. Private
queues can be used only by the machine where the queue resides.
http://www.syngress.com
Message Queuing Using MSMQ • Chapter 7 381
􀀻 MSMQ client machines are either dependent or independent.A
dependent client requires a connection to a remote queue to send
messages, whereas an independent client does not.
Creating a Simple Application
􀀻 Messages can be sent using two methods—by using a Message object or a
simple data type.
􀀻 When sending a simple data type, the message queue’s default properties
are used.
􀀻 When using the Message object, you can set its properties to handle your
messaging requirements instead of using the message queue’s default
properties.
Creating a Complex Application
􀀻 You can send complex objects as messages by using a formatter to
stream the message to XML.
􀀻 The class definitions of the complex object must contain attributes to
help the formatter.
Creating an Asynchronous Application
􀀻 An asynchronous message queuing application will immediately return
control back to the user while waiting for messages to arrive.
􀀻 The asynchronous receive method will also return after a specified period
of time if no messages were received.
􀀻 An asynchronous receive uses the ReceiveCompleted method of the
MessageQueue object to notify an application when a message has been
received.
http://www.syngress.com
382 Chapter 7 • Message Queuing Using MSMQ
Q: In what ways can I improve the performance of my MSMQ applications?
A: With MSMQ, as in other software areas, a tradeoff exists between security/
stability and performance.Acknowledgement messages can lessen performance
because they can effectively double the number of messages being
handled.The Recoverable property, although useful in persisting messages to
disk, can also cause performance problems with large numbers of messages. It
is important to do performance testing under expected loads before the
deployment of a new application.
Q: How can I programmatically list available queues?
A: The GetPublicQueuesByLabel, GetPublicQueuesByCategory, and
GetPublicQueuesByMachine methods of the MessageQueue class provide access
to queues on a network.To specify more exactly the type of queue you are
looking for, the GetPublicQueues method has a MessageQueueCriteria parameter
in which you can specify combinations of search criteria.
Q: I want to examine the contents of a message before actually removing it from
the queue. How can I do that?
A: The MessageQueue class has Peek and BeginPeek methods that allow both synchronous
and asynchronous retrieval of messages without removing them
from the queue.These methods return a Message object that you can then
examine, and you can store the ID of this message.Then, if your program
logic decides to remove this message from the queue, it can use the
ReceiveById method to remove the message. Using the ID for message
removal is important because another application may also remove the message
between your calls to Peek and Receive.
Q: How can I learn more about message queuing?
A: The microsoft.public.dotnet.general news group has some .NET-specific
MSMQ information; the microsoft.public.msmq groups are the main areas of
activity.
http://www.syngress.com
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book,
are designed to both measure your understanding of the concepts presented in
this chapter and to assist you with real-life implementation of these concepts. To
have your questions about this chapter answered by the author, browse to
http://www.syngress.com/solutions and click on the “Ask the Author” form.
ADO.NET
Solutions in this chapter:
 Introducing ADO.NET
 Working with System.Data.OleDb
 Working with SQL.NET
 Working with Odbc.NET
􀀻 Summary
􀀻 Solutions Fast Track
􀀻 Frequently Asked Questions
Chapter 8
383
384 Chapter 8 • ADO.NET
Introduction
ADO.NET is the latest implementation of Microsoft’s Universal Data Access
strategy. In the past few years, classic ActiveX Data Objects (ADO) has gone
through many changes, bug fixes, and enhancements.These libraries have been
the foundation for many Web sites and applications that are in place today.
ADO.NET will be no different in this respect because Microsoft is positioning
ADO.NET to be the primary data access technology for the .NET Framework.
This will ensure that the Data Access Architecture is mature and robust because
all the Common Language Runtime (CLR) languages will be using ADO.NET
for their primary means of communicating with data providers.
Flexible and efficient data access technologies are at the heart of dynamic
Web sites and Web applications. Classic ADO serialized data in a proprietary protocol
that limited its reach, and it could have been made more efficient.
ADO.NET serializes data using XML.This allows ADO.NET to take advantage
of a standards-based approach to moving data back and forth in your applications.
With rich support for any data source that can create or consume XML,
ADO.NET is truly the data access technology for current and future applications.
Through ADO.NET, you are able to connect to myriad data sources with the
speed and flexibility that today’s businesses require.
The goal for the developers of the ADO.NET architecture was to continue
the tradition of ADO by further removing the complexities of interacting with
different data providers and shielding you from the intricacies that would interfere
with your primary mission: packing functionality and usefulness into your
applications. After this chapter, you should feel comfortable with connecting,
viewing, and editing data using ADO.NET in the .NET Framework.
Introducing ADO.NET
To start with, let’s talk about the foundation.ADO.NET is based on XML, so
you have rich support for XML documents. Classic ADO had some support later
on for XML, but the format was difficult to use unless you were exchanging it
with another ADO client.The ADO.NET objects are consistent with the XML
specification and are well-defined. It is possible to take a plain XML document
with just a root node and open it in ADO.NET, add data to it, and save it back
out. Pretty handy for persistence.
The ADO Recordset is dead.ADO.NET has a couple of new ways to serve
data, which made the Recordset obsolete. In classic ADO, the Recordset object was a
http://www.syngress.com
http://www.syngress.com
representation of a database cursor.You could open a Recordset, navigate forwards
and backwards, change data, and leave it open. Leaving the Recordset open, however,
would result in wasting resources on the database server.This was undesirable.
In classic ADO, you could disconnect a Recordset and even save the Recordset
to disk; however, updating a disconnected Recordset was difficult.ADO.NET has
two options that work together to replace the Recordset: DataSet and the
DataReader.
You can think of the DataSet as an in-memory relational database. It has provisions
for multiple tables, relations within the DataSet, primary keys, views,
sorting—the list goes on. Classic ADO has no counterpart to the DataSet.The
DataSet is not connected to the data source and holds a copy of the data that is
put into it from the data source.You can populate a DataSet from any .NET
provider, and you can save the contents back to any .NET provider.
The DataSet requires a DataAdapter.The DataAdapter represents the connection
and commands to “fill” the DataSet. After the user is finished adding or
updating the DataSet, the Update method of the DataAdapter is called and the
changes are committed back to the data source.A couple of notes here; changes
are not required to be committed back to the original source, and you can
transfer data to another data source as long as the schema’s match.The other
thing to keep in mind, especially when developing for ASP.NET, is that this is a
disconnected copy of your data. It is suitable for a small subset of the data from
your data source. For ASP.NET, a possible use would be for a small amount of
data that needs to be retrieved more than once in the same page rendering, or
that will not change in the course of a user’s session. For example, consider a Web
application that has a drop-down list that contains the 50 states in the United
States of America. If more than one of these is used on a page, a DataSet could be
filled and every instance of the drop-down list is bound to this DataSet.This way,
a database connection is created and used once for all 50 states and the results can
be reused.
You can think of the DataReader as a fire hose recordset.A fire hose recordset
was a nickname given to a read-only, forward-only Recordset in classic ADO. So, a
DataReader is a forward-only, non-updateable stream of data from the data
provider. For ASP.NET work, this is the object that is most useful. Because Web
development is stateless, fast access to the data is more important than scrolling
and updating. Another noteworthy item is that the DataAdapter uses a DataReader
to populate a DataSet.
The next item we want to discuss is the idea of Managed Providers.A
Managed Provider is a namespace designed to connect to—and execute commands
ADO.NET • Chapter 8 385
386 Chapter 8 • ADO.NET
against—a data source.A good example is the SqlClient Managed Provider.This
namespace is written specifically to connect to Microsoft SQL Server.ADO.NET
can be separated into two fundamental architectures, the first being the data “containers,”
and the second being the Managed Providers. Data containers are the
DataSet, DataTable, and DataReader objects.The objects are data-source-agnostic in
that they do not contain any code specific to a data source.They do not really care
where the data comes from—they are generic in nature.
Managed Providers, on the other hand, are specific to a data source.
ADO.NET implements Managed Providers using different namespaces for the
different data providers. In classic ADO, the Provider Property dictated the data
source you were connecting to. For example,Microsoft Access would take a
provider of “Microsoft.Jet.OLEDB.4.0”. For SQL Server, it was “SQLOLEDB.1”.
So, for example, this code:
Dim oConn as ADODB.Connection
SET oConn = New ADODB.Connection
oConn.Provider=”SQLOLEDB.1″
becomes
SqlConnection oConn;
oConn = new SqlConnection(strConn);
More commonly, the Provider property was another parameter in the
Connection string.The Provider property is still used in the OleDb, and the Open
Database Connectivity (ODBC) namespaces, however, the SqlClient namespace
does not use a Provider property, and if the Provider property is left in the
Connection string for a SqlConnection object, the object will throw an exception.
Connection strings are discussed in detail later in the chapter.
ADO.NET Architecture
ADO.NET is the latest extension of the Universal Data Access technology. Its
architecture is similar to classic ADO in some respects, but a great departure in
others.ADO.NET is much simpler, less dependent on the data source, more flexible,
and the format of data is textual instead of binary.Textual formatted data is
more verbose than binary formatted data, and this makes it comparably larger.
The tradeoff is ease of transportation through disconnected networks, flexibility,
and speed.
http://www.syngress.com
ADO.NET • Chapter 8 387
Because data in ADO.NET is based on XML, Managed Providers are required
to serve data in a proper XML format. Once a developer has written data access
code, they only need to change a few parameters to connect to a different data
source.
ADO.NET is based on a connection-less principle that is designed to ease the
connection limitations that developers have traditionally had to deal with when
creating distributed solutions.You no longer need to maintain a connection, or
even worry about many of the connection options that developers had to deal
with in the past.
Because the ADO.NET classes inherit from the same core of data access
classes, switching data sources is much easier and less troublesome.Table 8.1
shows the core ADO.NET namespaces.
Table 8.1 ADO.NET Core Namespaces
Namespace Description
System.Data Makes up the core objects such as DataTable,
DataColumn, DataView, and Constraints. This
namespace forms the basis for the others.
System.Data.Common Defines generic objects shared by the different
data providers such as DataAdapter,
DataColumnMapping, and DataTableMapping.
This namespace is used by data providers and contains
collections useful for accessing data sources.
For the most part, you do not use this namespace
unless you are creating your own data provider.
System.Data.OleDb Defines objects that you use to connect to and
modify data in various data sources. It is written as
the generic data provider, and the implementation
provided by the .NET Framework in Beta2 contained
drivers for Microsoft SQL Server, the Microsoft OLE
DB Provider for Oracle, and Microsoft Provider for
Jet 4.0. This class is useful if your project connects to
many different data sources, but you want more
performance than the ODBC provider.
System.Data.SqlClient A data provider namespace created specifically for
Microsoft SQL Server version 7.0 and up. If you are
using Microsoft SQL Server, this namespace is
written to take advantage of the Microsoft SQL
Server API directly and provides better performance
than the more generic System.Data.OleDb namespace.
http://www.syngress.com
Continued
388 Chapter 8 • ADO.NET
System.Data.SqlTypes Provides classes for data types specific to Microsoft
SQL Server. These classes are designed specifically for
SQL Server and provide better performance. If you
do not use these specifically, the SQLClient objects
will do it for you, but may result in loss of precision
or type-conversion errors.
System.Data.Odbc This namespace is intended to work with all compliant
ODBC drivers. It is available as a separate
download from Microsoft.
The Command, Connection, DataReader, and DataAdapter are the core objects in
ADO.NET.They form the basis for all operations regarding data in .NET.These
objects are created from the System.Data.OleDb, System.Data.SqlClient, and the
System.Data.Odbc namespaces.
Understanding the Connection Object
Making a database connection in ADO.NET is really very simple.The most difficult
part of creating the connection is the Connection string.This is a semicolondelimited
string of name-value pairs. If you have worked with ODBC, or even
OLE-DB, they are basically the same with a twist for the SqlConnection object.
Because the only acceptable data source that the SqlConnection object can connect
to is Microsoft SQL Server, you do not need to specify a provider, it is understood
that SQL Server is the data provider.
It has become common to create what is referred to as the DAL, or Data
Access Layer.This implies a multitiered approach to application architecture, and
ADO.NET lends itself quite well for this purpose. Because the System.Data
namespace doesn’t really care about the data source or connection, the data container
objects such as the DataSet can be populated from any provider that can
understand how to connect between them and the data source. So, if a developer
has a page level DataSet, it can be populated from an OleDbDataReader object, or
the SqlDataReader object.The data source can be decided at runtime if the application
requires it.
Each Managed Provider implements a connection object which is specific to
the data sources it will connect to.The OleDb Managed Provider is specifically
written to connect to a data source that understand the OLE-DB protocols.The
same can be said for the ODBC, and SqlClient Managed Providers.
http://www.syngress.com
Table 8.1 Continued
Namespace Description
ADO.NET • Chapter 8 389
All of these Managed Providers are created specifically to interact with a particular
database API.Microsoft released the ODBC Managed Provider well after
the Beta 2 release of the .NET Framework.This demonstrates the extensibility of
the .NET Framework. For instance, you can create a Managed Provider specifically
for Oracle, or Exchange, and add them to the Framework.
Building the Connection String
The first step in creating a connection is the Connection string. Depending on the
namespace used, the Connection string will vary a little. Basically, the connection
string for a SqlConnection does not have the Provider attribute, and a Connection
string for ODBC must have the corresponding Data Source Name (DSN)
Registry entries.
http://www.syngress.com
Connection Pooling
Connection pooling for SqlConnections is handled in Windows 2000
Component services. Each connection pool is differentiated using a
unique connection string. The uniqueness of the connection string is verified
using an exact matching algorithm.
The SqlConnection is hosted in Windows 2000 Component services
to take advantage of the resource management that Component
Services provides. The .NET Framework SDK contains information on the
parameters that can be included in the connection string to modify the
default behavior of connection pooling for the SqlConnection object.
Connection pooling for the OleDbConnection object is handled
using OLE DB session pooling, which is handled by each individual OLE
DB provider if it supports connection pooling. Similar to SqlConnection
pooling, connection pooling with the OleDbConnection object is modified
with parameters in the connection string. These parameters are not
documented in the Framework SDK, because they are specific to the OLE
DB provider. Suffice to say that they are not the same as the
SqlConnection options. Therefore, the connection strings are not
portable across namespaces if they modify connection pooling.
Developing & Deploying…
390 Chapter 8 • ADO.NET
Connection to the SQL Server is done using the System.Data.SqlClient
namespace.This namespace contains the classes for the SqlConnection object. As
described above, the connection string is the hardest part of creating a connection.
This is not to say that Connection strings are hard to create, but rather that
connections in ADO.NET are not difficult to create.Table 8.2 lists some
common keys, and the default values with some simple explanations.
Table 8.2 Connection String Properties
Name Default Description
Connect Timeout 15 Seconds to try and make the con
-or- nection. When these are up, an
Connection Timeout exception is thrown.
Data Source <User Defined> The name or IP address of the SQL
-or- Server to make the connection with.
Server For servers with multiple instances
-or- of SQL Server, this would be
Address <servername>\<instancename>.
-or-
Addr
-or-
Network Address
Initial Catalog <User Defined> The name of the database. If you do
-or- not specify this, you will get a con-
Database nection to the default database
defined for the User ID.
Integrated Security ‘false’ Whether SQL Server will use the NT
-or- user credentials or expect a SQL
Trusted_Connection Server Username and password.
Password <User Defined> The password for the SQL Server
-or- account logging on. For integrated
Pwd security this is not specified.
Persist Security Info ‘false’ When set to ‘false’, security-sensitive
information, such as the password,
is not returned as part of the connection
if the connection is open or
has ever been in an open state.
Resetting the connection string
resets all connection string values
including the password.
User ID <User Defined> The SQL Server login account.
http://www.syngress.com
ADO.NET • Chapter 8 391
For example:
strConn = “Password=mypassword;User ID=admin;Initial
Catalog=northwind;Data Source=dbServer1”;
This connection string would work for a SqlConnection because it lacks the
Provider attribute. It would establish a connection to a Database named northwind,
on the server named dbServer1. It would then log in with a user name of admin,
using mypassword as a password.
A trick we have used in the past was to create a text file with .udl as the file
extension. Executing this file would start the Connection Wizard and allow you
to step through creating the connection string.When you are finished, open the
file in Notepad and copy the completed connection string. For a SqlConnection,
remove the Provider attribute.
Understanding the Command Object
The command objects, OleDbCommand, OdbcCommand, and SqlCommand allow
developers to execute statements directly against the database.They provide for a
simple and direct route to data, regardless of where the data resides.They can
have a collection of parameters that are used to pass variables in, and get variables
out. If a developer needs to get the return value of a stored procedure, the
Command object is the object they would use. Command objects are particularly
useful for executing INSERT, UPDATE, and DELETE statements, but they can
also generate DataReader and XMLDataReader objects for returning data:
string strSql = “SELECT * FROM Orders”;
string sConn = “Provider=SQLOLEDB.1;” +
“Password=password;” +
“Persist Security Info=True;” +
“User ID=sa;” +
“Initial Catalog=Northwind;” +
“Data Source=localhost”;
OleDbConnection myConnection = new OleDbConnection(sConn);
OleDbCommand myCmd = new OleDbCommand(strSql, myOleDbConnection);
Command objects are the only means available in ADO.NET to execute commands
against a data source.The Command objects are particularly suited for
calling stored procedures, which are the preferred method for relational data
access. Stored procedures allow some relational database management systems to
http://www.syngress.com
392 Chapter 8 • ADO.NET
precompile and take advantage of statistics that it has gathered on the source
tables.Take this stored procedure as a simple example:
CREATE PROCEDURE getShippers AS
Select *
From shippers
Order By CompanyName
This stored procedure just returns an ordered list of records from the shippers
table in the fictional Northwind database that installs with the .NET SDK.To
call this procedure, you can use a couple of different syntaxes.You can just specify
the name of the stored procedure instead of a SQL statement, or you can create a
command object explicitly.Take this as an example of replacing a SELECT statement
with the name of a stored procedure:
// strSql = “SELECT * FROM Shippers”;
strSql = “getShippers”;
objOleDbCommand = New OleDbCommand(strSql, myOleDbConnection);
Here, the line with the select statement in it is commented out, and the
stored procedure name is inserted. For a better example, let’s add an input parameter.
By adding a parameter to this stored procedure, you can now limit the rows
that the application uses and make it more efficient. For instance, say that you add
a parameter to the stored procedure that is used to find a shipper with a particular
ShipperID.To call it, just add the parameter in the order required by the
stored procedure. In this case, with one parameter, it would look like this:
strSql = “getShippersByID 2”;
This method is fine for instances when you are only trying to get some
records back from a stored procedure, but not very useful if you are trying to get
an output value or a return value. Here is where the parameter objects come into
play.To implement the example with a parameter, the code would look like this:
string strSP;
OleDbCommand objOleDbCmd;
OleDbParameter objParam;
OleDbConnection objConnection;
OleDbDataAdapter objAdapter;
DataSet myDataSet;
http://www.syngress.com
ADO.NET • Chapter 8 393
try
{
strSP = “getShippersByID”;
Get the new connection to the database. If you have a connection that is
available, you could use it instead of creating a new one:
objConnection = new OleDbConnection(sConn);
objConnection.Open();
Instantiate a new command object and specify the new connection you just
created. Set the type of command to stored procedure:
objOleDbCmd = new OleDbCommand(strSP, objConnection);
objOleDbCmd.CommandType = CommandType.StoredProcedure;
The line of code following this paragraph does several things. First, starting
from the inner parenthesis, it creates a new OleDbParameter with a data type of
unsigned integer and a size of 4.Then, it adds this new parameter to the
Parameters collection of the Command object that you just created. Finally, it puts a
reference to this newly created Parameter object in the variable objParam:
objParam = objOleDbCmd.Parameters.Add(New OleDbParameter(“@ID”, _
OleDbType.UnsignedInt, 4));
Here, you are setting the direction of the parameter and its value.The value is
easy enough to explain, but the direction is a little more complicated. For an
explanation of the different options you have for parameter direction, refer to
Table 8.3.
Table 8.3 Parameter Directions
Member Name Description
Input The parameter is an input parameter. This allows for data to
be passed into the command, but not out. You may have
more than one.
Output The parameter is an output parameter. It is used to return
variables, but you cannot use it to pass data into a command.
You must write the command specifically to populate
this variable as part of its routine. You may have more than
one.
http://www.syngress.com
Continued
394 Chapter 8 • ADO.NET
InputOutput The parameter is capable of both input and output. Use it
when you need to pass data into and out of a command in
one object. It is exactly what the name says it is: It performs
both the input and the output operations. You may have
more than one.
ReturnValue The parameter represents a return value. This is similar to the
output parameter, except that you can have only one.
objParam.Direction = ParameterDirection.Input;
objParam.Value = intShipperID;
This line of code sets the SelectCommand of the DataAdapter to the newly created
CommandObject objOleDbCmd.You have the option of specifying
SelectCommand, InsertCommand, DeleteCommand, and UpdateCommand:
objAdapter.SelectCommand = objOleDbCmd;
Here, you “fill” your DataSet by using the SelectCommand of the Adapter
object:
objAdapter.Fill(myDataSet);
Now, all that is left is to set the data source of our DataGrid and complete the
error handler:
DGorders.DataSource = myDataSet;
}
catch (Exception e)
{
MessageBox.Show(e.ToString);
}
finally
{
objConnection.Close();
}
This example demonstrated the use of an OleDbCommand object to populate
a DataSet.You passed the OleDbCommand object you created into the
http://www.syngress.com
Table 8.3 Continued
Member Name Description
ADO.NET • Chapter 8 395
SelectCommand property of the DataAdapter.When you called the Fill method,
ADO.NET used your OleDbCommand object to execute a DataReader and populate
your DataSet.
You had to create a Parameter object, and set its Direction to Input, then its
value. Note that in ADO you could make up your own names for the Parameter
objects that you created. In ADO.NET, you must ensure that your parameters are
named the same as they are in the definition of the stored procedure.ADO.NET
uses them to implement named parameters and it will throw an exception if it
doesn’t find a match. Of course, data types and sizes must also match.
To get an output parameter, you can modify your stored procedure to return
the current day of the server just as a demonstration of the output parameter.You
can easily turn this into an example of returning the ID of a newly created record:
objParam = objOleDbCmd.Parameters.Add(New OleDbParameter(“@CurrentDay”,_
OleDbType.Date, 8));
objParam.Direction = ParameterDirection.Output;
To access this value after the OleDbCommand.ExecuteNon Query method had
been called is simple:
dtServerDate = objSQLCmd.Parameters(“@CurrentDay”).Value;
Using the stored procedure in the SQL statement is simpler, but not as flexible,
as you can see here.You can also access the return value using a similar technique.
The only difference in using the return value is that you must declare a
parameter with the name of RETURN VALUE, and a direction of type return
value. After that, you access it just like any other output value.The return value
from a SQL Server stored procedure can only be a data type of Integer. If the previous
example were something like the number of days since an order date, you
could use the following lines of code to get it.The stored procedure might look
something like this:
CREATE PROCEDRUE GetDaysSinceLastOrder(@CustID nChar(5))
AS
DECLARE @iDays INT
Select @iDays = DATEDIFF(dd, Max(OrderDate), GETDATE())
From Orders
Where CustomerID = @CustID
Return @iDays
http://www.syngress.com
396 Chapter 8 • ADO.NET
The code to create the parameter and get the return value should look something
like this:
objParam = objOleDbCmd.Parameters.Add(New OleDbParameter(“RETURN VALUE”_
, OleDbType.Char, 5));
objParam.Direction = ParameterDirection.ReturnValue;
Play around with this object. It is probably going to be one of the most used
in your toolbox. Understanding how to use the output values and returning data
from them will be essential to your high performance development.
Understanding DataReaders
The DataReader is a read-only, forward scrolling data object that allows you to
gain access to rows in a streaming fashion.You’ll typically use it where you need
read-only access to data because it is much faster than using a DataSet.A DataSet
is populated behind the scenes using a DataReader, so if you don’t need the features
of a DataSet, you should not create one.A DataReader is created either from
the OleDb libraries, or from the SqlClient libraries.This is a simple example of
creating an OleDbDataReader from a Command object:
OleDbDataReader myReader = myCmd.ExecuteReader();
You now have a populated DataReader object that you can use like this:
while (myReader.Read())
{
// do some row-level data manipulation here
}
The DataReader object allows for much greater speed, especially if you need
to access a large amount of data. It does not allow you to update information, nor
does it allows you to store information like the DataSet object does, but it does
allow for very fast access to the data.
Understanding DataSets and DataAdapters
A DataSet is an in-memory copy of a portion of one or more databases.This may
be one table, or many tables. Imagine a small relational database residing in a variable.
This is a complete copy of the requested data. It is completely disconnected
from the original data source and doesn’t know anything about where the data
came from.You could populate the data from XML from your Microsoft BizTalk
Server, save it to Microsoft SQL Server, and then write it out to an XML file.
http://www.syngress.com
ADO.NET • Chapter 8 397
When you are finished with your operations, the entire DataSet is submitted
to the data source for processing. It takes care of standard data processing, such as
updating, deleting, and inserting records.The DataSet object is a key player in the
ADO.NET object model. Examine the object model in Figure 8.1 for the
DataSet object and the collections it can contain. Due to the architecture of
ADO.NET, several combinations of collections are possible.Take the Columns
collection as an example. As you can see, the DataTable object has a Columns collection
made up of DataColumn objects.The PrimaryKey property of the
DataTable contains a collection of DataColumns as well.This is the same
DataColumn object in the DataTables.Columns collection, but two different
instances of them.
http://www.syngress.com
Figure 8.1 DataSet Object Model and the Possible Collections It Can Contain
DataSet
Relations
Table Collection
DataTable
Rows
DataRelation
DefaultView
ChildRelations
ParentRelations
Constraints
Columns
DataColumn
DataRow
PrimaryKey
DefaultView
DataRelation
DataRelation
DataColumn
398 Chapter 8 • ADO.NET
DataTable
A DataSet contains a collection of DataTables.This collection is the key to the
DataSet’s versatility.They are tabularized representations of your data. Essentially
identical to the tables in your database, or other data source, they are added to
our DataSet just like you add objects to other collections. Once they are in your
DataSet, you can define properties, such as the DataRelations, Primarykeys, and so
on.You can create DataTables programmatically, or retrieve them from a database
through a SqlDataAdapter/OleDbDataAdapter object using the Fill method.
After you populate your DataSet with DataTable objects, you can access these
tables by using an index or the name you gave the table when you add it to the
DataSet.
The collection uses a zero-based index, so the first DataTable is at index 0:
ds.Tables[0];
The above mentioned method is more efficient, but harder to read, while the
one below is easier to read, but a little less efficient. How inefficient has yet to be
determined, but generally speaking your users won’t be able to tell, so unless you
have a compelling reason to use the index, this will be easier to maintain.
ds.Tables[“Orders”];
The Tables collection is the basis for DataSet operations. From the collection,
you can pull tables into separate DataTable variables and DataView objects.You
can also bind them to bindable controls on Windows Forms and Web Forms, or
act on them in the collection as in the previous examples.
DataColumn
A DataColumn is exactly what it sounds like: a column of data.The DataColumn
is the foundation of a DataTable and has very similar properties to a column in a
relational database table.A relational database table is often represented in a
spreadsheet-like format with rows and columns.The data in a DataTable is represented
in the same manner. So, a DataTable is made up of DataColumns and
DataRows.A DataTable contains a collection of DataColumns, and this could be
considered the DataTable’s schema, or structure.This representation contains no
data, but forms the basis or foundation to store and retrieve data.
DataColumns are .NET objects with properties and methods just like any other
.NET object. Remember that unlike the column in a classic ADO Recordset
object, a DataColumn is a true object, inheriting from the System.Object namespace.
http://www.syngress.com
ADO.NET • Chapter 8 399
This represents a huge shift forward in programming with data. In classic ADO,
data was stored in a proprietary format, which consisted of a string of variant
objects.These objects had all the overhead consistent with variants and resulted in
a flexible container for any type of data. It also meant that that ADO had to do a
lot of work behind the scenes sorting out data types and remembering the schema
of the data.
Because a DataColumn is a true object, it has a complement of properties and
methods that make interacting with it much more object-oriented in nature.
Refer to Table 8.4 for a listing and description of the properties of a DataColumn,
and Table 8.5 for the methods.
Table 8.4 DataColumn Properties
Property Name Description
AllowDBNull True or False, default is True. Determines whether the
column will allow Null values. Null values represent
the absence of a value and generally require special
handling.
AutoIncrement True or False, default is False. This indicates whether
the DataColumn will automatically increment a
counter. When this value is True, a numeric value will
be placed in this column. If the column is not of a
Int16, Int32, or Int64, it will be coerced to Int32. If the
DataTable is to be populated by an array, a Null must
be placed in the array position corresponding to the
AutoIncrement column in the DataTable.If an expression
is already present when this property is set, an
exception of type ArgumentException is thrown.
AutoIncrementSeed Default is 1. This is the starting value of the first row
in the column if the AutoIncrement property is set to
True.
AutoIncrementStep Default is 1. This is the value that the counter is incremented
by for each new row in the DataColumn is the
AutoIncrement property is True.
Caption Caption for the column. If a caption is not specified,
the ColumnName is returned.
ColumnMapping Determines the MappingType of the column, which
is used during the WriteXML method of the parent
DataSet.These are the MappingTypes and their
descriptions:
 Attribute XML attribute
http://www.syngress.com
Continued
400 Chapter 8 • ADO.NET
 Element XML element
 Hidden Internal structure
 SimpleContent XmlText node
ColumnName Name of the column in the DataColumnCollection. If a
ColumnName is not specified before the column is
added to the DataColumnCollection, the
DataColumnName is set to the default (Column1,
Column2, and so on).
Container Returns the container of the component (inherited
from MarshalByValueComponent).
DataType Sets, or returns, the type of data in the column. These
types are members of the System.Type class. Throws an
exception of type ArgumentException if data is present
in the DataColumn when the DataType is set.
DefaultValue Determines the default value for a new row.
DesignMode Returns a value indicating whether the component
is in design mode (inherited from
MarshalByValueComponent).
Expression Defines an expression used to filter rows or create an
aggregate column.
ExtendedProperties Returns a collection of custom user information.
MaxLength Defines the maximum length of a text column.
Namespace Defines or returns the namespace of the DataColumn.
Ordinal Returns the index or position of the column in the
DataColumnCollection collection.
Prefix Defines or returns an XML prefix used to alias the
namespace of the DataTable.
ReadOnly True or False, default is False. Indicates whether the
column allows changes once a row has been added to
the table.
Site Returns a reference to the parent. If Null reference or
nothing, the DataColumn does not reside in a container
(inherited from MarshalByValueComponent).
Table Returns a reference to the DataTable of which the
column belongs.
Unique True or False, default is false. Determines if the values
in each row of the column must be unique.
http://www.syngress.com
Table 8.4 Continued
Property Name Description
ADO.NET • Chapter 8 401
Table 8.5 DataColumn Methods
Method Names Description
Dispose Releases resources used by the component (inherited
from MarshalByValueComponent). Overloaded.
Equals Returns True if two instances of the Object are equal
(inherited from Object). Overloaded.
GetHashCode Hash function useful for hashing algorithms and data
structures similar to hash tables (inherited from Object).
GetService Returns the implementer of iServiceProvider interface
(inherited from MarshalByValueComponent).
GetType Returns the type of the current instance (inherited from
Object).
ToString Returns the existing column Expression. Overridden.
Because DataColumns are proper .NET objects, you can create a DataTable at
runtime, add DataColumns to the DataColumnCollection of the DataTable and populate
this programmatically, or by binding the DataTable to an object that supports
data binding, such as a DataGrid. Refer to Figure 8.2 for a simple example of creating
a DataTable and adding two DataColumns to the DataColumnCollection (you
can find the corresponding files on the CD that accompanies this book, in the
folders DataColumn\AutoIncrementExample).
Figure 8.2 Creating a Simple DataTable with Two DataColumns
(DataColumn\AutoIncrementExample)
private DataTable AddAutoIncrementColumn()
{
DataColumn myColumn = new DataColumn();
DataColumn myData = new DataColumn();
// Create an ID column
myColumn.DataType = System.Type.GetType(“System.Int32”);
myColumn.ColumnName = “PK_ID”;
myColumn.AutoIncrement = true;
myColumn.ReadOnly = true;
// Create a data column
myData.DataType = System.Type.GetType(“System.String”);
http://www.syngress.com
Continued
402 Chapter 8 • ADO.NET
myData.ColumnName = “strData”;
// Add the columns to a new DataTable.
DataTable myTable = new DataTable(“MyTable”);
myTable.Columns.Add(myColumn);
myTable.Columns.Add(myData);
// Return the new DataTable to the caller
return myTable;
}
This example demonstrated the creating of a DataTable and two DataColumns.
It also demonstrated setting some of the properties to make the table a little more
useful.
DataRow
The DataRow object actually represents a single row of data in a DataTable.The
DataRow is a fundamental part of a DataTable. DataRows are the objects that are
used to interrogate, insert, or delete data in a DataTable.A DataRow is not a part
of the DataTable definition or schema, but it represents the state of a DataTable.
DataRows contain not only data, but also error information for the row, versions
of the row, and of course, data.
As far as the DataTable is concerned, when you work with data you are
manipulating the DataRowCollection of a DataTable.You need to realize that a
DataTable contains a collection of DataRows.This becomes apparent when you
review the methods for a DataRow. In a database, for example, you execute an
INSERT statement to add rows to a table. Expecting an INSERT method of a
DataTable to add new rows would not be unrealistic; after all, the DataTable looks
and feels like a database table. Because the DataRow belongs in a collection, the
Add method is used to insert data.When data is retrieved, the Item property is
used to retrieve a specific column in the DataRow.You can place an entire row
into an array with a single method call.
For a listing of properties and methods, refer to Tables 8.6 and 8.7, respectively.
The DataSet object is a big reason the Recordset no longer exists in ADO.
http://www.syngress.com
Figure 8.2 Continued
ADO.NET • Chapter 8 403
Table 8.6 DataRow Properties
Property Name Description
HasErrors True or False, default is False. Indicates whether any
column in the row contains an error. Use GetColumnError
to return a single column in error, or GetColumnsInError
to return an array of columns in error.
Item An indexer for the DataRow class; sets or gets data in a
particular column. Overloaded.
ItemArray Allows all columns to be set or returned using an array.
RowError Sets or returns a custom error description for a DataRow.
RowState Used with the GetChanges and HasChanges method of
the dataset, the RowState depends on two things: the
changes that were made, and whether or not
AcceptChanges has been called.
 Added The DataRow has been added to a
DataRowCollection, and AcceptChanges has not
been called.
 Deleted The Delete method of the DataRow has
been called.
 Detached The DataRow is not part of a
DataRowCollection. A DataRow in this state may
have been removed from a DataRowCollection or
just created.
 Modified Data has been modified and AcceptChanges
has not been called.
 Unchanged Data has not changed since the last call
to AcceptChanges.
Table Returns a reference to the parent DataTable.
Table 8.7 DataRow Methods
Method Name Description
AcceptChanges Commits changes made to the DataRow since the last
time that AcceptChanges was called. When this method
is called, the EndEdit method is implicitly called. The
Current version of the data is discarded and the
Proposed version of the data becomes the new Current
version. If the RowState was deleted, the DataRow is
removed from the DataRowCollection. Calling the
AcceptChanges method does not update the data
http://www.syngress.com
Continued
404 Chapter 8 • ADO.NET
source; however, if the Update method of a
DataAdapter is called to update the data source, and
the AcceptChanges method of the DataRow or parent
DataTable has not been called, the changes are not
committed to the data source. The AcceptChanges
method of the DataTable calls the AcceptChanges
method for each DataRow in the DataRowCollection.
BeginEdit Puts the DataRow into edit mode and suspends data
validation events until the EndEdit method is called or
the AcceptChanges method is called. Begins the storing
of DataRow versions.
CancelEdit Cancels the edit mode of the current row and discards
the DataRow versions.
ClearErrors Clears the errors for the row, including the RowError
and errors set with SetColumnError.
Delete Sets the RowState to Deleted. The row is not removed
until the AcceptChanges method is called. Until the
AcceptChanges method is called, the row can be
“undeleted” by calling the RejectChanges method of
the DataRow.
EndEdit Ends the edit mode of the row, fires the
ValidationEvents, commits the Proposed data to the
Current data, and discards the versioned data.
Equals Returns True or False, determines whether two Object
instances are equal (inherited from Object). Overloaded.
GetChildRows Returns the DataRows that are related to the current
row using a DataRelation. Overloaded.
GetColumnError Returns the error description for a column. Overloaded.
GetColumnsInError Returns an array of columns that have errors.
GetHashCode Hash function useful for hashing algorithms and data
structures similar to hash tables (inherited from Object).
GetParentRow Returns the parent DataRow of the current DataRow
using the specified DataRelation. Overloaded.
GetParentRows Returns the parent DataRows of the current DataRow
using the specified DataRelation. Overloaded.
GetType Returns the Type of the current instance (inherited
from Object).
http://www.syngress.com
Table 8.7 Continued
Method Name Description
Continued
ADO.NET • Chapter 8 405
HasVersion Returns True if the specific version exists. Possible versions
are:
 Current DataRow contains current values.
 Default DataRow contains its default values.
 Original DataRow contains its original values.
 Proposed DataRow contains a proposed value.
IsNull Returns True if the specified column contains a Null value.
RejectChanges Rejects all changes made to the row since
AcceptChanges was last called.
SetColumnError Sets the error description for the current DataRow.
Overloaded.
SetParentRow Used in conjunction with a DataRelation to set the
parent DataRow for the current DataRow. Overloaded.
SetUnspecified Sets the value of a specified DataColumn to Unspecified.
ToString Returns a string that represents the current Object
(inherited from Object).
Looking at the Table 8.6 and Table 8.7, you can see how powerful the
DataRow object is and the possibilities it creates. For applications that need to
work with disconnected data, the DataRow makes these applications easy to
create, with some very powerful state management built in. Of course, when you
populate a DataTable from a DataSource,ADO.NET creates the DataColumns, and
then adds the DataRows to the DataRowCollection for you in one method call.
Differences between DataReader
Model and DataSet Model
Data in ADO.NET is disconnected for all practical purposes. Data access can be
broken down into two methods, or models.The DataSet model involves reading
the data into a local cache, interacting with it, and discarding, or synchronizing,
the data back to the source.The DataReader model does not allow for updating
data or reusing it.With a DataReader, data is read once and discarded when the
next row is read.
When you populate a DataSet from the database, a connection is opened, the
data is selected and returned into a DataTable, and then the connection is closed.
The data is present in the DataTable, and an application is free to interact with it
http://www.syngress.com
Table 8.7 Continued
Method Name Description
406 Chapter 8 • ADO.NET
in any manner, however, the database is free to do whatever it needs to do.
Resources are not being held on the database server while the application is
being used.
When a DataReader is used for data access, a connection is opened, and the
data is navigated using the Read method. It is not possible to “go back” and read
data that has previously been read, or rather it is not possible to scroll backward
in the data. Because a DataReader is forward-only and read-only, it is useful only
for retrieving the data and is very efficient.You need to realize that during the
scrolling process, resources are being held up on the server.This means that if an
application allows a user to manually navigate in a forward-only manner, the
database is serving the request and waiting.This may result in a resource problem
at the database. It is best to use the DataReader when fast access to the data is
needed, and the entire resultset is being consumed in a relatively short period of
time.This, of course, depends on several variables, such as number of users,
amount of data, hardware availability, and so on.
In both instances, the data is retrieved; however, with the DataSet it is persisted
in a DataTable. As stated earlier, a DataReader is used to populate a
DataTable, so in this regard if a developer needs to access the data once in a forward-
only mode, the DataReader provides a faster mechanism. On the other
hand, if this data is somewhat expensive to create, and it will be used repeatedly,
using a DataSet makes more sense.These are the types of decisions that you will
need to make during the course of designing the application.
The two models are similar in that they both provide data, but that is where
the similarities end.The DataReader provides a stream of data, whereas the
DataSet provides a rich object model with many methods and properties to
interact with the data in any scrolling direction an application would need.
Understanding the DataView Object
The DataView class is part of the System.Data namespace.The DataView’s main
purpose is to provide data binding to forms and controls. Additionally you can