Using IronPython with Mono

What Is Mono?

Mono (http://www.mono-project.com/) is a run time along the same lines as the .NET Framework, and it includes much of the functionality of the .NET Framework. In fact, with each release, Mono gets a bit closer to .NET Framework functionality. However, don’t get the idea that Mono will ever exactly match the .NET Framework. Platform differences, Microsoft copyrights, and other issues will always keep Mono just a bit different from the .NET Framework. Even so, Mono can run a considerable number of .NET applications. The following sections describe Mono, its advantages and limitations, in greater detail.

An Overview of the Mono Family

You can obtain Mono for a considerable number of platforms. In fact, the makers of Mono add new platforms with every release. At one time, Mono worked on just a few Linux implementations, Windows, and the Mac OS X. Over time, Mono support has increased to the exciting list of platforms that follows.

  • LiveCD: This is actually an openSUSE 11.2.1 (http://www.opensuse.org/en/) LiveCD (a CD or DVD that contains a bootable image — see http://en.wikipedia.org/wiki/ Live_CD for details) that includes Mono 2.6.1.
  • Mac OS X: You can use this installation on a number of Mac versions including Mac OS X Tiger (10.4), Leopard (10.5), and Snow Leopard (10.6) (it may work on other versions as well, but you’re on your own for support). The download includes Mono, Cocoa#, and Gtk# (GIMP Toolkit Sharp). You need to download the Client Software Development Kit (CSDK), available on the Mono site, separately. There are separate downloads for the Intel and PowerPC platforms. You can learn more about Mac OS X at http://www.apple.com/macosx/.
  • openSUSE: You can use this download for the openSUSE 11.0, 11.1, and 11.2 platforms. You must have your own system with openSUSE installed to use it. You can download openSUSE at http://software.opensuse.org/. Just in case you’re interested, the SUSE part of the name stands for Software und System-Entwicklung, which translates to software and systems development.
  • SLES/SLED: You can use this download for SUSE Linux Enterprise Server (SLES) or SUSE Linux Enterprise Desktop (SLED). SLES and SLED are the paid versions of SUSE from Novell. As with openSUSE, you must have your own system with SLES or SLED installed to use this version of Mono. You can find out more about SLES and SLED at http://www.novell.com/linux/.
  • Virtual PC: This is actually an openSUSE 11.2.1 virtual PC image that includes Mono 2.6.1. You could use this download to check out Linux functionality for your IronPython application on your PC without leaving Windows. Of course, performance won’t be very good, but it will get the job done.
  • VMware: This is actually an openSUSE 11.2.1 VMware image that includes Mono 2.6.1. You’d use it to check your application for Linux functionality without leaving the host operating system.
  • Windows: You can officially use this download for Windows 2000, XP, 2003, and Vista. Testing shows that it also works fine for Windows 7 and Windows Server 2008. The download includes Mono for Windows, Gtk# (a graphics library to display a user interface onscreen), and XSP (eXtensible Server Pages, an alternate Web server for serving ASP.NET pages). You can also get the Mono Migration Analyzer tool as a separate download.
  • Other: This is a group of less supported platforms including Debian and Ubuntu. At least these two platforms have supported packages. You can also get Mono in an unsupported form for Solaris, Nokia, and Maemo. Theoretically, you could support yet other platforms by compiling the source code found at http://ftp.novell.com/pub/mono/sources-stable/.

Of course, this list contains only a summary of the main Mono downloads. There are a large number of Mono add-ons a well. For example, you can obtain Mono Tools for Visual Studio (http:// go-mono.com/monotools/download/) if you want to work with Mono directly from Visual Studio. Unfortunately, the current version of this product only works with Visual Studio 2008. The developer should provide a Visual Studio 2010 version soon. You can obtain a trial version of Mono Tools for Visual Studio (registration is required), but you must pay for the full version.

IronPython does include support for Silverlight development. If you plan to use IronPython for Web applications and need to support multiple platforms, you might want to look at Moonlight (http:// mono-project.com/Moonlight) instead. This Silverlight replacement works on the same platforms that Mono does and should also work fine with IronPython.

Some of the extensions to Mono are well outside the scope of this book, but are interesting to contemplate. For example, you can get Mono Touch (http://monotouch.net/) to develop applications for the iPhone and iPod Touch devices. The point is that you can probably find some form of Mono to meet just about any need, but using Mono fully means learning some new techniques, such as creating user interfaces using Gtk#.

Considering the Reasons for Using Mono

You already know the reasons that you’re using the .NET Framework and this chapter isn’t about changing your mind. The .NET Framework is stable and many developers love the functionality it provides them for building great applications. However, you could think of Mono as another tool to extend the range of your applications. If for no other reason, the fact that you could run your IronPython application on Linux or the Mac OS X makes Mono a good choice for some forms of application development. In sum, the main reason for using Mono in place of the .NET Framework is flexibility.

As previously mentioned, Mono and the .NET Framework aren’t precisely the same. The first thought that most developers will have is that compatibility issues will be bad, and to a certain extent, they do cause problems. However, Mono also provides functionality that you won’t find when working with the .NET Framework. Features such as Gtk# actually make Mono a better product. In addition, with Mono you have a lightweight Web server for ASP.NET pages, XSP, that works on every Mono platform. Therefore, the differences between Mono and the .NET Framework aren’t always bad — sometimes they become downright useful.

Mono does provide direct support for IronPython, but you need to use a newer version of Mono (see http://www.mono-project.com/Python for details). The support isn’t all that good. The section “Running the Application from the Command Line” later in this chapter demonstrates the problem of using the Mono implementation of IronPython. Even so, you do get IronPython support that will likely improve as Mono improves, so this is an area where you can expect Mono to grow as an IronPython platform. In reality, the Mono community is quite excited about IronPython. You can find tutorials for using IronPython in a Mono environment at http://zetcode.com/tutorials/ ironpythontutorial/. If you want to see IronPython running under Mono on a Linux system, see the screenshot and description at http://www.ironpython.info/index.php/Mono.

Understanding Mono Limitations

Don’t get the idea that every .NET application will instantly run on Mono. For example, while Mono includes support for Language Integrated Query (LINQ), the support isn’t perfect. The LINQ to SQL support works fine for many applications, but not all of them. The Mono developers realize that the support isn’t complete and they plan to work on it (see the release notes at http://www.mono-project .com/Release_Notes_Mono_2.6.1 for details).

There are some obvious limitations for using Mono that should come to mind immediately. Because the purpose of Mono is to work across platforms, the P/Invoke calls in your extensions aren’t going to work. A P/Invoke call causes your extension to provide Windows-specific support, so using it on Linux wouldn’t work no matter what product you tried. The previous chapters in the book have emphasized when a particular technique is unlikely to produce useful cross-platform results.

The current version of Mono doesn’t work with .NET Framework 4.0 applications. The applications won’t start at all — you see an error message instead. However, Mono does work fine with older versions of the .NET Framework. It’s only a matter of time before Mono supports the .NET Framework 4.0, so this is a short-term limitation that you can easily overcome by using an older version of the .NET Framework when building your application. Given that IronPython doesn’t currently support the .NET Framework 4.0 in many respects, this particular problem isn’t much of an issue for IronPython developers.

In a few cases, you have to look around to determine whether you’ll encounter problems using Mono for a particular task. For example, if your ASP.NET application uses Web Parts, you can’t use Mono (see http://www.mono-project.com/ASP.NET). You also can’t use a precompiled updateable Web site.

Using Mono on Windows Server 2008 Server Core

Early versions of Windows Server 2008 Server Core (Server Core for short) don’t come with any form of the .NET Framework. Consequently, you can’t run any form of .NET application on early versions of Server Core unless you use Mono. The lack of .NET Framework support on Server Core led some people to come up with odd solutions to problems, such as running PowerShell (see the solution at http://dmitrysotnikov.wordpress.com/2008/05/15/powershell-on-server-core/).

Fortunately, Microsoft decided to provide a limited version of the .NET Framework for Windows Server 2008 Server Core Edition R2. You can read about it at http://technet.microsoft.com/ library/dd883268.aspx. However, this version of the .NET Framework still has significant limitations and you might actually find it better to use Mono for your .NET applications. For example, while you can now provide limited support for ASP.NET on Server Core, you might actually find the Mono alternative, XSP, to provide the solutions you need for your application.

Mono has generated quite a bit of interest from the Server Core crowd, especially anyone who uses Server Core as their main server. Server Core has a number of advantages that makes it popular with small- to medium-sized companies. It uses far less memory and other resources, runs faster, runs more reliably, and has a far smaller attack surface for those nefarious individuals who want to ruin your day by attacking your server. You can find a complete article about running applications on Server Core using Mono at http://www.devsource.com/c/a/Architecture/ Mixing-Server-Core-with-NET-Applications/.

Obtaining and Installing Mono

It’s time to obtain and install your copy of Mono. Of course, the first step is to download the product. You can find the various versions of Mono at http://www.go-mono.com/mono-downloads/ download.html. This section assumes you’re installing Mono version 2.6.1 on a Windows system. If you need to install Mono on another system, follow the instructions that the Mono Web site provides for those versions. After you complete the download, follow these steps to perform the installation.

  1. Double-click the mono-2.6.1-gtksharp-2.12.9-win32-1.exe file you downloaded from the Mono Web site. You see a Welcome page.
  2. Click Next. You see a License page.
  3. Read the licensing information. Select I Accept the Agreement, and then click Next. You see the Information page shown in Figure 19-1. Unlike most Information pages, this one actually contains a lot of useful information. Make sure you review the information it contains and click on the links it provides as needed. Especially important for keeping updated on Mono is joining the mailing list (http://www.mono-project.com/Mailing_Lists) or forums (http://www.go-mono.org/forums/). You can find these links at the bottom of the Information page.

    Make sure you review this Information page because it contains useful information.
    Figure 19-1: Make sure you review this Information page because it contains useful information.
  4. Read the release information and then click Next. You see the Select Destination Location page shown in Figure 19-2. Normally, you can accept the default installation location. Some developers prefer a less complex path to Mono, such as simply C:Mono, to make it easier to access from the command line. The chapter uses the default installation location.

    Select an installation location for Mono.
    Figure 19-2: Select an installation location for Mono.
  5. Provide an installation location for Mono and then click Next. You see the Select Components page shown in Figure 19-3. The components you select depend on what you plan to do with Mono — you can always change your setup later if necessary. If your only goal is to try Mono for your existing .NET applications and to create some simple IronPython applications, you really don’t need the Gtk# and XSP support. This chapter assumes that you perform a Compact Installation to obtain a minimum amount of support for working with the IronPython sample application.

    Choose the Mono components that you want to install.
    Figure 19-3: Choose the Mono components that you want to install.
  6. Select the components you want to install and then click Next. You see the Select Start Menu Folder page. This is where you choose a name for the folder that holds the Mono components. The default name normally works fine.
  7. Type a name for the Start menu folder (or simply accept the default) and then click Next. You see the Ready to Install page. This page provides a summary of the options that you’ve selected.
  8. Review the installation options and then click Install. You see the Installing page while the installer installs Mono on your machine. After a few minutes, you see a completion dialog box.
  9. Click Finish. You’re ready to begin using Mono.

Creating an IronPython Application with Mono

It’s time to begin working with Mono and IronPython to create an application. Of course, you’ll want to know a bit more about how Mono works before you just plunge into the project, so the first step is to look at Mono from a command line perspective. The first section that follows shows how to create an IPY environment variable and use it to open the IronPython console using Mono whenever you need it. The sections that follow show how to create a project, build a simple IronPython application, and then test the application in a number of ways.

Working at the Command Line

Mono works differently than the .NET Framework. When you want to use the .NET Framework to execute an application, you simply double-click the application and it starts. The same doesn’t hold true for Mono. If you want to execute an application using Mono, you must open the Mono command prompt and start it by specifically specifying Mono. Unfortunately, this limitation has an unusual effect on working with IronPython because you can no longer access IPY.EXE using the Path environment variable. Instead, you must create a special IPY environment variable using the following steps.

  1. Double-click the System applet in the Control Panel and choose the Advanced tab. You see the System Properties dialog box.
  2. Click Environment Variables. You see the Environment Variables dialog box.
  3. Click New in the System Variables section of the Environment Variables dialog box if you want to use IronPython from any account on the machine or the User Variables section if you want to use IronPython only from your personal account. You see a New System Variable or New User Variable dialog box. Except for the title, both dialog boxes are the same.
  4. Type IPY in the Variable Name field.
  5. Type C:Program FilesIronPython 2.6 or the location of your IronPython installation in the Variable Value field.
  6. Click OK three times to add the new environment variable, close the Environment Variables dialog box, and close the System Properties dialog box. You’re ready to begin working with IronPython.

At this point, you’re ready to begin working with Mono. Choose Start ➪ Programs ➪ Mono 2.6.1 for Windows ➪ Mono-2.6.1 Command Prompt to display a Mono command prompt. When you see the Mono command prompt, type Mono “%IPY%IPY.EXE” and press Enter. You’ll see the usual IronPython console.

The first thing you should notice is that the .NET Framework version reporting by the IronPython console is slightly different from the one you normally see. There isn’t any problem with this difference. In fact, it’s the only difference you’re going to notice as you work with the IronPython console. Let’s give it a try so you can see for yourself. Type the following code and you’ll see the standard responses shown in Figure 19-4.

[code]
import sys
for ThisPath in sys.path:
print ThisPath
[/code]

Running IronPython under Mono doesn’t really look any different.
Figure 19-4: Running IronPython under Mono doesn’t really look any different.

If you compare the results you see when running IronPython under the .NET Framework with the results you see when running IronPython under Mono, you won’t notice any differences. In fact, you can try out the applications in this book, and you won’t see any differences at all unless you need to work with an extension or other outside code source (and you might not even see any differences then). Working with Mono simply means you have access to more platforms when working with IronPython, not that you have more limitations.

Defining the Project

The project you create for working with Mono is going to be just a little different from the one you create when working strictly with the .NET Framework. You’ll still start up IronPython using the Visual Studio IDE, but there’s an extra step now: you must start Mono first.

  1. Choose File ➪ Open ➪ Project/Solution. You see the Open Project dialog box shown in Figure 19-5.
    Use Mono as the starting point for your project.
    Figure 19-5: Use Mono as the starting point for your project.

     

  2. Highlight Mono.EXE in the Program FilesMono-2.6.1bin folder of your machine (unless you used a different installation folder) and click Open. Visual Studio creates a solution based on Mono.
  3. Right-click the Mono entry in Solution Explorer and choose Properties from the context menu. You see the General tab of the Properties window shown in Figure 19-6.

    Set the Mono configuration for your project.
    Figure 19-6: Set the Mono configuration for your project.
  4. Type “C:Program FilesIronPython 2.6IPY.EXE“ -D TestMono.py in the Arguments field (change the folder location to match your IronPython installation).
  5. Click the ellipses in the Working Directory field to display the Browse for Folder dialog box. Locate the folder that contains the project you’re working on and click OK. The project folder appears in the Working Directory field of the Properties window.
  6. Choose File ➪ Save All. You see a Save File As dialog box.
  7. Type the solution name in the Object Name dialog box and click Save.
  8. Right-click the solution entry in Solution Explorer and choose Add ➪ New Item. You see the Add New Item dialog box.
  9. Highlight the Text File template. Type TestMono.py in the Name field and click Add. Visual Studio adds the Python file to your project and automatically opens it for you.

Creating the Code

It’s time to add some code to the IronPython file. This example provides a listing of the modules that IronPython is using. If you compare this list to the one that IronPython provides when you run the application using the .NET Framework, you’ll see the modules in a different order, but otherwise the output is the same. Listing 19-1 shows the code used for this example.

Listin g 19-1: Creating a simple Mono test program

[code]
# Obtain access to the sys module.
import sys
# Output a list of modules.
print ‘IronPython Module Listingn’
for ThisMod in sys.modules:
print ThisMod, sys.modules[ThisMod]
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

This example demonstrates a simple for loop to iterate through the list of modules found in the sys .modules dictionary. In this case, the code prints out two items. First, it prints out the module name. Second, it prints out the module information, which normally includes the module location. As always, the code ends with a pause, raw_input(), so that you can see the output before the window closes.

Running the Application from the IDE

Running the application is the first place you see some potential problems with using Mono. If you click Start Debugging, you see the No Debugging Information dialog box shown in Figure 19-7. If you click Yes, the program will run, but you won’t get any debugging support. This is one of the problems with using Mono exclusively. You’ll probably want to use the normal .NET Framework setup to debug your application first, and then move on to the Mono configuration described in this chapter to test the application under Mono.

Mono doesn’t provide any debugging support that Visual Studio understands.
Figure 19-7: Mono doesn’t provide any debugging support that Visual Studio understands.

To start the application successfully, choose Debug ➪ Start Without Debugging or press Ctrl+F5. The program will run normally and you’ll see the usual message at the end. Pressing Enter displays a second pause as shown in Figure 19-8. It seems that Mono provides its own pause so that you can see the results of executing the program, which is a nice touch for those times when you forget to add a pause of your own.

IronPython displays the list of modules found in the current setup.
Figure 19-8: IronPython displays the list of modules found in the current setup.

Running the Application from the Command Line

Interestingly enough, Mono does come with direct support for IronPython, but Mono supports IronPython 1.1, and the IronPython console supplied with Mono seems to do odd things. Open a Mono command prompt, type IPY, and press Enter. Now try typing 1+1 and pressing Enter. You’ll probably see results like those in Figure 19-9.

The IronPython console provided with Mono leaves a lot to be desired.
Figure 19-9: The IronPython console provided with Mono leaves a lot to be desired.

Of course, the question isn’t about the IronPython console, but whether it can run the example application. Press Ctrl+C to break out of the mess you’re seeing onscreen. Type Y and press Enter when you’re asked whether you want to stop the batch file. Then type IPY TestMono.py and press Enter. You’ll see that the application does indeed work, as shown in Figure 19-10. The number of modules is far smaller than the list shown in Figure 19-8, but it’s correct for the version of IronPython provided with Mono.

You can run the test application using the Mono version of IronPython.
Figure 19-10: You can run the test application using the Mono version of IronPython.

The picture isn’t completely gloomy. Developers are constantly trying new solutions for working with IronPython. You can find a potential fix for the problems described in this section of the chapter at http://ironpython-urls.blogspot.com/2009/06/mono-can-now-compile-ironpython-20 .html. The solution comes with the caveat that it might not work for you.

Interacting with Other .NET Languages under Mono

Mono originally focused its attention on C# development, but later added support for Visual Basic .NET as well. At this point, you can run any Visual Studio 2008–created application written under C# or Visual Basic.NET using Mono within the limitations described in the section “Understanding Mono Limitations” earlier in this chapter. Even DLR code appears to run fine in Mono within the current limits of the product, which aren’t many.

 

Extending IronPython Using Visual Basic.NET

Considering C# and VISUAL BASIC.NET Extension Similarities

Visual Basic.NET does have some distinct advantages over C# when building an extension. The most important of these distinctions is that Visual Basic.NET does more for you in the background. For example, Visual Basic.NET automatically creates a namespace for you — it isn’t something you have to think about. Visual Basic.NET also performs some type conversions automatically, so you don’t have to think about type conversions as much either. When you do need to perform a type conversion, you use the CType() function, which makes the kind of conversion a little more apparent.

You can easily use either C# or Visual Basic.NET to perform simple tasks. For example, either language works fine for creating a math library or for working with files. C# probably has an advantage in working with low-level extensions, especially those that interact with the Win32 API. On the other hand, the tendency of Visual Basic.NET to hide some of the gory details of programming works to your advantage when working with higher-level programming requirements, such as database access. Consequently, this chapter describes the requirements for creating a database extension.

Creating the Simple Visual Basic.NET Extension

The best place to begin learning how to create extensions is to create a very simple one. The sections that follow explore a simple Visual Basic.NET extension. This project creates a simple math library. In the process, it demonstrates some unique principles of creating extensions using Visual Basic.NET.

Creating the Project

A Visual Basic.NET extension project is nothing more than the typical class library. The following steps help you create the project for this example. You can use the same steps when working with the other examples — simply change the project name.

  1. Choose File ➪ New ➪ Project. You’ll see the New Project dialog box shown in Figure 17-1.

    Create a new project to hold your Visual Basic.NET extension.
    Figure 17-1: Create a new project to hold your Visual Basic.NET extension.
  2. Choose the Visual Basic folder in the Installed Templates list.
  3. Select .NET Framework 3.5 or an earlier version of the .NET Framework if you’re using Visual Studio 2010. Don’t select the .NET Framework 4.0 entry because IronPython won’t load extensions based on the .NET Framework 4.0. The list of templates changes when you change the .NET Framework version.
  4. Select the Class Library template.
  5. Check Create Directory for Solution if it isn’t already checked. When working with extensions, creating a solution directory provides a place for putting solution-level objects.
  6. Type Calcs in the Name field and click OK. Visual Studio creates a class library project for you.
  7. Right-click Class1.vb in Solution Explorer and choose Rename from the context menu. Visual Studio makes the filename editable.
  8. Type Calcs.VB for the new filename and press Enter. Visual Studio displays a dialog box that asks whether you’d like to rename all of the Class1.vb references to match the new filename.
  9. Click Yes. The project is ready for use.

Developing the Visual Basic.NET Extension

The Visual Basic.NET extension code for this example is relatively simple. Listing 17-1 shows the constructor, operator overrides, and methods used for this example.

Listin g 17-1: A simple calculations extension

[code]
Public Class Calcs
Private Data As Int32
Public Sub New(ByVal Value As Int32)
Me.Data = Value
End Sub
Public Overrides Function ToString() As String
Return Data.ToString()
End Function
Public Shared Operator +(ByVal Value1 As Calcs, _
ByVal Value2 As Calcs) As Calcs
Return New Calcs(Value1.Data + Value2.Data)
End Operator
Public Shared Operator -(ByVal Value1 As Calcs, _
ByVal Value2 As Calcs) As Calcs
Return New Calcs(Value1.Data – Value2.Data)
End Operator
Public Shared Operator *(ByVal Value1 As Calcs, _
ByVal Value2 As Calcs) As Calcs
Return New Calcs(Value1.Data * Value2.Data)
End Operator
Public Shared Operator /(ByVal Value1 As Calcs, _
ByVal Value2 As Calcs) As Calcs
Return New Calcs(Value1.Data / Value2.Data)
End Operator
Public Function Inc() As Calcs
Return New Calcs(Me.Data + 1)
End Function
Public Function Dec() As Calcs
Return New Calcs(Me.Data – 1)
End Function
End Class
[/code]

The code begins with a constructor that accepts an Int32 value as input. The example doesn’t include a default constructor because IronPython needs to assign a value to the object during the instantiation process. A default constructor would still need to assign a value to the private Data member, so it’s just better to assign a valid value to Data at the outset.

The ToString() override comes next. The default behavior for ToString() is to display the name of the class. You must override this behavior to display the value of Data. Notice that you must access Data as Me.Data — the copy of Data associated with this particular instance of the Calcs class.

The four Operator methods are defined as Shared, rather than Overrides. The Operator methods act as static class members so that you can use them naturally in IronPython. The input arguments for each method are the objects you create within IronPython. Consequently, there isn’t any concept of numeric type for Value1 or Value2 (you could theoretically use the same methods for any numeric value). The actual math operation occurs on the Data member of each object.

IronPython doesn’t support the ++ or — operators that are supported by Visual Basic for increment and decrement. Consequently, the class provides an Inc() and Dec() method. Notice that these methods aren’t defined as Shared because they work with a single object. You need to consider the differences between binary (those that work with two objects) and unary (those that work with a single object) operators when creating your extension. Binary operators are always declared as Shared, while unary operators appear as a standard method.

At this point, you can compile the class if desired. Start a copy of the IronPython console and type the following commands to load the extension.

[code]
import clr
clr.AddReferenceToFile(‘Calcs.DLL’)
import Calcs
dir(Calcs.Calcs)
[/code]

The dir() function shows the content of the Calcs extension as shown in Figure 17-2. Notice that Inc() and Dec() appear as you expect. However, there aren’t any entries for +, -, *, and / methods. These operators still work as you expect, but IronPython shows a Python equivalent for the operators in the form of __add__(), __radd__(), __sub__(), __rsub__(), __mul__(), __rmul__(), __div__ (), and __rdiv__(). These methods don’t appear unless you define the operators in your class.

If you’re looking at the class in the IronPython console, you might want to give it a quick try before you close up the console and move on to the next part of the example. Try this code and you’ll see an output of 15 from the __add__() method. Figure 17-2 shows the results of the calculation.

[code]
Value1 = Calcs.Calcs(10)
Value2 = Calcs.Calcs(5)
print Value1.__add__(Value2)
[/code]

The dir() function shows the content of the Calcs class.
Figure 17-2: The dir() function shows the content of the Calcs class.

Adding the IronPython Project

At this point, you have a Visual Basic.NET extension (or module) to use with IronPython. Of course, you’ll want to test it. The easiest way to do this is to add the IronPython project directly to the current solution. The following steps describe how to perform this task.

  1. Right-click the solution entry in Solution Explorer and choose Add ➪ Existing Project from the context menu. You’ll see the Add Existing Project dialog box shown in Figure 17-3.

    Locate IPY.EXE and add it to your solution.
    Figure 17-3: Locate IPY.EXE and add it to your solution.
  2. Locate IPY.EXE on your hard drive and highlight it. Click Open. You’ll see a new project entry added to the solution.
  3. Right-click the ipy entry in Solution Explorer and choose Set as Startup Project from the context menu. This step ensures that choosing one of the startup options from the Debug menu starts the IronPython application.
  4. Right-click the ipy entry in Solution Explorer and choose Properties from the context menu. You’ll see the General tab of the ipy Properties window shown in Figure 17-4.

    Configure the IronPython application to work with Calcs.DLL.
    Figure 17-4: Configure the IronPython application to work with Calcs.DLL.
  5. Type -D TestCalcs.py in the Arguments field.
  6. Click the ellipses in the Working Directory field to display the Browse for Folder dialog box. Locate the output folder of the Calcs.DLL (or other extension) file. Click OK. The IDE adds the correct directory information to the Working Directory field.
  7. Open Windows Explorer. Locate the CalcsCalcsbinDebug folder. Right-click in the right pane and choose New ➪ Text Document from the context menu. Name the file TestCalcs.py and press Enter. Click Yes if asked if you want to rename the file extension.
  8. Right-click the solution item in Solution Explorer and choose Add ➪ Existing Item from the context menu to display the Add Existing Item dialog box shown in Figure 17-5.
  9. Locate the TestCalcs.py file in the solution and click Add. Visual Studio adds TestCalcs.py to the Solution Items folder in Solution Explorer and automatically opens the file for you. You’re ready to add test code for the application.
Add the TestCalcs.py file to the solution.
Figure 17-5: Add the TestCalcs.py file to the solution.

Creating the IronPython Application

It’s time to write code to test Calcs.DLL. Listing 17-2 shows the code you’ll use for testing purposes.

Listin g 17-2: Testing the extension using IronPython

[code]
# Add a reference to the CLR
import clr
# Obtain access to the extension.
clr.AddReferenceToFile(‘Calcs.DLL’)
import Calcs
# Create an instance of the class and fill it with data.
Value1 = Calcs.Calcs(10)
# Print the original value, then decrement and increment it.
print ‘Original Value1 Content: ‘, Value1
print ‘Value1 + 1: ‘, Value1.Inc()
print ‘Value1 – 1: ‘, Value1.Dec()
# Create a second value and display it.
Value2 = Calcs.Calcs(5)
print ‘nOriginal Value2 Content: ‘, Value2
# Use the two values together in different ways.
print ‘nValue1 + Value2 = ‘, Value1 + Value2
print ‘Value1 – Value2 = ‘, Value1 – Value2
print ‘Value1 * Value2 = ‘, Value1 * Value2
print ‘Value1 / Value2 = ‘, Value1 / Value2
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing the Common Language Runtime (CLR). It then uses the AddReferenceToFile() method to create a reference to Calcs.DLL. The final step is to import the Calcs code.

Before the code can use the Calcs code, it must create an instance of it, Value1. Notice that the code calls the Calcs.Calcs() constructor with an initial value. Any time you want to assign a value to Value1, you must use the Calcs.Calcs() constructor. If you were to assign a value using Value1 = 15, it would change the type of Value1. A consequent addition, such as Value1 + Value2, would produce the following error:

[code]
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: unsupported operand type(s) for +: ‘int’ and ‘Calcs’
[/code]

One way to overcome this problem would be to override the = operator.

After the code creates Value1, it demonstrates the use of the Inc() and Dec() methods. These two methods simply add or remove 1 from the value of Value1. If you want to change the actual value of Value1, you need to make Value1 equal to the output of the method like this:

[code]
Value1 = Value1.Inc()
[/code]

The next step is to create Value2, a second Calcs object you can use for binary operations. The code outputs the initial value of Value2. The remainder of the example demonstrates the use of the various operators. As you can see, they work precisely as you would expect. You could even use them to create a third value like this:

[code]
Value3 = Value1 + Value2
[/code]

Figure 17-6 shows the output from this example. Except for the absence of the ++ and — operators, everything works much as you would expect.

Using Visual Basic.NET for User Interface Support

It’s certainly possible to create message boxes and even Windows Forms applications using IronPython. The biggest issue is that IronPython lacks support for the designers that make the task of writing Windows Forms code so easy. You have to be able to picture the form you want in your mind and then use trial and error to get it to appear in the application. Consequently, most developers will probably want to use a language such as Visual Basic.NET to create their Windows Forms applications and then make those forms accessible from IronPython as part of an extension.

Here are the results of using the Visual Basic.NET extension within IronPython.
Figure 17-6: Here are the results of using the Visual Basic.NET extension within IronPython.

The examples in the sections that follow aren’t all that complicated, but they do demonstrate the principles required to build your own library of message boxes and Windows Forms classes. By the time you finish these examples, you’ll have everything needed to create your own user interface library for use in IronPython.

Creating the User Interface Library Module

From an IronPython perspective, user interface elements come in two forms: messages boxes and Windows Forms. Obviously, Visual Basic.NET can create a host of user interface presentations, but if you start at this basic level, you’ll find the task of creating a user interface library module easier. The following sections describe how to create both a message box class and a Windows Forms class that you place in a single DLL for use with your IronPython application. Of course, a production DLL could have hundreds of different forms, depending on the user interface requirements for the application.

Defining Simple Message Boxes

Message boxes (created using the MessageBox class) are extremely useful for displaying short messages and getting canned responses. Depending on the buttons you provide, a user could tell you that the application should retry an operation or answer yes to simple questions. If you need a little more input, you can always rely on an input box (created with the InputBox() method of the Interaction class). Of course, an input box is still limited to a single field, but even so, it does extend the kinds of input you can receive from the user.

Listing 17-3 demonstrates both the MessageBox.Show() and InputBox() methods. In addition, you’ll see how to implement the __doc__() method that most IronPython developers rely upon to obtain information about your extension.

Listin g 17-3: Working with simple message boxes

[code]
Imports System.Windows.Forms
Public Class Dialogs
Public Function ShowMessage(ByVal Msg As String) As String
Return MessageBox.Show(Msg).ToString()
End Function
Public Function ShowMessage(ByVal Msg As String, _
ByVal Title As String) As String
Return MessageBox.Show(Msg, Title).ToString()
End Function
Public Function ShowMessage(ByVal Msg As String, ByVal Title As String, _
ByVal Buttons As Int16) As String
Return MessageBox.Show(Msg, Title, CType(Buttons, MessageBoxButtons) _
).ToString()
End Function
Public Function ShowMessage(ByVal Msg As String, ByVal Title As String, _
ByVal Buttons As Int16, ByVal Icon As Int16 _
) As String
Return MessageBox.Show(Msg, Title, CType(Buttons, MessageBoxButtons), _
CType(Icon, MessageBoxIcon)).ToString()
End Function
Public Function ShowMessage(ByVal Msg As String, ByVal Title As String, _
ByVal Buttons As Int16, ByVal Icon As Int16, _
ByVal DefaultButton As Int16) As String
Return MessageBox.Show(Msg, Title, CType(Buttons, MessageBoxButtons), _
CType(Icon, MessageBoxIcon), _
CType(DefaultButton, MessageBoxDefaultButton) _
).ToString()
End Function
Public Function GetInput(ByVal Msg As String, ByVal Title As String)
Return InputBox(Msg, Title, “Type a value”)
End Function
Public Function __doc__() As String
Return “This is a help string”
End Function
End Class
[/code]

Before you can compile this code, you need to add a reference to System.Windows .Forms.DLL. Right-click Dialogs in Solution Explorer and choose Add Reference from the context menu. You’ll see the Add Reference dialog box shown in Figure 17-7. Highlight the System.Windows.Forms entry and click OK. At this point, you also need to add an Imports System.Windows.Forms entry to your project and you’re ready to work with message boxes.

Add the System.Windows.Forms.DLL entry to your project.
Figure 17-7: Add the System.Windows.Forms.DLL entry to your project.

The code begins by creating a series of ShowMessage() methods. The first is relatively simple and the complexity increases with each ShowMessage() method entry. Notice that the ShowMessage() method uses Int16 input values to select the buttons, icon, and default button. You could also use enumerations to provide input values. The one thing you don’t want to do is ask the IronPython developer to provide a MessageBoxButtons, MessageBoxIcon, or MessageBoxDefaultButton value, because then the IronPython developer would need to import all the required .NET Framework functionality, reducing the usefulness of your extension. The CType() function helps you convert the Int16 values into the appropriate enumeration value. Interestingly enough, there are 21 forms of the MessageBox .Show() method, even though the example shows only five of them.

The GetInput() method shows just one of several InputBox() method variations you can use. In this case, the IronPython developer supplies the prompt (or message) and title to display onscreen. The GetInput() method supplies a default InputBox() value. Normally, you want to supply a value so that the user knows to type something and what you want the user to type. Even if the required input seems obvious to you, many users won’t know what to provide.

The __doc__() provides a help string for the IronPython developer. The example shows something quick, but in reality, you’d provide complete documentation for your class. The output string can use all the standard formatting characters. You could even read the content in from an external source, such as a file, to make it easy to provide updates without having to recompile the extension. Using an external file would also allow the IronPython developer to personalize the content.

Defining Complex Forms

A Windows Forms class can contain anything you want. It can even call other forms as needed. In fact, anything you can do with a Visual Basic.NET Windows Forms application is doable with IronPython. Of course, you do need to maintain interaction with the IronPython application. The following steps describe how to create a simple Windows Forms class for your extension.

  1. Right-click Dialogs in Solution Explorer and choose Add ➪ New Item. Select the Windows Forms entry in the Installed Templates list. You see the Add New Item dialog box shown in Figure 17-8.

    Add a Windows Form to your project.
    Figure 17-8: Add a Windows Form to your project.
  2. Highlight the Windows Form entry. Type TestForm.VB in the Name field and click Add. Visual Studio adds the new form to your project and automatically opens it for editing.
  3. Create the form just as you normally would for any static application. Figure 17-9 shows the form used for this example. It’s simple, but it contains multiple data entry fields and multiple exit options.

The form shown in Figure 17-9 is a little deceptive. Before you assume anything about this form, it does have a few differences from the forms you’ve created for your static applications.

The Windows Form can contain any level of complexity you desire.
Figure 17-9: The Windows Form can contain any level of complexity you desire.
  • Buttons that close the form, rather than do something within the form, must have the DialogResult property set to a unique value or you won’t be able to tell which button the user clicked. For this example, the DialogResult for btnOK is OK, while the DialogResult for btnCancel is Cancel.
  • Getting information from the form you create to the IronPython application can prove problematic. You could contrive all sorts of odd methods for accomplishing the task, but the simplest method is to set the Modifiers property for the individual controls (txtName and txtColor) to Public. In this case, using Public doesn’t create a problem because IronPython sets everything to public. In all other respects, there’s no difference between this form and any other form you’ve created in the past.

To make things simple, this example doesn’t use any code-behind for the form itself. Any codebehind works as you’d expect. There isn’t any difference between calling the form from IronPython than calling it from within your Visual Basic.NET application.

Accessing the User Interface Library Module from IronPython

It’s time to use the extension you’ve created with an IronPython application. The following sections describe an alternative way to set up your project so that you don’t have to create the IronPython file using Windows Explorer and show how to use the extension.

An Alternative Method for Adding the IronPython Project

There are a number of ways to configure a test setup for your extensions. The “Adding the IronPython Project” section shows one technique. The technique shown in that section works well when you want to maintain separate builds of your extension. For example, you might want to maintain separate debug and release builds.

Unfortunately, that earlier method is a bit clumsy — you have to create the IronPython file using Windows Explorer. The technique in this section avoids that problem. In addition, this technique shows how to maintain just one build — the build you’re currently using for debugging, testing, or experimentation. Use the following steps to create a centralized test configuration:

  1. Right-click Dialogs in Solution Explorer and choose Properties from the context menu. Select the Compile tab. You’ll see the Properties window shown in Figure 17-10.

    Configure the build to use a central output location.
    Figure 17-10: Configure the build to use a central output location.
  2. Click Browse next to the Build Output Path field to display the Select Output Path dialog box shown in Figure 17-11. Because you’ll add the IronPython test file at the solution level, you need to send the output to the solution level as well.
  3. Select the first Dialogs entry in the list and click OK. Visual Studio adds an absolute path to the Output Path field that you must change for every machine that uses the application. As an alternative, you could type .. (two periods and a backslash) in the field to place the output in the solution folder.
  4. Select the next configuration in the Configuration field.
  5. Perform Steps 2 through 4 for each configuration. Make sure each configuration uses the same output directory. Normally, your project will contain only Debug and Release configurations.
  6. Right-click the solution entry in Solution Explorer and choose Add ➪ Existing Project from the context menu. You’ll see the Add Existing Project dialog box shown in Figure 17-3.
  7. Locate IPY.EXE on your hard drive and highlight it. Click Open. You’ll see a new project entry added to the solution.

     Modify the output path as required for your application.
    Figure 17-11: Modify the output path as required for your application.
  8. Right-click the ipy entry in Solution Explorer and choose Set as Startup Project from the context menu.
  9. Right-click the ipy entry in Solution Explorer and choose Properties from the context menu. You’ll see the General tab of the ipy Properties window shown in Figure 17-4.
  10. Type -D DialogTest.py in the Arguments field.
  11. Click the ellipses in the Working Directory field to display the Browse for Folder dialog box. Locate the solution folder for the project (the first Dialogs folder). Click OK. The IDE adds the correct directory information to the Working Directory field.
  12. Right-click the solution entry in Solution Explorer and choose Add ➪ New Item from the context menu. You see the Add New Item dialog box shown in Figure 17-12.

    Add the IronPython test file to your project.
    Figure 17-12: Add the IronPython test file to your project.
  13. Type DialogTest.py in the Name field and click Add. Visual Studio adds the new file to the Solution Items folder in Solution Explorer and opens the file automatically for editing.

Performing the Message Box and Form Tests

The example is ready except for the test code. Listing 17-4 shows the IronPython code you need for this example.

Listin g 17-4: Testing the message boxes and forms

[code]
# Define the message box tests.
def TestMessages():
# Create a message box object.
MyDialog = Dialogs.Dialogs()
# Show the help information.
print ‘Dialogs Class Help Information.’
print MyDialog.__doc__()
# Test a simple message box.
print ‘nTesting a simple message box.’
print ‘Simple message box output: ‘,
print MyDialog.ShowMessage(‘Hello’)
# Perform a more complex test.
print ‘nA more complex message box.’
print ‘Complex message box output: ‘,
print MyDialog.ShowMessage(‘Hello Again’, ‘Title 2’, 3, 64, 256)
# Get some user input.
print ‘nUsing an InputBox.’
print ‘InputBox Output: ‘,
print MyDialog.GetInput(‘Type Your Name:’, ‘User Name Entry’)
# Define the form test.
def TestForm():
# Create the form instance.
MyForm = Dialogs.TestForm()
# Display the form and test the dialog result.
print ‘nThe form example.’
if MyForm.ShowDialog().ToString() == ‘OK’:
# Display the results.
print ‘The user clicked OK.’
print ‘User Name: ‘, MyForm.txtName.Text
print ‘Favorite Color: ‘, MyForm.txtColor.Text
# Display an alternate result.
else:
print ‘The user clicked cancel.’
# Import the Common Language Runtime.
import clr
# Access the extension.
clr.AddReferenceToFile(‘Dialogs.DLL’)
import Dialogs
# Test the message box code.
TestMessages()
# Test the form code.
TestForm()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing CLR support and then uses the AddReferenceToFile() to add a reference to the Dialogs.DLL. The next step is to import the Dialogs namespace for use. The __main__() function calls two functions, TestMessages() and TestForm(), to test the content of the Dialogs namespace. It then pauses so you can see the results.

The TestMessages() function begins by creating an instance of Dialogs.Dialogs, MyDialog. It then calls the MyDialog.__doc__() method to output the help information provided by the Dialogs class. Normally you’d use this method at the interactive console, but it’s good to see how the method works.

The next step is to test the MyDialog.ShowMessage() method. To keep you from clicking all afternoon, the test code uses just two forms of the method. The first form shows the simplest dialog box, while the second shows the most complex. The most complex dialog box (shown in Figure 17-13) contains a message, title, icon, and three buttons. Notice that the second button, rather than the first button, is selected by default. Normally, a message box selects the first button by default.

The complex message box can convey quite a bit of information for such a simple call.
Figure 17-13: The complex message box can convey quite a bit of information for such a simple call.

The next step is to display an input box. In this case, the MyDialog.GetInput() method displays an input box that contains a simple prompt and a title, as shown in Figure 17-14. Notice the default message in the input box. The input box automatically highlights this default entry so that the first thing the user types will erase the default content. The output from the MyDialog .GetInput() method is the text that the user types in the input box.

 Input boxes are good for small amounts of custom user input.
Figure 17-14: Input boxes are good for small amounts of custom user input.

The TestForm() function begins by creating an instance of the Dialogs.TestForm class, MyForm. The code then displays the dialog box shown in Figure 17-9 using the MyForm.ShowDialog() method. Notice that the example code adds a call to ToString(), so that the entire method call is MyForm.ShowDialog().ToString(). This is a technique for converting the System.Windows .Forms.DialogResult to a simple string that you can compare with the desired output, which is ‘OK‘ in this case.

When the call succeeds (the user clicks OK), the code prints the user’s name and favorite color. Notice that the code directly accesses both txtName.Text and txtColor.Text to obtain the required information. When the call fails (the user clicks Cancel), the code outputs a simple failure message. Figure 17-15 shows typical output from this example.

The IronPython output shows the results of the various dialog and form selections.
Figure 17-15: The IronPython output shows the results of the various dialog and form selections.

Using Visual Basic.NET for Database Support

Visual Basic.NET makes database management easy. Of course, there are all the handy designers that Visual Basic.NET makes available. The features of Server Explorer help as well. However, the fact that Visual Basic.NET tends to hide some of the details is what helps the most. The following sections provide a simple database management example that you could easily expand to help IronPython work with all sorts of data.

Obtaining and Configuring the Database

This example relies on an old standby, the Northwind database. Microsoft has passed this database by for significantly more complex examples, but Northwind remains unsurpassed in its ability to create useful examples with very little code, so it’s the database of choice for this chapter. You can download the Northwind database from http://www.microsoft.com/downloads/details .aspx?FamilyID=06616212-0356-46A0-8DA2-EEBC53A68034.

Make sure you have a database manager installed on your system. The Northwind database works just fine with versions of SQL Server as old as SQL Server 2000, but you should at least try a newer version, even if it’s SQL Server 2008 Express. The following steps tell you how to install the Northwind database.

  1. Double-click the SQL2000SampleDb.msi. You’ll see the normal Welcome dialog box for installing Microsoft products. Click Next. You’ll see the licensing agreement.
  2. Click I Agree after reading the license agreement, and then click Next. You’ll see an Installation Options dialog box. There aren’t any actual installation options.
  3. Click Next. You’ll see a Confirm Installation dialog box.
  4. Click Next. The installer installs the files into the C:SQL Server 2000 Sample Databases folder on your machine (you aren’t given a choice about the installation folder). After the installation is complete, you’ll see an Installation Complete dialog box.
  5. Click Close. The Northwind database and its associated script are now loaded on your machine.
  6. Open a command prompt in the C:SQL Server 2000 Sample Databases folder.
  7. Type OSQL -E -i InstNwnd.SQL and press Enter (the command line switches are case sensitive — make sure you type the command correctly). The OSQL utility will start building and installing the Northwind database. This process can take a while to complete — get a cup of coffee and enjoy. When the process is complete, you see a command prompt with a bunch of numbers on it and no error message, as shown in Figure 17-16.
The output from the OSQL utility doesn’t tell you much except if it encountered errors.
Figure 17-16: The output from the OSQL utility doesn’t tell you much except if it encountered errors.

Creating the Database Support Module

Creating a database support module is a multi-step process. At a minimum, you must first create a connection to the database and then work with that connection using code. The example that follows isn’t very complex. All that this example will do is retrieve some information from the database in the interest of keeping things simple. Even so, the basics shown in the example provide enough information for you to start creating database extensions of your own.

Creating a Connection to the Database

The first step in working with the Northwind database is to create a connection to it. The following steps describe how to perform this task.

  1. Right-click on the Data Connections entry in Server Explorer and choose Add Connection from the context menu. You may see the Choose Data Source dialog box shown in Figure 17-17. If not, you’ll see the Add Connection dialog box shown in Figure 17-18 and will need to proceed to Step 3.
  2. Highlight the Microsoft SQL Server entry. Select the .NET Framework Data Provider for SQL Server entry in the Data Provider field. Click Continue. You’ll see the Add Connection dialog box shown in Figure 17-18.

    Select the SQL Server data source to make the Northwind connection.
    Figure 17-17: Select the SQL Server data source to make the Northwind connection.
  3. Select or type the server name in the Server Name field. You can type a period (.) for the default server. The Add Connection dialog box automatically enables the Select or Enter a Database Name field.
  4. Select the Northwind database in the Select or Enter a Database Name field.
  5. Click Test Connection. You see a success message box (click OK to dismiss it).
  6. Click OK. Visual Studio displays the new connection in Server Explorer, as shown in Figure 17-19.
    The Add Connection dialog box lets you create and test a connection to the Northwind database.
    Figure 17-18: The Add Connection dialog box lets you create and test a connection to the Northwind database.

    The new connection appears in Server Explorer where you can work with it directly.
    Figure 17-19: The new connection appears in Server Explorer where you can work with it directly.
  7. Choose Data ➪ Add New Data Source. You’ll see the Data Source Configuration Wizard dialog box shown in Figure 17-20.

    Use the Data Source Configuration Wizard to create a coded connection.
    Figure 17-20: Use the Data Source Configuration Wizard to create a coded connection.
  8. Highlight Database and click Next. You’ll see the Choose Database Model page.
  9. Highlight the Dataset option and click Next. You’ll see the Choose Your Data Connection page. Notice that the Northwind database connection already appears in the connection field. The connection name will have your machine name, followed by the database name, followed by .dbo, such as main.Northwind.dbo. If it doesn’t, make sure you select it from the list. If the connection doesn’t appear in the list, click Cancel and start over with Step 1 because your connection wasn’t successful.
  10. Select the Northwind connection and click Next. The wizard will ask how you want to save the connection. There isn’t a good reason to change the default name provided.
  11. Click Next. You see the Choose Your Database Objects page shown in Figure 17-21.
  12. Check the Customers table entry, as shown in Figure 17-21. The example relies on the Customers table and none of the other database content. Click Finish. The new data source appears in the Data Sources window, as shown in Figure 17-22. If you can’t see this window, choose Data ➪ Show Data Sources.

Adding Database Manipulation Code

After all the work you performed to obtain access to the data, the actual database manipulation code is relatively easy. Listing 17-5 shows the small amount of code used to actually retrieve a particular record from the database based on the CustomerID field. Of course, you can add any level of complexity required.

Select the Customers table for this example.
Figure 17-21: Select the Customers table for this example.
The data source is ready to use in the example extension.
Figure 17-22: The data source is ready to use in the example extension.

Listin g 17-5: Retrieving data from the database

[code]
Public Function GetData(ByVal Customer As String) As _
NorthwindDataSet.CustomersRow
‘ Obtain access to the table.
Dim MyData As NorthwindDataSetTableAdapters.CustomersTableAdapter = _
New NorthwindDataSetTableAdapters.CustomersTableAdapter()
‘ Create a DataSet.
Dim DS As NorthwindDataSet.CustomersDataTable = _
New NorthwindDataSet.CustomersDataTable()
‘ Fill the DataSet with data.
MyData.Fill(DS)
‘ Find a particular record using the Customer ID.
Return DS.FindByCustomerID(Customer)
End Function
[/code]

The code begins by creating a TableAdapter object. Because the example relies on the Data Source Configuration Wizard, it has a specific TableAdapter to use in the form of the NorthwindDataSetTableAdapters.CustomersTableAdapter, MyData object. MyData provides the means to select information from the table. In addition, it can update, delete, and insert records. Essentially, MyData is the database connection.

The next step is to create a DataTable object. Again, the example has a specific version, NorthwindDataSet.CustomersDataTable class, DS object. DS contains all the data selected from the database through the TableAdapter object.

In order to get data from the database into the DataTable object, the code calls the MyData.Fill() method. Until the code calls this method, DS contains all of the information about the Customers table, but none of the records.

Finally, the code calls the DS.FindByCustomerID() method to find the record requested by the caller. The input argument to this method, Customer, is a string that contains the CustomerID field value. The output from the call is a NorthwindDataSet.CustomersRow object, which is a specialized form of the DataRow. Interestingly enough, IronPython can use the DataRow directly without having to translate it in any way.

Accessing the Database Module through IronPython

The example extension has a method, GetData(), that accepts a CustomerID as input and provides a NorthwindDataSet.CustomersRow as output. All you need now is some IronPython code to make the request and display the result. Listing 17-6 shows a typical example.

Listin g 17-6: Displaying a record onscreen

[code]
# Import the Common Language Runtime.
import clr
# Access the extension.
clr.AddReferenceToFile(‘Northwind.DLL’)
import Northwind
# Create an instance of the Northwind access object.
MyData = Northwind.DBAccess()
# Fill a row with data.
Row = MyData.GetData(‘ALFKI’)
# Display the data on screen.
print ‘All the data for Customer ID ALFKI’
print ‘nCustomer ID: ‘, Row.CustomerID
print ‘Company Name: ‘, Row.CompanyName
print ‘Contact Name: ‘,
if Row.IsContactNameNull():
print ‘Nothing’
else:
print Row.ContactName
print ‘Contact Title: ‘,
if Row.IsContactTitleNull():
print ‘Nothing’
else:
print Row.ContactTitle
print ‘Address: ‘,
if Row.IsAddressNull():
print ‘Nothing’
else:
print Row.Address
print ‘City: ‘,
if Row.IsCityNull():
print ‘Nothing’
else:
print Row.City
print ‘Region: ‘,
if Row.Is_RegionNull():
print ‘Nothing’
else:
print Row._Region
print ‘Postal Code: ‘,
if Row.IsPostalCodeNull():
print ‘Nothing’
else:
print Row.PostalCode
print ‘Country: ‘,
if Row.IsCountryNull():
print ‘Nothing’
else:
print Row.Country
print ‘Phone: ‘,
if Row.IsPhoneNull():
print ‘Nothing’
else:
print Row.Phone
print ‘Fax: ‘,
if Row.IsFaxNull():
print ‘Nothing’
else:
print Row.Fax
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

This listing looks like a lot of code, but the process is relatively simple. The example begins as usual by gaining access to CLR, using the AddReferenceToFile() method to create a reference to the extension, and creating an instance of the extension class.

At this point, the code calls MyData.GetData() with a CustomerID of ‘ALFKI‘. The output is placed in Row. If you use the dir() function on Row, you see it provides a lot more than a listing of fields that appear as part of the output. Figure 17-23 shows the attributes Row provides.

The output fields come in two types. The first are fields that the row must contain. These fields always contain data. The second are optional fields that might not contain data. If you try to print these fields, you’ll get an error. Consequently, the next section of code displays the mandatory fields first.

Row contains more than just fields.
Figure 17-23: Row contains more than just fields.

Notice the if…else structures that appear next. Every optional field includes an IsFieldNameNull() method. Before you print these optional fields, use the null check, such as Row.IsContactNameNull(), to verify that the field contains data. In this case, the code simply prints ‘Nothing‘ when the field is null.

You need to consider one other issue when working through your database access methods. Notice that the _Region field has an underscore in front of it. This underscore doesn’t appear in the database or in the Visual Basic.NET code — IronPython adds it for some reason. If you suddenly find that some fields aren’t accessible, even though you’re using the right name, check for an underscore. Figure 17-24 shows the output from this example.

The extension provides data to IronPython to output.
Figure 17-24: The extension provides data to IronPython to output.

 

Extending IronPython Using C#

Understanding the Requirements for an Extension

It’s important to understand that an extension, any extension, probably ties your code to Windows. Whenever you use an extension with IronPython, you rely on something other than the Python libraries to perform a task, which means you lose the platform independence for which Python is so famous. In short, extensions provide considerable flexibility and help you provide additional capabilities for IronPython, but this flexibility isn’t without cost. Every time you make a design decision of this sort, you must pay a price in the following:

  • Reduced reliability: Due to increased failure points.
  • Weakened security: More languages mean more places where someone could leave a security hole.
  • Impaired speed: Marshaling data between language barriers takes time.
  • Fewer platforms: In order to use an extension, you must find a platform that supports both IronPython and the extension language.

Writing an extension isn’t always straightforward. It isn’t as simple as writing some class library code and putting it in a DLL. In fact, you must spend considerable effort thinking about how an extension should be designed to make it useable. The following list considers just a few of the most important factors for your extension.

  • Python language requirements: IronPython may not support every feature that the static language supports. For example, you may find that IronPython doesn’t support a particular static language operator, such as the ++ operator.
  • IronPython developer mentality: An extension that performs tasks in a way that runs completely counter to the way that an IronPython developer normally does them isn’t very useful, because the IronPython developer will have to think too hard about using the extension. The best kind of extension is one that feels natural to the IronPython developer.
  • Flexibility: An extension should provide some significant advantage in flexibility. When you write an extension, write it with the benefit to the IronPython developer in mind, not simply because the functionality the extension provides is interesting.

The one factor that you don’t need to consider is whether something is doable. Normally, if you can perform a task with the static language you want to use to build the extension, then you can do it with IronPython as well. Sometimes, you have to massage the data or present the technique in a way that doesn’t match your normal methodology, but you can normally perform the task with a bit of effort.

Considering IronPython and Static Language Differences

IronPython is a dynamic language (a language that does things like decide variable type at run time, which is contrasted with a static language that decides everything during compile time). As such, it has some significant advantages for the human developer that a static language can’t provide. It’s true that the concept of language is foreign to the computer, but the human developer relies on certain characteristics of language to accomplish tasks quickly and with few errors. Consequently, as part of defining the reason to use an extension, you must consider the differences between IronPython and the static language of your choice.

Defining Why You Use a Static Language with IronPython

Typically, you use a static language with IronPython to gain a specific advantage. For example, IronPython doesn’t create graphical user interfaces very well, so using a static language to perform this task could provide a significant advantage in development time. In addition, you could probably reuse code that you already have on hand, which may reduce debugging time as well. Look for the advantages that you can gain when using a static language with IronPython. If you have problems describing the material benefit of an extension, then perhaps you really should look at another solution.

Make sure you consider the strengths of the static language when making your selections. For example, C# is often the best choice for Win32 API interaction because it supports unsafe pointers — a requirement for certain specialized Win32 API tasks. Of course, you should make sure that the use of the Win32 API is actually required. Perhaps a third-party library already has the solution you require and with a lot less work. Visual Basic.NET is often the best choice for database work because it takes care of so many tasks in the background for the developer. You don’t have to worry so much about coercing data types because Visual Basic addresses the need for you in the background.

Sometimes the use of a static language is practical. For example, you might have an overwhelming number of developers on your team who know C# or Visual Basic.NET, but know nothing about IronPython. In general, this is one of the poorest reasons to use a static language with IronPython, but the reality of development today is that you often use the tools you have on hand to accomplish the task. No one can afford to have developers sitting on their hands simply because the dynamic language is the best choice for a particular job.

Understanding Line Noise

There are good reasons to avoid using a static language with IronPython. You can write most code in IronPython using far fewer lines than a static language requires. Fewer lines of code translate into higher developer productivity and sometimes into fewer coding errors as well.

The additional code that a static code developer must write is often referred to as line noise. The code doesn’t substantially translate into useful output, but the static language requires it. For example, IronPython doesn’t require that you declare the type of a variable — you simply leave this task to IronPython.

While the extra code in a static language does tend to reduce the potential for unintended output, it can also make the code harder to read. With every benefit, there’s a corresponding negative. When you decide to use an extension with IronPython, you need to consider when it’s appropriate to work through the extra code and cumbersome features of static languages and when IronPython is truly the better choice.

Let’s look at a quick example. Say you want to create an array of names in a function and pass them back to a caller. Here’s the C# code to perform the task.

[code]
public String[] GetNames()
{
String[] Result = new String[4];
Result[0] = “John”;
Result[1] = “Amy”;
Result[2] = “Jose”;
Result[3] = “Carla”;
return Result;
}
public void ShowNames()
{
String[] TheNames = GetNames();
foreach (String Name in TheNames)
{
Console.WriteLine(Name);
}
}
[/code]

The code in GetNames() creates an array of String, fills it with names, and returns those names to the caller, ShowNames(). At this point, ShowNames() uses a foreach loop to display each name individually. Now take a look at the same functionality written in IronPython.

[code]
def GetNames():
return “John”, “Amy”, “Jose”, “Carla”
def ShowNames():
for Name in GetNames():
print Name
[/code]

The code performs the same task in both cases, but as you can see, the IronPython code is significantly shorter. In addition, the IronPython code is actually easier to read.

Considering Scoping Issues

One of the most important differences between IronPython and static languages such as C# is that IronPython doesn’t have the concept of scope within classes. Everything in an IronPython class is public, so you always have access to every element. Of course, this presents a dilemma for languages that do support scope. When creating an IronPython extension, your static language scope declarations will change as follows:

  • Public members remain public.
  • Protected members become public.
  • Protected Internal members become public.
  • Private members remain private and don’t appear at all to IronPython.
  • Internal members become private and don’t appear at all to IronPython.

Creating the Simple C# Extension

The example in the following sections provides a simple set of calculations. Think of it as the basic four-function calculator with a bit extra added. The example doesn’t do anything fancy, but it does demonstrate techniques you need to build any C# extension for IronPython. The rest of the examples in this chapter build on this example, so you should at least scan the techniques presented in the sections that follow.

Creating the Project

A C# extension project in Visual Studio is nothing more than the typical class library. The following steps help you create the project for this example. You can use the same steps when working with the other examples — all you need to do is change the project name.

  1. Choose File ➪ New ➪ Project. You see the New Project dialog box shown in Figure 16-1.

    Create a new project to hold your C# extension.
    Figure 16-1: Create a new project to hold your C# extension.
  2. Choose the Visual C# folder in the Installed Templates list.
  3. Select .NET Framework 3.5 or an earlier version of the .NET Framework. Don’t select the .NET Framework 4.0 entry. The list of templates changes when you change the .NET Framework version.
  4. Select the Class Library template.
  5. Type Calcs in the Name field and click OK. Visual Studio creates a class library project for you.
  6. Right-click Class1.cs in Solution Explorer and choose Rename from the context menu. Visual Studio makes the filename editable.
  7. Type Calcs.CS for the new filename and press Enter. Visual Studio displays a dialog box that asks whether you’d like to rename all of the Class1.cs references to match the new filename.
  8. Click Yes. The project is ready for use.

At the time of this writing, IronPython doesn’t support extensions written using the .NET Framework 4.0. You must create your extensions using the .NET Framework 3.5 or earlier. Otherwise, the extension will simply fail to load and IronPython won’t provide anything in the way of an explanation (at least, nothing useable). If you suspect that you’ve targeted the wrong .NET Framework version, choose Project ➪ ProjectName Properties. Select the Application tab of the Properties window and change the entry in the Target Framework field to .NET Framework 3.5, as shown in Figure 16-2. The IDE may ask permission to modify features in your setup and require that you restart your project to see the effects of the change.

Modify the Target Framework field to a version of the .NET Framework that works with IronPython.
Fi gure 16-2: Modify the Target Framework field to a version of the .NET Framework that works with IronPython.

Developing the C# Extension

The C# extension does have a few tricks to it, but generally speaking, if you know how to create a class library, you already know how to create the code for a C# extension. Listing 16-1 shows the code for the example extension.

Listin g 16-1: A simple calculations extension

[code]
public class Calcs
{
private Int32 Data;
public Calcs(Int32 Value)
{
this.Data = Value;
}
public override string ToString()
{
return Data.ToString();
}
public static Calcs operator +(Calcs Value1, Calcs Value2)
{
return new Calcs(Value1.Data + Value2.Data);
}
public static Calcs operator -(Calcs Value1, Calcs Value2)
{
return new Calcs(Value1.Data – Value2.Data);
}
public static Calcs operator *(Calcs Value1, Calcs Value2)
{
return new Calcs(Value1.Data * Value2.Data);
}
public static Calcs operator /(Calcs Value1, Calcs Value2)
{
return new Calcs(Value1.Data / Value2.Data);
}
public Calcs Inc()
{
return new Calcs(this.Data + 1);
}
public Calcs Dec()
{
return new Calcs(this.Data – 1);
}
}
[/code]

In most cases, you want to create a constructor that accepts the kind of data you want to manipulate with the extension. In this case, the constructor accepts an Int32 value. Interestingly enough, the constructor is the only place where you normally reference the data type of the data directly. In all other cases, you work with the data type indirectly by using the extension class.

Another issue is displaying the data in IronPython. The default implementation of the ToString() method displays the class name, which isn’t helpful. Consequently, you must override the default implementation of ToString() and provide your own output. In this case, the method simply returns the current value of the private variable Data as a string.

This example deals with operators. Of course, there are two kinds of operators, unary and binary. The method you implement for each kind of operator is different.

To create a binary operator, you must consider that the operator will work with two instances of the Calcs class. In short, the operator works with the base class and you must declare it as static. In this example, the + operator is binary, so the code declares it as static. The method also accepts the two instances of the Calcs class as input. In order to return output, the method must create a new instance of the Calcs class with the sum of the two input values. Notice that the method never defines what kind of data it works on, simply that the data is contained in an instance of the Calcs class.

Creating a unary operator is different because you’re working with a single instance of the Calcs class in this instance. To create a unary operator, you simply declare the method as a non-static member of the class, as shown for the Inc() and Dec() methods. In this case, because you’re working with a single value, the code uses this.Data (the internal representation of the data value of the single value) to perform the math. You may wonder why the code simply doesn’t create a ++ operator method. A ++ operator method would look like this and wouldn’t work in a unary manner within IronPython.

[code]
public static Calcs operator ++(Calcs Value1)
{
return new Calcs(Value1.Data + 1);
}
[/code]

If you compiled the class now, you could view it in the IronPython console. The following code provides the steps for loading the extension into memory.

[code]
import clr
clr.AddReferenceToFile(‘Calcs.DLL’)
import Calcs
dir(Calcs.Calcs)
[/code]

Figure 16-3 shows the output of the dir(Calcs.Calcs) call. Notice that Inc() and Dec() appear as you expect. However, there aren’t any entries for +, -, *, and / methods. These operators still work as you expect, but IronPython shows a Python equivalent for the operators in the form of the __add__(), __radd__(), __sub__(), __rsub__(), __mul__(), __rmul__(), __div__(), and __rdiv__(). These methods don’t appear unless you define the operators in your class.

If you’re looking at the class in the IronPython console, you might want to give it a quick try before you close up the console and move on to the next part of the example. Try this code and you’ll see an output of 15 from the __add__() method.

[code]
Value1 = Calcs.Calcs(10)
Value2 = Calcs.Calcs(5)
print Value1.__add__(Value2)
[/code]

The dir() function shows the content of the Calcs class.
Figure 16-3: The dir() function shows the content of the Calcs class.

Adding the IronPython Project

At this point, you have a C# extension (or module) to use with IronPython. Of course, you’ll want to test it. The easiest way to do this is to add the IronPython project directly to the current solution. The following steps describe how to perform this task.

  1. Right-click the solution entry in Solution Explorer and choose Add ➪ Existing Project from the context menu. You see the Add Existing Project dialog box shown in Figure 16-4.

    Locate IPY.EXE and add it to your solution.
    Figure 16-4: Locate IPY.EXE and add it to your solution.
  2. Locate IPY.EXE on your hard drive and highlight it. Click Open. You see a new project entry added to the solution.
  3. Right-click the ipy entry in Solution Explorer and choose Set as Startup Project from the context menu. This step ensures that choosing one of the startup options from the Debug menu starts the IronPython application.
  4. Right-click the ipy entry in Solution Explorer and choose Properties from the context menu. You’ll see the General tab of the ipy Properties window shown in Figure 16-5.

    Configure the IronPython application to work with Calcs.DLL.
    Figure 16-5: Configure the IronPython application to work with Calcs.DLL.
  5. Type -D TestCalcs.py in the Arguments field.
  6. Click the ellipses in the Working Directory field to display the Browse for Folder dialog box. Locate the output folder of the Calcs.DLL (or other extension) file. Click OK. The IDE adds the correct directory information to the Working Directory field.
  7. Open Windows Explorer. Locate the CalcsCalcsbinDebug folder. Right-click in the right pane and choose New ➪ Text Document from the context menu. Name the file TestCalcs.py and press Enter. Click Yes if asked if you want to rename the file extension.
  8. Right-click the solution item in Solution Explorer and choose Add ➪ Existing Item from the context menu to display the Add Existing Item dialog box shown in Figure 16-6.
  9. Locate the TestCalcs.py file in the solution and click Add. Visual Studio adds TestCalcs.py to the Solution Items folder in Solution Explorer and automatically opens the file for you. You’re ready to add test code for the application.
Add the TestCalcs.py file to the solution.
Figure 16-6: Add the TestCalcs.py file to the solution.

Creating the IronPython Application

Now that you have a file to use for the IronPython application, it’s time to add some code to it. The example code fully exercises everything you can do with the C# extension. Listing 16-2 shows the code you add to the TestCalcs.py file.

Listin g 16-2: Testing the extension using IronPython

[code]
# Add a reference to the CLR
import clr
# Obtain access to the extension.
clr.AddReferenceToFile(‘Calcs.DLL’)
import Calcs
# Create an instance of the class and fill it with data.
Value1 = Calcs.Calcs(10)
# Print the original value, then decrement and increment it.
print ‘Original Value1 Content: ‘, Value1
print ‘Value1 + 1: ‘, Value1.Inc()
print ‘Value1 – 1: ‘, Value1.Dec()
# Create a second value and display it.
Value2 = Calcs.Calcs(5)
print ‘nOriginal Value2 Content: ‘, Value2
# Use the two values together in different ways.
print ‘nValue1 + Value2 = ‘, Value1 + Value2
print ‘Value1 – Value2 = ‘, Value1 – Value2
print ‘Value1 * Value2 = ‘, Value1 * Value2
print ‘Value1 / Value2 = ‘, Value1 / Value2
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)

[/code]

The example begins by importing support for the Common Language Runtime (CLR). It then uses the AddReferenceToFile() method to reference the Calcs.DLL file and imports the code into IronPython. These steps are similar to those that you used to test the DLL initially.

The next step is to create an instance of the Calcs class, Value1. The code references Calcs twice — once for the namespace and a second time for the class itself. The next few code steps display the value of Value1 and show how to use the Inc() and Dec() methods. If you set Value1 equal to the output of Inc() or Dec(), it truly would increment or decrement the value of Value1. Because IronPython doesn’t support the ++ operator, however, you can’t use the ++ operator in your extension. On the other hand, you could implement the += and -= operators.

You can’t really test binary operators without a second variable, so the code creates a second instance of Calcs, Value2. The example then shows how the +, -, *, and / operators work. Figure 16-7 shows the output from this example.

Here are the results of using the C# extension within IronPython.
Figure 16-7: Here are the results of using the C# extension within IronPython.

Using C# for User Interface Support

It’s a painful process because you don’t have access to any designers, but the process is definitely doable. You may very well decide to use IronPython directly for all your graphics needs, simply to avoid using another language. However, C# or Visual Basic.NET make better choices for creating a user interface because you do get access to the designer support that these languages provide. With this in mind, the following sections describe how you can add graphic support to IronPython using a C# extension.

Defining a Library of Dialog Boxes

If you’re using IronPython as your main application language and relying on a static language for ancillary support, such as the user interface requirements, it makes sense to create all the dialog boxes you require and place them in a library. Of course, if the application is relatively complex, you might use several physical DLLs to perform the task or rely on a single DLL, but rely on multiple projects to accommodate a team of developers The point is that you need to plan how to store the dialog boxes in a manner that makes it efficient to work on the project.

There’s a tendency by some developers to create generic dialog boxes and then manipulate them in code. This technique does work well when you use the dialog boxes in the static language. However, the approach can become counterproductive when using the dialog boxes in IronPython. The IronPython code can become so complicated that it becomes unreliable and hard to maintain. In general, use specific dialog boxes whenever possible, which won’t require many (or any) changes.

IronPython doesn’t have a representation of every C# or Visual Basic.NET feature. For example, in the section “Developing the C# Extension” section earlier in this chapter, you’ll discover that IronPython doesn’t support the C# ++ operator, but it does support the += operator. It’s best to perform data manipulation in the static language environment when possible or pass the raw data to IronPython in a form it can readily use. For example, you might pass a list of field values to IronPython as a dictionary.

Marshaling data between languages can reduce application performance. You may find situations where you need to process data in a thread to maintain acceptable performance for the user. However, before you take time to create a complex threading solution, ask users to try the application in a test environment to determine whether the performance is acceptable.

Creating the Dialog Box Library in C#

Your dialog box library can support dialog boxes at two levels. It’s possible to meet some IronPython needs using a simple message box or prompt box. Because these solutions are already programmed for you, supporting them through the static language, where the features are easily accessed, is a good way to save on development and debugging time. You can customize the implementation of these standardized features to make them easy to use within IronPython — reducing the need to import a lot of managed assemblies into IronPython.

Of course, many user-interface needs require something more advanced than a simple message box. The following sections describe how to create simple message boxes and complex Windows Forms in C# that you can use in your IronPython application. The goal is to use the right kind of interface element for a given task and to make the interface element easy to access and process from within IronPython. The section “Creating the Simple C# Extension” earlier in this chapter describes how to set up the solution used for this example.

Defining Simple Message Boxes

This example is interesting because it shows how you can create overrides of your methods. The MessageBox.Show() method has 21 overrides in C#. Of course, you might not need all those overrides and the example shows only five of them. Before you can work with message boxes in a C# class, you need to add a reference to the System.Windows.Forms.DLL and add the following using statement.

[code]
using System.Windows.Forms;
[/code]

Now that you have the prerequisites in place, it’s time to look at some code. Listing 16-3 shows the code used to create this example.

Listin g 16-3: Creating a simple message box class

[code]
public class Dialogs
{
public Dialogs()
{
}
public String ShowMessage(String Msg)
{
return MessageBox.Show(Msg).ToString();
}
public String ShowMessage(String Msg, String Title)
{
return MessageBox.Show(Msg, Title).ToString();
}
public String ShowMessage(String Msg, String Title, Int16 Buttons)
{
return MessageBox.Show(Msg, Title,
(MessageBoxButtons)Buttons).ToString();
}
public String ShowMessage(String Msg, String Title, Int16 Buttons,
Int16 Icon)
{
return MessageBox.Show(Msg, Title,
(MessageBoxButtons)Buttons, (MessageBoxIcon)Icon).ToString();
}
public String ShowMessage(String Msg, String Title, Int16 Buttons,
Int16 Icon, Int16 DefaultButton)
{
return MessageBox.Show(Msg, Title,
(MessageBoxButtons)Buttons, (MessageBoxIcon)Icon,
(MessageBoxDefaultButton)DefaultButton).ToString();
}
}
[/code]

The code begins with the usual constructor. The constructor doesn’t really need to do anything in this case. Of course, you could set up the constructor to accept some of the required inputs, such as the message and message box title, but sending the information with the ShowMessage() method works just fine, too. The constructor could also set up default settings, if desired, that the developer could override with specific versions of ShowMessage().

The ShowMessage() method declarations come next. The methods are relatively simple. Each one calls a different override of the MessageBox.Show() method. Notice that you must coerce the MessageBoxButtons, MessageBoxIcon, MessageBoxDefaultButton values from the inputs. You could ask the caller to provide the actual enumerated values, but that approach would reduce the benefit of using this approach for working with message boxes, because the developer would need to load the required .NET assemblies anyway.

Even when working with simple message boxes, you can encounter a few problems. For example, the enumerations provided in the static environment make it simple to select a particular button combination or icon. IntelliSense displays the list of values from which you can choose. However, IronPython doesn’t provide IntelliSense, so there isn’t any simple method of selecting a button combination or icon from a list. The example uses numbers, which works fine for the button combinations because they’re numbered 0 through 5. However, the icons have values of 0, 16, 32, 48, and 64, which are hardly easy to remember. The default button values are equally odd at 0, 256, and 512. Tables 16-1 through 16-3 show the values for the message box enumerations. In a production environment, you’d probably create text equivalents for the developer, which you could translate in the extension, or provide some type of enumeration for the developer.

Message Box Button Combinations
Table 16-1: Message Box Button Combinations

 

Table 16-2: Message Box Icon Combinations
Table 16-2: Message Box Icon Combinations
Table 16-2: Message Box Icon Combinations
Table 16-2: Message Box Icon Combinations (continued)
Message Box Default Button Options
Table 16-3: Message Box Default Button Options

Using Enumerations with IronPython

There’s a way around the issue of enumerated values in .NET calls. You can simply choose to create your own enumeration. For example, let’s say you want to overcome the problem of working with the MessageBoxButtons enumeration. In this case, you create an enumeration and a new override of the ShowMessage() method as shown here.

[code]
public enum ButtonTypes
{
OK,
OKCancel,
AbortRetryIgnore,
YesNoCancel,
YesNo,
RetryCancel
}
public String ShowMessage(String Msg, String Title, ButtonTypes Buttons)
{
return MessageBox.Show(Msg, Title,
(MessageBoxButtons)Buttons).ToString();
}
[/code]

Notice that you must still use coercion to make the MessageBox.Show() call. However, the IronPython developer now has an enumeration to use when making the call. Here’s a typical call from within IronPython.

[code]
MyDialog.ShowMessage(‘Hello’, ‘Title’, MyDialog.ButtonTypes.OKCancel)
[/code]

The resulting message box would contain ‘Hello‘ as the message, ‘Title‘ as the message box title, and two buttons, OK and Cancel.

Considering Developer Help

As your extensions gain in complexity, you need to start providing some help to the IronPython developer. Most IronPython developers will spend part of their time in the interpreter trying things out. The developer will look to your documentation for help in using the extension you create. There are two forms of help, as shown here.

[code]
help(MyDialog.ShowMessage)
MyDialog.ShowMessage.__doc__()
[/code]

It turns out that IronPython automatically provides a form of the help() function help for you as shown in Figure 16-8. In this case

IronPython provides a kind of help for you automatically.
Figure 16-8: IronPython provides a kind of help for you automatically.

Unfortunately, IronPython doesn’t provide the __doc__() method by default. You must define it for yourself as part of the class you create. Here’s a simple __doc__() method you can use with the example. Of course, a production version would contain far more information.

[code]
public String __doc__()
{
return “This is a help string”;
}
[/code]

When you try this method out at the Python prompt, you see the outline shown in Figure 16-9. You can use all of the normal formatting characters to make the help provided by the __doc__() method look nice. For that matter, you could store the information externally and simply read it in as needed.

 You must define your own version of the __doc__() method.
Figure 16-9: You must define your own version of the __doc__() method.

Defining Complex Forms

At some point, simple message boxes simply won’t do the job for you. After all, you’ll want forms that contain a number of fields that you can use to process complex information from the user. In this case, you must create a standard Windows form for your extension. To accomplish this task, you begin by adding the form using the following steps.

  1. Right-click Dialogs in Solution Explorer and choose Add ➪ New Item. Select the Windows Forms entry in the Installed Templates list. You see the Add New Item dialog box shown in Figure 16-10.

    Add a Windows Form to your project
    Figure 16-10: Add a Windows Form to your project
  2. Highlight the Windows Form entry. Type TestForm.CS in the Name field and click Add. Visual Studio adds the new form to your project and automatically opens it for editing.

At this point, you can create the form just as you normally would for any static application. Figure 16-11 shows the form used for this example. It’s simple, but it contains multiple dataentry fields and multiple exit options.

The Windows Form can contain any level of complexity you desire.
Figure 16-11: The Windows Form can contain any level of complexity you desire.

Before you assume anything about this form, note that it does differ in a few ways from the forms you’ve created for your static applications. The first difference is that the buttons that close the form, rather than do something within the form, must have the DialogResult property set to a unique value or you won’t be able to tell which button the user clicked. For this example, the DialogResult for btnOK is OK, while the DialogResult for btnCancel is Cancel.

The second difference involves a problem with getting information from the form you create to the IronPython application. You could contrive all sorts of odd methods for accomplishing the task, but the simplest method is to set the Modifiers property for the individual controls (txtName and txtColor) to Public. In this case, using Public doesn’t create a problem because IronPython sets everything to public. In all other respects, there’s no difference between this form and any other form you’ve created in the past.

To make things simple, this example doesn’t use any code-behind for the form itself. Any codebehind works as you’d expect. There isn’t any difference between calling the form from IronPython than calling it from within your C# application.

Accessing the Dialog Box Library from IronPython

At this point, you have a nice collection of dialog box and form classes to use in an IronPython application. Of course, a production application would probably have quite a few more forms in it, but you have enough for testing and experimentation purposes. The following sections describe how to use these classes.

An Alternative Method for Adding the IronPython Project

There are a number of ways to configure a test setup for your extensions. The section “Adding the IronPython Project” earlier in this chapter shows one technique. The technique works well when you want to maintain separate builds of your extension. However, you might want to maintain just one build — the build you’re currently using for debugging, testing, or experimentation. Use the following steps to create a centralized test configuration.

  1. Right-click Dialogs in Solution Explorer and choose Properties from the context menu. Select the Build tab. You see the Properties window shown in Figure 16-12.
  2. Click Browse next to the Output Path field to display the Select Output Path dialog box shown in Figure 16-13. Because you’ll add the IronPython test file at the solution level, you need to send the output to the solution level as well.

    Configure the build to use a central output location.
    Figure 16-12: Configure the build to use a central output location.
  3. Select the first Dialogs entry in the list and click OK. Visual Studio adds an absolute path to the Output Path field that you must change for every machine that uses the application. As an alternative, you could type ..(two periods and a backslash) in the field to place the output in the solution folder.
  4. Select the next configuration in the Configuration field.
  5. Perform Steps 2 through 4 for each configuration. Make sure each configuration uses the same output directory. Normally, your project will only contain Debug and Release configurations.

    Modify the output path as required for your application.
    Figure 16-13: Modify the output path as required for your application.
  6. Right-click the solution entry in Solution Explorer and choose Add ➪ Existing Project from the context menu. You see the Add Existing Project dialog box shown in Figure 16-4.
  7. Locate IPY.EXE on your hard drive and highlight it. Click Open. You’ll see a new project entry added to the solution.
  8. Right-click the ipy entry in Solution Explorer and choose Set as Startup Project from the context menu.
  9. Right-click the ipy entry in Solution Explorer and choose Properties from the context menu. You see the General tab of the ipy Properties window shown in Figure 16-5.
  10. Type -D DialogTest.py in the Arguments field.
  11. Click the ellipses in the Working Directory field to display the Browse for Folder dialog box. Locate the solution folder for the project (the first Dialogs folder). Click OK. The IDE adds the correct directory information to the Working Directory field.
  12. Right-click the solution entry in Solution Explorer and choose Add ➪ New Item from the context menu. You see the Add New Item dialog box shown in Figure 16-14.

    Add the IronPython test file to your project.
    Figure 16-14: Add the IronPython test file to your project.
  13. Type DialogTest.py in the Name field and click Add. Visual Studio adds the new file to the Solution Items folder in Solution Explorer and opens the file automatically for editing.

Performing the Message Box and Form Tests

It’s finally time to test the message boxes and forms you’ve created. The code in this section performs a few simple tests and demonstrates how to obtain output from the message boxes and forms you’ve created. You can use this code as a starting point for more complex processing in your own application. Listing 16-4 shows the test code for this application.

Listin g 16-4: Testing the extension using IronPython

[code]
# Define the message box tests.
def TestMessages():
# Create a message box object.
MyDialog = Dialogs.Dialogs()
# Test a simple message box.
print ‘Testing a simple message box.’
print ‘Simple message box output: ‘,
print MyDialog.ShowMessage(‘Hello’)
# Perform a more complex test.
print ‘nA more complex message box.’
print ‘Complex message box output: ‘,
print MyDialog.ShowMessage(‘Hello Again’, ‘Title 2’, 3, 64, 256)
# Define the form test.
def TestForm():
# Create the form instance.
MyForm = Dialogs.TestForm()
# Display the form and test the dialog result.
print ‘nThe form example.’
if MyForm.ShowDialog().ToString() == ‘OK’:
# Display the results.
print ‘The user clicked OK.’
print ‘User Name: ‘, MyForm.txtName.Text
print ‘Favorite Color: ‘, MyForm.txtColor.Text
# Display an alternate result.
else:
print ‘The user clicked cancel.’
# Import the Common Language Runtime.
import clr
# Access the extension.
clr.AddReferenceToFile(‘Dialogs.DLL’)
import Dialogs
# Test the message box code.
TestMessages()
# Test the form code.
TestForm()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The test code begins by importing CLR and gaining access to the Dialogs namespace. This example demonstrates one of the benefits of using a namespace, easy access to multiple classes. It’s a good way to organize a library of forms to make them easy to access and to avoid naming conflicts.

The TestMessages() function contains the code to test the Dialogs.Dialogs class. This code begins by creating a Dialogs.Dialogs instance, MyDialog. In this case, the application begins by creating a simple message box and displaying it onscreen. This message box lacks a title and contains only an OK button. When the user clicks OK, the program prints the dialog result to screen.

The second test is a little more complex. This time the code relies on the most complex form of the ShowMessage() method to display a dialog box that contains a message, title, icon, and multiple buttons as shown in Figure 16-15. Notice that the figure shows that the message box also has the middle button selected by default. Pressing Enter will automatically select this default option. Normally, message boxes select the first button as the default. Depending on which button the user clicks, the application will display a message with the appropriate dialog result. You could also use this dialog result as part of an if…else statement to choose an appropriate course of action.

A more complex message box includes multiple buttons, a title, and an icon.
Figure 16-15: A more complex message box includes multiple buttons, a title, and an icon.

The TestForm() method begins by creating an instance of Dialogs. TestForm, MyForm. The dir() function will show you that MyForm now has access to all of the functionality normally associated with a Windows Forms class, but without importing any of the bulk associated with the System.Windows .Forms assembly. As with any Windows Form, you call ShowDialog() to display the form. However, the result of displaying the form is going to be something that IronPython can’t use directly. The way to overcome this problem is to call ShowDialog().ToString(). In this case, the output is a string that describes which button the user has clicked.

This portion of the example shows how to process the form data locally. When the user clicks OK, the dialog result is ‘OK‘ and the if statement succeeds. The code accesses the MyForm.txtName.Text and MyForm.txtColor.Text properties to determine what the user has typed. When the if statement fails, the code displays a message telling you that the user clicked Cancel. Figure 16-16 shows typical output from this example.

Here are the results of using the C# extension within IronPython.
Figure 16-16: Here are the results of using the C# extension within IronPython.

Using C# for Win32 Support

The Python language doesn’t really support much in the way of platform-specific functionality and that’s by design. One of the tenets of cross-platform compatibility is not to make an issue out of the platform on which the code runs. However, in some cases, you really do need to access the platform and discover things about it. For example, you might want to know more about the environment in which your application is executing, such as the size of the console window. You might even want to clear the console window (a feature that is missing from the IronPython console, without which your sessions can appear messy). An application may need to know something about the security in place for the current session. In short, you might have many reasons for wanting to know something more, but Python (and by extension, IronPython) largely lacks the functionality to provide this information.

The example in the following sections plays to a strength of C#, which is to interact with the Windows platform through a feature called Platform Invoke (P/Invoke). This example goes outside the managed .NET environment and relies on the Win32 API to access Windows functionality that you can’t access through .NET.

Creating the P/Invoke Code

Before you can write any P/Invoke code, you need to add the following using statement.

[code]
using System.Runtime.InteropServices;
[/code]

This statement provides access to the various special programming features that C# provides for accessing the Win32 API.

If you haven’t worked with the Win32 API in the past, you might find the use of structures, enumerations, and pointers confusing. In reality, all these events take place somewhere in the background when you execute any application. At some point, your managed code ends up interacting with the Win32 API to perform tasks because the basic Windows DLLs still rely on the Win32 API. Normally, CLR hides all these details from view so you don’t need to worry about them. Listing 16-5 shows the Win32 API access code — the lower-level code that does all the hard work for this example.

Listin g 16-5: Win32 API access code and structures

[code]
// This special class contains an enumeration of
// standard handles.
class StdHandleEnum
{
public const int STD_INPUT_HANDLE = -10;
public const int STD_OUTPUT_HANDLE = -11;
public const int STD_ERROR_HANDLE = -12;
};
// The GetStdHandle() function returns a handle to any
// standard input or output.
[DllImport(“kernel32.dll”, SetLastError=true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
// This sructure contains a screen coordinate.
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct COORD
{
public short X;
public short Y;
}
// Obtains the current display mode–fullscreen or fullscreen hardware.
[DllImport(“Kernel32.DLL”)]
public static extern bool GetConsoleDisplayMode(ref UInt32 lpModeFlags);
// An enumeration used to determine the current display mode.
public enum ConsoleDispMode
{
CONSOLE_WINDOWED = 0, // Only implied by function.
CONSOLE_FULLSCREEN = 1, // The console is fullscreen.
CONSOLE_FULLSCREEN_HARDWARE = 2 // The console owns the hardware.
}
// Obtains the size of the largest console window possible.
[DllImport(“Kernel32.DLL”)]
public static extern COORD
GetLargestConsoleWindowSize(IntPtr hConsoleOutput);
// Returns the console mode information.
[DllImport(“Kernel32.DLL”)]
public static extern bool GetConsoleMode(
IntPtr hConsoleHandle,
ref UInt32 lpMode);
public enum ModeFlags
{
// Input mode flags
ENABLE_PROCESSED_INPUT = 0x0001,
ENABLE_LINE_INPUT = 0x0002,
ENABLE_ECHO_INPUT = 0x0004,
ENABLE_WINDOW_INPUT = 0x0008,
ENABLE_MOUSE_INPUT = 0x0010,
// Output mode flags
ENABLE_PROCESSED_OUTPUT = 0x0001,
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
}
[/code]

Many of the Win32 API functions require you to know specific integer or hexadecimal values. Even C++ developers can’t remember these numbers. Normally, a C++ developer relies on define statements that put the numbers into human-readable form. The P/Invoke code used in this chapter does the same thing, but sometimes it places the numbers in an enumeration to make them even easier to use. The StdHandleEnum class provides a list of standard handles (pointers) for Windows devices: input, output, and error. However, these aren’t the actual handles.

In order to get the standard Windows handle, an application must call the GetStdHandle() function. This function is in kernel32.dll. The [DllImport()] attribute tells the compiler where to look for an external Win32 API function that you want to use in your code. In this case, the attribute also tells the compiler that you want any error information that the Win32 API can provide. The use of extern before the function name tells the compiler that the requested DLL contains a function of the name that follows. You can now call this function directly and CLR will automatically perform any required marshaling for you.

Many of the Win32 API calls provide coordinates — x and y locations that tell where something is or how large it is. The COORD structure provides a means of transferring this kind of information between the .NET environment and the Win32 API environment. Windows uses a very basic view of structures. Unfortunately, .NET often causes problems by trying to optimize the data structures and causes P/Invoke calls to fail even though they should succeed. The [StructLayout()] attribute tells the compiler how to create a data structure in memory, which overrides the normal optimization process.

You may create applications that need to run in full-screen mode, if for no other reason than that they require the additional screen real estate to present information to the user. The GetConsoleDisplayMode() function tells you what mode the console is currently in. If the console is in the wrong mode, you can ask the user to change the mode or simply stop the application before the screen mode causes any problems. This function returns flags, not an enumerated value. At least one of the flags is always set, but the return value can have multiple flags set. The ConsoleDispMode enumeration makes it easier to work through the flag settings and provide usable output. The section “Defining the GetCurrentDisplayMode() Method” later in this chapter provides more information about this function.

In some cases, you need to know the largest size console window that the system will support. The GetLargestConsoleWindowSize() function provides this information. You can use other Win32 API functions to adjust the size of the window to meet application requirements (which is a topic for another book). The section “Defining the GetConsoleWindowSize() Method” provides more information about this function.

It’s also handy to know what kinds of operations the console window will support. For example, it’s good to know whether the console window will respond to the mouse. The GetConsoleMode() function provides this kind of information. The output is in the form of flags that you must interpret in your code. The GetConsoleMode() function is special in that the output you receive depends on the kind of device handle you provide. The output differs when you provide an input handle, versus an output handle. The section “Defining the GetConsoleInfo() Method” provides additional information about how this technique works.

Developing the IronPython Callable Methods

The P/Invoke code shown in Listing 16-5 does expose the Win32 API calls needed to perform certain tasks with IronPython. Theoretically, you could rely on just the code in Listing 16-5 to gain the access you require in IronPython. However, the task would be difficult because you’d need to work through the required bit manipulations. It’s better to place the code you need to access the Win32 API in easily called methods, which is the purpose of the code in the following sections.

Defining Common Variables and the Constructor

Win32 API calls often reuse information. It’s not uncommon for functions to ask for the same information over and over. For example, any function that works with a window will probably need the handle for that window. With this requirement in mind, Listing 16-6 shows the common variables and the constructor used for this example.

Listin g 16-6: Common variables and constructor

[code]
UInt32 DisplayMode = 0; // The current display mode.
IntPtr hOut; // Handle to the output device.
IntPtr hIn; // Handle to the input device.
UInt32 ConsoleMode = 0; // The console mode information.
public ConMode()
{
// Obtain a handle to the console screen and console input.
hIn = GetStdHandle(StdHandleEnum.STD_INPUT_HANDLE);
hOut = GetStdHandle(StdHandleEnum.STD_OUTPUT_HANDLE);
}
[/code]

The common variables include the current display mode (such as windowed), the console mode information (such as whether it accepts mouse input), and the handles for the input and output devices. These variables represent common pieces of information that the developer requires for multiple calls.

The constructor initializes the input and output handles using the GetStdHandle() function. The input argument simply tells Windows which handle you want. The output is an IntPtr, a special kind of variable that points to something. An IntPtr is a safe pointer, meaning you can use it without problems in a managed language. C# also supports unsafe pointers that you should use only as a last resort.

Defining the GetCurrentDisplayMode() Method

Sometimes you need to know whether the console is presented in a windowed or full-screen mode. A windowed console can get covered up and needs to share resources with other windows. In addition, the text in a windowed console can be small and hard to read. On a positive note, using a windowed console makes it easier to share data between applications. In most cases, the user will prefer that you use a windowed console to make it easier to multitask between applications. Listing 16-7 shows how to detect the current console display mode.

Listin g 16-7: Obtaining the current display mode

[code]
public OutputMode GetCurrentDisplayMode()
{
// Get the current display mode.
if (GetConsoleDisplayMode(ref DisplayMode))
// Determine if the console is in windowed mode.
if (DisplayMode == (UInt32)ConsoleDispMode.CONSOLE_WINDOWED)
return OutputMode.Windowed;
else
{
// If the console is fullscreen mode, determine which
// of the potential conditions are true.
switch (DisplayMode)
{
case (UInt32)ConsoleDispMode.CONSOLE_FULLSCREEN:
return OutputMode.Fullscreen;
case (UInt32)ConsoleDispMode.CONSOLE_FULLSCREEN_HARDWARE:
return OutputMode.HadwareAccess;
case (UInt32)ConsoleDispMode.CONSOLE_FULLSCREEN +
(UInt32)ConsoleDispMode.CONSOLE_FULLSCREEN_HARDWARE:
return OutputMode.FullscreenHardwareAccess;
}
}
// Return a default value.
return OutputMode.Unknown;
}
[/code]

The code begins by calling GetConsoleDisplayMode() to obtain the display mode as a numeric value. The information is returned in DisplayMode, not as a return value from the function call. The function itself returns a success value that indicates the call was successful. The first if statement says that if the call is successful, then DisplayMode will contain the console display mode, and that the application should proceed to process it. Because DisplayMode provides a return value, you must include the ref keyword when passing it to the Win32 API.

Now that the code has a display mode value, it needs to process it. If a console is in windowed mode, all the code has to do is return a value that says it’s windowed. However, full-screen mode requires some additional processing. When a console is in full-screen mode, it can also have access to the hardware. This is virtual hardware access, but it still feels to the application as if the access is direct. Consequently, the code must now determine whether the console is simply in full-screen mode or it’s in full-screen mode with hardware access.

The call could fail, but it’s unlikely to. Even so, the GetCurrentDisplayMode() handles the potential problem by providing the OutputMode.Unknown return value. This value simply says that the method couldn’t determine the current console display mode.

Defining the GetConsoleWindowSize() Method

Sometimes an application needs to know the maximum windowed console that a machine can accommodate. You might need additional room to display complex textual information. The Win32 API returns this information in a COORD structure that simply states the number of rows and columns of text that a console can support at maximum size. The following code shows the GetConsoleWindowSize() method used to obtain this information.

[code]
public COORD GetConsoleWindowSize()
{
// Determine the largest screen size possible.
return GetLargestConsoleWindowSize(hOut);
}
[/code]

This method is easy. All it does is call the GetLargestConsoleWindowSize() function with the output handle. Make sure you provide the output handle, and not the input handle, when making this call. The X and Y members of COORD contain the maximum screen size on return from the call.

Defining the GetConsoleInfo() Method

Consoles can support a number of input and output methods. For example, a console can support the mouse, which may make it easier for the user to interact with your character-mode application. If a console provides support for echo, it re-displays commands sent to it from batch files and other forms of automation. Consequently, you might find it useful to know just what the console will do for you. Listing 16-8 shows how to determine the input and output handling that a console provides.

Listin g 16-8: Obtaining the console characteristics

[code]
public struct ConsoleData
{
public Boolean Echo;
public Boolean LineInput;
public Boolean MouseInput;
public Boolean ProcessedInput;
public Boolean WindowInput;
public Boolean ProcessedOutput;
public Boolean LineWrap;
}
public ConsoleData GetConsoleInfo()
{
// Create the required structure.
ConsoleData Output = new ConsoleData();
// Retrieve the input information.
if (GetConsoleMode(hIn, ref ConsoleMode))
{
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_ECHO_INPUT) ==
(UInt32)ModeFlags.ENABLE_ECHO_INPUT)
Output.Echo = true;
else
Output.Echo = false;
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_LINE_INPUT) ==
(UInt32)ModeFlags.ENABLE_LINE_INPUT)
Output.LineInput = true;
else
Output.LineInput = false;
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_MOUSE_INPUT) ==
(UInt32)ModeFlags.ENABLE_MOUSE_INPUT)
Output.MouseInput = true;
else
Output.MouseInput = false;
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_PROCESSED_INPUT) ==
(UInt32)ModeFlags.ENABLE_PROCESSED_INPUT)
Output.ProcessedInput = true;
else
Output.ProcessedInput = false;
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_WINDOW_INPUT) ==
(UInt32)ModeFlags.ENABLE_WINDOW_INPUT)
Output.WindowInput = true;
else
Output.WindowInput = false;
}
// Retrieve the output information.
if (GetConsoleMode(hOut, ref ConsoleMode))
{
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_PROCESSED_OUTPUT) ==
(UInt32)ModeFlags.ENABLE_PROCESSED_OUTPUT)
Output.ProcessedOutput = true;
else
Output.ProcessedOutput = false;
if ((ConsoleMode & (UInt32)ModeFlags.ENABLE_WRAP_AT_EOL_OUTPUT)
== (UInt32)ModeFlags.ENABLE_WRAP_AT_EOL_OUTPUT)
Output.LineWrap = true;
else
Output.LineWrap = false;
}
// Return the results.
return Output;
}
[/code]

