Sunteți pe pagina 1din 16

Volume 15, Number 1 January 2005 www.elementkjournals.

com

Professional-grade solutions for Microsoft Visual Basic .NET & 6

Taking it to .NET

Introduce .NET applications with a custom splash screen


by Sheila M. Davis Application: Microsoft Visual Basic .NET n the early days of application development, splash screens gave the user something to look at while waiting for the main form to complete its initial setup tasks and load. These days, it only takes a few seconds for a form to load. However, splash screens, like the one shown in Figure A, are still useful because they offer the perfect medium to convey important information about your application to users. As a matter of fact, Microsoft still uses them to announce their applications, including Visual Studio .NET. And you can easily create one to announce your .NET application as well.

In this issue:
Taking it to .NET: Introduce .NET applications with a custom splash screen VB Classic: Deftly decipher binary les, part 1: Data formats in a MIDI header Key binary terms every developer should know Avoid duplicate coding: Place common classes in a .NET class library Maximize performance by executing SQL stored procedures with a DataReader

Making a splash in the world of .NET

Designing a splash form


To begin, launch Visual Studio .NET and create a new Windows Application. Next, select Project | Add New Item from the Visual Studio .NET main menu and add a new Windows Form to the project. Then, decide what type of information you want the form to announce. The application name, a company name, and the application version are all

We often refer to a splash screen as if its a special type of component. In actuality, its just a plain old form that you load before you load your primary form. In this article, well give you a few pointers on how to create and use a splash type form. First, well help you create a simple splash form. Then, well show you how to load it from another form in your project. Well also offer some tips to make your splash screen more dynamic and keep the information it contains up to date.

In Visual Basic 6, the Application Wizard can generate a splash screen for you, complete with all the proper labels for your application information. Of course, as you can see in Figure B on the next page, it isnt much to look at. Actually, you really dont need any help generating a splash screen. All you need are a few well-placed Label controls for the application information, maybe a PictureBox control for a company logo, and a Timer component to control the length of time the splash screen is visible.

Building a splash screen

Figure A: Attach a splash screen to your project

to provide users with important version information and give your application a more rened feel.

common choices. Well leave it up to you to come up with a dazzling design. For our purposes, just add a single Label control to the new form and set its Name property to lblVersion. Then, add a Timer component to the forms system tray.

eight seconds has passed, the Timer Tick() event res. To stop the Timer and close the form, place the next set of code in the Timers Tick() event:
Timer1.Enabled = False Me.Close()

Sharpening the form


To give the splash form a cleaner look, you should remove the title bar by setting the forms FormBorderStyle property to None. Next, if you plan to have the splash form load on top of the main form, set the forms StartPosition to CenterParent. This will center the splash form over the main form when it loads. Also, be sure to give the form an appropriate name, such as Splash or [companyname]Splash. This way you avoid confusion when youre ready to access the form. Note: To appease those users who loathe splash screens, you should also copy the Tick() event code to the forms KeyPress() event. This way the user can press a keyboard key to immediately abort the splash screen. Also, dont make your splash screen too ashy. You dont want to have to show a splash screen to cover dead space while youre loading your splash screen. Thats all there is to creating a basic splash screen. Now, youre ready to use it. To get things started, click on the default windows form and select View Code from the default menu.

Adding the code


The code behind the splash form is quite simple. All you need to do is identify how long you want the form to display, identify the version, and close the form when the desired amount of time has elapsed. To perform the rst two tasks, right-click on the default form and select View Code from the shortcut menu. Then, add the following code to the code window:
Public setTimer As Short = 8000 Public setVersion As String = 1.0.0 Private Sub Splash_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Timer1.Interval = setTimer lblVersion.Text = Version: & setVersion Timer1.Enabled = True End Sub

To launch a splash screen, you simply load it just as you would any other form. However, in .NET, loading a form isnt as simple as just coding splash.show. Instead, you have to create a new instance of the form and then show it, like so:
Dim objForm as new formname objForm.Show

Launching a splash screen