This is one of the few situations in the chapter where you need to send a number of pieces of information back to IronPython. The ConsoleData structure contains an entry of each piece of information that the GetConsoleInfo() provides. An IronPython application can set the output of the call to a variable and then use the variable content to determine precisely how the console is configured.

The GetConsoleInfo() method is a little more complicated than the other calls in the extension. This method relies on the GetConsoleMode() function to obtain console information. However, notice that the method calls the GetConsoleMode() function twice, once with the input handle and again with the output handle. This method demonstrates how the use of the wrong handle could cause problems because the output from the GetConsoleMode() function differs with the handle you provide as input.

The return value from the GetConsoleMode() function is a series of flags. Notice how the code uses if statements to determine whether each flag is set. When a flag is set, the feature is enabled and the code sets that value in the ConsoleData data structure, Output, to true. The method ends by returning the fully completed ConsoleData data structure to the caller.

Writing an IronPython Application to Use P/Invoke

If you’ve been following along with the example, you know it’s finally time to use the ConMode class with IronPython. It’s now possible to determine the display mode, the size of the console window, and the capabilities it provides. Listing 16-9 shows the code used for testing this extension.

Listin g 16-9: Testing the Win32 API extension

[code]
# Import the Common Language Runtime.
import clr
# Access the extension.
clr.AddReferenceToFile(‘Win32API.DLL’)
import Win32API
# Create an instance of the class.
TestWin32 = Win32API.ConMode()
# Check the display mode.
print ‘The display mode is: ‘,
print TestWin32.GetCurrentDisplayMode()
# Obtain the largest possible window size.
print ‘nThe largest possible window size is: ‘
Size = TestWin32.GetConsoleWindowSize()
print ‘tColumns: ‘, Size.X
print ‘tRows: ‘, Size.Y
# Display the console characteristics.
print ‘nThe console has these characteristics:’
Chars = TestWin32.GetConsoleInfo()
print ‘tEcho Enabled: ‘, Chars.Echo
print ‘tLine Input Enabled: ‘, Chars.LineInput
print ‘tMouse Input Enabled: ‘, Chars.MouseInput
print ‘tProcessed Input Enabled: ‘, Chars.ProcessedInput
print ‘tWindow Input Enabled: ‘, Chars.WindowInput
print ‘tConsole Can Produce Processed Output:’, Chars.ProcessedOutput
print ‘tConsole Uses Line Wrap: ‘, Chars.LineWrap
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing CLR support. It then creates a reference to Win32API.DLL and imports the Win32API namespace into the IronPython environment. The next step is to create an instance of the Win32API.ConMode class, TestWin32.