In our example, we use the splash screen for informational purposes only. So, we load the main form and then display the splash screen over it. To use your splash screen in this manner, add the following code to the code window after the Windows generated code:
Private doSplash As Boolean = True Private Sub Form1_Activated(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Activated If doSplash Then doSplash = False Dim frmSplash As New Splash frmSplash.ShowDialog(Me) End If End Sub

The rst line of code in the forms Load() event tells the Timer to click for eight seconds. The next line sets the version, and the last line starts the Timer. Once

Figure B: Visual Basic 6 saves you a little time by offering a default splash form, but you could design something much better.

There are a few things wed like to point out about this code. First, instead of using just the Show() method, we use the ShowDialog() method to keep the main form off-limits while the splash form is visible. Also, since we used the CenterParent property value in the StartPosition of the Splash form, we pass a reference for the current form to the splash form to iden-

Inside Microsoft Visual Basic

tify the current form as the parent. Finally, we used a Boolean variable to make sure the splash screen res only once. Note: If you do need a splash screen to ll time before your form loads, instead of using the forms Activated() event, add the code that creates and shows the form below the call to InitializeComponent() in the New() method. This loads the splash screen before loading the form. Also, be sure to use the plain Show() method and call Me.SendToBack() in the Activated event of the main form.

Figure C: Modify the AssemblyInfo.vb le to store an important


application.

If you were to press [F5] and launch the program, youd probably see your bare-bones splash screen followed by the empty default form. However, if you recall, we hard-coded the version number in the splash form. In Visual Basic 6, you can keep your splash screen data fresh using the App class. In Visual Basic .NET, you have the Application class.

Varying the splash screen data

Figure D: Pass

the application information to the splash screen to keep it fresh and up to date.

Using the Application class


Just like the App class, the Visual Basic .NET Application class contains information about the application, such as current version, company name, and application name. However, unlike Visual Basic 6, you cant set this information using the project properties. Although .NET keeps the version up to date for you, youll have to manually set the other values, such as Application.ProductName, in the AssemblyInfo.vb le, as weve done in Figure C.

Shaping up splash screens


For a truly unique splash screen, leverage the powers of GDI+ to create shaped forms with three sides, ve sides, or no sides at all. You can even mold your form into a string of text. To discover how, check out the August 2004 article Whip VB .NET forms into shape with GDI+ objects and methods.

Passing the data


Once you create a new instance of a form, you have full access to all public members of that form, including variables and controls. This means you can set the splash forms application information labels using the actual label names or the global public variables we assigned to the labels. For example, to set the version label, place the next line of code immediately above the showDialog call:
frmSplash.setVersion = Application.ProductVersion

shorten the amount of time the splash screen displays, use the setTimer variable to assign a smaller number of milliseconds to the Timer s Interval property.

Figure D shows our sample screen with the version number we obtained from .NET. If youd like to

In this article, weve shown you how easily you can create a splash screen for your .NET application. Although some users and developers frown on splash screens, theyre still an excellent way to introduce your application and keep the user aware of the current version and licensing information. Just keep the design simple and the display time short, and your splash screen is sure to make a great rst impression.

Splashy rst impressions

Visit The EKJ ForumsElement K Journals NEW bulletin boards


Go to www.elementkjournals.com/forums www.elementkjournals.com

Meet people, get advice, find resources, share ideas, and make friends! You and your fellow EKJ Forum members create the contentreal, current, and always changing. Moderated by our expert editors, there are many different EKJ Forums to choose from. Check them out today!

January 2005

VB Classic

Deftly decipher binary les, part 1: Data formats in a MIDI header


by Jonathan Rabson Application: Microsoft Visual Basic 6 Download: http://download.elementkjournals.com/vbasic/200501/midi.zip

hen your program needs to read or manipulate les from another program, its nice if the les are in text or a standard data format. But sometimes your code will have to make sense of a binary le. For instance, if your software replaces or supplements a legacy program that uses a proprietary data format and has no adequate import/export capability or API, you may wonder how youre going to get the data. Some people just throw up their hands and re-enter records by hand. Of course, if the le in question is in a standardized binary format, such as MIDI for music or JPEG for graphics, APIs and ready-made COM objects often exist to interpret these les for you. But even then, you may want the exibility to alter the le in ways the makers of the ready-made solutions didnt anticipate. But, while its easy to obtain documentation on the basics of working with binary les, its harder to nd rich, real-world examples written in Visual Basic. So, well present a case study for you involving the simplied Musical Instrument Digital Interface (MIDI) processor shown in Figure A to give you a realistic overview of whats involved.

In this article, well focus on some key techniques for working with binary formats. The main commands well use are Open, Close, Get, and Seek. While youll see the syntax of these commands in action through some simple examples, well focus on the process of working with binary les. Well start with a comparison between the simple table-structure les you might nd under Help and the more complex binary les you may need to deal with on the job.

A MIDI case study

To give you the avor of real-life binary formats, we created a bare-bones utility that reads simple les in the MIDI format. While our utility, which you can obtain by downloading the le midi.zip from the URL listed at the beginning of the article, simplies things quite a bit (i.e., it assumes theres one track, no instructions on what instrument to use, no pedaling, and none of what the MIDI standard calls system exclusive messages), it does demonstrate basic techniques. For instance, well use our example to show how to work with various forms of data, such as bytes, unsigned integers, and signed integers. Well also show how handy the And operator is for working with binary data. Although well only be reading les, the concepts we cover will be crucial if you want to go to the next step and add data to a le, repair corrupted les, or change les in ways not possible through existing software. Note: This article contains many binary terms that, if youve never worked with binary les or havent worked with them since your teenage years, may seem unfamiliar. For this reason, weve provided a binary cheat sheet of sorts in the article Key binary terms every developer should know, which youll nd in this months issue.

Binary les using table structures


Figure A: Knowing how to work with binary les in Visual
Basic enables you to write a utility that makes sense of the bytes in a MIDI music le. Inside Microsoft Visual Basic

The Visual Basic binary le examples youre liable to encounter generally make the assumption that the le is constructed of segments that share the same organization, much like rows (or records) in a database

table. For instance, a typical example may involve a le for storing sales items, containing a series of records that each store a sequence of elds, such as ID, item name, category number, and priceeach stored using a particular data type that Visual Basic supports (Integer, String, etc.). Given this scenario, you can create a user-dened type similar to the following:
Public Type SalesItems ID As Integer ItemName As String CatNumber As Integer Price As Currency End Type

A reasonable question is: Are the binary les you encounter in real life going to be like that? The answer is yes and no. Clearly, a table structure is an extraordinarily useful way of organizing binary les, and its a good place to start if you have to decipher an undocumented le format for storing tabular information. But it isnt the only possible approach. Binary les in real life may be more complicated. Often they have some sort of header identifying the le as a certain type, and they may have footer information. In addition, there may be a number of different suborganizations within the lesimilar to having many database tables instead of one. And since one of the

Typical complexities in real-world examples

You can then dimension a variable of that type and an array to hold instances of that variable:
Dim StoreItems As SalesItems Dim arrayItems() As SalesItems Dim lCount As Long

What denes the structure in a binary le? You!


If youre new to working with binary les, you may wonder whats inside the le that says when a eld or record ends, or what the data type is. The answer is, typically nothing. Certain data, such as a variablelength string, may do something special on the last byte to show when that piece of data ends; and some data may instruct your code on how to read other data. But typically, binary les dont reveal their structure the way comma-delimited and XML les do. After all, one of the main reasons for using binary is to pack information into a small space. So, the only thing that determines how to interpret the le is your code following whatever format youve devised, or whatever specication documentation youve gotten from a company or downloaded off the internet. In other words, if your code expects the next four bytes to constitute what Visual Basic calls a Long data type, thats what youll get. Of course, if youre one byte off so what youre reading is intended to represent something completely different, Visual Basic wont complain (as long as there are bytes to read). However, the data you get probably wont be very useful.

Doing so enables you to read the data in chunks, one record at a time, into your array, like so:
Open c:\store.texmex For Binary _ Access Read As #1 While LOF(1) > Loc(1) ReDim Preserve arrayItems(lCount) Get #1, , StoreItems arrayItems(lCount) = StoreItems lCount = lCount + 1 Wend Close #1

Table A: Header information in a MIDI le Number of bytes Information stored


4 4 2 2 2 Chunk type Length Format Number of tracks Division

What your code is supposed to interpret it as


Fixed-length string; should spell MThd in a valid le, which means that this chunk of data is a header Unsigned integer (usually 6), signifying how many bytes are in the header Unsigned integer Unsigned integer If leftmost bit is 0: Unsigned integer If leftmost bit is 1: Leftmost byte signed integer*, rightmost byte unsigned integer Skip over number of bytes as determined by the Length (row two in this table) minus 6

Variable

Unknown (provided for future expandability)

*Technically, only the seven bits to the right of the rst bit form the signed integer for what MIDI calls SMPTE format (Society of Motion Picture and Television Engineers). But, if the leftmost bit is 1, taking the next seven bits as a signed integer is mathematically equivalent to taking all eight. Similarly, if the leftmost bit is 0, taking the entire two bytes is the same as taking only the rightmost 15 bits.

www.elementkjournals.com

January 2005

main uses for binary les is to store information compactly, there may be certain rules whereby a eld may be skippedsort of like a database table with a different number of lled columns in each row. Finally, the data may not correspond to any data types that Visual Basic provides. In which case, youd need to interpret the data yourself from the bytes, following the documentation you have for the le structure.

One of your rst tasks for interpreting data from a binary le is to set up any code youll need to call repeatedly, such as the SalesItems type we created in the previous example. However, in many cases, your setup activity will be quite a bit different. For example, consider the MIDI specication. MIDI les start with a header, which follows the format specied in Table A on the previous page. A few things stand out in the table. First, some of the information is in the format of unsigned integers, which dont correspond to any data type in Visual Basic. Second, if youre writing a program to read such a le, there are conditional decisions your code has to make to determine how to proceed.

Organizations of data you may encounter

The most basic task youll need to complete is grabbing a byte of data. This way, if you need data that doesnt correspond to any Visual Basic data type, you can always construct what you need from the bytes. Listing A shows a ready-made function that allows you to grab a byte from any position in a le. This function takes two arguments: intF is the le number you used in the Open statement to open the le. lPos (included for exibility in case you want to skip to a byte) is the byte in the le to start at (e.g., 1 if youre reading the le from the beginning, 9 if youve read 8 bytes so far and want to read the next one). TakeAByte() uses the Seek() function to nd what the next byte is if you havent specied lPos.

Taking a byte

Next, well look at the concept of an unsigned integer. The distinction between signed and unsigned may not make a difference for the small numbers youll usually encounter in MIDI headers, but the concept is fundamentaland its something youll need to know to deal with the rest of a MIDI le or to work with other binary formats. Visual Basics Integer and Long types represent signed integers; that is, they can be positive or negative. To represent both positive and negative numbers, computers generally use the twos complement system, meaning simply that you count the largest bit negatively. For instance, consider a typical 16-bit signed integer. (Well consider a generic case, not a Visual Basic Integer , since theres a slight complication, which youll see soon.) The rightmost, or 0 th bit, represents 1 if in the on position. Any other bit n in the on position represents 2 n, with the exception of the leftmost (15th) bit, which represents not 2 15, but -2 15. So, 1,000,000,000,000,011 represents -32,765 (i.e., -32,768 + 2 + 1). In contrast, in a 16-bit unsigned integer, the leftmost bit represents 2 15meaning that 1,000,000,000,000,011 represents 32,771 (i.e., 32,768 + 2 + 1). The idea of inverting the largest bit (also known as the most significant bit, or MSB) to represent signed integers is a great way to simplify the algorithm the computer uses to add numbers. Unfortunately, it can pose problems if your file format specifies unsigned integers and youre trying to interpret these with signed data types. There are three valid strategies for dealing with unsigned integers in Visual Basic: Use deduction to determine that the data will look the same whether you interpret as signed or unsigned. Loop through the bytes to generate the number. Mathematically convert from a signed integer.

Reading unsigned integers

Simplifying through deduction


The rst approach may be reasonable for simple applications. For example, if you know from the data that your two bytes will only represent a positive integer less than 2 , or your four bytes represent an integer less than 2 , then there shouldnt be any difference between signed or unsigned. In that case, you might consider grabbing data using Microsofts native types, and later doing whatever mathematics are required to implement the specied logic for any of the data. Accordingly, you might consider extracting the information similarly to the way we did the SalesItems example, using the following user-dened type:
15 31

Listing A: Function to take a byte


Public Function TakeAByte(intF As Integer, _ Optional lPos As Long = 0) As Byte Dim bytByte As Byte If lPos = 0 Then lPos = Seek(intF) End If Get #intFile, , bytByte TakeAByte = bytByte End Function

Inside Microsoft Visual Basic

Type Header ChunkType Length Format NTracks Division End Type

As As As As As

String * 4 Long Integer Integer Integer

There are three big problems with this, however: If a le violates your assumptions, you may end up with a bug at the most inconvenient moment. Visual Basics Long and Integer types store their bytes with the MSB on the right (i.e., the bytes for an Integer 1 are 10, not 01); whereas, in MIDI, its on the leftanother complication. In our experience, reading and writing with a userdened type all at once wont give you the bytes in the order you expect. Reading les with individual variables (or individual members of a Type) gives you more control and predictability.

This function takes three arguments, including two similar to our TakeAByte() function, and an argument called intLen containing the number of bytes for representing the number. To understand how this function works, remember that each next byte (counting back from the right) represents eight places (bits) over from the last byte, so the value of each next bytes bits is 28 (i.e., 256) times the bits of the previous byte. Of course, since we get the data from left to right, we start with the highest numbers rst. We know the number of bytes from intLen, so we can simply populate the variable lCount to represent the appropriate exponent of 256, and loop back using Step - 1. Certainly, this simplied example is far from perfect because were still using Long, so we really only get the data for 3 7 8 bytes. A 4-byte unsigned integer can be as large as 4,294,967,295; whereas, ours can only be 2,147,483,647. To get around that, you may need to store the results as Currencyor as arrays of bytes, using your own algorithms for any calculations you need to do.

Counting byte by byte


An alternative approach you can take is to simply add up the bytes yourself. While this isnt the best approach performance-wise, it isnt bad because in practice were only looping up to four times. This is also the best way to illustrate whats happening, and its the most exible method, allowing you to read a number whether its 1, 2, 3, or 4 bytes. To use this approach, simply copy the function shown in Listing B.

Converting from signed to unsigned


Perhaps the most elegant solution for dealing with unsigned integers is to use mathematical calculations to convert data types where necessary. (We wont tackle reversing byte order purely via math here, but well discuss some conversions using simple arithmetic.) For instance, suppose you use an Integer to represent the number of tracks. To account for the possibility (albeit unlikely) of 32,768 or more MIDI tracks, you could convert the Tracks number to a Long using the function shown in Listing C. If the leftmost bit is 1 (representing 32,768), we add twice that amount (65,536) to ip it from negative to positive. We represent the number as a Long, since we know a Long has more than enough room to represent a 2-byte unsigned integer. Listing D on the next page is similar, but converts an unsigned integer to the corresponding signed integer that uses the same pattern of bits.

Listing B: Function to read unsigned integers byte by byte


Public Function _ ReadFixdLenPosNum(intF As Integer, _ intLen As Integer, _ Optional lPos As Long = 0) _ As Long Dim lCount As Long Dim bytByte As Byte Dim lResult As Long If lPos = 0 Then lPos = Seek(intF) End If lResult = 0 For lCount = intLen - 1 To 0 Step -1 Get #intF, lPos, bytByte lResult = lResult + (bytByte * 256 ^ lCount) lPos = Seek(intF) Next lCount ReadFixdLenPosNum = lResult End Function

Listing C: Function to convert a signed Integer to an unsigned Long


Public Function _ IntToUnsignedLong(intSigned as Integer) As Long If (intSigned And 32768) > 0 Then IntToUnsignedLong = intSigned + 65536 Else IntToUnsignedLong = intSigned End If End Function

www.elementkjournals.com

January 2005

One important technique in Listings C and D is the use of the And operator. If the variables x and y represent numbers, then the expression (x And y) returns the number youd form by taking all the bits that are the same in both x and y. This means that if you want to know if a number contains a certain bit (i.e., power of 2), you can use And , as shown in the listings. Another use of And is to extract data from a larger chunk that youve already collected. For example, consider the second to last row of Table A, which specifies what the MIDI standard calls the Division. Suppose youve extracted that information either by using the Integer data type and converting to unsigned using our IntToUnsignedLong(), or by calling our ReadFixdLenPosNum() function. In either case, you have more work to do, because the standard says youre supposed to

Using And to compare bytes and extract data from larger chunks

Listing D: Function to convert from unsigned to signed


Public Function ConvertPosToTwosComp(lNum As Long, _ Optional intBits As Integer = 16) As Long Dim lTwosComplement As Long Dim lMSBValue As Long lMSBValue = 2 ^ (intBits - 1) If (lNum And lMSBValue) > 0 Then lTwosComplement = (lNum - lMSBValue) ConvertPosToTwosComp = -lMSBValue + _ lTwosComplement Else ConvertPosToTwosComp = lNum End If End Function

read the leftmost bit and then interpret the rest of the data accordingly. Listing E demonstrates one way to do this. Simply feed your MIDI header values into an object youve dened with a class module, and use a Property Let to perform any remaining calculations. In our Property Let, we store the raw value to a module-level variable for future reference. Then we compare the value with 215 to see what the rst bit is. If the leftmost bit is 1, we set a property in the class to signal that the data is stored using the second of the two possibilities shown in Table A for Division. You can see from the table that the leftmost byte is supposed to be a signed integer and the rightmost an unsigned integer. Now heres the clincher: To get the rightmost (i.e., lowest value) byte, we simply take (lVal And 255). The 255 value is the sum of the bits in a byte (mathematically, 20 + 21 + 22 + +2n-1 = 2n - 1). So that gets us our second byte. We subtract to get the rst byte, which we convert back to twos complement notation. (We cant use CInt() to do that because were working with numbers in Visual Basic, not with memory directly.) Of course, we could probably make this more elegant so we arent converting to an unsigned integer and then back to a signed one, but the process here is more useful for demonstration purposes. (In most MIDI les, the leftmost bit is 0 anyway.) Warning: When you use And and other similar operators to compare or manipulate bits, make sure your expression results in the correct data type and has parentheses as needed. Consider the following incorrect versions of the If statement from Listing C:
If intSigned And 32768 Then If intSigned And 32768 > 0 Then

Listing E: Using And to extract data


Public Property Let Division(lVal As Long) lModDivision = lVal Dim lFirstByte as long If (lVal And (32768)) > 0 Then UsesSMPTEFormat = True TicksPerFrame = (lVal And 255) lFirstByte = lVal - TicksPerFrame SMPTECodes = ConvertPosToTwosComp( _ lFirstByte, 8) Else UsesSMPTEFormat = False TicksPerQuarterNote = lVal End If End Property

The rst always acts as if the expression is False, because the expression returns a number instead of a Boolean. Visual Basic reads the second as if it said this:
If intSigned And (32768 > 0) Then

While weve only begun the journey of reading a MIDI le, weve highlighted much of the conceptual territory youll need to work with binary les. No doubt, some les you work with will have more complex or unique ways of representing data. However, once youre able to extract bytes, write functions to interpret data from a le, and use math to convert data in any way you need, youll have little problem reading or editing just about any binary format.

Having taken the rst byte

Inside Microsoft Visual Basic

Key binary terms every developer should know


by Jonathan Rabson Binary le. A le not intended for interpretation as text, usually containing some bytes of funny-looking characters when viewed in a text editor. Binary les conserve space by making full use of every possible value a byte can represent, not just the values that correspond to readable characters. Binary number. A number in base two. You can convert to regular base 10 numbers by multiplying the digits by 2n, where n is the number of places from the right, starting at 0. Bit. The fundamental unit in computer storage, having an on and off position represented respectively as 1 and 0. You can interpret a sequence of bits as a binary number. Byte. A sequence of eight consecutive bits, capable of representing 256 different values; also, a data type in Visual Basic for storing a byte and a unit for measuring computer memory. Fixed-length. A word, group of words, or data type that always contains the same number of bits or bytes; any unused portion usually shows up as 0s. For instance, the Long data type always takes up four bytes in Visual Basic, so the number 1 has one bit set to 1 and 31 bits set to 0. Fixed-length data can waste space if some of the data requires less than the amount specied. Least signicant bit (LSB). The bit representing the smallest power of two (i.e., 20, which is 1) in a sequence of bitstypically the rightmost bit. Most signicant bit (MSB). The bit representing the largest power of two in a sequence of bits typically the leftmost bit. For instance, in a 4-bit sequence, the leftmost bit is the most signicant bit and represents 23, so the sequence 1,000 stands for the number 8. Signed. A number format where negative numbers are possible. Text le. A le whose bytes are intended for interpretation as readable characters. For instance, the bit sequence 01000001 is 64, which, in a text le, represents the letter A. Twos complement. A system for representing numbers in which the most signicant bit is counted negatively. It gets its name because you can make the number negative by ipping (i.e., complementing) each bit and adding 1. The part of the number other than the most signicant bit is also called the twos complement. Unsigned. A number format where only positive numbers are possible. Variable length. A word, group of words, or data type that can contain different amounts of memory in different situations. For instance, the String data type in Visual Basic is variable length, unless you specify a xed length. Using variable length data can save space, but it requires extra processing to read, since the code must interpret how long the data is.

Avoid duplicate coding: Place common classes in a .NET class library


by Matthew MacDonald Application: Microsoft Visual Basic .NET Download: http://download.elementkjournals.com/vbasic/200501/classlibrary.zip

ts no secret that applications often need to share functionality. For example, you might build a web page and a Windows application that both need to access the same database component to retrieve a product catalog. In the world of .NET, you solve this problem by building a shared class library. The class library, when built, becomes a separate DLL le that any other .NET application can use, regardless of its language. Best of all, there are absolutely no versioning or conguration headaches, thanks to .NETs newfound love of metadata.

www.elementkjournals.com

January 2005

In this article, youll see how you can use Visual Studio .NET to quickly build your own class library. Youll learn the rules you need to follow to build and deploy class libraries to the clients that use them. Best of all, youll nd that all of these steps are much simpler than they were with traditional Visual Basic 6 and COM programming.

A class act

Listing A: A class for encrypting data to a le


Imports System.Security.Cryptography Imports System.IO Public Class FileEncryptor Private Crypt As New RijndaelManaged() Public Sub New(ByVal password As String) (Code for initializing object omitted. End Sub Public Sub EncryptFile(ByVal filePath As _ String, ByVal data As String) Open a file for writing. Dim fs As New FileStream(filePath, _ FileMode.Create) Set up the encryption. Dim Transform As ICryptoTransform = _ Crypt.CreateEncryptor() Dim cs As New CryptoStream(fs, _ Transform, CryptoStreamMode.Write) Encrypt and write the data. Dim w As New StreamWriter(cs) w.Write(data) w.Flush() cs.FlushFinalBlock() w.Close() End Sub Public Function DecryptFile(ByVal filePath As _ String) As String Open the file for reading. Dim fs As New FileStream(filePath, _ FileMode.Open) Set up the decryption.. Dim Transform As ICryptoTransform = _ Crypt.CreateDecryptor() Dim cs As New CryptoStream(fs, _ Transform, CryptoStreamMode.Read) Read and decrypt the data into a string. Dim r As New StreamReader(cs) DecryptFile = r.ReadToEnd() r.Close() End Function End Class

Technically, a .NET class library, or component, is nothing more than a collection of one or more classes compiled together as a unit. For example, when you install .NET, you install just over a dozen Microsoft-authored assemblies that provide all the tools youll use for building Windows applications, web applications, connecting to a database, reading les, and so on. The most important thing to understand about a class library is that a user cant directly launch it. Instead, a class library groups together some related features that another .NET application uses. Class libraries have many benets. Most importantly, they allow you to create pluggable applications, where you can debug, enhance, replace, and modify components individually without having to rebuild the entire application. Theyre also a great way to reuse code in different applications.

Class libraries 101

Creating a basic class library

To create a new class library project in Visual Studio .NET, follow these steps: 1. Select File | New | Project from the menu, if you arent already at the New Project dialog box. 2. Under the Visual Basic group, choose Class Library. 3. Choose your directory and project name (just as you would with any other project), and click OK. By default, youll start off with one class file, although you can add as many classes or modules as you want. Inside your class library, you can put the same code you would use in a Windows or web application. The difference is that you wont interact with user interface at all. Instead, youll write functions that read les, retrieve data from databases, perform calculations that are specic to your business, and so on.

Accessing classes with shared methods


There are two basic ways you can design classes. You can use shared methods, in which case the user can call the method without needing to create an object. Heres an example of a class with a shared method:
Public Class AccountUtility Public Shared Sub Deposit( _ acctID As String, amount As Decimal) (Code omitted.) End Sub End Class

10

Inside Microsoft Visual Basic

The next line of code shows how the client might call this method. Notice that you use the name of the class, rather than creating a new object:
AccountUtility.Deposit(342-42, 34.99)

Accessing classes through an instance


You can also use normal instance methods to access a class. As you might expect, in this case your code needs to create an instance of the class before it can use it, like so:
Dim objAU as new AccountUtility

2. Right-click on References in the Solution Explorer, and choose Add Reference. Then, click the .NET tab. Dont bother searching through the list provided for you; it corresponds to all the assemblies in the GAC (Global Assembly Cache), not the private assemblies designed for internal use. 3. To add your reference, click the Browse button at the .NET tab, and hunt for the DLL le from your class library project. 4. Once youve chosen the right le, click OK to continue. Visual Studio .NET copies the DLL le to the bin directory of the current project. This is an important fact to remember, because any new changes in your class library project wont be available in your client project until you copy the newly compiled DLL le and overwrite the previous copy in the client project. Visual Studio .NET takes this step automatically if the assembly at the source location has been modied. Note: As a shortcut, add both the class library and the client project to the same solution. Then, when you add the reference, choose the Projects tab, and pick the class library project name from the list. This way, Visual Studio .NET ensures that the client always gets the most recent version of the class library every time you build the solution.

Thats the approach well adopt in the next example.

To understand class libraries, it helps to consider a simple component. Listing A shows portions of a sample class that consists of two instance methods. These methods allow you to encrypt data and store it in a le, or decrypt data and retrieve it from the le. Note: For a complete listing of our test program, navigate to the URL listed at the beginning of the article and download classlibrary.zip. This component is useful, because it combines two tasks: cryptography and le I/O. (In a full-edged application, you might take even more steps, such as validating the data.) Note that when you create an instance of the FileEncryptor class, you need to supply the password you want to use. If you dont use the same password to read data as you used to write it, youll receive scrambled information. This makes the FileEncryptor class a handy way to keep information safe from prying eyes! Once youve perfected your class library, you can build it. (Just right-click on your project in the Solution Explorer and choose Build.) Visual Studio .NET will compile all your code into a single DLL assembly le. However, before you can actually test this class library component, youll need to build an executable application, as we describe in the next section.

Building a simple class

The classes dened in your class library project are now automatically available in your current project, just as though you had dened them in that project. The only difference is that you need to use the namespace of your project library to access its classes. Heres what you need to access the EncryptLibrary class library:
Imports EncryptLibrary

Using the library classes

Any .NET application can use your class library, even if its written in another language. (This makes perfect sense, of course, as all the .NET libraries are written in C#, and theyre easily usable in Visual Basic .NET programs.) To create a client for your class library, follow these steps: 1. Open an existing project or create a new Windows Application.

Creating a patron for your library

Now, its easy to respond to a button click and encrypt some text to a le. Just take a look at the following code to encrypt some text:
Private Sub cmdEncrypt_Click( _ sender As Object, e As EventArgs) _ Handles cmdEncrypt.Click Dim Encryptor As New _ FileEncryptor(txtPassword.Text) Encryptor.EncryptFile( _ encrypt.bin, txtText.Text) End Sub

www.elementkjournals.com

January 2005

11

Decrypting text is just as easy. The code in Listing B simply displays the decrypted text in a message box. You can play with this example to get a better understanding of how class libraries work. Clearly, you could have added the encryption code directly to the client, but this separation gives much more exibility for dividing the problem (and reusing your hard work).

become easier as Microsoft streamlines the upgrade process and probably starts sneaking the .NET les into such applications as Microsoft Internet Explorer and Windows operating system upgrades.

When you decide to deploy your new client project, youll notice how dramatic the differences are between COM and .NET. In COM development, you would need to rst copy the custom component to the Windows system directory on the new computer, register it, and then use the client program.

Deploying your class library

Most programmers are familiar with a programming problem affectionately known as DLL Hell. This problem is experienced most acutely when you install a new software product that updates a shared component used by other applications with an incompatible version. The end result is that a seemingly innocent taskupdating a system componentbackres miserably, rendering one or more applications unusable.

Revisiting DLL Hell

No registration required
In .NET development, no registration is required. You can copy the class library DLL and client EXE les to any directory on the new computer, and theyll work automatically. The EXE le has all the information that .NET needs about dependencies. It will automatically locate the class library DLL, as long as its present in the same directory. (In fact, you can even replace the class library with a new version, and it will work seamlessly, provided you dont change the name of the methods youre using, or the number or type of parameters each method uses.)

Relief through private assemblies


DLL Hell has its roots in the early days of Windows, when disk space and memory space were at such a premium that it didnt make sense for different applications to use different copies of the same components. In todays world, where hard drives and RAM chips are plentiful, the opposite is true: Its generally much better to save developer time and troubleshooting headaches by giving each application its own version of a component. In .NET, a component thats only used by a single application is called a private assembly. We compiled the FileEncryptor class library into a private assembly. That means that if two applications need to use it, youll make two copies and put one in each directory. Usually, this approach wont pose a problem. However, in some cases, this method falls short.

But the .NET Framework is required


Of course, this simple deployment still isnt quite as easy now as it should be in a few years. The problem is that while your assemblies have all the metadata they need to identify themselves and their dependencies, theyll still only work on another computer with the .NET Framework. If you copy your application to a program that doesnt have .NET, it wont work. Fortunately, .NET is easy to install, if you dont mind a 23 MB download. In the future, the task should

Going to the GAC


If you want to control how versioning works, or you have an extremely large component that you dont want to copy multiple times, then you need to step beyond private assemblies and into the GAC (global assembly cache). The GAC is a new system-wide storage place for shared components. Its better than the Windows system directory, because it has the ability to store multiple versions of a single component. If youd like to learn more about the GAC, check out the June 2004 article Take advantage of the global assembly cache to avoid

Listing B: Code to display decrypted text using the class library


Private Sub cmdDecrypt_Click( _ sender As Object, e As EventArgs) _ Handles cmdDecrypt.Click Try Dim Encryptor As New _ FileEncryptor(txtPassword.Text) MessageBox.Show( _ Encryptor.DecryptFile(encrypt.bin)) Catch MessageBox.Show( _ File not found or invalid password.) End Try End Sub

Got .NET?
Any client that you want to share your class library with must have .NET installed. To complete this step, just use the Windows Update feature by following these steps: 1. Go to the Windows Update website. You can select Windows Update from the Start menu, or just point your browser to http: //windowsupdate.microsoft.com. 2. Scan for updates. 3. Select .NET Framework Version. 4. Install it.

12

Inside Microsoft Visual Basic

.NETs DLL hell, which shows you how to place the FileEncryptor class library in the GAC.

In this article, weve shown you how to take advantage of .NETs class library feature to catalog your reusable classes. When you use class libraries, you avoid duplicate coding and can take advantage of other .NET features, such as the GAC, which allow you to store all your classes in a nice and orderly fashion.

.NETs Dewey decimal system

Matthew MacDonald is an author, educator, and MCSD developer with a passion for emerging technologies. Hes a contributor to several books about .NET, including titles at OReilly and Apress. Email him at matthew@prosetech.com, or browse his website at www.prosetech.com. In addition, check out The Book of VB .NET from No Starch Press, which demysties .NET development for VB programmers.

Maximize performance by executing SQL stored procedures with a DataReader


by Sheila M. Davis Applications: Microsoft Visual Basic .NET, Microsoft ADO.NET Download: http://download.elementkjournals.com/vbasic/200501/datareader.zip

QL stored procedures have been around for quite some time. Developers routinely use them to retrieve data from a SQL database more securely and efciently. If youre using SQL stored procedures for the performance boost, then you should be mindful that you can lose part of those gains by choosing the wrong method to execute the stored procedure and retrieve the data in .NET. In most cases, an ADO.NET DataReader works just ne for these tasks and it carries less baggage than a DataSet, giving you a little extra performance boost. (Were using the generic ADO.NET terms here. Keep in mind that in code, youll precede these terms with a specic provider, like SQLDataReader.)

Both the DataReader and the DataSet are excellent tools for retrieving data from a stored procedure. However, if you only need to retrieve small amounts of data for the purpose of displaying it, all the overhead of the DataSet to support two-way communication between the database and your .NET program is unnecessary. With the .NET DataReader, you can retrieve a readonly, forward-only stream of data from a stored procedure and dump it out one row at a time, as weve done in Figure A. In this article, well provide a couple of examples on how to retrieve data from a SQL stored procedure using a SQLDataReader. First, well show you how to execute a parameterized stored procedure and return the data shown in Figure A. Then, well go a step further and use a DataReader to retrieve multiple result sets from a stored procedure.

Dont bring an Uzi to a duck hunt

Figure A: If you only need to display data, the

DataReader allows you to do so more efciently than the DataSet.


Set. You start with a Connection, pass it a ConnectionString, create a Command object, and open the Connection.

However, this is where the similarities stop. For the DataReader, you dont need to pass the command to a DataAdapter or call the Fill() method to populate a DataSet. You just need to call the ExecuteReader command. The following lines of code illustrate the process:
Dim cn As SqlConnection =New SqlConnection( _ connection info) Dim myCMD As SqlCommand = _

A DataReader primer

The rst few steps for creating a SQLDataReader are exactly the same as those youll take to create a Data-

www.elementkjournals.com

January 2005

13

New SqlCommand(sql query, cn) myCMD.CommandType =CommandType.Text Dim myReader As SqlDataReader = _ myCMD.ExecuteReader()

Retrieving the DataReader data


Once you create the SQLDataReader , youll treat it just like you would a Recordset in classic ADO. You repeatedly call the Read() method until the DataReader retrieves all the rows. A While loop, similar to the one in the next set of code, works wonderfully:
Do While myReader.Read() get data Loop

nal syntax we provided. First, youll replace the sql query in the Command with a stored procedure name. Then, youll change the CommandType to StoredProcedure, and nally to access the parameter youll create a Parameter object and supply the name of the parameter and its SQL data type, as the following code snippit illustrates:
Dim myCMD As SqlCommand =New SqlCommand( _ storedprocedurename, cn) myCMD.CommandType = CommandType.StoredProcedure Dim param As SqlParameter =myCMD.Parameters.Add( _ parametername, parametertype) param.Direction = ParameterDirection.Input

The DataReader returns False when there are no rows left to retrieve. To obtain the column information, call the DataReaders GetSring() or GetValue() methods inside the loop with a column index value, like so:
myReader.GetString(0)

Youll use the SqlDbType enumeration to select a valid parameter type. Also, set the Direction property to Input to show data going into the stored procedure.

Accessing a stored procedure with the DataReader


When accessing a stored procedure with a DataReader, you only need to make a few changes from the origi-

As you know, a parameterized stored procedure requires additional input before it can execute properly. Consider the following stored procedure against the SQL Server Northwind database:
Create procedure dbo.[n Most Expensive Products] @GetRowCount int AS SET ROWCOUNT @GetRowCount SELECT Products.ProductName AS nMostExpensiveProducts, Products.UnitPrice FROM Products ORDER BY Products.UnitPrice DESC GO

A parameterized stored procedure

Listing A: Code to load a DataReader from a parameterized


stored procedure
Dim cn As SqlConnection = New SqlConnection( _ server=svr1;uid=uid1;pwd=pw1;database=Northwind) Dim cmdExpensive As SqlCommand = _ New SqlCommand(n Most Expensive Products, cn) cmdExpensive.CommandType = CommandType.StoredProcedure Dim rowcount As SqlParameter = cmdExpensive. _ Parameters.Add(@GetRowcount, SqlDbType.Int) rowcount.Direction = ParameterDirection.Input If Len(Trim(TextBox1.Text)) = 0 Then TextBox1.Text = 6 Dim recs As Short = CLng(TextBox1.Text) rowcount.Value = recs cn.Open() Dim myReader As SqlDataReader = _ cmdExpensive.ExecuteReader( _ CommandBehavior.CloseConnection) Do While myReader.Read() Dim x As String = Space(50) x = x.Insert(0, Trim(myReader.GetString(0))) x = x.Insert(25, Trim(myReader.GetValue(1))) Debug.WriteLine(x) Loop

The name of this stored procedure is n Most Expensive Products. It retrieves the most expensive items from the Northwind products database. The parameter @GetRowCount controls how many records the query retrieves.

Weve just shown you a parameterized stored procedure and earlier we showed you how to load a DataReader using one. In Listing A, we put the two pieces together inside a Buttons Click() event to produce the debug data shown in Figure A. (If testing this code, be sure to create the stored procedure in SQL Server, add a TextBox control to the form, and import System.Data.SQLClient in your .NET code.)

Putting it all together

A tale of two result sets

Oftentimes, it may be necessary to return multiple result sets in a single stored procedure. For example, the stored procedure in Listing B named Ten Best and

14

Inside Microsoft Visual Basic

Listing B: Stored procedure that returns two result sets


CREATE PROCEDURE dbo.[Ten Best and Worst] AS SELECT TOP 10 productname AS [Product Name], SUM(quantity) AS [Units Ordered] FROM [order details] od, products pd WHERE od.productid = pd.productid GROUP BY od.productid, productname ORDER BY [Units Ordered] DESC; SELECT TOP 10 productname AS [Product Name], SUM(quantity) AS [Units Ordered] FROM [order details] od, products pd WHERE od.productid = pd.productid GROUP BY od.productid, productname ORDER BY [Units Ordered] GO

Listing C: Code to load the DataReader


Dim cn As SqlConnection = _ New SqlConnection(server=svr1;uid=uid1; & _ pwd=pw1;database=Northwind) Dim cmdBestWorst As SqlCommand = _ New SqlCommand(Ten Best and Worst, cn) cmdBestWorst.CommandType = _ CommandType.StoredProcedure cn.Open() Dim myReader As SqlDataReader = _ cmdBestWorst.ExecuteReader( _ CommandBehavior.CloseConnection) LoadListview(myReader, ListView1) myReader.NextResult() LoadListview(myReader, ListView2) myReader.Close()

Worst returns the top 10 best-selling items and the top 10 worst selling items for the Northwind OrderDetails table. Youre sure to see a performance difference between the DataSet and the DataReader in procedures like this one. The DataAdapter would create two DataTables and return both result sets at once. However, the DataReader allows you to retrieve the result sets one at a time using the NextResult() method, like so:
myReader.NextResult()

Windows Application and add two ListView controls and a Button control to the default form. Next, right-click on the form and select View Code from the shortcut menu. Add the following line to the top of the code window:
Imports System.Data.SqlClient

Then, copy the code from Listing C into your Button1_Click() event subroutine. Be sure to adjust the connection information to reect valid ones on your system. Note: At any time, you may navigate to the URL listed at the top of this article and download the sample code in the le datareader.zip.
Back Issues
To order a back issue, call Customer Relations at (800) 223-8720. You can pay with MasterCard, VISA, Discover, or American Express.

Retrieving the result sets


To test the DataReader with multiple result sets, save the Ten Best and Worst stored procedure to your SQL Server 2000 Northwind database. Then, create a new
Inside Microsoft Visual Basic (ISSN 1066-7555) is published monthly by Element K Journals, 500 Canal View Blvd., Rochester, N.Y., 14623.

Editorial
Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sheila M. Davis

Customer Relations
U.S. toll-free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Canada toll-free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Outside the U.S. and Canada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Customer Relations fax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (800) 223-8720 (877) 203-5248 (585) 240-7301 (585) 292-4392

For subscriptions, fulllment questions, and requests for group subscriptions, address your letters to Element K Journals Customer Relations 500 Canal View Blvd. Rochester, NY 14623 Or contact Customer Relations via our website at www.elementkjournals.com/contact.asp.

Managing Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mike D. Jones Copy Editors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Christine Hunt Carrie Weih Contributing Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Matthew MacDonald Jonathan Rabson Graphic Designer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Amy Palermo

Copyright
2005 Element K Journals, a division of Element K Press LLC (Element K). Element K and the Element K logo are trademarks of Element K LLC. This work is an independently produced publication of Element K Journals, the content of which is the property of Element K or its afliates or third-party licensors and which is protected by copyright law in the U.S. and elsewhere. The right to copy and publish the Content is reserved, even for Content made available for free such as sample articles, tips, and graphics, none of which may be copied in whole or in part or further distributed in any form or medium without the express written permission of Element K. Questions or requests for permission to copy or republish any content may be directed to contentreuse@elementk.com. Visual Basic is a trademark of Microsoft Corporation. IBM is a registered trademark of IBM Corporation. Windows is a registered trademark of Microsoft Corporation. All other product names or services identied throughout this journal are trademarks or registered trademarks of their respective companies. Printed in the U.S.A.

You may address tips, special requests and other correspondence to The Editor, Inside Microsoft Visual Basic Element K Journals 500 Canal View Blvd. Rochester, NY 14623 Editorial Department fax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (585) 292-4391 Or contact us via internet email at ivb_editor@elementk.com. Sorry, but due to the volume of mail we receive, we cant always promise a reply, although we do read every letter.

Element K Journals
Publisher. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Doreen Bieryla Associate Publisher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Michelle Rogers Manager of Customer Relations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nicole Pate Manager, Journals Production . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Danielle Rumsey Graphic Design Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chuck Willey Director of Marketing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lynne Dundas Product Marketing Manager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sean McPartland

Are you moving?


If youve moved recently or youre planning to move, you can guarantee uninterrupted service on your subscription by calling us at (800) 223-8720 and giving us your new address. Or you can fax us your label with the appropriate changes at (585) 292-4392. Our Customer Relations department is also available via our website at www.elementkjournals.com/contact.asp. To see a list of our products, visit our website at www.elementkjournals.com.

Price
Domestic. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . $147/yr. Outside the U.S.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . $157/yr. Our Canadian GST # is 88903 7958 RT0001. CPM # is 40031405. Our QST # is 1018491237.

Postmaster
Postmaster: Send address changes to Inside Microsoft Visual Basic P.O. Box 92880 Rochester, NY 14692

www.elementkjournals.com

January 2005

15

Coming up:
Exploring COM+ with Visual Basic 6 More on working with stored procedures in .NET

2035

Please include account number from label with any correspondence.

This code is pretty much the same as the previous code to load a DataReader, except theres no parameter in this version and we use the NextResult() method to obtain the second result set.

the column headings for the list views, we use the GetName() method of the DataReader. Add this code to your code window after the Button Click() event.

Loading data into a ListView


In the previous block of code, we called the LoadListView() subroutine to load each ListView control with its result set. This subroutine, shown in Listing D, uses the same While loop to iterate the DataReader. To obtain

A multi-result set display

Listing D: Code to load the ListView controls from the


DataReader
Sub LoadListview(ByVal rdr As SqlDataReader, _ ByVal lv As ListView) lv.Clear() set headers Dim lvwColumn As ColumnHeader lvwColumn = New ColumnHeader lvwColumn.Text = rdr.GetName(0) lv.Columns.Add(lvwColumn) lvwColumn = New ColumnHeader lvwColumn.Text = rdr.GetName(1) lv.Columns.Add(lvwColumn) lvwColumn = Nothing get data Do While rdr.Read() Dim lvwRow As New ListViewItem lvwRow.Text = rdr.GetString(0) lvwRow.SubItems.Add(rdr.GetValue(1)) lv.Items.Add(lvwRow) Loop show data lv.GridLines = True lv.View = View.Details End Sub

After youve added the stored procedure to your SQL Server database and added all the code, press [F5] to build and run the application. When you click the button, .NET executes the stored procedure, retrieves the data from each result set and loads the two ListView control. If all goes well, your form should resemble the one shown in Figure B.

Take a performance run

In this article, weve shown you how to obtain data from a SQL Server stored procedure using a DataReader. In general, stored procedures are designed to be more efcient than outside queries. The DataReader can also increase an applications performance because it returns the data one row at a time rather than big globs like the DataSet. Pairing these two structures together create a perfect performance match.

Figure B: With the DataReader, you retrieve data from multiple result sets one at a time.

16

Inside Microsoft Visual Basic

S-ar putea să vă placă și