At this point, the code begins checking each console feature in turn, beginning with the console display mode, which doesn’t require any additional processing. The GetConsoleWindowSize() method call requires that the code display the Size.X (columns) and Size.Y (rows) values separately.

The GetConsoleInfo() method call comes next. This particular call requires a little more processing because it returns more information. The output of the call appears in Chars as a ConsoleData data structure. As you can see, the code simply displays the true or false value of each of the data structure members. Figure 16-17 shows the output from this example.

One of the most important issues when making Win32 API calls from IronPython is to ensure that the C# extension processes the data in an easy-to-use manner. In addition, you should provide a consistent method for returning the data from the C# extension to IronPython, such as using data structures (as shown in the example).

 

 

Using IronPython from Other .NET Languages

Understanding the Relationship between Dynamic and Static Languages

Something that most developers fail to consider is that, at some point, all languages generate the same thing — machine code. Without machine code, the software doesn’t execute. Your computer cares nothing at all about the idiosyncrasies of human language and it doesn’t care about communicating with you at all. Computers are quite selfish when you think about it. The circuitry that makes up your computer relies on software to change the position of switches — trillions of them in some cases. So computers use machine code and only machine code; languages are for humans.

When it comes to dynamic and static languages, it’s the way that humans view the languages that make them useful. A dynamic language offers the developer freedom of choice, call it the creative solution. A static language offers a reliable and stable paradigm — call it the comfort solution, the one that everyone’s used. How you feel about the languages partly affects your use of them. In the end, both dynamic and static language output ends up as machine code. Dynamic and static languages end up being tools that help you create applications faster and with fewer errors. If you really wanted to do so, you could write any application today using assembler (a low-level language just above machine code, see http://www.bing.com/reference/semhtml/Assembly_language for more information), but assembler is hardly the correct tool any longer — humans need a better tool to put applications together. The point is that you should use the tool that works best for a particular development process and not think that the tool is doing anything for your computer.

Anytime you use multiple languages, you must consider issues that have nothing to do with the dynamic or static nature of that language. For example, you must consider the data types that the languages support and provide a method for marshaling data from one language to the other. In fact, marshaling data is an important element in many areas of coding. If you want to communicate with the Win32 API from a .NET-managed language such as C# or Visual Basic.NET, you must marshal the data between the two environments. It’s important not to confuse communication and infrastructure requirements with differences between dynamic and static languages. Many resources you find do confuse these issues, which makes it hard for anyone to truly understand how dynamic and static languages differ.

Before you can use IronPython from other languages, it’s important to consider the way in which IronPython performs tasks. When an IronPython session starts, nothing exists — the environment begins with an empty slate. You’ve discovered throughout this book that IronPython calls upon certain script files as it starts to configure the environment automatically. These configuration tasks aren’t part of the startup; they are part of the configuration — something that occurs after the startup. The dynamic nature of IronPython means that all activity begins and ends with adding, changing, and removing environment features. There aren’t any compiled bits that you can examine statically. Everything in IronPython is dynamic.

When a static language such as C# or Visual Basic.NET attempts to access IronPython, it must accommodate the constant change. If you got nothing else out of Chapter 14 but this one fact, then the chapter was worth reading. In order to do this, C# and Visual Basic.NET rely upon events because they can’t actually accommodate change as part of the language. An event signals a change — an IronPython application has modified a class to contain a new method or property. It isn’t just the idea that the output or value has changed, but the method or property itself is new. In some cases, C# or Visual Basic.NET will also need to deal with the situation where a method or property simply goes away as well. The underlying mechanism of events, delegates, and caches is inspired and all but invisible, but to be successful at using the languages together, you must know they’re present.

The differences between dynamic and static languages go further than simply not knowing what code will execute next in a dynamic language. There’s also the matter of data typing. A static language assigns a type to the data it manages, which means that the compiler can make assumptions about the data and optimize access to it. A dynamic language also assigns types to the data it manages, but only does so at run time and even then the data type can change. Now, consider how this changeability complicates the matter of marshaling data from one language to the other. Because the data no longer has a stable type, the marshaling code can’t assume anything about it and must constantly check type to ensure the data it marshals appears in the right form in the target language.

The difference between dynamic and static languages, at least from a programming perspective, comes down to flexible coding and data typing. Everything else you may have heard either relates to differences between any two languages (such as the need to marshal data) or the political drama of which tool works best. This book won’t endeavor to tell you what tool to use. Certainly, I don’t tell anyone that a hammer works best for driving screws or that screwdrivers make wonderful ice picks (not that I believe either of these statements myself). The tool you use for a particular task is the one you can use best or the one called for by a particular job requirement. The point of this chapter and the rest of the book is to demonstrate that dynamic and static languages can work together successfully and in more than one way. The tool you use is up to you.

Creating an Externally Accessible IronPython Module

The first requirement for building an application that allows external access is to create the IronPython script you want to use. Ideally, this script will contain code that is fully debugged. You also want to test the code before you try to use it within C# or Visual Basic.NET. The following sections provide you with the techniques you use to create an IronPython script that you access from C# or Visual Basic .NET.

Considering Requirements for Externally Accessible Modules

The mistake that many developers will make is to think they must do something special in IronPython to make the code accessible. What you really need to do is create an IronPython script using the same techniques as always, and then test it directly. After you test the script using IronPython code, work with the target static language to gain the required access. This pretesting process is important to ensure that you aren’t fighting with a bad script in addition to potential problems marshaling data or interacting with methods that change.

Creating the IronPython Script

The IronPython script used for this example is quite simple in approach. All that the example call really does is add two numbers together. You could perform the task with far less code, but the point of this class is to demonstrate access techniques, so it’s purposely simple. Listing 15-1 shows the external module code and the code used to test it. As previously mentioned, testing your IronPython script is essential if you want the application to work properly.

Listin g 15-1: A test IronPython class for use in the examples

[code]
# The class you want to access externally.
class DoCalculations():
# A method within the class that adds two numbers.
def DoAdd(self, First, Second):
# Provide a result.
return First + Second
# A test suite in IronPython.
def __test__():
# Create the object.
MyCalc = DoCalculations()
# Perform the test.
print MyCalc.DoAdd(5, 10)
# Pause after the test session.
raw_input(‘nPress any key to continue…’)
# Execute the test.
# Comment this call out when you finish testing the code.
__test__()
[/code]

The class used for this example is DoCalculations(). It contains a single method, DoAdd(), that returns the sum of two numbers, First and Second. Overall, the class is simple.

The TestClass.py file also contains a __test__() function. This function creates an instance of DoCalculations(), MyCalc. It then prints the result of calling the DoAdd() method with values of 5 and 10. The example waits until you press Enter to exit.

In __main__(), you see a call to __test__(). You can execute the example at the command line, as shown in Figure 15-1. Make sure you use the –D command line switch to place the interpreter in debug mode. You could also open IPY.EXE interactively, load the file, and execute it inside the interpreter. When you know that the code works properly, be sure to comment out the call to __test__() in __main__().

Test the external module before you use it with your application.
Figure 15-1: Test the external module before you use it with your application.

Accessing the Module from C#

Now that you have an external module to use, you’ll probably want to access from an application. This section considers the requirements for accessing IronPython from C#. Don’t worry; the section “Accessing the Module from Visual Basic.NET” later in this chapter discusses access from Visual Basic. NET as well. If you follow these steps, you’ll find that access is relatively straightforward, even if it does get a bit convoluted at times. Microsoft promises the future versions of C# will make dynamic language access even easier.

Adding the Required C# References

Any application you create requires access to the dynamic language assemblies. The IronPython assemblies appear in the Program FilesIronPython 2.6 folder on your machine. Right-click References and choose Add Reference from the content menu to display the Add Reference dialog box. Select the Browse tab. In most cases, you only need the three DLLs shown in Figure 15-2 to access any IronPython script. (You may also need to add the IronPython .Modules.DLL file to the list in some cases.)

Add the required references from your IronPython setup.
Figure 15-2: Add the required references from your IronPython setup.

Select the assemblies you require by Ctrlclicking them in the Add Reference dialog box. Click OK when you’re finished. You’ll see the assemblies added to the References folder in Solution Explorer.

Adding the Required References to the Host Language

You can perform a multitude of tasks with IronPython. In fact, later chapters in the book show how to perform tasks such as testing your static application code. IronPython really is quite flexible. However, most people will start by executing external scripts and only need a few of the namespaces in the IronPython assemblies to do it. The following using statements provide everything needed to execute and manage most IronPython scripts.

[code]
using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
[/code]

Understanding the Use of ScriptEngine

You have many options for working with IronPython scripts. This first example takes an approach that works fine for Visual Studio 2008 developers, as well as those using Visual Studio 2010. It doesn’t require anything fancy and it works reliably for most scripts. Ease and flexibility concerns aside, this isn’t the shortest technique for working with IronPython scripts. This is the Method1 approach to working with IronPython scripts — the technique that nearly everyone can use and it appears in Listing 15-2.

Listin g 15-2: Using the script engine to access the script

[code]
static void Main(string[] args)
{
// Create an engine to access IronPython.
ScriptEngine Eng = Python.CreateEngine();
// Describe where to load the script.
ScriptSource Source = Eng.CreateScriptSourceFromFile(“TestClass.py”);
// Obtain the default scope for executing the script.
ScriptScope Scope = Eng.CreateScope();
// Create an object for performing tasks with the script.
ObjectOperations Ops = Eng.CreateOperations();
// Create the class object.
Source.Execute(Scope);
// Obtain the class object.
Object CalcClass = Scope.GetVariable(“DoCalculations”);
// Create an instance of the class.
Object CalcObj = Ops.Invoke(CalcClass);
// Get the method you want to use from the class instance.
Object AddMe = Ops.GetMember(CalcObj, “DoAdd”);
// Perform the add.
Int32 Result = (Int32)Ops.Invoke(AddMe, 5, 10);
// Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result);
// Pause after running the test.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadKey();
}
[/code]

Now that you have access to Eng, you can use it to perform various tasks. For example, you must tell Eng what scope to use when executing code, so the example creates a ScriptScope object, Scope. In order to perform tasks, you must also have an ObjectOperations object, Ops. The example uses the defaults provided for each of these objects. However, in a production application, you might decide to change some properties to make the application execute faster or with better security.

At this point, you can execute the script. The act of executing the script using Source.Execute() loads the script into memory and compiles it in a form that the static application can use. The Source.Execute() method associates Scope with the execution environment. At this point, the parameters for executing the script are set in stone — you can’t change them.

The script is in memory, but you can’t access any of its features just yet. The script contains a DoCalculations class that you access by calling Scope.GetVariable() to create CalcObj. The code gains access to the class by creating an instance of it, CalcObj, using Ops.Invoke(). At this point, CalcObj contains an instance of DoCalculations() in the IronPython module, but you can’t use it directly. Remember that you must marshal data between C# and IronPython. In addition, C# has to have a way to deal with the potential changes in the IronPython script.

This seems like a lot of work just to gain access to DoAdd, but you can finally use AddMe to perform the addition. A call to Ops.Invoke() with AddMe and the arguments you want to use performs all of the required marshaling for you. You must coerce the output to an Int32 (something that C# understands). Finally, the application outputs the result, as shown in Figure 15-3.

FThe example application calls the DoAdd() method and displays the result onscreen.
Figure 15-3: The example application calls the DoAdd() method and displays the result onscreen.

Using the dynamic Keyword

One of the new ways in which you can access IronPython in C# 4.0 is to use the dynamic keyword. This keyword makes it possible for you to cut out a lot of the code shown in Listing 15-2 to perform tasks with IronPython. It’s still not perfect, but you’ll do a lot less work. Listing 15-3 shows a short example that accesses the __test__() function found in Listing 15-1.

Listin g 15-3: Accessing IronPython using the dynamic keyword

[code]
static void Main(string[] args)
{
// Obtain the runtime.
var IPY = Python.CreateRuntime();
// Create a dynamic object containing the script.
dynamic TestPy = IPY.UseFile(“TestClass.py”);
// Execute the __test__() method.
TestPy.__test__();
}
[/code]

The next step is to load the script. The dynamic type, TestPy, contains all the features of the TestClass.py script after you load it using IPY.UseFile(). Figure 15-4 shows how TestPy appears after the script loads. Notice that the Locals window correctly identifies all the IronPython types in the file.  (Visual Basic.NET developers will have to wait for an update).

In this case, the example calls the __test__() function. This function outputs the same information shown in Figure 15-1.

Loading the script provides access to all of the features it contains.
Figure 15-4: Loading the script provides access to all of the features it contains.

Working with the App.CONFIG File

In some cases, you might want to configure your application using an App.CONFIG file. Using the App.CONFIG file tends to ensure that your application works better between development machines. In addition, using the App.CONFIG file can make it easier to work with DLR using older versions of Visual Studio. Most important of all, using the App.CONFIG file ensures that anyone working with the application uses the correct version of the DLLs so that any DLL differences aren’t a problem.

Your project won’t contain an App.CONFIG file at the outset. To add this file, right-click the project entry in Solution Explorer and choose Add ➪ New Item from the context menu. You see the Add New Item dialog box shown in Figure 15-5. Highlight the Application Configuration File entry as shown and click Add. Visual Studio automatically opens the file for you.

The App.CONFIG file contains entries that describe the Microsoft scripting configuration. In most cases, you begin by defining a <section> element, which describes a <microsoft.scripting> element. The <microsoft.scripting> element contains a list of languages you want to use in a <languages> element, as shown in Listing 15-4.

Listin g 15-4: Defining the App.CONFIG file content

[code]
<?xml version=”1.0” encoding=”utf-8” ?>
<configuration>
<configSections>
<section name=”microsoft.scripting”
type=”Microsoft.Scripting.Hosting.Configuration.Section,
Microsoft.Scripting, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35”
requirePermission=”false” />
</configSections>
<microsoft.scripting>
<languages>
<language names=”IronPython,Python,py”
extensions=”.py”
displayName=”IronPython 2.0 Beta”
type=”IronPython.Runtime.PythonContext,IronPython,
Version=2.6.10920.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35” />
</languages>
</microsoft.scripting>
</configuration>
[/code]

Use an App.CONFIG file to hold DLR configuration information.
Figure 15-5: Use an App.CONFIG file to hold DLR configuration information.

The <section> element includes attributes for name, type, and requirePermission. The type attribute should appear on one line, even though it appears on multiple lines in the book. This attribute describes the Microsoft.Scripting.DLL attributes. Especially important is the Version and PublicKeyToken entries.

The <microsoft.scripting> element contains a <languages> element at a minimum. Within the <languages> element you find individual <language> elements that are descriptions of the languages you want to use in your application.

For this example, you create a <language> element for IronPython that starts with a names attribute. It’s important to define all the names you plan to use to access the language — the example defines three of them. The extensions attribute describes the file extensions associated with the language, which is .py in this case. The displayName attribute simply tells how to display the language. Finally, the type attribute contains a description of the IronPython.DLL file. As with the type element for Microsoft.Scripting.DLL. Again, you need to exercise special care with the Version and PublicKeyToken entries.

Now that you have the App.CONFIG file created, it’s time to look at the application code. Listing 15-5 contains the source for this example.

Listin g 15-5: Using the App.CONFIG file in an application

[code]
static void Main(string[] args)
{
// Read the configuration information from App.CONFIG.
ScriptRuntimeSetup srs = ScriptRuntimeSetup.ReadConfiguration();
// Create a ScriptRuntime object from the configuration
// information.
ScriptRuntime runtime = new ScriptRuntime(srs);
// Create an engine to access IronPython.
ScriptEngine Eng = runtime.GetEngine(“Python”);
// Describe where to load the script.
ScriptSource Source = Eng.CreateScriptSourceFromFile(“TestClass.py”);
// Obtain the default scope for executing the script.
ScriptScope Scope = Eng.CreateScope();
// Create an object for performing tasks with the script.
ObjectOperations Ops = Eng.CreateOperations();
// Create the class object.
Source.Execute(Scope);
// Obtain the class object.
Object CalcClass = Scope.GetVariable(“DoCalculations”);
// Create an instance of the class.
Object CalcObj = Ops.Invoke(CalcClass);
// Get the method you want to use from the class instance.
Object AddMe = Ops.GetMember(CalcObj, “DoAdd”);
// Perform the add.
Int32 Result = (Int32)Ops.Invoke(AddMe, 5, 10);
// Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result);
// Pause after running the test.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadKey();
}
[/code]

The biggest difference between this example and the one shown in Listing 15-2 is that you don’t create the script engine immediately. Rather, the code begins by reading the configuration from the App.CONFIG file using ScriptRuntimeSetup.ReadConfiguration(). This information appears in srs and is used to create a ScriptRuntime object, runtime.

At this point, the code finally creates the ScriptEngine, Eng, as in the previous example. However, instead of using Python.CreateEngine(), this example relies on the runtime.GetEngine() method. For this example, the result is the same, except that you’ve had better control over how the ScriptEngine is created, which is the entire point of the example — exercising control over the IronPython environment. The rest of the example works the same as the example shown in Listing 15-2. The output is the same, as shown in Figure 15-3.

Accessing the Module from Visual Basic.NET

You might get the idea from the lack of Visual Basic.NET examples online that Microsoft has somehow forgotten Visual Basic.NET when it comes to DLR. Surprise! Just because the examples are nowhere to be seen (send me an e‑mail at [email protected] if you find a stash of Visual Basic.NET examples somewhere) doesn’t mean that you can’t work with IronPython from Visual Basic. In fact, the requirements for working with Visual Basic.NET are much the same as those for working with C#, as shown in the following sections.

Adding the Required Visual Basic.NET References

Visual Basic requires the same DLL references as C# does to work with IronPython. Figure 15-2 shows the assemblies you should add to your application to make it work properly. In this case, you right-click the project entry and choose Add Reference from the context menu to display an Add Reference dialog box similar to the one shown in Figure 15-2. Select the Browse tab and add the IronPython assemblies shown in Figure 15-2 by Ctrl-clicking on each of the assembly entries. Click OK. Visual Basic will add the references, but you won’t see them in Solution Explorer unless you click Show All Files at the top of the Solution Explorer window.

As with C#, you need to add some Imports statements to your code to access the various IronPython assemblies with ease. Most applications will require the following Imports statements at a minimum.

[code]
Imports System
Imports IronPython.Hosting
Imports IronPython.Runtime
Imports Microsoft.Scripting.Hosting
[/code]

Creating the Visual Basic.NET Code

As with all the other examples, you shouldn’t let the IronPython example dictate what you do in your own applications. You can obtain full access to any IronPython script from Visual Basic.NET and fully use every feature it provides.

Accessing IronPython scripts from Visual Basic.NET is much the same as accessing them from C# using the ScriptEngine object. Listing 15-6 shows the code you need to access the IronPython script used for all the examples

Listin g 15-6: Accessing IronPython from Visual Basic.NET

[code]
Sub Main()
‘ Create an engine to access IronPython.
Dim Eng As ScriptEngine = Python.CreateEngine()
‘ Describe where to load the script.
Dim Source As ScriptSource = Eng.CreateScriptSourceFromFile(“TestClass.py”)
‘ Obtain the default scope for executing the script.
Dim Scope As ScriptScope = Eng.CreateScope()
‘ Create an object for performing tasks with the script.
Dim Ops As ObjectOperations = Eng.CreateOperations()
‘ Create the class object.
Source.Execute(Scope)
‘ Obtain the class object.
Dim CalcClass As Object = Scope.GetVariable(“DoCalculations”)
‘ Create an instance of the class.
Dim CalcObj As Object = Ops.Invoke(CalcClass)
‘ Get the method you want to use from the class instance.
Dim AddMe As Object = Ops.GetMember(CalcObj, “DoAdd”)
‘ Perform the add.
Dim Result As Int32 = Ops.Invoke(AddMe, 5, 10)
‘ Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result)
‘ Pause after running the test.
Console.WriteLine(vbCrLf + “Press any key when ready…”)
Console.ReadKey()
End Sub
[/code]

As you can see from the listing, Visual Basic.NET code uses precisely the same process as C# does to access IronPython scripts. In fact, you should compare this listing to the content of Listing 15-2. The two examples are similar so that you can compare them. The output is also precisely the same. You’ll see the output shown in Figure 15-3 when you execute this example.

Developing Test Procedures for External Modules

Many developers are beginning to realize the benefits of extensive application testing. There are entire product categories devoted to the testing process now because testing is so important. Most, if not all, developer tools now include some idea of application testing with them. In short, you should have all the testing tools you need to test the static portion of your IronPython application.

Unfortunately, the testing tools might not work particularly well with the dynamic portion of the application. Creating a test that goes from the static portion of the application to the dynamic portion of the application is hard. (Consequently, you need to include a test harness with your dynamic code and perform thorough testing of the dynamic code before you use it with the static application. (When you think about a test harness, think about a horse, your application that has a harness added externally for testing purposes. You add the harness for testing and remove it for production work without modifying the application.) Listing 15-1 shows an example of how you might perform this task.

The test harness you create has to test everything, which is a daunting task to say the least. In addition, you need to expend extra effort to make the test harness error free — nothing would be worse than to chase an error through your code, only to find out that the error is in the test harness. At a minimum, your test harness should perform the following checks on your dynamic code:

  • Outputs with good inputs
  • Outputs with erroneous inputs
  • Exception handling within methods
  • Property value handling
  • Exceptions that occur on public members that would normally be private

Of course, you want to check every method and property of every class within the dynamic code. To ensure you actually test everything, make sure you create a checklist to use to verify your test harness. Because IronPython isn’t compiled, you’ll find that you must manually perform some checks to ensure the code works precisely as planned, but use as much automation as possible.

Debugging the External Module

Debugging isn’t hard, but it also isn’t as straightforward as you might think when working with IronPython. The debugger won’t take you directly to an error. You can’t test variables using the debugger from within the static language. In short, you have to poke and prod the external script to discover what ails it. Fortunately, you do have three tools at your disposal for discovering errors.

  • Exceptions
  • print Statements
  • An ErrorListener object

Let’s begin with the easiest of the three tools. The static language application won’t ignore outright errors in the script code. For example, you might have the following error in the script:

[code]
# Introduce an error.
print 1/0
[/code]

If your code has this error (and it really shouldn’t), you’ll see an exception dialog box like the one shown in Figure 15-6. Unfortunately, when you click View Detail, the content of the View Detail dialog box is nearly useless. The exception information won’t tell you where to find the error in your script. In fact, it may very well lead you on a wild goose chase that ends in frustration.

The static language application displays exceptions for your script.
Figure 15-6: The static language application displays exceptions for your script.

The name of the exception will provide clues as to where the error might exist, but you can’t confirm your suspicions without help. The only tool, besides vigorous script testing, is to include print statements such as these in your code.

[code]
# Display the values of First and Second.
print ‘Values in IronPython Script’
print ‘First = ‘, First
print ‘Second = ‘, Second
[/code]

When you run the script, you see the output shown in Figure 15-7. Most developers view print statements as a bit old school, but they do work if you use them correctly. Make sure you provide enough information to know where the script is failing to perform as expected. Even so, using print statements may feel a bit like wandering around in the dark, so you should place an emphasis on testing the script before you use it and after each change you make.

Using print statements may seem old school, but they work.
Figure 15-7: Using print statements may seem old school, but they work.

In some cases, you might make a small change to a script and it stops running completely — you might not see a script exception, just an indicator that something’s wrong because the application raises an unrelated exception. Syntax errors and other problems where the interpreter simply fails can cause the developer a lot of woe. For example, your application might have the following syntax error:

[code]
# Create a syntax error.
while True print ‘This is an error!’
[/code]

This code obviously won’t run. Because of the nature of the error, you might even pass it by while looking through your code. The answer to this problem is to create an ErrorListener class like the one shown in Listing 15-7.

Listin g 15-7: Create an ErrorListener to hear script semantic errors

[code]
class MyListener : ErrorListener
{
public override void ErrorReported(ScriptSource source,
string message,
SourceSpan span,
int errorCode,
Severity severity)
{
Console.WriteLine(“Script Error {0}: {1}“, errorCode, message);
Console.WriteLine(“Source: {0}“, source.GetCodeLine(span.Start.Line));
Console.WriteLine(“Severity: {0}“, severity.ToString());
}
}
[/code]

The ErrorListener contains just one method, ErrorReported(). This method can contain anything you need to diagnose errors. The example provides an adequate amount of information for most needs. However, you might decide to provide additional information based on the kind of script you’re using.

In order to use this approach, you must compile the script before you execute it. The compilation process must include the ErrorListener, as shown here.

[code]
// Compile the script.
Source.Compile(new MyListener());
[/code]

When you run the application now, you get some useful information about the syntax error, as shown in Figure 15-8.

The ErrorListener provides useful output on syntax errors.
Figure 15-8: The ErrorListener provides useful output on syntax errors.

 

Interacting with the DLR

The Dynamic Language Runtime (DLR) is a new feature of the .NET platform. Its intended purpose is to support dynamic languages, such as Python (through IronPython) and Ruby (through IronRuby). Without DLR, the .NET Framework can’t really run dynamic languages. In addition, DLR provides interoperability between dynamic languages, the .NET Framework, and static languages such as C# and Visual Basic.NET. Without DLR, dynamic and static languages can’t communicate. In order to meet these goals, DLR must provide basic functionality that marshals data and code calls between the dynamic and static environments. This functionality comes in a number of forms that are discussed in this chapter. You might be surprised to find that you’ve already used many of these features throughout the book. Here’s the list of features that DLR supports in order to accomplish its goals.

  • Hosting Application Programming Interfaces (APIs): In order to run dynamic language scripts, the host language must have access to the scripting engine. The Hosting APIs provide the support needed to host the dynamic language within the host environment through the scripting engine. This marshaling of code and data makes it possible to seamlessly integrate static and dynamic languages.
  • Extensions to Language Integrated Query (LINQ) ExpressionTree: Normally, a language would need to convert data, objects, and code into Microsoft Intermediate Language (MSIL) before it could translate anything into another language. Because all .NET languages eventually end up as MSIL, MSIL is the common language for all higher-level .NET languages. These extensions make it possible for language compilers to create higher-level constructs for communication purposes, rather than always relying on MSIL. The result is that the marshaling process takes less time and the application runs faster.
  • DynamicSite: This feature provides a call-site cache that dynamic languages use in place of making constant calls to other .NET languages. Because the call-site cache is already in a form that the dynamic language can use, the overall speed of the dynamic language application improves.
  • IDynamicObject: An interface used to interact with dynamic objects directly. If you create a class that implements IDynamicObject, DLR lets the class perform the required conversions, rather than rely on the built-in functionality. Essentially, you create an object that can have methods, properties, and events added dynamically during run time. You use IDynamicObject when you want to implement custom behaviors in your class.
  • ActionBinder: The ActionBinder is a utility that helps support .NET interoperability. The ActionBinder is language specific. It ensures that conversions of variable data, return values, and arguments all follow language-specific behaviors so that the host language sees the data in a form it understands.

These are the main tasks that DLR performs. Of course, it also provides other compiler utilities that you need to know about. The final section in this chapter provides an overview of these other features.

DLR is a constantly changing technology today, so you’ll want to keep up with the additions and changes to DLR. One of the better places to find general DLR resources online is at http://blogs.msdn.com/ironpython/ archive/2008/03/16/dlr-resources.aspx. This chapter also provides a number of specific resources you can use to discover more about DLR. The point is to keep track of what’s going on with this exciting technology and review your code as things change.

Obtaining DLR

It’s important to remember that IronPython relies on DLR to perform just about every task that IronPython executes. Therefore, you already have access to a certain level of DLR, even if you don’t install anything or do anything special. In fact, you’re using DLR in the background every time you use IronPython. However, you’re using DLR without really knowing it exists and without understanding what DLR itself can do for your application. So while you can use the direct approach to DLR, it can prove frustrating and less than friendly.

In order to truly understand DLR, you at least need documentation. Better yet, you can download the entire DLR package and begin to understand the true impact of this product. If nothing else, spend some time viewing the available components at http://www.codeplex.com/dlr. The following sections describe various methods of gaining access to DLR so you can use it to perform some custom tasks.

Using the Direct Method

The direct method is the easiest way to obtain the benefits of DLR, but it’s also the most limited. You simply add a reference to the IronPython.DLL file located in the Program FilesIronPython 2.6 folder of your hard drive. This technique works fine for embedding IronPython scripts in your C# or Visual Basic.NET application. In fact, you gain access to the following classes:

  • IronPython
  • IronPython.Compiler
  • IronPython.Compiler.Ast
  • IronPython.Hosting
  • IronPython.Modules
  • IronPython.Runtime
  • IronPython.Runtime.Binding
  • IronPython.Runtime.Exceptions
  • IronPython.Runtime.Operations
  • IronPython.Runtime.Types

For many developers, this is all the DLR support you need, especially if your application only requires cross-language support through the Hosting APIs. (You’ll still want to download the documentation that’s available on the main DLR Web site — the section “Downloading the Documentation” later in this chapter explains how to perform this task.) The following steps describe how to add the required reference to gain access to these classes.

  1. Create the .NET project.
  2. Right-click References in Solution Explorer and choose Add Reference from the context menu. You see the Add Reference dialog box.
  3. Select the Browse tab and locate the IronPython.DLL file, as shown in Figure 14-1.
  4. Click OK. Visual Studio adds the required reference to your project.
Add the IronPython.DLL file to your project.
Figure 14-1: Add the IronPython.DLL file to your project.

You make use of IronPython.DLL as you would any other .NET assembly. Simply add the required use or Imports statement to your code. The examples throughout the book tell you about these requirements for the individual example.

Downloading the Full DLR

If you really want to experience DLR, you need the complete package. The full DLR consists of a number of components and even the source code weighs in at a hefty 10.5 MB.

Before you begin the download, check out the release notes at http://dlr.codeplex.com/ wikipage?title=0.92_release_notes for additional information about DLR. For example, you might decide to get an IronPython- or IronRuby-specific download. The full release includes both language products (which can be helpful, even if you use only one of them).

You obtain the full DLR from http://dlr.codeplex.com/Release/ProjectReleases .aspx?ReleaseId=34834. When you click the DLR-0.92-Src.zip link, you see a licensing dialog box. Click I Agree to begin the download process.

After the download completes, extract the resulting DLR-0.92-Src.ZIP file into its own folder. The resulting Codeplex-DLR-0.92 folder contains the following items.

  • License.HTML and License.RTF: You can read the same licensing information in two different formats. Use whichever form works best for you.
  • Docs: A folder containing the complete documentation for DLR. The best place to begin is the DLR-Overview.DOC file.
  • Samples: A folder containing a number of sample applications that demonstrate DLR features. There’s only one IronPython sample in the whole batch — you’ll find it in the Codeplex-DLR-0.92SamplesSilverlightAppPythonpython folder.
  • Source: A folder that contains the complete DLR source code that you need to compile in order to use DLR to create applications. This folder should be your first stop after you read the DLR-Overview.DOC file.

Building the Full DLR

Before you can use DLR, you must build it. The previous section explains how to download a copy of the DLR source. The following sections describe three methods you can use to build DLR. For most developers, the easiest and fastest method is the command line build. However, if you want to review the code before you use it, you might want to load the solution in Visual Studio and take a peek.

Performing a Command Line Build

The command line build option requires that you use the Visual Studio command line, not a standard command line (which doesn’t contain a path to the utilities you need). The following steps describe how to perform the command line build:

  1. Choose Start ➪ Programs ➪ Microsoft Visual Studio 2008 ➪ Visual Studio Tools ➪ Visual Studio 2008 Command Prompt or Start ➪ Programs ➪ Microsoft Visual Studio 2010 ➪ ➤ Visual Studio Tools ➪ Visual Studio Command Prompt (2010). You’ll see a command prompt.
  2. Type CD Codeplex-DLR-0.92Src and press Enter. This command places you in the DLR source code directory.
  3. Type MSBuild Codeplex-DLR.SLN (when using Visual Studio 2008) or MSBuild Codeplex-DLR-Dev10.SLN (when using Visual Studio 2010) and press Enter. By default, you get a debug build. Use the /p:Configuration=Release command line switch (as in MSBuild Codeplex-DLR.SLN /p:Configuration=Release or MSBuild Codeplex-DLR-Dev10.SLN /p:Configuration=Release) to obtain a release build. You see a lot of text appear onscreen as MSBuild creates the DLR DLLs for you. Some of the text will appear unreadable (Microsoft uses some odd color combinations). When the build process is complete, you should see 0 Error(s) as the output, along with a build time, as shown in Figure 14-2. (If you don’t see a 0 error output, you should probably download the files again because there is probably an error in the files you downloaded.)
The build process should show 0 Error(s) as the output message.
Figure 14-2: The build process should show 0 Error(s) as the output message.

Don’t look for the output in the source code folders. The output from the build process appears in the Codeplex-DLR-0.92Bin40 folder when working with Visual Studio 2010, no matter which technique you use to build DLR. Visual Studio 2008 developers will find their output in the Codeplex-DLR-0.92 BinDebug or Codeplex-DLR-0.92BinRelease folders, depending on the kind of build created. Visual Studio 2008 developers will also find a separate Codeplex-DLR-0.92BinSilverlight Debug or Codeplex-DLR-0.92Bin Silverlight Release folder for Silverlight use.

Performing a Visual Studio 2008 Build

Some developers will want to perform a build from within Visual Studio 2008. To perform this task, simply double-click the Codeplex-DLR.SLN icon in the Codeplex-DLR-0.92Src folder. Choose Build ➪ Build Solution or press Ctrl+Shift+B. You’ll see a series of messages in the Output window. When the process is complete, you should see, “Build: 23 succeeded or up-to-date, 0 failed, 1 skipped” as the output.

You must select each of the options in the Solution Configurations combo box in turn and perform a build to create a complete setup. Otherwise, you’ll end up with just the Release build or just the Debug build. If you need Silverlight or FxCop support, you must also create these builds individually.

Don’t worry if you see a number of messages stating

[code]
Project file contains ToolsVersion=”4.0”, which is not supported by this
version of MSBuild. Treating the project as if it had ToolsVersion=”3.5”.
[/code]

because this is normal when using Visual Studio 2008. You’ll also see a number of warning messages (a total of 59 for the current DLR build) in the Errors window, which you can ignore when using the current release.

Performing a Visual Studio 2010 Build

A release version of DLR will build better if you have a copy of Visual Studio 2010 on your system. To perform this task, simply double-click the Codeplex-DLR-Dev10.SLN icon in the Codeplex-DLR-0.92Src folder. Set the Solution Configurations option to Release or Debug as needed (there aren’t any options to build Silverlight or FxCop output). Choose Build ➪ Build Solution or press Ctrl+Shift+B. You’ll see a series of messages in the Output window. When the process is complete, you should see, “Build: 15 succeeded or up-to-date, 0 failed, 2 skipped” as the output. The Warnings tab of the Error List window should show 24 warnings.

Downloading the Documentation

The download you performed earlier provides code and documentation, but you might find that the documentation is outdated. As with everything else about DLR, the documentation is in a constant state of flux. If you want to use DLR directly, then you need the documentation found at http://dlr.codeplex.com/wikipage?title=Docs and specs&referringTitle=Home. Unfortunately, you have to download each document separately

Reporting Bugs and Other Issues

At some point, you’ll run into something that doesn’t work as expected. Of course, this problem even occurs with production code, but you’ll definitely run into problems when using the current release of DLR. In this case, check out the listing of issues at http://www.codeplex.com/dlr/WorkItem/List.aspx. If you don’t find an issue entry that matches the problem you’re experiencing, make sure you report the bug online so it gets fixed. Of course, reporting applies equally to code and documentation. Documentation errors are often harder to find and fix than coding errors — at least where developers are concerned — because it’s easier to see the coding error in many cases.

Working with Hosting APIs

In fact, you may have even wondered whether it’s possible to use IronPython as a scripting language for your next application. Fortunately, you can use IronPython as the scripting language for your next application by relying on the Hosting APIs. It turns out that a lot of people have considered IronPython an optimal language for the task. The following sections consider a number of Hosting API questions, such as how you can use it in an actual application, what the host application needs in order to use the Hosting APIs, and what you’d need to do to embed IronPython as a scripting language in an application.

Using the Hosting APIs

The DLR specification lists a number of hosting scenarios, such as operating on dynamic objects you create within C# or Visual Basic.NET applications. (See the section “Working with IDynamicObject” later in this chapter for details on dynamic objects in C# and Visual Basic.NET.) It’s also possible to use the Hosting APIs to create a scripting environment within Silverlight or other types of Web applications.

Whatever sort of host environment you create, you can use it to execute code snippets or entire applications found in files. The script run time can appear locally or within a remote application so you can use this functionality to create agent applications or scripting that relies on server support. The Hosting APIs make it possible to choose a specific scripting engine to execute the code or to let DLR choose the most appropriate scripting engine for the task. This second option might seem foolhardy, but it can actually let your code use the most recent scripting engine, even if that engine wasn’t available at the time you wrote the host environment code.

Chaos could result if you couldn’t control the extent (range) of the script execution in some way. For example, two developers could create variables with the same name in different areas of the application. The Hosting APIs make it possible to add scope to script execution. The scope acts much like a namespace does when writing code. Just as a namespace eliminates naming conflicts in assemblies, scoping eliminates them in the scripting environment. Executing the code within a scope also provides it with an execution context (controlled using a ScriptScope). Scopes are either public or private, with private scopes providing a measure of protection for the scripting environment. A script can also import scopes for use within the environment or require the host to support a certain scope to execute the script.

The Hosting APIs also provide support for other functionality. For example, you can employ reflection to obtain information about object members, obtain parameter information, and view documentation. You can also control how the scripting engine resolves file content when dynamic languages import code files.

Understanding the Hosting APIs Usage Levels

The DLR documentation specifies that most developers will use the Hosting APIs at one of three levels that are dictated by application requirements. Here are the three basic levels.

  • Basic code: The basic code level (Level 1 in the documentation) relies on a few basic types to execute code within scopes. The code can interact with variable bindings within those scopes.
  • Advanced code execution: The next level (Level 2 in the documentation) adds intermediate types that provide additional control over how code executes. In addition, this level supports using compiled code in various scopes and permits use of various code sources.
  • Support overrides: The final level (Level 3 in the documentation) provides methods to override how DLR resolves filenames. The application can also use custom source content readers, reflect over objects for design-time tool support, provide late bound variable values from the host, and use remote ScriptRuntime objects.

The concept of a ScriptRuntime object is central to working with the Hosting APIs. A host always begins a session by creating the ScriptRuntime object and then using that object to perform tasks. You can create a ScriptRuntime object using several methods. Of course, the easiest method is to use the standard constructor, which requires a ScriptRuntimeSetup object as input. It’s also possible to create a ScriptRuntime object using these methods

  • ScriptRuntime.CreateFromConfiguration(): A factory method that lets you use a preconfigured scope to create the ScriptRuntime object. In fact, this factor method is just short for new ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration()).
  • ScriptRuntime.CreateRemote(): A factory method that helps you to create a ScriptRuntime object in another domain. The code must meet strict requirements to perform remote execution. See Section 4.1.3, “Create* Methods,” in the Hosting APIs specification for details.

At its name implies, a ScriptRuntimeSetup object gives a host full control over the ScriptRuntime object configuration. The ScriptRuntimeSetup object contains settings for debug mode, private execution, the host type, host arguments, and other setup features. Simply creating a ScriptRuntimeSetup object sets the defaults for executing a script. Once you use a ScriptRuntimeSetup object to create a ScriptRuntime object, you can’t change the settings — doing so will raise an exception.

The Hosting APIs actually support a number of objects that you use to create a scripting environment, load the code you want to execute, and control the execution process. The figure at http:// www.flickr.com/photos/john_lam/2220796647/ provides an overview of these objects and how you normally use them within the hosting session.

It’s important to isolate code during execution. The Hosting APIs provide three levels of isolation.

  • AppDomain: The highest isolation level, which affects the entire application. The AppDomain lets you execute code at different trust levels, and load and unload code as needed.
  • ScriptRuntime: Every AppDomain can have multiple ScriptRuntimes within it. Each ScriptRuntime object can have different name bindings, use different .NET assemblies, have different settings (one can be in debug mode, while another might not), and provide other settings and options support.
  • ScriptScope: Every ScriptRuntime can contain multiple ScriptScopes. A ScriptScope can provide variable binding isolation. In addition, you can use a ScriptScope to give executable code specific permissions.

Now that you have a better idea of how the pieces fit together, it’s important to consider which pieces you use to embed scripting support within an application. Generally, if you want basic code (Level 1) support, all you need are the objects shown in green at http://www.flickr.com/photos/ john_lam/2220796647/. In fact, if you want to use the default ScriptScope settings, all you really need to do is create the ScriptRuntime and then use the default ScriptScope.

Considering the Host Application

A host has to meet specific requirements before it can run IronPython as a scripting language. Chapter 15 discusses more of the details for C# and Visual Basic.NET developers. You’ll find that C# and Visual Basic.NET provide everything you need. However, it’s interesting to see just what the requirements are, especially if you’re using an older version of these languages. Section 3 of the DLR-Spec-Hosting.DOC file found in the Codeplex-DLR-0.92Docs folder contains complete information about the hosting requirements. Section 3.3 (and associated subsections) are especially important for most developers to read if they plan to use the Hosting APIs for anything special.

Embedding IronPython as a Scripting Language

Imagine that you’ve created a custom editor in your application where users can write IronPython scripts. They then save the script to disk (or you could read it from memory), and then they assign the script to a button or menu in your application. When the user selects the button or menu, your application executes the script. Creating this scenario isn’t as hard as you might imagine. DLR comes with most of the functionality you need built in.

Of course, you need a test script to start. Listing 14-1 shows the test script for this example. The example is purposely simple so that the example doesn’t become more focused on the IronPython code than the code that executes it. However, you could easily use any script you want as long as it’s a legitimate IronPython script.

Listin g 14-1: A simple IronPython script to execute

[code]
# A simple function call.
def mult(a, b):
return a * b
# Create a variable to hold the output.
Output = mult(5,10)
# Display the output.
print(‘5 * 10 =’),
print(Output)
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

In this case, the example has a simple function, mult(), that multiplies two numbers together. The __main__() part of the script multiplies two numbers and displays the result using the print() function. In short, the script isn’t very complicated.

Now that you have a script, you need to create an application to execute it. The example is a simple console application. In order to create the IronPython ScriptRuntime object, you need access to some of the IronPython assemblies. Right-click References in Solution Explorer and choose Add Reference from the context menu. You see the Add Reference dialog box shown in Figure 14-3. Ctrl+click each of the entries shown in Figure 14-3, then click OK to add them to your project.

Add the required references from your IronPython setup.
Figure 14-3: Add the required references from your IronPython setup.

The example also requires that you add using statements for a number of the assemblies. Here are the using statements that you must add for this example.

[code]
using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
[/code]

Now that the console project is set up, you can begin coding it. This example is very simple, but it actually works. You can execute an IronPython script using this technique. Of course, you can’t interact with it much. Chapter 15 provides more detailed examples, but this example is a good starting place. Listing 14-2 shows the minimum code you need to execute an IronPython script and display the result of executing it onscreen.

Listin g 14-2: Executing the IronPython script

[code]
static void Main(string[] args)
{
// Create an IronPython ScriptRuntime.
ScriptRuntime Runtime = IronPython.Hosting.Python.CreateRuntime();
// Execute the script file and return scope information about
// the task.
ScriptScope Scope = Runtime.ExecuteFile(“Test.py”);
// Display the name of the file executed.
Console.WriteLine(“rnExecuted {0}“,
Scope.GetVariable<string>(“__name__“));
// Keep the output visible.
Console.WriteLine(“rnPress any key…”);
Console.ReadLine();
}
[/code]

The code begins by creating the ScriptRuntime object, Runtime. Notice that you create this object by directly accessing the IronPython assemblies, rather than the DLR assemblies. There are many ways to accomplish this task, but using the technique shown is the simplest. The Runtime object contains default settings for everything. For example, this ScriptRuntime doesn’t provide debugging capability. Consequently, this technique is only useful when you have a debugged script to work with and may not do everything needed in a production environment where you let users execute their own scripts as part of an application.

The Runtime.ExecuteFile() method is just one of several ways to execute a script. You use it when a script appears in a file on disk, as is the case for this example. When you call the Runtime .ExecuteFile() method, your application actually calls on the IronPython interpreter to execute the code. The output from the script appears in Figure 14-4. As you can see, the code executes as you expect without any interference from the host. In fact, you can’t even tell that the application has a host.

The script output appears as you might expect.
Figure 14-4: The script output appears as you might expect.

When the Runtime.ExecuteFile() method call returns, the C# application that executed the script receives a ScriptScope object that it can use to interact with the application in various ways. This ScriptScope object, like the ScriptRuntime object, contains all the usual defaults. It’s a good idea to examine both Runtime and Scope in the debugger to see what these objects contain because you’ll find useful information in both.

The script is running in a host application. In fact, they share the same console window. To show how this works, the example writes output to the console window. It retrieves the __name__ property from Scope and displays it onscreen with the message, as shown in Figure 14-5. The point of this example is that the IronPython script truly is hosted and not running on its own. The technique shown here lets you perform simple interactions between C# or Visual Basic.NET and IronPython.

The output shows that the host and the IronPython environment share the same console.
Figure 14-5: The output shows that the host and the IronPython environment share the same console.

Understanding the Extensions to LINQ Expression Tree

Part of the premise behind DLR is that every .NET language eventually ends up in Microsoft Intermediate Language (MSIL) form. Whether you use C# or Visual Basic.NET, or even managed C++, the output from the compiler is MSIL. That’s how the various languages can get along. They rely on MSIL as an intermediary so that managed languages can work together.

The problem with compiling everything to MSIL is that MSIL doesn’t necessarily perform tasks quickly or easily when working with dynamic languages such as IronPython. It would be far easier if there were a mechanism for translating the code directly into something that C# or Visual Basic .NET could use. That’s where the LINQ Expression Tree (ET) comes into play. A LINQ ET can represent IronPython or other code (such as JavaScript) in a tree form that DLR can then translate into something that C# or Visual Basic.NET can understand. The result is a DLR tree that presents the code in an easily analyzable and mutable form. The example at http://blogs.msdn.com/hugunin/ archive/2007/05/15/dlr-trees-part-1.aspx explains how DRL trees work graphically. In this case, the author explains how a DLR tree can represent a JavaScript application — the same technique also applies to IronPython.

The LINQ ET originally appeared in the .NET Framework 3.5. In its original form, Microsoft used the LINQ ET to model LINQ expressions written in C# and Visual Basic.NET. In the .NET Framework 4.0, Microsoft added extensions for a number of reasons. For the purposes of this book, the most important reason to extend LINQ ETs is to accommodate the DLR semantics used to translate IronPython code into something that C# and Visual Basic.NET can understand.

DLR trees work in the background. It’s helpful to know they exist, but you generally won’t worry about them when working with IronPython so this section is short. However, let’s say you want to create a scripting language for your application that isn’t as complex as IronPython. Perhaps you want to implement an editor and everything that goes with it in your application. In this case, you may very well want to work with DLR trees. The examples found at http://weblogs.asp.net/ podwysocki/archive/2008/02/08/adventures-in-compilers-building-on-the-dlr.aspx show what you need to do to create your own language compiler. Once you have a compiler like this built, you could execute the code using a technique similar to the one shown in Listing 14-2.

It’s important to consider one word of warning, however, when working with the current version of DLR trees. As you scan through the specification, you’ll find that the authors have left behind copious notes about issues that aren’t resolved now or features that were left out of the current implementation due to a lack of time. The result is conversations such as the one at http://stackoverflow.com/ questions/250377/are-linq-expression-trees-turing-complete. If you look at section 2.4.1 of the specification, you find that a higher-level looping mechanism was indeed cut, but Microsoft is aware of the problem and plans to implement the feature in the future. In short, DLR trees have limits that you need to consider before implementing them in your application.

Considering DynamicSite

When working with a static language such as C# or Visual Basic.NET, the compiler knows what to emit in the form of MSIL based on the code the developer provides. However, dynamic code isn’t static — it can change based on any of a number of factors. One problem with dynamic languages is that DLR doesn’t always know what to emit during compile time because the real time event hasn’t occurred yet. Of course, the static language still needs some code in place because static languages need to know what to do at compile time. This seeming conundrum is handled by invoking a DynamicSite object. Using a DynamicSite object means that the static language knows what to call at compile time and DLR can fill the DynamicSite object with executable code during run time.

As with many parts of DLR, the action takes place behind the scenes — you don’t even know it occurs. However, it’s useful to know what happens so you at least know what to suspect when an error occurs. The act of invoking the DynamicSite method creates an operation to perform and a delegate. The delegate contains caching logic that is updated every time the arguments change. In short, as the dynamic language changes, DLR generates events that change the content of the cache as well.

At the center of working with DynamicSite is the UpdateBindingAndInvoke() method. The first time that application code calls the DynamicSite object, the UpdateBindingAndInvoke() method queries the arguments for the specified code. For example, the code might be something simple such as x + y, so the query would request the types of x and y. At this point, UpdateBindingAndInvoke() generates a delegate that contains the implementation of the code.

The next time the application invokes the DynamicSite object, the delegate checks the arguments in the call against those in the cache. If the argument types match, then the delegate simply uses the current implementation of the code. However, if the arguments are different, then the delegate calls UpdateBindingAndInvoke(), which creates a new delegate that contains a definition of the new code with the updated arguments. The new delegate contains checks for both sets of argument types and calls the appropriate implementation based on the arguments it receives. Of course, if none of the argument sets match the call, then the process starts over again with a call to UpdateBindingAndInvoke().

Working with IDynamicObject

This section discusses the IDynamicObject interface provided as part of DLR, which doesn’t affect IronPython directly, but could affect how you use other languages to interact with IronPython. You can easily skip this section and leave it for later reading if you plan to work exclusively with IronPython for the time being. This is a very short discussion of the topic that is meant to fill in the information you have about DLR and its use with IronPython.

As mentioned throughout the book, C# and Visual Basic.NET are both static languages. Microsoft doesn’t appear to have any desire to change this situation in upcoming versions of either language. Consequently, you can’t create dynamic types using C# or Visual Basic. There isn’t any technique for defining missing methods or dynamic classes using either language. However, you can consume dynamic types defined using a new interface, IDynamicObject.

The IDynamicObject interface tells DLR that the class knows how to dispatch operations on itself. In some respects, IDynamicInterface is a kind of managed form of the IQueryable interface that C++ developers use when creating COM objects. The concept isn’t new, but the implementation of it in the .NET environment is new.

There are many levels of complexity that you can build into your dynamic implementation. The example in this section is a very simple shell that you can build on when creating a fullfledged application. It’s designed to show a common implementation that you might use in an application. You can see another simple example at http://blogs.msdn.com/csharpfaq/ archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx.

The starting point for this example is a class that implements DynamicObject. In order to create such a class, you need to include the following using statements:

[code]
using System;
using System.Dynamic;
[/code]

The class is called ADynamicObject and appears in Listing 14-3.

Listin g 14-3: Creating a class to handle dynamic objects

[code]
// Any dynamic object you create must implement IDynamicObject.
public class ADynamicObject : DynamicObject
{
// Calls a method provided with the dynamic object.
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args, out object result)
{
Console.WriteLine(“InvokeMember of method {0}.”, binder.Name);
if (args.Length > 0)
{
Console.WriteLine(“tMethod call has {0} arguments.”, args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine(“ttArgument {0} is {1}.”, i, args[i]);
}
result = binder.Name;
return true;
}
// Gets the property value.
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
Console.WriteLine(“GetMember of property {0}.”, binder.Name);
result = binder.Name;
return true;
}
// Sets the property value.
public override bool TrySetMember(SetMemberBinder binder, object value)
Console.WriteLine(“SetMember of property {0} to {1}.”,
binder.Name, value);
return true;
}
}
[/code]

In this case, the code provides the ability to call methods, get property values, and set property values. Amazingly, DLR automatically calls the correct method without any hints from you.

Notice that each of the methods uses a different binder class: InvokeMemberBinder, GetMemberBinder, or SetMemberBinder as needed. The binder provides you with information about the member of interest. In most cases, you use the member name to locate the member within the dynamic object. In this case, the code simply displays the member name onscreen so you can see that the code called the correct member.

Two of these methods, TryInvokeMember() and TryGetMember(), return something to the caller. It’s important to remember that the data is marshaled, so you must use the out keyword for the argument that returns a value or the application will complain later (the compiler may very well accept the error without comment). In both cases, the code simply returns the binder.Name value. If you were building this dynamic object class for an application, you’d use the binder.Name value to access the actual property or method.

When invoking a method, the TryInvokeMember() method receives an array of arguments to use with the method call. The code shows how you detect the presence of arguments and then displays them onscreen for this example. In an actual application, you’d need to compare the arguments provided by the caller against those required by the method to ensure the caller has supplied enough arguments of the right type.

All three methods return true. If the code were to return false instead, you’d see a RuntimeBinderException in the caller code. This exception tells the caller that the requested method or property doesn’t exist.

When a C# application desires to create a dynamic object, it simply creates an instance of the dynamic class. The instance can create properties, methods, or other constructs as needed. Listing 14-4 shows an example of how a test application might appear.

Listin g 14-4: Using the ADynamicObject class

[code]
class Test
{
static void Main()
{
// Create a new dynamic object.
dynamic DynObject = new ADynamicObject();
// Set a property to a specific value.
Console.WriteLine(“Setting a Property to a Value”);
DynObject.AProp = 5;
// Use one property to set another property.
// You would see a property get, followed by a property set.
Console.WriteLine(“rnSetting a Property to another Property”);
DynObject.Prop1 = DynObject.AProp;
// Call a method and set its output to a property.
// You would see a method call, followed by a property set.
Console.WriteLine(“rnSetting a Property to a Method Output”);
DynObject.Prop2 = DynObject.AMethod();
// Call a method with a property argument and set a new property.
// You would see a property get, a method call, and finally a
// property set.
Console.WriteLine(“rnSetting a Property to Method Output with Args”);
DynObject.Prop3 = DynObject.AMethod(DynObject.AProp);
// Wait to see the results.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadLine();
}
}
[/code]

Notice that the code begins by creating a new dynamic object using the dynamic keyword. At this point, you can begin adding properties and methods to the resulting DynObject. Properties can receive values directly, from other properties, or from methods. Methods can use arguments to change their output. Figure 14-6 shows the output from this example. The path that the code takes through the various objects helps you understand how dynamic objects work.

The DynamicObject class actually provides support for a number of members. You can use these members to provide a complete dynamic implementation for your application. Here’s a list of the DynamicObject members you can override.

  • GetDynamicMemberNames()
  • GetMetaObject()
  • TryBinaryOperation()
  • TryConvert()
  • TryDeleteIndex()
  • TryDeleteMember()
  • TryGetIndex()
  • TryGetMember()
  • TryInvoke()
  • TryInvokeMember()
  • TrySetIndex()
  • TrySetMember()
  • TryUnaryOperation()
The output shows the process used to work with dynamic objects.
Figure 14-6: The output shows the process used to work with dynamic objects.

The point of all this is that you can implement a kind of dynamic object strategy for static languages, but it’s cumbersome compared to IronPython. You might use this approach when you need to provide a dynamic strategy for something small within C# or Visual Basic. This technique is also useful for understanding how IronPython works, at a very basic level. IronPython is far more robust than the code shown in this example, but the theory is the same.

Understanding the ActionBinder

DLR makes it possible to invoke dynamic code from within a static environment using a DynamicSite object. The actual process for creating the method invocation call is to create an Abstract Syntax Tree (AST). The AST has functions assigned to it using an Assign() method. When DLR wants to assign a new function to AST, it supplies a function name and provides a calling syntax using the Call() method. The Call() method accepts four arguments.

  • An object used to hold the function. Normally, the code calls the Create() method of the host class using GetMethod(“Create”).
  • A constant containing the name of the function as it appears within the host object.
  • The array of arguments supplied to the function.
  • A delegate instance used to invoke the code later. It’s this argument that you consider when working with an ActionBinder.

At this point, you have an object that holds the parameters of the function call, as well as a delegate used to execute the function. The problem now is one of determining how to call the function. After all, the rest of your code knows nothing about the delegate if you create it during run time, as is the case when working with dynamic languages. If none of the code knows about the delegate, there must be some way to call it other than directly.

To make rules work, your code has to include a GetRule() method that returns a StandardRule object. Inside GetRule() is a switch that selects an action based on the kind of action that DLR requests, such as a call (DynamicActionKind.Call). When DLR makes this request, the code creates a StandardRule object that contains an ActionBinder. The ActionBinder determines what kind of action the call performs. For example, you might decide that the ActionBinder should be LanguageContext.Binder, which defines a language context for the function. The language context is a definition of the language’s properties, such as its name, identifier, version, and specialized features. (You can learn more about how a language context works at http://www.dotnetguru .org/us/dlrus/DLR2.htm.) The code then calls SetCallRule() with the StandardRule object, the ActionBinder, and a list of arguments for the function.

Now, here’s the important consideration for this section. The ActionBinder is actually part of the language design. If you wanted to create a new language, then part of the design process is to design an ActionBinder for it. The ActionBinder performs an immense amount of work. For example, a call to ActionBinder.ConvertExpression() provides conversion information about the data types that the language supports. Of course, IronPython already performs this task for you, but it’s important to know how things work under the hood in case you encounter problems.

Understanding the Other DLR Features

DLR is a moving target at the time of this writing. The latest release, 0.92, isn’t even considered production code as of yet. Consequently, you might find that the version of DLR that you use has features not described in this chapter because they weren’t available at the time of this writing.

An ExpandoObject is a dynamic property bag. Essentially, you fill it with data you want to move from one language to another. It works just like any other property bag you’ve used in the past. Because the ExpandoObject class implements IDynamicMetaObjectProvider, you can use it with dynamic languages such as IronPython. You use this object when moving data from C# or Visual Basic.NET to IronPython.

Working with XML Data

Using the .NET XML Functionality

If you know how to work with XML in .NET languages such as C#, then you already know how to perform the same tasks in IronPython because you can import the System.Xml assembly to gain full access to this functionality. Because the XML capabilities of the .NET Framework are so well defined, using the System.Xml assembly may be all you need to perform tasks within your application. The main issue to consider is how you plan to use your application later. For example, if you plan to move your application to another platform, then using the .NET Framework solution won’t work. In addition, you need to consider data type translation in IronPython. The .NET data types that you use normally are translated into their IronPython counterparts, which could prove confusing for some developers. With these factors in mind, the following sections provide an overview of XML support in the .NET Framework from the IronPython perspective.

Considering the System.Xml Namespace

The System.Xml namespace provides access to the various classes used to interact with XML data. You use these classes to read, write, interpret, edit, build, and otherwise manage XML data. For example, you might use the XmlDeclaration class to begin building an XML data file from scratch when needed. All of these classes depend heavily on standards to ensure the file you create using IronPython is readable by other languages and applications. In fact, the System.Xml namespace supports these standards and specifications.

  • XML 1.0 (including Document Type Definition, DTD, support): http://www.w3.org/ TR/1998/REC-xml-19980210
  • XML Namespaces (both stream level and Document Object Model, DOM): http://www.w3.org/TR/REC-xml-names/
  • XSD Schemas: http://www.w3.org/2001/XMLSchema
  • XPath expressions: http://www.w3.org/TR/xpath
  • XSLT transformations: http://www.w3.org/TR/xslt
  • DOM Level 1 Core: http://www.w3.org/TR/REC-DOM-Level-1/
  • DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2/

Developing a Basic .NET XML Application

A .NET XML application will follow most of the same principles you use when working with a static language such as C# or Visual Basic.NET. In fact, you might not notice much difference at all except for the obvious structural requirements of a Python application. Consequently, you should find it easy to move your XML code over to IronPython because you really don’t have anything new to worry about. Listing 13-1 shows a simple XML application that creates an XML document, saves it to disk, reads it from disk, and then displays the content onscreen.

DOM-Only Support in the .NET Framework

It’s important to note that the .NET Framework supports DOM and not Simple API for XML (SAX). However, if you want SAX support, you can use the Python modules instead (see the “Working with xml.sax” section of this chapter). XML files include both data and context. In order to reconstruct the original dataset described by an XML file, you need a parser to read the text and then convert it to a usable object. DOM and SAX represent two different methods for interacting with XML documents without forcing the developer to create a parser. If you want more information about the DOM versus SAX approach to parsing XML parsers, check out the information at http://developerlife.com/tutorials/?p=28 and http://www.jamesh.id.au/ articles/libxml-sax/libxml-sax.html. Here’s a summary of the DOM features.

  • Object-based.
  • Object module is created automatically.
  • Element sequencing is preserved.
  • High memory usage.
  • Slow initial data retrieval.
  • Best for complex data structures.
  • In-memory document updates are supported.

SAX takes a completely different approach than DOM. Here’s a summary of the SAX features.

  • Event-based.
  • Object module is created by the application.
  • Element sequencing is ignored in favor of single events.
  • Low memory usage.
  • Fast initial data retrieval.
  • Best for simple data structures.
  • No document updates.

Listing 13-1: Reading and writing an XML document

[code]
# Import clr to add references.
import clr
# Add the required reference.
clr.AddReference(‘System.Xml’)
# Import the System.Xml classes.
from System.Xml import *
# This function creates the document and writes it to disk.
def CreateDocument():
# Create a document.
Doc = XmlDocument()
# Add the XML Declaration.
Declaration = Doc.CreateXmlDeclaration(‘1.0’, ‘utf-8’, ‘yes’)
Doc.AppendChild(Declaration)
# Create the root node.
Root = Doc.CreateNode(XmlNodeType.Element, ‘root’, None)
# Add child elements to the root.
MsgNode = Doc.CreateNode(XmlNodeType.Element, ‘Message’, None)
MsgNode.InnerXml = ‘Hello’
Root.AppendChild(MsgNode)
MsgNode = Doc.CreateNode(XmlNodeType.Element, ‘Message’, None)
MsgNode.InnerXml = ‘Goodbye’
Root.AppendChild(MsgNode)
# Add the root node to the document.
Doc.AppendChild(Root)
# Save the document to disk.
Doc.Save(‘Test.XML’)
def DisplayDocument():
# Create a document.
XMLDoc = XmlDocument()
# Load the XML data.
XMLDoc.Load(‘Test.XML’)
# Process the document.
for Nodes in XMLDoc:
if type(Nodes) == XmlElement:
for MsgNodes in Nodes:
print ‘Message:’, MsgNodes.InnerXml
# Interact with an XML document.
CreateDocument()
DisplayDocument()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing clr, which the application uses to add the required reference to System.Xml using the clr.AddReference() method. The code then imports the System.Xml classes.

The example relies on two functions to keep the code simple: CreateDocument(), which creates and saves the document to disk, and DisplayDocument(), which reads the document from disk and displays the content on screen. The example calls each of these functions in turn.

The CreateDocument() function begins by creating an XmlDocument object, Doc. As with any .NET application, Doc doesn’t contain anything when you create it. The first task is to add the XML declarations so that the result is a well-formed XML document using Doc.CreateXmlDeclaration(). Calling Doc.AppendChild() adds the declaration to the document.

Now it’s time to create some content. All XML documents have a root node, which is Root for this example. The code creates Root using Doc.CreateNode() with an XmlNodeType.Element type and ‘root‘ for a name. The example doesn’t work with XML namespaces, so the third argument is set to None.

The most efficient way to create an XML document from scratch is to add all the child nodes to Root before you add Root to the document. The code creates MsgNode using the same technique as for Root. It adds content to MsgNode using the MsgNode.InnerXml property and then adds the node to Root using Root.AppendChild(). The example provides two ‘Message‘ nodes.

At this point, the code adds Root to the document using Doc .AppendChild(). It then saves the document to disk using Doc.Save(). Figure 13-1 shows the typical output from this example when viewed in Notepad (you can use any text editor to view the output because the Doc .Save() method includes spaces and line feeds).

The XML document output looks much as you might expect.
Figure 13-1: The XML document output looks much as you might expect.

The DisplayDocument() function begins by creating a document, XMLDoc, using the XmlDocument class constructor. It then loads the previously created XML document using XMLDoc.Load(). At this point, XMLDoc contains everything the code created earlier and you can easily explore it using the IronPython console.

If you’ve worked with XML documents using C# or Visual Basic.NET, you know that these languages sometimes make it hard to get to the data you really want. IronPython makes things very easy. All you need is a for loop, as shown in the code. Simple if statements make it easy to locate nodes of a particular type, XmlElement in this case.

By the time the code reaches the second for loop, it’s working with the ‘Message‘ elements. The code simply prints the MsgNodes.InnerXml property value to the screen, as shown in Figure 13-2. By now you can see that IronPython makes it incredibly simple to work with XML documents using the .NET Framework approach.

The example outputs the message content in the XML document.
Figure 13-2: The example outputs the message content in the XML document.

Loading and Viewing the XMLUtil Module

The example in this section assumes that you’ve loaded the XMLUtil module from the IronPython Tutorial directory. The following steps show you how to load this module manually so you can see the content.

  1. Open the IronPython console.
  2. Type import sys and press Enter. This command imports the sys module so that you can add the required directory to it.
  3. Type sys.path.append(‘C:/Program Files/IronPython 2.6/Tutorial‘) and press Enter (make sure you change the path information to match the location of your IronPython installation). The XMLUtil.py module exists in the Tutorial directory. Using this module is fine for experimentation, but be sure you copy the XMLUtil.py module to another location for other uses.
  4. Type print sys.path and press Enter. You should see the new path added to the list.
  5. Type dir(XMLUtil) and press Enter. You see the list of methods available in XMLUtil (as shown in Figure 13-3), which includes the Walk() method used in the example.
The Walk() method makes viewing XML data easier.
Figure 13-3: The Walk() method makes viewing XML data easier.

Loading and Viewing the XMLUtil Module

As previously mentioned, the XMLUtil.py file isn’t anything so advanced that you couldn’t put it together yourself, but it’s an interesting module to work with and use. Listing 13-2 shows a short example of how you could use this module in an application.

Listin g 13-2: Walking an XML document using XMLUtil

[code]
# Add the path required to import xmlutil.
import sys
sys.path.append(‘C:/Program Files/IronPython 2.6/Tutorial’)
# Import xmlutil to access the Walk() function.
import xmlutil
# Import clr to add references.
import clr
# Add the required reference.
clr.AddReference(‘System.Xml’)
# Import the System.Xml classes.
from System.Xml import *
# Create a document.
XMLDoc = XmlDocument()
# Load the XML data.
XMLDoc.Load(‘Test.XML’)
# Walk the file contents.
print ‘Contents of Test.XML’
for Node in xmlutil.Walk(XMLDoc):
print ‘nName:’, Node.Name
print ‘Value:’, Node.Value
print ‘InnerXml’, Node.InnerXml
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The example begins by importing sys, appending the Tutorial folder path, and importing XMLUtil. The code then imports clr, adds a reference to System.Xml, and imports the System.Xml classes. There isn’t anything new about any of this code.

The example makes use of the Text.XML file created in the “Developing a Basic .NET XML Application” section of this chapter. It creates an XmlDocument object, XMLDoc, and loads Text.XML into it using the XMLDoc.Load() method. At this point, you have an XML document that you can walk (go from node-to-node and examine). The XMLUtil.Walk() method can walk any sort of XML document, so you should try it out with other files after you’ve worked with the example for a while.

The next step is to call on XMLUtil.Walk() to walk the XML document for you. The example shows output from the Name, Value, and InnerXml properties. However, you have access to all the properties provided for the various XML data types that the .NET Framework provides. Consequently, you can use XMLUtil.Walk() to display any information needed, or to manage that information. Just because the example displays properties doesn’t mean you have any limitation on how you interact with the output of XMLUtil.Walk(). Figure 13-4 shows the output of this example.

Screen shows the output of the Walk() method for Test.XML.
Figure 13-4: Screen shows the output of the Walk() method for Test.XML.

The XMLUtil.Walk() function is so important because it demonstrates a Python generator (described later in the section when you have the required background). Most languages don’t provide support for generators, so they require a little explanation. The issue at the center of this whole discussion is the variant list. You know that an application will need to process some number of items during run time, but you have no idea of how long this list is or whether the list will exist at all. A producer function is one that outputs values one at a time in response to a request. The producer keeps processing items until it runs out, so the length of the list is no longer a concern (even if the list contains no items at all). Most languages rely on a callback, an address to the requestor, to provide a place to send the producer output. The problem with using a callback is that the code must provide some means of retaining state information to remember previous values. In some cases, using callbacks leads to unnatural, convoluted coding techniques that are hard to write, harder to understand, and nearly impossible to update later.

Developers have a number of alternatives they can use. For example, the developer could simply use a very large list. However, lists require that the developer know what values should appear in the list during design time, and lists can consume large quantities of memory, making them a less than helpful solution in many cases. Another solution is to use an iterator to perform the task. Using an iterator makes it easier to get out of a loop when the processing is finished and eliminates the memory requirements. However, using an iterator shifts the burden of maintaining state information to the producer, complicating an already difficult programming task because the producer may not know anything about the caller. There are other solutions, as well, such as running the requestor and producer on separate threads so that each object can maintain state information without worrying about the potential corruption that occurs when running the code on a single thread. Unfortunately, multithreaded applications can run slowly and require a platform that fully supports multithreading, making your application less portable. In short, most languages don’t provide a good solution to the problem of working with data of variant length.

A generator creates a situation where the producer continuously outputs individual results as in a loop, maintaining its state locally. The requestor actually views the function as a type of iterator, even though the producer isn’t coded to provide an iterator. To accomplish this task, Python provides the yield statement shown in Figure 13-5. The yield statement returns an intermediate result from the producer to the requestor, while the producer continues to process a list of items.

The code in Figure 13-5 begins with the definition of a function named Walk(). This function accepts some kind of XML as input. The first yield statement sends the entire xml input back to the requestor (the example application shown in Listing 13-2). Consequently, you see #document as the Name and the entire XML document as the InnerXml.

The second call to Walk() moves past the first yield statement. Because the second item doesn’t meet the hasattr(xml, “Attributes“) requirement, the code moves onto the loop statement at the bottom of the code listing shown in Figure 13-5. The effect of this loop is to obtain the child elements of the entire document. So the second call to Walk() ends with yield c, which returns the XML declaration element. As a result, you see xml for the Name, version=“1.0“ encoding=“utf-8“ standalone=“yes“ for the Value, and nothing for the InnerXml. This second call ends processing of the XML declaration.

The XMLUtil.Walk() function is interesting because it provides a generator.
Figure 13-5: The XMLUtil.Walk() function is interesting because it provides a generator.

The third call to Walk() begins processing of the root node. It’s interesting to trace through the code in the debugger because you see the for loops in XMLUtil.Walk() used to trace through each element of the input xml as if it were using recursion or perhaps some type of iteration, but the fact is that the code merely combines the for loop with a yield statement to feed each partial result back to the requestor. Using the Python debugger is actually a bit more helpful in this case than using the Visual Studio debugger because the Visual Studio debugger won’t show you the value of xml, child, or c so that you can see the changing values. The example code for this book includes XMLUtilDemo2.py for the purpose of using the Python debugger. Follow these steps to load the debugger so you can trace through the example yourself.

  1. Open the IronPython console.
  2. Type import sys and press Enter. This command imports the sys module so that you can add the required directory to it.
  3. Type sys.path.append(‘C:/Program Files/IronPython 2.6/Tutorial‘) and press Enter (make sure you change the path information to match the location of your IronPython installation).
  4. Type import XMLUtil and press Enter to import the support file (important if you want to see how the generator works).
  5. Type import XMLUtilDemo2 and press Enter to import the source code file.
  6. Type import pdb and press Enter to import the debugger.
  7. Type pdb.run(‘XMLUtilDemo2.main()‘) to start the debugger. At this point, you can single step through the code to see how everything works.

Using the Python Modules

At one point, the Python modules were stable and straightforward to use, but later versions are less stable and, when it comes to IronPython, may be missing required elements completely. Consequently, you might see tutorials such as the one at http://www.boddie.org.uk/python/XML_intro.html and wonder why they don’t work. These tutorials are based on earlier versions of Python and don’t account for the missing CPython elements in IronPython. The following sections describe how to overcome these problems in your application when you use the Python approach to XML file management in IronPython.

Working with xml.dom.minidom

The xml.dom.minidom module is designed to help you work with XML using the DOM approach. However, this module is far from complete in IronPython, partly due to the CPython support required in standard Python. The actual document support is complete, so you won’t have a problem building, editing, and managing XML documents. It’s the write and read support that are lacking.

Fortunately, you can overcome write issues by using a different approach to outputting the document to disk (or other media). Standard Python development practice is to use the xml.dom.ext .PrettyPrint() method, which simply doesn’t exist in IronPython. You get around the problem by performing the task in two steps, rather than one, as shown in Listing 13-3.

The reading problem isn’t as easy to solve. Standard Python development practice is to use the xml .dom.minidom.parse() method. This method does exist in IronPython, but it outputs an error stating

[code]
ImportError: No module named pyexpat
[/code]

This module actually is missing. In order to fix this problem, you must download the pyexpat. py file from https://fepy.svn.sourceforge.net/svnroot/fepy/trunk/lib/. Place this file in your Program FilesIronPython 2.6Lib, not the Program FilesIronPython 2.6Lib xmldom folder as you might think. As shown in Listing 13-3, the standard Python techniques work just fine now.

Listin g 13-3: Managing XML documents using the Python approach

[code]
# Import the required XML support.
import xml.dom.minidom
def CreateDocument():
# Create an XML document.
Doc = xml.dom.minidom.Document()
# Create the root node.
Root = Doc.createElement(‘root’)
# Add the message nodes.
MsgNode = Doc.createElement(‘Message’)
Message = Doc.createTextNode(‘Hello’)
MsgNode.appendChild(Message)
Root.appendChild(MsgNode)
MsgNode = Doc.createElement(‘Message’)
Message = Doc.createTextNode(‘Goodbye’)
MsgNode.appendChild(Message)
Root.appendChild(MsgNode)
# Append the root node to the document.
Doc.appendChild(Root)
# Create the output document.
MyFile = open(‘Test2.XML’, ‘w’)
# Write the output.
MyFile.write(Doc.toprettyxml(encoding=’utf-8’))
# Close the document.
MyFile.close()
def DisplayDocument():
# Read the existing XML document.
XMLDoc = xml.dom.minidom.parse(‘Test2.XML’)
# Print the message node content.
for ThisChild in XMLDoc.getElementsByTagName(‘Message’):
print ‘Message:’, ThisChild.firstChild.toxml().strip(‘nt’)
CreateDocument()
DisplayDocument()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The first thing you should notice is that the code for this example is much shorter than its .NET counterpart, even though the result is essentially the same. Despite the problems with the Python libraries, you can write concise code for manipulating XML using Python.

The code begins by importing the only module it needs, xml.dom.minidom. It then calls CreateDocument() and DisplayDocument() in turn, just as the .NET example does. In fact, the output from this example is precisely the same. You see the same output shown in Figure 13-2 when you run this example.

The CreateDocument() function begins by creating an XML document, Doc, using xml.dom .minidom.Document(). The XML document automatically contains the XML declaration, so unlike the .NET version of the code, you don’t need to add it manually. So the first processing task is to create the root node using Doc.createElement(‘root‘).

As with the .NET example, this example creates two MsgNode elements that contain different messages. The technique used is different from the .NET example. Instead of setting an InnerXml property, the code creates an actual text node using Doc.createTextNode(). However, the result is the same, as shown in Figure 13-6. The last step is to add Root to Doc using Doc.appendChild().

A big difference between IronPython and Python is how you write the XML to a file. As previously mentioned, you can’t use the xml.dom.ext.PrettyPrint() method. In this case, the code creates a file, MyFile, using open(). The arguments define the filename and the mode, where ‘w‘ signifies write. In order to write the text to a file, you use a two-step process. First, the code creates formatting XML by calling Doc.toprettyxml(). The function accepts an optional encoding argument, but there isn’t any way to define the resulting XML document as stand-alone using the standalone=“yes“ attribute (see Figure 13-1). Second, the code writes the data to the file buffer using MyFile.write().

The Python output is similar, but not precisely the same as the .NET output.
Figure 13-6: The Python output is similar, but not precisely the same as the .NET output.

[code]
Calling MyFile.write() doesn’t write the data to disk. In order to clear the file buffer, you must call MyFile.close(). Theoretically, IronPython will call MyFile.close() when the application ends, but there isn’t any guarantee of this behavior, so you must specifically call MyFile.close() to ensure there isn’t any data loss.
[/code]

The DisplayDocument() function comes next. Reading an XML document from disk and placing it in a variable is almost too easy when using IronPython. All you need to do is make a single call to xml.dom.minidom.parse(). That’s it! The document is immediately ready for use.

The second step is to display the same output shown in Figure 13-2. Again, all you need in IronPython is a simple for loop, rather than the somewhat lengthy .NET code. In this case, you ask IronPython to retrieve the nodes you want using XMLDoc.getElementsByTagName(). The output is a list that you can process one element at a time. The print statement calls on a complex-looking call sequence.

[code]
ThisChild.firstChild.toxml().strip(‘nt’)
[/code]

However, if you take this call sequence apart, it really isn’t all that hard to understand. Every iteration of the loop places one of the MsgNode elements in ThisChild. The first (and only) child of MsgNode is the Message text node, so you can retrieve it using the firstChild property. The firstChild property contains a DOM Text node object, so you convert it to XML using the toxml() method. Unfortunately, the resulting string contains control characters, so you remove them using the strip(‘nt‘) method. The result is a simple value output.

Working with xml.sax

It’s important to remember that SAX is an event-driven method of working with XML. An application looks at a small number of bits out of an entire document. Consequently, SAX can be a good method for processing larger documents that you can’t read into memory at one time. A SAX application normally relies on three constructs:

  • One or more sources as input
  • A parser (normally, only one is used)
  • One or more handlers to respond to input events

There are many different Python SAX modules. Each of these modules provides different implementations of the three constructions. The default SAX implementation provides just four handlers. These handlers are implemented as classes that you use to interact with the events generated by the input file.

  • ContentHandler: Provides the main SAX interface for handling document events. Most applications use this interface as a minimum because it provides the basic support required for any document. The example shows how to use this handler, which is provided as part of the xml.sax module.
  • DTDHandler: Manages all of the Document Type Definition (DTD) events.
  • EntityResolver: Resolves external entities such as files referenced by processing instructions.
  • ErrorHandler: Reports any errors or warnings that the parser encounters when it processes the XML. Provided as part of the xml.sax module.

Now that you have a little better idea of what to expect, it’s time to look at an actual example. Listing 13-4 shows a simple SAX implementation that includes all of the constructs you normally need. Of course, you can easily add to this example to make it do considerably more than it does now.

Listin g 13-4: Parsing an XML document using SAX

[code]
# Import the required module.
import xml.sax
# Create a handler based on the default ContentHandler class.
class MessageHandler(xml.sax.ContentHandler):
# Contains the message text.
Message = ‘’
# Determines when the content is a message.
IsMessage = False
# Check for the kind of element before processing it.
def startElement(self, name, attrs):
if name == ‘Message’:
self.IsMessage = True
self.Message = ‘’
else:
self.IsMessage = False
# If this is the right kind of element, display the message for it.
def endElement(self, name):
if name == ‘Message’:
print ‘Message:’, self.Message.strip(‘nt’)
# Add each of the characters of the message to the Message variable.
def characters(self, ch):
if self.IsMessage:
self.Message += ch
# Create a parser.
Parser = xml.sax.make_parser()
# Create a handler for the parser and tell the parser to use it.
Handler = MessageHandler()
Parser.setContentHandler(Handler)
# Open a source and parse it using the parser with the custom handler.
Parser.parse(open(‘Test2.XML’))
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing the required xml.sax module. You don’t need anything fancy to create a basic SAX handler. Remember that SAX processes the file one character at a time and generates events based on the characters that the parser sees. Consequently, the code may seem a little odd for someone who is used to working with complete elements, but SAX gives you fine control over the processing cycle, including locating errors within the file.

The centerpiece of this example is the MessageHandler class. This class includes a variable to hold the message (Message), an indicator of whether an element is a message (IsMessage), and the three methods described in the following list.

  • startElement(): The parser calls this method at the beginning of an element.
  • endElement(): The parser calls this method at the end of an element.
  • characters(): Every character read from the source generates a call to characters().

For this example, the startElement() method checks the element name. If the element is a ‘Message‘ element, then the code sets IsMessage to True and clears Message of any existing content. This is a preparatory step.

When the characters() method sees that IsMessage is True, it appends every character it receives to Message. Remember that these are individual characters, so you can’t assume much about the content except that the flow is from the beginning of the file to the end of it. In other words, you won’t receive characters out of order.

The endElement() checks the element name again. When the element name is ‘Message‘, the code prints the content of Message. Because Message contains all of the characters from the source, you must use strip(‘nt‘) to remove any control characters. The output from this example is the same as shown in Figure 13-2.

Now that you understand the handler, it’s time to see how you put it to work. The main part of the code begins by creating a parser, Parser, using xml.sax.make_parser(). Remember that the parser simply generates events based on the input characters it sees. The handler performs the actual interpretation of those characters.

The next step is to create an instance of MessageHandler named Handler. The code uses Parser .setContentHandler() to assign the handler to Parser. Otherwise, Parser won’t know which handler to use to process the XML characters.

In order to process the XML file, the code still requires a source — the third construct. The open(‘Test2.XML‘) call opens Test2.XML as a source and passes this source to Parser through the Parser.parse() method. It’s the call to the Parser.parse() method that actually begins the process of generating events.

Debugging IronPython Applications

Understanding IronPython Warnings

Warnings are simply indicators that something could be wrong with your application or might not work under all conditions. For example, if you use a deprecated (outdated) function, you might later find that the application refuses to work on all machines. You can use warnings for all kinds of purposes, including providing debugging messages for your application.

The main difference between a warning and an exception is that a warning won’t stop the application. When the interpreter encounters a warning, it outputs the warning information to the standard error device unless the interpreter is ignoring the warning. In some cases, you need to tell the interpreter to ignore a warning because the warning is due to a bug in someone else’s code, a known issue that you can’t fix, or simply something that is obscuring other potential errors in your code. A standard warning looks like this:

[code]
__main__:1: UserWarning: deprecated
[/code]

The elements are separated by colons (:) and each warning message contains the following elements (unless you change the message formatting to meet a specific need).

  • Function name (such as __main__)
  • Line number where the warning appears
  • Warning category
  • Message

You’ll discover more about these elements as the chapter progresses. In the meantime, it’s also important to know that you can issue warnings, filter them, change the message formatting, and perform other tasks using the warning-related functions shown in Table 12-1. You see these functions in action in the sections that follow.

Warning-Related Functions and Their Purpose
Table 12-1: Warning-Related Functions and Their Purpose
Warning-Related Functions and Their Purpose
Table 12-1: Warning-Related Functions and Their Purpose (Continue)

Working with Actions

Before you do too much with warnings, it’s important to know that warnings have an action associated with them. For example, you can choose to turn a particular warning into an exception or to ignore it completely. You can apply actions to warnings in a number of ways using either the filterwarnings() or simplefilter() function. Table 12-2 shows the list of standard warning actions.

Standard Warning Actions
Table 12-2: Standard Warning Actions
Table 12-2 (continued)
Table 12-2 (continued)

It’s important to work with a few warnings to see how filtering works because filters are exceptionally important. In order to use warnings, you import the warnings module. Figure 12-1 shows a typical instance of the default action. Notice that the first time the code issues the warnings .warn(“deprecated“, DeprecationWarning) warning, the interpreter displays a message. (Don’t worry too much about the specific arguments for the warnings.warn()function for right now; you see them explained in the “Working with Messages” and “Working with Categories” sections of the chapter.) However, the interpreter ignores the same warning the second time. If you change the message, however, the interpreter displays another message.

The default action displays each message just one time.
Figure 12-1: The default action displays each message just one time.

Of course, you could always associate a different action with the warnings .warn(“deprecated“, DeprecationWarning) warning. To make this change, you can use the simplefilter() function as shown in Figure 12-2. Now when you issue the warning, it appears every time.

You can set the warning to appear every time.
Figure 12-2: You can set the warning to appear every time.

Unfortunately, as shown in the figure, the change affects every message. Using the simplefilter() function affects every message in every module for a particular message category. Both the newmessage and deprecated messages always appear. Let’s say you want to make just the deprecated message always appear. To perform this task, you use the filterwarnings() function as shown in Figure 12-3 (after first resetting the category using the resetwarnings() function).

Use the filterwarnings() function when you need better control over filtering.
Figure 12-3: Use the filterwarnings() function when you need better control over filtering.

In this case, the warnings.warn(“deprecated“, DeprecationWarning) warning appears every time because its action is set to always. However, the warnings .warn(“newmessage“, DeprecationWarning) warning appears only once because it uses the default action.

You can also set an action at the command line using the –W command line switch. For example, to set the interpreter to always display warning messages, you’d use the –W always command line switch. The –W command line switch accepts an action, message, category, module, or line number (lineno) as input. You can include as many –W command line switches as needed on the command line to filter the warning messages.

The resetwarnings() function affects every warning category and every message in every module. You might not want to reset an entire filtering configuration by using the resetwarnings() function. In this case, simply use the filterwarnings() or simplefilter() function to set the warning back to the default action.

At this point, you might wonder how to obtain a list of the filters you’ve defined. For that matter, you don’t even know if there are default filters that the interpreter defines for you. Fortunately, the warnings class provides two attributes, default_action and filters, which provide this information to you. Listing 12-1 shows how to use these two attributes.

Listin g 12-1: Discovering the default action and installed filters

[code]
# Import the required modules.
import warnings
# Display the default action.
print ‘Default action:’, warnings.default_action
# Display the default filters.
print ‘nDefault Filters:’
for filter in warnings.filters:
print ‘Action:’, filter[0],
print ‘Msg:’, filter[1],
print ‘Cat:’, str(filter[2]).split(“‘“)[1].split(‘.’)[1],
print ‘Module:’, filter[3],
print ‘Line:’, filter[4]
# Add new filters.
warnings.filterwarnings(‘always’, message=’Test’, category=UserWarning)
warnings.filterwarnings(‘always’, message=’Test2’, category=UserWarning,
module=’Test’)
warnings.filterwarnings(‘always’, message=’Test3’, category=UserWarning,
module=’Test’, append=True)
# Display the updated filters.
print ‘nUpdated Filters:’
for filter in warnings.filters:
print ‘Action:’, filter[0],
try:
print ‘Msg:’, filter[1].pattern,
except AttributeError:
print ‘None’,
print ‘Cat:’, str(filter[2]).split(“‘“)[1].split(‘.’)[1],
try:
if len(filter[3].pattern) == 0:
print ‘Module: Undefined’,
else:
print ‘Module:’, filter[3].pattern,
except AttributeError:
print ‘Module: None’,
print ‘Line:’, filter[4]
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing the warnings module. It then displays (using warnings.default_ action) the default action that the interpreter will take when it encounters a warning. As shown in Figure 12-4 and described in Table 12-2, the default action is ‘default‘.

The example shows the default actions and filters, along with the output of filter changes.
Figure 12-4: The example shows the default actions and filters, along with the output of filter changes.

The next step is to show the default filters that the interpreter provides for you. It may surprise you to know that the interpreter does include some default filters for the PendingDeprecationWarning, ImportWarning, and BytesWarning, as shown in Figure 12-4. These default filters make the interpreter easier and more enjoyable to use, but could also hide important bugs, so you need to be aware of them.

In order to show how actions and filters work, the example adds three filters using the warnings .filterwarnings() function. The first filter simply tells the interpreter to always display warnings about the Test message provided in the UserWarning category. The second filter specifies that the Test2 warning will appear in the Test module. The third filter specifies that the interpreter should append the warning filter to the end of the filter list, rather than add it to the front of the list as is traditional. You can see the result of all three filter additions in Figure 12-4.

The code used to display the filter information is different in this case because the simple display method used earlier won’t work. What you’ll see as output for the message and module information is something like

[code]
<RE_Pattern object at 0x000000000000002C>
[/code]

which isn’t particularly useful. In order to get information from the message and module elements, you must access the pattern attribute. Unfortunately, this attribute isn’t available with the default filters, so the solution is to create a try…except AttributeError structure, as shown in the code. When the code encounters a default filter entry, it simply prints None as it would have done in the past.

Working with modules presents a special problem. If you look at the first filter declaration, it doesn’t include the Module attribute. Unfortunately, the interpreter takes this omission to mean that you want to create a blank entry, not a null entry. Consequently, the module code also handles the empty entry scenario by saying the module is undefined. If you want to create a null module entry, you must use Module=None as part of your filter declaration.

Notice in Figure 12-4 that the first two filters appear at the front of the list and in reverse order. That’s because the interpreter always adds new filters to the beginning of the list unless you include the append=True attribute. Because the third filter includes this attribute, it appears at the end of the list.

Working with Messages

A message is simply the text that you want to appear as part of the warning. The message is specific information about the warning so that someone viewing the warning will know precisely why the warning is issued. For example, if you issue a DeprecationWarning category warning, the output will automatically tell the viewer that something is deprecated. As a result, your message doesn’t have to tell the viewer that something is deprecated, but it does have to tell the viewer what is deprecated. In many cases, this means supplying the name of the feature such as a method name, attribute, function, or even a class.

Simply telling someone that a feature is deprecated usually isn’t enough information. At a minimum, you must include information about an alternative. For example, you might want to suggest another class or a different function. Even if there is no alternative, you should at least tell the viewer that there isn’t an alternative. Otherwise, the viewer is going to spend hours looking for something that doesn’t exist.

You can’t always tell someone why something is deprecated, but you should when you can. For example, it would be helpful to know that an old function is unstable and that the new function fixes this problem. It’s a good idea to extend this information by saying that the old function is supplied for backward compatibility (assuming that this really is the case).

In some cases, you also need to provide some idea of when a feature is deprecated, especially if the action occurs in the future. Perhaps your organization knows that a function is unstable but hasn’t come up with a fix yet. The fix will appear in the next version of a module as a new function. Having this information will help organizations that rely on your module to plan ahead for required updates.

The point of messages is that they should provide robust information — everything that someone needs to make good decisions. Of course, you don’t want to provide too much information either (anything over three well-written sentences is too much). If you feel the viewer needs additional information, you can always provide it as part of the feature’s help. That way, people who are curious can always find more information. Make sure you note the availability of additional information as part of your message.

Message consistency is another consideration. Remember that filters work with messages as well as categories and other warning elements. If two modules require the same message, make sure you use the same message to ensure filtering works as anticipated. In fact, copying and pasting the message is encouraged to reduce the risk of typographical errors.

If you ever want to see how your message will appear to others, you can use the formatwarning() function to perform the task. Try it out now. Open a copy of the IronPython console and try the following code.

[code]
import warnings
warnings.formatwarning(‘Bad Input’, UserWarning, ‘My.py’, 5, ‘import warnings’)
[/code]

You’ll see results similar to those shown in Figure 12-5. Notice that the output contains linefeeds like this: ‘My.py:5: UserWarning: Bad Inputn import warningsn‘. When you work with the printed version, the warning appears on multiple lines, as shown near the bottom of Figure 12-5.

Use formatwarning() to see how your warning will appear.
Figure 12-5: Use formatwarning() to see how your warning will appear.

Of course, it’s handy to know the arguments for the formatwarning() function. The following list provides a brief description of each argument.

  • Message: The message you want to display to the user.
  • Category: The warning category you want to use.
  • Filename: The name of the file where the warning occurred (not necessarily the current file).
  • Line number: In most cases, this value contains the line at which the warning is detected, which isn’t always the line at which the warning occurs. For example, it’s possible for a warning to appear at the end of a structure, rather than at the appropriate line within the structure.
  • Line of code: An optional entry that shows the line of code at which the warning occurs. If you don’t supply this argument, the formatwarnings() function defaults to a value of None. The IronPython implementation differs from the standard in this regard. According to the standard, the interpreter is supposed to read the file, obtain the correct line of code, and display the specified line when you don’t provide the appropriate text.

Working with Categories

A warning category is a means of identifying a particular kind of warning. The category makes it possible to group like warnings together and reduces the risk that someone will misinterpret the meaning of a message. In short, a category is a way to pigeonhole a particular message so that others know what you intend. Of course, filtering considers the warning category, so you also need to use the correct category to ensure filtering works as expected. Table 12-3 contains a list of the warning message categories, including a general Warning class that you shouldn’t ever use because it’s too general.

Warning Message Categories
Table 12-3: Warning Message Categories
Warning Message Categories
Table 12-3: Warning Message Categories (Continue)
Warning Message Categories
Table 12-3: Warning Message Categories (Continue)

The warning categories are used with almost every warnings module function. For example, you supply a category when setting a filter or creating a new message. There is always an exception. The resetwarnings() function doesn’t require any input, not even a warning category, because it resets the entire warning environment to a default state.

Obtaining Error Information

Errors will happen in your application, even if you use good exception handling. The handlers you create only react to the errors you know about. Applications also encounter unknown errors. In this case, your application has to have a way to obtain error information and display it to the user (or at least record it in a log file).

It’s important to remember that you normally obtain error information in an application using the exception process described. This section of the chapter is more designed for those situations where you need to work with a generic exception or obtain more detailed information than the specific exceptions provide.

As with many things, IronPython provides a number of methods for obtaining error information. In fact, you might be surprised at how many ways you can retrieve information once you really start looking. The following sections discuss the most common methods for obtaining error information.

Using the sys Module

The sys module contains a wealth of useful functions and attributes you use to obtain, track, and manage error information. One of the first things you should know about the sys module is that it contains the sys.stderr attribute, which defines where the interpreter sends error output. Normally, the output goes to the console window, but you can redirect the error output to any object that has a write() method associated with it, such as a file. If you want to later reset the sys.stderr attribute to the console, the sys.__stderr__ attribute always contains the original output location, so using sys.stderr = sys.__stderr__ performs a reset.

Obtaining error information seems like it should be straightforward, but it’s harder than most developers initially think because obtaining error information often affects application execution in unforeseen ways. In addition, ensuring that the caller receives the right information in a multithreaded application is difficult. The caller could also make unfortunate changes to error information objects, such as the traceback object, creating problems with circular references that the garbage collector is unable to handle. Consequently, you find a lot of functions in sys that look like they should do something useful (and this section covers them), but the two functions you need to keep in mind when working with IronPython are.

  • sys.exc_info(): Returns a tuple containing three items:
    • type: The type of the error, such as ZeroDivisionError. You can find a list of all standard exception types in the exceptions module.
    • value: The human readable string that defines the error. For example, a ZeroDivisionError might provide ZeroDivisionError(‘Attempted to divide by zero.‘,) as a value.
    • traceback: An object that describes the stack trace for an exception. Normally, you won’t use this information directly unless you truly need to obtain the stack trace information, which can prove difficult. If you need stack trace information, consider using the traceback module features instead
  • sys.exc_clear(): Clears the existing exceptions from the current thread. After you call this function, sys.exc_info() returns None for all three elements in the tuple.

The sys.exc_info() function isn’t very hard to use, but you can’t really try it out by executing it directly in the IronPython console. You need to place it within a try…except structure instead. The following code shows a quick demonstration you can type directly into the console window.

[code]
try:
5/0
except:
type, value = sys.exc_info()[:2]
print type
print value
[/code]

The example uses a simple division by zero to create an exception. As previously noted, you normally need just the first two elements of the tuple, which you can obtain using sys.exc_info()[:2]. When you execute this code, you see the following output.

[code]
<type ‘exceptions.ZeroDivisionError’>
Attempted to divide by zero.
[/code]

Some IronPython sys module functions affect only the interactive thread (which means they’re safe to use in multithreaded applications because there is only one interactive thread in any given session). You could use these functions to determine the current type, value, and traceback for an exception, but only for the interactive session, which means these functions are completely useless for your application. In most cases, you avoid using these three functions.

  • sys.last_traceback()
  • sys.last_type()
  • sys.last_value()

You could run into problems when working with some functions in the sys module. For example, these three functions are global, which means they aren’t specific to the current thread and are therefore, unsafe to use in a multithreaded application.

  • sys.exc_type()
  • sys.exc_value()
  • sys.exc_traceback()

Interestingly enough, these three functions are also listed as deprecated (outdated) in most Python implementations (including IronPython). As with all IronPython modules, you also have access to low-level functions in the sys module. The following list is low-level modules you can use for special needs, but won’t normally use in your application.

  • sys.excepthook(type, value, traceback): The system calls this low-level function each time it generates an exception. To use this function, you supply the same tuple of values as you receive when you call sys.exc_info().
  • sys._getframe([depth]): The system calls this low-level function to display a frame object from the call stack. If the caller supplies a depth value, the frame object is at that call stack depth. The default depth value setting is 0. IronPython doesn’t appear to implement this function, but you may encounter it in other versions of Python, so it pays to know about this function.

If you want to control how much information the interpreter provides when you request a traceback, you can always set the sys.tracebacklimit attribute. The sys.tracebacklimit attribute defaults to 1,000. It doesn’t actually appear when you perform a dir() command. In fact, until you set it, printing the sys.tracebacklimit attribute returns an AttributeError. Use code like this

[code]
sys.tracebacklimit = 3
[/code]

to modify the traceback level. Now when you try to print the sys.tracebacklimit attribute, you get back the value you supplied.

Using the traceback Module

The traceback module adds to the capabilities of the sys module described in the “Using the sys Module” section of the chapter. In addition, it adds to the standard exception handling capabilities of IronPython by making it easier to obtain complex information about exceptions in general. The traceback module does focus on tracebacks, which are the IronPython equivalent of a call stack.

The most common call is traceback.print_exc(). Essentially, this call prints out the current exception information. You can use it in a try…except structure, much as you’d use the sys.exc_info() function, but with fewer limitations. Figure 12-6 shows a typical view of the traceback.print_exc() function in action.

Obtain traceback information with ease using the traceback.print_exc() function.
Figure 12-6: Obtain traceback information with ease using the traceback.print_exc() function.

You may find that you want a string that you can manipulate, rather than direct output. In this case, you use the traceback.format_exc() function and place its output in a variable. The information is the same as shown in Figure 12-6, but you have the full capability of string manipulation functions to output the information in any form desired.

All of the traceback output functions include a level argument that defines how many levels of trace information you want. The default setting provides 1,000 levels, which may be a little more information than you want. Many of the traceback output functions also include a file argument that accepts the name of a file you can use for output (such as application logging). If you don’t provide the file argument, it defaults to using the sys.stderr device (normally the console).

Some of the traceback functions are macros for longer function combinations. For example, when you type traceback.print_last(), what you’re really doing is executing print_exception(sys.last_ type, sys.last_value, sys.last_traceback, limit, file). Obviously, typing traceback .print_last() is a lot less work!

IronPython is missing some extremely important functionality when it comes to the traceback module. You can’t use traceback.print_stack(), traceback.extract_stack(), or traceback.format_ stack() to obtain current stack information. The code shown in Figure 12-7 is standard output when working with Python. Figure 12-8 shows what happens when you execute this code in IronPython. Instead of getting a nice stack trace you can use for debugging (see Figure 12-7), you get nothing at all (see Figure 12-8). This is a known issue (see the issue information at http://ironpython.codeplex .com/WorkItem/View.aspx?WorkItemId=25543).

Python provides full stack information you can use for debugging.
Figure 12-7: Python provides full stack information you can use for debugging.
IronPython lacks support for stack traces, making debugging significantly more difficult.
Figure 12-8: IronPython lacks support for stack traces, making debugging significantly more difficult.

The traceback module contains a number of interesting functions that you can use to debug your application. You can see these functions described at http://docs.python.org/library/traceback .html. Don’t assume that all of these functions work as they do in Python. There are currently a number of outstanding traceback module issues for IronPython.

Debugging with the Python Debugger

You might not know it, but Python and IronPython come with a debugger module, pdb (for Python debugger). Like any module, you have full access to the debugger source code and can modify it as needed. This section describes the default debugger performance.

It’s possible to use pdb with any Python file by invoking the debugger at the command line using the –m command line switch. Here’s how you’d invoke it for the example shown in Listing 12-1.

[code]
IPY -m pdb ShowFilters.py
[/code]

Unfortunately, using this command line format limits what you can do with the debugger. Although you can single step through code, you can’t work with variables easily and some other debugger commands may not work as anticipated.

The debugger works better if you configure your application to use a main() module. Most of the examples in this book don’t use a main() function for the sake of simplicity, but you should use one for any production code you create. The ShowFilters2.py file contains the modifications to provide a main() function. Essentially, you encase the code in Listing 12-1 in the main() function and then call it using the following code:

[code]
# Create an entry point for debugging.
if __name__ == “__main__“:
main()
[/code]

Using the debugger is very much like old-style DOS debuggers such as the Debug utility. You issue commands and the debugger responds without output based on the application environment and variable content. The lack of a visual display may prove troublesome to developers who have never used a character-mode debugger, but pdb is actually more effective than any of the graphical alternatives in helping you locate problems with your application — at least, in the Python code. Use these steps to start the pdb:

  1. Start the IronPython console by selecting it from the Start menu or typing IPY at the command line.
  2. Type import pdb and press Enter to import the Python debugger.
  3. Type import ApplicationName where ApplicationName is the name of the file that contains your application and press Enter. For example, if your application appears in ShowFilters2.py, then you’d type import ShowFilters2 (without the file extension) and press Enter.
  4. Type pdb.run(‘ApplicationName.FunctionName()‘) where ApplicationName is the name of the application and FunctionName is the name of the function you want to test, and press Enter. For example, if your application is named ShowFilters2 and the function you want to test is main(), you’d type pdb.run(‘ShowFilters2.main()‘) and press Enter. The standard console prompt changes to a pdb prompt, as shown in Figure 12-9.
The Python debugger uses a special pdb prompt where you can enter debugging commands.
Figure 12-9: The Python debugger uses a special pdb prompt where you can enter debugging commands.

Now that you have a debugger prompt, you can begin debugging your application. Here is a list of standard debugger commands you can issue:

  • a or args: Displays the list of arguments supplied to the current function. If there aren’t any arguments, the call simply returns without displaying anything.
  • alias: Creates an alias for a complex command. For example, you might need to use a for loop to drill down into a list to see its contents. You could use an alias to create a command to perform that task without having to write the complete code every time. An alias can include replaceable variables, just as you would use for a batch file.
  • b or break: Defines a breakpoint when you supply a line number or a function name. When you provide a function name, the breakpoint appears at the first executable line within the function. If an application spans multiple files, you can specify a filename, followed by a colon, followed by a line number (no function name allowed), such as ShowFilters2:1. A breakpoint can also include a condition. To add the condition, follow the breakpoint specification with a comma and the condition you want to use, such as ShowFilters2:2, Filter == None. If you type just b or break, the debugger shows the current breakpoints. Use the cl or clear command to clear breakpoints you create.
  • bt, w, or where: Prints a stack trace with the most current frame at the bottom of the list. You can use this feature to see how the application arrived at the current point of execution.
  • c, cont, or continue: Continues application execution until the application ends or the debugger encounters a breakpoint.
  • cl or clear: Clears one or more breakpoints. You can specify the breakpoint to clear by providing one or more breakpoint numbers separated by spaces. As an alternative, you can supply a line number or a filename and line number combination (where the filename and line number are separated by a colon).
  • commands: Defines one or more commands that execute when the debugger arrives at a line of code specified by a breakpoint. You include the optional breakpoint as part of the commands command. If you don’t supply a breakpoint, then the commands command refers to the last breakpoint you set. To stop adding commands to a breakpoint, simply type end. If you want to remove the commands for a breakpoint, type commands, press Enter, type end, and press Enter again. A command can consist of any interactive Python or debugger command. For example, if you want to automatically move to the next line of code, you’d simply add step as one of the commands.
  • condition: Adds a condition to a breakpoint. You must supply a breakpoint number and a Boolean statement (in string format) as arguments. The debugger doesn’t honor a breakpoint with a condition unless the condition evaluates to True. The condition command lets you add a condition to a breakpoint after defining the breakpoint, rather than as part of defining the breakpoint. If you use condition with a breakpoint, but no condition, then the debugger removes a condition from a breakpoint, rather than adding one.
  • d or down: Moves the frame pointer down one level in the stack trace to a new frame.
  • debug: Enters a recursive debugger that helps you debug complex statements.
  • disable: Disables one or more breakpoints so that they still exist, but the debugger ignores them. You can separate multiple breakpoint numbers with spaces to disable a group of breakpoints at once.
  • enable: Enables one or more breakpoints so that the debugger responds to them. You can separate multiple breakpoint numbers with spaces to enable a group of breakpoints at once. Enabling a breakpoint doesn’t override any conditions that are set on the breakpoint. The condition must still evaluate to True before the debugger reacts to the breakpoint.
  • EOF: Tells the debugger to handle the End of File (EOF) as a command. Normally, this means ending the debugger session once the debugger reaches EOF.
  • exit or q or quit: Ends the debugging session. Make sure you type exit, and not exit(), which still ends the IronPython console session.
  • h or help: Displays information about the debugger. If you don’t provide an argument, help displays a list of available debugging commands. Adding an argument shows information about the specific debugging command.
  • ignore: Creates a condition where the debugger ignores a breakpoint a specific number of times. For example, you might want to debug a loop with a breakpoint set at a specific line of code within the breakpoint. You could use the ignore command to ignore the first five times through the loop and stop at the sixth. You must supply a breakpoint number and a count to use this command. The debugger automatically ignores the breakpoint until the count is 0.
  • j or jump: Forces the debugger to jump to the line of code specified as an argument.
  • l or list: Displays the specified lines of code. If you don’t supply any arguments with the command, the debugger displays 11 lines of code starting with the current line. When you supply just a starting point (a code line number), the debugger displays 11 lines of code starting with the starting point you specify. To control the listing completely, supply both a starting and ending point.
  • n or next: Continues execution to the next line of code. If the current line of code is a function, the debugger executes all of the code within the function and stops at the next line of code in the current function. In sum, this command works much like a step over command in most other debuggers.
  • p: Prints the value of an expression as the debugger sees it. Don’t confuse this command with the IronPython print() function, which prints an expression based on how IronPython sees it.
  • pp: Performs a pretty print. Essentially, this command is the same as the p command, except that the debugger interprets any control characters within the output so that the output appears with line feeds, carriage returns, tabs, and other formatting in place.
  • r or return: Continues execution until the current function returns. This command works much like a step out command in most other debuggers
  • restart: Restarts the current application at the beginning so that you can retest it. The command lets you supply optional arguments that appear as part of the sys.argv attribute. This command preserves debugger history, breakpoints, actions, and options.
  • run: Starts the application when used within Python as demonstrated earlier in this section. However, this command is simply an alias for restart when used within the debugger environment.
  • s or step: Executes the current line of code and then moves to the next line of code, even if that line of code appears within another function. This command works much like a step into command in most other debuggers
  • tbreak: Performs precisely like a break command, except that the debugger removes the breakpoint when the debugger stops at it the first time. This is a useful command when you want to execute a breakpoint just one time.
  • u or up: Moves the frame pointer up one level in the stack trace to an old frame.
  • unalias: Removes the specified alias (see the alias command for additional details).
  • unt or until: Continues execution until such time as the line number is greater than the current line number or the current frame returns. This command works much like a combination of the step over and step out commands in most other debuggers (see next, return, and step for other stepping commands).
  • whatis: Displays the type of the argument that you supply.

Debugging with the CLR Debugger

The CLR debugger, CLRDbg.EXE, is part of the .NET Framework SDK. You find it in the GuiDebug folder of your .NET Framework installation or in the Program FilesMicrosoft.NETSDKv2.0 GuiDebug folder. However, if you installed Visual Studio without installing the SDK, you might not see a GuiDebug folder. In this case, you can download and install the .NET Framework SDK separately. You can obtain the .NET Framework SDK for various platforms at these locations.

  • .NET Framework 2.0: http://msdn.microsoft.com/en-us/netframework/aa731542.aspx
  • .NET Framework 3.0: http://msdn.microsoft.com/en-us/netframework/bb264589.aspx
  • .NET Framework 3.5: http://msdn.microsoft.com/en-us/netframework/cc378097.aspx
  • .NET Framework 3.5 SP1: http://msdn.microsoft.com/en-us/netframework/ aa569263.aspx

This section relies on the CLRDbg.EXE version found in the .NET Framework 2.0 SDK. However, the instructions work fine for every other version of the CLR debugger as well. The newer versions of the debugger may include a few additional features that you won’t likely use or need when working with IronPython. The following steps describe how to start the debugger.

  1. Start the CLR debugger. If you installed the .NET Framework SDK separately, choose Start ➪ Programs ➪ Microsoft .NET Framework SDK v2.0 ➪ Tools ➪ Microsoft CLR Debugger. It’s also possible to start the CLR debugger from the command line by typing CLRDbg and pressing Enter as long as the debugger’s location appears in the path. You see the Microsoft CLR Debugger window.

    Provide the information needed to debug your application.
    Figure 12-10: Provide the information needed to debug your application.
  2. Choose Debug ➪ Program to Debug. You see the Program to Debug dialog box shown in Figure 12-10. This dialog box is where you enter the IronPython executable and script information, along with any command line switches you want to use.
  3. Click the ellipsis (…) in the Program field and use the Find Program to Debug dialog box to locate the IPY.EXE file. Click Open to add the IPY.EXE information to the dialog box.
  4. Type –D NameOfScript.py in the Arguments field (the example uses –D ShowFilters2 .py). Type any additional command line arguments you want to use while working with the application.
  5. Click the ellipses in the Working Directory field and use the Browse for Working Directory dialog box to locate the script directory (not the IPY.EXE directory). Click Open to select the working directory.
  6. Click OK. The CLR debugger prepares the debugging environment. However, you don’t see any files opened. You must open any files you wish to interact with as a separate step.
  7. Choose File ➪ Open ➪ File. Locate the source files you want to debug (ShowFilters2.py for the example). Click Open. You see the source file opened in the Microsoft CLR Debugger window. Figure 12-11 shows an example of how your display should look when working with the example. (The figure shows the debugger in debugging mode.)

    Open the source files you want to debug.
    Figure 12-11: Open the source files you want to debug.

At this point, you can begin working with the script just as you would with the Visual Studio debugger. The next section, “Using Visual Studio for IronPython Debugging,” discusses this debugger in more detail.

Using Visual Studio for IronPython Debugging

When you click Start Debugging, the debugger stops at the line of code as you might expect. Now, create a watch for both filter and filters. As shown in Figure 12-12, you can drill down into a complex object and examine it. In many cases, you must look through the Non-Public Members to find what you want, but the data is there for you to peruse. In this case, you can see all five elements in filters and even see the pattern data. Notice that the Type column is truly helpful in showing you which types to use when interacting with the data.

Watches let you drill down into both Python and .NET data.
Figure 12-12: Watches let you drill down into both Python and .NET data.

Unfortunately, Figure 12-12 also shows the other side of the coin. You can’t access warnings .filters even though it should be available. The Visual Studio debugger often produces poor results when working with Python-specific objects. If you have a need for working with these objects.

As shown in Figure 12-13, you can use the Immediate window to query objects directly. However, you can’t drill down into an object as you might have in the past. Consequently, entering ? filter works just fine, but entering ? filter[0] doesn’t.

The Immediate window is only partially useful when working with IronPython.
Figure 12-13: The Immediate window is only partially useful when working with IronPython.

In general, you’ll find that using the Python debugger works better for some Python-specific applications. Even though the Visual Studio debugger does provide a nice visual display, the quality of information isn’t quite as good. Of course, the picture changes when your application mixes Python and .NET code. In this case, the Visual Studio debugger can be your best friend because it knows how to work with the .NET objects.

 Defining and Using Exceptions

Exceptions are an essential part of any application. In fact, most developers have no problem using them at all. Unfortunately, many developers also misuse exceptions. Instead of providing robust code that handles common problems, the developer simply raises an exception and hopes someone else does something about the issue. Exceptions are generally used to address conditions that you couldn’t anticipate.

IronPython provides access to both Python exception and .NET exceptions, so the developer actually has twice as many opportunities to catch errors before they become a problem. It’s important to use the correct kind of exception handling. If you’re working with .NET code, you’ll normally use a .NET exception. Python exceptions address anything that isn’t .NET-specific. The following sections provide additional information about exceptions.

Implementing Python Exceptions

Python provides a number of standard exceptions, just as the .NET Framework does. You find these exceptions in the exceptions module. To see the list of standard exceptions, import the exceptions module and perform a dir() command on it, as shown in Figure 12-14.

Python stores its list of standard exceptions in the exceptions module.
Figure 12-14: Python stores its list of standard exceptions in the exceptions module.

The various exceptions provide different amounts of information. For example, when working with an IOError, you can access the errno, filename, message, and strerror attributes. On the other hand, a ZeroDivisionError provides only the message attribute. You can use the dir(exceptions .ExceptionName) command to obtain information about each of the exception attributes.

As with .NET, you can create custom exceptions using Python. The documentation for creating a custom exception is a bit sketchy, but you can create a custom exception (usually with the word Error in the name by convention) for every need. Listing 12-2 shows all of the Python exception basics, including creating a relatively flexible custom exception.

Listin g 12-2: Discovering the default action and installed filters

[code]
# Import the required modules.
import exceptions
# Define a custom exception.
class MyError(exceptions.Exception):
errno = 0
message = ‘Nothing’
def __init__(self, errno=0, message=’Nothing’):
self.errno = errno
self.message = message
def __str__(self):
return repr(self.message)
# Display the Error exception list.
for Error in dir(exceptions):
if ‘Error’ in Error:
print Error
# Create a standard exception.
try:
5/0
except ZeroDivisionError as (errinfo):
print “nDivide by Zero error: {0}“.format(errinfo)
# Create a custom exception.
try:
raise MyError(5, ‘Hello from MyError’)
except MyError, Info:
print “Custom Error({0}): {1}“.format(Info.errno, Info.message)
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing exceptions. The for loop lists all of the exceptions (the names of the types) found in exceptions, as shown in Figure 12-15. Notice how the code uses if ‘Error‘ in Error to locate just the exceptions in the module. This technique is useful for a lot of tasks in IronPython where you need to filter the output in some way.

The example shows basic exception handling and creation for Python.
Figure 12-15: The example shows basic exception handling and creation for Python.

The next bit of code raises a standard exception and then handles it. The output shows just a message. Notice that this exception relies on the as clause to access the error information.

It’s time to look at a custom exception, which begins with the MyError class definition. At a minimum, you should define both __init__() and __str__() or the exception won’t work as intended. Notice how __init__() assigns default values to both errno and message. You can’t depend on the caller to provide this information, so including default values is the best way to approach the problem. You can always assign other values later in the code based on the actual errors.

Make sure you create attributes for any amplifying information you want the caller to have. In this case, the example defines two attributes errno and message.

The __str__() method should return a human-readable message. You can return just the text portion of the exception or return some combination of exception attributes. The important thing is to return something that the developer will find useful should the exception occur. You can test this behavior out with the example by typing raise MyError. Here’s the output you’ll see.

[code]
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
__main__.MyError: ‘Nothing’
[/code]

Because you didn’t provide any arguments, the output shows the default values. Try various combinations to see how the output works. The example tries the exception in a try…except statement. Notice that a custom exception differs from a standard exception in that you don’t use the as clause and simply provide a comma with a variable (Info in this case) instead. You can then use the variable to access the exception attributes as shown. Figure 12-15 shows how the custom exception outputs information. Of course, your custom exception can provide any combination of values.

Implementing .NET Exceptions

In general, you want to avoid using .NET exceptions in your IronPython applications, except in those cases where you need to provide specific functionality for .NET code. The problem is that IronPython views such exceptions from a Python perspective. Consequently, trapping .NET exceptions can prove tricky unless you spend some time working with them in advance.

Many .NET exceptions are available in the System assembly so you need to import it before you can perform any serious work. After that, you can raise a .NET exception much as you do a Python exception. Handling the exception follows the same route as using a try…except statement. However, the problem is that the exception you get isn’t the exception you raised. Look at Figure 12-16 and you see that the ArgumentException becomes a ValueError and the ArithmeticException becomes an ArithmeticError.

Sloppy programming will cost you so much time as to make the programming experience a nightmare. Using a combination of warnings, error trapping, and exceptions will make your code significantly easier to debug. Of course, choosing the right debugging tool is also a requirement if you want to go home this weekend, rather than spending it in your office debugging your latest application.

 

 

 

Developing ASP.NET Applications

Considering IronPyth on Web Application Compatibility

IronPython (and Python for that matter) have huge potential for making Web sites significantly easier to build and manage. In fact, there are a lot of options for using Python (and IronPython) to perform Web development tasks that you can see at http://wiki.python.org/ moin/WebProgramming and http://www.fredshack.com/docs/pythonweb.html. The problem with IronPython is that it can’t use the C/C++ libraries used by Python, which means that some of these solutions won’t work. For example, it’s unlikely that you could use IronPython in a Common Gateway Interface (CGI) application because CGI relies on modules written in C/ C++. Consequently, the first rule for Web development with IronPython is to make sure that IronPython can actually interact with the desired modules.

Unfortunately, IronPython isn’t integrated into Visual Studio. Because Visual Studio relies on some level of integration to provide full Web development support, you’ll almost certainly find that some projects are out of reach. It’s not that you can’t create them using IronPython; it’s that the process would be so horribly time consuming and error prone that using another language would be a better idea. You do have full .NET capability when working with IronPython, as demonstrated by the example in this chapter. All you really need is to add Dynamic Language Runtime (DLR) support to the picture and things will work out just fine.

The picture isn’t all gloom. IronPython actually proves to be a good development language for some tasks. As you progress through this chapter, you’ll find that IronPython actually makes code behind tasks exceptionally easy. It’s conceivable that you can build and test simple Web projects using IronPython considerably faster than using another language such as C# or Visual Basic.NET.

It’s important to remember that you don’t have to work with IronPython alone. Your Web application can include other technologies, such as Silverlight. You can also rely on other languages, such as C# or Visual Basic.NET, to fill in the gaps in IronPython coverage. Having another language at your disposal is all about flexibility, and IronPython is a great add-on language for any Web application project.

Obtaining ASP.NET Dynamic Language Support

Microsoft built ASP.NET with extensibility in mind, but the native support tends to focus more on static languages such as Visual Basic.NET and C#, rather than dynamic languages such as IronPython. The Dynamic Language Runtime (DLR) is an add-on for ASP.NET that makes it possible to use languages, such as IronPython, that make typing decisions at run time, rather than compile time. You must download and install this support before you can use IronPython to create a Web application. The following sections describe DLR in more detail.

DLR Limitations

The DLR is currently a work in progress. The overall feel is of an alpha product that shows promise, but still has more than a few warts. In addition, the product currently lacks these features (in order of their importance to you as a developer):

  • IntelliSense: Most developers depend on IntelliSense to provide clues as to what works and what doesn’t — in essence, what to write next. Without IntelliSense support, most developers find themselves peaking at the documentation and spending hours being frustrated with the development environment. Because a default relies so heavily on IntelliSense during the entire development process, its lack is keenly felt. Let’s hope that Microsoft will choose to add this feature sooner rather than later.
  • Limited designer support: If you worked through the examples in Chapter 8, you probably have a good idea of why designer support is so important. Sure, you can create a perfectly usable interface without a designer, but doing so becomes time consuming and many developers give up before they get their user interface completely right. The better the designer support, the faster you can work.
  • Project templates: The lack of templates isn’t anything new. You’ve created examples throughout this book without them, so not having them now isn’t that big a deal. However, it will be nice to have the convenience of project templates when Microsoft creates them.
  • ASP.NET Model-View-Controller (MVC) pattern: MVC is a development pattern that Microsoft is pushing very hard because it provides better control over the development process. You can learn more about ASP.NET MVC at http://www.asp.net/mvc/. Microsoft eventually plans to add MVC to DLR by extending MVC for IronRuby (see http:// github.com/jschementi/ironrubymvc for additional details).
  • Language Services Support: A new language feature that Microsoft plans to provide sometime in the future. Details about this feature are unavailable as of this writing, but it’s likely that Language Services Support will somehow make DLR more flexible and able to support a myriad of languages.

If you read the information at http://www.asp.net/DynamicLanguages/ carefully, you notice that it contains a wealth of caveats. The DLR is essentially an alpha version of a product that may not even appear as part of ASP .NET. Consequently, you need to use DLR to see what’s possible, rather than as a production tool for needs you have today. Anything you create using DLR today is likely to require updates and changes tomorrow (assuming you can use DLR at all). The lack of solid DLR commitment by Microsoft is one reason this chapter provides an overview of ASP.NET application development, rather than in-depth information.

Getting DLR

Before you can use DLR, you must download and install it. The files you need for the installation appear at http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=17613. Download both the documentation and binaries files so that you have a complete setup. The documentation file is very small, so you might not think you even received the download at first.

The files are simple ZIP files, so you can extract them to your root directory. The examples in this chapter use a source folder of C:ironpython-2.6-beta1-aspnet-200905 for the DLR-specific examples. You need to change the path in the examples to match the location you used to extract the files on your machine.

The ironpython-2.6-beta1-aspnet-200905.ZIP file contains two folders: bin and examples. The bin folder contains a complete set of IronPython files, including the files required to make an IronPython script work as an ASP.NET application.

Using the Sample Applications

The ironpython-2.6-beta1-aspnet-200905examples folder contains two example applications. You should run at least one of these examples to ensure you have a good installation (simple as it is, sometimes there’s a configuration on your machine that prevents the examples from working as intended). To start as simply as possible, use the following steps to run the basic Hello Web forms example:

  1. Open a command prompt. It doesn’t have to be a VS2010 command prompt — any command prompt will do. If you’re using Vista or above, make sure you open the command prompt with administrator rights by right-clicking the Command Prompt icon in the Start menu and choosing Run As Administrator from the context menu.
  2. Type CD WINDOWSMicrosoft.NETFrameworkv2.0.50727 and press Enter to change directories to the .NET Framework 2.0 folder on your system. If you don’t have the .NET Framework 2.0 installed on your system, then type CD Program FilesCommon Files Microsoft SharedDevServer10.0 and press Enter to gain access to the ASP.NET Development Server folder on your system.
  3. Type WebDev.WebServer /port:85 /path:C:ironpython-2.6-beta1-aspnet-200905 exampleshello-webforms and press Enter (change your path information if you need to do so). You may need to type WebDev.WebServer20 if you’re using an alternate folder location. This action will start the ASP.NET Development Server on port 85. You’ll see an ASP.NET Development Server icon appear in the Notification Area.
  4. Right-click the ASP.NET Development Server icon and choose Open in Web Browser from the context menu. Your Web browser should open up and you should see a simple Web page like the one shown in Figure 11-1.

    This simple Web page relies on IronPython for support.
    Figure 11-1: This simple Web page relies on IronPython for support.
  5. Test the Web page to see if it works. Type your name in the Enter Your Name field and click Submit. You should see your name appear in place of the Your Name Here label shown in Figure 11-1. It’s a really simple example, but it will tell you whether you‘re getting the right results.
  6. Right-click the ASP.NET Development Server icon and choose Stop from the context menu. The server is no longer available.

The DLR package includes a second example. To use it, simply type WebDev.WebServer /port:85 / path:C:ironpython-2.6-beta1-aspnet-200905examplesalbum-handler and press Enter at the command prompt (again, make sure you use a path that matches your machine setup). This example is a little more complicated. When you initially display the browser, you see a list of filenames, which isn’t particularly helpful. Click the album-ipy.aspx entry and wait a few seconds. Eventually, you’ll see the Test icon shown in Figure 11-2.

The second example is a little more interesting
Figure 11-2: The second example is a little more interesting — at least it has graphics.

Click the Test icon and you’ll see more graphics. You can click the La Flore icon to see some flowers, or click one of the scenic images, as shown in Figure 11-3. Spend a bit of time with this application and you’ll find that it really is pretty interesting. Now, consider that this application is written in IronPython. Even though DLR isn’t a fully supported technology yet, it does have some amazing capabilities.

Creating a Web Site

You might think that creating an IronPython Web site is going to be complex at first — especially with a lack of IDE support. However, an IronPython Web site doesn’t really contain many parts that you have to worry about. A basic Web site starts with the root folder, which contains three items:

  • An Active Server Page Framework (.ASPX) file containing the user interface
  • The code behind file for the .ASPX file containing IronPython code
  • The Web.CONFIG file that contains all of the required configuration entries (such as the location of the special binaries used to interpret the IronPython script)

In addition to these files, you need a bin folder that contains the executables for working with IronPython. These files won’t vary between projects, so you may as well copy the bin folder whenever you create a new project. The following list is the files found in the bin folder.

  • IronPython.DLL
  • IronPython.Modules.DLL
  • Microsoft.Dynamic.DLL
  • Microsoft.Scripting.Core.DLL
  • Microsoft.Scripting.DLL
  • Microsoft.Scripting.ExtensionAttribute.DLL
  • Microsoft.Web.Scripting.DLL

It’s important to remember that IronPython is scripted. As a consequence, much of the compiled language baggage that you might have had to consider in the past isn’t an issue when working with IronPython. Don’t let the seeming simplicity of the Web site fool you, however; IronPython is just as capable as any other language.

Building a Basic ASP.NET Site Using IronPython

You’ve seen someone else’s examples for working with IronPython on a Web site. Now it’s time to create an IronPython Web site of your own. The example in this section isn’t meant to do anything too impressive. This section focuses on a process you can use to build ASP.NET applications of your own instead. The following sections describe every step you need to take to create any ASP.NET application using IronPython.

Creating the Project

Neither Visual Studio nor DLR provides a project template for you to use. In addition, you have more work to do when creating an ASP.NET application, so creating a project isn’t quite as easy as it should be. The DLR instructions suggest copying the hello-webforms folder to another location and using it as a starting point, which will work but will prove cumbersome. The following steps create a different kind of setup, one that will prove better in the long run, but require more work on your part now.

  1. Create a folder for your application. The example uses Calculator, but you can use any name you want.
  2. Copy the hello-webformsbin folder to your project folder.
  3. Create a .BAT file (the example uses Start.BAT) with the following content:
    [code]
    @Echo Off
    REM Change this directory to the location of WebDev.Webserver
    CD WINDOWSMicrosoft.NETFrameworkv2.0.50727
    REM Start the Web server.
    Start /B WebDev.WebServer /port:%1 /path:%2
    REM Open the Web browser to the right Web site.
    Start “C:Program FilesMozilla FirefoxFirefox” http://localhost:%1/
    @Echo On
    [/code]
    This batch file changes directories to the correct location for the WebDev.Webserver utility. It then starts the WebDev.Webserver utility with the port and path information you provide as part of the project arguments. Finally, it starts your browser so you can see the results.
  4. Start Visual Studio and choose File ➪ Open ➪ Project/Solution. Locate CMD.EXE found in the WindowsSystem32 folder and click Open. This step creates a project based on the command processor.
  5. Right-click cmd in Solution Explorer and choose Properties from the context menu. You see the General tab of the cmd Properties window, as shown in Figure 11-4.

    Set the properties for your project.
    Figure 11-4: Set the properties for your project.
  6. Type /C Start.BAT 85 “C:255 – Source CodeChapter11Calculator“ in the Arguments field. The arguments start with the /C command line switch, which tells the command processor to process all of the commands you’ve requested, and then terminate itself. The Start.BAT argument is the file you created in Step 3. This batch file requires two input arguments, the port number you want to use and the location of the example application.
  7. Type the location of your project, such as C:255 – Source CodeChapter11 Calculator, in the Working Directory folder.
  8. Right-click the Solution entry in Solution Explorer and choose Add ➪ New Item from the context menu. You’ll see the Add New Item dialog box shown in Figure 11-5.
  9. Highlight the HTML Page template and type Default.ASPX in the Name field. Click Add. Visual Studio adds a new ASPX file to your project.
  10. Right-click the Solution entry in Solution Explorer and choose Add ➪ New Item from the context menu to display the Add New Item dialog box again.
  11. Highlight the Text File template and type Default.ASPX.py in the Name field. Click Add. Visual Studio adds the code behind file for the Default.ASPX page.
  12. Copy the Web.CONFIG file from the hello-webforms folder to your project folder.
  13. Right-click the Solution entry in Solution Explorer and choose Add ➪ Existing Item from the context menu to display the Add Existing Item dialog box shown in Figure 11-6.
    Select items to add from the Add New Item dialog box.
    Figure 11-5: Select items to add from the Add New Item dialog box.

    Copy the Web.CONFIG file from an existing project.
    Figure 11-6: Copy the Web.CONFIG file from an existing project.
  14. Locate and highlight the Web.CONFIG file as shown in Figure 11-6. Click Add. Your project is now ready to go.

Defining the User Interface

The template used for this example is actually an .HTM file so it doesn’t contain a few essential entries; you need to use it as an .ASPX file. First, you must tell the Web service which language to use and where to find the code behind file. Add the following code as the first line in the .ASPX file.

[code]
<%@ Page Language=”IronPython” CodeFile=”Default.aspx.py” %>
[/code]

When you complete this step, close and then reopen the Default.ASPX file. Otherwise, the IDE is going to spend a lot of time complaining. The next step is to change where the code runs. You want it to run at the server so you change the <head> tag, as shown in the following code:

[code]
<head runat=”server”>
[/code]

Now you need to create content for the Web page. In this case, the user interface provides a simple four-function calculator. Listing 11-1 shows the code needed to perform this task.

Listin g 11-1: Defining a user interface for the example application

[code]
<form ID=”form1” runat=”server”>
<div>
<asp:Label ID=”lblInput” runat=”server”
text=”Type an input value:”/>
<asp:TextBox ID=”txtInput” runat=”server” />
<asp:Label ID=”lblError” runat=”server”
text=”Type a number in the input field!”
style=”color:red”
visible=”false” />
</div>
<div>
<asp:Button ID=”btnAdd” runat=”server” text=”+”
OnClick=”btnAdd_Click” />
<asp:Button ID=”btnSub” runat=”server” text=”-“
OnClick=”btnSub_Click” />
<asp:Button ID=”btnMul” runat=”server” text=”*“
OnClick=”btnMul_Click” />
<asp:Button ID=”btnDiv” runat=”server” text=”/“
OnClick=”btnDiv_Click” />
</div>
<div>
<asp:Label ID=”lblResult” runat=”server”
text=”Current Value:” />
<asp:TextBox ID=”txtResult” runat=”server” text=”0”
readonly=”true” />
</div>
<div>
<asp:Button ID=”btnClear” runat=”server” text=”Clear”
OnClick=”btnClear_Click” />
</div>
</form>
[/code]

As the listing shows, you work with IronPython code in the same way that you work with any .ASPX file. Unfortunately, the designer has not a clue as to what to do with your code, so you have to write it all by hand. Theoretically, you could start a Web project and then simply move the .ASPX file from that project to your IronPython project, but that seems like a lot of work unless your interface is relatively complex. If you find that you can’t quite remember all the ASP.NET controls at your disposal, you can find a complete list at http://www.w3schools.com/ASPNET/aspnet_refwebcontrols.asp in an easily accessible form.

In this case, the controls appear in four separate groups: input, control, result, and clearing. Figure 11-7 shows a typical view of the example form. Some common errors that developers make are not including the runat=”server” attribute and not providing the proper connectivity to events in the code behind, such as OnClick=”btnClear_Click”. Notice that you can use styles, just as you normally do, with the style attribute. One of the attributes that developers can forget about is visible=”false”, which makes the control invisible.

The example form is a simple four-function calculator.
Figure 11-7: The example form is a simple four-function calculator.

Creating the Code Behind

The code behind for this example is in pure IronPython. So, while you won’t see much difference in Default.ASPX, you’ll find that Default.ASPX.py looks completely different from any Web project you’ve worked with in the past. Listing 11-2 shows the code for this example.

Listin g 11-2: Creating the code behind for the example

[code]
# Import the required assemblies.
from System import *
# Respond to an Add button click.
def btnAdd_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Addition = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value + Addition
txtResult.Text = str(Value)
# Respond to a Subtraction button click.
def btnSub_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Subtract = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value – Subtract
txtResult.Text = str(Value)
# Respond to a Multiplication button click.
def btnMul_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Multiply = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value * Multiply
txtResult.Text = str(Value)
# Respond to a Division button click.
def btnDiv_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Divide = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value / Divide
txtResult.Text = str(Value)
# Respond to a Clear button click.
def btnClear_Click(sender, e):
txtResult.Text = ‘0’
[/code]

The code begins by importing the required assemblies. As with any IronPython application, you can use a combination of Python modules and .NET assemblies to create your application. You also have full access to both Python and .NET functionality in your application, so the considerable flexibility that IronPython provides is still available in this environment.

Each of the event handlers must provide both the sender and e arguments as shown. You don’t include a self argument in this case, as you would with other IronPython code. As you might expect, the sender argument contains a reference to the control that called the event handler, while e contains a list of event arguments (normally set to None).

The four math buttons begin by obtaining the current value of txtResult (the output TextBox) as an Int32 value. Because txtResult is read-only, you don’t need to worry about someone putting an incorrect value into it. Consequently, this task doesn’t provide any error trapping code.

The next step is to obtain the new value for the math operation from txtInput. In this case, you’re relying on the user to provide the correct input value, which means that the application code could receive anything. Someone might even try to enter a script in order to fool your application into doing something improper. Using the Int32.Parse() method means that any input other than a number triggers an exception, which your code can handle by simply not processing the input. The try…except structure does just that. If the user inputs an incorrect value, the Web page displays an error message, rather than doing anything with the input.

Now that the code has two inputs to process, it performs the required math operation. After the math operation is complete, the code outputs the result to txtResult.Text.

The btnClear_Click() event handler is relatively simple. All it does is place a 0 in txtResult .Text. The next math operation starts with a zero value, which means that txtResult is cleared.

Starting the Visual Studio Built-In Web Server

It’s time to begin testing your application. Many developers don’t realize it, but the .NET Framework includes a special utility that makes it possible to host Web sites without having a full-fledged Web server. The WebDev.WebServer utility originally appeared as part of the .NET Framework 2.0. When you build an application for testing purposes with Visual Studio, you’re using this built-in Web server to execute the code.

You find the WebDev.WebServer.EXE file in the WINDOWSMicrosoft.NETFrameworkv2.0.50727 folder on your system. Alternatively, you can also find versions of this utility in the Program Files Common FilesMicrosoft SharedDevServer10.0 folder as WebDev.WebServer20.EXE or WebDev.WebServer40.EXE.

The amazing part of the built-in Web server is that it works fine for any Web site using any kind of code. If you want to test your standard HTML pages, that’s fine — just point the built-in Web server to the correct directory on your hard drive. Of course, you can’t run some types of applications because the built-in Web server isn’t designed to handle them. For example, you can’t execute your PHP code. This little deficiency doesn’t matter for your IronPython application, however, because the built-in Web server will see it as a standard ASP.NET application.

Unlike your full-fledged Web server, the built-in Web server doesn’t provide outside access, which is the reason you want to use it to test your unsecured, experimental IronPython Webforms application. You don’t have to worry about prying eyes seeing your tests and possibly using them as a means to gain entrance to your machine. More important, because this server is virtual, it’s less likely that a failed experiment will cause your system to crash. The following sections describe the WebDev.WebServer utility in more detail.

Understanding the WebDev.WebServer Command Line Syntax

The WebDev.WebServer utility provides only a few command line switches because you perform most configuration tasks using a special Notification Area icon. Here’s the command line syntax for this utility.

[code]
WebDev.WebServer /port:<PortNumber> /path:<PhysicalPath> [/vpath:<VirtualPath>]
WebDev.WebServer20 /port:<PortNumber> /path:<PhysicalPath>
[/vpath:<VirtualPath>]
WebDev.WebServer40 /port:<PortNumber> /path:<PhysicalPath>
[/vpath:<VirtualPath>]
[/code]

The following list provides an explanation of each of the command line switches.

  • /port:PortNumber: Defines the port number used to host the application. Because your application isn’t accessible to the outside world and you use a local browser to access the Web server, the port you use isn’t as important as it usually is. You can’t select port 80 if you also have IIS installed on the system. Any port number between 1 and 65,535 will work as long as the port you select isn’t in use.
  • /path:PhysicalPath: Specifies the physical location of the application you want to host in the browser. You must provide the full path, but you don’t include a filename.
  • /vpath:VirtualPath: Provides a virtual path for the application where VirtualPath is normally the application name, such as /MyApp. The default setting provides a virtual path of /.
  • ?: Displays the help information for the WebDev.WebServer utility.

Using the Built-In Web Server with a Batch File

When you use a batch (.BAT) file to start the application, use the Start utility to execute WebDev .WebServer. Otherwise, the Web server won’t start properly. In addition, you should include the Start utility’s /B command line switch (it isn’t mandatory). The /B command line switch tells Windows not to open a new window to start the application. If Windows opens a new window, the Web server will start, but it may not display the Web page. Here’s a modified command line for batch files.

[code]
Start /B WebDev.WebServer /port:7171 /path:”F:My Web Site”
[/code]

Interacting with the Built-In Web Server

The WebDev.WebServer utility creates an icon in the Notification Area when you start it. In fact, a popup message alerts you to the presence of this icon. Right-click this icon and you see three options:

  • Open in Web Browser: Tells the utility to start the default Web browser and navigate to the Web page hosted by the Web server. In most cases, WebDev.WebServer uses the same defaults as any full Web server you have set up on your machine. Otherwise, you can count on these defaults working:
    • Default.HTM
    • Default.ASP
    • Default.ASPX
    • Index.HTM
  • Stop: Stops the server and makes any Web pages inaccessible.
  • Show Details: Displays the ASP.NET Development Server dialog box shown in Figure 11-8 where you can see details about the Web server. In addition, this dialog box provides a link to access the default Web page.
The ASP.NET Development Server dialog box provides details about the Web server.
Figure 11-8: The ASP.NET Development Server dialog box provides details about the Web server.

Considering the Built-In Web Server Limitations

It’s important to realize that we’re not using the WebDev.WebServer utility for production purposes. The following list helps you better understand why you can’t use this utility for every purpose.

  • Functionality: The WebDev.WebServer utility doesn’t create a full-fledged Web server. Some of the functionality you rely on, such as the ability to add users or work with virtual directories, simply isn’t available.
  • Security: Any security that you want to set up has to appear as part of your application in a Web.CONFIG file. The WebDev.WebServer utility does tend to follow whatever rules you set for Internet Information Server (IIS) if you have it installed. However, you can’t count on this behavior when working on your own and you certainly can’t count on it when you send the application to other machines.
  • Administrative tools support: Anything you normally configure through the Internet Information Services console located in the Administrative Tools folder of the Control Panel is unavailable when working with the WebDev.WebServer utility. Consequently, if your application relies on a special ISAPI Filter, you won’t be able to execute it using the WebDev.WebServer utility. The same holds true for anything else that you normally have to add using the Internet Information Services console.
  • Single user Web server: It’s important to remember that this is a single-user Web server. No one outside the local machine can access the Web server because theoretically, it doesn’t exist — it’s virtual. This means that you can’t perform some types of testing using the built-in Web server. The feature that makes it so secure also prevents you from performing some kinds of real-world testing. Multi-user applications simply won’t work with the built-in Web server.

Savvy developers can get around some of the WebDev.WebServer configuration limitations through judicious use of the Web.CONFIG file and by creating resources locally. Make sure you don’t assume that an application won’t work simply because you have to configure it in a new way.

Performing Basic Testing

Your IronPython Web application works just like any other Web application you create. Because IronPython is fully .NET capable, you can use any control set you want within the application. Of course, you also have access to standard Web controls. All of this flexibility increases complexity and makes it necessary to test your application fully.

The example application is relatively simple, so testing isn’t cumbersome. When you first start the application by pressing Ctrl+F5, you see the Web page shown in Figure 11-7. When you type a value into the Type an Input Value field and click one of the math buttons (+, -, *, or /), the application performs the desired task. Trying to input an invalid value triggers an error message like the one shown in Figure 11-9.

Incorrect values trigger an error message.
Figure 11-9: Incorrect values trigger an error message.

Interestingly enough, the scripting nature of IronPython makes it possible to use IronPython to test your Web application. This gives you an advantage over compiled languages such as C# and Visual Basic.NET.

Considering Debugging

You may be wondering whether this project can provide any debugging. The fact is that you don’t get direct debugging when working with DLR, even if you use a full Web server. However, there are four ways in which you can debug your application.

  • Use print statements and other old standbys to determine what your application is doing.
  • Attach the debugger to the running process after the fact by choosing Debug ➪ Attach to Process within Visual Studio.
  • Import the Microsoft.Scripting.Debugging.DLL found in the Program Files IronPython 2.6 folder and add debugging information manually.
  • Rely on the output error message from the Web server, such as the one shown in Figure 11-10.

    The Web server provides you with error messages as needed.
    Figure 11-10: The Web server provides you with error messages as needed.

Even though Webforms use isn’t quite ready for prime time, you should still take time to experiment. At the very least, try the example programs that come with DLR to see that IronPython is a viable Web development language. Use the example application in this chapter as a starting point for your own experimentation. In short, have a bit of fun using IronPython to create Web pages. Consider how the dynamic nature of IronPython could help you in future Web application development, because DLR won’t remain in alpha status forever.

Getting Started with Visual C# 2010 for Windows Phone

Visual C# 2010 Express

At the time of this writing, the current version of the development tool for Windows Phone 7 is Visual Studio 2010. To make development simple for newcomers to the Windows Mobile platform, Microsoft has set up a package that will install everything you need to develop, compile, and run code in the emulator or on a physical Windows Phone device—for free. The download URL at this time is http://www.microsoft. com/express/Phone. If you are using a licensed copy of Visual Studio 2010, such as the Professional, Premium, or Ultimate edition, then you will find XNA Game Studio 4.0 and related tools at http://create.msdn.com (the App Hub website). The App Hub website, shown in Figure 2.1, also contains links to the development tools.

The App Hub website has download links to the development tools.
FIGURE 2.1 The App Hub website has download links to the development tools.

The most common Windows Phone developer will be using the free version of Visual C# 2010, called the Express edition. This continues the wonderful gift Microsoft first began giving developers with the release of Visual Studio 2005. At that time, the usual “professional” versions of Visual Studio were still available, of course, and I would be remiss if I failed to point out that a licensed copy of Visual Studio is required by any person or organization building software for business activities (including both for-profit and nonprofit). The usual freelance developer will also need one of the professional editions of Visual Studio, if it is used for profit. But any single person who is just learning, or any organization that just wants to evaluate Visual Studio for a short time, prior to buying a full license, can take advantage of the free Express editions. I speak of “editions” because each language is treated as a separate product. The professional editions include all the languages, but the free Express editions, listed here, are each installed separately:

  • Visual C# 2010 Express
  • Visual Basic 2010 Express
  • Visual C++ 2010 Express

The version of Visual Studio we will be using is called Visual Studio 2010 Express for Windows Phone. This is a “package” with the Windows Phone SDK already prepackaged with Visual C# 2010 Express. (Despite the name, “Visual Studio” here supports only the C# language.) It’s a nice package that makes it very easy to get started doing Windows Phone development. But if you are using Visual Studio 2010 Professional (or one of the other editions) along with the Windows Phone SDK, you will see a lot more project templates in the New Project dialog, shown in Figure 2.2.

The New Project dialog in Visual C# 2010 Express.
FIGURE 2.2 The New Project dialog in Visual C# 2010 Express.
  • Windows Phone Application (Visual C#)
  • Windows Phone Databound Application (Visual C#)
  • Windows Phone Class Library (Visual C#)
  • Windows Phone Panorama Application (Visual C#)
  • Windows Phone Pivot Application (Visual C#)
  • Windows Phone Game (4.0) (Visual C#)
  • Windows Phone Game Library (4.0) (Visual C#)
  • Windows Game (4.0) (Visual C#)
  • Windows Game Library (4.0) (Visual C#)
  • Xbox 360 Game (4.0) (Visual C#)
  • Xbox 360 Game Library (4.0) (Visual C#)
  • Content Pipeline Extension Library (4.0)
  • Empty Content Project (4.0) (Visual C#)

As you can see, even in this limited version of Visual Studio 2010, all the XNA Game Studio 4.0 project templates are included—not just those limited to Windows Phone. The project templates with “(4.0)” in the name come from the XNA Game Studio SDK, which is what we will be primarily using to build Windows Phone games. The first five project templates come with the Silverlight SDK. That’s all we get with this version of Visual Studio 2010. It’s not even possible to build a basic Windows application here—only Windows Phone (games or apps), Windows (game only), and Xbox 360 (obviously, game only). The first five project templates are covered in the next section, “Using Silverlight for WP7.”

Did you notice that all of these project templates are based on the C# language? Unfortunately for Visual Basic fans, we cannot use Basic to program games or apps for Windows Phone using Visual C# 2010 Express. You can install Visual Basic 2010 Express with Silverlight and then use that to make WP7 applications. XNA, however, supports only C#.

We don’t look at Xbox 360 development in this book at all. If you’re interested in the subject, see my complementary book XNA Game Studio 4.0 for Xbox 360 Developers [Cengage, 2011].

Using Silverlight for WP7

Microsoft Silverlight is a web browser plug-in “runtime.” Silverlight is not, strictly speaking, a development tool. It might be compared to DirectX, in that it is like a library, but for rich-content web apps. It’s similar to ASP.NET in that Silverlight applications run in a web browser, but it is more capable for building consumer applications (while ASP.NET is primarily for business apps). But the way Silverlight applications are built is quite different from ASP.NET—it’s more of a design tool with an editing environment called Expression Blend. The design goal of Silverlight is to produce web applications that are rich in media support, and it supports all standard web browsers (not just Internet Explorer, which is a pleasant surprise!), including Firefox and Safari on Mac.

Using Expression Blend to Build Silverlight Projects

Microsoft Expression Blend 4 is a free tool installed with the Windows Phone package that makes it easier to design Silverlight-powered web pages with rich media content support. Blend can be used to design and create engaging user experiences for Silverlight pages. Windows application support is possible with the WPF (Windows Presentation Foundation) library. A key feature of Blend is that it separates design from programming. As you can see in Figure 2.3, the New Project dialog in Blend lists the same project types found in Visual C# 2010 Express.

Expression Blend is a Silverlight development tool for web designers.
FIGURE 2.3 Expression Blend is a Silverlight development tool for web designers.

Let’s create a quick Expression Blend project to see how it works. While working on this quick first project, keep in mind that we’re not building a “Blend” project, but a “Silverlight” project—using Blend. Blend is a whole new Silverlight design and development tool, not affiliated with Visual Studio (but probably based on it). The Silverlight library is already installed on the Windows Phone emulator and actual phones.

Here’s how to create the project:

  1. Create a Windows Phone Application project using the New Project dialog. Click File, New Project.
  2. Blend creates a standard project for Windows Phone, complete with an application title and opening page for the app.
  3. Run the project with Project, Run Project, or by pressing F5. The running program is shown in Figure 2.4.
Our first project with Expression Blend.
FIGURE 2.4 Our first project with Expression Blend.

This is a useless app, but it shows the steps needed to create a new project and run it in the Windows Phone emulator. Did you notice how large the emulator window appears? That’s full size with respect to the screen resolution of WP7. As you’ll recall from the first hour, the resolution is 480×800. That is enough pixels to support 480p DVD movies, but not the 740p or 1080p HD standards. Still, DVD quality is great for a phone! And when rotated to landscape mode, 800×480 is a lot of screen real estate for a game too.

You can make quick and easy changes to the labels at the top and experiment with the design controls in the toolbox on the left. Here you can see that the application title and page title have been renamed, and some images and shapes have been added to the page. Pressing F5 again brings it up in the emulator, shown in Figure 2.5.

Now that you’ve seen what’s possible with Expression Blend’s more designer-friendly editor, let’s take a look at the same Silverlight project in Visual Studio 2010.

Silverlight Projects

The Silverlight runtime for WP7 supports some impressive media types with many different audio and video codecs, vector graphics, bitmap graphics, and animation. That should trigger the perimeter alert of any game developer worth their salt! Silverlight brings some highly interactive input mechanisms to the Web, including accelerometer motion detection, multitouch input (for devices that support it),camera, microphone input, and various phone-type features (like accessing an address book and dialing).

Making quick changes to the page is easy with Expression Blend.
FIGURE 2.5 Making quick changes to the page is easy with Expression Blend.

To find out whether your preferred web browser supports Silverlight, visit the installer web page at http://www.microsoft.com/getsilverlight/get-started/install.

The Visual Studio 2010 project templates specific to Silverlight are highlighted in bold in the list below. These are the same project templates shown in Expression Blend!

  • Windows Phone Application (Visual C#)
  • Windows Phone Databound Application (Visual C#)
  • Windows Phone Class Library (Visual C#)
  • Windows Phone Panorama Application (Visual C#)
  • Windows Phone Pivot Application (Visual C#)
  • Windows Phone Game (4.0) (Visual C#)
  • Windows Phone Game Library (4.0) (Visual C#)
  • Windows Game (4.0) (Visual C#)
  • Windows Game Library (4.0) (Visual C#)
  • Xbox 360 Game (4.0) (Visual C#)
  • Xbox 360 Game Library (4.0) (Visual C#)
  • Content Pipeline Extension Library (4.0)
  • Empty Content Project (4.0) (Visual C#)

Let’s create a quick project in Visual Studio in order to compare it with Expression Blend. You’ll note right away that it is not the same rich design environment, but is more programmer oriented.

Comparing Visual Studio with Expression Blend

Let’s create a new project in Visual C# 2010 in order to compare it with Expression Blend. Follow these steps:

  1. Open the New Project dialog with File, New Project.
  2. Next, in the New Project dialog, choose the target folder for the project and type in a project name, as shown in Figure 2.6.

    Creating a new Silverlight project in Visual C# 2010 Express.
    FIGURE 2.6 Creating a new Silverlight project in Visual C# 2010 Express.
  3. Click the OK button to generate the new project shown in the figure. Not very user-friendly, is it? First of all, double-clicking a label does not make it editable, among other limitations (compared to Expression Blend). Where are the control properties? Oh, yes, in the Properties window in Visual Studio. See Figure 2.7. This is also very data-centric, which programmers love and designers loathe. The view on the left is how the page appears on the device (or emulator); the view on the right is the HTML-like source code behind the page, which can be edited.
  4. Bring up the Properties window (if not already visible) by using the View menu. Select a control on the page, such as the application title. Scroll down in the Properties to the Text property, where you can change the label’s text, as shown in Figure 2.8. Play around with the various properties to change the horizontal alignment, the color of the text, and so on. Open the Toolbox (located on the left side of Visual Studio) to gain access to new controls such as the Ellipse control shown here.
    The new Silverlight project has been created.
    FIGURE 2.7 The new Silverlight project has been created.

    Adding content to the Silverlight page.
    FIGURE 2.8 Adding content to the Silverlight page.

XNA Game Studio

XNA Game Studio 4.0 was released in the fall of 2010. (From now on, let’s just shorten this to “XNA” or “XNA 4.0”, even though “Game Studio” is the name of the SDK, and “XNA” is the overall product name.) XNA 4.0 saw several new improvements to the graphics system, but due to the hardware of the Xbox 360, XNA is still based on Direct3D 9 (not the newer versions, Direct3D 10 or 11). This is actually very good news for a beginner, since Direct3D 9 is much easier to learn than 10 or 11. Although XNA abstracts the C++-based DirectX libraries into the C#-based XNA Framework, there is still much DirectX-ish code that you have to know in order to build a capable graphics engine in XNA. While XNA 4.0 added WP7 support, it simultaneously dropped support for Zune (the portable multimedia and music player).

I have a Zune HD, and it’s a nice device! It can play 720p HD movies and even export them to an HDTV via an adapter and HDMI cable. It plays music well too. But, like many consumers, I just did not have much incentive to go online and download games for the Zune. This is, of course, purely a subjective matter of opinion, but it’s disappointing for game developers who put effort into making games for Zune. Fortunately, the code base is largely the same (thanks to XNA and C#), so those Zune games can be easily ported to WP7 now.

Rendering states, enumerations, return values, and so forth are the same in XNA as they are in Direct3D, so it could be helpful to study a Direct3D book to improve your skills as an XNA programmer!

The project templates for Windows Phone might surprise you—there are only two! We can build a Windows Phone game or a game library. All the other templates are related to the other platforms supported by XNA.

  • Windows Phone Application (Visual C#)
  • Windows Phone Databound Application (Visual C#)
  • Windows Phone Class Library (Visual C#)
  • Windows Phone Panorama Application (Visual C#)
  • Windows Phone Pivot Application (Visual C#)
  • Windows Phone Game (4.0) (Visual C#)
  • Windows Phone Game Library (4.0) (Visual C#)
  • Windows Game (4.0) (Visual C#)
  • Windows Game Library (4.0) (Visual C#)
  • Xbox 360 Game (4.0) (Visual C#)
  • Xbox 360 Game Library (4.0) (Visual C#)
  • Content Pipeline Extension Library (4.0)
  • Empty Content Project (4.0) (Visual C#)

Let’s build a quick XNA project for Windows Phone to see what it looks like. We’ll definitely be doing a lot of this in upcoming chapters since XNA is our primary focus (the coverage of Silverlight was only for the curious—grab a full-blown Silverlight or Expression Blend book for more complete and in-depth coverage.

Creating Your First XNA 4.0 Project

Let’s create a new XNA 4.0 project in Visual C# 2010, so we can use this as a comparison with the previous project created with Expression Blend. Follow these steps:

  1. Create a new project. We’ll be basing these tutorials around Visual Studio 2010 Express for Windows Phone. The processes will be similar to using the Professional version, but you will see many more project templates in the New Project dialog. Open the File menu and choose New Project. The New Project dialog is shown in Figure 2.9.

    Creating a new XNA 4.0 project.
    FIGURE 2.9 Creating a new XNA 4.0 project.
  2. The new project has been created. Note, from Figure 2.10, the code that has been automatically generated for the XNA project. If you have ever worked with XNA before, this will be no surprise—the code looks exactly like the generated code for Windows and Xbox 360 projects!

    The new XNA 4.0 project has been created.
    FIGURE 2.10 The new XNA 4.0 project has been created.
  3. Run the project with Build, Run, or by pressing F5. The emulator will come up, as shown in Figure 2.11. Doesn’t look like much—just a blue screen! That’s exactly what we want to see, because we haven’t written any game code yet.
  4. Add a SpriteFont to the Content project. Right-click the content project, called XNA ExampleContent(Content) in the Solution Explorer. Choose Add, New Item, as shown in Figure 2.12.
    Running the XNA project in the Windows Phone emulator.
    FIGURE 2.11 Running the XNA project in the Windows Phone emulator.

    Adding a new item to the content project.
    FIGURE 2.12 Adding a new item to the content project.
  5. In the Add Item dialog, choose the Sprite Font item from the list, as shown in Figure 2.13, and leave the filename as SpriteFont1.spritefont.

    Adding a new SpriteFont content item to the project.
    FIGURE 2.13 Adding a new SpriteFont content item to the project.
  6. Create the font variable. The comments in the code listing have been removed to make the code easier to read. We’ll dig into the purpose of all this code in the next hour, so don’t be concerned with understanding all the code yet. Type in the two new bold lines of code shown here to add the font variable.
    [code]
    public class Game1 : Microsoft.Xna.Framework.Game
    {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    //new font variable
    SpriteFont font;
    public Game1()
    {
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = “Content”;
    TargetElapsedTime = TimeSpan.FromTicks(333333);
    }
    [/code]
  7. Load the font. Enter the two new lines shown in bold in the LoadContent method.
    [code]
    protected override void Initialize()
    {
    base.Initialize();
    }
    protected override void LoadContent()
    {
    spriteBatch = new SpriteBatch(GraphicsDevice);
    //load the font
    font = Content.Load<SpriteFont>(“SpriteFont1”);
    }
    protected override void UnloadContent()
    {
    }
    [/code]
  8. Print a message on the screen. Using the SpriteBatch and SpriteFont objects, we can print any text message. This is done from the Draw method—add the code highlighted in bold.
    [code]
    protected override void Update(GameTime gameTime)
    {
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
    ButtonState.Pressed)
    this.Exit();
    base.Update(gameTime);
    }
    protected override void Draw(GameTime gameTime)
    {
    GraphicsDevice.Clear(Color.CornflowerBlue);
    //print a message
    spriteBatch.Begin();
    string text = “HELLO FROM XNA!”;
    Vector2 pos = font.MeasureString(text);
    spriteBatch.DrawString(font, text, pos, Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
    }
    }
    [/code]
  9. Run the program using Debug, Start Debugging, or by pressing F5. The program will come up in the emulator, shown in Figure 2.14. Now there’s just one big problem: The font is too small, and the screen needs to be rotated to landscape mode so we can read it!
  10. Click the emulator window to cause the little control menu to appear at the upper right. There are two icons that will rotate the window left or right, allowing us to switch from portrait to landscape mode. All XNA projects will default to portrait mode by default. Landscape mode is shown in Figure 2.15.
    The text message is displayed in the emulator— sideways!
    FIGURE 2.14 The text message is displayed in the emulator— sideways!

    Rotating the emulator window to landscape mode for XNA projects.
    FIGURE 2.15 Rotating the emulator window to landscape mode for XNA projects.
  11. Enlarge the font. We’re almost done; there’s just one final thing I want to show you how to do here. Open the font file you created, SpriteFont1. spritefont. Change the size value from 14 to 36. Now rerun the project by pressing F5. The new, large font is shown in Figure 2.16.

    Enlarging the font to make it more readable.
    FIGURE 2.16 Enlarging the font to make it more readable.

XNA or Silverlight: What’s the Verdict?

We have now seen two projects developed with the two different, and somewhat competing tools: XNA and Silverlight. Which should we choose? This is really a matter of preference when it comes to developing a game. Although XNA is far more capable due to its rendering capabilities, Silverlight can be used to make a game as well, with form-based control programming. For portable, touchscreen applications, it’s a given: Silverlight. But for serious game development, XNA is the only serious option.

We covered quite a bit of information regarding Visual Studio 2010, the project templates available for Windows Phone, and the value-added tool Expression Blend. A sample project was presented using Expression Blend with a corresponding Silverlight project in Visual Studio, as well as an XNA project. We’re off to a good start and already writing quite a bit of code! In the next hour, you will create your first Windows Phone game.

Windows Phone Performance Optimization, Fast! to Faster!

Optimizing your game’s performance

Games belong to a class of real-time software. This means that they are not only expected to produce the correct result, but they must also complete this within a fixed time window. In general, game developers shoot for a minimum of displaying 30 frames per second in order to produce smooth, glitch-free animations; and most prefer 60 frames per second. This means that all of the game calculations getting the player input, implementing enemy AI, moving objects, collision detection and handling, and drawing each frame must be completed within 16.7 milliseconds! When you consider that most modern video games have hundreds, or even thousands, of objects that have to be updated and drawn within that time period, it is no wonder that programmers feel they have to optimize every line of code.

However, many XNA programmers are not familiar with the tools and methods for determining when, where, how, or even if, they should optimize their code. The point of this recipe is to help you answer these questions.

Getting ready

The following section will help you to optimize your game’s performances

Design versus implementation

A common response by those who question, or even outright disagree, with the idea that optimizing the code early is a bad idea, is to point out that it is far easier to change software early in its lifecycle than after it has been written. That is, of course, very true. That is why it is important to understand the difference between the design optimization and implementation optimization.

While designing a game (or any software), you must take into account the size and complexity of your game, and select the correct data structures and algorithms that can support it. A simple 2D shooter or a platformer with no more than a hundred objects interacting at any given time can probably get away with a brute force approach for handling movements and collisions. Maintaining a simple list or an array of objects and iterating through it each frame will most likely work fine, and will be very simple to implement and debug.

However, a more complex game world, with perhaps thousands of active objects, will need an efficient method of partitioning the game space to minimize the number of object interaction tests in each frame. Similarly, games requiring detailed enemy AI will need to rely on algorithms that can produce “intelligent” actions as quickly as possible.

There are many resources available that discuss game programming algorithms. Some of them are as follows:

  • The use of quadtrees and octrees for partitioning the game world to minimize collision detection tests
  • The minimax algorithm with alpha-beta pruning for efficiently finding the “best” move in two player strategy games (please check the wiki link for more information at http://en.wikipedia.org/wiki/Alpha-beta_pruning)
  • The A* algorithm for efficient path finding (for more detail about the A* algorithm, please check the wiki link at http://en.wikipedia.org/wiki/A*_search_ algorithm)

The selection of appropriate data structures and algorithms during the design phase has a far greater impact on the eventual performance of your game than any implementation optimization you will make, as your algorithms determine the maximum number of operations your game will have to perform during each frame.

In order to demonstrate this point, imagine that for your first game you write a simple 2D shooter that relies on a brute force approach to collision detection. In every frame, you simply test every active object against every other active object to see if they intersect. As you decide to have only a limited number of enemies active at a time, it works well and easily runs at 60 frames per second.

With that experience under your belt, you now want to write a second game that is far more ambitious. This time you decide to write a Zelda-like adventure game with a large scrolling game board and hundreds of objects moving around it simultaneously. (The Legend of Zelda, an NDS game from Nintendo. You can find out more about this game at: http:// en.wikipedia.org/wiki/The_Legend_of_Zelda.) Using your existing code as a starting point, you get well into the game’s implementation before you discover that the brute force approach that worked very well in your simple game does not work so well in this new game. In fact, you may be measuring screen draws in seconds per frame instead of frames per second!

The reason is that, comparing every object against every other object is what is known as an O(n2) algorithm (for more information on estimating the algorithm time complexity, please see the classic book Introduction to Algorithm second edition, http://www.amazon. com/Introduction-Algorithms-Thomas-H-Cormen/dp/0262033844). That is, the number of operations that have to be performed is related to the square of the number of objects on which you are operating. If you have 10 objects in your game, you only have to perform a hundred tests to see if there are any collisions. If you have a hundred objects, you have to perform ten thousand tests, which may still be possible on a modern PC if each test can be done quickly enough. However, if you have five hundred just five times as many as the last example you will have to perform 250,000 collision tests. Even if each test took only 67 microseconds, you would still be using the entire 16.7 milliseconds frame time (usually at 60 frames per second) just for collision detection. The point is that it does not matter how efficiently you implement that algorithm in a code, its performance will still devolve exponentially with the number of objects in your game, and will therefore be the single greatest limiting factor to the size of your game.

Game runs slow?

Ok, so your game is playable with most of the features you want to be implemented. However, when you test the application, you find that the animation runs like a robot, the character should run, but it is crawling. What is wrong there? You might say, it is about the compiler features, such as the foreach keyword, or ask whether you need to pass the matrices by reference, not by values.

You have two choices: stop there and take a step back or fix it, and start going into each method trying to figure out how to find your way around the problem on a case-by-case basis. Maybe you will even succeed and get the game back into the runnable state that you had it in hours earlier. Maybe you are even lucky enough to have not introduced yet more bugs into the process. However, in all likelihood, you have not fixed the problem and now you have code that does not run any better than when you started, but is harder to understand, harder to debug, and has kludges in it to get around problems that you introduced trying to fix the wrong problem. Your time will be much better spent finding out where your problems are before you try to fix them.

Measuring the running time

A prototype is just a simplified version of software (in this case, your game), that focuses on one particular aspect of it. Prototypes are often used as proofs of concept to show that the software will be able to work as expected. As prototypes don’t have to deal with all of the details that the final software will, they can be written quickly so that, if necessary, different approaches can be evaluated.

Prototypes are frequently used to evaluate user interfaces, so that customers can provide early feedback. This can be useful for game programming too, as if you can implement a working display and control scheme, you may be able to find out what works and doesn’t work before you get too far along in the actual implementation of the game. However, the use of prototypes that we are concerned with here is to determine whether an algorithm is fast enough for the game we want to write. To do that, we will want to benchmark it. Benchmarking is just the process of timing how long an algorithm takes to run.

How to do it…

Fortunately, the .NET framework makes benchmarking very easy by providing the System. Debug.Stopwatch class. The Stopwatch class provides a Start and a Stop method. It keeps track of the total number of clock ticks that occur between calls to Start and Stop. Even better, like a real stopwatch, it keeps a running count of ticks between successive calls to Start and Stop. You can find out how much time has passed by querying its ElapsedTicks or ElapsedMilliseconds properties. A Reset() method lets us reset Stopwatch back to zero.

Now, follow the steps to take advantage of the Stopwatch class:

  1. As a showcase, the following code gives you a general picture on how to use the Stopwatch class for time measuring:
    [code]
    public abstract class Sprite
    {
    public Vector2 Position { get; set; }
    public Color Color { get; set; }
    // Sprite’s collision rectangle in screen coordinates.
    public BoundingRectangle BoundingBox { get; }
    public Sprite(
    string imageName,
    BoundingRectangle boundingBox);
    public virtual void Initialize();
    public virtual void LoadGraphicsContent(
    ContentManager content);
    public virtual void Update(GameTime time);
    public virtual void Draw(SpriteBatch spriteBatch);
    // Tests for collision with another Sprite. If a
    // collision occurs, it calls the Collide method for
    // both Sprites. Returns true if images collide.
    public bool TestCollision(Sprite item);
    // Called when the TestCollision method detects a
    // collision with another Sprite.
    //
    protected virtual void Collide(
    BoundingRectangle overlap,
    Sprite item);
    }
    [/code]
  2. As it is an abstract, it is intended to be used as a parent to other Sprite classes that will implement its behavior, so we will create our own TestSprite class. TestSprite will generate a random starting position, directional movement vector, and speed (in pixels per second), as shown here:
    [code]
    public override void Initialize()
    {
    // Set starting position.
    Position =
    new Vector2(
    random.Next(screenWidth),
    random.Next(screenHeight));
    // Create a random movement vector.
    direction.X = (float)random.NextDouble() * 2 – 1;
    direction.Y = (float)random.NextDouble() * 2 – 1;
    direction.Normalize();
    // Determine random speed in pixels per second.
    speed = (float)random.NextDouble() * 300 + 150;
    }
    [/code]
  3. In each frame, the following code will update its position based on its movement direction, speed, and the amount of time that has elapsed. It will also test to see if it has hit the edge of the screen, and deflect away from it:
    [code]
    public override void Update(GameTime time)
    {
    // Reset color back to white.
    Color = Microsoft.Xna.Framework.Graphics.Color.White;
    // Calculate movement vector.
    Vector2 move =
    (float)time.ElapsedGameTime.TotalSeconds *
    speed * direction;
    // Determine new position.
    UpdatePosition(move);
    }
    private void UpdatePosition(Vector2 move)
    {
    Position += move;
    if ((BoundingBox.Left < 0) ||
    (BoundingBox.Right > screenWidth))
    {
    direction.X = -direction.X;
    Position -= new Vector2(move.X, 0);
    }
    if ((BoundingBox.Top < 0) ||
    (BoundingBox.Bottom > screenHeight))
    {
    direction.Y = -direction.Y;
    Position -= new Vector2(0, move.Y);
    }
    }
    [/code]
  4. We will talk more about collision testing next. For now, we will see what it takes to time just moving our TestSprite around the screen. Inside our game, we will create a TestSprite object and call its Initialize() and LoadGraphicsContent() methods at appropriate places. And we will create SpriteBatch for our game and pass it to Draw(). Now all we need is to use Stopwatch to time it in the Update() method. In order to do this, we will create a couple of helper methods that start and stop Stopwatch, and print the amount of time it takes for each update:
    [code]
    private Stopwatch updateTimer;
    private int updates = 0;
    private int framesPerSecond;
    private void StartTimer()
    {
    updateTimer.Start();
    }
    private void StopTimer()
    {
    updateTimer.Stop();
    updates++;
    // Show the results every five seconds.
    if (updates == 5 * framesPerSecond)
    {
    Debug.WriteLine(
    updates + ” updates took ” +
    updateTimer.ElapsedTicks + ” ticks (” +
    updateTimer.ElapsedMilliseconds +
    ” milliseconds).”);
    int msPerUpdate =
    (int)updateTimer.ElapsedMilliseconds / updates;
    Debug.WriteLine(
    “Each update took ” +
    msPerUpdate + ” milliseconds.”);
    // Reset stopwatch.
    updates = 0;
    updateTimer.Reset();
    }
    }
    [/code]
  5. By putting calls to StartTimer and StopTimer around the calls to our sprite’s Update() method, we will get a report of the average time each call takes:
    [code]
    300 updates took 34931 ticks (9 milliseconds).
    Each update took 0.03 milliseconds.
    300 updates took 24445 ticks (6 milliseconds).
    Each update took 0.02 milliseconds.
    300 updates took 23541 ticks (6 milliseconds).
    Each update took 0.02 milliseconds.
    300 updates took 23583 ticks (6 milliseconds).
    Each update took 0.02 milliseconds.
    300 updates took 23963 ticks (6 milliseconds).
    Each update took 0.02 milliseconds.
    [/code]

How it works…

In step 1, the Initialize(), LoadGraphicsContent(), Update(), and Draw() methods are the standard methods for Windows Phone 7 XNA Game Programming. Additionally, it provides properties for getting and setting the position and color. For collision detection, the Collide() method called by TestCollision() tests for collision with another Sprite BoundingBox values intersect.

In step 3, an actual game may want to determine the actual point of intersection so it could deflect away from that point more realistically. If you need that level of realism, you would probably want to go ahead and implement your strategy here, so you could time it. However, all we are trying to prototype here is a basic update time, so that this version is fine for our needs.

Note that the Update() method does not test for collisions. We don’t want individual sprite testing for collisions because to do so, our Sprite class would have to know about other game objects and we would be severely limiting our design options for collision testing. Any change to our collision-testing algorithm could, and likely would, affect our Sprite class. We want to avoid anything that limits future design changes, so we will give our Sprite class the ability to test for collisions, but require another part of our code to determine what objects should be tested.

In step 6, each call took on average of 20 microseconds (on my development laptop your results will vary). However, notice that the very first set of updates took almost one and a half times as long to run as the others. That is because the first time these methods are called, the JIT compiler compiles the code and our Stopwatch is timing that as well. It is also possible, as this is a fairly small amount of code that is being called repeatedly, that some or all of it may be fitting in the cache, which will increase the speed of later calls.

These show some of the problems with benchmarking code. Another problem is that we are adding some time by using Stopwatch itself. Thus, benchmark times for prototype code can be used as a general guide, but cannot be relied upon for exact values. In fact, exact values of the time it takes for functions to run are very hard to determine. Although intended only to describe quantum phenomena, a variation of the Heisenberg Uncertainty Principle is at play here: the act of measuring something affects the thing being measured.

There’s more…

Now let’s expand our prototype to help us determine whether we can get away with a brute force approach to collision detection.

First, let’s look at the collision handling code that I have already placed in the Collide method. Remember that this gets called, for both sprites, whenever the TestCollision() method determines a collision between two sprites. All it does is set the Sprite’s color to red:

[code]
protected override void Collide(
BoundingRectangle overlap,
Sprite item)
{
// Turn the sprite red to indicate collision.
Color = Color.Red;
}
[/code]

Let’s give this a test by replacing our single TestSprite with an array of TestSprites. Every place we referenced TestSprite in the original code, we now have to loop through the array to handle all of our TestSprites. In order to make this a little easier to manage, we will refactor our original sprite.Update() call in the Update() method into a new UpdateSprites() method that updates every sprite. We will add a new HandleCollisions() method to our game to test for collisions. Finally, we will change the Update() method, so that it only calls StartTimer and StopTimer around the call to HandleCollisions(). The relevant sections look like the following code:
[code]
private TestSprite[] sprites = new TestSprite[10];
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
{
this.Exit();
}
UpdateSprites(gameTime);
StartTimer();
HandleCollisions();
StopTimer();
base.Update(gameTime);
}
private void UpdateSprites(GameTime gameTime)
{
foreach (Sprite sprite in sprites)
{
sprite.Update(gameTime);
}
}
private void HandleCollisions()
{
// This is brute force approach
for (int i = 0; i < sprites.Length; i++)
{
for (int j = i + 1; j < sprites.Length; j++)
{
sprites[i].TestCollision(sprites[j]);
}
}
}
[/code]

Looking at that, you may wonder why I am not using foreach for the HandleCollisions call. It is simply because with foreach, we have no way of knowing what sprites we already tested. This algorithm tests every sprite against every other sprite exactly once.

What are the results? On my machine, with 10 sprites, I get the following:
[code]
300 updates took 48827 ticks (13 milliseconds).
Each update took 0.04333333 milliseconds.
300 updates took 42466 ticks (11 milliseconds).
Each update took 0.03666667 milliseconds.
300 updates took 42371 ticks (11 milliseconds).
Each update took 0.03666667 milliseconds.
300 updates took 43086 ticks (12 milliseconds).
Each update took 0.04 milliseconds.
300 updates took 43449 ticks (12 milliseconds).
Each update took 0.04 milliseconds.
[/code]

Wow! Handling collisions for 10 sprites takes only twice as long as it did just to move one sprite. How could that be? It is partly due to the overhead of using the Stopwatch class and making method calls, and partly due to the fact that we are measuring very fast operations. Obviously, the closer you get to the resolution of the underlying timer, the more error you get in trying to time things.

Before we go on, notice also that the impact of the JIT compiler during our first set of updates is significantly less. This shows how effective the JIT compilation is and why we don’t need to worry about it affecting the performance of our game. We may take a performance hit the first time a section of code is running, but it is relatively miniscule to our overall performance.

Now let’s see what happens when we increase the number of sprites to 100:

[code]
300 updates took 2079460 ticks (580 milliseconds).
Each update took 1.933333 milliseconds.
300 updates took 2156954 ticks (602 milliseconds).
Each update took 2.006667 milliseconds.
300 updates took 2138909 ticks (597 milliseconds).
Each update took 1.99 milliseconds.
300 updates took 2150696 ticks (600 milliseconds).
Each update took 2 milliseconds.
300 updates took 2169919 ticks (606 milliseconds).
Each update took 2.02 milliseconds.
[/code]

Whether you should be impressed or dismayed depends on how you want to use this collision-handling algorithm. On one hand, averaging 2 milliseconds per frame is still a miniscule part of our 16.7 millisecond frame timing. If you are not planning to have more than a hundred sprites or so, this algorithm will suit your needs perfectly. However, looking at the relative time difference per sprite gives a completely different perspective. It takes us 50 times as long to handle 10 times the number of sprites.

How about when the number is increased to 500? I urge you to run this code, so that you can see the results for yourself!

[code]
300 updates took 28266113 ticks (7896 milliseconds).
Each update took 26.32 milliseconds.
300 updates took 28179606 ticks (7872 milliseconds).
Each update took 26.24 milliseconds.
300 updates took 28291296 ticks (7903 milliseconds).
Each update took 26.34333 milliseconds.
300 updates took 28199114 ticks (7877 milliseconds).
Each update took 26.25667 milliseconds.
300 updates took 28182787 ticks (7873 milliseconds).
Each update took 26.24333 milliseconds.
[/code]

At this time there is no way to hide the dismay. The movement is clearly getting far less than our desired 60 frames per second! In fact, just the HandleCollisions() call alone is taking almost twice our allotted 16.7 milliseconds per frame. Multiplying the number of objects by 5 increased our time by 13! The times are not increasing exactly in quadric, due to overhead, but the rate of increase is clear.

Does this mean we should never consider this algorithm? Hopefully, at this point the answer is obvious. Many games can easily get away with only having an order of a hundred or so objects active at a time, which we have clearly shown can be handled easily. The fact that the algorithm is trivial to implement and maintain makes it a no-brainer for a large number of games.

On the other hand, if you know you will need to have hundreds of objects, you will need another solution. You have two options: optimize this algorithm, or find a new one. Anyone who is experienced with code optimization will see several obvious ways to make both the algorithm and its implementation more efficient.

For starters, most games don’t actually need to test every object against every other object. Taking the Space Invasion game as an example, I don’t need to test invaders for collision with other invaders. In fact, it is almost crazy to do so.

Another obvious optimization is that the Sprite class’s BoundingBox property is adding the sprite’s current screen position to its internal BoundingRectangle every time TestCollision is called, this despite the fact that the position changes only once or twice per frame. TestCollision, on the other hand, is called once for every other sprite in the game.

In addition, the Sprite’s TestCollision code is computing the actual intersection rectangle even though we are not using it here. We could easily save some time by not computing it. However, we give ourselves more flexibility by going ahead and doing it. Remember that this is supposed to be a generic Sprite class that can be used for many games.

These suggestions don’t even get into implementation optimizations, such as always passing our BoundingBoxes by reference instead of value; and providing direct access to member variables instead of accessing them through properties. These are exactly the types of optimizations suggested by many efficiency proponents in the XNA forums. However, these also make the code less readable, harder to debug, and harder to maintain.

As Space Invasion never has more than around 60 objects on the screen at a time, the unoptimized brute force approach works just fine. In addition, that is undoubtedly true for many other games as well. However, what if your game does need more than 100 collidable objects? Should you not make those optimizations so you can handle them?

The answer is… maybe. By making some of these optimizations, we can get this same brute force algorithm to handle 500 objects at a far more reasonable 6.4 milliseconds per frame.

[code]
300 updates took 6682522 ticks (1866 milliseconds).
Each update took 6.22 milliseconds.
300 updates took 7038462 ticks (1966 milliseconds).
Each update took 6.553333 milliseconds.
300 updates took 7023610 ticks (1962 milliseconds).
Each update took 6.54 milliseconds.
300 updates took 6718281 ticks (1876 milliseconds).
Each update took 6.253334 milliseconds.
300 updates took 7136208 ticks (1993 milliseconds).
Each update took 6.643333 milliseconds.
[/code]

That is an impressive improvement and shows how significantly performance can be optimized through these techniques. However, the disadvantages mentioned earlier less maintainable and less flexible code should not be ignored. In addition, even if you do these sorts of implementation optimizations, keep in mind that this algorithm will still degrade exponentially as you add more objects. You may be able to move up from 100 to 500 objects, but it won’t get you to 1000. At some point, you need to recognize that you need a different algorithm to efficiently handle more objects, such as the one that partitions your game space, like quad trees.

Finally, remember that 6.4 milliseconds is still 40 percent of your entire frame time. If you are maintaining on the order of a thousand or more objects at a time, other parts of your code are almost certainly also going to be difficult to manage at a reasonable frame rate. Is optimizing your collision detection the best use of your time? How do you know in advance which ones to optimize? Optimizing all of them as you go will surely take you longer to write, not to mention make your code more difficult to debug and maintain.

If benchmarking shows your algorithm has problems without implementation optimizations, you are probably better off with a different algorithm.

Using the EQATEC Profiler to profile your game’s running time

Profiling your game performance is a significant part of the whole game development process. No matter how efficient the used algorithms are, or how powerful the hardware is, you still need to get sufficiently accurate CPU running time charts for different functions calling in different hardware conditions. Choosing a good profiling tool will help you to find the hot spots which consume most CPU game resources, and lead you to create the most efficient optimization. For Windows Phone, EQATEC is a good choice and in this recipe, you will learn how to use the EQATEC Profiler to profile your Window Phone game.

Getting ready

You can download the EQATEC Profiler from the official company website located at the following URL:

[code]
http://www.eqatec.com/Profiler/
[/code]

The following screenshot shows what website looks like:

EQATEC Profiler from the official company

After clicking on the Download EQATEC Profiler, a new page will let you choose the profiler version; the free version is fine for our needs. After filling out some basic information, the website will send a URL for downloading the profiler to your e-mail address. When you have installed the downloaded profiler, you are ready for profiling your Windows Phone 7 game.

How to do it…

Carry out the following steps:

  1. Run the EQATEC Profiler through the Start menu or through the root directory where the profiler binary application is located. If the profiler runs correctly, you should get the following screen:
    EQATEC Profiler through the Start menu
  2. The Browse button lets you locate the root directory of your Windows Phone 7 XAP file for profiling. When the directory is set, you will choose the XAP file which will be listed in the list box under the App path textbox. The testing XAP file and source code can be found in the bundle file of this chapter. After that, you should click on the Browse button for building the profile description file that processed the number of methods in the designated application and some application metadata.
  3. Then, after selecting the application you want to profile, click on the Run button to start the profiling. When the Run button is clicked, a prompt will pop up asking you about the device to be used for profiling, Windows Phone 7 Device or Windows Phone 7 Emulator. In the example, we chose the emulator as shown in the following screenshot:
    Windows Phone 7 Device or Windows Phone 7 Emulator
  4. Under the Run tab, if you are sure the Windows Phone 7 application is ready, it is time for profiling. The window should look similar to the following screenshot:
    Run tab, if you are sure the Windows Phone 7
  5. Now, click on the yellow Run app button. The profiler will automatically start a Windows Phone 7 emulator and connect to the emulator. Next, it will install the profiled Windows Phone 7 XAP file on the emulator. When this step is done, profiler will start to track and profile the designated application. At this moment, if you want to know the actual time of every method in your application costs, you need to click on the Take snapshot button with a timer symbol under the information list box, and a new snapshot report which includes the running time of every method will be generated. Then, click on the yellow View button once you have chosen the report you want to review.
  6. In the snapshot view window, you will find how many milliseconds every method takes. The windows will look similar to the following screenshot:
    many milliseconds every method

How it works…

The time of every method is listed in the list box:

  • Initialize() method: 693 MS
  • LoadContent() method: 671 MS
  • Draw() method: 122 MS
  • DrawModel() method: 50 MS
  • Update() method: 43 MS

You can find more details in the Details of Individual methods panel. This panel will tell you the percentage of called method costs on time of the caller. In this example, the LoadContent() method consumes 671 MS which occupies 97percent of the Initialize() method total time.

Reducing the game contents’ loading time

As you know, most of the time, before playing a game, a screen for loading game contents will show up with a running progress bar. Without this, you may feel the game is stuck and not responding. If you know that the game is loading and can see its progress, you know that all you have to do is wait. Usually, no one wants to wait too long for playing games, however. It is wasting time and can cause frustration to the user. For better user experiences, the following sections will tell you how to reduce the loading time of game contents.

Making good loading decisions

Often, the first step in reducing loading times is to understand where the current greatest expenses are. Highlighting the frequency and timing of content loading is an effective way to evaluate and adjust loading times, as well as validate that the right content (no more and no less) is being loaded in a given scenario. Consider instrumenting the following:

  • The time required to load an asset; the System.Diagnostics.Stopwatch object can be used for this
  • The frequency with which each asset has been loaded over multiple game levels, across sessions, and so on
  • The frequency with which each asset is freed
  • The average lifetime

Using the XNA content pipeline to reduce file size

Compressing the game contents into an XNB file will make a great file size reduction in building time.

For the XNA framework, assets are shared between PC, Xbox, and Windows Phone 7 platforms, and you can reuse the textures, models, and so on. If a texture is consistently scaled down for display on a Windows Phone 7, consider performing that scaling offline, rather than taking the processing penalty bandwidth and memory overhead when loading the content.

Developers may also want to exploit other texture types, such as PNG, where doing so would not contribute to already compressed assets. For sparse textures, PNG on Windows Phone will typically demonstrate superior compression to a DXT-compressed content that is brought through the XNA content pipeline. In order to use other texture types, the source files must be copied to the output directory and not compiled in the content pipeline.

Note that, while DXT-compressed assets can be used natively by Windows Phone 7 GPUs, many formats including PNG need to be expanded at runtime to a raw format of 32 bits per pixel. This expansion can lead to increased memory overhead compared to DXT compression.

In order to balance the runtime memory footprint of DXT with the loading time footprint of more aggressive compression formats, developers may choose to apply custom compression and runtime decompression to the DXT content (as built by the XNA pipeline into .xnb files), which can lead to a significant reduction in loading times. Developers should balance the loading time considerations with CPU requirements to decode their custom-encoded content, as well as with memory requirements to handle and manipulate the decompressed data. The offline custom compression and runtime title-managed decompression of the DXT content can offer a good balance of reduced size (and thus, reduced loading time) without large runtime memory costs.

Developers can also pack multiple images into a single texture, as demonstrated by the content processor in the spritesheet. We have already discussed in Chapter 4, Heads Up Display (HUD)—Your Phone Game User Interface, that spritesheets avoid DXT power-of-two restrictions imposed by the XNA content processor, and optimize the file loading (replacing many small files with one larger one).

In the realm of sound, if native audio assets from a console title are 48 kHz, consider down sampling them to 44.1 kHz (prior to applying the XNA pipeline’s own compression) for use on the phone. This will realize an immediate 8 percent savings (approximately) on storage and reading bandwidth, as well as mild CPU savings for running at the native sampling rate of the Windows Phone device (44.1 kHz).

Beyond compression, decreasing loading times can focus on data organization that focuses on the efforts of loading the content that is needed to drive to an initial interactive state, rather than preparing all possible loaded data. This is particularly important in avoiding the watchdog timer; a title that loads data for too long prior to drawing to the screen risks being terminated by the system. Developers should also give similar attention to the in-game content loading. Remember that returning to gameplay from interruptions (SMS, phone, app purchase, and so on) invalidates all the previously loaded content.

Evaluating the asynchronous background loading

Even if the game takes a substantial set-up time, there are numerous techniques to getting the user into some kind of interactive state sooner. Anything from a simplified arcade-style loading screen to cut-scenes, trivia, “did you know” facts, and other low-CPU-impact techniques can be leveraged to help smooth the setup and transition from loading to gameplay.

Loading to an initial menu state or a cut-scene, and then continuing to load additional assets in the background would seem to be appropriate strategies for masking loading times from the consumer. However, LoadContent() performs byte copies of each loaded texture asset that uses the XNA content pipeline, generating garbage. Moreover, LoadContent(), overall, will trigger the garbage collection at each megabyte of loaded data. Depending on the actual interactivity of foreground scenes, the potential CPU cost taken by garbage collection may be acceptable; playback of pre-rendered video cut-scenes takes advantage of purpose-built hardware, so the CPU utilization is typically negligible. Similarly, static or intermittently animated menu systems would likely have more success here than attempting to generate the CPU-intensive content rendered in-engine during the background loading.

Considering the custom serialization

Microsoft’s .NET framework provides an easy to use method for serializing data onto disks, using types present in the System.Xml.Serialization namespace. Simplicity always comes with tradeoffs, however; in this case, the tradeoff is the file size. The default serialization schema is verbose. The behavior of the XmlSerializer is trivially easy to change, however, and can result in significant savings in file sizes.

As an example, let’s consider the following class definition:

[code]
public class TestClass
{
public int count;
public float size;
public bool enabled;
public string
LongNameOfAMinorFieldThatDoesntNeedALongNameInTheFile = “test”;
}
[/code]

The preceding class definition, when serialized with the default XmlSerializer, produces the following XML:

[code]
<?xml version=”1.0″?>
<TestClass xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<count>0</count>
<size>0</size>
<enabled>false</enabled>
<LongNameOfAMinorFieldThatDoesntNeedALongNameInTheFile>test
</LongNameOfAMinorFieldThatDoesntNeedALongNameInTheFile>
</TestClass>
[/code]

The default behavior of XmlSerializer is to treat each public field or property as an XML element. This generates quite a bit of extra data in the file; this XML file uses 332 bytes on the disk to serialize four fields. With a few simple changes, we can get significantly smaller files from XmlSerializer. Consider the following class declaration:

[code]
public class TestClass2
{
[XmlAttribute(AttributeName=”count”)]
public int count;
[XmlAttribute(AttributeName=”size”)]
public float size;
[XmlAttribute(AttributeName=”enable”)]
public bool enabled;
[XmlAttribute(AttributeName = “longName”)]
public string
LongNameOfAMinorFieldThatDoesntNeedALongNameInTheFile = “test”;
}
[/code]

With XmlAttribute added to properties, the XmlSerializer treats the field as attributes rather than elements, and gives the attributes alternative names. The resulting XML is the following:
[code]
<?xml version=”1.0″?>
<TestClass2 xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:xsd=”http://www.w3.org/2001/XMLSchema” count =”0″ size =”0″
enable =”false” longName =”test” />
[/code]

The serialized file has significantly less wasted text. The file size also shrank to 167 bytes. This is a saving of roughly 50 percent, and a more reasonable file size to serialize four fields. Modifying your serialization code to prefer the XML attributes to XML elements will often result in similar savings. Even if you don’t perform renaming, as we did in this example, you will generally get close to a 50 percent reduction, as every XmlElement has to have a closing tag, while attributes don’t.

Avoid using XmlAttribute for complex types, or for collections of types. The space savings are minimal in these cases, and the resulting file is considerably more difficult to read. For larger amounts of data, consider writing a custom binary serialization code. In all cases, ensure that you time any new code to confirm any realized performance gains over the default Serializer settings.

Improving game performance with garbage collection

Discussing the garbage collector (GC) that runs on Windows Phone 7 devices is helpful for the Windows Phone 7 game developer. Anyone who has programmed in XNA for Windows or Xbox 360 before knows the GC well.

Value types versus reference types

One of the first things you must understand is the difference between value types and reference types. Value types such as int, float, Vector3, Matrix, and struct (this includes nullable types; a nullable type such as BOOL is just a special struct) live on the stack. The GC does not care about the stack. Well, technically, it cares slightly, but only to the extent that the system begins to run low on memory, and you would have to be trying very hard to get enough items on the stack to cause the system to run low on memory. So don’t worry about calling “new Vector3()” or “Matrix.CreateTranslation()” in your methods that run regularly (such as Update and Draw) it is just a stack allocation and it won’t anger the GC.

Classes are an entirely different matter. Classes, arrays (including arrays of value types, for example, int[ ]), collections (List<>, Dictionary<>, and so on.), and strings (yes, strings) are all reference types and they live on the heap. The heap is the GC’s caring. It pays attention to everything that shows up on the heap and to everything that no longer has any business there, but is still hanging around.

Defining a true value checking method

Take a look at the following code listing:

[code]
void CheckForTrue(bool value)
{
string trueText = “The value is true.”;
string falseText = “The value is false.”;
if (value == true)
{
Console.WriteLine(trueText);
}
else
{
Console.WriteLine(falseText);
}
return;
}
[/code]

Every time this method runs, trueText and falseText will both be allocated on the heap and will “go out of scope” when the the method is run. In other words, “gone out of scope” simply means that there are no more references to an object. A string declared with const never goes out of scope, and thus does not matter to GC for all practical purposes. This is also true for any object declared as static readonly, as once it is created it exists forever. However, the same is not true for a normal static, though many might mistakenly assume so. A static object without the readonly keyword applied to it will generally exist for the life of a program. However, if it is ever set to null, then unless there is some other reference to it, it goes out of scope and is subject to garbage collection.

Technically, the GC runs for every 1 MB of heap allocation. Whenever the GC is running, it takes time to comb through the heap and destroy any objects that are no longer in scope. Depending on how many references you have and how complex nesting of objects is, this can take a bit of time. In XNA, the clock is on a fixed time-step by default and in Windows Phone 7, the default frame rate is 30 FPS. This means that there are 33.3333333 milliseconds available for Update() and Draw() methods to finish their CPU-side tasks. Draw prepares things on the CPU-side, then hands over the actual drawing to the GPU which, being a separate processor, does not usually affect the Update/Draw side of things, except for stalls, but those are beyond the scope of this book and most people will never run into them anyway. If they finish ahead of time, the CPU hangs out and waits until it is time to run Update() again. If not, then the system takes notice that it is running behind and will skip as many draws as necessary to catch back up.

This is where the GC comes in. Normally, your code will complete just fine within 33.33 milliseconds, thereby maintaining a nice even 30 FPS (if your code does not normally complete within that time, you will see serious constant performance problems that may even cause your game to crash after a little while if XNA gets so far behind that it throws up its hands and surrenders). However, when the GC runs, it eats into that time. If you have kept the heap nice and simple, the GC will run nice and fast and this likely won’t matter. However, keeping a simple heap that the GC can run through quickly is a difficult programming task that requires a lot of planning and/or rewriting, and even then is not fool proof (sometimes, you just have a lot of stuff on the heap in a complex game with many assets). A much simpler option assuming you can do it is to limit or even eliminate all allocations during gameplay. You will obviously be allocating heap memory when you first start the game (for example, when loading assets in the LoadContent() method), and you will be allocating memory when loading levels, if you have a game with levels and decide to load each one in an interstitial screen. You will also be allocating memory when changing game screens. However, a small stutter from a couple of dropped frames in between levels or while switching screens is not a big concern the player is not going to accidentally fall off a cliff or get hit by an enemy projectile or anything when those things are happening. In fact, sometimes it makes a lot of sense to intentionally trigger the GC right before the game is going to (re)start. Triggering the GC resets the 1 MB counter and can prevent situations where the counter is at .94 MB when the level begins, such that even a small number of minimal allocations that would otherwise be perfectly acceptable, can cause problems.

Therefore, the goal is to minimize heap allocations. How do we do that? Well, the biggest contributors are needlessly creating new objects in your Update or Draw cycle and boxing value types. First, a quick note on boxing; the simplest example of boxing is casting a value type like int or enum to object in order to pass it as a state. Boxing is a great feature of .NET, but not recommended for game programming because of the heap allocations that can trigger the GC. So keep an eye out for it and try not to do it.

Another big contributor is creating new reference types. Every new instance of an object causes a heap allocation and increases that counter ever so slightly. There are several coding practices that will help you to eliminate needless heap allocation and increase performance for your game.

Using StringBuilder for string operations

Make any strings that never change into const strings.

Where you need strings that change, consider using System.Text.StringBuilder (visit http://msdn.microsoft.com/en-us/library/system.text. stringbuilder.aspx for more information on StringBuilder). All XNA methods that take a string (for example, SpriteBatch.DrawString) will also take a StringBuilder object. Make sure to use one of the constructors which take a default capacity and set it to a value high enough to hold as many characters as you plan, plus a few extra for good measure. If the internal array is large, it will never have to resize itself, and thus will never generate any heap allocations after it is created!

Drawing integer in string without garbage

If you need to draw an int value, such as a score or the number of lives a player has, consider using the following block of code (thanks to Stephen Styrchak):

[code]
public static class SpriteBatchExtensions
{
private static string[] digits = { “0”, “1”, “2”, “3”, “4”, “5”,
“6”, “7”, “8”, “9” };
private static string[] charBuffer = new string[10];
private static float[] xposBuffer = new float[10];
private static readonly string minValue =
Int32.MinValue.ToString(CultureInfo.InvariantCulture);
// Extension method for SpriteBatch that draws an integer
// without allocating any memory. This function avoids garbage
// collections that are normally caused by calling
// Int32.ToString or String.Format. the equivalent of calling
// spriteFont.MeasureString on
// value.ToString(CultureInfo.InvariantCulture).
public static Vector2 DrawInt32(this SpriteBatch spriteBatch,
SpriteFont spriteFont, int value,
Vector2 position, Color color)
{
Vector2 nextPosition = position;
if (value == Int32.MinValue)
{
nextPosition.X = nextPosition.X +
spriteFont.MeasureString(minValue).X;
spriteBatch.DrawString(spriteFont, minValue, position,
color);
position = nextPosition;
}
else
{
if (value < 0)
{
nextPosition.X = nextPosition.X +
spriteFont.MeasureString(“-“).X;
spriteBatch.DrawString(spriteFont, “-“, position,
color);
value = -value;
position = nextPosition;
}
int index = 0;
do
{
int modulus = value % 10;
value = value / 10;
charBuffer[index] = digits[modulus];
xposBuffer[index] = spriteFont.MeasureString
(digits[modulus]).X;
index += 1;
}
while (value > 0);
for (int i = index – 1; i >= 0; –i)
{
nextPosition.X = nextPosition.X + xposBuffer[i];
spriteBatch.DrawString(spriteFont, charBuffer[i],
position, color);
position = nextPosition;
}
}
return position;
}
}
[/code]

Taking advantage of the list for sprites

If you have a Sprites class, for example, create an object pool to reuse it rather than letting it fall out of scope and creating a new one each time one ceases to exist in the game and each time you need a new one. As an example, create a generic List<> of your Sprites class (refer http://msdn.microsoft.com/en-us/library/6sh2ey19. aspx for more information on lists). Use the List<> constructor overload that takes a default capacity and make sure to set it to a value high enough to contain all the objects of that sort and will exist at one time in your game (for example, 300). Then, use a for loop to go through and create all of the objects in the list up to the capacity. Add a public bool IsAlive { get; set; } property to your class to keep track of which ones are being used at any particular time. When you need a new one, loop through the list until you find one, where IsAlive is false. Take that one, set IsAlive to true, set the other properties (such as its position, direction, and so on.) to their appropriate values, and continue. When doing collision detection, loop through using a for or a foreach loop and process only the objects for which IsAlive is true. The same approach should be followed for updating and drawing them. Whenever one is no longer needed (for example, when it collides with something or it goes off screen), simply set its IsAlive to false and it will now be available for reuse without any memory allocation. If you want to be creative, you can expand on this further in several different ways. You could keep a count of the number of live objects, so that once you have processed that number in your update and draw methods, you can use the break keyword to get out of the loop early, rather than go all the way to the end. Alternatively, you could keep two lists: one for storing live objects and one for dead objects, and move objects between the two lists as appropriate.

Preferring struct rather than class when just an instance is needed

If you do want to create something, you can just create a new “instance” of each Update() or Draw() method. Try creating a struct instead of a class. Structures can perform most of the things that classes can (the major limitation being that they cannot inherit from another structure, a class, or anything else, but they can implement interfaces). Moreover, structures live on the stack and not on the heap, so unless you have a reference type, like a string or a class, as a field or property of the structure, you will not generate any trash using a structure. Remember, though, that an array of structures is a reference type (as are all arrays), and thus lives on the heap and counts towards the GC trigger limit whenever created.

Avoiding use of LINQ in game developing

Don’t use LINQ. It looks cool. It makes your code shorter, simpler, and perhaps even easier to read. However, LINQ queries can easily become a big source of trash. They are fine in your startup code, as you are going to generate trash there anyway, just by loading assets and preparing game resources. However, don’t use it in Update(), Draw(), or any other method that gets called during the gameplay.

Minimizing the use of ToString()

Minimize use of ToString(). At a minimum, it creates a string, which lives on the heap (refer to the Drawing integer in string without garbage section discussed earlier in this chapter). If you do need to use ToString(), try to limit how often it is called. If the string only changes every level, then generate it only once at the beginning of the level. If it only changes when a certain value changes, then generate it only when that value changes. Any limits you can set are worth it. The amount of time it takes to check a Boolean condition is so small as to be almost non-existent. You could probably fit tens and even hundreds of thousands of true/false checks for the time it takes the GC to run on a complex heap.